8013912: Nashorn needs to reuse temporary symbols

Wed, 08 May 2013 15:51:36 +0200

author
attila
date
Wed, 08 May 2013 15:51:36 +0200
changeset 254
d28180d97c61
parent 253
fb1d7ea3e1b6
child 255
18ce1cd3026c

8013912: Nashorn needs to reuse temporary symbols
Reviewed-by: jlaskey, lagergren

src/jdk/nashorn/internal/codegen/Attr.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/CompilationPhase.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Compiler.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/CompilerConstants.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/FinalizeTypes.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/AccessNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/BlockLexicalContext.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/CallNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/IdentNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/IndexNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Node.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/RuntimeNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Symbol.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/TemporarySymbols.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/TypeOverride.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/codegen/Attr.java	Tue May 07 14:43:17 2013 +0200
     1.2 +++ b/src/jdk/nashorn/internal/codegen/Attr.java	Wed May 08 15:51:36 2013 +0200
     1.3 @@ -33,7 +33,6 @@
     1.4  import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
     1.5  import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
     1.6  import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
     1.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
     1.8  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
     1.9  import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
    1.10  import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
    1.11 @@ -43,7 +42,6 @@
    1.12  import static jdk.nashorn.internal.ir.Symbol.IS_LET;
    1.13  import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
    1.14  import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
    1.15 -import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
    1.16  import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
    1.17  import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
    1.18  import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
    1.19 @@ -55,7 +53,6 @@
    1.20  import java.util.Iterator;
    1.21  import java.util.List;
    1.22  import java.util.Set;
    1.23 -
    1.24  import jdk.nashorn.internal.codegen.types.Type;
    1.25  import jdk.nashorn.internal.ir.AccessNode;
    1.26  import jdk.nashorn.internal.ir.BinaryNode;
    1.27 @@ -81,6 +78,7 @@
    1.28  import jdk.nashorn.internal.ir.Statement;
    1.29  import jdk.nashorn.internal.ir.SwitchNode;
    1.30  import jdk.nashorn.internal.ir.Symbol;
    1.31 +import jdk.nashorn.internal.ir.TemporarySymbols;
    1.32  import jdk.nashorn.internal.ir.TernaryNode;
    1.33  import jdk.nashorn.internal.ir.TryNode;
    1.34  import jdk.nashorn.internal.ir.UnaryNode;
    1.35 @@ -134,10 +132,13 @@
    1.36      private static final DebugLogger LOG   = new DebugLogger("attr");
    1.37      private static final boolean     DEBUG = LOG.isEnabled();
    1.38  
    1.39 +    private final TemporarySymbols temporarySymbols;
    1.40 +
    1.41      /**
    1.42       * Constructor.
    1.43       */
    1.44 -    Attr() {
    1.45 +    Attr(final TemporarySymbols temporarySymbols) {
    1.46 +        this.temporarySymbols = temporarySymbols;
    1.47          this.localDefs   = new ArrayDeque<>();
    1.48          this.localUses   = new ArrayDeque<>();
    1.49          this.returnTypes = new ArrayDeque<>();
    1.50 @@ -410,7 +411,7 @@
    1.51              final boolean anonymous = functionNode.isAnonymous();
    1.52              final String  name      = anonymous ? null : functionNode.getIdent().getName();
    1.53              if (anonymous || body.getExistingSymbol(name) != null) {
    1.54 -                newFunctionNode = (FunctionNode)Attr.ensureSymbol(lc, body, FunctionNode.FUNCTION_TYPE, newFunctionNode);
    1.55 +                newFunctionNode = (FunctionNode)ensureSymbol(lc, FunctionNode.FUNCTION_TYPE, newFunctionNode);
    1.56              } else {
    1.57                  assert name != null;
    1.58                  final Symbol self = body.getExistingSymbol(name);
    1.59 @@ -421,7 +422,7 @@
    1.60  
    1.61          //unknown parameters are promoted to object type.
    1.62          newFunctionNode = finalizeParameters(newFunctionNode);
    1.63 -        finalizeTypes(newFunctionNode);
    1.64 +        newFunctionNode = finalizeTypes(newFunctionNode);
    1.65          for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
    1.66              if (symbol.getSymbolType().isUnknown()) {
    1.67                  symbol.setType(Type.OBJECT);
    1.68 @@ -533,8 +534,7 @@
    1.69  
    1.70          setBlockScope(name, symbol);
    1.71  
    1.72 -        if (symbol != null && !identNode.isInitializedHere()) {
    1.73 -
    1.74 +        if (!identNode.isInitializedHere()) {
    1.75              symbol.increaseUseCount();
    1.76          }
    1.77          addLocalUse(identNode.getName());
    1.78 @@ -790,7 +790,7 @@
    1.79              // var x; with no init will be treated like a use of x by
    1.80              // leaveIdentNode unless we remove the name from the localdef list.
    1.81              removeLocalDef(name);
    1.82 -            return newVarNode.setSymbol(lc, symbol);
    1.83 +            return end(newVarNode.setSymbol(lc, symbol));
    1.84          }
    1.85  
    1.86          addLocalDef(name);
    1.87 @@ -828,10 +828,10 @@
    1.88      @Override
    1.89      public Node leaveDECINC(final UnaryNode unaryNode) {
    1.90          // @see assignOffset
    1.91 -        ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
    1.92 +        final UnaryNode newUnaryNode = unaryNode.setRHS(ensureAssignmentSlots(unaryNode.rhs()));
    1.93          final Type type = arithType();
    1.94 -        newType(unaryNode.rhs().getSymbol(), type);
    1.95 -        return end(ensureSymbol(type, unaryNode));
    1.96 +        newType(newUnaryNode.rhs().getSymbol(), type);
    1.97 +        return end(ensureSymbol(type, newUnaryNode));
    1.98      }
    1.99  
   1.100      @Override
   1.101 @@ -1384,9 +1384,7 @@
   1.102              final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
   1.103              assert paramSymbol != null;
   1.104  
   1.105 -            if (paramSymbol != null) {
   1.106 -                newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
   1.107 -            }
   1.108 +            newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
   1.109  
   1.110              LOG.info("Initialized param ", pos, "=", paramSymbol);
   1.111              pos++;
   1.112 @@ -1521,19 +1519,25 @@
   1.113       *
   1.114       * see NASHORN-258
   1.115       *
   1.116 -     * @param functionNode   the current function node (has to be passed as it changes in the visitor below)
   1.117       * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes
   1.118       */
   1.119 -    private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
   1.120 -        assignmentDest.accept(new NodeVisitor() {
   1.121 +    private Node ensureAssignmentSlots(final Node assignmentDest) {
   1.122 +        final LexicalContext attrLexicalContext = getLexicalContext();
   1.123 +        return assignmentDest.accept(new NodeVisitor() {
   1.124              @Override
   1.125              public Node leaveIndexNode(final IndexNode indexNode) {
   1.126                  assert indexNode.getSymbol().isTemp();
   1.127                  final Node index = indexNode.getIndex();
   1.128                  //only temps can be set as needing slots. the others will self resolve
   1.129                  //it is illegal to take a scope var and force it to be a slot, that breaks
   1.130 -                if (index.getSymbol().isTemp() && !index.getSymbol().isConstant()) {
   1.131 -                     index.getSymbol().setNeedsSlot(true);
   1.132 +                Symbol indexSymbol = index.getSymbol();
   1.133 +                if (indexSymbol.isTemp() && !indexSymbol.isConstant() && !indexSymbol.hasSlot()) {
   1.134 +                    if(indexSymbol.isShared()) {
   1.135 +                        indexSymbol = temporarySymbols.createUnshared(indexSymbol);
   1.136 +                    }
   1.137 +                    indexSymbol.setNeedsSlot(true);
   1.138 +                    attrLexicalContext.getCurrentBlock().putSymbol(attrLexicalContext, indexSymbol);
   1.139 +                    return indexNode.setIndex(index.setSymbol(attrLexicalContext, indexSymbol));
   1.140                  }
   1.141                  return indexNode;
   1.142              }
   1.143 @@ -1558,22 +1562,30 @@
   1.144       *
   1.145       * @param functionNode
   1.146       */
   1.147 -    private static void finalizeTypes(final FunctionNode functionNode) {
   1.148 +    private FunctionNode finalizeTypes(final FunctionNode functionNode) {
   1.149          final Set<Node> changed = new HashSet<>();
   1.150 +        FunctionNode currentFunctionNode = functionNode;
   1.151          do {
   1.152              changed.clear();
   1.153 -            functionNode.accept(new NodeVisitor() {
   1.154 +            final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor() {
   1.155  
   1.156 -                private void widen(final Node node, final Type to) {
   1.157 +                private Node widen(final Node node, final Type to) {
   1.158                      if (node instanceof LiteralNode) {
   1.159 -                        return;
   1.160 +                        return node;
   1.161                      }
   1.162                      Type from = node.getType();
   1.163                      if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
   1.164 -                        LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to);
   1.165 -                        newType(node.getSymbol(), to);
   1.166 -                        changed.add(node);
   1.167 +                        LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
   1.168 +                        Symbol symbol = node.getSymbol();
   1.169 +                        if(symbol.isShared() && symbol.wouldChangeType(to)) {
   1.170 +                            symbol = temporarySymbols.getTypedTemporarySymbol(to);
   1.171 +                        }
   1.172 +                        newType(symbol, to);
   1.173 +                        final Node newNode = node.setSymbol(getLexicalContext(), symbol);
   1.174 +                        changed.add(newNode);
   1.175 +                        return newNode;
   1.176                      }
   1.177 +                    return node;
   1.178                  }
   1.179  
   1.180                  @Override
   1.181 @@ -1599,20 +1611,23 @@
   1.182                  @Override
   1.183                  public Node leaveBinaryNode(final BinaryNode binaryNode) {
   1.184                      final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
   1.185 +                    BinaryNode newBinaryNode = binaryNode;
   1.186                      switch (binaryNode.tokenType()) {
   1.187                      default:
   1.188                          if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
   1.189                              break;
   1.190                          }
   1.191 -                        widen(binaryNode.lhs(), widest);
   1.192 +                        newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest));
   1.193                      case ADD:
   1.194 -                        widen(binaryNode, widest);
   1.195 -                        break;
   1.196 +                        newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
   1.197                      }
   1.198 -                    return binaryNode;
   1.199 +                    return newBinaryNode;
   1.200                  }
   1.201              });
   1.202 +            getLexicalContext().replace(currentFunctionNode, newFunctionNode);
   1.203 +            currentFunctionNode = newFunctionNode;
   1.204          } while (!changed.isEmpty());
   1.205 +        return currentFunctionNode;
   1.206      }
   1.207  
   1.208      private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
   1.209 @@ -1626,14 +1641,12 @@
   1.210          newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
   1.211  //        ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
   1.212  
   1.213 -        ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
   1.214 -
   1.215 -        return end(ensureSymbol(destType, binaryNode));
   1.216 +        return end(ensureSymbol(destType, ensureAssignmentSlots(binaryNode)));
   1.217      }
   1.218  
   1.219      private Node ensureSymbol(final Type type, final Node node) {
   1.220          LOG.info("New TEMPORARY added to ", getLexicalContext().getCurrentFunction().getName(), " type=", type);
   1.221 -        return Attr.ensureSymbol(getLexicalContext(), getLexicalContext().getCurrentBlock(), type, node);
   1.222 +        return ensureSymbol(getLexicalContext(), type, node);
   1.223      }
   1.224  
   1.225      private Symbol newInternal(final String name, final Type type) {
   1.226 @@ -1694,15 +1707,8 @@
   1.227          localUses.peek().add(name);
   1.228      }
   1.229  
   1.230 -    static Node ensureSymbol(final LexicalContext lc, final Block block, final Type type, final Node node) {
   1.231 -        Symbol symbol = node.getSymbol();
   1.232 -        if (symbol != null) {
   1.233 -            return node;
   1.234 -        }
   1.235 -        final String uname = lc.getCurrentFunction().uniqueName(TEMP_PREFIX.symbolName());
   1.236 -        symbol = new Symbol(uname, IS_TEMP, type);
   1.237 -        block.putSymbol(lc, symbol);
   1.238 -        return node.setSymbol(lc, symbol);
   1.239 +    private Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) {
   1.240 +        return temporarySymbols.ensureSymbol(lc, type, node);
   1.241      }
   1.242  
   1.243      /**
   1.244 @@ -1771,6 +1777,10 @@
   1.245      }
   1.246  
   1.247      private Node end(final Node node, final boolean printNode) {
   1.248 +        if(node instanceof Statement) {
   1.249 +            // If we're done with a statement, all temporaries can be reused.
   1.250 +            temporarySymbols.reuse();
   1.251 +        }
   1.252          if (DEBUG) {
   1.253              final StringBuilder sb = new StringBuilder();
   1.254  
     2.1 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue May 07 14:43:17 2013 +0200
     2.2 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Wed May 08 15:51:36 2013 +0200
     2.3 @@ -21,6 +21,7 @@
     2.4  import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
     2.5  import jdk.nashorn.internal.ir.LexicalContext;
     2.6  import jdk.nashorn.internal.ir.Node;
     2.7 +import jdk.nashorn.internal.ir.TemporarySymbols;
     2.8  import jdk.nashorn.internal.ir.debug.ASTWriter;
     2.9  import jdk.nashorn.internal.ir.debug.PrintVisitor;
    2.10  import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
    2.11 @@ -171,7 +172,12 @@
    2.12      ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
    2.13          @Override
    2.14          FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
    2.15 -            return (FunctionNode)enterAttr(fn).accept(new Attr());
    2.16 +            final TemporarySymbols ts = compiler.getTemporarySymbols();
    2.17 +            final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts));
    2.18 +            if(compiler.getEnv()._print_mem_usage) {
    2.19 +                Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount());
    2.20 +            }
    2.21 +            return newFunctionNode;
    2.22          }
    2.23  
    2.24          /**
    2.25 @@ -179,14 +185,14 @@
    2.26           * and the function symbols to object
    2.27           * @param functionNode node where to start iterating
    2.28           */
    2.29 -        private FunctionNode enterAttr(final FunctionNode functionNode) {
    2.30 +        private FunctionNode enterAttr(final FunctionNode functionNode, final TemporarySymbols ts) {
    2.31              return (FunctionNode)functionNode.accept(new NodeVisitor() {
    2.32                  @Override
    2.33                  public Node leaveFunctionNode(final FunctionNode node) {
    2.34                      final LexicalContext lc = getLexicalContext();
    2.35                      if (node.isLazy()) {
    2.36                          FunctionNode newNode = node.setReturnType(getLexicalContext(), Type.OBJECT);
    2.37 -                        return Attr.ensureSymbol(lc, lc.getCurrentBlock(), Type.OBJECT, newNode);
    2.38 +                        return ts.ensureSymbol(lc, Type.OBJECT, newNode);
    2.39                      }
    2.40                      //node may have a reference here that needs to be nulled if it was referred to by
    2.41                      //its outer context, if it is lazy and not attributed
    2.42 @@ -249,7 +255,7 @@
    2.43          FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
    2.44              final ScriptEnvironment env = compiler.getEnv();
    2.45  
    2.46 -            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
    2.47 +            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes(compiler.getTemporarySymbols()));
    2.48  
    2.49              if (env._print_lower_ast) {
    2.50                  env.getErr().println(new ASTWriter(newFunctionNode));
     3.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java	Tue May 07 14:43:17 2013 +0200
     3.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Wed May 08 15:51:36 2013 +0200
     3.3 @@ -36,6 +36,8 @@
     3.4  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
     3.5  import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
     3.6  
     3.7 +import jdk.nashorn.internal.ir.TemporarySymbols;
     3.8 +
     3.9  import java.io.File;
    3.10  import java.lang.reflect.Field;
    3.11  import java.security.AccessController;
    3.12 @@ -53,7 +55,6 @@
    3.13  import java.util.Map.Entry;
    3.14  import java.util.Set;
    3.15  import java.util.logging.Level;
    3.16 -
    3.17  import jdk.internal.dynalink.support.NameCodec;
    3.18  import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
    3.19  import jdk.nashorn.internal.codegen.types.Type;
    3.20 @@ -100,6 +101,8 @@
    3.21  
    3.22      private CodeInstaller<ScriptEnvironment> installer;
    3.23  
    3.24 +    private final TemporarySymbols temporarySymbols = new TemporarySymbols();
    3.25 +
    3.26      /** logger for compiler, trampolines, splits and related code generation events
    3.27       *  that affect classes */
    3.28      public static final DebugLogger LOG = new DebugLogger("compiler");
    3.29 @@ -394,7 +397,7 @@
    3.30          return newFunctionNode;
    3.31      }
    3.32  
    3.33 -    private Class<?> install(final FunctionNode functionNode, final String className, final byte[] code) {
    3.34 +    private Class<?> install(final String className, final byte[] code) {
    3.35          LOG.fine("Installing class ", className);
    3.36  
    3.37          final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
    3.38 @@ -436,7 +439,7 @@
    3.39  
    3.40          final String   rootClassName = firstCompileUnitName();
    3.41          final byte[]   rootByteCode  = bytecode.get(rootClassName);
    3.42 -        final Class<?> rootClass     = install(functionNode, rootClassName, rootByteCode);
    3.43 +        final Class<?> rootClass     = install(rootClassName, rootByteCode);
    3.44  
    3.45          int length = rootByteCode.length;
    3.46  
    3.47 @@ -450,7 +453,7 @@
    3.48              final byte[] code = entry.getValue();
    3.49              length += code.length;
    3.50  
    3.51 -            installedClasses.put(className, install(functionNode, className, code));
    3.52 +            installedClasses.put(className, install(className, code));
    3.53          }
    3.54  
    3.55          for (final CompileUnit unit : compileUnits) {
    3.56 @@ -508,6 +511,10 @@
    3.57          return installer;
    3.58      }
    3.59  
    3.60 +    TemporarySymbols getTemporarySymbols() {
    3.61 +        return temporarySymbols;
    3.62 +    }
    3.63 +
    3.64      void addClass(final String name, final byte[] code) {
    3.65          bytecode.put(name, code);
    3.66      }
     4.1 --- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Tue May 07 14:43:17 2013 +0200
     4.2 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Wed May 08 15:51:36 2013 +0200
     4.3 @@ -105,25 +105,25 @@
     4.4      ARGUMENTS("arguments", Object.class, 2),
     4.5  
     4.6      /** prefix for iterators for for (x in ...) */
     4.7 -    ITERATOR_PREFIX(":iter"),
     4.8 +    ITERATOR_PREFIX(":i"),
     4.9  
    4.10      /** prefix for tag variable used for switch evaluation */
    4.11 -    SWITCH_TAG_PREFIX(":tag"),
    4.12 +    SWITCH_TAG_PREFIX(":s"),
    4.13  
    4.14      /** prefix for all exceptions */
    4.15 -    EXCEPTION_PREFIX(":exception"),
    4.16 +    EXCEPTION_PREFIX(":e"),
    4.17  
    4.18      /** prefix for quick slots generated in Store */
    4.19 -    QUICK_PREFIX(":quick"),
    4.20 +    QUICK_PREFIX(":q"),
    4.21  
    4.22      /** prefix for temporary variables */
    4.23 -    TEMP_PREFIX(":temp"),
    4.24 +    TEMP_PREFIX(":t"),
    4.25  
    4.26      /** prefix for literals */
    4.27 -    LITERAL_PREFIX(":lit"),
    4.28 +    LITERAL_PREFIX(":l"),
    4.29  
    4.30      /** prefix for regexps */
    4.31 -    REGEX_PREFIX(":regex"),
    4.32 +    REGEX_PREFIX(":r"),
    4.33  
    4.34      /** "this" used in non-static Java methods; always in slot 0 */
    4.35      JAVA_THIS(null, 0),
     5.1 --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue May 07 14:43:17 2013 +0200
     5.2 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Wed May 08 15:51:36 2013 +0200
     5.3 @@ -55,6 +55,7 @@
     5.4  import jdk.nashorn.internal.ir.RuntimeNode.Request;
     5.5  import jdk.nashorn.internal.ir.SwitchNode;
     5.6  import jdk.nashorn.internal.ir.Symbol;
     5.7 +import jdk.nashorn.internal.ir.TemporarySymbols;
     5.8  import jdk.nashorn.internal.ir.TernaryNode;
     5.9  import jdk.nashorn.internal.ir.ThrowNode;
    5.10  import jdk.nashorn.internal.ir.TypeOverride;
    5.11 @@ -87,7 +88,10 @@
    5.12  
    5.13      private static final DebugLogger LOG = new DebugLogger("finalize");
    5.14  
    5.15 -    FinalizeTypes() {
    5.16 +    private final TemporarySymbols temporarySymbols;
    5.17 +
    5.18 +    FinalizeTypes(final TemporarySymbols temporarySymbols) {
    5.19 +        this.temporarySymbols = temporarySymbols;
    5.20      }
    5.21  
    5.22      @Override
    5.23 @@ -227,21 +231,27 @@
    5.24          return leaveASSIGN(binaryNode);
    5.25      }
    5.26  
    5.27 +    private boolean symbolIsInteger(Node node) {
    5.28 +        final Symbol symbol = node.getSymbol();
    5.29 +        assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + getLexicalContext().getCurrentFunction().getSource();
    5.30 +        return true;
    5.31 +    }
    5.32 +
    5.33      @Override
    5.34      public Node leaveBIT_AND(final BinaryNode binaryNode) {
    5.35 -        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
    5.36 +        assert symbolIsInteger(binaryNode);
    5.37          return leaveBinary(binaryNode, Type.INT, Type.INT);
    5.38      }
    5.39  
    5.40      @Override
    5.41      public Node leaveBIT_OR(final BinaryNode binaryNode) {
    5.42 -        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
    5.43 +        assert symbolIsInteger(binaryNode);
    5.44          return leaveBinary(binaryNode, Type.INT, Type.INT);
    5.45      }
    5.46  
    5.47      @Override
    5.48      public Node leaveBIT_XOR(final BinaryNode binaryNode) {
    5.49 -        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
    5.50 +        assert symbolIsInteger(binaryNode);
    5.51          return leaveBinary(binaryNode, Type.INT, Type.INT);
    5.52      }
    5.53  
    5.54 @@ -251,8 +261,7 @@
    5.55          final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
    5.56          // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
    5.57          // in that case, update the node type as well
    5.58 -        propagateType(newBinaryNode, newBinaryNode.lhs().getType());
    5.59 -        return newBinaryNode;
    5.60 +        return propagateType(newBinaryNode, newBinaryNode.lhs().getType());
    5.61      }
    5.62  
    5.63      @Override
    5.64 @@ -261,8 +270,7 @@
    5.65          final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs()));
    5.66          // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
    5.67          // in that case, update the node type as well
    5.68 -        propagateType(newBinaryNode, newBinaryNode.rhs().getType());
    5.69 -        return newBinaryNode;
    5.70 +        return propagateType(newBinaryNode, newBinaryNode.rhs().getType());
    5.71      }
    5.72  
    5.73      @Override
    5.74 @@ -364,6 +372,7 @@
    5.75  
    5.76      @Override
    5.77      public Node leaveExecuteNode(final ExecuteNode executeNode) {
    5.78 +        temporarySymbols.reuse();
    5.79          return executeNode.setExpression(discard(executeNode.getExpression()));
    5.80      }
    5.81  
    5.82 @@ -489,8 +498,8 @@
    5.83  
    5.84      @Override
    5.85      public Node leaveVarNode(final VarNode varNode) {
    5.86 -        final Node rhs = varNode.getInit();
    5.87 -        if (rhs != null) {
    5.88 +        final Node init = varNode.getInit();
    5.89 +        if (init != null) {
    5.90              final SpecializedNode specialized = specialize(varNode);
    5.91              final VarNode specVarNode = (VarNode)specialized.node;
    5.92              Type destType = specialized.type;
    5.93 @@ -498,8 +507,11 @@
    5.94                  destType = specVarNode.getType();
    5.95              }
    5.96              assert specVarNode.hasType() : specVarNode + " doesn't have a type";
    5.97 -            return specVarNode.setInit(convert(rhs, destType));
    5.98 +            final Node convertedInit = convert(init, destType);
    5.99 +            temporarySymbols.reuse();
   5.100 +            return specVarNode.setInit(convertedInit);
   5.101          }
   5.102 +        temporarySymbols.reuse();
   5.103          return varNode;
   5.104      }
   5.105  
   5.106 @@ -678,7 +690,7 @@
   5.107          }
   5.108      }
   5.109  
   5.110 -    private static <T extends Node> SpecializedNode specialize(final Assignment<T> assignment) {
   5.111 +    <T extends Node> SpecializedNode specialize(final Assignment<T> assignment) {
   5.112          final Node node = ((Node)assignment);
   5.113          final T lhs = assignment.getAssignmentDest();
   5.114          final Node rhs = assignment.getAssignmentSource();
   5.115 @@ -700,9 +712,9 @@
   5.116          }
   5.117  
   5.118          final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to));
   5.119 -        propagateType(newNode, to);
   5.120 +        final Node typePropagatedNode = propagateType(newNode, to);
   5.121  
   5.122 -        return new SpecializedNode(newNode, to);
   5.123 +        return new SpecializedNode(typePropagatedNode, to);
   5.124      }
   5.125  
   5.126  
   5.127 @@ -741,7 +753,7 @@
   5.128       * @param to      new type
   5.129       */
   5.130      @SuppressWarnings("unchecked")
   5.131 -    private static <T extends Node> T setTypeOverride(final T node, final Type to) {
   5.132 +    <T extends Node> T setTypeOverride(final T node, final Type to) {
   5.133          final Type from = node.getType();
   5.134          if (!node.getType().equals(to)) {
   5.135              LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
   5.136 @@ -750,7 +762,7 @@
   5.137              }
   5.138          }
   5.139          LOG.info("Type override for lhs in '", node, "' => ", to);
   5.140 -        return ((TypeOverride<T>)node).setType(to);
   5.141 +        return ((TypeOverride<T>)node).setType(temporarySymbols, getLexicalContext(), to);
   5.142      }
   5.143  
   5.144      /**
   5.145 @@ -808,7 +820,7 @@
   5.146          final LexicalContext lc = getLexicalContext();
   5.147          //This is the only place in this file that can create new temporaries
   5.148          //FinalizeTypes may not introduce ANY node that is not a conversion.
   5.149 -        return Attr.ensureSymbol(lc, lc.getCurrentBlock(), to, resultNode);
   5.150 +        return temporarySymbols.ensureSymbol(lc, to, resultNode);
   5.151      }
   5.152  
   5.153      private static Node discard(final Node node) {
   5.154 @@ -836,12 +848,13 @@
   5.155       * @param node
   5.156       * @param to
   5.157       */
   5.158 -    private static void propagateType(final Node node, final Type to) {
   5.159 -        final Symbol symbol = node.getSymbol();
   5.160 -        if (symbol.isTemp()) {
   5.161 -            symbol.setTypeOverride(to);
   5.162 +    private Node propagateType(final Node node, final Type to) {
   5.163 +        Symbol symbol = node.getSymbol();
   5.164 +        if (symbol.isTemp() && symbol.getSymbolType() != to) {
   5.165 +            symbol = symbol.setTypeOverrideShared(to, temporarySymbols);
   5.166              LOG.info("Type override for temporary in '", node, "' => ", to);
   5.167          }
   5.168 +        return node.setSymbol(getLexicalContext(), symbol);
   5.169      }
   5.170  
   5.171      /**
   5.172 @@ -866,7 +879,7 @@
   5.173       * Whenever an explicit conversion is needed and the convertee is a literal, we can
   5.174       * just change the literal
   5.175       */
   5.176 -    static class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> {
   5.177 +    class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> {
   5.178          private final Type type;
   5.179  
   5.180          LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) {
   5.181 @@ -894,7 +907,7 @@
   5.182  
   5.183              if (literalNode != null) {
   5.184                  //inherit literal symbol for attr.
   5.185 -                literalNode = (LiteralNode<?>)literalNode.setSymbol(null, parent.getSymbol());
   5.186 +                literalNode = (LiteralNode<?>)literalNode.setSymbol(getLexicalContext(), parent.getSymbol());
   5.187              }
   5.188  
   5.189              return literalNode;
     6.1 --- a/src/jdk/nashorn/internal/ir/AccessNode.java	Tue May 07 14:43:17 2013 +0200
     6.2 +++ b/src/jdk/nashorn/internal/ir/AccessNode.java	Wed May 08 15:51:36 2013 +0200
     6.3 @@ -119,10 +119,10 @@
     6.4      }
     6.5  
     6.6      @Override
     6.7 -    public AccessNode setType(final Type type) {
     6.8 +    public AccessNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
     6.9          logTypeChange(type);
    6.10 -        getSymbol().setTypeOverride(type); //always a temp so this is fine.
    6.11 -        return new AccessNode(this, base, property.setType(type), isFunction(), hasCallSiteType());
    6.12 +        final AccessNode newAccessNode = (AccessNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts));
    6.13 +        return new AccessNode(newAccessNode, base, property.setType(ts, lc, type), isFunction(), hasCallSiteType());
    6.14      }
    6.15  
    6.16      @Override
     7.1 --- a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java	Tue May 07 14:43:17 2013 +0200
     7.2 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java	Wed May 08 15:51:36 2013 +0200
     7.3 @@ -64,7 +64,6 @@
     7.4      }
     7.5  
     7.6      @Override
     7.7 -    @SuppressWarnings("unchecked")
     7.8      public <T extends LexicalContextNode> T pop(final T node) {
     7.9          T expected = node;
    7.10          if (node instanceof Block) {
     8.1 --- a/src/jdk/nashorn/internal/ir/CallNode.java	Tue May 07 14:43:17 2013 +0200
     8.2 +++ b/src/jdk/nashorn/internal/ir/CallNode.java	Wed May 08 15:51:36 2013 +0200
     8.3 @@ -170,7 +170,7 @@
     8.4      }
     8.5  
     8.6      @Override
     8.7 -    public CallNode setType(final Type type) {
     8.8 +    public CallNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
     8.9          if (this.type == type) {
    8.10              return this;
    8.11          }
    8.12 @@ -200,7 +200,7 @@
    8.13                      setFunction(function.accept(visitor)).
    8.14                      setArgs(Node.accept(visitor, Node.class, args)).
    8.15                      setFlags(flags).
    8.16 -                    setType(type).
    8.17 +                    setType(null, lc, type).
    8.18                      setEvalArgs(evalArgs == null ?
    8.19                              null :
    8.20                              evalArgs.setCode(evalArgs.getCode().accept(visitor)).
     9.1 --- a/src/jdk/nashorn/internal/ir/IdentNode.java	Tue May 07 14:43:17 2013 +0200
     9.2 +++ b/src/jdk/nashorn/internal/ir/IdentNode.java	Wed May 08 15:51:36 2013 +0200
     9.3 @@ -61,7 +61,7 @@
     9.4       */
     9.5      public IdentNode(final long token, final int finish, final String name) {
     9.6          super(token, finish);
     9.7 -        this.name = name;
     9.8 +        this.name = name.intern();
     9.9          this.callSiteType = null;
    9.10          this.flags = 0;
    9.11      }
    9.12 @@ -101,7 +101,7 @@
    9.13      }
    9.14  
    9.15      @Override
    9.16 -    public IdentNode setType(final Type type) {
    9.17 +    public IdentNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
    9.18          // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
    9.19          if (this.callSiteType == type) {
    9.20              return this;
    10.1 --- a/src/jdk/nashorn/internal/ir/IndexNode.java	Tue May 07 14:43:17 2013 +0200
    10.2 +++ b/src/jdk/nashorn/internal/ir/IndexNode.java	Wed May 08 15:51:36 2013 +0200
    10.3 @@ -106,6 +106,18 @@
    10.4          return index;
    10.5      }
    10.6  
    10.7 +    /**
    10.8 +     * Set the index expression for this node
    10.9 +     * @param index new index expression
   10.10 +     * @return a node equivalent to this one except for the requested change.
   10.11 +     */
   10.12 +    public IndexNode setIndex(Node index) {
   10.13 +        if(this.index == index) {
   10.14 +            return this;
   10.15 +        }
   10.16 +        return new IndexNode(this, base, index, isFunction(), hasCallSiteType());
   10.17 +    }
   10.18 +
   10.19      @Override
   10.20      public BaseNode setIsFunction() {
   10.21          if (isFunction()) {
   10.22 @@ -115,10 +127,10 @@
   10.23      }
   10.24  
   10.25      @Override
   10.26 -    public IndexNode setType(final Type type) {
   10.27 +    public IndexNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
   10.28          logTypeChange(type);
   10.29 -        getSymbol().setTypeOverride(type); //always a temp so this is fine.
   10.30 -        return new IndexNode(this, base, index, isFunction(), true);
   10.31 +        final IndexNode newIndexNode = (IndexNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts));
   10.32 +        return new IndexNode(newIndexNode, base, index, isFunction(), true);
   10.33      }
   10.34  
   10.35  }
    11.1 --- a/src/jdk/nashorn/internal/ir/Node.java	Tue May 07 14:43:17 2013 +0200
    11.2 +++ b/src/jdk/nashorn/internal/ir/Node.java	Wed May 08 15:51:36 2013 +0200
    11.3 @@ -27,7 +27,6 @@
    11.4  
    11.5  import java.util.ArrayList;
    11.6  import java.util.List;
    11.7 -
    11.8  import jdk.nashorn.internal.codegen.types.Type;
    11.9  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   11.10  import jdk.nashorn.internal.parser.Token;
    12.1 --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java	Tue May 07 14:43:17 2013 +0200
    12.2 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java	Wed May 08 15:51:36 2013 +0200
    12.3 @@ -390,7 +390,7 @@
    12.4      }
    12.5  
    12.6      @Override
    12.7 -    public RuntimeNode setType(final Type type) {
    12.8 +    public RuntimeNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
    12.9          if (this.callSiteType == type) {
   12.10              return this;
   12.11          }
    13.1 --- a/src/jdk/nashorn/internal/ir/Symbol.java	Tue May 07 14:43:17 2013 +0200
    13.2 +++ b/src/jdk/nashorn/internal/ir/Symbol.java	Wed May 08 15:51:36 2013 +0200
    13.3 @@ -29,7 +29,6 @@
    13.4  import java.util.HashSet;
    13.5  import java.util.Set;
    13.6  import java.util.StringTokenizer;
    13.7 -
    13.8  import jdk.nashorn.internal.codegen.types.Type;
    13.9  import jdk.nashorn.internal.runtime.Context;
   13.10  import jdk.nashorn.internal.runtime.Debug;
   13.11 @@ -69,6 +68,8 @@
   13.12      public static final int IS_FUNCTION_SELF = 1 << 10;
   13.13      /** Is this a specialized param? */
   13.14      public static final int IS_SPECIALIZED_PARAM = 1 << 11;
   13.15 +    /** Is this symbol a shared temporary? */
   13.16 +    public static final int IS_SHARED = 1 << 12;
   13.17  
   13.18      /** Null or name identifying symbol. */
   13.19      private final String name;
   13.20 @@ -154,6 +155,16 @@
   13.21          this(name, flags, type, -1);
   13.22      }
   13.23  
   13.24 +    private Symbol(final Symbol base, final String name, final int flags) {
   13.25 +        this.flags = flags;
   13.26 +        this.name = name;
   13.27 +
   13.28 +        this.fieldIndex = base.fieldIndex;
   13.29 +        this.slot = base.slot;
   13.30 +        this.type = base.type;
   13.31 +        this.useCount = base.useCount;
   13.32 +    }
   13.33 +
   13.34      private static String align(final String string, final int max) {
   13.35          final StringBuilder sb = new StringBuilder();
   13.36          sb.append(string.substring(0, Math.min(string.length(), max)));
   13.37 @@ -331,6 +342,24 @@
   13.38      }
   13.39  
   13.40      /**
   13.41 +     * Returns true if this symbol is a temporary that is being shared across expressions.
   13.42 +     * @return true if this symbol is a temporary that is being shared across expressions.
   13.43 +     */
   13.44 +    public boolean isShared() {
   13.45 +        return (flags & IS_SHARED) == IS_SHARED;
   13.46 +    }
   13.47 +
   13.48 +    /**
   13.49 +     * Creates an unshared copy of a symbol. The symbol must be currently shared.
   13.50 +     * @param newName the name for the new symbol.
   13.51 +     * @return a new, unshared symbol.
   13.52 +     */
   13.53 +    public Symbol createUnshared(final String newName) {
   13.54 +        assert isShared();
   13.55 +        return new Symbol(this, newName, flags & ~IS_SHARED);
   13.56 +    }
   13.57 +
   13.58 +    /**
   13.59       * Flag this symbol as scope as described in {@link Symbol#isScope()}
   13.60       */
   13.61      /**
   13.62 @@ -339,10 +368,23 @@
   13.63       public void setIsScope() {
   13.64          if (!isScope()) {
   13.65              trace("SET IS SCOPE");
   13.66 +            assert !isShared();
   13.67 +            flags |= IS_SCOPE;
   13.68          }
   13.69 -        flags |= IS_SCOPE;
   13.70      }
   13.71  
   13.72 +     /**
   13.73 +      * Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary.
   13.74 +      */
   13.75 +     public void setIsShared() {
   13.76 +         if(!isShared()) {
   13.77 +             assert isTemp();
   13.78 +             trace("SET IS SHARED");
   13.79 +             flags |= IS_SHARED;
   13.80 +         }
   13.81 +     }
   13.82 +
   13.83 +
   13.84      /**
   13.85       * Check if this symbol is a variable
   13.86       * @return true if variable
   13.87 @@ -397,7 +439,10 @@
   13.88       */
   13.89      public void setCanBeUndefined() {
   13.90          assert type.isObject() : type;
   13.91 -        flags |= CAN_BE_UNDEFINED;
   13.92 +        if(!canBeUndefined()) {
   13.93 +            assert !isShared();
   13.94 +            flags |= CAN_BE_UNDEFINED;
   13.95 +        }
   13.96      }
   13.97  
   13.98      /**
   13.99 @@ -405,7 +450,10 @@
  13.100       * @param type the primitive type it occurs with, currently unused but can be used for width guesses
  13.101       */
  13.102      public void setCanBePrimitive(final Type type) {
  13.103 -        flags |= CAN_BE_PRIMITIVE;
  13.104 +        if(!canBePrimitive()) {
  13.105 +            assert !isShared();
  13.106 +            flags |= CAN_BE_PRIMITIVE;
  13.107 +        }
  13.108      }
  13.109  
  13.110      /**
  13.111 @@ -445,7 +493,10 @@
  13.112       * Flag this symbol as a let
  13.113       */
  13.114      public void setIsLet() {
  13.115 -        flags |= IS_LET;
  13.116 +        if(!isLet()) {
  13.117 +            assert !isShared();
  13.118 +            flags |= IS_LET;
  13.119 +        }
  13.120      }
  13.121  
  13.122      /**
  13.123 @@ -474,7 +525,10 @@
  13.124       * @param fieldIndex field index - a positive integer
  13.125       */
  13.126      public void setFieldIndex(final int fieldIndex) {
  13.127 -        this.fieldIndex = fieldIndex;
  13.128 +        if(this.fieldIndex != fieldIndex) {
  13.129 +            assert !isShared();
  13.130 +            this.fieldIndex = fieldIndex;
  13.131 +        }
  13.132      }
  13.133  
  13.134      /**
  13.135 @@ -490,7 +544,10 @@
  13.136       * @param flags flags
  13.137       */
  13.138      public void setFlags(final int flags) {
  13.139 -        this.flags = flags;
  13.140 +        if(this.flags != flags) {
  13.141 +            assert !isShared();
  13.142 +            this.flags = flags;
  13.143 +        }
  13.144      }
  13.145  
  13.146      /**
  13.147 @@ -530,6 +587,7 @@
  13.148       */
  13.149      public void setSlot(final int slot) {
  13.150          if (slot != this.slot) {
  13.151 +            assert !isShared();
  13.152              trace("SET SLOT " + slot);
  13.153              this.slot = slot;
  13.154          }
  13.155 @@ -555,6 +613,15 @@
  13.156      }
  13.157  
  13.158      /**
  13.159 +     * Returns true if calling {@link #setType(Type)} on this symbol would effectively change its type.
  13.160 +     * @param newType the new type to test for
  13.161 +     * @return true if setting this symbols type to a new value would effectively change its type.
  13.162 +     */
  13.163 +    public boolean wouldChangeType(final Type newType) {
  13.164 +        return Type.widest(this.type, newType) != this.type;
  13.165 +    }
  13.166 +
  13.167 +    /**
  13.168       * Only use this if you know about an existing type
  13.169       * constraint - otherwise a type can only be
  13.170       * widened
  13.171 @@ -564,12 +631,33 @@
  13.172      public void setTypeOverride(final Type type) {
  13.173          final Type old = this.type;
  13.174          if (old != type) {
  13.175 +            assert !isShared();
  13.176              trace("TYPE CHANGE: " + old + "=>" + type + " == " + type);
  13.177              this.type = type;
  13.178          }
  13.179      }
  13.180  
  13.181      /**
  13.182 +     * Sets the type of the symbol to the specified type. If the type would be changed, but this symbol is a shared
  13.183 +     * temporary, it will instead return a different temporary symbol of the requested type from the passed temporary
  13.184 +     * symbols. That way, it never mutates the type of a shared temporary.
  13.185 +     * @param type the new type for the symbol
  13.186 +     * @param ts a holder of temporary symbols
  13.187 +     * @return either this symbol, or a different symbol if this symbol is a shared temporary and it type would have to
  13.188 +     * be changed.
  13.189 +     */
  13.190 +    public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) {
  13.191 +        if(getSymbolType() != type) {
  13.192 +            if(isShared()) {
  13.193 +                assert !hasSlot();
  13.194 +                return ts.getTypedTemporarySymbol(type);
  13.195 +            }
  13.196 +            setTypeOverride(type);
  13.197 +        }
  13.198 +        return this;
  13.199 +    }
  13.200 +
  13.201 +    /**
  13.202       * From a lexical context, set this symbol as needing scope, which
  13.203       * will set flags for the defining block that will be written when
  13.204       * block is popped from the lexical context stack, used by codegen
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/src/jdk/nashorn/internal/ir/TemporarySymbols.java	Wed May 08 15:51:36 2013 +0200
    14.3 @@ -0,0 +1,169 @@
    14.4 +/*
    14.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    14.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    14.7 + *
    14.8 + * This code is free software; you can redistribute it and/or modify it
    14.9 + * under the terms of the GNU General Public License version 2 only, as
   14.10 + * published by the Free Software Foundation.  Oracle designates this
   14.11 + * particular file as subject to the "Classpath" exception as provided
   14.12 + * by Oracle in the LICENSE file that accompanied this code.
   14.13 + *
   14.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   14.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   14.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14.17 + * version 2 for more details (a copy is included in the LICENSE file that
   14.18 + * accompanied this code).
   14.19 + *
   14.20 + * You should have received a copy of the GNU General Public License version
   14.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   14.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   14.23 + *
   14.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   14.25 + * or visit www.oracle.com if you need additional information or have any
   14.26 + * questions.
   14.27 + */
   14.28 +
   14.29 +package jdk.nashorn.internal.ir;
   14.30 +
   14.31 +import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
   14.32 +import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
   14.33 +
   14.34 +import java.util.HashMap;
   14.35 +import java.util.Map;
   14.36 +import jdk.nashorn.internal.codegen.types.Type;
   14.37 +
   14.38 +/**
   14.39 + * Class that holds reusable temporary symbols by type.
   14.40 + *
   14.41 + */
   14.42 +public class TemporarySymbols {
   14.43 +    private static final String prefix = TEMP_PREFIX.symbolName() + "$";
   14.44 +
   14.45 +    private int totalSymbolCount;
   14.46 +    private final Map<Type, TypedTemporarySymbols> temporarySymbolsByType = new HashMap<>();
   14.47 +
   14.48 +    /**
   14.49 +     * Associates a temporary symbol of a given type with a node, if the node doesn't already have any symbol.
   14.50 +     * @param lc the current lexical context
   14.51 +     * @param type the type of the temporary symbol
   14.52 +     * @param node the node
   14.53 +     * @return the node that is guaranteed to have a symbol.
   14.54 +     */
   14.55 +    public Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) {
   14.56 +        final Symbol symbol = node.getSymbol();
   14.57 +        if (symbol != null) {
   14.58 +            return node;
   14.59 +        }
   14.60 +        return node.setSymbol(lc, getTypedTemporarySymbol(type));
   14.61 +    }
   14.62 +
   14.63 +    /**
   14.64 +     * Given a type, returns a temporary symbol of that type.
   14.65 +     * @param type the required type of the symbol.
   14.66 +     * @return a temporary symbol of the required type.
   14.67 +     */
   14.68 +    public Symbol getTypedTemporarySymbol(final Type type) {
   14.69 +        return getTypedTemporarySymbols(type).getTemporarySymbol(type);
   14.70 +    }
   14.71 +
   14.72 +    private TypedTemporarySymbols getTypedTemporarySymbols(final Type type) {
   14.73 +        TypedTemporarySymbols temporarySymbols = temporarySymbolsByType.get(type);
   14.74 +        if(temporarySymbols == null) {
   14.75 +            temporarySymbols = new TypedTemporarySymbols();
   14.76 +            temporarySymbolsByType.put(type, temporarySymbols);
   14.77 +        }
   14.78 +        return temporarySymbols;
   14.79 +    }
   14.80 +
   14.81 +    /**
   14.82 +     * This method is called to signal to this object that all the symbols it holds can be reused now.
   14.83 +     */
   14.84 +    public void reuse() {
   14.85 +        for(TypedTemporarySymbols ts: temporarySymbolsByType.values()) {
   14.86 +            ts.reuse();
   14.87 +        }
   14.88 +    }
   14.89 +
   14.90 +    /**
   14.91 +     * Given a shared symbol, creates an unshared copy of it with a unique name.
   14.92 +     * @param symbol the shared symbol
   14.93 +     * @return the unshared, uniquely named copy of the symbol
   14.94 +     */
   14.95 +    public Symbol createUnshared(Symbol symbol) {
   14.96 +        return symbol.createUnshared(getUniqueName());
   14.97 +    }
   14.98 +
   14.99 +    private String getUniqueName() {
  14.100 +        return prefix + (++totalSymbolCount);
  14.101 +    }
  14.102 +
  14.103 +    /**
  14.104 +     * Returns the total number of symbols this object created during its lifetime.
  14.105 +     * @return the total number of symbols this object created during its lifetime.
  14.106 +     */
  14.107 +    public int getTotalSymbolCount() {
  14.108 +        return totalSymbolCount;
  14.109 +    }
  14.110 +
  14.111 +    private class TypedTemporarySymbols {
  14.112 +        private Symbol[] symbols = new Symbol[16];
  14.113 +        private int nextFreeSymbol = 0;
  14.114 +        private int symbolCount = 0;
  14.115 +
  14.116 +        Symbol getTemporarySymbol(final Type type) {
  14.117 +            while(nextFreeSymbol < symbolCount) {
  14.118 +                final Symbol nextSymbol = symbols[nextFreeSymbol];
  14.119 +                assert nextSymbol != null;
  14.120 +                // If it has a slot, we can't reuse it.
  14.121 +                if(!nextSymbol.hasSlot()) {
  14.122 +                    final Type symbolType = nextSymbol.getSymbolType();
  14.123 +                    if(symbolType == type) {
  14.124 +                        assert nextSymbol.isTemp();
  14.125 +                        assert !nextSymbol.isScope();
  14.126 +                        // If types match, we can reuse it.
  14.127 +                        nextSymbol.setIsShared();
  14.128 +                        nextFreeSymbol++;
  14.129 +                        return nextSymbol;
  14.130 +                    }
  14.131 +                    // If its type changed, but it doesn't have a slot then move it to its new home according to its
  14.132 +                    // new type.
  14.133 +                    getTypedTemporarySymbols(symbolType).addSymbol(nextSymbol);
  14.134 +                }
  14.135 +                // If we can move another symbol into its place, do that and repeat the analysis for this symbol.
  14.136 +                --symbolCount;
  14.137 +                if(symbolCount != nextFreeSymbol) {
  14.138 +                    final Symbol lastFreeSymbol = symbols[symbolCount];
  14.139 +                    symbols[nextFreeSymbol] = lastFreeSymbol;
  14.140 +                }
  14.141 +                symbols[symbolCount] = null;
  14.142 +            }
  14.143 +            return createNewSymbol(type);
  14.144 +        }
  14.145 +
  14.146 +        private Symbol createNewSymbol(final Type type) {
  14.147 +            ensureCapacity();
  14.148 +            final Symbol symbol = symbols[nextFreeSymbol] = new Symbol(getUniqueName(), IS_TEMP, type);
  14.149 +            nextFreeSymbol++;
  14.150 +            symbolCount++;
  14.151 +            return symbol;
  14.152 +        }
  14.153 +
  14.154 +        private void addSymbol(Symbol symbol) {
  14.155 +            ensureCapacity();
  14.156 +            symbols[symbolCount++] = symbol;
  14.157 +        }
  14.158 +
  14.159 +        void reuse() {
  14.160 +            nextFreeSymbol = 0;
  14.161 +        }
  14.162 +
  14.163 +        private void ensureCapacity() {
  14.164 +            if(symbolCount == symbols.length) {
  14.165 +                final Symbol[] newSymbols = new Symbol[symbolCount * 2];
  14.166 +                System.arraycopy(symbols, 0, newSymbols, 0, symbolCount);
  14.167 +                symbols = newSymbols;
  14.168 +            }
  14.169 +        }
  14.170 +    }
  14.171 +
  14.172 +}
    15.1 --- a/src/jdk/nashorn/internal/ir/TypeOverride.java	Tue May 07 14:43:17 2013 +0200
    15.2 +++ b/src/jdk/nashorn/internal/ir/TypeOverride.java	Wed May 08 15:51:36 2013 +0200
    15.3 @@ -43,10 +43,12 @@
    15.4      /**
    15.5       * Set the override type
    15.6       *
    15.7 -     * @param type  the type
    15.8 +     * @param ts temporary symbols
    15.9 +     * @param lc the current lexical context
   15.10 +     * @param type the type
   15.11       * @return a node equivalent to this one except for the requested change.
   15.12       */
   15.13 -    public T setType(final Type type);
   15.14 +    public T setType(final TemporarySymbols ts, final LexicalContext lc, final Type type);
   15.15  
   15.16      /**
   15.17       * Returns true if this node can have a callsite override, e.g. all scope ident nodes

mercurial