Thu, 31 Jan 2013 18:34:42 +0100
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
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 +}