8006529: Methods always get callee - it should be conditional

Thu, 31 Jan 2013 18:34:42 +0100

author
attila
date
Thu, 31 Jan 2013 18:34:42 +0100
changeset 62
f7825c1a11d3
parent 61
9c1e7ae975db
child 63
697f700d90c0

8006529: Methods always get callee - it should be conditional
Summary: This commit streamlines the bytecode function signatures, prologue, local variable use, scope creation, and invocation. It started out quite innocently when we noticed that we always emit __callee__ parameters for all functions even when they are not needed, but it turned out to be quite a deep rabbit hole. In the end, I identified exact conditions when functions need to have a callee parameter, when they need to receive parent scope, when they need to create their own scope, when they need to have variable arity signature, and when they need to have an "arguments" object, and made sure that callee parameters in signatures only show up when they are needed, that parent function's scope is only passed to a child function when it is needed, that the function only creates its own scope when it is needed. In crypto.js, the number of scopes dropped from 446 to 244, and the number of callees dropped from 315 to 145.
Reviewed-by: jlaskey, lagergren

src/jdk/nashorn/internal/codegen/Attr.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/CodeGenerator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Compiler.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/FinalizeTypes.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Lower.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/MethodEmitter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/objects/FieldObjectCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/types/Type.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Block.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/FunctionNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Symbol.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/Global.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/NativeArguments.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/parser/Parser.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ArgumentSetter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptFunction.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptRuntime.java file | annotate | diff | comparison | revisions
test/script/basic/JDK-8006529-b.js file | annotate | diff | comparison | revisions
test/script/basic/JDK-8006529-b.js.EXPECTED file | annotate | diff | comparison | revisions
test/script/basic/JDK-8006529.js file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/internal/codegen/CompilerAccess.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/codegen/Attr.java	Thu Jan 31 20:07:40 2013 +0530
     1.2 +++ b/src/jdk/nashorn/internal/codegen/Attr.java	Thu Jan 31 18:34:42 2013 +0100
     1.3 @@ -45,7 +45,6 @@
     1.4  import java.util.LinkedList;
     1.5  import java.util.List;
     1.6  import java.util.Set;
     1.7 -
     1.8  import jdk.nashorn.internal.codegen.types.Type;
     1.9  import jdk.nashorn.internal.ir.AccessNode;
    1.10  import jdk.nashorn.internal.ir.BinaryNode;
    1.11 @@ -413,6 +412,13 @@
    1.12              // we have never seen this before, it can be undefined
    1.13              newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
    1.14              symbol.setCanBeUndefined();
    1.15 +            symbol.setIsScope();
    1.16 +        }
    1.17 +
    1.18 +        if(symbol.isGlobal()) {
    1.19 +            getCurrentFunctionNode().setUsesGlobalSymbol();
    1.20 +        } else if(symbol.isScope()) {
    1.21 +            getCurrentFunctionNode().setUsesScopeSymbol(symbol);
    1.22          }
    1.23  
    1.24          if (symbol != oldSymbol && !identNode.isInitializedHere()) {
    1.25 @@ -666,7 +672,9 @@
    1.26              }
    1.27              final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this);
    1.28  
    1.29 -            args.add(currentFunctionNode.getScopeNode());
    1.30 +            if (!failDelete) {
    1.31 +                args.add(currentFunctionNode.getScopeNode());
    1.32 +            }
    1.33              args.add(literalNode);
    1.34              args.add(strictFlagNode);
    1.35  
    1.36 @@ -1157,21 +1165,24 @@
    1.37          //return symbol is always object as it's the __return__ thing. What returnType is is another matter though
    1.38      }
    1.39  
    1.40 -    private static void initVarArg(final FunctionNode functionNode) {
    1.41 -        assert functionNode.getCalleeNode() != null;
    1.42 +    private void initVarArg(final FunctionNode functionNode) {
    1.43 +        if (functionNode.isVarArg()) {
    1.44 +            final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
    1.45 +            varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
    1.46 +            varArgsSymbol.setNeedsSlot(true);
    1.47 +            functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
    1.48 +            LOG.info("Initialized varargs symbol: " + varArgsSymbol);
    1.49  
    1.50 -        final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
    1.51 -        newType(varArgsSymbol, Type.OBJECT_ARRAY);
    1.52 -        varArgsSymbol.setNeedsSlot(true);
    1.53 -        functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
    1.54 -        LOG.info("Initialized varargs symbol: " + varArgsSymbol);
    1.55 -
    1.56 -        final String    argumentsName   = functionNode.getArgumentsNode().getName();
    1.57 -        final Symbol    argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_PARAM | IS_INTERNAL, null);
    1.58 -        newType(argumentsSymbol, Type.OBJECT);
    1.59 -        argumentsSymbol.setNeedsSlot(true);
    1.60 -        functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
    1.61 -        LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol);
    1.62 +            if (functionNode.needsArguments()) {
    1.63 +                final String    argumentsName   = functionNode.getArgumentsNode().getName();
    1.64 +                final Symbol    argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_VAR | IS_INTERNAL, null);
    1.65 +                newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
    1.66 +                argumentsSymbol.setNeedsSlot(true);
    1.67 +                functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
    1.68 +                addLocalDef(argumentsName);
    1.69 +                LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol);
    1.70 +            }
    1.71 +        }
    1.72      }
    1.73  
    1.74      private static void initCallee(final FunctionNode functionNode) {
    1.75 @@ -1238,7 +1249,7 @@
    1.76              LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations);
    1.77          }
    1.78  
    1.79 -        // parameters should not be slots for a vararg function, make sure this is the case
    1.80 +        // parameters should not be slots for a function that uses variable arity signature
    1.81          if (functionNode.isVarArg()) {
    1.82              for (final IdentNode param : functionNode.getParameters()) {
    1.83                  param.getSymbol().setNeedsSlot(false);
     2.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Jan 31 20:07:40 2013 +0530
     2.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Jan 31 18:34:42 2013 +0100
     2.3 @@ -201,6 +201,9 @@
     2.4          final Symbol symbol = identNode.getSymbol();
     2.5  
     2.6          if (!symbol.isScope()) {
     2.7 +            if(symbol.isParam()) {
     2.8 +                return method.loadParam(symbol);
     2.9 +            }
    2.10              assert symbol.hasSlot() && symbol.getSlot() != 0 || symbol.isThis();
    2.11              return method.load(symbol);
    2.12          }
    2.13 @@ -383,7 +386,7 @@
    2.14          for (final Symbol symbol : symbols) {
    2.15              /*
    2.16               * The following symbols are guaranteed to be defined and thus safe
    2.17 -             * from having unsigned written to them: parameters internals this
    2.18 +             * from having undefined written to them: parameters internals this
    2.19               *
    2.20               * Otherwise we must, unless we perform control/escape analysis,
    2.21               * assign them undefined.
    2.22 @@ -843,6 +846,15 @@
    2.23          /* Fix the predefined slots so they have numbers >= 0, like varargs. */
    2.24          frame.realign();
    2.25  
    2.26 +        if (isFunctionNode) {
    2.27 +            if (function.needsParentScope()) {
    2.28 +                initParentScope();
    2.29 +            }
    2.30 +            if (function.needsArguments()) {
    2.31 +                initArguments(function);
    2.32 +            }
    2.33 +        }
    2.34 +
    2.35          /*
    2.36           * Determine if block needs scope, if not, just do initSymbols for this block.
    2.37           */
    2.38 @@ -851,7 +863,6 @@
    2.39               * Determine if function is varargs and consequently variables have to
    2.40               * be in the scope.
    2.41               */
    2.42 -            final boolean isVarArg    = function.isVarArg();
    2.43              final boolean varsInScope = function.varsInScope();
    2.44  
    2.45              // TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope.
    2.46 @@ -859,39 +870,34 @@
    2.47              final List<String> nameList = new ArrayList<>();
    2.48              final List<Symbol> locals   = new ArrayList<>();
    2.49  
    2.50 -            // If there are variable arguments, we need to load them (functions only).
    2.51 -            if (isFunctionNode && isVarArg) {
    2.52 -                method.loadVarArgs();
    2.53 -                method.loadCallee();
    2.54 -                method.load(function.getParameters().size());
    2.55 -                globalAllocateArguments();
    2.56 -                method.storeArguments();
    2.57 -            }
    2.58  
    2.59              // Initalize symbols and values
    2.60              final List<Symbol> newSymbols = new ArrayList<>();
    2.61              final List<Symbol> values     = new ArrayList<>();
    2.62  
    2.63 +            final boolean hasArguments = function.needsArguments();
    2.64              for (final Symbol symbol : symbols) {
    2.65                  if (symbol.isInternal() || symbol.isThis()) {
    2.66                      continue;
    2.67                  }
    2.68  
    2.69 -                if (symbol.isVar() && (varsInScope || symbol.isScope())) {
    2.70 +                if (symbol.isVar()) {
    2.71 +                    if(varsInScope || symbol.isScope()) {
    2.72 +                        nameList.add(symbol.getName());
    2.73 +                        newSymbols.add(symbol);
    2.74 +                        values.add(null);
    2.75 +                        assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName();
    2.76 +                        assert !symbol.hasSlot()  : "slot for " + symbol + " should have been removed in Lower already" + function.getName();
    2.77 +                    } else {
    2.78 +                        assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
    2.79 +                        locals.add(symbol);
    2.80 +                    }
    2.81 +                } else if (symbol.isParam() && (varsInScope || hasArguments || symbol.isScope())) {
    2.82                      nameList.add(symbol.getName());
    2.83                      newSymbols.add(symbol);
    2.84 -                    values.add(null);
    2.85 -                    assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName();
    2.86 -                    assert !symbol.hasSlot()  : "slot for " + symbol + " should have been removed in Lower already" + function.getName();
    2.87 -                } else if (symbol.isVar()) {
    2.88 -                    assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
    2.89 -                    locals.add(symbol);
    2.90 -                } else if (symbol.isParam() && (varsInScope || isVarArg || symbol.isScope())) {
    2.91 -                    nameList.add(symbol.getName());
    2.92 -                    newSymbols.add(symbol);
    2.93 -                    values.add(isVarArg ? null : symbol);
    2.94 -                    assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName() + " varsInScope="+varsInScope+" isVarArg="+isVarArg+" symbol.isScope()=" + symbol.isScope();
    2.95 -                    assert !(isVarArg && symbol.hasSlot())  : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
    2.96 +                    values.add(hasArguments ? null : symbol);
    2.97 +                    assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName() + " varsInScope="+varsInScope+" hasArguments="+hasArguments+" symbol.isScope()=" + symbol.isScope();
    2.98 +                    assert !(hasArguments && symbol.hasSlot())  : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
    2.99                  }
   2.100              }
   2.101  
   2.102 @@ -901,15 +907,11 @@
   2.103              // we may have locals that need to be initialized
   2.104              initSymbols(locals);
   2.105  
   2.106 -            if (isFunctionNode) {
   2.107 -                initScope();
   2.108 -            }
   2.109 -
   2.110              /*
   2.111               * Create a new object based on the symbols and values, generate
   2.112               * bootstrap code for object
   2.113               */
   2.114 -            final FieldObjectCreator<Symbol> foc = new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, isVarArg) {
   2.115 +            final FieldObjectCreator<Symbol> foc = new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, hasArguments) {
   2.116                  @Override
   2.117                  protected Type getValueType(final Symbol value) {
   2.118                      return value.getSymbolType();
   2.119 @@ -919,6 +921,15 @@
   2.120                  protected void loadValue(final Symbol value) {
   2.121                      method.load(value);
   2.122                  }
   2.123 +
   2.124 +                @Override
   2.125 +                protected void loadScope(MethodEmitter m) {
   2.126 +                    if(function.needsParentScope()) {
   2.127 +                        m.loadScope();
   2.128 +                    } else {
   2.129 +                        m.loadNull();
   2.130 +                    }
   2.131 +                }
   2.132              };
   2.133              foc.makeObject(method);
   2.134  
   2.135 @@ -929,18 +940,37 @@
   2.136  
   2.137              method.storeScope();
   2.138          } else {
   2.139 +            // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
   2.140 +            // we need to assign them separately here.
   2.141 +            int nextParam = 0;
   2.142 +            if (isFunctionNode && function.isVarArg()) {
   2.143 +                for (final IdentNode param : function.getParameters()) {
   2.144 +                    param.getSymbol().setFieldIndex(nextParam++);
   2.145 +                }
   2.146 +            }
   2.147              initSymbols(symbols);
   2.148 -
   2.149 -            if (isFunctionNode) {
   2.150 -                initScope();
   2.151 -            }
   2.152          }
   2.153  
   2.154          // Debugging: print symbols? @see --print-symbols flag
   2.155          printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
   2.156      }
   2.157  
   2.158 -    private void initScope() {
   2.159 +    private void initArguments(final FunctionNode function) {
   2.160 +        method.loadVarArgs();
   2.161 +        if(function.needsCallee()) {
   2.162 +            method.loadCallee();
   2.163 +        } else {
   2.164 +            // If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the
   2.165 +            // caller.
   2.166 +            assert function.isStrictMode();
   2.167 +            method.loadNull();
   2.168 +        }
   2.169 +        method.load(function.getParameters().size());
   2.170 +        globalAllocateArguments();
   2.171 +        method.storeArguments();
   2.172 +    }
   2.173 +
   2.174 +    private void initParentScope() {
   2.175          method.loadCallee();
   2.176          method.invoke(ScriptFunction.GET_SCOPE);
   2.177          method.storeScope();
   2.178 @@ -1622,7 +1652,8 @@
   2.179          final String name       = splitNode.getName();
   2.180  
   2.181          final Class<?>   rtype  = fn.getReturnType().getTypeClass();
   2.182 -        final Class<?>[] ptypes = fn.isVarArg() ?
   2.183 +        final boolean needsArguments = fn.needsArguments();
   2.184 +        final Class<?>[] ptypes = needsArguments ?
   2.185                  new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class, Object.class} :
   2.186                  new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class};
   2.187  
   2.188 @@ -1647,9 +1678,13 @@
   2.189  
   2.190          final MethodEmitter caller = splitNode.getCaller();
   2.191          caller.loadThis();
   2.192 -        caller.loadCallee();
   2.193 +        if(fn.needsCallee()) {
   2.194 +            caller.loadCallee();
   2.195 +        } else {
   2.196 +            caller.loadNull();
   2.197 +        }
   2.198          caller.loadScope();
   2.199 -        if (fn.isVarArg()) {
   2.200 +        if (needsArguments) {
   2.201              caller.loadArguments();
   2.202          }
   2.203          caller.invoke(splitCall);
   2.204 @@ -3142,17 +3177,34 @@
   2.205  
   2.206              target.accept(new NodeVisitor(compileUnit, method) {
   2.207                  @Override
   2.208 +                protected Node enterDefault(Node node) {
   2.209 +                    throw new AssertionError("Unexpected node " + node + " in store epilogue");
   2.210 +                };
   2.211 +
   2.212 +                @Override
   2.213 +                public Node enter(final UnaryNode node) {
   2.214 +                    if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
   2.215 +                        method.convert(node.rhs().getType());
   2.216 +                    }
   2.217 +                    return node;
   2.218 +                }
   2.219 +
   2.220 +                @Override
   2.221                  public Node enter(final IdentNode node) {
   2.222 -                    final Symbol symbol = target.getSymbol();
   2.223 +                    final Symbol symbol = node.getSymbol();
   2.224                      if (symbol.isScope()) {
   2.225                          if (symbol.isFastScope(currentFunction)) {
   2.226 -                            storeFastScopeVar(target.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
   2.227 +                            storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
   2.228                          } else {
   2.229 -                            method.dynamicSet(target.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
   2.230 +                            method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
   2.231                          }
   2.232                      } else {
   2.233 -                        assert targetSymbol != null;
   2.234 -                        method.store(targetSymbol);
   2.235 +                        assert symbol != null;
   2.236 +                        if(symbol.isParam()) {
   2.237 +                            method.storeParam(symbol);
   2.238 +                        } else {
   2.239 +                            method.store(symbol);
   2.240 +                        }
   2.241                      }
   2.242                      return null;
   2.243  
   2.244 @@ -3203,7 +3255,7 @@
   2.245      }
   2.246  
   2.247      private MethodEmitter globalAllocateArguments() {
   2.248 -        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocateArguments", methodDescriptor(Object.class, Object[].class, Object.class, int.class));
   2.249 +        return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocateArguments", methodDescriptor(ScriptObject.class, Object[].class, Object.class, int.class));
   2.250      }
   2.251  
   2.252      private MethodEmitter globalNewRegExp() {
     3.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java	Thu Jan 31 20:07:40 2013 +0530
     3.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Thu Jan 31 18:34:42 2013 +0100
     3.3 @@ -111,8 +111,8 @@
     3.4      /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */
     3.5      public static final String GLOBAL_OBJECT = OBJECTS_PACKAGE + '/' + "Global";
     3.6  
     3.7 -    /** Name of the ScriptObjectImpl, cannot be referred to as .class @see FunctionObjectCreator */
     3.8 -    public static final String SCRIPTOBJECT_IMPL_OBJECT = OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
     3.9 +    /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */
    3.10 +    public static final String SCRIPTFUNCTION_IMPL_OBJECT = OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
    3.11  
    3.12      /** Name of the Trampoline, cannot be referred to as .class @see FunctionObjectCreator */
    3.13      public static final String TRAMPOLINE_OBJECT = OBJECTS_PACKAGE + '/' + "Trampoline";
    3.14 @@ -374,6 +374,7 @@
    3.15                  LOG.info("Lowering '" + functionNode.getName() + "'");
    3.16                  functionNode.accept(new Lower(this));
    3.17                  state.add(State.LOWERED);
    3.18 +                debugPrintAST();
    3.19  
    3.20                  LOG.info("Attributing types '" + functionNode.getName() + "'");
    3.21                  functionNode.accept(new Attr(this));
     4.1 --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Thu Jan 31 20:07:40 2013 +0530
     4.2 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Thu Jan 31 18:34:42 2013 +0100
     4.3 @@ -27,7 +27,6 @@
     4.4  
     4.5  import java.util.HashSet;
     4.6  import java.util.List;
     4.7 -
     4.8  import jdk.nashorn.internal.codegen.types.Type;
     4.9  import jdk.nashorn.internal.ir.AccessNode;
    4.10  import jdk.nashorn.internal.ir.Assignment;
    4.11 @@ -425,6 +424,19 @@
    4.12  
    4.13      @Override
    4.14      public Node enter(final FunctionNode functionNode) {
    4.15 +        // If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
    4.16 +        // this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
    4.17 +        // need for the callee.
    4.18 +        if(!functionNode.needsCallee()) {
    4.19 +            functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
    4.20 +        }
    4.21 +        // Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
    4.22 +        // own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
    4.23 +        // this phase.
    4.24 +        if(!(functionNode.needsScope() || functionNode.needsParentScope())) {
    4.25 +            functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
    4.26 +        }
    4.27 +
    4.28          updateSymbols(functionNode);
    4.29          return functionNode;
    4.30      }
    4.31 @@ -548,7 +560,6 @@
    4.32       * @param block block for which to to finalize type info.
    4.33       */
    4.34      private static void updateSymbols(final Block block) {
    4.35 -
    4.36          if (!block.needsScope()) {
    4.37              return; // nothing to do
    4.38          }
     5.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java	Thu Jan 31 20:07:40 2013 +0530
     5.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java	Thu Jan 31 18:34:42 2013 +0100
     5.3 @@ -38,7 +38,6 @@
     5.4  import java.util.Arrays;
     5.5  import java.util.Deque;
     5.6  import java.util.List;
     5.7 -
     5.8  import jdk.nashorn.internal.ir.AccessNode;
     5.9  import jdk.nashorn.internal.ir.BaseNode;
    5.10  import jdk.nashorn.internal.ir.BinaryNode;
    5.11 @@ -956,14 +955,16 @@
    5.12          final long token  = functionNode.getToken();
    5.13          final int  finish = functionNode.getFinish();
    5.14  
    5.15 -        LOG.info("Init this, scope, result, callee, varargs and argument for " + functionNode.getName());
    5.16 -
    5.17          functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
    5.18          functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
    5.19          functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
    5.20          functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
    5.21 -        functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
    5.22 -        functionNode.setArgumentsNode(new IdentNode(source, token, finish, functionNode.hideArguments() ? compiler.uniqueName('$' + ARGUMENTS.tag()) : ARGUMENTS.tag()));
    5.23 +        if(functionNode.isVarArg()) {
    5.24 +            functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
    5.25 +            if(functionNode.needsArguments()) {
    5.26 +                functionNode.setArgumentsNode(new IdentNode(source, token, finish, ARGUMENTS.tag()));
    5.27 +            }
    5.28 +        }
    5.29      }
    5.30  
    5.31  }
     6.1 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Thu Jan 31 20:07:40 2013 +0530
     6.2 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Thu Jan 31 18:34:42 2013 +0100
     6.3 @@ -86,10 +86,12 @@
     6.4  import jdk.nashorn.internal.ir.RuntimeNode;
     6.5  import jdk.nashorn.internal.ir.SplitNode;
     6.6  import jdk.nashorn.internal.ir.Symbol;
     6.7 +import jdk.nashorn.internal.runtime.ArgumentSetter;
     6.8  import jdk.nashorn.internal.runtime.Context;
     6.9  import jdk.nashorn.internal.runtime.DebugLogger;
    6.10  import jdk.nashorn.internal.runtime.JSType;
    6.11  import jdk.nashorn.internal.runtime.Scope;
    6.12 +import jdk.nashorn.internal.runtime.ScriptObject;
    6.13  import jdk.nashorn.internal.runtime.linker.Bootstrap;
    6.14  import jdk.nashorn.internal.runtime.options.Options;
    6.15  import org.dynalang.dynalink.support.NameCodec;
    6.16 @@ -273,7 +275,7 @@
    6.17       */
    6.18      private ArrayType popArray() {
    6.19          final Type type = stack.pop();
    6.20 -        assert type.isArray();
    6.21 +        assert type.isArray() : type;
    6.22          return (ArrayType)type;
    6.23      }
    6.24  
    6.25 @@ -311,6 +313,7 @@
    6.26       * @return the method emitter
    6.27       */
    6.28      public MethodEmitter _new(final String classDescriptor) {
    6.29 +        debug("new", classDescriptor);
    6.30          method.visitTypeInsn(NEW, classDescriptor);
    6.31          pushType(Type.OBJECT);
    6.32          return this;
    6.33 @@ -833,6 +836,42 @@
    6.34      }
    6.35  
    6.36      /**
    6.37 +     * Push a non-scope function parameter to the stack. Function parameters always arrive into a function as either
    6.38 +     * explicit parameters on the stack, or collected into a final variable-arity {@code Object[]} parameter. If they
    6.39 +     * end up being scoped (i.e. referenced from a child function or eval), then they're loaded as scoped symbols and
    6.40 +     * this function is not invoked for them. If they aren't scoped, then they will be loaded from one of three places.
    6.41 +     * First, if the function has an Arguments object, they're loaded from it. Otherwise, if the parameters come in a
    6.42 +     * {@code Object[]} array, they are loaded from the array. Finally, if neither is the case, they're simply loaded
    6.43 +     * from their bytecode slot.
    6.44 +     *
    6.45 +     * @param symbol the symbol representing the parameter.
    6.46 +     *
    6.47 +     * @return the method emitter
    6.48 +     */
    6.49 +    public MethodEmitter loadParam(final Symbol symbol) {
    6.50 +        assert symbol != null && symbol.isParam() && !symbol.isScope();
    6.51 +        if(symbol.hasSlot()) {
    6.52 +            // Check that we aren't vararg, except if we're loading "this"
    6.53 +            assert symbol.isThis() || !functionNode.isVarArg() : "Symbol=" + symbol + " functionNode=" + functionNode.getName();
    6.54 +            // Just load it from a local variable
    6.55 +            return load(symbol);
    6.56 +        }
    6.57 +        assert functionNode.isVarArg();
    6.58 +        if(functionNode.needsArguments()) {
    6.59 +            // ScriptObject.getArgument(int) on arguments
    6.60 +            loadArguments();
    6.61 +            load(symbol.getFieldIndex());
    6.62 +            ScriptObject.GET_ARGUMENT.invoke(this);
    6.63 +        } else {
    6.64 +            // array load from __varargs__
    6.65 +            loadVarArgs();
    6.66 +            load(symbol.getFieldIndex());
    6.67 +            arrayload();
    6.68 +        }
    6.69 +        return this;
    6.70 +    }
    6.71 +
    6.72 +    /**
    6.73       * Push a local variable to the stack, given an explicit bytecode slot
    6.74       * This is used e.g. for stub generation where we know where items like
    6.75       * "this" and "scope" reside.
    6.76 @@ -917,7 +956,7 @@
    6.77       * @return the method emitter
    6.78       */
    6.79      public MethodEmitter loadArguments() {
    6.80 -        debug("load arguments " + functionNode.getVarArgsNode().getSymbol());
    6.81 +        debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
    6.82          assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
    6.83          return load(functionNode.getArgumentsNode().getSymbol());
    6.84      }
    6.85 @@ -1000,6 +1039,40 @@
    6.86      }
    6.87  
    6.88      /**
    6.89 +     * Pop a value from the stack and store it in a non-scope function parameter. Function parameters always arrive into
    6.90 +     * a function as either explicit parameters on th stack, or collected into a final variable-arity {@code Object[]}
    6.91 +     * parameter. If they end up being scoped (i.e. referenced from a child function or eval), then they're stored as
    6.92 +     * scoped symbols are and this function is not invoked for them. If they aren't scoped, then they will be stored
    6.93 +     * to one of three places. First, if the function has an Arguments object, they're stored to it. Otherwise, if the
    6.94 +     * parameters come in a {@code Object[]} array, they are stored to the array. Finally, if neither is the case,
    6.95 +     * they're simply stored to their bytecode slot.
    6.96 +     *
    6.97 +     * @param symbol the symbol representing the parameter.
    6.98 +     *
    6.99 +     */
   6.100 +    public void storeParam(final Symbol symbol) {
   6.101 +        assert symbol != null && symbol.isParam() && !symbol.isScope();
   6.102 +        if(symbol.hasSlot()) {
   6.103 +            assert !functionNode.isVarArg();
   6.104 +            // Just store it to a local variable
   6.105 +            store(symbol);
   6.106 +            return;
   6.107 +        }
   6.108 +        assert functionNode.isVarArg();
   6.109 +        if(functionNode.needsArguments()) {
   6.110 +            loadArguments();
   6.111 +            load(symbol.getFieldIndex());
   6.112 +            ArgumentSetter.SET_ARGUMENT.invoke(this);
   6.113 +        } else {
   6.114 +            // varargs without arguments object - just do array store to __varargs__
   6.115 +            loadVarArgs();
   6.116 +            load(symbol.getFieldIndex());
   6.117 +            ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
   6.118 +        }
   6.119 +    }
   6.120 +
   6.121 +
   6.122 +    /**
   6.123       * Pop a value from the stack and store it in a given local variable
   6.124       * slot.
   6.125       *
     7.1 --- a/src/jdk/nashorn/internal/codegen/objects/FieldObjectCreator.java	Thu Jan 31 20:07:40 2013 +0530
     7.2 +++ b/src/jdk/nashorn/internal/codegen/objects/FieldObjectCreator.java	Thu Jan 31 18:34:42 2013 +0100
     7.3 @@ -70,20 +70,28 @@
     7.4      /**
     7.5       * Constructor
     7.6       *
     7.7 -     * @param codegen  code generator
     7.8 -     * @param keys     keys for fields in object
     7.9 -     * @param symbols  symbols for fields in object
    7.10 -     * @param values   values (or null where no value) to be written to the fields
    7.11 -     * @param isScope  is this a scope object
    7.12 -     * @param isVarArg is this a vararg object
    7.13 +     * @param codegen      code generator
    7.14 +     * @param keys         keys for fields in object
    7.15 +     * @param symbols      symbols for fields in object
    7.16 +     * @param values       values (or null where no value) to be written to the fields
    7.17 +     * @param isScope      is this a scope object
    7.18 +     * @param hasArguments does the created object have an "arguments" object
    7.19       */
    7.20 -    public FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values, final boolean isScope, final boolean isVarArg) {
    7.21 -        super(codegen, keys, symbols, isScope, isVarArg);
    7.22 +    public FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values, final boolean isScope, final boolean hasArguments) {
    7.23 +        super(codegen, keys, symbols, isScope, hasArguments);
    7.24          this.values        = values;
    7.25          this.callSiteFlags = codegen.getCallSiteFlags();
    7.26      }
    7.27  
    7.28      /**
    7.29 +     * Loads the scope on the stack through the passed method emitter.
    7.30 +     * @param method the method emitter to use
    7.31 +     */
    7.32 +    protected void loadScope(final MethodEmitter method) {
    7.33 +        method.loadScope();
    7.34 +    }
    7.35 +
    7.36 +    /**
    7.37       * Construct an object.
    7.38       *
    7.39       * @param method the method emitter
    7.40 @@ -96,7 +104,7 @@
    7.41          loadMap(method); //load the map
    7.42  
    7.43          if (isScope()) {
    7.44 -            method.loadScope();
    7.45 +            loadScope(method);
    7.46  
    7.47              if (isVarArg()) {
    7.48                  method.loadArguments();
     8.1 --- a/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Thu Jan 31 20:07:40 2013 +0530
     8.2 +++ b/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Thu Jan 31 18:34:42 2013 +0100
     8.3 @@ -26,7 +26,7 @@
     8.4  package jdk.nashorn.internal.codegen.objects;
     8.5  
     8.6  import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
     8.7 -import static jdk.nashorn.internal.codegen.Compiler.SCRIPTOBJECT_IMPL_OBJECT;
     8.8 +import static jdk.nashorn.internal.codegen.Compiler.SCRIPTFUNCTION_IMPL_OBJECT;
     8.9  import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
    8.10  import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
    8.11  import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
    8.12 @@ -93,10 +93,14 @@
    8.13           * Instantiate the function object, must be referred to by name as
    8.14           * class is not available at compile time
    8.15           */
    8.16 -        method._new(SCRIPTOBJECT_IMPL_OBJECT).dup();
    8.17 +        method._new(SCRIPTFUNCTION_IMPL_OBJECT).dup();
    8.18          method.load(functionNode.isAnonymous() ? "" : identNode.getName());
    8.19          loadHandle(method, signature);
    8.20 -        method.loadScope();
    8.21 +        if(functionNode.needsParentScope()) {
    8.22 +            method.loadScope();
    8.23 +        } else {
    8.24 +            method.loadNull();
    8.25 +        }
    8.26          method.getStatic(compileUnit.getUnitClassName(), SOURCE.tag(), SOURCE.descriptor());
    8.27          method.load(token);
    8.28          method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
    8.29 @@ -111,7 +115,7 @@
    8.30           */
    8.31          method.load(functionNode.needsCallee());
    8.32          method.load(functionNode.isStrictMode());
    8.33 -        method.invoke(constructorNoLookup(SCRIPTOBJECT_IMPL_OBJECT,
    8.34 +        method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_OBJECT,
    8.35                      String.class,
    8.36                      MethodHandle.class,
    8.37                      ScriptObject.class,
     9.1 --- a/src/jdk/nashorn/internal/codegen/types/Type.java	Thu Jan 31 20:07:40 2013 +0530
     9.2 +++ b/src/jdk/nashorn/internal/codegen/types/Type.java	Thu Jan 31 18:34:42 2013 +0100
     9.3 @@ -779,13 +779,13 @@
     9.4      };
     9.5  
     9.6      /** Singleton for method handle arrays used for properties etc. */
     9.7 -    public static final ArrayType METHODHANDLE_ARRAY = new ArrayType(new MethodHandle[0].getClass());
     9.8 +    public static final ArrayType METHODHANDLE_ARRAY = new ArrayType(MethodHandle[].class);
     9.9  
    9.10      /** This is the singleton for string arrays */
    9.11 -    public static final ArrayType STRING_ARRAY = new ArrayType(new String[0].getClass());
    9.12 +    public static final ArrayType STRING_ARRAY = new ArrayType(String[].class);
    9.13  
    9.14      /** This is the singleton for object arrays */
    9.15 -    public static final ArrayType OBJECT_ARRAY = new ArrayType(new Object[0].getClass());
    9.16 +    public static final ArrayType OBJECT_ARRAY = new ArrayType(Object[].class);
    9.17  
    9.18      /** This type, always an object type, just a toString override */
    9.19      public static final Type THIS = new ObjectType() {
    10.1 --- a/src/jdk/nashorn/internal/ir/Block.java	Thu Jan 31 20:07:40 2013 +0530
    10.2 +++ b/src/jdk/nashorn/internal/ir/Block.java	Thu Jan 31 18:34:42 2013 +0100
    10.3 @@ -584,7 +584,7 @@
    10.4       * Set the needs scope flag.
    10.5       */
    10.6      public void setNeedsScope() {
    10.7 -        this.needsScope = true;
    10.8 +        needsScope = true;
    10.9      }
   10.10  
   10.11  }
    11.1 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Jan 31 20:07:40 2013 +0530
    11.2 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Jan 31 18:34:42 2013 +0100
    11.3 @@ -47,6 +47,7 @@
    11.4  import jdk.nashorn.internal.parser.Parser;
    11.5  import jdk.nashorn.internal.runtime.Source;
    11.6  import jdk.nashorn.internal.runtime.UserAccessorProperty;
    11.7 +import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
    11.8  
    11.9  /**
   11.10   * IR representation for function (or script.)
   11.11 @@ -150,36 +151,40 @@
   11.12      private int flags;
   11.13  
   11.14      /** Is anonymous function flag. */
   11.15 -    private static final int IS_ANONYMOUS            = 0b0000_0000_0000_0001;
   11.16 +    private static final int IS_ANONYMOUS                = 0b0000_0000_0000_0001;
   11.17      /** Is statement flag */
   11.18 -    private static final int IS_STATEMENT            = 0b0000_0000_0000_0010;
   11.19 +    private static final int IS_STATEMENT                = 0b0000_0000_0000_0010;
   11.20      /** is this a strict mode function? */
   11.21 -    private static final int IS_STRICT_MODE          = 0b0000_0000_0000_0100;
   11.22 -    /** is this is a vararg function? */
   11.23 -    private static final int IS_VAR_ARG              = 0b0000_0000_0000_1000;
   11.24 +    private static final int IS_STRICT_MODE              = 0b0000_0000_0000_0100;
   11.25 +    /** Does the function use the "arguments" identifier ? */
   11.26 +    private static final int USES_ARGUMENTS              = 0b0000_0000_0000_1000;
   11.27      /** Are we lowered ? */
   11.28 -    private static final int IS_LOWERED              = 0b0000_0000_0001_0000;
   11.29 +    private static final int IS_LOWERED                  = 0b0000_0000_0001_0000;
   11.30      /** Has this node been split because it was too large? */
   11.31 -    private static final int IS_SPLIT                = 0b0000_0000_0010_0000;
   11.32 +    private static final int IS_SPLIT                    = 0b0000_0000_0010_0000;
   11.33      /** Is this function lazily compiled? */
   11.34 -    private static final int IS_LAZY                 = 0b0000_0000_0100_0000;
   11.35 -    /** Does the function contain eval? */
   11.36 -    private static final int HAS_EVAL                = 0b0000_0000_1000_0000;
   11.37 +    private static final int IS_LAZY                     = 0b0000_0000_0100_0000;
   11.38 +    /** Does the function call eval? */
   11.39 +    private static final int HAS_EVAL                    = 0b0000_0000_1000_0000;
   11.40      /** Does the function contain a with block ? */
   11.41 -    private static final int HAS_WITH                = 0b0000_0001_0000_0000;
   11.42 -    /** Does a child function contain a with or eval? */
   11.43 -    private static final int HAS_CHILD_WITH_OR_EVAL  = 0b0000_0010_0000_0000;
   11.44 -    /** Hide arguments? */
   11.45 -    private static final int HIDE_ARGS               = 0b0000_0100_0000_0000;
   11.46 +    private static final int HAS_WITH                    = 0b0000_0001_0000_0000;
   11.47 +    /** Does a descendant function contain a with or eval? */
   11.48 +    private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0010_0000_0000;
   11.49 +    /** Does the function define "arguments" identifier as a parameter of nested function name? */
   11.50 +    private static final int DEFINES_ARGUMENTS           = 0b0000_0100_0000_0000;
   11.51      /** Does the function need a self symbol? */
   11.52 -    private static final int NEEDS_SELF_SYMBOL       = 0b0000_1000_0000_0000;
   11.53 +    private static final int NEEDS_SELF_SYMBOL           = 0b0000_1000_0000_0000;
   11.54 +    /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
   11.55 +    private static final int USES_ANCESTOR_SCOPE         = 0b0001_0000_0000_0000;
   11.56  
   11.57      /** Does this function or any nested functions contain a with or an eval? */
   11.58 -    private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_CHILD_WITH_OR_EVAL;
   11.59 +    private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
   11.60      /** Does this function need to store all its variables in scope? */
   11.61      private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT;
   11.62 -    /** Does this function need a scope object? */
   11.63 -    private static final int NEEDS_SCOPE           = HAS_ALL_VARS_IN_SCOPE | IS_VAR_ARG;
   11.64 +    /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
   11.65 +    private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
   11.66 +    /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval. */
   11.67 +    private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL;
   11.68  
   11.69      /** What is the return type of this function? */
   11.70      private Type returnType = Type.UNKNOWN;
   11.71 @@ -334,6 +339,11 @@
   11.72          }
   11.73      }
   11.74  
   11.75 +    @Override
   11.76 +    public boolean needsScope() {
   11.77 +        return super.needsScope() || isScript();
   11.78 +    }
   11.79 +
   11.80      /*
   11.81       * Frame management.
   11.82       */
   11.83 @@ -503,12 +513,29 @@
   11.84       * Flag this function as using the {@code with} keyword
   11.85       */
   11.86      public void setHasWith() {
   11.87 -        this.flags |= HAS_WITH;
   11.88 -        // with requires scope in parents.
   11.89 -        FunctionNode parentFunction = findParentFunction();
   11.90 -        while (parentFunction != null) {
   11.91 -            parentFunction.setHasNestedWithOrEval();
   11.92 -            parentFunction = parentFunction.findParentFunction();
   11.93 +        if(!hasWith()) {
   11.94 +            this.flags |= HAS_WITH;
   11.95 +            // with requires scope in parents.
   11.96 +            // TODO: refine this. with should not force all variables in parents to be in scope, only those that are
   11.97 +            // actually referenced as identifiers by name
   11.98 +            markParentForWithOrEval();
   11.99 +        }
  11.100 +    }
  11.101 +
  11.102 +    private void markParentForWithOrEval() {
  11.103 +        // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope.
  11.104 +        setNeedsScope();
  11.105 +
  11.106 +        final FunctionNode parentFunction = findParentFunction();
  11.107 +        if(parentFunction != null) {
  11.108 +            parentFunction.setDescendantHasWithOrEval();
  11.109 +        }
  11.110 +    }
  11.111 +
  11.112 +    private void setDescendantHasWithOrEval() {
  11.113 +        if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
  11.114 +            flags |= HAS_DESCENDANT_WITH_OR_EVAL;
  11.115 +            markParentForWithOrEval();
  11.116          }
  11.117      }
  11.118  
  11.119 @@ -522,22 +549,15 @@
  11.120      }
  11.121  
  11.122      /**
  11.123 -     * Flag this function as using the {@code eval} keyword
  11.124 +     * Flag this function as calling the {@code eval} function
  11.125       */
  11.126      public void setHasEval() {
  11.127 -        this.flags |= HAS_EVAL;
  11.128 -        // eval requires scope in parents.
  11.129 -        FunctionNode parentFunction = findParentFunction();
  11.130 -        while (parentFunction != null) {
  11.131 -            parentFunction.setHasNestedWithOrEval();
  11.132 -            parentFunction = parentFunction.findParentFunction();
  11.133 +        if(!hasEval()) {
  11.134 +            this.flags |= HAS_EVAL;
  11.135 +            markParentForWithOrEval();
  11.136          }
  11.137      }
  11.138  
  11.139 -    private void setHasNestedWithOrEval() {
  11.140 -        flags |= HAS_CHILD_WITH_OR_EVAL;
  11.141 -    }
  11.142 -
  11.143      /**
  11.144       * Test whether this function or any of its nested functions contains a <tt>with</tt> statement
  11.145       * or an <tt>eval</tt> call.
  11.146 @@ -623,11 +643,13 @@
  11.147      }
  11.148  
  11.149      /**
  11.150 -     * Check if this function needs the {@code callee} parameter
  11.151 -     * @return true if the function uses {@code callee}
  11.152 +     * Check if this function's generated Java method needs a {@code callee} parameter. Functions that need access to
  11.153 +     * their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object
  11.154 +     * (since it exposes {@code arguments.callee} property) will need to have a callee parameter.
  11.155 +     * @return true if the function's generated Java method needs a {@code callee} parameter.
  11.156       */
  11.157      public boolean needsCallee() {
  11.158 -        return getCalleeNode() != null;
  11.159 +        return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
  11.160      }
  11.161  
  11.162      /**
  11.163 @@ -666,37 +688,60 @@
  11.164      }
  11.165  
  11.166      /**
  11.167 -     * Check if this function needs to use var args, which is the case for
  11.168 -     * e.g. {@code eval} and functions where {@code arguments} is used
  11.169 -     *
  11.170 -     * @return true if the function needs to use var args
  11.171 +     * Does this function's method needs to be variable arity (gather all script-declared parameters in a final
  11.172 +     * {@code Object[]} parameter. Functions that need to have the "arguments" object as well as functions that simply
  11.173 +     * declare too many arguments for JVM to handle with fixed arity will need to be variable arity.
  11.174 +     * @return true if the Java method in the generated code that implements this function needs to be variable arity.
  11.175 +     * @see #needsArguments()
  11.176 +     * @see LinkerCallSite#ARGLIMIT
  11.177       */
  11.178      public boolean isVarArg() {
  11.179 -        return (flags & IS_VAR_ARG) != 0 && !isScript();
  11.180 +        return needsArguments() || parameters.size() > LinkerCallSite.ARGLIMIT;
  11.181      }
  11.182  
  11.183      /**
  11.184 -     * Flag this function as needing to use var args vector
  11.185 +     * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
  11.186 +     * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
  11.187 +     * defining a local variable named "arguments" still requires construction of the Arguments object (see
  11.188 +     * ECMAScript 5.1 Chapter 10.5).
  11.189 +     * @see #needsArguments()
  11.190       */
  11.191 -    public void setIsVarArg() {
  11.192 -        this.flags |= IS_VAR_ARG;
  11.193 +    public void setDefinesArguments() {
  11.194 +        this.flags |= DEFINES_ARGUMENTS;
  11.195      }
  11.196  
  11.197      /**
  11.198 -     * Ugly special case:
  11.199 -     * Tells the compiler if {@code arguments} variable should be hidden and given
  11.200 -     * a unique name, for example if it is used as an explicit parameter name
  11.201 -     * @return true of {@code arguments} should be hidden
  11.202 +     * Returns true if this function needs to have an Arguments object defined as a local variable named "arguments".
  11.203 +     * Functions that use "arguments" as identifier and don't define it as a name of a parameter or a nested function
  11.204 +     * (see ECMAScript 5.1 Chapter 10.5), as well as any function that uses eval or with, or has a nested function that
  11.205 +     * does the same, will have an "arguments" object. Also, if this function is a script, it will not have an
  11.206 +     * "arguments" object, because it does not have local variables; rather the Global object will have an explicit
  11.207 +     * "arguments" property that provides command-line arguments for the script.
  11.208 +     * @return true if this function needs an arguments object.
  11.209       */
  11.210 -    public boolean hideArguments() {
  11.211 -        return (flags & HIDE_ARGS) != 0;
  11.212 +    public boolean needsArguments() {
  11.213 +        // uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since
  11.214 +        // for top-level script, "arguments" is picked up from Context by Global.init() instead.
  11.215 +        return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isScript();
  11.216      }
  11.217  
  11.218      /**
  11.219 -     * Flag this function as needing to hide the {@code arguments} vectir
  11.220 +     * Flags this function as one that uses the "arguments" identifier.
  11.221 +     * @see #needsArguments()
  11.222       */
  11.223 -    public void setHideArguments() {
  11.224 -        this.flags |= HIDE_ARGS;
  11.225 +    public void setUsesArguments() {
  11.226 +        flags |= USES_ARGUMENTS;
  11.227 +    }
  11.228 +
  11.229 +    /**
  11.230 +     * Returns true if this function needs access to its parent scope. Functions referencing variables outside their
  11.231 +     * scope (including global variables), as well as functions that call eval or have a with block, or have nested
  11.232 +     * functions that call eval or have a with block, will need a parent scope. Top-level script functions also need a
  11.233 +     * parent scope since they might be used from within eval, and eval will need an externally passed scope.
  11.234 +     * @return true if the function needs parent scope.
  11.235 +     */
  11.236 +    public boolean needsParentScope() {
  11.237 +        return (flags & NEEDS_PARENT_SCOPE) != 0 || isScript();
  11.238      }
  11.239  
  11.240      /**
  11.241 @@ -749,14 +794,9 @@
  11.242          this.name = name;
  11.243      }
  11.244  
  11.245 -    @Override
  11.246 -    public boolean needsScope() {
  11.247 -        return needsScope || isScript() || (flags & NEEDS_SCOPE) != 0;
  11.248 -    }
  11.249 -
  11.250      /**
  11.251 -     * Check if this function should have all its variables in scope, regardless
  11.252 -     * of other properties.
  11.253 +     * Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
  11.254 +     * functions having with and/or eval blocks are such.
  11.255       *
  11.256       * @return true if all variables should be in scope
  11.257       */
  11.258 @@ -780,6 +820,7 @@
  11.259       */
  11.260      public void setIsSplit() {
  11.261          this.flags |= IS_SPLIT;
  11.262 +        setNeedsScope();
  11.263      }
  11.264  
  11.265      /**
  11.266 @@ -893,6 +934,38 @@
  11.267      }
  11.268  
  11.269      /**
  11.270 +     * Marks this function as one using any global symbol. The function and all its parent functions will all be marked
  11.271 +     * as needing parent scope.
  11.272 +     * @see #needsParentScope()
  11.273 +     */
  11.274 +    public void setUsesGlobalSymbol() {
  11.275 +        this.flags |= USES_ANCESTOR_SCOPE;
  11.276 +        final FunctionNode parentFn = findParentFunction();
  11.277 +        if(parentFn != null) {
  11.278 +            parentFn.setUsesGlobalSymbol();
  11.279 +        }
  11.280 +    }
  11.281 +
  11.282 +    /**
  11.283 +     * Marks this function as using a specified scoped symbol. The function and its parent functions up to but not
  11.284 +     * including the function defining the symbol will be marked as needing parent scope. The function defining the
  11.285 +     * symbol will be marked as one that needs to have its own scope.
  11.286 +     * @param symbol the symbol being used.
  11.287 +     * @see #needsParentScope()
  11.288 +     */
  11.289 +    public void setUsesScopeSymbol(final Symbol symbol) {
  11.290 +        if(symbol.getBlock() == this) {
  11.291 +            setNeedsScope();
  11.292 +        } else {
  11.293 +            this.flags |= USES_ANCESTOR_SCOPE;
  11.294 +            final FunctionNode parentFn = findParentFunction();
  11.295 +            if(parentFn != null) {
  11.296 +                parentFn.setUsesScopeSymbol(symbol);
  11.297 +            }
  11.298 +        }
  11.299 +    }
  11.300 +
  11.301 +    /**
  11.302       * Return the node representing {@code this} in this function
  11.303       * @return IdentNode representing {@code this}
  11.304       */
    12.1 --- a/src/jdk/nashorn/internal/ir/Symbol.java	Thu Jan 31 20:07:40 2013 +0530
    12.2 +++ b/src/jdk/nashorn/internal/ir/Symbol.java	Thu Jan 31 18:34:42 2013 +0100
    12.3 @@ -83,7 +83,7 @@
    12.4      /** Local variable slot. -1 indicates external property. */
    12.5      private int slot;
    12.6  
    12.7 -    /** Field number in scope or property. */
    12.8 +    /** Field number in scope or property; array index in varargs when not using arguments object. */
    12.9      private int fieldIndex;
   12.10  
   12.11      /** Number of times this symbol is used in code */
   12.12 @@ -330,7 +330,11 @@
   12.13          }
   12.14  
   12.15          if (isScope()) {
   12.16 -            sb.append(" S");
   12.17 +            if(isGlobal()) {
   12.18 +                sb.append(" G");
   12.19 +            } else {
   12.20 +                sb.append(" S");
   12.21 +            }
   12.22          }
   12.23  
   12.24          if (canBePrimitive()) {
   12.25 @@ -382,7 +386,9 @@
   12.26              trace("SET IS SCOPE");
   12.27          }
   12.28          flags |= IS_SCOPE;
   12.29 -        getBlock().setNeedsScope();
   12.30 +        if(!isGlobal()) {
   12.31 +            getBlock().setNeedsScope();
   12.32 +        }
   12.33      }
   12.34  
   12.35      /**
    13.1 --- a/src/jdk/nashorn/internal/objects/Global.java	Thu Jan 31 20:07:40 2013 +0530
    13.2 +++ b/src/jdk/nashorn/internal/objects/Global.java	Thu Jan 31 18:34:42 2013 +0100
    13.3 @@ -1213,7 +1213,7 @@
    13.4       *
    13.5       * @return the new array
    13.6       */
    13.7 -    public static Object allocateArguments(final Object[] arguments, final Object callee, final int numParams) {
    13.8 +    public static ScriptObject allocateArguments(final Object[] arguments, final Object callee, final int numParams) {
    13.9          return NativeArguments.allocate(arguments, (ScriptFunction)callee, numParams);
   13.10      }
   13.11  
    14.1 --- a/src/jdk/nashorn/internal/objects/NativeArguments.java	Thu Jan 31 20:07:40 2013 +0530
    14.2 +++ b/src/jdk/nashorn/internal/objects/NativeArguments.java	Thu Jan 31 18:34:42 2013 +0100
    14.3 @@ -551,7 +551,8 @@
    14.4       * @return Arguments Object
    14.5       */
    14.6      public static ScriptObject allocate(final Object[] arguments, final ScriptFunction callee, final int numParams) {
    14.7 -        final boolean isStrict = callee.isStrict();
    14.8 +        // Strict functions won't always have a callee for arguments, and will pass null instead.
    14.9 +        final boolean isStrict = callee == null || callee.isStrict();
   14.10          return isStrict ? new NativeStrictArguments(arguments, numParams) : new NativeArguments(arguments, callee, numParams);
   14.11      }
   14.12  
    15.1 --- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Thu Jan 31 20:07:40 2013 +0530
    15.2 +++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Thu Jan 31 18:34:42 2013 +0100
    15.3 @@ -49,6 +49,7 @@
    15.4      // per-function object flags
    15.5      private static final int IS_STRICT  = 0b0000_0001;
    15.6      private static final int IS_BUILTIN = 0b0000_0010;
    15.7 +    private static final int HAS_CALLEE = 0b0000_0100;
    15.8  
    15.9      // set this function to be a builtin function
   15.10      private void setIsBuiltin() {
   15.11 @@ -221,6 +222,16 @@
   15.12      }
   15.13  
   15.14      @Override
   15.15 +    public final boolean hasCalleeParameter() {
   15.16 +        return (flags & HAS_CALLEE) != 0;
   15.17 +    }
   15.18 +
   15.19 +    @Override
   15.20 +    protected void setHasCalleeParameter() {
   15.21 +        flags |= HAS_CALLEE;
   15.22 +    }
   15.23 +
   15.24 +    @Override
   15.25      public final boolean isBuiltin() {
   15.26          return (flags & IS_BUILTIN) != 0;
   15.27      }
    16.1 --- a/src/jdk/nashorn/internal/parser/Parser.java	Thu Jan 31 20:07:40 2013 +0530
    16.2 +++ b/src/jdk/nashorn/internal/parser/Parser.java	Thu Jan 31 18:34:42 2013 +0100
    16.3 @@ -98,7 +98,6 @@
    16.4  import jdk.nashorn.internal.runtime.Context;
    16.5  import jdk.nashorn.internal.runtime.JSErrorType;
    16.6  import jdk.nashorn.internal.runtime.ParserException;
    16.7 -import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
    16.8  
    16.9  /**
   16.10   * Builds the IR.
   16.11 @@ -334,7 +333,6 @@
   16.12  
   16.13          if (EVAL.tag().equals(name)) {
   16.14              function.setHasEval();
   16.15 -            function.setIsVarArg();
   16.16          }
   16.17      }
   16.18  
   16.19 @@ -346,7 +344,7 @@
   16.20          final String name = ident.getName();
   16.21  
   16.22          if (ARGUMENTS.tag().equals(name)) {
   16.23 -            function.setIsVarArg();
   16.24 +            function.setUsesArguments();
   16.25          }
   16.26      }
   16.27  
   16.28 @@ -2517,6 +2515,9 @@
   16.29  
   16.30          if (isStatement && !isInWithBlock()) {
   16.31              functionNode.setIsStatement();
   16.32 +            if(ARGUMENTS.tag().equals(name.getName())) {
   16.33 +                functionNode.findParentFunction().setDefinesArguments();
   16.34 +            }
   16.35          }
   16.36  
   16.37          if (isAnonymous) {
   16.38 @@ -2536,7 +2537,7 @@
   16.39                  String parameterName = parameter.getName();
   16.40  
   16.41                  if (ARGUMENTS.tag().equals(parameterName)) {
   16.42 -                    functionNode.setHideArguments();
   16.43 +                    functionNode.setDefinesArguments();
   16.44                  }
   16.45  
   16.46                  if (parametersSet.contains(parameterName)) {
   16.47 @@ -2555,14 +2556,10 @@
   16.48              }
   16.49          } else if (arity == 1) {
   16.50              if (ARGUMENTS.tag().equals(parameters.get(0).getName())) {
   16.51 -                functionNode.setHideArguments();
   16.52 +                functionNode.setDefinesArguments();
   16.53              }
   16.54          }
   16.55  
   16.56 -        if (arity > LinkerCallSite.ARGLIMIT) {
   16.57 -            functionNode.setIsVarArg();
   16.58 -        }
   16.59 -
   16.60          if (isStatement) {
   16.61              final VarNode var = new VarNode(source, functionToken, finish, name, referenceNode);
   16.62              if (isInWithBlock()) {
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/src/jdk/nashorn/internal/runtime/ArgumentSetter.java	Thu Jan 31 18:34:42 2013 +0100
    17.3 @@ -0,0 +1,67 @@
    17.4 +/*
    17.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    17.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    17.7 + *
    17.8 + * This code is free software; you can redistribute it and/or modify it
    17.9 + * under the terms of the GNU General Public License version 2 only, as
   17.10 + * published by the Free Software Foundation.  Oracle designates this
   17.11 + * particular file as subject to the "Classpath" exception as provided
   17.12 + * by Oracle in the LICENSE file that accompanied this code.
   17.13 + *
   17.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   17.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   17.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   17.17 + * version 2 for more details (a copy is included in the LICENSE file that
   17.18 + * accompanied this code).
   17.19 + *
   17.20 + * You should have received a copy of the GNU General Public License version
   17.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   17.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   17.23 + *
   17.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   17.25 + * or visit www.oracle.com if you need additional information or have any
   17.26 + * questions.
   17.27 + */
   17.28 +package jdk.nashorn.internal.runtime;
   17.29 +
   17.30 +import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
   17.31 +
   17.32 +import jdk.nashorn.internal.codegen.CompilerConstants.Call;
   17.33 +
   17.34 +/**
   17.35 + * A class with static helper methods invoked from generated bytecode for setting values of parameters of variable-arity
   17.36 + * functions.
   17.37 + */
   17.38 +public class ArgumentSetter {
   17.39 +
   17.40 +    /** Method handle for setting a function argument at a given index in an arguments object. Used from generated bytecode */
   17.41 +    public static final Call SET_ARGUMENT      = staticCall(ArgumentSetter.class, "setArgument", void.class, Object.class, ScriptObject.class, int.class);
   17.42 +
   17.43 +    /** Method handle for setting a function argument at a given index in an arguments array. Used from generated bytecode */
   17.44 +    public static final Call SET_ARRAY_ELEMENT = staticCall(ArgumentSetter.class, "setArrayElement", void.class, Object.class, Object[].class, int.class);
   17.45 +
   17.46 +
   17.47 +    /**
   17.48 +     * Used from generated bytecode to invoke {@link ScriptObject#setArgument(int, Object)} without having to reorder
   17.49 +     * the arguments on the stack. When we're generating a store into the argument, we first have the value on the
   17.50 +     * stack, and only afterwards load the target object and the index.
   17.51 +     * @param value the value to write at the given argument index.
   17.52 +     * @param arguments the arguments object that we're writing the value to
   17.53 +     * @param key the index of the argument
   17.54 +     */
   17.55 +    public static void setArgument(final Object value, final ScriptObject arguments, final int key) {
   17.56 +        arguments.setArgument(key, value);
   17.57 +    }
   17.58 +
   17.59 +    /**
   17.60 +     * Used from generated bytecode to set a variable arity parameter - an array element - without having to reorder
   17.61 +     * the arguments on the stack. When we're generating a store into the array, we first have the value on the
   17.62 +     * stack, and only afterwards load the target array and the index.
   17.63 +     * @param value the value to write at the given argument index.
   17.64 +     * @param arguments the arguments array that we're writing the value to
   17.65 +     * @param key the index of the argument
   17.66 +     */
   17.67 +    public static void setArrayElement(final Object value, final Object[] arguments, final int key) {
   17.68 +        arguments[key] = value;
   17.69 +    }
   17.70 +}
    18.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu Jan 31 20:07:40 2013 +0530
    18.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu Jan 31 18:34:42 2013 +0100
    18.3 @@ -128,7 +128,28 @@
    18.4              final PropertyMap map,
    18.5              final ScriptObject scope,
    18.6              final MethodHandle[] specs) {
    18.7 -        this(name, methodHandle, map, scope, null, 0, false, specs);
    18.8 +        this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
    18.9 +    }
   18.10 +
   18.11 +    /**
   18.12 +     * Heuristic to figure out if the method handle has a callee argument. If it's type is either
   18.13 +     * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
   18.14 +     * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
   18.15 +     * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
   18.16 +     * they also always receive a callee.
   18.17 +     * @param methodHandle the examined method handle
   18.18 +     * @return true if the method handle expects a callee, false otherwise
   18.19 +     */
   18.20 +    private static boolean needsCallee(MethodHandle methodHandle) {
   18.21 +        final MethodType type = methodHandle.type();
   18.22 +        final int len = type.parameterCount();
   18.23 +        if(len == 0) {
   18.24 +            return false;
   18.25 +        }
   18.26 +        if(type.parameterType(0) == boolean.class) {
   18.27 +            return len > 2 && type.parameterType(2) == ScriptFunction.class;
   18.28 +        }
   18.29 +        return len > 1 && type.parameterType(1) == ScriptFunction.class;
   18.30      }
   18.31  
   18.32      /**
   18.33 @@ -193,27 +214,27 @@
   18.34              constructorCount++;
   18.35          }
   18.36  
   18.37 -        // needCallee => scope != null
   18.38 -        assert !needsCallee || scope != null;
   18.39          this.name   = name;
   18.40          this.source = source;
   18.41          this.token  = token;
   18.42          this.scope  = scope;
   18.43 +        if(needsCallee) {
   18.44 +            setHasCalleeParameter();
   18.45 +        }
   18.46  
   18.47          final MethodType type       = methodHandle.type();
   18.48          final int        paramCount = type.parameterCount();
   18.49          final boolean    isVarArg   = type.parameterType(paramCount - 1).isArray();
   18.50  
   18.51 -        final MethodHandle mh = MH.asType(methodHandle, adaptType(type, scope != null, isVarArg));
   18.52 +        final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
   18.53  
   18.54          this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
   18.55  
   18.56 +        if (needsCallee && !isVarArg) {
   18.57 +            this.arity--;
   18.58 +        }
   18.59 +
   18.60          if (scope != null) {
   18.61 -            if (needsCallee) {
   18.62 -                if (!isVarArg) {
   18.63 -                    this.arity--;
   18.64 -                }
   18.65 -            }
   18.66              this.invokeHandle    = mh;
   18.67              this.constructHandle = mh;
   18.68          } else if (isConstructor(mh)) {
   18.69 @@ -698,9 +719,8 @@
   18.70          return hasCalleeParameter() ? MH.bindTo(bound, this) : bound;
   18.71      }
   18.72  
   18.73 -    private boolean hasCalleeParameter() {
   18.74 -        return scope != null;
   18.75 -    }
   18.76 +    protected abstract boolean hasCalleeParameter();
   18.77 +    protected abstract void setHasCalleeParameter();
   18.78  
   18.79      /**
   18.80       * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
   18.81 @@ -878,12 +898,9 @@
   18.82          MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
   18.83  
   18.84          if (hasCalleeParameter()) {
   18.85 -            final MethodHandle allocate = MH.bindTo(MethodHandles.exactInvoker(ALLOCATE.type()), ScriptFunction.ALLOCATE);
   18.86 -            handle = MH.foldArguments(handle, allocate);
   18.87 -            handle = MH.asType(handle, handle.type().changeParameterType(0, Object.class)); // ScriptFunction => Object
   18.88 +            handle = MH.foldArguments(handle, ALLOCATE);
   18.89          } else {
   18.90 -            final MethodHandle allocate = MH.dropArguments(MH.bindTo(ScriptFunction.ALLOCATE, this), 0, Object.class);
   18.91 -            handle = MH.filterArguments(handle, 0, allocate);
   18.92 +            handle = MH.filterArguments(handle, 0, ALLOCATE);
   18.93          }
   18.94  
   18.95          final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type);
    19.1 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Thu Jan 31 20:07:40 2013 +0530
    19.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Thu Jan 31 18:34:42 2013 +0100
    19.3 @@ -566,13 +566,12 @@
    19.4       * This is 'delete' that always fails. We have to check strict mode and throw error.
    19.5       * That is why this is a runtime function. Or else we could have inlined 'false'.
    19.6       *
    19.7 -     * @param obj       object with property to delete
    19.8       * @param property  property to delete
    19.9       * @param strict    are we in strict mode
   19.10       *
   19.11       * @return false always
   19.12       */
   19.13 -    public static boolean FAIL_DELETE(final Object obj, final Object property, final Object strict) {
   19.14 +    public static boolean FAIL_DELETE(final Object property, final Object strict) {
   19.15          if (Boolean.TRUE.equals(strict)) {
   19.16              syntaxError("strict.cant.delete", safeToString(property));
   19.17          }
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/test/script/basic/JDK-8006529-b.js	Thu Jan 31 18:34:42 2013 +0100
    20.3 @@ -0,0 +1,65 @@
    20.4 +/*
    20.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    20.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    20.7 + * 
    20.8 + * This code is free software; you can redistribute it and/or modify it
    20.9 + * under the terms of the GNU General Public License version 2 only, as
   20.10 + * published by the Free Software Foundation.
   20.11 + * 
   20.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   20.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   20.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   20.15 + * version 2 for more details (a copy is included in the LICENSE file that
   20.16 + * accompanied this code).
   20.17 + * 
   20.18 + * You should have received a copy of the GNU General Public License version
   20.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   20.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20.21 + * 
   20.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   20.23 + * or visit www.oracle.com if you need additional information or have any
   20.24 + * questions.
   20.25 + */
   20.26 +
   20.27 +/**
   20.28 + * JDK-8006529 : Constructor functions that don't need callees must not get
   20.29 + * linked with a MethodHandle boud to a specific function instance.
   20.30 + * @test
   20.31 + * @run
   20.32 + */
   20.33 +
   20.34 +Object.defineProperty(Object.prototype, "extends", {
   20.35 +  value: function (superConstructor) {
   20.36 +    function ProtoBridge() { }
   20.37 +    ProtoBridge.prototype = superConstructor.prototype;
   20.38 +    this.prototype = new ProtoBridge();
   20.39 +    this.superConstructor = superConstructor;
   20.40 +  }
   20.41 +});
   20.42 +
   20.43 +function A() {
   20.44 +}
   20.45 +A.prototype.f = function () {
   20.46 +  this.g();
   20.47 +}
   20.48 +
   20.49 +function B() {
   20.50 +  B.superConstructor.call(this);
   20.51 +  this.f();
   20.52 +}
   20.53 +B.extends(A);
   20.54 +
   20.55 +B.prototype.g = function () {
   20.56 +  print("It worked!")
   20.57 +}
   20.58 +
   20.59 +function C() {
   20.60 +  C.superConstructor.call(this);
   20.61 +}
   20.62 +C.extends(B);
   20.63 +
   20.64 +var x = [B, C]
   20.65 +for(var i in x) {
   20.66 +  print("Doing " + i)
   20.67 +  new x[i]()
   20.68 +}
   20.69 \ No newline at end of file
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/test/script/basic/JDK-8006529-b.js.EXPECTED	Thu Jan 31 18:34:42 2013 +0100
    21.3 @@ -0,0 +1,4 @@
    21.4 +Doing 0
    21.5 +It worked!
    21.6 +Doing 1
    21.7 +It worked!
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/test/script/basic/JDK-8006529.js	Thu Jan 31 18:34:42 2013 +0100
    22.3 @@ -0,0 +1,158 @@
    22.4 +/*
    22.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    22.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    22.7 + * 
    22.8 + * This code is free software; you can redistribute it and/or modify it
    22.9 + * under the terms of the GNU General Public License version 2 only, as
   22.10 + * published by the Free Software Foundation.
   22.11 + * 
   22.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   22.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   22.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   22.15 + * version 2 for more details (a copy is included in the LICENSE file that
   22.16 + * accompanied this code).
   22.17 + * 
   22.18 + * You should have received a copy of the GNU General Public License version
   22.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   22.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   22.21 + * 
   22.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22.23 + * or visit www.oracle.com if you need additional information or have any
   22.24 + * questions.
   22.25 + */
   22.26 +
   22.27 +/**
   22.28 + * JDK-8006529 : Methods should not always get callee parameter, and they
   22.29 + * should not be too eager in creation of scopes.
   22.30 + *
   22.31 + * @test
   22.32 + * @run
   22.33 + */
   22.34 +
   22.35 +// compile(script) -- compiles a script specified as a string with its 
   22.36 +// source code, returns a jdk.nashorn.internal.ir.FunctionNode object 
   22.37 +// representing it.
   22.38 +var compile = (function() {
   22.39 +    var Compiler       = Java.type("jdk.nashorn.internal.codegen.Compiler")
   22.40 +    var Context        = Java.type("jdk.nashorn.internal.runtime.Context")
   22.41 +    var Source         = Java.type("jdk.nashorn.internal.runtime.Source")
   22.42 +    var CompilerAccess = Java.type("jdk.nashorn.internal.codegen.CompilerAccess")
   22.43 +    return function(source) {
   22.44 +	    var compiler = Compiler.compiler(new Source("<no name>", source), Context.getContext())
   22.45 +        compiler.compile()
   22.46 +        return CompilerAccess.getScriptNode(compiler)
   22.47 +    }
   22.48 +})();
   22.49 +
   22.50 +var allAssertions = (function() {
   22.51 +    var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'needsScope', 'needsSelfSymbol', 'isSplit', 'hasEval', 'hasWith', 'hasDeepWithOrEval', 'varsInScope', 'isStrictMode']
   22.52 +    var allAssertions = {}
   22.53 +    for(var assertion in allAssertionList) {
   22.54 +        allAssertions[allAssertionList[assertion]] = true
   22.55 +    }
   22.56 +    return allAssertions;
   22.57 +})();
   22.58 +
   22.59 +// test(f[, assertions...]) tests whether all the specified assertions on the
   22.60 +// passed function node are true.
   22.61 +function test(f) {
   22.62 +    var assertions = {}
   22.63 +    for(var i = 1; i < arguments.length; ++i) {
   22.64 +        var assertion = arguments[i]
   22.65 +        if(!allAssertions[assertion]) {
   22.66 +            throw "Unknown assertion " + assertion + " for " + f;
   22.67 +        }
   22.68 +        assertions[assertion] = true
   22.69 +    }
   22.70 +    for(var assertion in allAssertions) {
   22.71 +        var expectedValue = !!assertions[assertion]
   22.72 +        if(f[assertion] == null) {
   22.73 +            throw "Can't find " + assertion + " on " + f;
   22.74 +        }
   22.75 +        if(f[assertion]() !== expectedValue) {
   22.76 +            throw "Expected " + assertion + " === " + expectedValue + " for " + f;
   22.77 +        }
   22.78 +    }
   22.79 +}
   22.80 +
   22.81 +// testFirstFn(script[, assertions...] tests whether all the specified
   22.82 +// assertions are true in the first function in the given script; "script"
   22.83 +// is a string with the source text of the script.
   22.84 +function testFirstFn(script) {
   22.85 +    arguments[0] = compile(script).functions[0]
   22.86 +    test.apply(null, arguments)
   22.87 +}
   22.88 +
   22.89 +// ---------------------------------- ACTUAL TESTS START HERE --------------
   22.90 +
   22.91 +// The simplest possible functions have no attributes set
   22.92 +testFirstFn("function f() { }")
   22.93 +testFirstFn("function f(x) { x }")
   22.94 +
   22.95 +// A function referencing a global needs parent scope, and it needs callee
   22.96 +// (because parent scope is passed through callee)
   22.97 +testFirstFn("function f() { x }", 'needsCallee', 'needsParentScope')
   22.98 +
   22.99 +// A function referencing "arguments" will have to be vararg. It also needs
  22.100 +// the callee, as it needs to fill out "arguments.callee".
  22.101 +testFirstFn("function f() { arguments }", 'needsCallee', 'isVarArg')
  22.102 +
  22.103 +// A function referencing "arguments" will have to be vararg. If it is
  22.104 +// strict, it will not have to have a callee, though.
  22.105 +testFirstFn("function f() {'use strict'; arguments }", 'isVarArg', 'isStrictMode')
  22.106 +
  22.107 +// A function defining "arguments" as a parameter will not be vararg.
  22.108 +testFirstFn("function f(arguments) { arguments }")
  22.109 +
  22.110 +// A function defining "arguments" as a nested function will not be vararg.
  22.111 +testFirstFn("function f() { function arguments() {}; arguments; }")
  22.112 +
  22.113 +// A function defining "arguments" as a local variable will be vararg.
  22.114 +testFirstFn("function f() { var arguments; arguments; }", 'isVarArg', 'needsCallee')
  22.115 +
  22.116 +// A self-referencing function defined as a statement doesn't need a self 
  22.117 +// symbol, as it'll rather obtain itself from the parent scope.
  22.118 +testFirstFn("function f() { f() }", 'needsCallee', 'needsParentScope')
  22.119 +
  22.120 +// A self-referencing function defined as an expression needs a self symbol,
  22.121 +// as it can't obtain itself from the parent scope.
  22.122 +testFirstFn("(function f() { f() })", 'needsCallee', 'needsSelfSymbol')
  22.123 +
  22.124 +// A child function accessing parent's variable triggers the need for scope
  22.125 +// in parent
  22.126 +testFirstFn("(function f() { var x; function g() { x } })", 'needsScope')
  22.127 +
  22.128 +// A child function accessing parent's parameter triggers the need for scope
  22.129 +// in parent
  22.130 +testFirstFn("(function f(x) { function g() { x } })", 'needsScope')
  22.131 +
  22.132 +// A child function accessing a global variable triggers the need for parent
  22.133 +// scope in parent
  22.134 +testFirstFn("(function f() { function g() { x } })", 'needsParentScope', 'needsCallee')
  22.135 +
  22.136 +// A child function redefining a local variable from its parent should not 
  22.137 +// affect the parent function in any way
  22.138 +testFirstFn("(function f() { var x; function g() { var x; x } })")
  22.139 +
  22.140 +// Using "with" unleashes a lot of needs: parent scope, callee, own scope, 
  22.141 +// and all variables in scope. Actually, we could make "with" less wasteful,
  22.142 +// and only put those variables in scope that it actually references, similar
  22.143 +// to what nested functions do with variables in their parents.
  22.144 +testFirstFn("(function f() { var o; with(o) {} })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasWith', 'hasDeepWithOrEval', 'varsInScope')
  22.145 +
  22.146 +// Using "eval" is as bad as using "with" with the added requirement of
  22.147 +// being vararg, 'cause we don't know if eval will be using "arguments".
  22.148 +testFirstFn("(function f() { eval() })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasEval', 'isVarArg', 'hasDeepWithOrEval', 'varsInScope')
  22.149 +
  22.150 +// Nested function using "with" is pretty much the same as the parent
  22.151 +// function needing with.
  22.152 +testFirstFn("(function f() { function g() { var o; with(o) {} } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'varsInScope')
  22.153 +// Nested function using "eval" is almost the same as parent function using
  22.154 +// eval, but at least the parent doesn't have to be vararg.
  22.155 +testFirstFn("(function f() { function g() { eval() } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'varsInScope')
  22.156 +
  22.157 +// Function with 250 named parameters is ordinary
  22.158 +testFirstFn("function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, p63, p64, p65, p66, p67, p68, p69, p70, p71, p72, p73, p74, p75, p76, p77, p78, p79, p80, p81, p82, p83, p84, p85, p86, p87, p88, p89, p90, p91, p92, p93, p94, p95, p96, p97, p98, p99, p100, p101, p102, p103, p104, p105, p106, p107, p108, p109, p110, p111, p112, p113, p114, p115, p116, p117, p118, p119, p120, p121, p122, p123, p124, p125, p126, p127, p128, p129, p130, p131, p132, p133, p134, p135, p136, p137, p138, p139, p140, p141, p142, p143, p144, p145, p146, p147, p148, p149, p150, p151, p152, p153, p154, p155, p156, p157, p158, p159, p160, p161, p162, p163, p164, p165, p166, p167, p168, p169, p170, p171, p172, p173, p174, p175, p176, p177, p178, p179, p180, p181, p182, p183, p184, p185, p186, p187, p188, p189, p190, p191, p192, p193, p194, p195, p196, p197, p198, p199, p200, p201, p202, p203, p204, p205, p206, p207, p208, p209, p210, p211, p212, p213, p214, p215, p216, p217, p218, p219, p220, p221, p222, p223, p224, p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p235, p236, p237, p238, p239, p240, p241, p242, p243, p244, p245, p246, p247, p248, p249, p250) { p250 = p249 }")
  22.159 +
  22.160 +// Function with 251 named parameters is variable arguments
  22.161 +testFirstFn("function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, p63, p64, p65, p66, p67, p68, p69, p70, p71, p72, p73, p74, p75, p76, p77, p78, p79, p80, p81, p82, p83, p84, p85, p86, p87, p88, p89, p90, p91, p92, p93, p94, p95, p96, p97, p98, p99, p100, p101, p102, p103, p104, p105, p106, p107, p108, p109, p110, p111, p112, p113, p114, p115, p116, p117, p118, p119, p120, p121, p122, p123, p124, p125, p126, p127, p128, p129, p130, p131, p132, p133, p134, p135, p136, p137, p138, p139, p140, p141, p142, p143, p144, p145, p146, p147, p148, p149, p150, p151, p152, p153, p154, p155, p156, p157, p158, p159, p160, p161, p162, p163, p164, p165, p166, p167, p168, p169, p170, p171, p172, p173, p174, p175, p176, p177, p178, p179, p180, p181, p182, p183, p184, p185, p186, p187, p188, p189, p190, p191, p192, p193, p194, p195, p196, p197, p198, p199, p200, p201, p202, p203, p204, p205, p206, p207, p208, p209, p210, p211, p212, p213, p214, p215, p216, p217, p218, p219, p220, p221, p222, p223, p224, p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p235, p236, p237, p238, p239, p240, p241, p242, p243, p244, p245, p246, p247, p248, p249, p250, p251) { p250 = p251 }", 'isVarArg')
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/test/src/jdk/nashorn/internal/codegen/CompilerAccess.java	Thu Jan 31 18:34:42 2013 +0100
    23.3 @@ -0,0 +1,35 @@
    23.4 +
    23.5 +package jdk.nashorn.internal.codegen;
    23.6 +
    23.7 +import java.lang.reflect.Field;
    23.8 +import jdk.nashorn.internal.ir.FunctionNode;
    23.9 +
   23.10 +/**
   23.11 + * Since Compiler class doesn't give us access to its private {@code functionNode} field, we use this reflection-based
   23.12 + * access-check disabling helper to get to it in compilation tests.
   23.13 + *
   23.14 + */
   23.15 +public class CompilerAccess {
   23.16 +    private static final Field FUNCTION_NODE_FIELD = getCompilerFunctionNodeField();
   23.17 +    static {
   23.18 +        FUNCTION_NODE_FIELD.setAccessible(true);
   23.19 +    }
   23.20 +
   23.21 +    /**
   23.22 +     * Given a compiler, return its {@code functionNode} field, representing the root function (i.e. the compiled script).
   23.23 +     * @param compiler the compiler that already run its {@link Compiler#compile()} method.
   23.24 +     * @return the root function node representing the compiled script.
   23.25 +     * @throws IllegalAccessException
   23.26 +     */
   23.27 +    public static FunctionNode getScriptNode(Compiler compiler) throws IllegalAccessException {
   23.28 +        return (FunctionNode)FUNCTION_NODE_FIELD.get(compiler);
   23.29 +    }
   23.30 +
   23.31 +    private static Field getCompilerFunctionNodeField() {
   23.32 +        try {
   23.33 +            return Compiler.class.getDeclaredField("functionNode");
   23.34 +        } catch (NoSuchFieldException e) {
   23.35 +            throw new AssertionError("", e);
   23.36 +        }
   23.37 +    }
   23.38 +}

mercurial