Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2006 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.internal;
     18 
     19 import com.google.inject.internal.InjectorImpl.InjectorOptions;
     20 import com.google.inject.spi.Dependency;
     21 import java.util.IdentityHashMap;
     22 import java.util.Map;
     23 
     24 /**
     25  * Internal context. Used to coordinate injections and support circular dependencies.
     26  *
     27  * @author crazybob (at) google.com (Bob Lee)
     28  */
     29 final class InternalContext implements AutoCloseable {
     30 
     31   private final InjectorOptions options;
     32 
     33   private final Map<Object, ConstructionContext<?>> constructionContexts =
     34       new IdentityHashMap<Object, ConstructionContext<?>>();
     35 
     36   /** Keeps track of the type that is currently being requested for injection. */
     37   private Dependency<?> dependency;
     38 
     39   /**
     40    * Keeps track of the hierarchy of types needed during injection.
     41    *
     42    * <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
     43    * even indices, and sources on odd indices. This structure is to avoid the memory overhead of
     44    * DependencyAndSource objects, which can add to several tens of megabytes in large applications.
     45    */
     46   private Object[] dependencyStack = new Object[16];
     47 
     48   private int dependencyStackSize = 0;
     49 
     50 
     51   /**
     52    * The number of times {@link #enter()} has been called + 1 for initial construction. This value
     53    * is decremented when {@link #exit()} is called.
     54    */
     55   private int enterCount;
     56 
     57   /**
     58    * A single element array to clear when the {@link #enterCount} hits {@code 0}.
     59    *
     60    * <p>This is the value stored in the {@code InjectorImpl.localContext} thread local.
     61    */
     62   private final Object[] toClear;
     63 
     64   InternalContext(InjectorOptions options, Object[] toClear) {
     65     this.options = options;
     66     this.toClear = toClear;
     67     this.enterCount = 1;
     68   }
     69 
     70   /** Should only be called by InjectorImpl.enterContext(). */
     71   void enter() {
     72     enterCount++;
     73   }
     74 
     75   /** Should be called any any method that received an instance via InjectorImpl.enterContext(). */
     76   @Override
     77   public void close() {
     78     int newCount = --enterCount;
     79     if (newCount < 0) {
     80       throw new IllegalStateException("Called close() too many times");
     81     }
     82     if (newCount == 0) {
     83       toClear[0] = null;
     84     }
     85   }
     86 
     87   InjectorOptions getInjectorOptions() {
     88     return options;
     89   }
     90 
     91   @SuppressWarnings("unchecked")
     92   <T> ConstructionContext<T> getConstructionContext(Object key) {
     93     ConstructionContext<T> constructionContext =
     94         (ConstructionContext<T>) constructionContexts.get(key);
     95     if (constructionContext == null) {
     96       constructionContext = new ConstructionContext<>();
     97       constructionContexts.put(key, constructionContext);
     98     }
     99     return constructionContext;
    100   }
    101 
    102   Dependency<?> getDependency() {
    103     return dependency;
    104   }
    105 
    106   /** Sets the new current dependency & adds it to the state. */
    107   Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
    108     Dependency<?> previous = this.dependency;
    109     this.dependency = dependency;
    110     doPushState(dependency, source);
    111     return previous;
    112   }
    113 
    114 
    115   /** Pops the current state & sets the new dependency. */
    116   void popStateAndSetDependency(Dependency<?> newDependency) {
    117     popState();
    118     this.dependency = newDependency;
    119   }
    120 
    121 
    122   /** Adds to the state without setting the dependency. */
    123   void pushState(com.google.inject.Key<?> key, Object source) {
    124     doPushState(key, source);
    125   }
    126 
    127 
    128   private void doPushState(Object dependencyOrKey, Object source) {
    129     int localSize = dependencyStackSize;
    130     Object[] localStack = dependencyStack;
    131     if (localStack.length < localSize + 2) {
    132       localStack = dependencyStack =
    133         java.util.Arrays.copyOf(localStack, (localStack.length * 3) / 2 + 2);
    134     }
    135     localStack[localSize++] = dependencyOrKey;
    136     localStack[localSize++] = source;
    137     dependencyStackSize = localSize;
    138   }
    139 
    140 
    141   /** Pops from the state without setting a dependency. */
    142   void popState() {
    143     // N.B. we don't null out the array entries.  It isn't necessary since all the objects in the
    144     // array (Key, Dependency, or Binding source objects) are all tied to the lifetime of the
    145     // injector, which is greater than the lifetime of this object.  So removing them from the array
    146     // doesn't matter.
    147     dependencyStackSize -= 2;
    148   }
    149 
    150 
    151   /** Returns the current dependency chain (all the state stored in the dependencyStack). */
    152   java.util.List<com.google.inject.spi.DependencyAndSource> getDependencyChain() {
    153     com.google.common.collect.ImmutableList.Builder<com.google.inject.spi.DependencyAndSource>
    154         builder = com.google.common.collect.ImmutableList.builder();
    155     for (int i = 0; i < dependencyStackSize; i += 2) {
    156       Object evenEntry = dependencyStack[i];
    157       Dependency<?> dependency;
    158       if (evenEntry instanceof com.google.inject.Key) {
    159         dependency = Dependency.get((com.google.inject.Key<?>) evenEntry);
    160       } else {
    161         dependency = (Dependency<?>) evenEntry;
    162       }
    163       builder.add(new com.google.inject.spi.DependencyAndSource(dependency, dependencyStack[i + 1]));
    164     }
    165     return builder.build();
    166   }
    167 
    168 }
    169