Allow turning off stack trace collection in Guice.

By setting guice_include_stack_traces flag OFF, Guice does not collect stack traces for identifying the declaring source of a binding. Instead it uses the first non-skipped module class name from the modules stack. As a result, in some cases, error messages can be slightly different with this flag. For example, the file name and line number are not always available.

A sample error message with this flag :

Guice creation errors:
1) Received null converting foo (bound at com.google.inject.TypeConversionTest.configure(Unknown Source) (via modules: com.google.inject.TypeConversionTest -> com.google.inject.TypeConversionTest)) to java.util.Date
using CustomConverter which matches only(java.util.Date) (bound at com.google.inject.TypeConversionTest.configure(Unknown Source) (via modules: com.google.inject.TypeConversionTest -> com.google.inject.TypeConversionTest -> com.google.inject.TypeConversionTest)).
while locating java.util.Date annotated with @com.google.inject.TypeConversionTest()
for field at com.google.inject.TypeConversionTest.date(TypeConversionTest.java:478)
at com.google.inject.TypeConversionTest.configure(Unknown Source) (via modules: com.google.inject.TypeConversionTest -> com.google.inject.TypeConversionTest)

This also changes the DEFAULT flag name to ONLY_FOR_DECLARTION_SOURCE.
Adds InternalFlags.getIncludeStackTraceOption() to wrap system property access.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=53610378
diff --git a/build.xml b/build.xml
index 544117b..81ed3ee 100644
--- a/build.xml
+++ b/build.xml
@@ -78,11 +78,24 @@
           excludes="build/**,**/.svn/**,classes/**,.settings/**,bin/**,latest-api-diffs/**,latest-javadoc/**,.classpath,.project"/>
     </zip>
   </target>
-  
+
   <target name="test.dist"
-      depends="jar, test.compile-with-deps"
-      description="Execute JUnit tests against distribution jar.">
-    <java fork="true" 
+    description="Execute JUnit tests against distribution jar.">
+    <antcall target="test.dist.run">
+      <param name="jvmarg-value" value="-Dguice_include_stack_traces="/>
+    </antcall>
+    <antcall target="test.dist.run">
+      <param name="jvmarg-value" value="-Dguice_include_stack_traces=OFF"/>
+    </antcall>
+    <antcall target="test.dist.run">
+      <param name="jvmarg-value" value="-Dguice_include_stack_traces=COMPLETE"/>
+    </antcall>
+  </target>
+
+  <target name="test.dist.run"
+    depends="jar, test.compile-with-deps"
+    description="Execute JUnit tests against distribution jar with the given jvmarg.">
+    <java fork="true"
         classname="junit.textui.TestRunner"
         failonerror="true"
         taskname="junit">
@@ -98,7 +111,8 @@
         <pathelement location="lib/build/bnd-0.0.384.jar"/>
         <pathelement location="lib/build/felix-2.0.5.jar"/>
       </classpath>
-      <arg value="com.google.inject.AllTests"/>    
+      <arg value="com.google.inject.AllTests"/>
+      <jvmarg value="${jvmarg-value}"/>
       <syspropertyset>
         <propertyref name="guice.custom.loader"/>
         <propertyref name="version"/>
diff --git a/core/src/com/google/inject/internal/InternalFlags.java b/core/src/com/google/inject/internal/InternalFlags.java
new file mode 100644
index 0000000..9458281
--- /dev/null
+++ b/core/src/com/google/inject/internal/InternalFlags.java
@@ -0,0 +1,33 @@
+package com.google.inject.internal;
+
+
+/**
+ * Contains flags for Guice.
+ */
+public class InternalFlags {
+
+  /**
+   * The options for Guice stack trace collection.
+   */
+  public enum IncludeStackTraceOption {
+    // No stack trace collection
+    OFF,
+    // Minimum stack trace collection (Default)
+    ONLY_FOR_DECLARING_SOURCE,
+    // Full stack trace for everything
+    COMPLETE
+  }
+
+
+  public static IncludeStackTraceOption getIncludeStackTraceOption() {
+    String propertyValue = System.getProperty(
+        "guice_include_stack_traces");
+    if (IncludeStackTraceOption.OFF.name().equals(propertyValue)) {
+        return IncludeStackTraceOption.OFF;
+    }
+    if (IncludeStackTraceOption.COMPLETE.name().equals(propertyValue)) {
+        return IncludeStackTraceOption.COMPLETE;
+    }
+    return IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE;
+  }
+}
diff --git a/core/src/com/google/inject/internal/util/SourceProvider.java b/core/src/com/google/inject/internal/util/SourceProvider.java
index d627554..b36cea8 100644
--- a/core/src/com/google/inject/internal/util/SourceProvider.java
+++ b/core/src/com/google/inject/internal/util/SourceProvider.java
@@ -92,4 +92,17 @@
     }
     throw new AssertionError();
   }
+
+  /**
+   * Returns the non-skipped module class name.
+   */
+  public Object getFromClassNames(List<String> moduleClassNames) {
+    Preconditions.checkNotNull(moduleClassNames, "The list of module class names cannot be null.");
+    for (final String moduleClassName : moduleClassNames) {
+      if (!shouldBeSkipped(moduleClassName)) {
+        return new StackTraceElement(moduleClassName, "configure", null, -1);
+      }
+    }
+    return UNKNOWN_SOURCE;
+  }
 }
diff --git a/core/src/com/google/inject/internal/util/StackTraceElements.java b/core/src/com/google/inject/internal/util/StackTraceElements.java
index db10c28..69f930f 100644
--- a/core/src/com/google/inject/internal/util/StackTraceElements.java
+++ b/core/src/com/google/inject/internal/util/StackTraceElements.java
@@ -33,6 +33,10 @@
  */
 public class StackTraceElements {
 
+  private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+  private static final InMemoryStackTraceElement[] EMPTY_INMEMORY_STACK_TRACE = 
+      new InMemoryStackTraceElement[0];
+
   /*if[AOP]*/
   static final LoadingCache<Class<?>, LineNumbers> lineNumbersCache =
       CacheBuilder.newBuilder().weakKeys().softValues().build(
@@ -100,6 +104,9 @@
    */
   public static InMemoryStackTraceElement[] convertToInMemoryStackTraceElement(
       StackTraceElement[] stackTraceElements) {
+    if (stackTraceElements.length == 0) {
+      return EMPTY_INMEMORY_STACK_TRACE;
+    }
     InMemoryStackTraceElement[] inMemoryStackTraceElements = 
         new InMemoryStackTraceElement[stackTraceElements.length];
     for (int i = 0; i < stackTraceElements.length; i++) {
@@ -114,6 +121,9 @@
    */
   public static StackTraceElement[] convertToStackTraceElement(
       InMemoryStackTraceElement[] inMemoryStackTraceElements) {
+    if (inMemoryStackTraceElements.length == 0) {
+      return EMPTY_STACK_TRACE;
+    }
     StackTraceElement[] stackTraceElements = 
         new StackTraceElement[inMemoryStackTraceElements.length];
     for (int i = 0; i < inMemoryStackTraceElements.length; i++) {
diff --git a/core/src/com/google/inject/spi/ElementSource.java b/core/src/com/google/inject/spi/ElementSource.java
index cfe7bb9..35246d4 100644
--- a/core/src/com/google/inject/spi/ElementSource.java
+++ b/core/src/com/google/inject/spi/ElementSource.java
@@ -19,9 +19,9 @@
  * lists {@link StackTraceElement StackTraceElements} in reverse chronological order. The first 
  * element (index zero) is the last method call and the last element is the first method invocation.
  * By default, the stack trace is not collected. The default behavior can be changed by setting the 
- * {@code guice_include_stack_traces} flag value. The value can be either {@code DEFAULT} or 
- * {@code COMPLETE}. Note that collecting stack traces for every binding can cause a performance hit
- * when the injector is created.
+ * {@code guice_include_stack_traces} flag value. The value can be either {@code OFF}, {@code
+ * ONLY_FOR_DECLARING_SOURCE} or {@code COMPLETE}. Note that collecting stack traces for every
+ * binding can cause a performance hit when the injector is created.
  * <p>
  * The sequence of class names of {@link Module modules} involved in the element creation can be 
  * retrieved by {@link #getModuleClassNames()}. Similar to {@link #getStackTrace()}, the order is 
@@ -35,7 +35,7 @@
 public final class ElementSource {
 
   /** 
-   * The {@link ElementSource source} of element that this element created from (if there is any), 
+   * The {@link ElementSource source} of element that this element created from (if there is any),
    * otherwise {@code null}.
    */
   final ElementSource originalElementSource;
@@ -44,8 +44,8 @@
   final ModuleSource moduleSource;
   
   /** 
-   * The partial call stack that starts at the last module {@link Module#Configure(Binder) 
-   * configure(Binder)} call. 
+   * The partial call stack that starts at the last module {@link Module#Configure(Binder)
+   * configure(Binder)} call. The value is empty if stack trace collection is off.
    */
   final InMemoryStackTraceElement[] partialCallStack;
   
@@ -59,7 +59,7 @@
 
   /**
    * Creates a new {@ElementSource} from the given parameters. 
-   * @param originalElementSource The source of element that this element created from (if there is 
+   * @param originalElementSource The source of element that this element created from (if there is
    * any), otherwise {@code null}.
    * @param declaringSource the source (in)directly declared the element.
    * @param moduleSource the moduleSource when the element is bound
@@ -68,17 +68,17 @@
    */
   ElementSource(/* @Nullable */ ElementSource originalSource, Object declaringSource, 
       ModuleSource moduleSource, StackTraceElement[] partialCallStack) {
+    Preconditions.checkNotNull(declaringSource, "declaringSource cannot be null.");
     Preconditions.checkNotNull(moduleSource, "moduleSource cannot be null.");
     Preconditions.checkNotNull(partialCallStack, "partialCallStack cannot be null.");
-    Preconditions.checkNotNull(declaringSource, "declaringSource cannot be null.");
     this.originalElementSource = originalSource;
+    this.declaringSource = declaringSource;
     this.moduleSource = moduleSource;
     this.partialCallStack = StackTraceElements.convertToInMemoryStackTraceElement(partialCallStack);
-    this.declaringSource = declaringSource;
   }
   
   /**
-   * Returns the {@link ElementSource} of the element this was created or copied from.  If this was 
+   * Returns the {@link ElementSource} of the element this was created or copied from. If this was
    * not created or copied from another element, returns {@code null}.
    */
   public ElementSource getOriginalElementSource() {
@@ -96,29 +96,17 @@
   }
   
   /**
-   * Returns the class names of modules involved in creating this {@link Element}.  The first
+   * Returns the class names of modules involved in creating this {@link Element}. The first
    * element (index 0) is the class name of module that defined the element, and the last element
-   * is the class name of root module. In the cases where the class name is null an empty string
-   * is returned.
+   * is the class name of root module.
    */
   public List<String> getModuleClassNames() {
-    String[] classNames = new String[moduleSource.size()];
-    ModuleSource current = moduleSource;
-    int cursor = 0;
-    while (current != null) {
-      classNames[cursor] = current.getModuleClassName();
-      if (classNames[cursor] == null) {
-          classNames[cursor] = "";
-      }
-      current = current.getParent();
-      cursor++;
-    }
-    return ImmutableList.<String>copyOf(classNames);
+    return moduleSource.getModuleClassNames();
   }
-  
+
   /**
-   * Returns the position of {@link Module#configure(Binder) configure(Binder)} method call in the 
-   * {@link #getStackTrace() stack trace} for modules that their classes returned by 
+   * Returns the position of {@link Module#configure(Binder) configure(Binder)} method call in the
+   * {@link #getStackTrace() stack trace} for modules that their classes returned by
    * {@link #getModuleClassNames()}. For example, if the stack trace looks like the following:
    * <p>
    * {@code
@@ -131,28 +119,23 @@
    * <p>
    * 1 and 3 are returned.
    * <p>
-   * In the cases where stack trace is not available (i.e.,the stack trace was not collected), 
-   * it returns an empty array.
+   * In the cases where stack trace is not available (i.e., the stack trace was not collected),
+   * it returns -1 for all module positions.
    */
   public List<Integer> getModuleConfigurePositionsInStackTrace() {
-    if (!isStackTraceRetained()) {
-      return ImmutableList.<Integer>of();
-    }
     int size = moduleSource.size();
     Integer[] positions = new Integer[size];
-    int position = partialCallStack.length;
-    positions[0] = position - 1;
+    int chunkSize = partialCallStack.length;
+    positions[0] = chunkSize - 1;
     ModuleSource current = moduleSource;
-    int cursor = 1;
-    while (cursor < size) {
-      position += current.getPartialCallStack().length;
-      positions[cursor] = position - 1;
+    for (int cursor = 1; cursor < size; cursor++) {
+      chunkSize = current.getPartialCallStackSize();
+      positions[cursor] = positions[cursor - 1] + chunkSize;
       current = current.getParent();
-      cursor++;
     }
     return ImmutableList.<Integer>copyOf(positions);
   }
-  
+
   /**
    * Returns the sequence of method calls that ends at one of {@link Binder} {@code bindXXX()} 
    * methods and eventually defines the element. Note that {@link #getStackTrace()} lists {@link 
@@ -164,7 +147,7 @@
   public StackTraceElement[] getStackTrace() {
     int modulesCallStackSize = moduleSource.getStackTraceSize();
     int chunkSize = partialCallStack.length;
-    int size = moduleSource.getStackTraceSize() + partialCallStack.length;
+    int size = moduleSource.getStackTraceSize() + chunkSize;
     StackTraceElement[] callStack = new StackTraceElement[size];
     System.arraycopy(
         StackTraceElements.convertToStackTraceElement(partialCallStack), 0, callStack, 0, 
@@ -172,13 +155,6 @@
     System.arraycopy(moduleSource.getStackTrace(), 0, callStack, chunkSize, modulesCallStackSize);
     return callStack;
   }
-  
-  /**
-   * Returns true if stack trace was collected.
-   */ 
-  public boolean isStackTraceRetained() {
-    return (partialCallStack.length > 0);
-  }
 
   /**
    * Returns {@code getDeclaringSource().toString()} value.
diff --git a/core/src/com/google/inject/spi/Elements.java b/core/src/com/google/inject/spi/Elements.java
index cfbda5b..44d49ff 100644
--- a/core/src/com/google/inject/spi/Elements.java
+++ b/core/src/com/google/inject/spi/Elements.java
@@ -17,10 +17,13 @@
 package com.google.inject.spi;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
+import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+
 import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Binding;
@@ -65,14 +68,6 @@
  */
 public final class Elements {
 
-  private enum IncludeStackTraceFlagValues {
-    // Minimum stack trace collection
-    DEFAULT,
-    // Full stack trace for everything
-    COMPLETE
-  }
-
-
   private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
       = new DefaultBindingTargetVisitor<Object, Object>() {
     @Override public Object visit(InstanceBinding<?> binding) {
@@ -113,14 +108,12 @@
     for (Module module : modules) {
       binder.install(module);
     }
-    if (collectCompleteStackTrace()) {
-      // Free the memory consumed by the stack trace elements cache
-      StackTraceElements.clearCache();
-    }
+    // Free the memory consumed by the stack trace elements cache
+    StackTraceElements.clearCache();
     binder.rehashKeys();
     return Collections.unmodifiableList(binder.elements);
   }
-
+  
   private static class ElementsAsModule implements Module {
     private final Iterable<? extends Element> elements;
 
@@ -147,11 +140,6 @@
     return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR;
   }
 
-  private static boolean collectCompleteStackTrace() {
-    return IncludeStackTraceFlagValues.COMPLETE.name().equals(
-        System.getProperty("guice_include_stack_traces"));
-  }
-
   private static class RecordingBinder implements Binder, PrivateBinder, RehashableKeys {
     private final Stage stage;
     private final Set<Module> modules;
@@ -245,7 +233,7 @@
     public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener) {
       elements.add(new TypeListenerBinding(getElementSource(), listener, typeMatcher));
     }
-
+    
     public void bindListener(Matcher<? super Binding<?>> bindingMatcher,
         ProvisionListener... listeners) {
       elements.add(new ProvisionListenerBinding(getElementSource(), bindingMatcher, listeners));
@@ -266,7 +254,7 @@
         }
         if (module instanceof PrivateModule) {
           binder = binder.newPrivateBinder();
-        }
+        }      
         try {
           module.configure(binder);
         } catch (RuntimeException e) {
@@ -336,7 +324,7 @@
       elements.add(new TypeConverterBinding(getElementSource(), typeMatcher, converter));
     }
 
-    public RecordingBinder withSource(final Object source) {
+    public RecordingBinder withSource(final Object source) {            
       return new RecordingBinder(this, source, null);
     }
 
@@ -357,15 +345,15 @@
       rehashables.add(binder);
       return binder;
     }
-
+    
     public void disableCircularProxies() {
       elements.add(new DisableCircularProxiesOption(getElementSource()));
     }
-
+    
     public void requireExplicitBindings() {
-      elements.add(new RequireExplicitBindingsOption(getElementSource()));
+      elements.add(new RequireExplicitBindingsOption(getElementSource()));     
     }
-
+    
     public void requireAtInjectOnConstructors() {
       elements.add(new RequireAtInjectOnConstructorsOption(getElementSource()));
     }
@@ -403,10 +391,10 @@
 
     private ModuleSource getModuleSource(Module module) {
       StackTraceElement[] partialCallStack;
-      if (!collectCompleteStackTrace()) {
-        partialCallStack = new StackTraceElement[0];
-      } else {
+      if (getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE) {
         partialCallStack = getPartialCallStack(new Throwable().getStackTrace());
+      } else {
+        partialCallStack = new StackTraceElement[0];
       }
       if (moduleSource == null) {
         return new ModuleSource(module, partialCallStack);
@@ -415,39 +403,47 @@
     }
 
     private ElementSource getElementSource() {
-      Object declaringSource = source;
       // Full call stack
-      StackTraceElement[] callStack;
+      StackTraceElement[] callStack = null;
       // The call stack starts from current top module configure and ends at this method caller
-      StackTraceElement[] partialCallStack;
-      if (!collectCompleteStackTrace()) {
-        callStack = null;
-        partialCallStack = new StackTraceElement[0];
-      } else {
-        callStack = new Throwable().getStackTrace();
-        partialCallStack = getPartialCallStack(callStack);
-      }
-      if (declaringSource == null) {
-        // TODO(salmanmir): can we avoid getting the full stack trace by using modules stack?
-        if (callStack == null) {
-          callStack = new Throwable().getStackTrace();
-        }
-        declaringSource = sourceProvider.get(callStack);
-      }
+      StackTraceElement[] partialCallStack = new StackTraceElement[0];
+      // The element original source
       ElementSource originalSource = null;
+      // The element declaring source
+      Object declaringSource = source;
       if (declaringSource instanceof ElementSource) {
         originalSource = (ElementSource) declaringSource;
         declaringSource = originalSource.getDeclaringSource();
       }
+      IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption();
+      if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
+          (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE 
+          && declaringSource == null)) {
+        callStack = new Throwable().getStackTrace();
+      }
+      if (stackTraceOption == IncludeStackTraceOption.COMPLETE) {
+        partialCallStack = getPartialCallStack(callStack);
+      }
+      if (declaringSource == null) {
+        // So 'source' and 'originalSource' are null otherwise declaringSource has some value
+        if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
+            stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) {
+          // With the above conditions and assignments 'callStack' is non-null
+          declaringSource = sourceProvider.get(callStack);
+        } else { // or if (stackTraceOption == IncludeStackTraceOptions.OFF)
+          // As neither 'declaring source' nor 'call stack' is available use 'module source'
+          declaringSource = sourceProvider.getFromClassNames(moduleSource.getModuleClassNames());
+        }
+      }
       // Build the binding call stack
       return new ElementSource(
           originalSource, declaringSource, moduleSource, partialCallStack);
     }
 
     /**
-     * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It
-     * also removes the last two elements in order to make {@link #install(Module)} the last call
-     * in the call stack.
+     * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It  
+     * also removes the last two elements in order to make {@link #install(Module)} the last call 
+     * in the call stack.  
      */
     private StackTraceElement[] getPartialCallStack(StackTraceElement[] callStack) {
       int toSkip = 0;
@@ -461,7 +457,7 @@
       System.arraycopy(callStack, 1, partialCallStack, 0, chunkSize);
       return partialCallStack;
     }
-
+    
     @Override public void rehashKeys() {
       for (RehashableKeys rehashable : rehashables) {
         rehashable.rehashKeys();
diff --git a/core/src/com/google/inject/spi/ModuleSource.java b/core/src/com/google/inject/spi/ModuleSource.java
index 627ff4d..4e2a549 100644
--- a/core/src/com/google/inject/spi/ModuleSource.java
+++ b/core/src/com/google/inject/spi/ModuleSource.java
@@ -1,20 +1,22 @@
 package com.google.inject.spi;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Module;
 import com.google.inject.internal.util.StackTraceElements;
 import com.google.inject.internal.util.StackTraceElements.InMemoryStackTraceElement;
 
+import java.util.List;
+
 /**
  * Associated to a {@link Module module}, provides the module class name, the parent module {@link
  * ModuleSource source}, and the call stack that ends just before the module {@link
  * Module#configure(Binder) configure(Binder)} method invocation.  
  */
 final class ModuleSource {
-   
+
   /**
-   * The class name of module that this {@link ModuleSource} associated to. This value is null when
-   * the module class {@link Class#getName() getName()} returns null.
+   * The class name of module that this {@link ModuleSource} associated to.
    */
   private final String moduleClassName;
   
@@ -27,9 +29,9 @@
    * The chunk of call stack that starts from the parent module {@link Module#configure(Binder) 
    * configure(Binder)} call and ends just before the module {@link Module#configure(Binder) 
    * configure(Binder)} method invocation. For a module without a parent module the chunk starts 
-   * from the bottom of call stack.
+   * from the bottom of call stack. The array is non-empty if stack trace collection is on.
    */
-  private final InMemoryStackTraceElement[] partialCallStack;  
+  private final InMemoryStackTraceElement[] partialCallStack;
 
   /**
    * Creates a new {@link ModuleSource} with a {@literal null} parent.
@@ -60,7 +62,7 @@
   }
   
   /** 
-   * Returns the corresponding module class name. The value can be null.
+   * Returns the corresponding module class name.
    *
    * @see Class#getName()
    */
@@ -69,16 +71,17 @@
   }
 
   /**
-   * Returns the chunk of call stack that starts from the parent module {@link 
-   * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link 
-   * Module#configure(Binder) configure(Binder)} method invocation
+   * Returns the chunk of call stack that starts from the parent module {@link
+   * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link
+   * Module#configure(Binder) configure(Binder)} method invocation. The return array is non-empty
+   * only if stack trace collection is on.
    */
   StackTraceElement[] getPartialCallStack() {
     return StackTraceElements.convertToStackTraceElement(partialCallStack);
   }
   
   /**
-   * Returns the size of partial call stack.
+   * Returns the size of partial call stack if stack trace collection is on otherwise zero.
    */
   int getPartialCallStackSize() {
     return partialCallStack.length;
@@ -101,7 +104,23 @@
   ModuleSource getParent() {
     return parent;
   }
-  
+
+  /**
+   * Returns the class names of modules in this module source. The first element (index 0) is filled
+   * by this object {@link #getModuleClassName()}. The second element is filled by the parent's 
+   * {@link #getModuleClassName()} and so on.
+   */
+  List<String> getModuleClassNames() {
+    ImmutableList.Builder<String> classNames = ImmutableList.builder();
+    ModuleSource current = this;
+    while (current != null) {
+      String className = current.moduleClassName;
+      classNames.add(className);
+      current = current.parent;
+    }
+    return classNames.build();
+  }
+
   /**
    * Returns the size of {@link ModuleSource ModuleSources} chain (all parents) that ends at this 
    * object.
@@ -115,22 +134,24 @@
   
   /**
    * Returns the size of call stack that ends just before the module {@link Module#configure(Binder)
-   * configure(Binder)} method invocation (see {@link #getStackTrace()}). 
+   * configure(Binder)} method invocation (see {@link #getStackTrace()}).
    */
   int getStackTraceSize() {
     if (parent == null) {
       return partialCallStack.length;
     }
-    return parent.getStackTraceSize() + partialCallStack.length;  
-  }  
-  
+    return parent.getStackTraceSize() + partialCallStack.length;
+  }
+
   /**
-   * Returns the full call stack that ends just before the module {@link Module#configure(Binder) 
-   * configure(Binder)} method invocation. 
+   * Returns the full call stack that ends just before the module {@link Module#configure(Binder)
+   * configure(Binder)} method invocation. The return array is non-empty if stack trace collection
+   * on.
    */
   StackTraceElement[] getStackTrace() {
-    StackTraceElement[] callStack = new StackTraceElement[getStackTraceSize()];
-    int cursor = 0; // Index 0 stores the top Module.configure() 
+    int stackTraceSize = getStackTraceSize();
+    StackTraceElement[] callStack = new StackTraceElement[stackTraceSize];
+    int cursor = 0;
     ModuleSource current = this;
     while (current != null) {
       StackTraceElement[] chunk = 
diff --git a/core/test/com/google/inject/Asserts.java b/core/test/com/google/inject/Asserts.java
index cd56c3a..d46a1ff 100644
--- a/core/test/com/google/inject/Asserts.java
+++ b/core/test/com/google/inject/Asserts.java
@@ -17,6 +17,8 @@
 
 package com.google.inject;
 
+import static com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
+import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
@@ -56,6 +58,33 @@
   }
 
   /**
+   * Returns the source file appears in error messages based on {@link 
+   * #getIncludeStackTraceOption()} value.
+   */
+  public static String getDeclaringSourcePart(Class clazz) {
+    if (getIncludeStackTraceOption() == IncludeStackTraceOption.OFF) {
+      return ".configure(Unknown Source";
+    }
+    return ".configure(" + clazz.getSimpleName() + ".java:";
+  }
+
+  /**
+   * Returns true if {@link #getIncludeStackTraceOption()} returns {@link
+   * IncludeStackTraceOption#OFF}.
+   */
+  public static boolean isIncludeStackTraceOff() {
+    return getIncludeStackTraceOption() == IncludeStackTraceOption.OFF;
+  }
+
+  /**
+   * Returns true if {@link #getIncludeStackTraceOption()} returns {@link
+   * IncludeStackTraceOption#COMPLETE}.
+   */
+  public static boolean isIncludeStackTraceComplete() {
+    return getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE;
+  }
+
+  /**
    * Fails unless {@code expected.equals(actual)}, {@code
    * actual.equals(expected)} and their hash codes are equal. This is useful
    * for testing the equals method itself.
diff --git a/core/test/com/google/inject/BinderTest.java b/core/test/com/google/inject/BinderTest.java
index 5b9f55d..5875e37 100644
--- a/core/test/com/google/inject/BinderTest.java
+++ b/core/test/com/google/inject/BinderTest.java
@@ -19,6 +19,8 @@
 import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
 import static com.google.inject.Asserts.assertNotSerializable;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
+import static com.google.inject.Asserts.isIncludeStackTraceOff;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -99,16 +101,27 @@
       });
     } catch (CreationException e) {
       assertEquals(4, e.getErrorMessages().size());
-      assertContains(e.getMessage(),
-          "1) No implementation for java.lang.Runnable was bound.",
-          "at " + getClass().getName(),
-          "2) No implementation for " + Comparator.class.getName() + " was bound.",
-          "at " + getClass().getName(),
-          "3) No implementation for java.util.concurrent.Callable<java.lang.String> was bound.",
-          "at " + getClass().getName(),
-          "4) No implementation for java.util.Date annotated with @"
-              + Named.class.getName() + "(value=date) was bound.",
-          "at " + getClass().getName());
+      String segment1 = "No implementation for " + Comparator.class.getName() + " was bound.";
+      String segment2 = "No implementation for java.util.Date annotated with @"
+          + Named.class.getName() + "(value=date) was bound.";
+      String segment3 = "No implementation for java.lang.Runnable was bound.";
+      String segment4 = " No implementation for java.util.concurrent.Callable<java.lang.String> was"
+          + " bound.";
+      String atSegment = "at " + getClass().getName();
+      String sourceFileName = getDeclaringSourcePart(getClass());
+      if (isIncludeStackTraceOff()) {
+        assertContains(e.getMessage(),
+            segment1, atSegment, sourceFileName,
+            segment2, atSegment, sourceFileName,
+            segment3, atSegment, sourceFileName,
+            segment4, atSegment, sourceFileName);
+      } else {
+        assertContains(e.getMessage(),
+            segment3, atSegment, sourceFileName,
+            segment1, atSegment, sourceFileName,
+            segment4, atSegment, sourceFileName,
+            segment2, atSegment, sourceFileName);
+      } 
     }
   }
 
@@ -125,7 +138,7 @@
       assertContains(e.getMessage(),
           "No implementation for java.lang.Runnable was bound.",
           "for field at " + NeedsRunnable.class.getName(), ".runnable(BinderTest.java:",
-          "at " + getClass().getName(), ".configure(BinderTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -159,7 +172,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Binding points to itself.",
-          "at " + getClass().getName(), ".configure(BinderTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -263,7 +276,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) A binding to java.lang.String[] was already configured at " + getClass().getName(),
-          "at " + getClass().getName(), ".configure(BinderTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
       assertContains(expected.getMessage(), "1 error");
     }
     
@@ -317,7 +330,7 @@
       assertContains(expected.getMessage(),
         "1) A binding to java.lang.String was already configured at " + ConstantModule.class.getName(),
         asModuleChain(ParentModule.class, FooModule.class, ConstantModule.class),
-        "at " + ConstantModule.class.getName(), ".configure(BinderTest.java:",
+        "at " + ConstantModule.class.getName(), getDeclaringSourcePart(getClass()),
         asModuleChain(ParentModule.class, BarModule.class, ConstantModule.class));
       assertContains(expected.getMessage(), "1 error");
     }
@@ -341,7 +354,7 @@
       assertContains(expected.getMessage(),
         "1) A binding to " + HasImplementedBy1.class.getName()
         + " was already configured at " + getClass().getName(),
-        "at " + getClass().getName(), ".configure(BinderTest.java:");
+        "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
       assertContains(expected.getMessage(), "1 error");
     }
   }
@@ -459,10 +472,10 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Binding to Provider is not allowed.",
-          "at " + BinderTest.class.getName(), "configure(BinderTest.java:");
+          "at " + BinderTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
-  
+
   static class OuterCoreModule extends AbstractModule {
     @Override protected void configure() {
       install(new InnerCoreModule());
@@ -493,47 +506,47 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "Binding to core guice framework type is not allowed: AbstractModule.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Binder.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Binding.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Injector.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Key.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Module.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to Provider is not allowed.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Scope.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Stage.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: TypeLiteral.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
           
           "Binding to core guice framework type is not allowed: Key.",
-          "at " + InnerCoreModule.class.getName() + ".configure(BinderTest.java:",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterCoreModule.class, InnerCoreModule.class));
     }
   }
diff --git a/core/test/com/google/inject/BindingAnnotationTest.java b/core/test/com/google/inject/BindingAnnotationTest.java
index 9827d3e..b119953 100644
--- a/core/test/com/google/inject/BindingAnnotationTest.java
+++ b/core/test/com/google/inject/BindingAnnotationTest.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import junit.framework.TestCase;
@@ -57,7 +58,8 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(), "No implementation for java.lang.String annotated with",
           "BindingAnnotationTest$Blue(value=5) was bound",
-          "at " + BindingAnnotationTest.class.getName(), ".configure(BindingAnnotationTest.java:");
+          "at " + BindingAnnotationTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
   
@@ -86,7 +88,8 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(), "No implementation for java.lang.String annotated with",
           "BindingAnnotationTest$Color",
-          "at " + BindingAnnotationTest.class.getName(), ".configure(BindingAnnotationTest.java:");
+          "at " + BindingAnnotationTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -104,7 +107,8 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(), "No implementation for java.lang.String annotated with",
           "BindingAnnotationTest$Blue(value=5) was bound",
-          "at " + BindingAnnotationTest.class.getName(), ".configure(BindingAnnotationTest.java:");
+          "at " + BindingAnnotationTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/DuplicateBindingsTest.java b/core/test/com/google/inject/DuplicateBindingsTest.java
index eb9f503..27a2fd1 100644
--- a/core/test/com/google/inject/DuplicateBindingsTest.java
+++ b/core/test/com/google/inject/DuplicateBindingsTest.java
@@ -16,7 +16,7 @@
 
 package com.google.inject;
 
-import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.*;
 import static com.google.inject.name.Names.named;
 
 import com.google.common.base.Objects;
@@ -85,7 +85,8 @@
       fail("should have failed");
     } catch(CreationException ce) {
       assertContains(ce.getMessage(),
-          "A binding to " + Foo.class.getName() + " was already configured at " + FailingProviderModule.class.getName(),
+          "A binding to " + Foo.class.getName() + " was already configured " +
+          "at " + FailingProviderModule.class.getName(),
           "at " + FailingProviderModule.class.getName()
           );
     }
@@ -123,7 +124,7 @@
         new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo)
     );
   }
-  
+
   public void testMixedScopeFails() {
     try {
       Guice.createInjector(
@@ -132,20 +133,25 @@
       );
       fail("expected exception");
     } catch(CreationException ce) {
-      assertContains(ce.getMessage(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("pInstance") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("pKey") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("linkedKey") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + FooImpl.class.getName() + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("constructor") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName());
+      String segment1 = "A binding to " + Foo.class.getName() + " annotated with "
+          + named("pInstance") + " was already configured at " + SimpleModule.class.getName();
+      String segment2 = "A binding to " + Foo.class.getName() + " annotated with " + named("pKey")
+          + " was already configured at " + SimpleModule.class.getName();
+      String segment3 = "A binding to " + Foo.class.getName() + " annotated with " 
+          + named("constructor") + " was already configured at " + SimpleModule.class.getName();
+      String segment4 = "A binding to " + FooImpl.class.getName() + " was already configured at "
+          + SimpleModule.class.getName();
+      String atSegment = "at " + ScopedModule.class.getName();
+      if (isIncludeStackTraceOff()) {
+        assertContains(ce.getMessage(), segment1 , atSegment, segment2, atSegment, segment3,
+            atSegment, segment4, atSegment);
+      } else {
+        assertContains(ce.getMessage(), segment1 , atSegment, segment2, atSegment, segment4,
+            atSegment, segment3, atSegment);
+      }
     }
   }
-  
+
   @SuppressWarnings("unchecked")
   public void testMixedTargetsFails() {
     try {
diff --git a/core/test/com/google/inject/NullableInjectionPointTest.java b/core/test/com/google/inject/NullableInjectionPointTest.java
index 10eaefd..9cf22cc 100644
--- a/core/test/com/google/inject/NullableInjectionPointTest.java
+++ b/core/test/com/google/inject/NullableInjectionPointTest.java
@@ -1,6 +1,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 
 import junit.framework.TestCase;
 
@@ -122,7 +123,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "Binding to null instances is not allowed.",
-          "at " + getClass().getName(), ".configure(NullableInjectionPointTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/ParentInjectorTest.java b/core/test/com/google/inject/ParentInjectorTest.java
index 4ea47ed..4940b77 100644
--- a/core/test/com/google/inject/ParentInjectorTest.java
+++ b/core/test/com/google/inject/ParentInjectorTest.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -44,8 +45,8 @@
       fail("Created the same explicit binding on both parent and child");
     } catch (CreationException e) {
       assertContains(e.getMessage(), "A binding to ", A.class.getName(), " was already configured",
-          " at ", getClass().getName(), ".configure(ParentInjectorTest.java:",
-          " at ", getClass().getName(), ".configure(ParentInjectorTest.java:");
+          " at ", getClass().getName(), getDeclaringSourcePart(getClass()),
+          " at ", getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/PrivateModuleTest.java b/core/test/com/google/inject/PrivateModuleTest.java
index 7477f5a..05db62b 100644
--- a/core/test/com/google/inject/PrivateModuleTest.java
+++ b/core/test/com/google/inject/PrivateModuleTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.name.Names.named;
 
 import com.google.common.collect.ImmutableSet;
@@ -121,7 +122,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(), "Cannot expose java.lang.String on a standard binder. ",
           "Exposed bindings are only applicable to private binders.",
-          " at " + PrivateModuleTest.class.getName(), "configure(PrivateModuleTest.java:");
+          " at " + PrivateModuleTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -195,8 +196,8 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "A binding to java.lang.String was already configured at ",
-          getClass().getName(), ".configure(PrivateModuleTest.java:",
-          " at " + getClass().getName(), ".configure(PrivateModuleTest.java:");
+          getClass().getName(), getDeclaringSourcePart(getClass()),
+          " at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -218,7 +219,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "Could not expose() " + AB.class.getName() + ", it must be explicitly bound",
-          ".configure(PrivateModuleTest.java:");
+          getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -244,7 +245,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) No implementation for " + C.class.getName() + " was bound.",
-          "at " + getClass().getName(), ".configure(PrivateModuleTest.java:",
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
           "2) No implementation for " + String.class.getName(), "Named(value=a) was bound.",
           "for field at " + AB.class.getName() + ".a(PrivateModuleTest.java:",
           "3) No implementation for " + String.class.getName(), "Named(value=b) was bound.",
@@ -450,7 +451,7 @@
     assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class, named("b"))),
         privateElements.getExposedKeys());
     assertContains(privateElements.getExposedSource(Key.get(String.class, named("b"))).toString(),
-        PrivateModuleTest.class.getName(), ".configure(PrivateModuleTest.java:");
+        PrivateModuleTest.class.getName(), getDeclaringSourcePart(getClass()));
     Injector privateInjector = privateElements.getInjector();
     assertEquals("private", privateInjector.getInstance(Key.get(String.class, Names.named("a"))));
   }
@@ -508,7 +509,7 @@
           "while locating " + PrivateFoo.class.getName());
     }
   }
-  
+
   private static class FailingModule extends AbstractModule {
     @Override protected void configure() {
       bind(Collection.class).to(List.class);
diff --git a/core/test/com/google/inject/ProvisionExceptionTest.java b/core/test/com/google/inject/ProvisionExceptionTest.java
index aa4bfd5..e96a0f3 100644
--- a/core/test/com/google/inject/ProvisionExceptionTest.java
+++ b/core/test/com/google/inject/ProvisionExceptionTest.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.Asserts.reserialize;
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
@@ -94,7 +95,8 @@
       assertTrue(e.getCause() instanceof UnsupportedOperationException);
       assertContains(e.getMessage(),
           "1) Error in custom provider, java.lang.UnsupportedOperationException",
-          "at " + ProvisionExceptionTest.class.getName(), ".configure(ProvisionExceptionTest.java");
+          "at " + ProvisionExceptionTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -136,7 +138,8 @@
       fail();
     } catch (ProvisionException e) {
       assertContains(e.getMessage(), "1) User Exception",
-          "at " + ProvisionExceptionTest.class.getName(), ".configure(ProvisionExceptionTest.java");
+          "at " + ProvisionExceptionTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/RequestInjectionTest.java b/core/test/com/google/inject/RequestInjectionTest.java
index 9d3072f..dfd0f77 100644
--- a/core/test/com/google/inject/RequestInjectionTest.java
+++ b/core/test/com/google/inject/RequestInjectionTest.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.inject.matcher.Matchers;
@@ -128,7 +129,7 @@
       assertContains(expected.getMessage(),
           "1) Error in custom provider, java.lang.UnsupportedOperationException",
           "for field at " + NeedsRunnable.class.getName() + ".runnable(RequestInjectionTest.java:",
-          "at " + getClass().getName(), ".configure(RequestInjectionTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/ScopesTest.java b/core/test/com/google/inject/ScopesTest.java
index 9940d53..d95c797 100644
--- a/core/test/com/google/inject/ScopesTest.java
+++ b/core/test/com/google/inject/ScopesTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.name.Names.named;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -201,7 +202,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) No scope is bound to " + CustomScoped.class.getName(),
-          "at " + getClass().getName(), ".configure(ScopesTest.java:",
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
           "2) No scope is bound to " + CustomScoped.class.getName(),
           "at " + C.class.getName() + ".class");
     }
@@ -278,7 +279,7 @@
       assertContains(expected.getMessage(),
           "1) Please annotate " + NotRuntimeRetainedScoped.class.getName()
               + " with @Retention(RUNTIME).",
-          "at " + InnerRuntimeModule.class.getName() + ".configure(ScopesTest.java:",
+          "at " + InnerRuntimeModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterRuntimeModule.class, InnerRuntimeModule.class));
     }
   }
@@ -300,7 +301,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Please annotate " + Deprecated.class.getName() + " with @ScopeAnnotation.",
-          "at " + InnerDeprecatedModule.class.getName() + ".configure(ScopesTest.java:",
+          "at " + InnerDeprecatedModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterDeprecatedModule.class, InnerDeprecatedModule.class));
     }
   }
@@ -329,10 +330,10 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Scope Scopes.NO_SCOPE is already bound to " + CustomScoped.class.getName()
-              + " at " + CustomNoScopeModule.class.getName() + ".configure(ScopesTest.java:",
+              + " at " + CustomNoScopeModule.class.getName() + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterScopeModule.class, CustomNoScopeModule.class),
           "Cannot bind Scopes.SINGLETON.",
-          "at " + ScopesTest.class.getName(), ".configure(ScopesTest.java:",
+          "at " + ScopesTest.class.getName(), getDeclaringSourcePart(getClass()),
           asModuleChain(OuterScopeModule.class, CustomSingletonModule.class));
     }
   }
diff --git a/core/test/com/google/inject/TypeConversionTest.java b/core/test/com/google/inject/TypeConversionTest.java
index de1d462..e318477 100644
--- a/core/test/com/google/inject/TypeConversionTest.java
+++ b/core/test/com/google/inject/TypeConversionTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.common.collect.Iterables;
@@ -178,7 +179,7 @@
     } catch (ConfigurationException expected) {
       assertContains(expected.getMessage(),
           "Error converting 'invalid' (bound at " + InnerErrorModule.class.getName()
-              + ".configure(" + TypeConversionTest.class.getSimpleName() + ".java:",
+              + getDeclaringSourcePart(getClass()),
           asModuleChain(OuterErrorModule.class, InnerErrorModule.class),
           "using TypeConverter<Integer> which matches identicalTo(class java.lang.Integer)"
               + " (bound at [unknown source]).",
@@ -269,15 +270,16 @@
     assertTrue(injector.getTypeConverterBindings().contains(converterBinding));
   }
 
-  public void testInvalidCustomValue() throws CreationException {
-    Module module = new AbstractModule() {
-      @Override protected void configure() {
-        convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), failingTypeConverter());
-        bindConstant().annotatedWith(NumericValue.class).to("invalid");
-        bind(DateHolder.class);
-      }
-    };
+  static class InvalidCustomValueModule extends AbstractModule {
+    @Override protected void configure() {
+      convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), failingTypeConverter());
+      bindConstant().annotatedWith(NumericValue.class).to("invalid");
+      bind(DateHolder.class);
+    }
+  }
 
+  public void testInvalidCustomValue() throws CreationException {
+    Module module = new InvalidCustomValueModule();
     try {
       Guice.createInjector(module);
       fail();
@@ -286,9 +288,9 @@
       assertTrue(cause instanceof UnsupportedOperationException);
       assertContains(expected.getMessage(),
           "1) Error converting 'invalid' (bound at ", getClass().getName(),
-          ".configure(TypeConversionTest.java:", "to java.util.Date",
+          getDeclaringSourcePart(getClass()), "to java.util.Date",
           "using BrokenConverter which matches only(java.util.Date) ",
-          "(bound at " + getClass().getName(), ".configure(TypeConversionTest.java:",
+          "(bound at " + getClass().getName(), getDeclaringSourcePart(getClass()),
           "Reason: java.lang.UnsupportedOperationException: Cannot convert",
           "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:");
     }
@@ -332,12 +334,12 @@
       assertContains(expected.getMessage(),
           "1) Received null converting 'foo' (bound at ",
           getClass().getName(),
-          ".configure(TypeConversionTest.java:",
+          getDeclaringSourcePart(getClass()),
           asModuleChain(OuterModule.class, InnerModule.class),
           "to java.util.Date",
           "using CustomConverter which matches only(java.util.Date) ",
           "(bound at " + getClass().getName(),
-          ".configure(TypeConversionTest.java:",
+          getDeclaringSourcePart(getClass()),
           asModuleChain(OuterModule.class, InnerModule.class, ConverterNullModule.class),
           "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:",
           asModuleChain(OuterModule.class, InnerModule.class));
@@ -358,12 +360,12 @@
       assertContains(expected.getMessage(),
           "1) Type mismatch converting 'foo' (bound at ",
           getClass().getName(),
-          ".configure(TypeConversionTest.java:",
+          getDeclaringSourcePart(getClass()),
           asModuleChain(OuterModule.class, InnerModule.class),
           "to java.util.Date",
           "using CustomConverter which matches only(java.util.Date) ",
           "(bound at " + getClass().getName(),
-          ".configure(TypeConversionTest.java:",
+          getDeclaringSourcePart(getClass()),
           asModuleChain(OuterModule.class, InnerModule.class, ConverterCustomModule.class),
           "Converter returned -1.",
           "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:",
@@ -429,18 +431,18 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Multiple converters can convert 'foo' (bound at ", getClass().getName(),
-          ".configure(TypeConversionTest.java:",
+          getDeclaringSourcePart(getClass()),
           asModuleChain(OuterAmbiguousModule.class, InnerAmbiguousModule.class),
           "to java.util.Date:",
           "CustomConverter which matches only(java.util.Date) (bound at "
               + Ambiguous1Module.class.getName()
-              + ".configure(" + TypeConversionTest.class.getSimpleName() + ".java:",
+              + getDeclaringSourcePart(getClass()),
           asModuleChain(
               OuterAmbiguousModule.class, InnerAmbiguousModule.class, Ambiguous1Module.class),
           "and",
           "CustomConverter which matches only(java.util.Date) (bound at "
               + Ambiguous2Module.class.getName()
-              + ".configure(" + TypeConversionTest.class.getSimpleName() + ".java:",
+              + getDeclaringSourcePart(getClass()),
           asModuleChain(
               OuterAmbiguousModule.class, InnerAmbiguousModule.class, Ambiguous2Module.class),
           "Please adjust your type converter configuration to avoid overlapping matches.",
@@ -460,7 +462,7 @@
     };
   }
 
-  private TypeConverter failingTypeConverter() {
+  private static TypeConverter failingTypeConverter() {
     return new TypeConverter() {
       public Object convert(String value, TypeLiteral<?> toType) {
         throw new UnsupportedOperationException("Cannot convert");
diff --git a/core/test/com/google/inject/TypeListenerTest.java b/core/test/com/google/inject/TypeListenerTest.java
index ee0b6e2..e4b5f63 100644
--- a/core/test/com/google/inject/TypeListenerTest.java
+++ b/core/test/com/google/inject/TypeListenerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.matcher.Matchers.any;
 import static com.google.inject.matcher.Matchers.only;
 import static com.google.inject.name.Names.named;
@@ -194,12 +195,12 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
           asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
           "of " + B.class.getName(), 
           "Reason: java.lang.ClassCastException: whoops, failure #1",
           "2) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
           asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
           "of " + C.class.getName(),
           "Reason: java.lang.ClassCastException: whoops, failure #2");
@@ -216,7 +217,7 @@
     } catch (ConfigurationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
           "of " + B.class.getName(),
           "Reason: java.lang.ClassCastException: whoops, failure #3");
     }
@@ -228,7 +229,7 @@
     } catch (ConfigurationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
           "of " + B.class.getName(),
           "Reason: java.lang.ClassCastException: whoops, failure #3");
     }
@@ -295,7 +296,7 @@
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at ",
-          TypeListenerTest.class.getName(), ".configure(TypeListenerTest.java:",
+          TypeListenerTest.class.getName(), getDeclaringSourcePart(getClass()),
           "of " + A.class.getName(),
           " Reason: java.lang.ClassCastException: whoops, failure #1");
     }
diff --git a/core/test/com/google/inject/internal/util/LineNumbersTest.java b/core/test/com/google/inject/internal/util/LineNumbersTest.java
index 68a2b26..da2824a 100644
--- a/core/test/com/google/inject/internal/util/LineNumbersTest.java
+++ b/core/test/com/google/inject/internal/util/LineNumbersTest.java
@@ -17,6 +17,7 @@
 package com.google.inject.internal.util;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.CreationException;
@@ -46,7 +47,7 @@
       assertContains(expected.getMessage(),
           "1) No implementation for " + B.class.getName() + " was bound.",
           "for parameter 0 at " + A.class.getName() + ".<init>(LineNumbersTest.java:",
-          "at " + LineNumbersTest.class.getName(), ".configure(LineNumbersTest.java:");
+          "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -75,7 +76,7 @@
       assertContains(expected.getMessage(),
           "1) No implementation for " + B.class.getName() + " was bound.",
           "for parameter 0 at " + A.class.getName() + ".<init>(LineNumbersTest.java:",
-          "at " + LineNumbersTest.class.getName(), ".configure(LineNumbersTest.java:");
+          "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -125,7 +126,7 @@
       assertContains(expected.getMessage(),
           "1) No implementation for " + B.class.getName() + " was bound.",
           "for parameter 0 at " + GeneratingClassLoader.name + ".<init>(Unknown Source)",
-          "at " + LineNumbersTest.class.getName(), ".configure(LineNumbersTest.java:");
+          "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
   
diff --git a/core/test/com/google/inject/spi/ElementSourceTest.java b/core/test/com/google/inject/spi/ElementSourceTest.java
index 5c63f4a..93fbeac 100644
--- a/core/test/com/google/inject/spi/ElementSourceTest.java
+++ b/core/test/com/google/inject/spi/ElementSourceTest.java
@@ -1,5 +1,6 @@
 package com.google.inject.spi;
 
+import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.inject.AbstractModule;
@@ -21,15 +22,10 @@
  */
 public class ElementSourceTest extends TestCase {
 
-  private static final StackTraceElement BINDER_INSTALL =
-      new StackTraceElement("com.google.inject.spi.Elements$RecordingBinder", "install",
+  private static final StackTraceElement BINDER_INSTALL = 
+      new StackTraceElement("com.google.inject.spi.Elements$RecordingBinder", "install", 
           "Unknown Source", 234 /* line number*/);
-
-  @Override
-  protected void tearDown() throws Exception {
-    System.clearProperty("guice_include_stack_traces");
-  }
-
+  
   public void testCallStackSize() {
     ModuleSource moduleSource = createModuleSource();
     StackTraceElement[] bindingCallStack = new StackTraceElement[3];
@@ -42,10 +38,9 @@
     ElementSource elementSource = new ElementSource(
         null /* No original element source */, "" /* Don't care */, moduleSource, bindingCallStack);
     assertEquals(10 /* call stack size */, elementSource.getStackTrace().length);
-  }
+  }  
 
-  public void testGetCallStack_IntegrationTest_Default() throws Exception {
-    System.setProperty("guice_include_stack_traces", "DEFAULT");
+  public void testGetCallStack_IntegrationTest() throws Exception {
     List<Element> elements = Elements.getElements(new A());
     for (Element element : elements) {
       if (element instanceof Binding) {
@@ -53,105 +48,95 @@
         Class<? extends Annotation> annotationType = binding.getKey().getAnnotationType();
         if (annotationType != null && annotationType.equals(SampleAnnotation.class)) {
           ElementSource elementSource = (ElementSource) binding.getSource();
-          // Check module stack
           List<String> moduleClassNames = elementSource.getModuleClassNames();
+          // Check module class names
           // Module C
           assertEquals("com.google.inject.spi.ElementSourceTest$C", moduleClassNames.get(0));
           // Module B
           assertEquals("com.google.inject.spi.ElementSourceTest$B", moduleClassNames.get(1));
           // Module A
           assertEquals("com.google.inject.spi.ElementSourceTest$A", moduleClassNames.get(2));
-          // Check call stack
           StackTraceElement[] callStack = elementSource.getStackTrace();
-          assertEquals(0, elementSource.getStackTrace().length);
-          return;
+          switch(getIncludeStackTraceOption()) {
+            case OFF:
+              // Check declaring source
+              StackTraceElement stackTraceElement = 
+                  (StackTraceElement) elementSource.getDeclaringSource();
+              assertEquals(new StackTraceElement(
+                  "com.google.inject.spi.ElementSourceTest$C", "configure", null, -1), 
+                  stackTraceElement);
+              // Check call stack
+              assertEquals(0, callStack.length);
+              return;
+            case ONLY_FOR_DECLARING_SOURCE:
+                // Check call stack
+                assertEquals(0, callStack.length);
+                return;
+            case COMPLETE:
+              // Check call stack
+              int skippedCallStackSize = new Throwable().getStackTrace().length - 1;
+              assertEquals(skippedCallStackSize + 15, elementSource.getStackTrace().length);
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[0].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[1].getClassName());
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[2].getClassName());
+              // Module C
+              assertEquals("com.google.inject.spi.ElementSourceTest$C",
+                  callStack[3].getClassName());
+              assertEquals("configure",
+                  callStack[3].getMethodName());
+              assertEquals("Unknown Source",
+                  callStack[3].getFileName());
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[4].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[5].getClassName());
+              // Module B
+              assertEquals("com.google.inject.spi.ElementSourceTest$B",
+                  callStack[6].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[7].getClassName());
+              // Module A
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[8].getClassName());
+              assertEquals("com.google.inject.spi.ElementSourceTest$A",
+                  callStack[9].getClassName());
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[10].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[11].getClassName());
+              assertEquals("com.google.inject.spi.Elements",
+                  callStack[12].getClassName());
+              assertEquals("com.google.inject.spi.Elements",
+                  callStack[13].getClassName());
+              assertEquals("com.google.inject.spi.ElementSourceTest",
+                  callStack[14].getClassName());
+              // Check modules index
+              List<Integer> indexes = elementSource.getModuleConfigurePositionsInStackTrace();
+              assertEquals((int) indexes.get(0), 4);
+              assertEquals((int) indexes.get(1), 6);
+              assertEquals((int) indexes.get(2), 10);
+              return;
+          }
         }
       }
     }
     fail("The test should not reach this line.");
-  }
-
-  public void testGetCallStack_IntegrationTest_Complete() throws Exception {
-    System.setProperty("guice_include_stack_traces", "COMPLETE");
-    List<Element> elements = Elements.getElements(new A());
-    for (Element element : elements) {
-      if (element instanceof Binding) {
-        Binding<?> binding = (Binding<?>) element;
-        Class<? extends Annotation> annotationType = binding.getKey().getAnnotationType();
-        if (annotationType != null && annotationType.equals(SampleAnnotation.class)) {
-          ElementSource elementSource = (ElementSource) binding.getSource();
-          // Check module stack
-          List<String> moduleClassNames = elementSource.getModuleClassNames();
-          // Module C
-          assertEquals("com.google.inject.spi.ElementSourceTest$C", moduleClassNames.get(0));
-          // Module B
-          assertEquals("com.google.inject.spi.ElementSourceTest$B", moduleClassNames.get(1));
-          // Module A
-          assertEquals("com.google.inject.spi.ElementSourceTest$A", moduleClassNames.get(2));
-          // Check call stack
-          StackTraceElement[] callStack = elementSource.getStackTrace();
-          int skippedCallStackSize = new Throwable().getStackTrace().length - 1;
-          assertEquals(skippedCallStackSize + 15, elementSource.getStackTrace().length);
-          assertEquals("com.google.inject.spi.Elements$RecordingBinder",
-              callStack[0].getClassName());
-          assertEquals("com.google.inject.spi.Elements$RecordingBinder",
-              callStack[1].getClassName());
-          assertEquals("com.google.inject.AbstractModule",
-              callStack[2].getClassName());
-          // Module C
-          assertEquals("com.google.inject.spi.ElementSourceTest$C",
-              callStack[3].getClassName());
-          assertEquals("configure",
-              callStack[3].getMethodName());
-          assertEquals("Unknown Source",
-              callStack[3].getFileName());
-          assertEquals("com.google.inject.AbstractModule",
-              callStack[4].getClassName());
-          assertEquals("com.google.inject.spi.Elements$RecordingBinder",
-              callStack[5].getClassName());
-          // Module B
-          assertEquals("com.google.inject.spi.ElementSourceTest$B",
-              callStack[6].getClassName());
-          assertEquals("com.google.inject.spi.Elements$RecordingBinder",
-              callStack[7].getClassName());
-          // Module A
-          assertEquals("com.google.inject.AbstractModule",
-              callStack[8].getClassName());
-          assertEquals("com.google.inject.spi.ElementSourceTest$A",
-              callStack[9].getClassName());
-          assertEquals("com.google.inject.AbstractModule",
-              callStack[10].getClassName());
-          assertEquals("com.google.inject.spi.Elements$RecordingBinder",
-              callStack[11].getClassName());
-          assertEquals("com.google.inject.spi.Elements",
-              callStack[12].getClassName());
-          assertEquals("com.google.inject.spi.Elements",
-              callStack[13].getClassName());
-          assertEquals("com.google.inject.spi.ElementSourceTest",
-              callStack[14].getClassName());
-          // Check modules index
-          List<Integer> indexes = elementSource.getModuleConfigurePositionsInStackTrace();
-          assertEquals((int) indexes.get(0), 4);
-          assertEquals((int) indexes.get(1), 6);
-          assertEquals((int) indexes.get(2), 10);
-          return;
-        }
-      }
-    }
-    fail("The test should not reach this line.");
-  }
+  }  
 
   private ModuleSource createModuleSource() {
     // First module
     StackTraceElement[] partialCallStack = new StackTraceElement[1];
-    partialCallStack[0] = BINDER_INSTALL;
+    partialCallStack[0] = BINDER_INSTALL;    
     ModuleSource moduleSource = new ModuleSource(new A(), partialCallStack);
-    // Second module
+    // Second module 
     partialCallStack = new StackTraceElement[2];
     partialCallStack[0] = BINDER_INSTALL;
     partialCallStack[1] = new StackTraceElement(
         "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100);
-    moduleSource = moduleSource.createChild(new B(), partialCallStack);
+    moduleSource = moduleSource.createChild(new B(), partialCallStack);    
     // Third module
     partialCallStack = new StackTraceElement[4];
     partialCallStack[0] = BINDER_INSTALL;
@@ -168,14 +153,14 @@
       install(new B());
     }
   }
-
+  
   private static class B implements Module {
     @Override
     public void configure(Binder binder) {
       binder.install(new C());
     }
   }
-
+  
   @Retention(RUNTIME)
   @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
   @BindingAnnotation
@@ -186,5 +171,5 @@
     public void configure() {
       bind(String.class).annotatedWith(SampleAnnotation.class).toInstance("the value");
     }
-  }
+  }  
 }
diff --git a/core/test/com/google/inject/spi/ElementsTest.java b/core/test/com/google/inject/spi/ElementsTest.java
index 2299e44..e2fcff9 100644
--- a/core/test/com/google/inject/spi/ElementsTest.java
+++ b/core/test/com/google/inject/spi/ElementsTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
+import static com.google.inject.Asserts.isIncludeStackTraceComplete;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.common.collect.ImmutableMap;
@@ -86,8 +88,9 @@
             assertEquals("Message A 5 C", command.getMessage());
             assertNull(command.getCause());
             assertContains(command.getSources().toString(),
-                ElementsTest.class.getName(), ".configure(ElementsTest.java:");
-            assertContains(command.getSource(), "ElementsTest.java");
+                ElementsTest.class.getName(),
+                getDeclaringSourcePart(ElementsTest.class));
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -107,7 +110,7 @@
             assertEquals("A", command.getCause().getMessage());
             assertEquals(command.getMessage(),
                 "An exception was caught and reported. Message: A");
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -596,7 +599,7 @@
             assertEquals("Setting the scope is not permitted when binding to a single instance.",
                 command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -913,8 +916,8 @@
             one.expose(Collection.class).annotatedWith(SampleAnnotation.class);
             one.bind(List.class).to(ArrayList.class);
 
-            PrivateBinder two = binder().withSource("1 ElementsTest.java")
-                .newPrivateBinder().withSource("2 ElementsTest.java");
+            PrivateBinder two = binder().withSource("1 FooBar")
+                .newPrivateBinder().withSource("2 FooBar");
             two.expose(String.class).annotatedWith(Names.named("a"));
             two.expose(b);
             two.bind(List.class).to(ArrayList.class);
@@ -936,14 +939,14 @@
           }
         },
 
-        new FailingElementVisitor() {
+        new ExternalFailureVisitor() {
           @Override public Void visit(PrivateElements two) {
             assertEquals(ab, two.getExposedKeys());
-            assertEquals("1 ElementsTest.java", two.getSource().toString());
+            assertEquals("1 FooBar", two.getSource().toString());
             checkElements(two.getElements(),
-                new FailingElementVisitor() {
+                new ExternalFailureVisitor() {
                   @Override public <T> Void visit(Binding<T> binding) {
-                    assertEquals("2 ElementsTest.java", binding.getSource().toString());
+                    assertEquals("2 FooBar", binding.getSource().toString());
                     assertEquals(Key.get(List.class), binding.getKey());
                     return null;
                   }
@@ -976,7 +979,7 @@
             assertEquals("More than one annotation is specified for this binding.",
                 command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1003,7 +1006,7 @@
           @Override public Void visit(Message command) {
             assertEquals("Implementation is set more than once.", command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1030,7 +1033,7 @@
           @Override public Void visit(Message command) {
             assertEquals("Scope is set more than once.", command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1058,7 +1061,7 @@
             assertEquals("More than one annotation is specified for this binding.",
                 command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1085,7 +1088,7 @@
           @Override public Void visit(Message message) {
             assertEquals("Constant value is set more than once.", message.getMessage());
             assertNull(message.getCause());
-            assertContains(message.getSource(), "ElementsTest.java");
+            assertContains(message.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1214,7 +1217,6 @@
     assertEquals(1, aConfigureCount.get());
   }
 
-
   /**
    * Ensures the module performs the commands consistent with {@code visitors}.
    */
@@ -1231,11 +1233,14 @@
       if (!(element instanceof Message)) {
           ElementSource source = (ElementSource) element.getSource();
           assertTrue(source.getModuleClassNames().size() > 0);
-          assertFalse(source.isStackTraceRetained()) ;
-          assertEquals(0, source.getStackTrace().length);
+          if (isIncludeStackTraceComplete()) {
+            assertTrue(source.getStackTrace().length > 0);
+          } else {
+            assertEquals(0, source.getStackTrace().length);
+          }
       }
       if (!(visitor instanceof ExternalFailureVisitor)) {
-        assertContains(element.getSource().toString(), "ElementsTest.java");
+        assertContains(element.getSource().toString(), getDeclaringSourcePart(ElementsTest.class));
       }
       element.acceptVisitor(visitor);
     }
diff --git a/core/test/com/google/inject/spi/SpiBindingsTest.java b/core/test/com/google/inject/spi/SpiBindingsTest.java
index ccf50a9..870c911 100644
--- a/core/test/com/google/inject/spi/SpiBindingsTest.java
+++ b/core/test/com/google/inject/spi/SpiBindingsTest.java
@@ -17,6 +17,8 @@
 package com.google.inject.spi;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
+import static com.google.inject.Asserts.isIncludeStackTraceComplete;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -407,11 +409,14 @@
   }
 
   public void checkBindingSource(Binding binding) {
-    assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+    assertContains(binding.getSource().toString(), getDeclaringSourcePart(getClass()));
     ElementSource source = (ElementSource) binding.getSource();
     assertTrue(source.getModuleClassNames().size() > 0);
-    assertFalse(source.isStackTraceRetained()) ;
-    assertEquals(0, source.getStackTrace().length);
+    if (isIncludeStackTraceComplete()) {
+      assertTrue(source.getStackTrace().length > 0);
+    } else {
+      assertEquals(0, source.getStackTrace().length);
+    }
   }
   
   public void checkInjector(Module module, ElementVisitor<?>... visitors) {
diff --git a/pom.xml b/pom.xml
index 9c9b94b..48863a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -273,6 +273,7 @@
           <version>2.5</version>
           <configuration>
             <redirectTestOutputToFile>true</redirectTestOutputToFile>
+            <!--<argLine>-Dguice_include_stack_traces=OFF</argLine>-->
           </configuration>
         </plugin>
         <!--