All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.inject.rebind.output.ReachabilityAnalyzer Maven / Gradle / Ivy

Go to download

GIN (GWT INjection) brings automatic dependency injection to Google Web Toolkit client-side code. GIN is built on top of Guice and uses (a subset of) Guice's binding language.

There is a newer version: 2.1.2
Show newest version
 /*
 * Copyright 2012 Google Inc.
 *
 * 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 com.google.gwt.inject.rebind.output;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.inject.rebind.GinScope;
import com.google.gwt.inject.rebind.GinjectorBindings;
import com.google.gwt.inject.rebind.RootBindings;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.binding.Dependency;
import com.google.gwt.inject.rebind.binding.ExposedChildBinding;
import com.google.gwt.inject.rebind.binding.ParentBinding;
import com.google.gwt.inject.rebind.reflect.FieldLiteral;
import com.google.gwt.inject.rebind.reflect.MethodLiteral;
import com.google.gwt.inject.rebind.util.GuiceUtil;
import com.google.gwt.inject.rebind.util.MemberCollector;
import com.google.gwt.inject.rebind.util.PrettyPrinter;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionPoint;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * Utility class that determines which bindings from a {@link GinjectorBindings}
 * hierarchy should actually be written in the generated code.
 *
 * 

This eliminates a significant amount of code that we know will never be * invoked, based on our high-level knowledge of the generated code. This is * preferable to relying entirely on GWT's reachability analysis, which is * (understandably) imperfect and sometimes makes different judgements as we * restructure the generated code files. */ final class ReachabilityAnalyzer { private Set reachable = null; private Map>> reachableMemberInjects = null; private final GuiceUtil guiceUtil; private final TreeLogger logger; private final MemberCollector memberCollector; private final GinjectorBindings rootBindings; @Inject public ReachabilityAnalyzer( GuiceUtil guiceUtil, Provider memberCollectorProvider, @RootBindings GinjectorBindings rootBindings, TreeLogger logger) { this.guiceUtil = guiceUtil; this.logger = logger; this.memberCollector = memberCollectorProvider.get(); this.rootBindings = rootBindings; this.memberCollector.setMethodFilter(MemberCollector.ALL_METHOD_FILTER); } /** * Tests whether the given binding is reachable from a true root. * *

A true root is a key that the user can actually create. In contrast, * the dependency tree we generate earlier in the process is rooted at keys * that must exist semantically, even if they are never created. * *

More specifically, the following keys are true roots: * *

    *
  • Every non-Void key that is the return value of an injector method. *
  • Every key that is a parameter to an injector method. *
  • Every key that is injected as an eager singleton. *
  • Every key that is a parameter to a static injection method on a class * for which static injection was requested. *
  • Every key that is the type of a statically injected field of a class * for which static injection was requested. *
*/ boolean isReachable(Binding binding) { if (reachable == null) { computeReachable(); } return reachable.contains(binding); } boolean isReachableMemberInject(GinjectorBindings bindings, TypeLiteral type) { if (reachableMemberInjects == null) { computeReachable(); } return getReachableMemberInjects(bindings).contains(type); } private void computeReachable() { reachable = new LinkedHashSet(); reachableMemberInjects = new LinkedHashMap>>(); logger.log(TreeLogger.DEBUG, "Begin reachability analysis"); // Note on implementation: for simplicity, we use a Binding as the node of // the graph that we run reachability on. This would be incoherent before // binding resolution (when we didn't know which bindings existed), but now // that all the bindings are created, every key that we can inject has a // unique Binding object. Since the caller of this routine is interested in // determining which bindings to output, it's more convenient to just work // at the binding level. traceGinjectorMethods(); traceEagerSingletons(); traceStaticInjections(); logger.log(TreeLogger.DEBUG, "End reachability analysis"); } /** Traces out bindings that are reachable from a GInjector method. */ private void traceGinjectorMethods() { TypeLiteral ginjectorInterface = rootBindings.getGinjectorInterface(); for (MethodLiteral method : memberCollector.getMethods(ginjectorInterface)) { if (!guiceUtil.isMemberInject(method)) { // It's a constructor method, so just trace to the key that's // constructed. Key key = guiceUtil.getKey(method); PrettyPrinter.log(logger, TreeLogger.DEBUG, "ROOT -> %s:%s [%s]", rootBindings, key, method); traceKey(key, rootBindings); } else { Key sourceKey = guiceUtil.getKey(method); getReachableMemberInjects(rootBindings).add(sourceKey.getTypeLiteral()); for (Dependency dependency : guiceUtil.getMemberInjectionDependencies(sourceKey, sourceKey.getTypeLiteral())) { Key targetKey = dependency.getTarget(); PrettyPrinter.log( logger, TreeLogger.DEBUG, "ROOT -> %s:%s [%s]", rootBindings, targetKey, method); traceKey(targetKey, rootBindings); } } } } /** Traces out bindings that are reachable from an eager singleton. */ private void traceEagerSingletons() { doTraceEagerSingletons(rootBindings); } private void doTraceEagerSingletons(GinjectorBindings bindings) { for (Map.Entry, Binding> entry : bindings.getBindings()) { Key key = entry.getKey(); Binding binding = entry.getValue(); GinScope scope = bindings.determineScope(key); if (scope == GinScope.EAGER_SINGLETON) { PrettyPrinter.log(logger, TreeLogger.DEBUG, "ROOT -> %s:%s [eager singleton: %s]", bindings, key, binding); traceKey(key, bindings); } } for (GinjectorBindings child : bindings.getChildren()) { doTraceEagerSingletons(child); } } /** * Traces out bindings that are reachable from statically injected fields and * methods. */ private void traceStaticInjections() { doTraceStaticInjections(rootBindings); } private void doTraceStaticInjections(GinjectorBindings bindings) { for (Class klass : bindings.getStaticInjectionRequests()) { traceStaticInjectionsFor(klass, bindings); } for (GinjectorBindings child : bindings.getChildren()) { doTraceStaticInjections(child); } } private void traceStaticInjectionsFor(Class klass, GinjectorBindings bindings) { TypeLiteral type = TypeLiteral.get(klass); for (InjectionPoint injectionPoint : InjectionPoint.forStaticMethodsAndFields(klass)) { Member member = injectionPoint.getMember(); if (member instanceof Method) { Method methodRaw = (Method) member; TypeLiteral declaringClass = TypeLiteral.get(methodRaw.getDeclaringClass()); MethodLiteral method = MethodLiteral.get(methodRaw, declaringClass); for (Key key : method.getParameterKeys()) { PrettyPrinter.log(logger, TreeLogger.DEBUG, "ROOT -> %s:%s [static injection: %s]", bindings, key, method); traceKey(key, bindings); } } else if (member instanceof Field) { Field fieldRaw = (Field) member; TypeLiteral declaringClass = TypeLiteral.get(fieldRaw.getDeclaringClass()); FieldLiteral field = FieldLiteral.get(fieldRaw, declaringClass); Key key = guiceUtil.getKey(field); PrettyPrinter.log(logger, TreeLogger.DEBUG, "ROOT -> %s:%s [static injection: %s]", bindings, key, field); traceKey(key, bindings); } } } /** * Marks the binding of the given key in the given {@link GinjectorBindings} * as reachable, and traces out its dependencies. */ private void traceKey(Key key, GinjectorBindings bindings) { Binding binding = bindings.getBinding(key); // Make sure the binding is present: optional bindings might be missing. if (binding != null) { if (!reachable.add(binding)) { // The binding was already marked as reachable. return; } getReachableMemberInjects(bindings).addAll(binding.getMemberInjectRequests()); for (Dependency dependency : binding.getDependencies()) { if (dependency.getSource().equals(key)) { Key target = dependency.getTarget(); PrettyPrinter.log(logger, TreeLogger.DEBUG, "%s:%s -> %s:%s [%s]", bindings, key, bindings, dependency.getTarget(), binding); traceKey(target, bindings); } } // Special cases: parent / child bindings induce dependencies between // GinjectorBindings objects, which can't be represented in the standard // dependency graph. if (binding instanceof ParentBinding) { ParentBinding parentBinding = (ParentBinding) binding; PrettyPrinter.log(logger, TreeLogger.DEBUG, "%s:%s -> %s:%s [inherited]", bindings, key, parentBinding.getParentBindings(), key); traceKey(key, parentBinding.getParentBindings()); } else if (binding instanceof ExposedChildBinding) { ExposedChildBinding exposedChildBinding = (ExposedChildBinding) binding; PrettyPrinter.log(logger, TreeLogger.DEBUG, "%s:%s -> %s:%s [exposed]", bindings, key, exposedChildBinding.getChildBindings(), key); traceKey(key, exposedChildBinding.getChildBindings()); } } } private Set> getReachableMemberInjects(GinjectorBindings bindings) { Set> result = reachableMemberInjects.get(bindings); if (result == null) { result = new LinkedHashSet>(); reachableMemberInjects.put(bindings, result); } return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy