Skip to content

Commit

Permalink
Inline requests for set bindings
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=158996281
  • Loading branch information
ronshapiro committed Jun 14, 2017
1 parent 5b3097d commit 7bd173e
Show file tree
Hide file tree
Showing 8 changed files with 806 additions and 322 deletions.
66 changes: 66 additions & 0 deletions java/dagger/internal/SetBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2017 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dagger.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* A fluent builder class that returns a {@link Set}. Used in component implementations where a set
* must be created in one fluent statement for inlined request fulfillments.
*/
public final class SetBuilder<T> {
private final List<T> contributions;

private SetBuilder(int estimatedSize) {
contributions = new ArrayList<>(estimatedSize);
}

/**
* {@code estimatedSize} is the number of bindings which contribute to the set. They may each
* provide {@code [0..n)} instances to the set. Because the final size is unknown, {@code
* contributions} are collected in a list and only hashed in {@link #create()}.
*/
public static <T> SetBuilder<T> newSetBuilder(int estimatedSize) {
return new SetBuilder<T>(estimatedSize);
}

public SetBuilder<T> add(T t) {
contributions.add(t);
return this;
}

public SetBuilder<T> addAll(Collection<? extends T> collection) {
contributions.addAll(collection);
return this;
}

public Set<T> create() {
switch (contributions.size()) {
case 0:
return Collections.emptySet();
case 1:
return Collections.singleton(contributions.get(0));
default:
return Collections.unmodifiableSet(new HashSet<>(contributions));
}
}
}
37 changes: 23 additions & 14 deletions java/dagger/internal/codegen/RequestFulfillmentRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,30 @@ private RequestFulfillment createRequestFulfillment(BindingKey bindingKey) {
ProviderFieldRequestFulfillment providerFieldRequestFulfillment =
new ProviderFieldRequestFulfillment(bindingKey, memberSelect);

if (provisionBinding.bindingKind().equals(ContributionBinding.Kind.SUBCOMPONENT_BUILDER)) {
return new SubcomponentBuilderRequestFulfillment(
bindingKey, providerFieldRequestFulfillment, subcomponentNames.get(bindingKey));
switch (provisionBinding.bindingKind()) {
case SUBCOMPONENT_BUILDER:
return new SubcomponentBuilderRequestFulfillment(
bindingKey, providerFieldRequestFulfillment, subcomponentNames.get(bindingKey));
case SYNTHETIC_MULTIBOUND_SET:
return new SetBindingRequestFulfillment(
bindingKey,
provisionBinding,
resolvedBindingsMap,
this,
providerFieldRequestFulfillment);
case INJECTION:
case PROVISION:
if (provisionBinding.implicitDependencies().isEmpty()
&& !provisionBinding.scope().isPresent()
&& !provisionBinding.requiresModuleInstance()
&& provisionBinding.bindingElement().isPresent()) {
return new SimpleMethodRequestFulfillment(
bindingKey, provisionBinding, providerFieldRequestFulfillment, this);
}
// fall through
default:
return providerFieldRequestFulfillment;
}

if (provisionBinding.implicitDependencies().isEmpty()
&& !provisionBinding.scope().isPresent()
&& !provisionBinding.requiresModuleInstance()
&& provisionBinding.bindingElement().isPresent()
&& (provisionBinding.bindingKind().equals(INJECTION)
|| provisionBinding.bindingKind().equals(PROVISION))) {
return new SimpleMethodRequestFulfillment(
bindingKey, provisionBinding, providerFieldRequestFulfillment, this);
}
return providerFieldRequestFulfillment;
default:
throw new AssertionError();
}
Expand Down
121 changes: 121 additions & 0 deletions java/dagger/internal/codegen/SetBindingRequestFulfillment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright (C) 2017 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dagger.internal.codegen;

import static com.google.common.collect.Iterables.getOnlyElement;
import static com.squareup.javapoet.CodeBlock.of;
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;

import com.google.common.collect.ImmutableMap;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import dagger.internal.SetBuilder;
import java.util.Collections;
import javax.lang.model.type.TypeMirror;

/**
* A {@link RequestFulfillment} for {@link
* dagger.internal.codegen.ContributionBinding.Kind#SYNTHETIC_MULTIBOUND_SET}
*/
final class SetBindingRequestFulfillment extends SimpleInvocationRequestFulfillment {
private final ProvisionBinding binding;
private final ImmutableMap<BindingKey, ResolvedBindings> resolvedBindingsMap;
private final RequestFulfillmentRegistry registry;

SetBindingRequestFulfillment(
BindingKey bindingKey,
ProvisionBinding binding,
ImmutableMap<BindingKey, ResolvedBindings> resolvedBindingsMap,
RequestFulfillmentRegistry registry,
RequestFulfillment delegate) {
super(bindingKey, delegate);
this.binding = binding;
this.resolvedBindingsMap = resolvedBindingsMap;
this.registry = registry;
}

@Override
CodeBlock getSimpleInvocation(DependencyRequest request, ClassName requestingClass) {
// TODO(ronshapiro): if you have ImmutableSet on your classpath, use ImmutableSet.Builder
// otherwise, we can consider providing our own static factories for multibinding cases where
// all of the dependencies are @IntoSet
switch (binding.dependencies().size()) {
case 0:
return collectionsStaticFactoryInvocation(
request, requestingClass, CodeBlock.of("emptySet()"));
case 1:
{
DependencyRequest dependency = getOnlyElement(binding.dependencies());
if (isSingleValue(dependency)) {
return collectionsStaticFactoryInvocation(
request,
requestingClass,
CodeBlock.of(
"singleton($L)",
getRequestFulfillmentForDependency(dependency, requestingClass)));
}
}
// fall through
default:
CodeBlock.Builder instantiation = CodeBlock.builder();
instantiation
.add("$T.", SetBuilder.class)
.add(maybeTypeParameter(request, requestingClass))
.add("newSetBuilder($L)", binding.dependencies().size());
for (DependencyRequest dependency : binding.dependencies()) {
String builderMethod = isSingleValue(dependency) ? "add" : "addAll";
instantiation.add(
".$L($L)",
builderMethod,
getRequestFulfillmentForDependency(dependency, requestingClass));
}
return instantiation.add(".create()").build();
}
}

private CodeBlock getRequestFulfillmentForDependency(
DependencyRequest dependency, ClassName requestingClass) {
return registry
.getRequestFulfillment(dependency.bindingKey())
.getSnippetForDependencyRequest(dependency, requestingClass);
}

private static CodeBlock collectionsStaticFactoryInvocation(
DependencyRequest request, ClassName requestingClass, CodeBlock methodInvocation) {
return CodeBlock.builder()
.add("$T.", Collections.class)
.add(maybeTypeParameter(request, requestingClass))
.add(methodInvocation)
.build();
}

private static CodeBlock maybeTypeParameter(
DependencyRequest request, ClassName requestingClass) {
TypeMirror elementType = SetType.from(request.key()).elementType();
return isTypeAccessibleFrom(elementType, requestingClass.packageName())
? of("<$T>", elementType)
: of("");
}

private boolean isSingleValue(DependencyRequest dependency) {
return resolvedBindingsMap
.get(dependency.bindingKey())
.contributionBinding()
.contributionType()
.equals(ContributionType.SET);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2016 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dagger.internal.codegen;

import com.google.common.util.concurrent.Futures;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;

/**
* A {@link RequestFulfillment} that can fulfill its request with a simple call when possible, and
* otherwise delegates to a backing provider field.
*/
abstract class SimpleInvocationRequestFulfillment extends RequestFulfillment {
private final RequestFulfillment delegate;

SimpleInvocationRequestFulfillment(BindingKey bindingKey, RequestFulfillment delegate) {
super(bindingKey);
this.delegate = delegate;
}

abstract CodeBlock getSimpleInvocation(DependencyRequest request, ClassName requestingClass);

@Override
final CodeBlock getSnippetForDependencyRequest(
DependencyRequest request, ClassName requestingClass) {
switch (request.kind()) {
case INSTANCE:
return getSimpleInvocation(request, requestingClass);
case FUTURE:
return CodeBlock.of(
"$T.immediateFuture($L)", Futures.class, getSimpleInvocation(request, requestingClass));
default:
return delegate.getSnippetForDependencyRequest(request, requestingClass);
}
}

@Override
final CodeBlock getSnippetForFrameworkDependency(
FrameworkDependency frameworkDependency, ClassName requestingClass) {
return delegate.getSnippetForFrameworkDependency(frameworkDependency, requestingClass);
}
}
27 changes: 3 additions & 24 deletions java/dagger/internal/codegen/SimpleMethodRequestFulfillment.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.STATIC;

import com.google.common.util.concurrent.Futures;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.TypeName;
Expand All @@ -41,49 +40,29 @@
* requests whenever possible. In cases where direct invocation is not possible, this implementation
* delegates to one that uses a {@link javax.inject.Provider}.
*/
final class SimpleMethodRequestFulfillment extends RequestFulfillment {
final class SimpleMethodRequestFulfillment extends SimpleInvocationRequestFulfillment {

private final ProvisionBinding provisionBinding;
private final RequestFulfillment providerDelegate;
private final RequestFulfillmentRegistry registry;

SimpleMethodRequestFulfillment(
BindingKey bindingKey,
ProvisionBinding provisionBinding,
RequestFulfillment providerDelegate,
RequestFulfillmentRegistry registry) {
super(bindingKey);
super(bindingKey, providerDelegate);
checkArgument(
provisionBinding.implicitDependencies().isEmpty(),
"framework deps are not currently supported");
checkArgument(!provisionBinding.scope().isPresent());
checkArgument(!provisionBinding.requiresModuleInstance());
checkArgument(provisionBinding.bindingElement().isPresent());
this.provisionBinding = provisionBinding;
this.providerDelegate = providerDelegate;
this.registry = registry;
}

@Override
CodeBlock getSnippetForDependencyRequest(DependencyRequest request, ClassName requestingClass) {
switch (request.kind()) {
case INSTANCE:
return invokeMethodOrProxy(requestingClass);
case FUTURE:
return CodeBlock.of(
"$T.immediateFuture($L)", Futures.class, invokeMethodOrProxy(requestingClass));
default:
return providerDelegate.getSnippetForDependencyRequest(request, requestingClass);
}
}

@Override
CodeBlock getSnippetForFrameworkDependency(
FrameworkDependency frameworkDependency, ClassName requestingClass) {
return providerDelegate.getSnippetForFrameworkDependency(frameworkDependency, requestingClass);
}

private CodeBlock invokeMethodOrProxy(ClassName requestingClass) {
CodeBlock getSimpleInvocation(DependencyRequest request, ClassName requestingClass) {
ExecutableElement bindingElement = asExecutable(provisionBinding.bindingElement().get());
return requiresProxyAccess(bindingElement, requestingClass.packageName())
? invokeProxyMethod(requestingClass)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,18 @@

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import dagger.internal.codegen.DependencyRequest.Kind;

final class SubcomponentBuilderRequestFulfillment extends RequestFulfillment {
private final RequestFulfillment delegate;
final class SubcomponentBuilderRequestFulfillment extends SimpleInvocationRequestFulfillment {
private final String subcomponentBuilderName;

SubcomponentBuilderRequestFulfillment(
BindingKey bindingKey, RequestFulfillment delegate, String subcomponentBuilderName) {
super(bindingKey);
this.delegate = delegate;
super(bindingKey, delegate);
this.subcomponentBuilderName = subcomponentBuilderName;
}

@Override
CodeBlock getSnippetForDependencyRequest(DependencyRequest request, ClassName requestingClass) {
if (request.kind().equals(Kind.INSTANCE)) {
return CodeBlock.of("new $LBuilder()", subcomponentBuilderName);
}
return delegate.getSnippetForDependencyRequest(request, requestingClass);
}

@Override
CodeBlock getSnippetForFrameworkDependency(
FrameworkDependency frameworkDependency, ClassName requestingClass) {
return delegate.getSnippetForFrameworkDependency(frameworkDependency, requestingClass);
CodeBlock getSimpleInvocation(DependencyRequest request, ClassName requestingClass) {
return CodeBlock.of("new $LBuilder()", subcomponentBuilderName);
}
}
Loading

0 comments on commit 7bd173e

Please sign in to comment.