changeset 246:5a3f7867e19c

8013477: Node.setSymbol needs to be copy on write - enable IR snapshots for recompilation based on callsite type specialization. [not enabled by default, hidden by a flag for now] Reviewed-by: jlaskey, hannesw
author lagergren
date Fri, 03 May 2013 15:33:54 +0200
parents c8023561505b
children 829b06307fb2
files bin/jjs src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java src/jdk/nashorn/internal/codegen/Attr.java src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/CompilationPhase.java src/jdk/nashorn/internal/codegen/Compiler.java src/jdk/nashorn/internal/codegen/FinalizeTypes.java src/jdk/nashorn/internal/codegen/ObjectCreator.java src/jdk/nashorn/internal/codegen/Splitter.java src/jdk/nashorn/internal/ir/Block.java src/jdk/nashorn/internal/ir/CatchNode.java src/jdk/nashorn/internal/ir/FunctionNode.java src/jdk/nashorn/internal/ir/LexicalContext.java src/jdk/nashorn/internal/ir/LexicalContextNode.java src/jdk/nashorn/internal/ir/LiteralNode.java src/jdk/nashorn/internal/ir/Node.java src/jdk/nashorn/internal/ir/Symbol.java src/jdk/nashorn/internal/objects/NativeRegExp.java src/jdk/nashorn/internal/parser/AbstractParser.java src/jdk/nashorn/internal/parser/Parser.java src/jdk/nashorn/internal/runtime/CompiledFunction.java src/jdk/nashorn/internal/runtime/CompiledFunctions.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/JSONFunctions.java src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java src/jdk/nashorn/internal/runtime/ScriptEnvironment.java src/jdk/nashorn/internal/runtime/ScriptObject.java src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java src/jdk/nashorn/internal/runtime/resources/Options.properties src/jdk/nashorn/tools/Shell.java test/script/basic/paramspec.js test/script/basic/paramspec.js.EXPECTED test/script/basic/runsunspider.js test/script/currently-failing/logcoverage.js test/script/trusted/logcoverage.js
diffstat 37 files changed, 935 insertions(+), 705 deletions(-) [+]
line wrap: on
line diff
--- a/bin/jjs	Thu May 02 15:01:16 2013 -0300
+++ b/bin/jjs	Fri May 03 15:33:54 2013 +0200
@@ -26,4 +26,4 @@
 
 [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
 
-$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*
+$JAVA_HOME/bin/java -server -XX:+TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java	Fri May 03 15:33:54 2013 +0200
@@ -31,7 +31,6 @@
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineFactory;
 import jdk.nashorn.internal.runtime.Version;
-import sun.reflect.Reflection;
 
 /**
  * JSR-223 compliant script engine factory for Nashorn. The engine answers for:
--- a/src/jdk/nashorn/internal/codegen/Attr.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/codegen/Attr.java	Fri May 03 15:33:54 2013 +0200
@@ -29,17 +29,21 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
+import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
 import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
 import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
 import static jdk.nashorn.internal.ir.Symbol.IS_LET;
 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
 import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
+import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
 import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
 import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
 import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
@@ -51,6 +55,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BinaryNode;
@@ -90,7 +95,6 @@
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 
 /**
@@ -133,9 +137,9 @@
      * Constructor.
      */
     Attr() {
-        localDefs = new ArrayDeque<>();
-        localUses = new ArrayDeque<>();
-        returnTypes = new ArrayDeque<>();
+        this.localDefs   = new ArrayDeque<>();
+        this.localUses   = new ArrayDeque<>();
+        this.returnTypes = new ArrayDeque<>();
     }
 
     @Override
@@ -150,67 +154,48 @@
 
     @Override
     public Node leaveAccessNode(final AccessNode accessNode) {
-        ensureSymbol(Type.OBJECT, accessNode);  //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
-        end(accessNode);
-        return accessNode;
+        //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
+        return end(ensureSymbol(Type.OBJECT, accessNode));
     }
 
-    private void enterFunctionBody() {
+    private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
+        initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL, FunctionNode.FUNCTION_TYPE);
+        initCompileConstant(THIS, body, IS_PARAM | IS_THIS, Type.OBJECT);
 
-        final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
-        final Block body = getLexicalContext().getCurrentBlock();
-        initCallee(body);
-        initThis(body);
         if (functionNode.isVarArg()) {
-            initVarArg(body, functionNode.needsArguments());
+            initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL, Type.OBJECT_ARRAY);
+            if (functionNode.needsArguments()) {
+                initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL, Type.typeFor(ScriptObject.class));
+                addLocalDef(ARGUMENTS.symbolName());
+            }
         }
 
         initParameters(functionNode, body);
-        initScope(body);
-        initReturn(body);
+        initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL, Type.typeFor(ScriptObject.class));
+        initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL, Type.OBJECT);
+    }
 
-        if (functionNode.isProgram()) {
-            initFromPropertyMap(body);
-        } else if(!functionNode.isDeclared()) {
-            // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
 
-            if (functionNode.getSymbol() != null) {
-                // a temporary left over from an earlier pass when the function was lazy
-                assert functionNode.getSymbol().isTemp();
-                // remove it
-                functionNode.setSymbol(null);
-            }
-            final boolean anonymous = functionNode.isAnonymous();
-            final String name = anonymous ? null : functionNode.getIdent().getName();
-            if (anonymous || body.getExistingSymbol(name) != null) {
-                // The function is either anonymous, or another local identifier already trumps its name on entry:
-                // either it has the same name as one of its parameters, or is named "arguments" and also references the
-                // "arguments" identifier in its body.
-                ensureSymbol(functionNode, Type.typeFor(ScriptFunction.class), functionNode);
-            } else {
-                final Symbol selfSymbol = defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF, functionNode);
-                assert selfSymbol.isFunctionSelf();
-                newType(selfSymbol, Type.OBJECT);
-            }
-        }
-
-        /*
-         * This pushes all declarations (except for non-statements, i.e. for
-         * node temporaries) to the top of the function scope. This way we can
-         * get around problems like
-         *
-         * while (true) {
-         *   break;
-         *   if (true) {
-         *     var s;
-         *   }
-         * }
-         *
-         * to an arbitrary nesting depth.
-         *
-         * @see NASHORN-73
-         */
-
+    /**
+     * This pushes all declarations (except for non-statements, i.e. for
+     * node temporaries) to the top of the function scope. This way we can
+     * get around problems like
+     *
+     * while (true) {
+     *   break;
+     *   if (true) {
+     *     var s;
+     *   }
+     * }
+     *
+     * to an arbitrary nesting depth.
+     *
+     * see NASHORN-73
+     *
+     * @param functionNode the FunctionNode we are entering
+     * @param body the body of the FunctionNode we are entering
+     */
+    private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
         // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
         // in a separate step above) and "var" declarations in for loop initializers.
         body.accept(new NodeOperatorVisitor() {
@@ -220,27 +205,52 @@
             }
 
             @Override
-            public boolean enterVarNode(final VarNode varNode) {
-
+            public Node leaveVarNode(final VarNode varNode) {
                 // any declared symbols that aren't visited need to be typed as well, hence the list
-
                 if (varNode.isStatement()) {
-
-                    final IdentNode ident = varNode.getName();
-                    final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident));
+                    final IdentNode ident  = varNode.getName();
+                    final Symbol    symbol = defineSymbol(body, ident.getName(), IS_VAR);
                     functionNode.addDeclaredSymbol(symbol);
                     if (varNode.isFunctionDeclaration()) {
                         newType(symbol, FunctionNode.FUNCTION_TYPE);
                     }
+                    return varNode.setName((IdentNode)ident.setSymbol(getLexicalContext(), symbol));
                 }
-                return false;
+                return varNode;
             }
         });
     }
 
+    private void enterFunctionBody() {
+
+        final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+        final Block body = getLexicalContext().getCurrentBlock();
+
+        initFunctionWideVariables(functionNode, body);
+
+        if (functionNode.isProgram()) {
+            initFromPropertyMap(body);
+        } else if (!functionNode.isDeclared()) {
+            // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
+            assert functionNode.getSymbol() == null;
+
+            final boolean anonymous = functionNode.isAnonymous();
+            final String  name      = anonymous ? null : functionNode.getIdent().getName();
+            if (!(anonymous || body.getExistingSymbol(name) != null)) {
+                assert !anonymous && name != null;
+                newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT);
+            }
+        }
+
+        acceptDeclarations(functionNode, body);
+    }
+
     @Override
     public boolean enterBlock(final Block block) {
         start(block);
+        //ensure that we don't use information from a previous compile. This is very ugly TODO
+        //the symbols in the block should really be stateless
+        block.clearSymbols();
 
         if (getLexicalContext().isFunctionBody()) {
             enterFunctionBody();
@@ -257,14 +267,13 @@
     }
 
     @Override
-    public Node leaveCallNode(final CallNode callNode) {
-        ensureSymbol(callNode.getType(), callNode);
-        return end(callNode);
+    public boolean enterCallNode(final CallNode callNode) {
+        return start(callNode);
     }
 
     @Override
-    public boolean enterCallNode(final CallNode callNode) {
-        return start(callNode);
+    public Node leaveCallNode(final CallNode callNode) {
+        return end(ensureSymbol(callNode.getType(), callNode));
     }
 
     @Override
@@ -275,23 +284,31 @@
         start(catchNode);
 
         // define block-local exception variable
-        final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
+        final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET);
         newType(def, Type.OBJECT);
         addLocalDef(exception.getName());
 
         return true;
     }
 
+    @Override
+    public Node leaveCatchNode(final CatchNode catchNode) {
+        final IdentNode exception = catchNode.getException();
+        final Block  block        = getLexicalContext().getCurrentBlock();
+        final Symbol symbol       = findSymbol(block, exception.getName());
+        assert symbol != null;
+        return end(catchNode.setException((IdentNode)exception.setSymbol(getLexicalContext(), symbol)));
+    }
+
     /**
      * Declare the definition of a new symbol.
      *
      * @param name         Name of symbol.
      * @param symbolFlags  Symbol flags.
-     * @param node         Defining Node.
      *
      * @return Symbol for given name or null for redefinition.
      */
-    private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
+    private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
         int    flags  = symbolFlags;
         Symbol symbol = findSymbol(block, name); // Locate symbol.
 
@@ -337,7 +354,7 @@
 
             // Create and add to appropriate block.
             symbol = new Symbol(name, flags);
-            symbolBlock.putSymbol(name, symbol);
+            symbolBlock.putSymbol(getLexicalContext(), symbol);
 
             if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
                 symbol.setNeedsSlot(true);
@@ -346,10 +363,6 @@
             symbol.setFlags(flags);
         }
 
-        if (node != null) {
-            node.setSymbol(symbol);
-        }
-
         return symbol;
     }
 
@@ -357,30 +370,22 @@
     public boolean enterFunctionNode(final FunctionNode functionNode) {
         start(functionNode, false);
 
+        if (functionNode.isLazy()) {
+            return false;
+        }
+
+        //an outermost function in our lexical context that is not a program (runScript)
+        //is possible - it is a function being compiled lazily
         if (functionNode.isDeclared()) {
             final Iterator<Block> blocks = getLexicalContext().getBlocks();
             if (blocks.hasNext()) {
-                defineSymbol(
-                    blocks.next(),
-                    functionNode.getIdent().getName(),
-                    IS_VAR,
-                    functionNode);
-            } else {
-                // Q: What's an outermost function in a lexical context that is not a program?
-                // A: It's a function being compiled lazily!
-                assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram();
+                defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
             }
         }
 
-        if (functionNode.isLazy()) {
-            LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT");
-            ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode);
-            end(functionNode);
-            return false;
-        }
-
         returnTypes.push(functionNode.getReturnType());
         pushLocalsFunction();
+
         return true;
     }
 
@@ -390,8 +395,29 @@
 
         final LexicalContext lc = getLexicalContext();
 
+        final Block body = newFunctionNode.getBody();
+
+        //look for this function in the parent block
+        if (functionNode.isDeclared()) {
+            final Iterator<Block> blocks = getLexicalContext().getBlocks();
+            if (blocks.hasNext()) {
+                newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName()));
+            }
+        } else if (!functionNode.isProgram()) {
+            final boolean anonymous = functionNode.isAnonymous();
+            final String  name      = anonymous ? null : functionNode.getIdent().getName();
+            if (anonymous || body.getExistingSymbol(name) != null) {
+                newFunctionNode = (FunctionNode)Attr.ensureSymbol(lc, body, FunctionNode.FUNCTION_TYPE, newFunctionNode);
+            } else {
+                assert name != null;
+                final Symbol self = body.getExistingSymbol(name);
+                assert self != null && self.isFunctionSelf();
+                newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, body.getExistingSymbol(name));
+            }
+        }
+
         //unknown parameters are promoted to object type.
-        finalizeParameters(newFunctionNode);
+        newFunctionNode = finalizeParameters(newFunctionNode);
         finalizeTypes(newFunctionNode);
         for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
             if (symbol.getSymbolType().isUnknown()) {
@@ -400,8 +426,6 @@
             }
         }
 
-        final Block body = newFunctionNode.getBody();
-
         if (newFunctionNode.hasLazyChildren()) {
             //the final body has already been assigned as we have left the function node block body by now
             objectifySymbols(body);
@@ -409,7 +433,7 @@
 
         if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
             final IdentNode callee = compilerConstant(CALLEE);
-            final VarNode selfInit =
+            VarNode selfInit =
                 new VarNode(
                     newFunctionNode.getSource(),
                     newFunctionNode.getToken(),
@@ -420,7 +444,6 @@
             LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
 
             final List<Node> newStatements = new ArrayList<>();
-            newStatements.add(selfInit);
             assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
 
             final IdentNode name       = selfInit.getName();
@@ -428,9 +451,10 @@
 
             assert nameSymbol != null;
 
-            name.setSymbol(nameSymbol);
-            selfInit.setSymbol(nameSymbol);
+            selfInit = selfInit.setName((IdentNode)name.setSymbol(lc, nameSymbol));
+            selfInit = (VarNode)selfInit.setSymbol(lc, nameSymbol);
 
+            newStatements.add(selfInit);
             newStatements.addAll(body.getStatements());
             newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
         }
@@ -447,34 +471,32 @@
 
         end(newFunctionNode, false);
 
-        return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode));
+        return newFunctionNode;
     }
 
     @Override
     public Node leaveCONVERT(final UnaryNode unaryNode) {
         assert false : "There should be no convert operators in IR during Attribution";
-        end(unaryNode);
-        return unaryNode;
+        return end(unaryNode);
     }
 
     @Override
-    public boolean enterIdentNode(final IdentNode identNode) {
+    public Node leaveIdentNode(final IdentNode identNode) {
         final String name = identNode.getName();
 
         start(identNode);
 
+        final LexicalContext lc = getLexicalContext();
+
         if (identNode.isPropertyName()) {
             // assign a pseudo symbol to property name
             final Symbol pseudoSymbol = pseudoSymbol(name);
             LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
             LOG.unindent();
-            identNode.setSymbol(pseudoSymbol);
-            return false;
+            return identNode.setSymbol(lc, pseudoSymbol);
         }
 
-        final LexicalContext lc        = getLexicalContext();
-        final Block          block     = lc.getCurrentBlock();
-        final Symbol         oldSymbol = identNode.getSymbol();
+        final Block block = lc.getCurrentBlock();
 
         Symbol symbol = findSymbol(block, name);
 
@@ -495,12 +517,11 @@
                 }
             }
 
-            identNode.setSymbol(symbol);
             // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
             maybeForceScope(symbol);
         } else {
             LOG.info("No symbol exists. Declare undefined: ", symbol);
-            symbol = defineSymbol(block, name, IS_GLOBAL, identNode);
+            symbol = defineSymbol(block, name, IS_GLOBAL);
             // we have never seen this before, it can be undefined
             newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
             symbol.setCanBeUndefined();
@@ -509,14 +530,14 @@
 
         setBlockScope(name, symbol);
 
-        if (symbol != oldSymbol && !identNode.isInitializedHere()) {
+        if (symbol != null && !identNode.isInitializedHere()) {
             symbol.increaseUseCount();
         }
         addLocalUse(identNode.getName());
 
         end(identNode);
 
-        return false;
+        return identNode.setSymbol(lc, symbol);
     }
 
     /**
@@ -525,7 +546,7 @@
      * @param symbol the symbol that might be scoped
      */
     private void maybeForceScope(final Symbol symbol) {
-        if(!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
+        if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
             Symbol.setSymbolIsScope(getLexicalContext(), symbol);
         }
     }
@@ -612,11 +633,11 @@
     private Symbol findSymbol(final Block block, final String name) {
         // Search up block chain to locate symbol.
 
-        for(final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
+        for (final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
             // Find name.
             final Symbol symbol = blocks.next().getExistingSymbol(name);
             // If found then we are good.
-            if(symbol != null) {
+            if (symbol != null) {
                 return symbol;
             }
         }
@@ -625,39 +646,19 @@
 
     @Override
     public Node leaveIndexNode(final IndexNode indexNode) {
-        ensureSymbol(Type.OBJECT, indexNode); //TODO
-        return indexNode;
+        return end(ensureSymbol(Type.OBJECT, indexNode));
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public boolean enterLiteralNode(final LiteralNode literalNode) {
-        try {
-            start(literalNode);
-            assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
-
-            if (literalNode instanceof ArrayLiteralNode) {
-                final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
-                final Node[]           array            = arrayLiteralNode.getValue();
-
-                for (int i = 0; i < array.length; i++) {
-                    final Node element = array[i];
-                    if (element != null) {
-                        array[i] = element.accept(this);
-                    }
-                }
-                arrayLiteralNode.analyze();
-                //array literal node now has an element type and all elements are attributed
-            } else {
-                assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
-            }
-
-            getLexicalContext().getCurrentFunction().newLiteral(literalNode);
-        } finally {
-            end(literalNode);
+    public Node leaveLiteralNode(final LiteralNode literalNode) {
+        assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
+        assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
+        final Symbol symbol = new Symbol(getLexicalContext().getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType());
+        if (literalNode instanceof ArrayLiteralNode) {
+            ((ArrayLiteralNode)literalNode).analyze();
         }
-
-        return false;
+        return literalNode.setSymbol(getLexicalContext(), symbol);
     }
 
     @Override
@@ -667,18 +668,13 @@
 
     @Override
     public Node leaveObjectNode(final ObjectNode objectNode) {
-        ensureSymbol(Type.OBJECT, objectNode);
-        return end(objectNode);
+        return end(ensureSymbol(Type.OBJECT, objectNode));
     }
 
-    //TODO is this correct why not leave?
     @Override
-    public boolean enterPropertyNode(final PropertyNode propertyNode) {
+    public Node leavePropertyNode(final PropertyNode propertyNode) {
         // assign a pseudo symbol to property name, see NASHORN-710
-        start(propertyNode);
-        propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
-        end(propertyNode);
-        return true;
+        return propertyNode.setSymbol(getLexicalContext(), new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
     }
 
     @Override
@@ -763,12 +759,9 @@
         final IdentNode ident = varNode.getName();
         final String    name  = ident.getName();
 
-        final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident);
+        final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR);
         assert symbol != null;
 
-        LOG.info("VarNode ", varNode, " set symbol ", symbol);
-        varNode.setSymbol(symbol);
-
         // NASHORN-467 - use before definition of vars - conservative
         if (isLocalUse(ident.getName())) {
             newType(symbol, Type.OBJECT);
@@ -780,22 +773,31 @@
 
     @Override
     public Node leaveVarNode(final VarNode varNode) {
-        final Node      init  = varNode.getInit();
-        final IdentNode ident = varNode.getName();
+        VarNode newVarNode = varNode;
+
+        final Node      init  = newVarNode.getInit();
+        final IdentNode ident = newVarNode.getName();
         final String    name  = ident.getName();
 
         if (init == null) {
             // var x; with no init will be treated like a use of x by
-            // visit(IdentNode) unless we remove the name
-            // from the localdef list.
+            // leaveIdentNode unless we remove the name from the localdef list.
             removeLocalDef(name);
-            return varNode;
+            return newVarNode;
         }
 
         addLocalDef(name);
 
-        final Symbol  symbol   = varNode.getSymbol();
-        final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56
+        final LexicalContext lc = getLexicalContext();
+        final Symbol  symbol = findSymbol(lc.getCurrentBlock(), ident.getName());
+        assert symbol != null;
+
+        final IdentNode newIdent = (IdentNode)ident.setSymbol(lc, symbol);
+
+        newVarNode = newVarNode.setName(newIdent);
+        newVarNode = (VarNode)newVarNode.setSymbol(lc, symbol);
+
+        final boolean isScript = lc.getDefiningFunction(symbol).isProgram(); //see NASHORN-56
         if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
             // Forbid integers as local vars for now as we have no way to treat them as undefined
             newType(symbol, init.getType());
@@ -803,25 +805,19 @@
             newType(symbol, Type.OBJECT);
         }
 
-        assert varNode.hasType() : varNode;
+        assert newVarNode.hasType() : newVarNode + " has no type";
 
-        end(varNode);
-
-        return varNode;
+        return end(newVarNode);
     }
 
     @Override
     public Node leaveADD(final UnaryNode unaryNode) {
-        ensureSymbol(arithType(), unaryNode);
-        end(unaryNode);
-        return unaryNode;
+        return end(ensureSymbol(arithType(), unaryNode));
     }
 
     @Override
     public Node leaveBIT_NOT(final UnaryNode unaryNode) {
-        ensureSymbol(Type.INT, unaryNode);
-        end(unaryNode);
-        return unaryNode;
+        return end(ensureSymbol(Type.INT, unaryNode));
     }
 
     @Override
@@ -830,9 +826,7 @@
         ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
         final Type type = arithType();
         newType(unaryNode.rhs().getSymbol(), type);
-        ensureSymbol(type, unaryNode);
-        end(unaryNode);
-        return unaryNode;
+        return end(ensureSymbol(type, unaryNode));
     }
 
     @Override
@@ -908,23 +902,25 @@
 
     @Override
     public Node leaveNEW(final UnaryNode unaryNode) {
-        ensureSymbol(Type.OBJECT, unaryNode);
-        end(unaryNode);
-        return unaryNode;
+        return end(ensureSymbol(Type.OBJECT, unaryNode));
     }
 
     @Override
     public Node leaveNOT(final UnaryNode unaryNode) {
-        ensureSymbol(Type.BOOLEAN, unaryNode);
-        end(unaryNode);
-        return unaryNode;
+        return end(ensureSymbol(Type.BOOLEAN, unaryNode));
     }
 
     private IdentNode compilerConstant(CompilerConstants cc) {
         final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
-        final IdentNode node = new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
-        node.setSymbol(functionNode.compilerConstant(cc));
-        return node;
+        return (IdentNode)
+            new IdentNode(
+                functionNode.getSource(),
+                functionNode.getToken(),
+                functionNode.getFinish(),
+                cc.symbolName()).
+                setSymbol(
+                    getLexicalContext(),
+                    functionNode.compilerConstant(cc));
     }
 
     @Override
@@ -952,15 +948,12 @@
 
     @Override
     public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
-        ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode);
-        return runtimeNode;
+        return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode));
     }
 
     @Override
     public Node leaveSUB(final UnaryNode unaryNode) {
-        ensureSymbol(arithType(), unaryNode);
-        end(unaryNode);
-        return unaryNode;
+        return end(ensureSymbol(arithType(), unaryNode));
     }
 
     @Override
@@ -982,18 +975,16 @@
 
         ensureTypeNotUnknown(lhs);
         ensureTypeNotUnknown(rhs);
-        ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
-
-        end(binaryNode);
-
-        return binaryNode;
+        //even if we are adding two known types, this can overflow. i.e.
+        //int and number -> number.
+        //int and int are also number though.
+        //something and object is object
+        return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode));
     }
 
     @Override
     public Node leaveAND(final BinaryNode binaryNode) {
-        ensureSymbol(Type.OBJECT, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(ensureSymbol(Type.OBJECT, binaryNode));
     }
 
     /**
@@ -1013,8 +1004,7 @@
             Symbol symbol = findSymbol(block, name);
 
             if (symbol == null) {
-                symbol = defineSymbol(block, name, IS_GLOBAL, ident);
-                binaryNode.setSymbol(symbol);
+                symbol = defineSymbol(block, name, IS_GLOBAL);
             } else {
                 maybeForceScope(symbol);
             }
@@ -1025,6 +1015,31 @@
         return true;
     }
 
+
+    /**
+     * This assign helper is called after an assignment, when all children of
+     * the assign has been processed. It fixes the types and recursively makes
+     * sure that everyhing has slots that should have them in the chain.
+     *
+     * @param binaryNode assignment node
+     */
+    private Node leaveAssignmentNode(final BinaryNode binaryNode) {
+        BinaryNode newBinaryNode = binaryNode;
+
+        final Node lhs = binaryNode.lhs();
+        final Node rhs = binaryNode.rhs();
+        final Type type;
+
+        if (rhs.getType().isNumeric()) {
+            type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+        } else {
+            type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
+        }
+
+        newType(lhs.getSymbol(), type);
+        return end(ensureSymbol(type, newBinaryNode));
+    }
+
     private boolean isLocal(FunctionNode function, Symbol symbol) {
         final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol);
         // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
@@ -1173,14 +1188,12 @@
 
     @Override
     public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
-        ensureSymbol(binaryNode.rhs().getType(), binaryNode);
-        return binaryNode;
+        return end(ensureSymbol(binaryNode.rhs().getType(), binaryNode));
     }
 
     @Override
     public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
-        ensureSymbol(binaryNode.lhs().getType(), binaryNode);
-        return binaryNode;
+        return end(ensureSymbol(binaryNode.lhs().getType(), binaryNode));
     }
 
     @Override
@@ -1189,15 +1202,10 @@
     }
 
     private Node leaveCmp(final BinaryNode binaryNode) {
-        final Node lhs = binaryNode.lhs();
-        final Node rhs = binaryNode.rhs();
+        ensureTypeNotUnknown(binaryNode.lhs());
+        ensureTypeNotUnknown(binaryNode.rhs());
 
-        ensureSymbol(Type.BOOLEAN, binaryNode);
-        ensureTypeNotUnknown(lhs);
-        ensureTypeNotUnknown(rhs);
-
-        end(binaryNode);
-        return binaryNode;
+        return end(ensureSymbol(Type.BOOLEAN, binaryNode));
     }
 
     private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
@@ -1207,11 +1215,9 @@
         // as, say, an int : function(x) { return x & 4711 }, and x is not defined in
         // the function. to make this work, uncomment the following two type inferences
         // and debug.
-
         //newType(binaryNode.lhs().getSymbol(), operandType);
         //newType(binaryNode.rhs().getSymbol(), operandType);
-        ensureSymbol(destType, binaryNode);
-        return binaryNode;
+        return ensureSymbol(destType, binaryNode);
     }
 
     private Node coerce(final BinaryNode binaryNode, final Type type) {
@@ -1295,9 +1301,7 @@
 
     @Override
     public Node leaveOR(final BinaryNode binaryNode) {
-        ensureSymbol(Type.OBJECT, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(ensureSymbol(Type.OBJECT, binaryNode));
     }
 
     @Override
@@ -1346,50 +1350,13 @@
         ensureTypeNotUnknown(rhs);
 
         final Type type = Type.widest(lhs.getType(), rhs.getType());
-        ensureSymbol(type, ternaryNode);
-
-        end(ternaryNode);
-        assert ternaryNode.getSymbol() != null;
-
-        return ternaryNode;
+        return end(ensureSymbol(type, ternaryNode));
     }
 
-    private void initThis(final Block block) {
-        final Symbol thisSymbol = defineSymbol(block, THIS.symbolName(), IS_PARAM | IS_THIS, null);
-        newType(thisSymbol, Type.OBJECT);
-        thisSymbol.setNeedsSlot(true);
-    }
-
-    private void initScope(final Block block) {
-        final Symbol scopeSymbol = defineSymbol(block, SCOPE.symbolName(), IS_VAR | IS_INTERNAL, null);
-        newType(scopeSymbol, Type.typeFor(ScriptObject.class));
-        scopeSymbol.setNeedsSlot(true);
-    }
-
-    private void initReturn(final Block block) {
-        final Symbol returnSymbol = defineSymbol(block, RETURN.symbolName(), IS_VAR | IS_INTERNAL, null);
-        newType(returnSymbol, Type.OBJECT);
-        returnSymbol.setNeedsSlot(true);
-        //return symbol is always object as it's the __return__ thing. What returnType is is another matter though
-    }
-
-    private void initVarArg(final Block block, final boolean needsArguments) {
-        final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null);
-        varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
-        varArgsSymbol.setNeedsSlot(true);
-
-        if (needsArguments) {
-            final Symbol    argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null);
-            newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
-            argumentsSymbol.setNeedsSlot(true);
-            addLocalDef(ARGUMENTS.symbolName());
-        }
-    }
-
-    private void initCallee(final Block block) {
-        final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null);
-        newType(calleeSymbol, FunctionNode.FUNCTION_TYPE);
-        calleeSymbol.setNeedsSlot(true);
+    private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) {
+        final Symbol symbol = defineSymbol(block, cc.symbolName(), flags);
+        newType(symbol, type);
+        symbol.setNeedsSlot(true);
     }
 
     /**
@@ -1399,19 +1366,28 @@
      * @param functionNode the function node
      */
     private void initParameters(final FunctionNode functionNode, final Block body) {
+        int pos = 0;
         for (final IdentNode param : functionNode.getParameters()) {
             addLocalDef(param.getName());
-            final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM, param);
+
+            final Type callSiteParamType = functionNode.getHints().getParameterType(pos);
+            int flags = IS_PARAM;
+            if (callSiteParamType != null) {
+                LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that.");
+                flags |= Symbol.IS_SPECIALIZED_PARAM;
+            }
+
+            final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
+            assert paramSymbol != null;
+
             if (paramSymbol != null) {
-                final Type callSiteParamType = functionNode.getSpecializedType(param);
-                if (callSiteParamType != null) {
-                    LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that.");
-                }
                 newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
             }
 
-            LOG.info("Initialized param ", paramSymbol);
+            LOG.info("Initialized param ", pos, "=", paramSymbol);
+            pos++;
         }
+
     }
 
     /**
@@ -1420,14 +1396,19 @@
      *
      * @param functionNode functionNode
      */
-    private static void finalizeParameters(final FunctionNode functionNode) {
+    private FunctionNode finalizeParameters(final FunctionNode functionNode) {
+        final List<IdentNode> newParams = new ArrayList<>();
         final boolean isVarArg = functionNode.isVarArg();
 
-        for (final IdentNode ident : functionNode.getParameters()) {
-            final Symbol paramSymbol = ident.getSymbol();
+        int pos = 0;
+        for (final IdentNode param : functionNode.getParameters()) {
+            final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName());
+            assert paramSymbol != null;
+            assert paramSymbol.isParam();
+            newParams.add((IdentNode)param.setSymbol(getLexicalContext(), paramSymbol));
 
             assert paramSymbol != null;
-            Type type = functionNode.getSpecializedType(ident);
+            Type type = functionNode.getHints().getParameterType(pos);
             if (type == null) {
                 type = Type.OBJECT;
             }
@@ -1436,7 +1417,7 @@
             // this function, we can tell the runtime system that no matter what the
             // call site is, use this information. TODO
             if (!paramSymbol.getSymbolType().isObject()) {
-                LOG.finest("Parameter ", ident, " could profit from specialization to ", paramSymbol.getSymbolType());
+                LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType());
             }
 
             newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
@@ -1445,7 +1426,11 @@
             if (isVarArg) {
                 paramSymbol.setNeedsSlot(false);
             }
+
+            pos++;
         }
+
+        return functionNode.setParameters(getLexicalContext(), newParams);
     }
 
     /**
@@ -1459,7 +1444,7 @@
 
         for (final Property property : map.getProperties()) {
             final String key    = property.getKey();
-            final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null);
+            final Symbol symbol = defineSymbol(block, key, IS_GLOBAL);
             newType(symbol, Type.OBJECT);
             LOG.info("Added global symbol from property map ", symbol);
         }
@@ -1498,7 +1483,7 @@
          * objects as parameters, for example +, but not *, which is known
          * to coerce types into doubles
          */
-        if (node.getType().isUnknown() || symbol.isParam()) {
+        if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) {
             newType(symbol, Type.OBJECT);
             symbol.setCanBeUndefined();
          }
@@ -1614,29 +1599,6 @@
         } while (!changed.isEmpty());
     }
 
-    /**
-     * This assign helper is called after an assignment, when all children of
-     * the assign has been processed. It fixes the types and recursively makes
-     * sure that everyhing has slots that should have them in the chain.
-     *
-     * @param binaryNode assignment node
-     */
-    private Node leaveAssignmentNode(final BinaryNode binaryNode) {
-        final Node lhs = binaryNode.lhs();
-        final Node rhs = binaryNode.rhs();
-
-        final Type type;
-        if (rhs.getType().isNumeric()) {
-            type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
-        } else {
-            type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
-        }
-        ensureSymbol(type, binaryNode);
-        newType(lhs.getSymbol(), type);
-        end(binaryNode);
-        return binaryNode;
-    }
-
     private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
         return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType());
     }
@@ -1646,25 +1608,20 @@
         final Node lhs = binaryNode.lhs();
 
         newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
-        ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
+//        ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
 
         ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
 
-        end(binaryNode);
-        return binaryNode;
+        return end(ensureSymbol(destType, binaryNode));
     }
 
-    private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) {
-        LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type);
-        return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node);
-    }
-
-    private Symbol ensureSymbol(final Type type, final Node node) {
-        return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node);
+    private Node ensureSymbol(final Type type, final Node node) {
+        LOG.info("New TEMPORARY added to ", getLexicalContext().getCurrentFunction().getName(), " type=", type);
+        return Attr.ensureSymbol(getLexicalContext(), getLexicalContext().getCurrentBlock(), type, node);
     }
 
     private Symbol newInternal(final String name, final Type type) {
-        final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null);
+        final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
         iter.setType(type); // NASHORN-73
         return iter;
     }
@@ -1721,6 +1678,17 @@
         localUses.peek().add(name);
     }
 
+    static Node ensureSymbol(final LexicalContext lc, final Block block, final Type type, final Node node) {
+        Symbol symbol = node.getSymbol();
+        if (symbol != null) {
+            return node;
+        }
+        final String uname = lc.getCurrentFunction().uniqueName(TEMP_PREFIX.symbolName());
+        symbol = new Symbol(uname, IS_TEMP, type);
+        block.putSymbol(lc, symbol);
+        return node.setSymbol(lc, symbol);
+    }
+
     /**
      * Pessimistically promote all symbols in current function node to Object types
      * This is done when the function contains unevaluated black boxes such as
@@ -1731,8 +1699,7 @@
     private static void objectifySymbols(final Block body) {
         body.accept(new NodeVisitor() {
             private void toObject(final Block block) {
-                for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
-                    final Symbol symbol = iter.next();
+                for (final Symbol symbol : block.getSymbols()) {
                     if (!symbol.isTemp()) {
                         newType(symbol, Type.OBJECT);
                     }
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri May 03 15:33:54 2013 +0200
@@ -568,8 +568,7 @@
      * @param block block containing symbols.
      */
     private void symbolInfo(final Block block) {
-        for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
-            final Symbol symbol = iter.next();
+        for (final Symbol symbol : block.getSymbols()) {
             if (symbol.hasSlot()) {
                 method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
             }
@@ -937,11 +936,10 @@
 
     private static int assignSlots(final Block block, final int firstSlot) {
         int nextSlot = firstSlot;
-        for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
-            final Symbol next = iter.next();
-            if (next.hasSlot()) {
-                next.setSlot(nextSlot);
-                nextSlot += next.slotCount();
+        for (final Symbol symbol : block.getSymbols()) {
+            if (symbol.hasSlot()) {
+                symbol.setSlot(nextSlot);
+                nextSlot += symbol.slotCount();
             }
         }
         return nextSlot;
@@ -1002,10 +1000,7 @@
 
             final boolean hasArguments = function.needsArguments();
 
-            final Iterator<Symbol> symbols = block.symbolIterator();
-
-            while (symbols.hasNext()) {
-                final Symbol symbol = symbols.next();
+            for (final Symbol symbol : block.getSymbols()) {
 
                 if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
                     continue;
@@ -1076,12 +1071,7 @@
                 }
             }
 
-            final Iterator<Symbol> iter = block.symbolIterator();
-            final List<Symbol> symbols = new ArrayList<>();
-            while (iter.hasNext()) {
-                symbols.add(iter.next());
-            }
-            initSymbols(symbols);
+            initSymbols(block.getSymbols());
         }
 
         // Debugging: print symbols? @see --print-symbols flag
@@ -2364,7 +2354,6 @@
     public boolean enterDISCARD(final UnaryNode unaryNode) {
         final Node rhs = unaryNode.rhs();
 
-       // System.err.println("**** Enter discard " + unaryNode);
         discard.push(rhs);
         load(rhs);
 
@@ -2373,7 +2362,7 @@
             method.pop();
             discard.pop();
         }
-       // System.err.println("**** Leave discard " + unaryNode);
+
         return false;
     }
 
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Fri May 03 15:33:54 2013 +0200
@@ -42,7 +42,7 @@
      */
     LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
         @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn0) {
+        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
 
             /*
              * For lazy compilation, we might be given a node previously marked
@@ -58,8 +58,7 @@
              * function from a trampoline
              */
 
-            final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
-            assert outermostFunctionNode == fn0;
+            final FunctionNode outermostFunctionNode = fn;
 
             final Set<FunctionNode> neverLazy = new HashSet<>();
             final Set<FunctionNode> lazy      = new HashSet<>();
@@ -172,20 +171,26 @@
     ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
         @Override
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            return (FunctionNode)initReturnTypes(fn).accept(new Attr());
+            return (FunctionNode)enterAttr(fn).accept(new Attr());
         }
 
         /**
          * Pessimistically set all lazy functions' return types to Object
+         * and the function symbols to object
          * @param functionNode node where to start iterating
          */
-        private FunctionNode initReturnTypes(final FunctionNode functionNode) {
+        private FunctionNode enterAttr(final FunctionNode functionNode) {
             return (FunctionNode)functionNode.accept(new NodeVisitor() {
                 @Override
                 public Node leaveFunctionNode(final FunctionNode node) {
-                    return node.isLazy() ?
-                           node.setReturnType(getLexicalContext(), Type.OBJECT) :
-                           node.setReturnType(getLexicalContext(), Type.UNKNOWN);
+                    final LexicalContext lc = getLexicalContext();
+                    if (node.isLazy()) {
+                        FunctionNode newNode = node.setReturnType(getLexicalContext(), Type.OBJECT);
+                        return Attr.ensureSymbol(lc, lc.getCurrentBlock(), Type.OBJECT, newNode);
+                    }
+                    //node may have a reference here that needs to be nulled if it was referred to by
+                    //its outer context, if it is lazy and not attributed
+                    return node.setReturnType(lc, Type.UNKNOWN).setSymbol(lc, null);
                 }
             });
         }
@@ -207,6 +212,7 @@
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
             final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
 
+//            assert fn.isProgram() ;
             final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
 
             assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
@@ -216,15 +222,6 @@
                 compiler.setStrictMode(true);
             }
 
-            /*
-            newFunctionNode.accept(new NodeVisitor() {
-                @Override
-                public boolean enterFunctionNode(final FunctionNode functionNode) {
-                    assert functionNode.getCompileUnit() != null : functionNode.getName() + " " + Debug.id(functionNode) + " has no compile unit";
-                    return true;
-                }
-            });*/
-
             return newFunctionNode;
         }
 
--- a/src/jdk/nashorn/internal/codegen/Compiler.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Fri May 03 15:33:54 2013 +0200
@@ -77,6 +77,8 @@
     /** Name of the objects package */
     public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
 
+    private Source source;
+
     private final Map<String, byte[]> bytecode;
 
     private final Set<CompileUnit> compileUnits;
@@ -87,12 +89,10 @@
 
     private final ScriptEnvironment env;
 
-    private final String scriptName;
+    private String scriptName;
 
     private boolean strict;
 
-    private FunctionNode functionNode;
-
     private CodeInstaller<ScriptEnvironment> installer;
 
     /** logger for compiler, trampolines, splits and related code generation events
@@ -168,6 +168,41 @@
     }
 
     /**
+     * Environment information known to the compile, e.g. params
+     */
+    public static class Hints {
+        private final Type[] paramTypes;
+
+        /** singleton empty hints */
+        public static final Hints EMPTY = new Hints();
+
+        private Hints() {
+            this.paramTypes = null;
+        }
+
+        /**
+         * Constructor
+         * @param paramTypes known parameter types for this callsite
+         */
+        public Hints(final Type[] paramTypes) {
+            this.paramTypes = paramTypes;
+        }
+
+        /**
+         * Get the parameter type for this parameter position, or
+         * null if now known
+         * @param pos position
+         * @return parameter type for this callsite if known
+         */
+        public Type getParameterType(final int pos) {
+            if (paramTypes != null && pos < paramTypes.length) {
+                return paramTypes[pos];
+            }
+            return null;
+        }
+    }
+
+    /**
      * Standard (non-lazy) compilation, that basically will take an entire script
      * and JIT it at once. This can lead to long startup time and fewer type
      * specializations
@@ -207,21 +242,22 @@
      * @param strict       should this compilation use strict mode semantics
      */
     //TODO support an array of FunctionNodes for batch lazy compilation
-    Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) {
+    Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final CompilationSequence sequence, final boolean strict) {
         this.env           = env;
-        this.functionNode  = functionNode;
         this.sequence      = sequence;
         this.installer     = installer;
-        this.strict        = strict || functionNode.isStrict();
         this.constantData  = new ConstantData();
         this.compileUnits  = new HashSet<>();
         this.bytecode      = new HashMap<>();
+    }
 
+    private void initCompiler(final FunctionNode functionNode) {
+        this.strict        = strict || functionNode.isStrict();
         final StringBuilder sb = new StringBuilder();
         sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
                 append('$').
                 append(safeSourceName(functionNode.getSource()));
-
+        this.source = functionNode.getSource();
         this.scriptName = sb.toString();
     }
 
@@ -229,52 +265,43 @@
      * Constructor
      *
      * @param installer    code installer
-     * @param functionNode function node (in any available {@link CompilationState}) to compile
      * @param strict       should this compilation use strict mode semantics
      */
-    public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final boolean strict) {
-        this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), strict);
+    public Compiler(final CodeInstaller<ScriptEnvironment> installer, final boolean strict) {
+        this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), strict);
     }
 
     /**
      * Constructor - compilation will use the same strict semantics as in script environment
      *
      * @param installer    code installer
-     * @param functionNode function node (in any available {@link CompilationState}) to compile
      */
-    public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode) {
-        this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
+    public Compiler(final CodeInstaller<ScriptEnvironment> installer) {
+        this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
     }
 
     /**
      * Constructor - compilation needs no installer, but uses a script environment
      * Used in "compile only" scenarios
      * @param env a script environment
-     * @param functionNode functionNode to compile
      */
-    public Compiler(final ScriptEnvironment env, final FunctionNode functionNode) {
-        this(env, null, functionNode, sequence(env._lazy_compilation), env._strict);
+    public Compiler(final ScriptEnvironment env) {
+        this(env, null, sequence(env._lazy_compilation), env._strict);
     }
 
     /**
      * Execute the compilation this Compiler was created with
-     * @params param types if known, for specialization
+     * @param functionNode function node to compile from its current state
      * @throws CompilationException if something goes wrong
      * @return function node that results from code transforms
      */
-    public FunctionNode compile() throws CompilationException {
-        return compile(null);
-    }
+    public FunctionNode compile(final FunctionNode functionNode) throws CompilationException {
+        FunctionNode newFunctionNode = functionNode;
 
-    /**
-     * Execute the compilation this Compiler was created with
-     * @param paramTypes param types if known, for specialization
-     * @throws CompilationException if something goes wrong
-     * @return function node that results from code transforms
-     */
-    public FunctionNode compile(final Class<?> paramTypes) throws CompilationException {
+        initCompiler(newFunctionNode); //TODO move this state into functionnode?
+
         for (final String reservedName : RESERVED_NAMES) {
-            functionNode.uniqueName(reservedName);
+            newFunctionNode.uniqueName(reservedName);
         }
 
         final boolean fine = !LOG.levelAbove(Level.FINE);
@@ -283,7 +310,7 @@
         long time = 0L;
 
         for (final CompilationPhase phase : sequence) {
-            this.functionNode = phase.apply(this, functionNode);
+            newFunctionNode = phase.apply(this, newFunctionNode);
 
             final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
             time += duration;
@@ -293,7 +320,7 @@
 
                 sb.append(phase.toString()).
                     append(" done for function '").
-                    append(functionNode.getName()).
+                    append(newFunctionNode.getName()).
                     append('\'');
 
                 if (duration > 0L) {
@@ -309,7 +336,7 @@
         if (info) {
             final StringBuilder sb = new StringBuilder();
             sb.append("Compile job for '").
-                append(functionNode.getName()).
+                append(newFunctionNode.getName()).
                 append("' finished");
 
             if (time > 0L) {
@@ -321,16 +348,15 @@
             LOG.info(sb);
         }
 
-        return functionNode;
+        return newFunctionNode;
     }
 
-    private Class<?> install(final String className, final byte[] code) {
+    private Class<?> install(final FunctionNode functionNode, final String className, final byte[] code) {
         LOG.fine("Installing class ", className);
 
         final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
 
         try {
-            final Source   source    = getSource();
             final Object[] constants = getConstantData().toArray();
             // Need doPrivileged because these fields are private
             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@@ -355,9 +381,10 @@
 
     /**
      * Install compiled classes into a given loader
+     * @param functionNode function node to install - must be in {@link CompilationState#EMITTED} state
      * @return root script class - if there are several compile units they will also be installed
      */
-    public Class<?> install() {
+    public Class<?> install(final FunctionNode functionNode) {
         final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
 
         assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
@@ -366,7 +393,7 @@
 
         final String   rootClassName = firstCompileUnitName();
         final byte[]   rootByteCode  = bytecode.get(rootClassName);
-        final Class<?> rootClass     = install(rootClassName, rootByteCode);
+        final Class<?> rootClass     = install(functionNode, rootClassName, rootByteCode);
 
         int length = rootByteCode.length;
 
@@ -380,7 +407,7 @@
             final byte[] code = entry.getValue();
             length += code.length;
 
-            installedClasses.put(className, install(className, code));
+            installedClasses.put(className, install(functionNode, className, code));
         }
 
         for (final CompileUnit unit : compileUnits) {
@@ -430,10 +457,6 @@
         this.strict = strict;
     }
 
-    FunctionNode getFunctionNode() {
-        return functionNode;
-    }
-
     ConstantData getConstantData() {
         return constantData;
     }
@@ -442,10 +465,6 @@
         return installer;
     }
 
-    Source getSource() {
-        return functionNode.getSource();
-    }
-
     void addClass(final String name, final byte[] code) {
         bytecode.put(name, code);
     }
@@ -496,7 +515,7 @@
     }
 
     private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
-        final ClassEmitter classEmitter = new ClassEmitter(env, functionNode.getSource().getName(), unitClassName, strict);
+        final ClassEmitter classEmitter = new ClassEmitter(env, source.getName(), unitClassName, strict);
         final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
 
         classEmitter.begin();
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Fri May 03 15:33:54 2013 +0200
@@ -30,7 +30,6 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
@@ -354,13 +353,6 @@
         return true;
     }
 
-    /*
-    @Override
-    public Node leaveBlock(final Block block) {
-        final LexicalContext lc = getLexicalContext();
-        return block;//.setFlag(lc, lc.getFlags(block));
-    }*/
-
     @Override
     public Node leaveCatchNode(final CatchNode catchNode) {
         final Node exceptionCondition = catchNode.getExceptionCondition();
@@ -551,8 +543,7 @@
         final boolean        allVarsInScope = functionNode.allVarsInScope();
         final boolean        isVarArg       = functionNode.isVarArg();
 
-        for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
-            final Symbol symbol = iter.next();
+        for (final Symbol symbol : block.getSymbols()) {
             if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
                 continue;
             }
@@ -812,14 +803,12 @@
 
         LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
 
+        assert !node.isTerminal();
+
         final LexicalContext lc = getLexicalContext();
         //This is the only place in this file that can create new temporaries
         //FinalizeTypes may not introduce ANY node that is not a conversion.
-        lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
-
-        assert !node.isTerminal();
-
-        return resultNode;
+        return Attr.ensureSymbol(lc, lc.getCurrentBlock(), to, resultNode);
     }
 
     private static Node discard(final Node node) {
@@ -905,7 +894,7 @@
 
             if (literalNode != null) {
                 //inherit literal symbol for attr.
-                literalNode.setSymbol(parent.getSymbol());
+                literalNode = (LiteralNode<?>)literalNode.setSymbol(null, parent.getSymbol());
             }
 
             return literalNode;
--- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Fri May 03 15:33:54 2013 +0200
@@ -27,7 +27,6 @@
 
 import java.util.List;
 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_PADDING;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_ROUNDING;
 import jdk.nashorn.internal.ir.Symbol;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.PropertyMap;
--- a/src/jdk/nashorn/internal/codegen/Splitter.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/codegen/Splitter.java	Fri May 03 15:33:54 2013 +0200
@@ -75,7 +75,7 @@
      */
     public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
         this.compiler             = compiler;
-        this.outermost = functionNode;
+        this.outermost            = functionNode;
         this.outermostCompileUnit = outermostCompileUnit;
     }
 
@@ -95,7 +95,7 @@
         final LexicalContext lc = getLexicalContext();
 
         long weight = WeighNodes.weigh(functionNode);
-        final boolean top = compiler.getFunctionNode() == outermost;
+        final boolean top = fn.isProgram(); //compiler.getFunctionNode() == outermost;
 
         if (weight >= SPLIT_THRESHOLD) {
             LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
@@ -273,7 +273,9 @@
             return literal;
         }
 
-        getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
+        final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+
+        getLexicalContext().setFlag(functionNode, FunctionNode.IS_SPLIT);
 
         if (literal instanceof ArrayLiteralNode) {
             final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
--- a/src/jdk/nashorn/internal/ir/Block.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/Block.java	Fri May 03 15:33:54 2013 +0200
@@ -30,7 +30,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -104,18 +103,26 @@
         this(source, token, finish, statements.toArray(new Node[statements.size()]));
     }
 
-    private Block(final Block block, final int finish, final List<Node> statements, final int flags) {
+    private Block(final Block block, final int finish, final List<Node> statements, final int flags, final Map<String, Symbol> symbols) {
         super(block);
         this.statements = statements;
         this.flags      = flags;
-        this.symbols    = block.symbols; //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
+        this.symbols    = new LinkedHashMap<>(symbols); //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
         this.entryLabel = new Label(block.entryLabel);
-        this.finish = finish;
+        this.finish     = finish;
+    }
+
+    /**
+     * Clear the symbols in a block
+     * TODO: make this immutable
+     */
+    public void clearSymbols() {
+        symbols.clear();
     }
 
     @Override
     public Node ensureUniqueLabels(final LexicalContext lc) {
-        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
+        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
     }
 
     /**
@@ -137,15 +144,15 @@
      * Get an iterator for all the symbols defined in this block
      * @return symbol iterator
      */
-    public Iterator<Symbol> symbolIterator() {
-        return symbols.values().iterator();
+    public List<Symbol> getSymbols() {
+        return Collections.unmodifiableList(new ArrayList<>(symbols.values()));
     }
 
     /**
      * Retrieves an existing symbol defined in the current block.
      * @param name the name of the symbol
      * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't
-     * define a symbol with this name.
+     * define a symbol with this name.T
      */
     public Symbol getExistingSymbol(final String name) {
         return symbols.get(name);
@@ -241,17 +248,17 @@
         if (!statements.isEmpty()) {
             lastFinish = statements.get(statements.size() - 1).getFinish();
         }
-        return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags));
+        return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols));
     }
 
     /**
      * Add or overwrite an existing symbol in the block
      *
-     * @param name   name of symbol
+     * @param lc     get lexical context
      * @param symbol symbol
      */
-    public void putSymbol(final String name, final Symbol symbol) {
-        symbols.put(name, symbol);
+    public void putSymbol(final LexicalContext lc, final Symbol symbol) {
+        symbols.put(symbol.getName(), symbol);
     }
 
     /**
@@ -268,7 +275,7 @@
         if (this.flags == flags) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
+        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
     }
 
     @Override
@@ -296,7 +303,7 @@
             return this;
         }
 
-        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE));
+        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols));
     }
 
     /**
@@ -306,13 +313,11 @@
      * @return next slot
      */
     public int nextSlot() {
-        final Iterator<Symbol> iter = symbolIterator();
         int next = 0;
-        while (iter.hasNext()) {
-        final Symbol symbol = iter.next();
-        if (symbol.hasSlot()) {
-            next += symbol.slotCount();
-        }
+        for (final Symbol symbol : getSymbols()) {
+            if (symbol.hasSlot()) {
+                next += symbol.slotCount();
+            }
         }
         return next;
     }
--- a/src/jdk/nashorn/internal/ir/CatchNode.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/CatchNode.java	Fri May 03 15:33:54 2013 +0200
@@ -138,7 +138,12 @@
         return body;
     }
 
-    private CatchNode setException(final IdentNode exception) {
+    /**
+     * Resets the exception of a catch block
+     * @param exception new exception
+     * @return new catch node if changed, same otherwise
+     */
+    public CatchNode setException(final IdentNode exception) {
         if (this.exception == exception) {
             return this;
         }
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Fri May 03 15:33:54 2013 +0200
@@ -25,16 +25,12 @@
 
 package jdk.nashorn.internal.ir;
 
-import static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
-import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
-import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
-
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -95,6 +91,10 @@
     @Ignore
     private final IdentNode ident;
 
+    /** Parsed version of functionNode */
+    @Ignore
+    private final FunctionNode snapshot;
+
     /** The body of the function node */
     private final Block body;
 
@@ -127,6 +127,9 @@
     @Ignore
     private final EnumSet<CompilationState> compilationState;
 
+    @Ignore
+    private final Compiler.Hints hints;
+
     /** Function flags. */
     private final int flags;
 
@@ -176,6 +179,9 @@
     /** Does this function have nested declarations? */
     public static final int HAS_FUNCTION_DECLARATIONS   = 1 << 13;
 
+    /** Can this function be specialized? */
+    public static final int CAN_SPECIALIZE              = 1 << 14;
+
     /** Does this function or any nested functions contain an eval? */
     private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
 
@@ -219,37 +225,52 @@
         final int flags) {
         super(source, token, finish);
 
-        this.ident             = ident;
-        this.name              = name;
-        this.kind              = kind;
-        this.parameters        = parameters;
-        this.firstToken        = firstToken;
-        this.lastToken         = token;
-        this.namespace         = namespace;
-        this.compilationState  = EnumSet.of(CompilationState.INITIALIZED);
-        this.declaredSymbols   = new HashSet<>();
-        this.flags             = flags;
-        this.compileUnit       = null;
-        this.body              = null;
+        this.ident            = ident;
+        this.name             = name;
+        this.kind             = kind;
+        this.parameters       = parameters;
+        this.firstToken       = firstToken;
+        this.lastToken        = token;
+        this.namespace        = namespace;
+        this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
+        this.declaredSymbols  = new HashSet<>();
+        this.flags            = flags;
+        this.compileUnit      = null;
+        this.body             = null;
+        this.snapshot         = null;
+        this.hints            = null;
     }
 
-    private FunctionNode(final FunctionNode functionNode, final long lastToken, final int flags, final Type returnType, final CompileUnit compileUnit, final EnumSet<CompilationState> compilationState, final Block body) {
+    private FunctionNode(
+        final FunctionNode functionNode,
+        final long lastToken,
+        final int flags,
+        final Type returnType,
+        final CompileUnit compileUnit,
+        final EnumSet<CompilationState> compilationState,
+        final Block body,
+        final List<IdentNode> parameters,
+        final FunctionNode snapshot,
+        final Compiler.Hints hints) {
         super(functionNode);
-        this.flags = flags;
-        this.returnType = returnType;
-        this.compileUnit = compileUnit;
-        this.lastToken = lastToken;
+
+        this.flags            = flags;
+        this.returnType       = returnType;
+        this.compileUnit      = compileUnit;
+        this.lastToken        = lastToken;
         this.compilationState = compilationState;
-        this.body  = body;
+        this.body             = body;
+        this.parameters       = parameters;
+        this.snapshot         = snapshot;
+        this.hints            = hints;
 
         // the fields below never change - they are final and assigned in constructor
-        this.name = functionNode.name;
-        this.ident = functionNode.ident;
-        this.namespace = functionNode.namespace;
+        this.name            = functionNode.name;
+        this.ident           = functionNode.ident;
+        this.namespace       = functionNode.namespace;
         this.declaredSymbols = functionNode.declaredSymbols;
-        this.kind  = functionNode.kind;
-        this.parameters = functionNode.parameters;
-        this.firstToken = functionNode.firstToken;
+        this.kind            = functionNode.kind;
+        this.firstToken      = functionNode.firstToken;
     }
 
     @Override
@@ -261,6 +282,36 @@
     }
 
     /**
+     * Get the version of this function node's code as it looked upon construction
+     * i.e typically parsed and nothing else
+     * @return initial version of function node
+     */
+    public FunctionNode getSnapshot() {
+        return snapshot;
+    }
+
+    /**
+     * Take a snapshot of this function node at a given point in time
+     * and store it in the function node
+     * @param lc lexical context
+     * @return function node
+     */
+    public FunctionNode snapshot(final LexicalContext lc) {
+        if (this.snapshot == this) {
+            return this;
+        }
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, this, hints));
+    }
+
+    /**
+     * Can this function node be regenerated with more specific type args?
+     * @return true if specialization is possible
+     */
+    public boolean canSpecialize() {
+        return getFlag(CAN_SPECIALIZE);
+    }
+
+    /**
      * Get the compilation state of this function
      * @return the compilation state
      */
@@ -307,7 +358,28 @@
         }
         final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
         newState.add(state);
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body, parameters, snapshot, hints));
+    }
+
+    /**
+     * Get any compiler hints that may associated with the function
+     * @return compiler hints
+     */
+    public Compiler.Hints getHints() {
+        return this.hints == null ? Compiler.Hints.EMPTY : hints;
+    }
+
+    /**
+     * Set compiler hints for this function
+     * @param lc    lexical context
+     * @param hints compiler hints
+     * @return new function if hints changed
+     */
+    public FunctionNode setHints(final LexicalContext lc, final Compiler.Hints hints) {
+        if (this.hints == hints) {
+            return this;
+        }
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
     }
 
     /**
@@ -319,20 +391,6 @@
         return namespace.uniqueName(base);
     }
 
-    /**
-     * Create a virtual symbol for a literal.
-     *
-     * @param literalNode Primary node to use symbol.
-     *
-     * @return Symbol used.
-     */
-    public Symbol newLiteral(final LiteralNode<?> literalNode) {
-        final String uname = uniqueName(LITERAL_PREFIX.symbolName());
-        final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
-        literalNode.setSymbol(symbol);
-
-        return symbol;
-    }
 
     @Override
     public void toString(final StringBuilder sb) {
@@ -374,7 +432,7 @@
         if (this.flags == flags) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
     }
 
     @Override
@@ -483,7 +541,7 @@
         if(this.body == body) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
     }
 
     /**
@@ -551,7 +609,7 @@
         if (this.lastToken == lastToken) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
     }
 
     /**
@@ -599,13 +657,17 @@
     }
 
     /**
-     * Get a specialized type for an identity, if one exists
-     * @param node node to check specialized type for
-     * @return null if no specialization exists, otherwise type
+     * Reset the compile unit used to compile this function
+     * @see Compiler
+     * @param  lc lexical context
+     * @param  parameters the compile unit
+     * @return function node or a new one if state was changed
      */
-    @SuppressWarnings("static-method")
-    public Type getSpecializedType(final IdentNode node) {
-        return null; //TODO implement specialized types later
+    public FunctionNode setParameters(final LexicalContext lc, final List<IdentNode> parameters) {
+        if (this.parameters == parameters) {
+            return this;
+        }
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
     }
 
     /**
@@ -674,7 +736,10 @@
                     returnType),
                 compileUnit,
                 compilationState,
-                body));
+                body,
+                parameters,
+                snapshot,
+                hints));
     }
 
     /**
@@ -705,7 +770,7 @@
         if (this.compileUnit == compileUnit) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
     }
 
     /**
@@ -717,19 +782,6 @@
      *
      * @return Symbol used.
      */
-    public Symbol ensureSymbol(final Block block, final Type type, final Node node) {
-        Symbol symbol = node.getSymbol();
-
-        // If no symbol already present.
-        if (symbol == null) {
-            final String uname = uniqueName(TEMP_PREFIX.symbolName());
-            symbol = new Symbol(uname, IS_TEMP, type);
-            block.putSymbol(uname, symbol);
-            node.setSymbol(symbol);
-        }
-
-        return symbol;
-    }
 
     /**
      * Get the symbol for a compiler constant, or null if not available (yet)
--- a/src/jdk/nashorn/internal/ir/LexicalContext.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/LexicalContext.java	Fri May 03 15:33:54 2013 +0200
@@ -64,7 +64,6 @@
             for (int i = sp - 1; i >= 0; i--) {
                 if (stack[i] == node) {
                     flags[i] |= flag;
-                    //System.err.println("Setting flag " + node + " " + flag);
                     return;
                 }
             }
@@ -117,8 +116,6 @@
         return (FunctionNode)stack[0];
     }
 
-
-
     /**
      * Pushes a new block on top of the context, making it the innermost open block.
      * @param node the new node
--- a/src/jdk/nashorn/internal/ir/LexicalContextNode.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/LexicalContextNode.java	Fri May 03 15:33:54 2013 +0200
@@ -70,4 +70,16 @@
         final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
         return lc.pop(newNode);
     }
+
+    /**
+     * Set the symbol and replace in lexical context if applicable
+     * @param lc     lexical context
+     * @param symbol symbol
+     * @return new node if symbol changed
+     */
+    @Override
+    public Node setSymbol(final LexicalContext lc, final Symbol symbol) {
+        return Node.replaceInLexicalContext(lc, this, (LexicalContextNode)super.setSymbol(null, symbol));
+    }
+
 }
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/LiteralNode.java	Fri May 03 15:33:54 2013 +0200
@@ -659,9 +659,12 @@
          * Copy constructor
          * @param node source array literal node
          */
-        protected ArrayLiteralNode(final ArrayLiteralNode node) {
-            super(node);
+        private ArrayLiteralNode(final ArrayLiteralNode node, final Node[] value) {
+            super(node, value);
             this.elementType = node.elementType;
+            this.presets     = node.presets;
+            this.postsets    = node.postsets;
+            this.units       = node.units;
         }
 
         /**
@@ -750,9 +753,8 @@
                     break;
                 }
 
-                final Symbol symbol = node.getSymbol();
-                assert symbol != null; //don't run this on unresolved nodes or you are in trouble
-                Type symbolType = symbol.getSymbolType();
+                assert node.getSymbol() != null; //don't run this on unresolved nodes or you are in trouble
+                Type symbolType = node.getSymbol().getSymbolType();
                 if (symbolType.isUnknown()) {
                     symbolType = Type.OBJECT;
                 }
@@ -813,7 +815,8 @@
         }
 
         /**
-         * Get indices of arrays containing computed post sets
+         * Get indices of arrays containing computed post sets. post sets
+         * are things like non literals e.g. "x+y" instead of i or 17
          * @return post set indices
          */
         public int[] getPostsets() {
@@ -849,17 +852,17 @@
         @Override
         public Node accept(final NodeVisitor visitor) {
             if (visitor.enterLiteralNode(this)) {
-                for (int i = 0; i < value.length; i++) {
-                    final Node element = value[i];
-                    if (element != null) {
-                        value[i] = element.accept(visitor);
-                    }
-                }
-                return visitor.leaveLiteralNode(this);
+                final List<Node> oldValue = Arrays.asList(value);
+                final List<Node> newValue = Node.accept(visitor, Node.class, oldValue);
+                return visitor.leaveLiteralNode(oldValue != newValue ? setValue(newValue) : this);
             }
             return this;
         }
 
+        private ArrayLiteralNode setValue(final List<Node> value) {
+            return new ArrayLiteralNode(this, value.toArray(new Node[value.size()]));
+        }
+
         @Override
         public void toString(final StringBuilder sb) {
             sb.append('[');
--- a/src/jdk/nashorn/internal/ir/Node.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/Node.java	Fri May 03 15:33:54 2013 +0200
@@ -252,10 +252,17 @@
      * Assign a symbol to this node. See {@link Node#getSymbol()} for explanation
      * of what a symbol is
      *
+     * @param lc lexical context
      * @param symbol the symbol
+     * @return new node
      */
-    public void setSymbol(final Symbol symbol) {
-        this.symbol = symbol;
+    public Node setSymbol(final LexicalContext lc, final Symbol symbol) {
+        if (this.symbol == symbol) {
+            return this;
+        }
+        final Node newNode = (Node)clone();
+        newNode.symbol = symbol;
+        return newNode;
     }
 
     /**
@@ -274,7 +281,7 @@
         final List<T> newList = new ArrayList<>();
 
         for (final Node node : list) {
-            final T newNode = clazz.cast(node.accept(visitor));
+            final T newNode = node == null ? null : clazz.cast(node.accept(visitor));
             if (newNode != node) {
                 changed = true;
             }
--- a/src/jdk/nashorn/internal/ir/Symbol.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/ir/Symbol.java	Fri May 03 15:33:54 2013 +0200
@@ -67,6 +67,8 @@
     public static final int IS_INTERNAL      = 1 << 9;
     /** Is this a function self-reference symbol */
     public static final int IS_FUNCTION_SELF = 1 << 10;
+    /** Is this a specialized param? */
+    public static final int IS_SPECIALIZED_PARAM = 1 << 11;
 
     /** Null or name identifying symbol. */
     private final String name;
@@ -384,6 +386,15 @@
     }
 
     /**
+     * Check if this symbol is a function parameter of known
+     * narrowest type
+     * @return true if parameter
+     */
+    public boolean isSpecializedParam() {
+        return (flags & IS_SPECIALIZED_PARAM) == IS_SPECIALIZED_PARAM;
+    }
+
+    /**
      * Check whether this symbol ever has primitive assignments. Conservative
      * @return true if primitive assignments exist
      */
--- a/src/jdk/nashorn/internal/objects/NativeRegExp.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java	Fri May 03 15:33:54 2013 +0200
@@ -794,15 +794,15 @@
 
         RegExpResult match;
         final int inputLength = string.length();
-        int lastLength = -1;
-        int lastIndex = 0;
-        int lastLastIndex = 0;
+        int splitLastLength = -1;
+        int splitLastIndex = 0;
+        int splitLastLastIndex = 0;
 
-        while ((match = execSplit(string, lastIndex)) != null) {
-            lastIndex = match.getIndex() + match.length();
+        while ((match = execSplit(string, splitLastIndex)) != null) {
+            splitLastIndex = match.getIndex() + match.length();
 
-            if (lastIndex > lastLastIndex) {
-                matches.add(string.substring(lastLastIndex, match.getIndex()));
+            if (splitLastIndex > splitLastLastIndex) {
+                matches.add(string.substring(splitLastLastIndex, match.getIndex()));
                 final Object[] groups = match.getGroups();
                 if (groups.length > 1 && match.getIndex() < inputLength) {
                     for (int index = 1; index < groups.length && matches.size() < limit; index++) {
@@ -810,7 +810,7 @@
                     }
                 }
 
-                lastLength = match.length();
+                splitLastLength = match.length();
 
                 if (matches.size() >= limit) {
                     break;
@@ -818,10 +818,10 @@
             }
 
             // bump the index to avoid infinite loop
-            if (lastIndex == lastLastIndex) {
-                lastIndex++;
+            if (splitLastIndex == splitLastLastIndex) {
+                splitLastIndex++;
             } else {
-                lastLastIndex = lastIndex;
+                splitLastLastIndex = splitLastIndex;
             }
         }
 
@@ -829,12 +829,12 @@
             // check special case if we need to append an empty string at the
             // end of the match
             // if the lastIndex was the entire string
-            if (lastLastIndex == string.length()) {
-                if (lastLength > 0 || execSplit("", 0) == null) {
+            if (splitLastLastIndex == string.length()) {
+                if (splitLastLength > 0 || execSplit("", 0) == null) {
                     matches.add("");
                 }
             } else {
-                matches.add(string.substring(lastLastIndex, inputLength));
+                matches.add(string.substring(splitLastLastIndex, inputLength));
             }
         }
 
@@ -899,10 +899,6 @@
         }
     }
 
-    private void setGlobal(final boolean global) {
-        regexp.setGlobal(global);
-    }
-
     boolean getGlobal() {
         return regexp.isGlobal();
     }
--- a/src/jdk/nashorn/internal/parser/AbstractParser.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/parser/AbstractParser.java	Fri May 03 15:33:54 2013 +0200
@@ -249,6 +249,7 @@
      *
      * @param errorType  The error type of the warning
      * @param message    Warning message.
+     * @param errorToken error token
      */
     protected final void warning(final JSErrorType errorType, final String message, final long errorToken) {
         errors.warning(error(errorType, message, errorToken));
--- a/src/jdk/nashorn/internal/parser/Parser.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/parser/Parser.java	Fri May 03 15:33:54 2013 +0200
@@ -305,6 +305,11 @@
         if (isStrictMode) {
             flags |= FunctionNode.IS_STRICT;
         }
+        if (env._specialize_calls != null) {
+            if (env._specialize_calls.contains(name)) {
+                flags |= FunctionNode.CAN_SPECIALIZE;
+            }
+        }
 
         // Start new block.
         FunctionNode functionNode =
@@ -320,11 +325,11 @@
                 kind,
                 flags);
 
-        functionNode = functionNode.setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
         lc.push(functionNode);
         // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
         // FunctionNode.
         newBlock();
+
         return functionNode;
     }
 
@@ -332,14 +337,19 @@
      * Restore the current block.
      */
     private Block restoreBlock(final Block block) {
-        return lc.pop(block);//.setFlag(lc, flags);
+        return lc.pop(block);
     }
 
+
     private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) {
         final Block newBody = restoreBlock(lc.getFunctionBody(functionNode));
 
-        return lc.pop(functionNode).setBody(lc, newBody).setLastToken(lc, lastToken);
-    }
+        return lc.pop(functionNode).
+            setBody(lc, newBody).
+            setLastToken(lc, lastToken).
+            setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED).
+            snapshot(lc);
+        }
 
     /**
      * Get the statements in a block.
@@ -529,6 +539,7 @@
 
         script = restoreFunctionNode(script, token); //commit code
         script = script.setBody(lc, script.getBody().setNeedsScope(lc));
+
         return script;
     }
 
@@ -800,7 +811,6 @@
      * @param ident         Identifier that is verified
      * @param contextString String used in error message to give context to the user
      */
-    @SuppressWarnings("fallthrough")
     private void verifyStrictIdent(final IdentNode ident, final String contextString) {
         if (isStrictMode) {
             switch (ident.getName()) {
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Fri May 03 15:33:54 2013 +0200
@@ -88,7 +88,7 @@
 
         int weight = Type.typeFor(type.returnType()).getWeight();
         for (final Class<?> paramType : type.parameterArray()) {
-            final int pweight = Type.typeFor(paramType).getWeight();
+            final int pweight = Type.typeFor(paramType).getWeight() * 2; //params are more important than call types as return values are always specialized
             weight += pweight;
         }
         return weight;
--- a/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Fri May 03 15:33:54 2013 +0200
@@ -69,5 +69,4 @@
         return best(type).moreGenericThan(type);
     }
 
-
 }
--- a/src/jdk/nashorn/internal/runtime/Context.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Fri May 03 15:33:54 2013 +0200
@@ -411,7 +411,7 @@
         return ScriptRuntime.apply(func, evalThis);
     }
 
-    private Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
+    private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
         if (srcStr.startsWith(prefix)) {
             final String resource = resourcePath + srcStr.substring(prefix.length());
             // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
@@ -759,10 +759,10 @@
         final CodeSource   cs     = url == null ? null : new CodeSource(url, (CodeSigner[])null);
         final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
 
-        final Compiler compiler = new Compiler(installer, functionNode, strict);
+        final Compiler compiler = new Compiler(installer, strict);
 
-        compiler.compile();
-        script = compiler.install();
+        final FunctionNode newFunctionNode = compiler.compile(functionNode);
+        script = compiler.install(newFunctionNode);
 
         if (global != null) {
             global.cacheClass(source, script);
--- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Fri May 03 15:33:54 2013 +0200
@@ -25,8 +25,6 @@
 
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.runtime.ScriptObject.isArray;
-
 import java.lang.invoke.MethodHandle;
 import java.util.Iterator;
 import java.util.List;
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Fri May 03 15:33:54 2013 +0200
@@ -30,9 +30,12 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.util.LinkedList;
+
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.parser.Token;
@@ -148,10 +151,10 @@
 
          if (functionNode.isLazy()) {
              Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
-             final Compiler compiler = new Compiler(installer, functionNode);
-             functionNode = compiler.compile();
+             final Compiler compiler = new Compiler(installer);
+             functionNode = compiler.compile(functionNode);
              assert !functionNode.isLazy();
-             compiler.install();
+             compiler.install(functionNode);
 
              // we don't need to update any flags - varArgs and needsCallee are instrincic
              // in the function world we need to get a destination node from the compile instead
@@ -164,23 +167,114 @@
          assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
 
          // code exists - look it up and add it into the automatically sorted invoker list
-         code.add(
-            new CompiledFunction(
-                MH.findStatic(
+         addCode(functionNode, null, null);
+    }
+
+    private MethodHandle addCode(final FunctionNode fn, final MethodHandle guard, final MethodHandle fallback) {
+        final MethodHandle target =
+            MH.findStatic(
                     LOOKUP,
-                    functionNode.getCompileUnit().getCode(),
-                    functionNode.getName(),
-                    new FunctionSignature(functionNode).
-                        getMethodType())));
+                    fn.getCompileUnit().getCode(),
+                    fn.getName(),
+                    new FunctionSignature(fn).
+                        getMethodType());
+        MethodHandle mh = target;
+        if (guard != null) {
+            try {
+                mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
+            } catch (Throwable e) {
+                e.printStackTrace();
+            }
+        }
+
+        final CompiledFunction cf = new CompiledFunction(mh);
+        code.add(cf);
+
+        return cf.getInvoker();
     }
 
+    private static Type runtimeType(final Object arg) {
+        if (arg == null) {
+            return Type.OBJECT;
+        }
+
+        final Class<?> clazz = arg.getClass();
+        assert !clazz.isPrimitive() : "always boxed";
+        if (clazz == Double.class) {
+            return JSType.isRepresentableAsInt((double)arg) ? Type.INT : Type.NUMBER;
+        } else if (clazz == Integer.class) {
+            return Type.INT;
+        } else if (clazz == Long.class) {
+            return Type.LONG;
+        } else if (clazz == String.class) {
+            return Type.STRING;
+        }
+        return Type.OBJECT;
+    }
+
+    @SuppressWarnings("unused")
+    private static boolean paramTypeGuard(final Type[] compileTimeTypes, final Type[] runtimeTypes, Object... args) {
+        //System.err.println("Param type guard " + Arrays.asList(args));
+        return false;
+    }
+
+    private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Type[].class, Object[].class);
+
     @Override
     MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
         final MethodHandle mh = super.getBestInvoker(callSiteType, args);
-        if (code.isLessSpecificThan(callSiteType)) {
-            // opportunity for code specialization - we can regenerate a better version of this method
+
+        if (!functionNode.canSpecialize() || !code.isLessSpecificThan(callSiteType)) {
+            return mh;
         }
-        return mh;
+
+        final FunctionNode snapshot = functionNode.getSnapshot();
+        int i;
+
+        //classes known at runtime
+        final LinkedList<Type> runtimeArgs = new LinkedList<>();
+        for (i = args.length - 1; i >= args.length - snapshot.getParameters().size(); i--) {
+            runtimeArgs.addLast(runtimeType(args[i]));
+        }
+
+        //classes known at compile time
+        final LinkedList<Type> compileTimeArgs = new LinkedList<>();
+        for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) {
+            compileTimeArgs.addLast(Type.typeFor(callSiteType.parameterType(i)));
+        }
+
+        //the classes known at compile time are a safe to generate as primitives without parameter guards
+        //the classes known at runtime are safe to generate as primitives IFF there are parameter guards
+        MethodHandle guard = null;
+        for (i = 0; i < compileTimeArgs.size(); i++) {
+            final Type runtimeType = runtimeArgs.get(i);
+            final Type compileType = compileTimeArgs.get(i);
+
+            if (compileType.isObject() && !runtimeType.isObject()) {
+                if (guard == null) {
+                    guard = PARAM_TYPE_GUARD;
+                    guard = MH.insertArguments(guard, 0, compileTimeArgs.toArray(new Type[compileTimeArgs.size()]), runtimeArgs.toArray(new Type[runtimeArgs.size()]));
+                }
+            }
+        }
+
+        //System.err.println("Specialized " + name + " " + runtimeArgs + " known=" + compileTimeArgs);
+
+        assert snapshot != null;
+        assert snapshot != functionNode;
+
+        final Compiler compiler = new Compiler(installer);
+        final FunctionNode compiledSnapshot = compiler.compile(snapshot.setHints(null, new Compiler.Hints(compileTimeArgs.toArray(new Type[compileTimeArgs.size()]))));
+
+        compiler.install(compiledSnapshot);
+
+        final MethodHandle nmh = addCode(compiledSnapshot, guard, mh);
+
+        return nmh;
+    }
+
+    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types));
     }
 
 }
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Fri May 03 15:33:54 2013 +0200
@@ -26,9 +26,13 @@
 package jdk.nashorn.internal.runtime;
 
 import java.io.PrintWriter;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
+import java.util.StringTokenizer;
 import java.util.TimeZone;
+
 import jdk.nashorn.internal.codegen.Namespace;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.options.KeyValueOption;
@@ -151,6 +155,9 @@
     /** is this environment in scripting mode? */
     public final boolean _scripting;
 
+    /** is the JIT allowed to specializ calls based on callsite types? */
+    public final Set<String> _specialize_calls;
+
     /** is this environment in strict mode? */
     public final boolean _strict;
 
@@ -213,6 +220,17 @@
         _version              = options.getBoolean("version");
         _verify_code          = options.getBoolean("verify.code");
 
+        final String specialize = options.getString("specialize.calls");
+        if (specialize == null) {
+            _specialize_calls = null;
+        } else {
+            _specialize_calls = new HashSet<>();
+            final StringTokenizer st = new StringTokenizer(specialize, ",");
+            while (st.hasMoreElements()) {
+                _specialize_calls.add(st.nextToken());
+            }
+        }
+
         int callSiteFlags = 0;
         if (options.getBoolean("profile.callsites")) {
             callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_PROFILE;
@@ -247,6 +265,18 @@
     }
 
     /**
+     * Can we specialize a particular method name?
+     * @param functionName method name
+     * @return true if we are allowed to generate versions of this method
+     */
+    public boolean canSpecialize(final String functionName) {
+        if (_specialize_calls == null) {
+            return false;
+        }
+        return _specialize_calls.isEmpty() || _specialize_calls.contains(functionName);
+    }
+
+    /**
      * Get the output stream for this environment
      * @return output print writer
      */
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Fri May 03 15:33:54 2013 +0200
@@ -662,9 +662,9 @@
         }
 
         if (deep) {
-            final ScriptObject proto = getProto();
-            if(proto != null) {
-                return proto.findProperty(key, deep, stopOnNonScope, start);
+            final ScriptObject myProto = getProto();
+            if (myProto != null) {
+                return myProto.findProperty(key, deep, stopOnNonScope, start);
             }
         }
 
--- a/src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java	Fri May 03 15:33:54 2013 +0200
@@ -107,16 +107,16 @@
 
     class DefaultMatcher implements RegExpMatcher {
         final String input;
-        final Matcher matcher;
+        final Matcher defaultMatcher;
 
         DefaultMatcher(final String input) {
             this.input = input;
-            this.matcher = pattern.matcher(input);
+            this.defaultMatcher = pattern.matcher(input);
         }
 
         @Override
         public boolean search(final int start) {
-            return matcher.find(start);
+            return defaultMatcher.find(start);
         }
 
         @Override
@@ -126,37 +126,37 @@
 
         @Override
         public int start() {
-            return matcher.start();
+            return defaultMatcher.start();
         }
 
         @Override
         public int start(final int group) {
-            return matcher.start(group);
+            return defaultMatcher.start(group);
         }
 
         @Override
         public int end() {
-            return matcher.end();
+            return defaultMatcher.end();
         }
 
         @Override
         public int end(final int group) {
-            return matcher.end(group);
+            return defaultMatcher.end(group);
         }
 
         @Override
         public String group() {
-            return matcher.group();
+            return defaultMatcher.group();
         }
 
         @Override
         public String group(final int group) {
-            return matcher.group(group);
+            return defaultMatcher.group(group);
         }
 
         @Override
         public int groupCount() {
-            return matcher.groupCount();
+            return defaultMatcher.groupCount();
         }
     }
 
--- a/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java	Fri May 03 15:33:54 2013 +0200
@@ -121,16 +121,16 @@
 
     class JoniMatcher implements RegExpMatcher {
         final String input;
-        final Matcher matcher;
+        final Matcher joniMatcher;
 
         JoniMatcher(final String input) {
             this.input = input;
-            this.matcher = regex.matcher(input.toCharArray());
+            this.joniMatcher = regex.matcher(input.toCharArray());
         }
 
         @Override
         public boolean search(final int start) {
-            return matcher.search(start, input.length(), Option.NONE) > -1;
+            return joniMatcher.search(start, input.length(), Option.NONE) > -1;
         }
 
         @Override
@@ -140,27 +140,27 @@
 
         @Override
         public int start() {
-            return matcher.getBegin();
+            return joniMatcher.getBegin();
         }
 
         @Override
         public int start(final int group) {
-            return group == 0 ? start() : matcher.getRegion().beg[group];
+            return group == 0 ? start() : joniMatcher.getRegion().beg[group];
         }
 
         @Override
         public int end() {
-            return matcher.getEnd();
+            return joniMatcher.getEnd();
         }
 
         @Override
         public int end(final int group) {
-            return group == 0 ? end() : matcher.getRegion().end[group];
+            return group == 0 ? end() : joniMatcher.getRegion().end[group];
         }
 
         @Override
         public String group() {
-            return input.substring(matcher.getBegin(), matcher.getEnd());
+            return input.substring(joniMatcher.getBegin(), joniMatcher.getEnd());
         }
 
         @Override
@@ -168,13 +168,13 @@
             if (group == 0) {
                 return group();
             }
-            final Region region = matcher.getRegion();
+            final Region region = joniMatcher.getRegion();
             return input.substring(region.beg[group], region.end[group]);
         }
 
         @Override
         public int groupCount() {
-            final Region region = matcher.getRegion();
+            final Region region = joniMatcher.getRegion();
             return region == null ? 0 : region.numRegs - 1;
         }
     }
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java	Fri May 03 15:33:54 2013 +0200
@@ -934,7 +934,7 @@
         return true;
     }
 
-    private void unicode(final int value, final StringBuilder buffer) {
+    private static void unicode(final int value, final StringBuilder buffer) {
         final String hex = Integer.toHexString(value);
         buffer.append('u');
         for (int i = 0; i < 4 - hex.length(); i++) {
@@ -944,7 +944,7 @@
     }
 
     // Convert what would have been a backreference into a unicode escape, or a number literal, or both.
-    private void octalOrLiteral(final String numberLiteral, final StringBuilder buffer) {
+    private static void octalOrLiteral(final String numberLiteral, final StringBuilder buffer) {
         final int length = numberLiteral.length();
         int octalValue = 0;
         int pos = 0;
--- a/src/jdk/nashorn/internal/runtime/resources/Options.properties	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties	Fri May 03 15:33:54 2013 +0200
@@ -288,12 +288,12 @@
     dependency="--anon-functions=true"  \
 }
 
-nashorn.option.timezone = {                    \
-    name="-timezone",                          \
-    short_name="-t",                           \
-    params="<timezone>",                       \
-    desc="Set timezone for script execution.", \
-    type=TimeZone                              \
+nashorn.option.specialize.calls = {                                                \
+    name="--specialize-calls",                                                     \
+    is_undocumented=true,                                                          \
+    type=String,                                                                   \
+    params="[=function_1,...,function_n]",                                         \
+    desc="Specialize all or a set of method according to callsite parameter types" \
 }
 
 nashorn.option.stdout = {                                               \
@@ -312,6 +312,14 @@
     desc="Redirect stderr to a filename or to another tty, e.g. stdout" \
 }
 
+nashorn.option.timezone = {                    \
+    name="-timezone",                          \
+    short_name="-t",                           \
+    params="<timezone>",                       \
+    desc="Set timezone for script execution.", \
+    type=TimeZone                              \
+}
+
 nashorn.option.trace.callsites = {                                              \
     name="--trace-callsites",                                                   \
     short_name="-tcs",                                                          \
--- a/src/jdk/nashorn/tools/Shell.java	Thu May 02 15:01:16 2013 -0300
+++ b/src/jdk/nashorn/tools/Shell.java	Fri May 03 15:33:54 2013 +0200
@@ -270,7 +270,7 @@
                 }
 
                 //null - pass no code installer - this is compile only
-                new Compiler(env, functionNode).compile();
+                new Compiler(env).compile(functionNode);
             }
         } finally {
             env.getOut().flush();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/paramspec.js	Fri May 03 15:33:54 2013 +0200
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * paramspec - test that Attr doesn't break parameters when specializing
+ *
+ * @run
+ * @test
+ */
+
+function f(a) {
+    var b = ~a;
+    return b == ~17;
+}
+
+print(f("17"));
+print(f(17));
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/paramspec.js.EXPECTED	Fri May 03 15:33:54 2013 +0200
@@ -0,0 +1,2 @@
+true
+true
--- a/test/script/basic/runsunspider.js	Thu May 02 15:01:16 2013 -0300
+++ b/test/script/basic/runsunspider.js	Fri May 03 15:33:54 2013 +0200
@@ -86,7 +86,7 @@
 	    changed = true;
 	}
     } catch (e) {
-	print("error: " + e);
+	print("error: " + e.printStackTrace());
 	if (e.toString().indexOf(tests) == 1) {
 	    throw e;
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/currently-failing/logcoverage.js	Fri May 03 15:33:54 2013 +0200
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * mh_coverage.js
+ * Screen scrape various logs to ensure that we cover enough functionality, 
+ * e.g. method handle instrumentation
+ *
+ * @test
+ * @run
+ */
+
+/*
+ * creates new script engine initialized with given options and
+ * runs given code on it. Returns standard output captured.
+ */
+
+function runScriptEngine(opts, name) {
+    var imports = new JavaImporter(
+        Packages.jdk.nashorn.api.scripting,
+        java.io, java.lang, java.util);
+
+    with(imports) {
+        var fac = new NashornScriptEngineFactory();
+        // get current System.err
+        var oldErr = System.err;
+	var oldOut = System.out;
+        var baosErr = new ByteArrayOutputStream();
+        var newErr = new PrintStream(baosErr);
+        var baosOut = new ByteArrayOutputStream();
+	var newOut = new PrintStream(baosOut);
+        try {
+            // set new standard err
+            System.setErr(newErr);
+            System.setOut(newOut);
+            var strType = Java.type("java.lang.String");
+            var engine = fac.getScriptEngine(Java.toJavaArray(opts, strType));
+	    var reader = new java.io.FileReader(name);
+            engine.eval(reader);
+            newErr.flush();
+	    newOut.flush();
+            return new java.lang.String(baosErr.toByteArray());
+        } finally {
+            // restore System.err to old value
+            System.setErr(oldErr);
+	    System.setOut(oldOut);
+        }
+    }
+}
+
+var str;
+
+var methodsCalled = [
+   'asCollector', 
+   'asType', 
+   'bindTo', 
+   'dropArguments', 
+   'explicitCastArguments', 
+   'filterArguments', 
+   'filterReturnValue', 
+   'findStatic', 
+   'findVirtual',  
+   'foldArguments', 
+   'getter', 
+   'guardWithTest', 
+   'insertArguments', 
+   'methodType', 
+   'setter'
+];
+
+function check(str, strs) {
+    for each (s in strs) {
+       if (str.indexOf(s) !== -1) {
+	   continue;
+       }
+       print(s + " not found");
+       return;
+    }
+    print("check ok!");
+}
+
+str = runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/NASHORN-19.js");
+str += runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/varargs.js");
+
+check(str, methodsCalled);
+check(str, ['return', 'get', 'set', '[fields]']);
+check(str, ['codegen']);
+
+print("hello, world!");
--- a/test/script/trusted/logcoverage.js	Thu May 02 15:01:16 2013 -0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- * 
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- * 
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- * 
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- * 
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/**
- * mh_coverage.js
- * Screen scrape various logs to ensure that we cover enough functionality, 
- * e.g. method handle instrumentation
- *
- * @test
- * @run
- */
-
-/*
- * creates new script engine initialized with given options and
- * runs given code on it. Returns standard output captured.
- */
-
-function runScriptEngine(opts, name) {
-    var imports = new JavaImporter(
-        Packages.jdk.nashorn.api.scripting,
-        java.io, java.lang, java.util);
-
-    with(imports) {
-        var fac = new NashornScriptEngineFactory();
-        // get current System.err
-        var oldErr = System.err;
-	var oldOut = System.out;
-        var baosErr = new ByteArrayOutputStream();
-        var newErr = new PrintStream(baosErr);
-        var baosOut = new ByteArrayOutputStream();
-	var newOut = new PrintStream(baosOut);
-        try {
-            // set new standard err
-            System.setErr(newErr);
-            System.setOut(newOut);
-            var strType = Java.type("java.lang.String");
-            var engine = fac.getScriptEngine(Java.toJavaArray(opts, strType));
-	    var reader = new java.io.FileReader(name);
-            engine.eval(reader);
-            newErr.flush();
-	    newOut.flush();
-            return new java.lang.String(baosErr.toByteArray());
-        } finally {
-            // restore System.err to old value
-            System.setErr(oldErr);
-	    System.setOut(oldOut);
-        }
-    }
-}
-
-var str;
-
-var methodsCalled = [
-   'asCollector', 
-   'asType', 
-   'bindTo', 
-   'dropArguments', 
-   'explicitCastArguments', 
-   'filterArguments', 
-   'filterReturnValue', 
-   'findStatic', 
-   'findVirtual',  
-   'foldArguments', 
-   'getter', 
-   'guardWithTest', 
-   'insertArguments', 
-   'methodType', 
-   'setter'
-];
-
-function check(str, strs) {
-    for each (s in strs) {
-       if (s.indexOf(str) !== -1) {
-	   continue;
-       }
-       print(method + "not found");
-       return;
-    }
-    print("check ok!");
-}
-
-str = runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/NASHORN-19.js");
-
-check(str, methodsCalled);
-check(str, ['return', 'get', 'set', '[fields]']);
-check(str, ['codegen']);
-
-print("hello, world!");