Reitveld: http://gwt-code-reviews.appspot.com/89810

Changes normal GWT vtable setup from

function foo() { ... }
_.foo = foo;


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7587 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index bcdf051..c7d07f5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -92,6 +92,7 @@
 import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder;
 import com.google.gwt.dev.js.EvalFunctionsAtTopScope;
 import com.google.gwt.dev.js.JsBreakUpLargeVarStatements;
+import com.google.gwt.dev.js.JsDuplicateFunctionRemover;
 import com.google.gwt.dev.js.JsIEBlockSizeVisitor;
 import com.google.gwt.dev.js.JsInliner;
 import com.google.gwt.dev.js.JsNormalizer;
@@ -106,6 +107,7 @@
 import com.google.gwt.dev.js.JsUnusedFunctionRemover;
 import com.google.gwt.dev.js.JsVerboseNamer;
 import com.google.gwt.dev.js.SizeBreakdown;
+import com.google.gwt.dev.js.ast.JsBlock;
 import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.util.AbstractTextOutput;
@@ -261,7 +263,7 @@
       // (7) Generate a JavaScript code DOM from the Java type declarations
       jprogram.typeOracle.recomputeAfterOptimizations();
       JavaToJavaScriptMap map = GenerateJavaScriptAST.exec(jprogram, jsProgram,
-          options.getOutput(), symbolTable);
+          options.getOutput(), symbolTable, propertyOracles);
 
       // (8) Normalize the JS AST.
       // Fix invalid constructs created during JS AST gen.
@@ -269,7 +271,7 @@
       // Resolve all unresolved JsNameRefs.
       JsSymbolResolver.exec(jsProgram);
       // Move all function definitions to a top-level scope, to reduce weirdness
-      EvalFunctionsAtTopScope.exec(jsProgram);
+      EvalFunctionsAtTopScope.exec(jsProgram, map);
 
       // (9) Optimize the JS AST.
       if (options.isAggressivelyOptimize()) {
@@ -315,6 +317,18 @@
         case OBFUSCATED:
           obfuscateMap = JsStringInterner.exec(jprogram, jsProgram);
           JsObfuscateNamer.exec(jsProgram);
+          if (JsStackEmulator.getStackMode(propertyOracles) ==
+              JsStackEmulator.StackMode.STRIP) {
+            boolean changed = false;
+            for (int i = 0; i < jsProgram.getFragmentCount(); i++) {
+              JsBlock fragment = jsProgram.getFragmentBlock(i);
+              changed = JsDuplicateFunctionRemover.exec(jsProgram, fragment)
+                        || changed;
+            }
+            if (changed) {
+              JsUnusedFunctionRemover.exec(jsProgram);
+            }
+          }
           break;
         case PRETTY:
           // We don't intern strings in pretty mode to improve readability
@@ -781,7 +795,7 @@
     // Also remember $entry, which we'll handle specially in GenerateJsAst
     JMethod registerEntry = program.getIndexedMethod("Impl.registerEntry");
     program.addEntryMethod(registerEntry);
-
+    
     for (String mainClassName : mainClassNames) {
       block.addStmt(makeStatsCalls(program, mainClassName));
       JDeclaredType mainType = program.getFromTypeMap(mainClassName);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 7bc960f..fcbfe3c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
 import com.google.gwt.dev.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.InternalCompilerException;
@@ -87,6 +88,7 @@
 import com.google.gwt.dev.jjs.ast.js.JsonArray;
 import com.google.gwt.dev.jjs.ast.js.JsonObject;
 import com.google.gwt.dev.jjs.ast.js.JsonObject.JsonPropInit;
+import com.google.gwt.dev.js.JsStackEmulator;
 import com.google.gwt.dev.js.ast.JsArrayAccess;
 import com.google.gwt.dev.js.ast.JsArrayLiteral;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
@@ -133,6 +135,7 @@
 import com.google.gwt.dev.js.ast.JsVars;
 import com.google.gwt.dev.js.ast.JsWhile;
 import com.google.gwt.dev.js.ast.JsVars.JsVar;
+import com.google.gwt.dev.util.collect.IdentityHashSet;
 import com.google.gwt.dev.util.collect.Maps;
 
 import java.util.ArrayList;
@@ -337,14 +340,21 @@
       }
 
       // my global name
-      JsName globalName;
+      JsName globalName = null;
       assert x.getEnclosingType() != null;
       String mangleName = mangleNameForGlobal(x);
-      globalName = topScope.declareName(mangleName, name);
-      x.getSourceInfo().addCorrelation(program.getCorrelator().by(globalName));
-      names.put(x, globalName);
-      recordSymbol(x, globalName);
 
+      /*
+       * Only allocate a name for a function if it is native, not polymorphic,
+       * or stack-stripping is disabled.
+       */
+      if (!stripStack || !polymorphicNames.containsKey(x) || x.isNative()) {
+        globalName = topScope.declareName(mangleName, name);
+        x.getSourceInfo().addCorrelation(
+            program.getCorrelator().by(globalName));
+        names.put(x, globalName);
+        recordSymbol(x, globalName);
+      }
       JsFunction jsFunction;
       if (x.isNative()) {
         // set the global name of the JSNI peer
@@ -355,7 +365,19 @@
         // create a new peer JsFunction
         SourceInfo sourceInfo = x.getSourceInfo().makeChild(
             CreateNamesAndScopesVisitor.class, "Translated JS function");
+        /*
+         * It would be more correct here to check for an inline assignment,
+         * such as var foo = function blah() {} and introduce a separate scope
+         * for the function's name according to EcmaScript-262, but this would
+         * mess up stack traces by allowing two inner scope function names to
+         * onfuscate to the same identifier, making function names no longer
+         * a 1:1 mapping to obfuscated symbols. Leaving them in global scope
+         * causes no harm. 
+         */
         jsFunction = new JsFunction(sourceInfo, topScope, globalName, true);
+        if (polymorphicNames.containsKey(x)) {
+          polymorphicJsFunctions.add(jsFunction);
+        }
       }
       methodBodyMap.put(x.getBody(), jsFunction);
       jsFunction.getSourceInfo().addCorrelation(
@@ -632,7 +654,8 @@
       // declare all methods into the global scope
       for (int i = 0; i < jsFuncs.size(); ++i) {
         JsFunction func = jsFuncs.get(i);
-        if (func != null) {
+        // don't add polymorphic JsFuncs, inline decl into vtable assignment
+        if (func != null && !polymorphicJsFunctions.contains(func)) {
           globalStmts.add(func.makeStmt());
         }
       }
@@ -1558,14 +1581,14 @@
       // Add it first, so that script-tag chunking in IFrameLinker works
       globalStatements.add(0, nullFunc.makeStmt());
     }
-
+    
     private void generateSeedFuncAndPrototype(JClassType x,
         List<JsStatement> globalStmts) {
       SourceInfo sourceInfo = x.getSourceInfo().makeChild(
           GenerateJavaScriptVisitor.class, "Seed and function prototype");
       if (x != program.getTypeJavaLangString()) {
         JsName seedFuncName = names.get(x);
-
+        
         // seed function
         // function com_example_foo_Foo() { }
         JsFunction seedFunc = new JsFunction(sourceInfo, topScope,
@@ -1705,7 +1728,11 @@
         if (!method.isStatic() && !method.isAbstract()) {
           JsNameRef lhs = polymorphicNames.get(method).makeRef(sourceInfo);
           lhs.setQualifier(globalTemp.makeRef(sourceInfo));
-          JsNameRef rhs = names.get(method).makeRef(sourceInfo);
+          /*
+           * Inline JsFunction rather than reference, e.g.
+           * _.vtableName = function functionName() { ... }
+           */
+          JsExpression rhs = methodBodyMap.get(method.getBody());
           JsExpression asg = createAssignment(lhs, rhs);
           JsExprStmt asgStat = new JsExprStmt(x.getSourceInfo(), asg);
           globalStmts.add(asgStat);
@@ -1889,9 +1916,10 @@
   }
 
   public static JavaToJavaScriptMap exec(JProgram program, JsProgram jsProgram,
-      JsOutputOption output, Map<StandardSymbolData, JsName> symbolTable) {
+      JsOutputOption output, Map<StandardSymbolData, JsName> symbolTable,
+      PropertyOracle[] propertyOracles) {
     GenerateJavaScriptAST generateJavaScriptAST = new GenerateJavaScriptAST(
-        program, jsProgram, output, symbolTable);
+        program, jsProgram, output, symbolTable, propertyOracles);
     return generateJavaScriptAST.execImpl();
   }
 
@@ -1932,9 +1960,9 @@
   private final JsScope objectScope;
   private final JsOutputOption output;
   private final Map<JMethod, JsName> polymorphicNames = new IdentityHashMap<JMethod, JsName>();
-
+  private final Set<JsFunction> polymorphicJsFunctions = new IdentityHashSet<JsFunction>();
   private final JProgram program;
-
+  
   /**
    * All of the fields and polymorphic methods in String.
    * 
@@ -1952,6 +1980,12 @@
   private final Set<JDeclaredType> specialObfuscatedTypes = new HashSet<JDeclaredType>();
 
   /**
+   * If true, polymorphic functions are made anonymous vtable declarations and
+   * not assigned topScope identifiers.
+   */
+  private boolean stripStack;
+  
+  /**
    * Maps JsNames to machine-usable identifiers.
    */
   private final Map<StandardSymbolData, JsName> symbolTable;
@@ -1968,7 +2002,8 @@
   private final Map<JsStatement, JMethod> vtableInitForMethodMap = new HashMap<JsStatement, JMethod>();
 
   private GenerateJavaScriptAST(JProgram program, JsProgram jsProgram,
-      JsOutputOption output, Map<StandardSymbolData, JsName> symbolTable) {
+      JsOutputOption output, Map<StandardSymbolData, JsName> symbolTable,
+      PropertyOracle[] propertyOracles) {
     this.program = program;
     typeOracle = program.typeOracle;
     this.jsProgram = jsProgram;
@@ -1978,6 +2013,8 @@
     this.output = output;
     this.symbolTable = symbolTable;
 
+    this.stripStack = JsStackEmulator.getStackMode(propertyOracles) == 
+        JsStackEmulator.StackMode.STRIP;
     /*
      * Because we modify String's prototype, all fields and polymorphic methods
      * on String's super types need special handling.
diff --git a/dev/core/src/com/google/gwt/dev/js/EvalFunctionsAtTopScope.java b/dev/core/src/com/google/gwt/dev/js/EvalFunctionsAtTopScope.java
index b22a69c..7352d05 100644
--- a/dev/core/src/com/google/gwt/dev/js/EvalFunctionsAtTopScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/EvalFunctionsAtTopScope.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.js;
 
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
 import com.google.gwt.dev.js.ast.JsBlock;
 import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsExpression;
@@ -23,6 +24,7 @@
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsProgramFragment;
 import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.js.ast.JsExprStmt;
 
 import java.util.HashSet;
 import java.util.ListIterator;
@@ -38,17 +40,33 @@
  */
 public class EvalFunctionsAtTopScope extends JsModVisitor {
 
-  public static void exec(JsProgram jsProgram) {
-    EvalFunctionsAtTopScope fev = new EvalFunctionsAtTopScope();
+
+  public static void exec(JsProgram jsProgram, JavaToJavaScriptMap map) {
+    EvalFunctionsAtTopScope fev = new EvalFunctionsAtTopScope(map);
     fev.accept(jsProgram);
   }
 
+  private JsStatement currentStatement;
+  
   private final Set<JsFunction> dontMove = new HashSet<JsFunction>();
 
   private final Stack<ListIterator<JsStatement>> itrStack = new Stack<ListIterator<JsStatement>>();
 
+  private JavaToJavaScriptMap java2jsMap;
+  
   private final Stack<JsBlock> scopeStack = new Stack<JsBlock>();
-
+  
+  
+  
+  public EvalFunctionsAtTopScope(JavaToJavaScriptMap java2jsMap) {
+    this.java2jsMap = java2jsMap;
+  }
+  
+  @Override
+  public void endVisit(JsExprStmt x, JsContext<JsStatement> ctx) {
+    currentStatement = null;     
+  }
+  
   @Override
   public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
     scopeStack.pop();
@@ -91,11 +109,22 @@
   }
 
   @Override
+  public boolean visit(JsExprStmt x, JsContext<JsStatement> ctx) {
+    currentStatement = x;
+    return true;
+  }
+
+  @Override
   public boolean visit(JsFunction x, JsContext<JsExpression> ctx) {
+    JsFunction func = JsStaticEval.isFunctionDecl(currentStatement);
+    
     /*
      * We do this during visit() to preserve first-to-last evaluation order.
+     * We check if this function is a vtable declaration and don't 
+     * move functions used in other expressions or are in vtable assignments.
      */
-    if (x.getName() != null && !dontMove.contains(x)) {
+    if (x.getName() != null && !dontMove.contains(x) && 
+        !isVtableDeclaration(currentStatement)) {
       /*
        * Reinsert this function into the statement immediately before the
        * current statement. The current statement will have already been
@@ -116,6 +145,7 @@
     return true;
   }
 
+
   @Override
   public boolean visit(JsProgram x, JsContext<JsProgram> ctx) {
     scopeStack.push(x.getGlobalBlock());
@@ -127,4 +157,8 @@
     scopeStack.push(x.getGlobalBlock());
     return true;
   }
+  
+  private boolean isVtableDeclaration(JsStatement currentStatement) {
+    return java2jsMap.vtableInitToMethod(currentStatement) != null;
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java b/dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
new file mode 100644
index 0000000..5e92127
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2009 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.dev.js;
+
+import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsModVisitor;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsVisitor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Replace references to functions which have post-obfuscation duplicate bodies
+ * by reference to a canonical one. Intended to run only when stack trace
+ * stripping is enabled.
+ */
+public class JsDuplicateFunctionRemover {
+
+  private class DuplicateFunctionBodyRecorder extends JsVisitor {
+
+    Map<String, JsName> uniqueBodies = new HashMap<String, JsName>();
+    Map<JsName, JsName> duplicateOriginalMap = new HashMap<JsName, JsName>();
+
+    public Map<JsName, JsName> getDuplicateMap() {
+      return duplicateOriginalMap;
+    }
+
+    @Override
+    public boolean visit(JsFunction x, JsContext<JsExpression> ctx) {
+      /*
+       * At this point, unpruned zero-arg functions with empty 
+       * bodies are Js constructor seed functions. 
+       * If constructors are ever inlined into seed functions, revisit this.
+       * Don't process anonymous functions.
+       */
+      if (x.getName() != null && x.getParameters().size() > 0 || 
+          x.getBody().getStatements().size() > 0) {
+        String fnSource = x.toSource();
+        String body = fnSource.substring(fnSource.indexOf("("));
+        JsName original = uniqueBodies.get(body);
+        if (original != null) {
+          duplicateOriginalMap.put(x.getName(), original);
+        } else {
+          uniqueBodies.put(body, x.getName());
+        }
+      }
+      return false;
+    }
+  }
+
+  private class ReplaceDuplicateInvocationNameRefs extends JsModVisitor {
+    private Map<JsName, JsName> duplicateMap;
+
+    public ReplaceDuplicateInvocationNameRefs(
+        Map<JsName, JsName> duplicateMap) {
+      this.duplicateMap = duplicateMap;
+    }
+
+    @Override
+    public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
+      JsName orig = duplicateMap.get(x.getName());
+      if (orig != null && x.getName().getEnclosing() == program.getScope()) {
+        ctx.replaceMe(orig.makeRef(x.getSourceInfo()));
+      }
+    }
+  }
+
+  public static boolean exec(JsProgram program, JsBlock fragment) {
+    return new JsDuplicateFunctionRemover(program).execImpl(fragment);
+  }
+
+  private JsProgram program;
+
+  public JsDuplicateFunctionRemover(JsProgram program) {
+    this.program = program;
+  }
+
+  private boolean execImpl(JsBlock fragment) {
+    DuplicateFunctionBodyRecorder dfbr = new DuplicateFunctionBodyRecorder();
+    dfbr.accept(fragment);
+    ReplaceDuplicateInvocationNameRefs rdup
+        = new ReplaceDuplicateInvocationNameRefs(dfbr.getDuplicateMap());
+    rdup.accept(fragment);
+    return rdup.didChange();
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
index ab22d33..e47f597 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
@@ -70,6 +70,8 @@
  */
 public class JsStackEmulator {
 
+  private static final String PROPERTY_NAME = "compiler.stackMode";
+
   /**
    * Resets the global stack depth to the local stack index and top stack frame
    * after calls to Exceptions.caught. This is created by
@@ -799,9 +801,21 @@
     }
   }
 
-  private static final String PROPERTY_NAME = "compiler.emulatedStack";
-
+  /**
+   * Corresponds to property compiler.stackMode in EmulateJsStack.gwt.xml
+   * module.
+   */
+  public enum StackMode {
+    STRIP, NATIVE, EMULATED;
+  }
+  
   public static void exec(JsProgram program, PropertyOracle[] propertyOracles) {
+    if (getStackMode(propertyOracles) == StackMode.EMULATED) {
+      (new JsStackEmulator(program, propertyOracles)).execImpl();
+    }
+  }
+
+  public static StackMode getStackMode(PropertyOracle[] propertyOracles) {
     SelectionProperty property;
     try {
       property = propertyOracles[0].getSelectionProperty(TreeLogger.NULL,
@@ -814,9 +828,21 @@
 
     String value = property.getCurrentValue();
     assert value != null : property.getName() + " did not have a value";
-    if (Boolean.valueOf(value)) {
-      (new JsStackEmulator(program, propertyOracles)).execImpl();
+    StackMode stackMode = StackMode.valueOf(value.toUpperCase());
+    // Check for multiply defined properties
+    if (propertyOracles.length > 1) {
+      for (int i = 1; i < propertyOracles.length; ++i) {
+        try {
+          property = propertyOracles[i].getSelectionProperty(TreeLogger.NULL,
+              PROPERTY_NAME);
+        } catch (BadPropertyValueException e) {
+          // OK! 
+        }
+        assert value.equals(property.getCurrentValue()) : 
+            "compiler.stackMode property has multiple values.";
+      }
     }
+    return stackMode;
   }
 
   private JsFunction caughtFunction;
diff --git a/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml b/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml
index 723063a..e97591c 100644
--- a/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml
+++ b/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml
@@ -42,4 +42,10 @@
       <when-property-is name="user.agent" value="opera" />
     </any>
   </replace-with>
+  
+  <replace-with
+      class="com.google.gwt.core.client.impl.StackTraceCreator.CollectorNull">
+    <when-type-is class="com.google.gwt.core.client.impl.StackTraceCreator.Collector" />
+    <when-property-is name="compiler.stackMode" value="strip" />
+  </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/core/EmulateJsStack.gwt.xml b/user/src/com/google/gwt/core/EmulateJsStack.gwt.xml
index 849cc54..5ae5b0e 100644
--- a/user/src/com/google/gwt/core/EmulateJsStack.gwt.xml
+++ b/user/src/com/google/gwt/core/EmulateJsStack.gwt.xml
@@ -34,4 +34,11 @@
     <when-type-is class="com.google.gwt.core.client.impl.StackTraceCreator.Collector" />
     <when-property-is name="compiler.emulatedStack" value="true" />
   </replace-with>
+  
+  <!-- if set to 'strip', stack information can be removed or corrupted -->
+  <define-property name="compiler.stackMode" values="strip,native,emulated" />
+  <set-property name="compiler.stackMode" value="native" />
+  <set-property name="compiler.stackMode" value="emulated" >
+    <when-property-is name="compiler.emulatedStack" value="true" />
+  </set-property>  
 </module>
diff --git a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
index b7e70cf..6555d8c 100644
--- a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
+++ b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
@@ -341,6 +341,26 @@
   }
 
   /**
+   * When compiler.stackMode = strip, we stub out the collector.
+   */
+  static class CollectorNull extends Collector {
+    @Override
+    public  JsArrayString collect() {
+      return JsArrayString.createArray().cast();
+    }
+
+    @Override
+    public void createStackTrace(JavaScriptException e) {
+      e.setStackTrace(new StackTraceElement[0]);
+    }
+
+    @Override
+    public void fillInStackTrace(Throwable t) {
+      t.setStackTrace(new StackTraceElement[0]);
+    }
+  }
+  
+  /**
    * Create a stack trace based on a JavaScriptException. This method should
    * only be called in web mode.
    */