# HG changeset patch # User lana # Date 1410403072 25200 # Node ID 00cc73c2862df86d059f67e93a29a1639de44dae # Parent b2287cac7813c70ed7f679d9a46fe774bd4005f8# Parent b40ef4386b01bc7861f2cf085801d4e0fc4ed640 Merge diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Sep 10 19:37:52 2014 -0700 @@ -194,12 +194,12 @@ */ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers. - // body.accept(new NodeVisitor(new LexicalContext()) { @Override - public boolean enterFunctionNode(final FunctionNode nestedFn) { - // Don't descend into nested functions - return false; + protected boolean enterDefault(final Node node) { + // Don't bother visiting expressions; var is a statement, it can't be inside an expression. + // This will also prevent visiting nested functions (as FunctionNode is an expression). + return !(node instanceof Expression); } @Override @@ -443,12 +443,27 @@ if (lc.isFunctionBody()) { block.clearSymbols(); + final FunctionNode fn = lc.getCurrentFunction(); + if (isUnparsedFunction(fn)) { + // It's a skipped nested function. Just mark the symbols being used by it as being in use. + for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) { + nameIsUsed(name, null); + } + // Don't bother descending into it, it must be empty anyway. + assert block.getStatements().isEmpty(); + return false; + } + enterFunctionBody(); } return true; } + private boolean isUnparsedFunction(final FunctionNode fn) { + return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); + } + @Override public boolean enterCatchNode(final CatchNode catchNode) { final IdentNode exception = catchNode.getException(); @@ -492,18 +507,13 @@ @Override public boolean enterFunctionNode(final FunctionNode functionNode) { - // TODO: once we have information on symbols used by nested functions, we can stop descending into nested - // functions with on-demand compilation, e.g. add - // if(!thisProperties.isEmpty() && env.isOnDemandCompilation()) { - // return false; - // } start(functionNode, false); thisProperties.push(new HashSet()); - //an outermost function in our lexical context that is not a program - //is possible - it is a function being compiled lazily if (functionNode.isDeclared()) { + // Can't use lc.getCurrentBlock() as we can have an outermost function in our lexical context that + // is not a program - it is a function being compiled on-demand. final Iterator blocks = lc.getBlocks(); if (blocks.hasNext()) { final IdentNode ident = functionNode.getIdent(); @@ -511,6 +521,11 @@ } } + // Every function has a body, even the ones skipped on reparse (they have an empty one). We're + // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that + // are used in them. + assert functionNode.getBody() != null; + return true; } @@ -533,7 +548,7 @@ /** * This has to run before fix assignment types, store any type specializations for - * paramters, then turn then to objects for the generic version of this method + * parameters, then turn them into objects for the generic version of this method. * * @param functionNode functionNode */ @@ -733,14 +748,20 @@ @Override public Node leaveBlock(final Block block) { - // It's not necessary to guard the marking of symbols as locals with this "if"condition for correctness, it's - // just an optimization -- runtime type calculation is not used when the compilation is not an on-demand - // optimistic compilation, so we can skip locals marking then. + // It's not necessary to guard the marking of symbols as locals with this "if" condition for + // correctness, it's just an optimization -- runtime type calculation is not used when the compilation + // is not an on-demand optimistic compilation, so we can skip locals marking then. if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { - for (final Symbol symbol: block.getSymbols()) { - if (!symbol.isScope()) { - assert symbol.isVar() || symbol.isParam(); - compiler.declareLocalSymbol(symbol.getName()); + // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand + // compilation, and we're skipping parsing the function bodies for nested functions, this + // basically only means their parameters. It'd be enough to mistakenly declare to be a local a + // symbol in the outer function named the same as one of the parameters, though. + if (lc.getFunction(block) == lc.getOutermostFunction()) { + for (final Symbol symbol: block.getSymbols()) { + if (!symbol.isScope()) { + assert symbol.isVar() || symbol.isParam(); + compiler.declareLocalSymbol(symbol.getName()); + } } } } @@ -811,24 +832,45 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { - - return markProgramBlock( + final FunctionNode finalizedFunction; + if (isUnparsedFunction(functionNode)) { + finalizedFunction = functionNode; + } else { + finalizedFunction = + markProgramBlock( removeUnusedSlots( createSyntheticInitializers( finalizeParameters( lc.applyTopFlags(functionNode)))) - .setThisProperties(lc, thisProperties.pop().size()) - .setState(lc, CompilationState.SYMBOLS_ASSIGNED)); + .setThisProperties(lc, thisProperties.pop().size())); + } + return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED); } @Override public Node leaveIdentNode(final IdentNode identNode) { - final String name = identNode.getName(); - if (identNode.isPropertyName()) { return identNode; } + final Symbol symbol = nameIsUsed(identNode.getName(), identNode); + + if (!identNode.isInitializedHere()) { + symbol.increaseUseCount(); + } + + IdentNode newIdentNode = identNode.setSymbol(symbol); + + // If a block-scoped var is used before its declaration mark it as dead. + // We can only statically detect this for local vars, cross-function symbols require runtime checks. + if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) { + newIdentNode = newIdentNode.markDead(); + } + + return end(newIdentNode); + } + + private Symbol nameIsUsed(final String name, final IdentNode origin) { final Block block = lc.getCurrentBlock(); Symbol symbol = findSymbol(block, name); @@ -847,24 +889,11 @@ maybeForceScope(symbol); } else { log.info("No symbol exists. Declare as global: ", name); - symbol = defineSymbol(block, name, identNode, IS_GLOBAL | IS_SCOPE); + symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE); } functionUsesSymbol(symbol); - - if (!identNode.isInitializedHere()) { - symbol.increaseUseCount(); - } - - IdentNode newIdentNode = identNode.setSymbol(symbol); - - // If a block-scoped var is used before its declaration mark it as dead. - // We can only statically detect this for local vars, cross-function symbols require runtime checks. - if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) { - newIdentNode = newIdentNode.markDead(); - } - - return end(newIdentNode); + return symbol; } @Override @@ -912,7 +941,6 @@ return functionNode; } - assert functionNode.getId() == 1; return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE)); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ClassEmitter.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ClassEmitter.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ClassEmitter.java Wed Sep 10 19:37:52 2014 -0700 @@ -59,6 +59,7 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.Set; + import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; @@ -135,6 +136,16 @@ /** Set of constants access methods required. */ private Set> constantMethodNeeded; + private int methodCount; + + private int initCount; + + private int clinitCount; + + private int fieldCount; + + private final Set methodNames; + /** * Constructor - only used internally in this class as it breaks * abstraction towards ASM or other code generator below @@ -146,6 +157,11 @@ this.context = context; this.cw = cw; this.methodsStarted = new HashSet<>(); + this.methodNames = new HashSet<>(); + } + + public Set getMethodNames() { + return methodNames; } /** @@ -209,6 +225,38 @@ } /** + * Get the method count, including init and clinit methods + * @return method count + */ + public int getMethodCount() { + return methodCount; + } + + /** + * Get the clinit count + * @return clinit count + */ + public int getClinitCount() { + return clinitCount; + } + + /** + * Get the init count + * @return init count + */ + public int getInitCount() { + return initCount; + } + + /** + * Get the field count + * @return field count + */ + public int getFieldCount() { + return fieldCount; + } + + /** * Convert a binary name to a package/class name. * * @param name Binary name. @@ -359,9 +407,16 @@ */ @Override public void end() { - assert classStarted; + assert classStarted : "class not started for " + unitClassName; if (unitClassName != null) { + final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE)); + initMethod.begin(); + initMethod.load(Type.OBJECT, 0); + initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class); + initMethod.returnVoid(); + initMethod.end(); + defineCommonUtilities(); } @@ -419,6 +474,8 @@ } SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class rtype, final Class... ptypes) { + methodCount++; + methodNames.add(methodName); return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); } @@ -446,6 +503,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter method(final EnumSet methodFlags, final String methodName, final Class rtype, final Class... ptypes) { + methodCount++; + methodNames.add(methodName); return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes)); } @@ -471,6 +530,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter method(final EnumSet methodFlags, final String methodName, final String descriptor) { + methodCount++; + methodNames.add(methodName); return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null)); } @@ -481,6 +542,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter method(final FunctionNode functionNode) { + methodCount++; + methodNames.add(functionNode.getName()); final FunctionSignature signature = new FunctionSignature(functionNode); final MethodVisitor mv = cw.visitMethod( ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0), @@ -499,6 +562,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter restOfMethod(final FunctionNode functionNode) { + methodCount++; + methodNames.add(functionNode.getName()); final MethodVisitor mv = cw.visitMethod( ACC_PUBLIC | ACC_STATIC, functionNode.getName(), @@ -516,6 +581,7 @@ * @return method emitter to use for weaving */ MethodEmitter clinit() { + clinitCount++; return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class); } @@ -525,6 +591,7 @@ * @return method emitter to use for weaving ()V */ MethodEmitter init() { + initCount++; return method(INIT.symbolName(), void.class); } @@ -535,6 +602,7 @@ * @return method emitter to use for weaving ()V */ MethodEmitter init(final Class... ptypes) { + initCount++; return method(INIT.symbolName(), void.class, ptypes); } @@ -547,6 +615,7 @@ * @return method emitter to use for weaving (...)V */ MethodEmitter init(final EnumSet flags, final Class... ptypes) { + initCount++; return method(flags, INIT.symbolName(), void.class, ptypes); } @@ -561,6 +630,7 @@ * @see ClassEmitter.Flag */ final void field(final EnumSet fieldFlags, final String fieldName, final Class fieldType, final Object value) { + fieldCount++; cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd(); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Sep 10 19:37:52 2014 -0700 @@ -1622,9 +1622,18 @@ @Override protected void evaluate() { - method.load(ITERATOR_TYPE, iterSlot); - // TODO: optimistic for-in iteration - method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class)); + new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) { + @Override + void loadStack() { + method.load(ITERATOR_TYPE, iterSlot); + } + + @Override + void consumeStack() { + method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class)); + convertOptimisticReturnValue(); + } + }.emit(); } }.store(); body.accept(this); diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Wed Sep 10 19:37:52 2014 -0700 @@ -31,6 +31,7 @@ import java.util.Deque; import java.util.HashMap; import java.util.Map; + import jdk.nashorn.internal.IntDeque; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Block; @@ -158,7 +159,9 @@ CompileUnit popCompileUnit(final CompileUnit oldUnit) { assert compileUnits.peek() == oldUnit; - compileUnits.pop(); + final CompileUnit unit = compileUnits.pop(); + assert unit.hasCode() : "compile unit popped without code"; + unit.setUsed(); return compileUnits.isEmpty() ? null : compileUnits.peek(); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Sep 10 19:37:52 2014 -0700 @@ -48,6 +48,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; + import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.ir.FunctionNode; @@ -300,6 +301,7 @@ } }, + /** * Reuse compile units, if they are already present. We are using the same compiler * to recompile stuff @@ -334,6 +336,8 @@ if (phases.isRestOfCompilation()) { sb.append("$restOf"); } + //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what + //fills those out anyway. Thus no need for a copy constructor final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); map.put(oldUnit, newUnit); @@ -430,8 +434,14 @@ FunctionNode newFunctionNode = fn; + //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped + //in CodeGeneration - the rest can be used as a working "is compile unit used" metric + fn.getCompileUnit().setUsed(); + compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); + final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); + try { // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program // in the lazy + optimistic world. See CodeGenerator.skipFunction(). @@ -455,12 +465,18 @@ final ClassEmitter classEmitter = compileUnit.getClassEmitter(); classEmitter.end(); + if (!compileUnit.isUsed()) { + compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); + continue; + } + final byte[] bytecode = classEmitter.toByteArray(); assert bytecode != null; final String className = compileUnit.getUnitClassName(); + compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used - compiler.addClass(className, bytecode); + CompileUnit.increaseEmitCount(); // should we verify the generated code? if (senv._verify_code) { @@ -536,6 +552,9 @@ // initialize function in the compile units for (final CompileUnit unit : compiler.getCompileUnits()) { + if (!unit.isUsed()) { + continue; + } unit.setCode(installedClasses.get(unit.getUnitClassName())); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompileUnit.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompileUnit.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompileUnit.java Wed Sep 10 19:37:52 2014 -0700 @@ -42,6 +42,10 @@ private Class clazz; + private boolean isUsed; + + private static int emittedUnitCount; + CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) { this.className = className; this.weight = initialWeight; @@ -52,6 +56,33 @@ return new TreeSet<>(); } + static void increaseEmitCount() { + emittedUnitCount++; + } + + public static int getEmittedUnitCount() { + return emittedUnitCount; + } + + /** + * Check if this compile unit is used + * @return true if tagged as in use - i.e active code that needs to be generated + */ + public boolean isUsed() { + return isUsed; + } + + public boolean hasCode() { + return (classEmitter.getMethodCount() - classEmitter.getInitCount() - classEmitter.getClinitCount()) > 0; + } + + /** + * Tag this compile unit as used + */ + public void setUsed() { + this.isUsed = true; + } + /** * Return the class that contains the code for this unit, null if not * generated yet @@ -121,7 +152,8 @@ @Override public String toString() { - return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']'; + final String methods = classEmitter != null ? classEmitter.getMethodNames().toString() : ""; + return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + " hasCode=" + methods + ']'; } @Override diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Wed Sep 10 19:37:52 2014 -0700 @@ -38,7 +38,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -51,8 +50,8 @@ import java.util.function.Consumer; import java.util.logging.Level; import jdk.internal.dynalink.support.NameCodec; -import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.Optimistic; import jdk.nashorn.internal.ir.debug.ClassHistogramElement; @@ -65,6 +64,7 @@ import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; @@ -248,6 +248,15 @@ return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); } + @SuppressWarnings("unused") //TODO I'll use this soon + private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) { + final LinkedList list = new LinkedList<>(); + for (final CompilationPhase p : phases) { + list.add(p == phase ? newPhase : p); + } + return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); + } + private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) { final LinkedList list = new LinkedList<>(); for (final CompilationPhase p : phases) { @@ -473,6 +482,19 @@ return typeEvaluator.getOptimisticType(node); } + /** + * Returns true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property names retrieved through + * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its + * property name types. + * @param expr the expression to test + * @return true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property iterators. + */ + boolean hasStringPropertyIterator(final Expression expr) { + return typeEvaluator.hasStringPropertyIterator(expr); + } + void addInvalidatedProgramPoint(final int programPoint, final Type type) { invalidatedProgramPoints.put(programPoint, type); } @@ -675,16 +697,8 @@ CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) { final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict()); final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); - classEmitter.begin(); - final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE)); - initMethod.begin(); - initMethod.load(Type.OBJECT, 0); - initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class); - initMethod.returnVoid(); - initMethod.end(); - return compileUnit; } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Sep 10 19:37:52 2014 -0700 @@ -551,13 +551,19 @@ final Expression init = forNode.getInit(); if(forNode.isForIn()) { - forNode.getModify().accept(this); - enterTestFirstLoop(forNode, null, init); + final JoinPredecessorExpression iterable = forNode.getModify(); + iterable.accept(this); + enterTestFirstLoop(forNode, null, init, + // If we're iterating over property names, and we can discern from the runtime environment + // of the compilation that the object being iterated over must use strings for property + // names (e.g., it is a native JS object or array), then we'll not bother trying to treat + // the property names optimistically. + !forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression())); } else { if(init != null) { init.accept(this); } - enterTestFirstLoop(forNode, forNode.getModify(), null); + enterTestFirstLoop(forNode, forNode.getModify(), null, false); } return false; } @@ -792,7 +798,8 @@ return false; } - private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, final Expression iteratorValues) { + private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, + final Expression iteratorValues, final boolean iteratorValuesAreObject) { final JoinPredecessorExpression test = loopNode.getTest(); if(isAlwaysFalse(test)) { test.accept(this); @@ -814,8 +821,12 @@ jumpToLabel(test, breakLabel); } if(iteratorValues instanceof IdentNode) { - // Receives iterator values; they're currently all objects (JDK-8034954). - onAssignment((IdentNode)iteratorValues, LvarType.OBJECT); + final IdentNode ident = (IdentNode)iteratorValues; + // Receives iterator values; the optimistic type of the iterator values is tracked on the + // identifier, but we override optimism if it's known that the object being iterated over will + // never have primitive property names. + onAssignment(ident, iteratorValuesAreObject ? LvarType.OBJECT : + toLvarType(compiler.getOptimisticType(ident))); } final Block body = loopNode.getBody(); body.accept(this); @@ -955,7 +966,7 @@ if(whileNode.isDoWhile()) { enterDoWhileLoop(whileNode); } else { - enterTestFirstLoop(whileNode, null, null); + enterTestFirstLoop(whileNode, null, null, false); } return false; } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Wed Sep 10 19:37:52 2014 -0700 @@ -71,7 +71,6 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; -import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Source; @@ -93,9 +92,6 @@ private final DebugLogger log; - // needed only to get unique eval id - private final CodeInstaller installer; - /** * Constructor. */ @@ -143,7 +139,6 @@ } }); - this.installer = compiler.getCodeInstaller(); this.log = initLogger(compiler.getContext()); } @@ -566,16 +561,13 @@ private String evalLocation(final IdentNode node) { final Source source = lc.getCurrentFunction().getSource(); final int pos = node.position(); - // Code installer is null when running with --compile-only, use 0 as id in that case - final long id = installer == null ? 0 : installer.getUniqueEvalId(); return new StringBuilder(). append(source.getName()). append('#'). append(source.getLine(pos)). append(':'). append(source.getColumn(pos)). - append("@"). - append(id). + append(""). toString(); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectClassGenerator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Wed Sep 10 19:37:52 2014 -0700 @@ -53,6 +53,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; + import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.AccessorProperty; @@ -308,7 +309,7 @@ newEmptyInit(className, classEmitter); newAllocate(className, classEmitter); - return toByteArray(classEmitter); + return toByteArray(className, classEmitter); } /** @@ -341,7 +342,7 @@ initWithArguments.returnVoid(); initWithArguments.end(); - return toByteArray(classEmitter); + return toByteArray(className, classEmitter); } /** @@ -484,15 +485,13 @@ * @param classEmitter Open class emitter. * @return Byte codes for the class. */ - private byte[] toByteArray(final ClassEmitter classEmitter) { + private byte[] toByteArray(final String className, final ClassEmitter classEmitter) { classEmitter.end(); final byte[] code = classEmitter.toByteArray(); final ScriptEnvironment env = context.getEnv(); - if (env._print_code && env._print_code_dir == null) { - env.getErr().println(ClassEmitter.disassemble(code)); - } + DumpBytecode.dumpBytecode(env, log, code, className); if (env._verify_code) { context.verify(code); diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Sep 10 19:37:52 2014 -0700 @@ -55,6 +55,19 @@ this.runtimeScope = runtimeScope; } + /** + * Returns true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property names retrieved through + * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its + * property name types. + * @param expr the expression to test + * @return true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property iterators. + */ + boolean hasStringPropertyIterator(final Expression expr) { + return evaluateSafely(expr) instanceof ScriptObject; + } + Type getOptimisticType(final Optimistic node) { assert compiler.useOptimisticTypes(); diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Wed Sep 10 19:37:52 2014 -0700 @@ -277,6 +277,14 @@ } /** + * Returns the number of statements in the block. + * @return the number of statements in the block. + */ + public int getStatementCount() { + return statements.size(); + } + + /** * Returns the line number of the first statement in the block. * @return the line number of the first statement in the block, or -1 if the block has no statements. */ diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CompileUnitHolder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CompileUnitHolder.java Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.ir; + +import jdk.nashorn.internal.codegen.CompileUnit; + +/** + * Marker interface for things in the IR that can hold compile units. + * {@link CompileUnit} + */ +public interface CompileUnitHolder { + /** + * Return the compile unit held by this instance + * @return compile unit + */ + public CompileUnit getCompileUnit(); +} diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Wed Sep 10 19:37:52 2014 -0700 @@ -34,10 +34,8 @@ import java.util.Collections; import java.util.EnumSet; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; import java.util.function.Function; import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.CompileUnit; @@ -48,6 +46,7 @@ import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.UserAccessorProperty; @@ -57,7 +56,7 @@ * IR representation for function (or script.) */ @Immutable -public final class FunctionNode extends LexicalContextExpression implements Flags { +public final class FunctionNode extends LexicalContextExpression implements Flags, CompileUnitHolder { /** Type used for all FunctionNodes */ public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class); @@ -110,8 +109,11 @@ /** Source of entity. */ private final Source source; - /** Unique ID used for recompilation among other things */ - private final int id; + /** + * Opaque object representing parser state at the end of the function. Used when reparsing outer functions + * to skip parsing inner functions. + */ + private final Object endParserState; /** External function identifier. */ @Ignore @@ -256,6 +258,14 @@ /** trace callsite values in this function? */ public static final int IS_TRACE_VALUES = 1 << 26; + /** + * Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a + * parameter on invocation. Note that we aren't, in fact using this flag in function nodes. + * Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData} + * will, however, cache the value of this flag. + */ + public static final int NEEDS_CALLEE = 1 << 27; + /** extension callsite flags mask */ public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST | @@ -271,16 +281,9 @@ /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; - /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. - * We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */ + /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. */ private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL; - /** Used to signify "null", e.g. if someone asks for the parent of the program node */ - public static final int NO_FUNCTION_ID = 0; - - /** Where to start assigning global and unique function node ids */ - public static final int FIRST_FUNCTION_ID = NO_FUNCTION_ID + 1; - /** What is the return type of this function? */ private Type returnType = Type.UNKNOWN; @@ -288,11 +291,10 @@ * Constructor * * @param source the source - * @param id unique id * @param lineNumber line number * @param token token * @param finish finish - * @param firstToken first token of the funtion node (including the function declaration) + * @param firstToken first token of the function node (including the function declaration) * @param namespace the namespace * @param ident the identifier * @param name the name of the function @@ -302,7 +304,6 @@ */ public FunctionNode( final Source source, - final int id, final int lineNumber, final long token, final int finish, @@ -316,7 +317,6 @@ super(token, finish); this.source = source; - this.id = id; this.lineNumber = lineNumber; this.ident = ident; this.name = name; @@ -331,11 +331,13 @@ this.body = null; this.thisProperties = 0; this.rootClass = null; + this.endParserState = null; } private FunctionNode( final FunctionNode functionNode, final long lastToken, + Object endParserState, final int flags, final String name, final Type returnType, @@ -347,6 +349,7 @@ final Class rootClass) { super(functionNode); + this.endParserState = endParserState; this.lineNumber = functionNode.lineNumber; this.flags = flags; this.name = name; @@ -361,7 +364,6 @@ // the fields below never change - they are final and assigned in constructor this.source = functionNode.source; - this.id = functionNode.id; this.ident = functionNode.ident; this.namespace = functionNode.namespace; this.kind = functionNode.kind; @@ -429,11 +431,11 @@ } /** - * Get the unique ID for this function + * Get the unique ID for this function within the script file. * @return the id */ public int getId() { - return id; + return position(); } /** @@ -535,6 +537,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -606,6 +609,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -644,15 +648,24 @@ } /** - * Check if the {@code eval} keyword is used in this function + * Check if this function has a call expression for the identifier "eval" (that is, {@code eval(...)}). * - * @return true if {@code eval} is used + * @return true if {@code eval} is called. */ public boolean hasEval() { return getFlag(HAS_EVAL); } /** + * Returns true if a function nested (directly or transitively) within this function {@link #hasEval()}. + * + * @return true if a nested function calls {@code eval}. + */ + public boolean hasNestedEval() { + return getFlag(HAS_NESTED_EVAL); + } + + /** * Get the first token for this function * @return the first token */ @@ -741,6 +754,7 @@ new FunctionNode( this, lastToken, + endParserState, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : @@ -839,6 +853,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -899,6 +914,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -911,6 +927,41 @@ } /** + * Returns the end parser state for this function. + * @return the end parser state for this function. + */ + public Object getEndParserState() { + return endParserState; + } + + /** + * Set the end parser state for this function. + * @param lc lexical context + * @param endParserState the parser state to set + * @return function node or a new one if state was changed + */ + public FunctionNode setEndParserState(final LexicalContext lc, final Object endParserState) { + if (this.endParserState == endParserState) { + return this; + } + return Node.replaceInLexicalContext( + lc, + this, + new FunctionNode( + this, + lastToken, + endParserState, + flags, + name, + returnType, + compileUnit, + compilationState, + body, + parameters, + thisProperties, rootClass)); + } + + /** * Get the name of this function * @return the name */ @@ -934,6 +985,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -999,6 +1051,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -1077,6 +1130,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, type, @@ -1102,6 +1156,7 @@ * @see Compiler * @return the compile unit */ + @Override public CompileUnit getCompileUnit() { return compileUnit; } @@ -1123,6 +1178,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -1178,6 +1234,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Wed Sep 10 19:37:52 2014 -0700 @@ -351,8 +351,7 @@ } /** - * Get the function for this block. If the block is itself a function - * this returns identity + * Get the function for this block. * @param block block for which to get function * @return function for block */ diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Wed Sep 10 19:37:52 2014 -0700 @@ -603,7 +603,7 @@ * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can * be split if they are too large, for bytecode generation reasons */ - public static final class ArrayUnit { + public static final class ArrayUnit implements CompileUnitHolder { /** Compile unit associated with the postsets range. */ private final CompileUnit compileUnit; @@ -642,6 +642,7 @@ * The array compile unit * @return array compile unit */ + @Override public CompileUnit getCompileUnit() { return compileUnit; } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitNode.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitNode.java Wed Sep 10 19:37:52 2014 -0700 @@ -39,7 +39,7 @@ * Node indicating code is split across classes. */ @Immutable -public class SplitNode extends LexicalContextStatement implements Labels { +public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder { /** Split node method name. */ private final String name; @@ -116,6 +116,7 @@ * Get the compile unit for this split node * @return compile unit */ + @Override public CompileUnit getCompileUnit() { return compileUnit; } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java Wed Sep 10 19:37:52 2014 -0700 @@ -631,6 +631,24 @@ } /** + * Returns a method handle that creates a wrapper object for a JS primitive value. + * + * @param self receiver object + * @return method handle to create wrapper objects for primitive receiver + */ + public static MethodHandle getPrimitiveWrapFilter(final Object self) { + if (self instanceof String || self instanceof ConsString) { + return NativeString.WRAPFILTER; + } else if (self instanceof Number) { + return NativeNumber.WRAPFILTER; + } else if (self instanceof Boolean) { + return NativeBoolean.WRAPFILTER; + } + throw new IllegalArgumentException("Unsupported primitive: " + self); + } + + + /** * Create a new empty script object * * @return the new ScriptObject diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeBoolean.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeBoolean.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeBoolean.java Wed Sep 10 19:37:52 2014 -0700 @@ -51,9 +51,9 @@ public final class NativeBoolean extends ScriptObject { private final boolean value; - // Method handle to create an object wrapper for a primitive boolean - private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class)); - // Method handle to retrieve the Boolean prototype object + /** Method handle to create an object wrapper for a primitive boolean. */ + static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class)); + /** Method handle to retrieve the Boolean prototype object. */ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); // initialized by nasgen diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java Wed Sep 10 19:37:52 2014 -0700 @@ -28,6 +28,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -697,7 +698,7 @@ if (methodHandle != null) { return new GuardedInvocation( methodHandle, - testJSAdaptor(adaptee, findData.getGetter(Object.class, UnwarrantedOptimismException.INVALID_PROGRAM_POINT), findData.getOwner(), func), + testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func), adaptee.getProtoSwitchPoint(hook, findData.getOwner())); } } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeNumber.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeNumber.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeNumber.java Wed Sep 10 19:37:52 2014 -0700 @@ -57,9 +57,9 @@ @ScriptClass("Number") public final class NativeNumber extends ScriptObject { - // Method handle to create an object wrapper for a primitive number - private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class)); - // Method handle to retrieve the Number prototype object + /** Method handle to create an object wrapper for a primitive number. */ + static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class)); + /** Method handle to retrieve the Number prototype object. */ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); /** ECMA 15.7.3.2 largest positive finite value */ diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java Wed Sep 10 19:37:52 2014 -0700 @@ -71,9 +71,9 @@ private final CharSequence value; - // Method handle to create an object wrapper for a primitive string - private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); - // Method handle to retrieve the String prototype object + /** Method handle to create an object wrapper for a primitive string */ + static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); + /** Method handle to retrieve the String prototype object */ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); // initialized by nasgen diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/AbstractParser.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/AbstractParser.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/AbstractParser.java Wed Sep 10 19:37:52 2014 -0700 @@ -326,18 +326,28 @@ } /** - * Check next token and advance. + * Check current token and advance to the next token. * * @param expected Expected tokenType. * * @throws ParserException on unexpected token type */ protected final void expect(final TokenType expected) throws ParserException { + expectDontAdvance(expected); + next(); + } + + /** + * Check current token, but don't advance to the next token. + * + * @param expected Expected tokenType. + * + * @throws ParserException on unexpected token type + */ + protected final void expectDontAdvance(final TokenType expected) throws ParserException { if (type != expected) { throw error(expectMessage(expected)); } - - next(); } /** diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java Wed Sep 10 19:37:52 2014 -0700 @@ -35,6 +35,7 @@ import static jdk.nashorn.internal.parser.TokenType.ESCSTRING; import static jdk.nashorn.internal.parser.TokenType.EXECSTRING; import static jdk.nashorn.internal.parser.TokenType.FLOATING; +import static jdk.nashorn.internal.parser.TokenType.FUNCTION; import static jdk.nashorn.internal.parser.TokenType.HEXADECIMAL; import static jdk.nashorn.internal.parser.TokenType.LBRACE; import static jdk.nashorn.internal.parser.TokenType.LPAREN; @@ -85,6 +86,9 @@ /** Type of last token added. */ private TokenType last; + private final boolean pauseOnFunctionBody; + private boolean pauseOnNextLeftBrace; + private static final String SPACETAB = " \t"; // ASCII space and tab private static final String LFCR = "\n\r"; // line feed and carriage return (ctrl-m) @@ -182,20 +186,23 @@ * @param scripting are we in scripting mode */ public Lexer(final Source source, final TokenStream stream, final boolean scripting) { - this(source, 0, source.getLength(), stream, scripting); + this(source, 0, source.getLength(), stream, scripting, false); } /** - * Contructor + * Constructor * * @param source the source * @param start start position in source from which to start lexing * @param len length of source segment to lex * @param stream token stream to lex * @param scripting are we in scripting mode + * @param pauseOnFunctionBody if true, lexer will return from {@link #lexify()} when it encounters a + * function body. This is used with the feature where the parser is skipping nested function bodies to + * avoid reading ahead unnecessarily when we skip the function bodies. */ - public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting) { + public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean pauseOnFunctionBody) { super(source.getContent(), 1, start, len); this.source = source; this.stream = stream; @@ -203,6 +210,8 @@ this.nested = false; this.pendingLine = 1; this.last = EOL; + + this.pauseOnFunctionBody = pauseOnFunctionBody; } private Lexer(final Lexer lexer, final State state) { @@ -216,6 +225,7 @@ pendingLine = state.pendingLine; linePosition = state.linePosition; last = EOL; + pauseOnFunctionBody = false; } static class State extends Scanner.State { @@ -810,6 +820,9 @@ final int length = scanIdentifier(); // Check to see if it is a keyword. final TokenType type = TokenLookup.lookupKeyword(content, start, length); + if (type == FUNCTION && pauseOnFunctionBody) { + pauseOnNextLeftBrace = true; + } // Add keyword or identifier token. add(type, start); } @@ -1597,6 +1610,9 @@ // We break to let the parser decide what it is. if (canStartLiteral(type)) { break; + } else if (type == LBRACE && pauseOnNextLeftBrace) { + pauseOnNextLeftBrace = false; + break; } } else if (Character.isJavaIdentifierStart(ch0) || ch0 == '\\' && ch1 == 'u') { // Scan and add identifier or keyword. diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Wed Sep 10 19:37:52 2014 -0700 @@ -148,7 +148,7 @@ /** to receive line information from Lexer when scanning multine literals. */ protected final Lexer.LineInfoReceiver lineInfoReceiver; - private int nextFunctionId; + private RecompilableScriptFunctionData reparsedFunction; /** * Constructor @@ -171,7 +171,7 @@ * @param log debug logger if one is needed */ public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final DebugLogger log) { - this(env, source, errors, strict, FunctionNode.FIRST_FUNCTION_ID, 0, log); + this(env, source, errors, strict, 0, log); } /** @@ -181,15 +181,13 @@ * @param source source to parse * @param errors error manager * @param strict parser created with strict mode enabled. - * @param nextFunctionId starting value for assigning new unique ids to function nodes * @param lineOffset line offset to start counting lines from * @param log debug logger if one is needed */ - public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int nextFunctionId, final int lineOffset, final DebugLogger log) { + public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) { super(source, errors, strict, lineOffset); this.env = env; this.namespace = new Namespace(env.getNamespace()); - this.nextFunctionId = nextFunctionId; this.scripting = env._scripting; if (this.scripting) { this.lineInfoReceiver = new Lexer.LineInfoReceiver() { @@ -228,6 +226,16 @@ } /** + * Sets the {@link RecompilableScriptFunctionData} representing the function being reparsed (when this + * parser instance is used to reparse a previously parsed function, as part of its on-demand compilation). + * This will trigger various special behaviors, such as skipping nested function bodies. + * @param reparsedFunction the function being reparsed. + */ + public void setReparsedFunction(final RecompilableScriptFunctionData reparsedFunction) { + this.reparsedFunction = reparsedFunction; + } + + /** * Execute parse and return the resulting function node. * Errors will be thrown and the error manager will contain information * if parsing should fail @@ -264,7 +272,7 @@ try { stream = new TokenStream(); - lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions); + lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, reparsedFunction != null); lexer.line = lexer.pendingLine = lineOffset + 1; line = lineOffset; @@ -472,7 +480,6 @@ final FunctionNode functionNode = new FunctionNode( source, - nextFunctionId++, functionLine, token, Token.descPosition(token), @@ -2828,10 +2835,14 @@ FunctionNode functionNode = null; long lastToken = 0L; + final boolean parseBody; + Object endParserState = null; try { // Create a new function block. functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine); - + assert functionNode != null; + final int functionId = functionNode.getId(); + parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); // Nashorn extension: expression closures if (!env._no_syntax_extensions && type != LBRACE) { /* @@ -2847,34 +2858,143 @@ assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); // EOL uses length field to store the line number final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken)); - final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); - appendStatement(returnNode); + // Only create the return node if we aren't skipping nested functions. Note that we aren't + // skipping parsing of these extended functions; they're considered to be small anyway. Also, + // they don't end with a single well known token, so it'd be very hard to get correctly (see + // the note below for reasoning on skipping happening before instead of after RBRACE for + // details). + if (parseBody) { + final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); + appendStatement(returnNode); + } functionNode.setFinish(lastFinish); - } else { - expect(LBRACE); - - // Gather the function elements. - final List prevFunctionDecls = functionDeclarations; - functionDeclarations = new ArrayList<>(); - try { - sourceElements(false); - addFunctionDeclarations(functionNode); - } finally { - functionDeclarations = prevFunctionDecls; + expectDontAdvance(LBRACE); + if (parseBody || !skipFunctionBody(functionNode)) { + next(); + // Gather the function elements. + final List prevFunctionDecls = functionDeclarations; + functionDeclarations = new ArrayList<>(); + try { + sourceElements(false); + addFunctionDeclarations(functionNode); + } finally { + functionDeclarations = prevFunctionDecls; + } + + lastToken = token; + if (parseBody) { + // Since the lexer can read ahead and lexify some number of tokens in advance and have + // them buffered in the TokenStream, we need to produce a lexer state as it was just + // before it lexified RBRACE, and not whatever is its current (quite possibly well read + // ahead) state. + endParserState = new ParserState(Token.descPosition(token), line, linePosition); + + // NOTE: you might wonder why do we capture/restore parser state before RBRACE instead of + // after RBRACE; after all, we could skip the below "expect(RBRACE);" if we captured the + // state after it. The reason is that RBRACE is a well-known token that we can expect and + // will never involve us getting into a weird lexer state, and as such is a great reparse + // point. Typical example of a weird lexer state after RBRACE would be: + // function this_is_skipped() { ... } "use strict"; + // because lexer is doing weird off-by-one maneuvers around string literal quotes. Instead + // of compensating for the possibility of a string literal (or similar) after RBRACE, + // we'll rather just restart parsing from this well-known, friendly token instead. + } } - - lastToken = token; expect(RBRACE); functionNode.setFinish(finish); } } finally { functionNode = restoreFunctionNode(functionNode, lastToken); } + + // NOTE: we can only do alterations to the function node after restoreFunctionNode. + + if (parseBody) { + functionNode = functionNode.setEndParserState(lc, endParserState); + } else if (functionNode.getBody().getStatementCount() > 0){ + // This is to ensure the body is empty when !parseBody but we couldn't skip parsing it (see + // skipFunctionBody() for possible reasons). While it is not strictly necessary for correctness to + // enforce empty bodies in nested functions that were supposed to be skipped, we do assert it as + // an invariant in few places in the compiler pipeline, so for consistency's sake we'll throw away + // nested bodies early if we were supposed to skip 'em. + functionNode = functionNode.setBody(null, functionNode.getBody().setStatements(null, + Collections.emptyList())); + } + + if (reparsedFunction != null) { + // We restore the flags stored in the function's ScriptFunctionData that we got when we first + // eagerly parsed the code. We're doing it because some flags would be set based on the + // content of the function, or even content of its nested functions, most of which are normally + // skipped during an on-demand compilation. + final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId()); + if (data != null) { + // Data can be null if when we originally parsed the file, we removed the function declaration + // as it was dead code. + functionNode = functionNode.setFlags(lc, data.getFunctionFlags()); + // This compensates for missing markEval() in case the function contains an inner function + // that contains eval(), that now we didn't discover since we skipped the inner function. + if (functionNode.hasNestedEval()) { + assert functionNode.hasScopeBlock(); + functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(null)); + } + } + } printAST(functionNode); return functionNode; } + private boolean skipFunctionBody(final FunctionNode functionNode) { + if (reparsedFunction == null) { + // Not reparsing, so don't skip any function body. + return false; + } + // Skip to the RBRACE of this function, and continue parsing from there. + final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId()); + if (data == null) { + // Nested function is not known to the reparsed function. This can happen if the FunctionNode was + // in dead code that was removed. Both FoldConstants and Lower prune dead code. In that case, the + // FunctionNode was dropped before a RecompilableScriptFunctionData could've been created for it. + return false; + } + final ParserState parserState = (ParserState)data.getEndParserState(); + assert parserState != null; + + stream.reset(); + lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions); + line = parserState.line; + linePosition = parserState.linePosition; + // Doesn't really matter, but it's safe to treat it as if there were a semicolon before + // the RBRACE. + type = SEMICOLON; + k = -1; + next(); + + return true; + } + + /** + * Encapsulates part of the state of the parser, enough to reconstruct the state of both parser and lexer + * for resuming parsing after skipping a function body. + */ + private static class ParserState { + private final int position; + private final int line; + private final int linePosition; + + ParserState(final int position, final int line, final int linePosition) { + this.position = position; + this.line = line; + this.linePosition = linePosition; + } + + Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting) { + final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, true); + newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON)); + return newLexer; + } + } + private void printAST(final FunctionNode functionNode) { if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { env.getErr().println(new ASTWriter(functionNode)); @@ -3247,6 +3367,9 @@ } else { lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL); } + // NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip + // parsing a nested function. functionBody() contains code to compensate for the lack of invoking + // this method when the parser skips a nested function. lc.setBlockNeedsScope(lc.getFunctionBody(fn)); } } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenStream.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenStream.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenStream.java Wed Sep 10 19:37:52 2014 -0700 @@ -209,4 +209,8 @@ in = count; buffer = newBuffer; } + + void reset() { + in = out = count = base = 0; + } } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java Wed Sep 10 19:37:52 2014 -0700 @@ -80,12 +80,6 @@ public long getUniqueScriptId(); /** - * Get next unique eval id - * @return unique eval id - */ - public long getUniqueEvalId(); - - /** * Store a compiled script for later reuse * @param source the script source * @param mainClassName the main class name diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Wed Sep 10 19:37:52 2014 -0700 @@ -27,7 +27,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; - import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -39,6 +38,7 @@ import java.util.TreeMap; import java.util.function.Supplier; import java.util.logging.Level; + import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Wed Sep 10 19:37:52 2014 -0700 @@ -196,16 +196,11 @@ } @Override - public long getUniqueEvalId() { - return context.getUniqueEvalId(); - } - - @Override - public void storeScript(final String classInfoFile, final Source source, final String mainClassName, + public void storeScript(final String cacheKey, final Source source, final String mainClassName, final Map classBytes, final Map initializers, final Object[] constants, final int compilationId) { if (context.codeStore != null) { - context.codeStore.storeScript(classInfoFile, source, mainClassName, classBytes, initializers, constants, compilationId); + context.codeStore.storeScript(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId); } } @@ -334,9 +329,6 @@ /** Unique id for script. Used only when --loader-per-compile=false */ private final AtomicLong uniqueScriptId; - /** Unique id for 'eval' */ - private final AtomicLong uniqueEvalId; - /** Optional class filter to use for Java classes. Can be null. */ private final ClassFilter classFilter; @@ -450,7 +442,6 @@ this.uniqueScriptId = new AtomicLong(); } this.errors = errors; - this.uniqueEvalId = new AtomicLong(); // if user passed -classpath option, make a class loader with that and set it as // thread context class loader so that script can access classes from that path. @@ -1190,10 +1181,6 @@ }, CREATE_LOADER_ACC_CTXT); } - private long getUniqueEvalId() { - return uniqueEvalId.getAndIncrement(); - } - private long getUniqueScriptId() { return uniqueScriptId.getAndIncrement(); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/FindProperty.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/FindProperty.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/FindProperty.java Wed Sep 10 19:37:52 2014 -0700 @@ -29,7 +29,9 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import java.lang.invoke.MethodHandle; +import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.codegen.ObjectClassGenerator; +import jdk.nashorn.internal.objects.Global; /** * This class represents the result from a find property search. @@ -79,25 +81,17 @@ * @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic * @return method handle for the getter */ - public MethodHandle getGetter(final Class type, final int programPoint) { + public MethodHandle getGetter(final Class type, final int programPoint, final LinkRequest request) { final MethodHandle getter; if (isValid(programPoint)) { getter = property.getOptimisticGetter(type, programPoint); } else { getter = property.getGetter(type); } - return getGetterInner(getter); - } - - private MethodHandle getGetterInner(final MethodHandle getter) { if (property instanceof UserAccessorProperty) { - final UserAccessorProperty uc = (UserAccessorProperty)property; - final ScriptObject owner = getOwner(); - final ScriptObject container = (owner != null) ? owner : self; - return MH.insertArguments(getter, 0, uc.getAccessors(container)); + return insertAccessorsGetter((UserAccessorProperty) property, request, getter); } return getter; - } /** @@ -111,18 +105,31 @@ * * @return method handle for the getter */ - public MethodHandle getSetter(final Class type, final boolean strict) { - final MethodHandle setter = property.getSetter(type, getOwner().getMap()); + public MethodHandle getSetter(final Class type, final boolean strict, final LinkRequest request) { + MethodHandle setter = property.getSetter(type, getOwner().getMap()); if (property instanceof UserAccessorProperty) { - final UserAccessorProperty uc = (UserAccessorProperty)property; - final ScriptObject owner = getOwner(); - final ScriptObject container = (owner != null) ? owner : self; - return MH.insertArguments(setter, 0, uc.getAccessors(container), strict ? property.getKey() : null); + setter = MH.insertArguments(setter, 1, strict ? property.getKey() : null); + return insertAccessorsGetter((UserAccessorProperty) property, request, setter); } return setter; } + // Fold an accessor getter into the method handle of a user accessor property. + private MethodHandle insertAccessorsGetter(final UserAccessorProperty uap, final LinkRequest request, final MethodHandle mh) { + MethodHandle superGetter = uap.getAccessorsGetter(); + if (isInherited()) { + superGetter = ScriptObject.addProtoFilter(superGetter, getProtoChainLength()); + } + if (request != null && !(request.getReceiver() instanceof ScriptObject)) { + final MethodHandle wrapFilter = Global.getPrimitiveWrapFilter(request.getReceiver()); + superGetter = MH.filterArguments(superGetter, 0, wrapFilter.asType(wrapFilter.type().changeReturnType(superGetter.type().parameterType(0)))); + } + superGetter = MH.asType(superGetter, superGetter.type().changeParameterType(0, Object.class)); + + return MH.foldArguments(mh, superGetter); + } + /** * Return the {@code ScriptObject} owning of the property: this means the prototype. * @return owner of property @@ -136,7 +143,7 @@ * @return appropriate receiver */ public ScriptObject getGetterReceiver() { - return property != null && property.hasGetterFunction(prototype) ? self : prototype; + return property != null && property instanceof UserAccessorProperty ? self : prototype; } /** diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Sep 10 19:37:52 2014 -0700 @@ -84,6 +84,12 @@ /** Allocator map from makeMap() */ private final PropertyMap allocatorMap; + /** + * Opaque object representing parser state at the end of the function. Used when reparsing outer function + * to help with skipping parsing inner functions. + */ + private final Object endParserState; + /** Code installer used for all further recompilation/specialization of this ScriptFunction */ private transient CodeInstaller installer; @@ -98,9 +104,8 @@ /** Id to parent function if one exists */ private RecompilableScriptFunctionData parent; - private final boolean isDeclared; - private final boolean isAnonymous; - private final boolean needsCallee; + /** Copy of the {@link FunctionNode} flags. */ + private final int functionFlags; private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); @@ -136,15 +141,14 @@ super(functionName(functionNode), Math.min(functionNode.getParameters().size(), MAX_ARITY), - getFlags(functionNode)); + getDataFlags(functionNode)); this.functionName = functionNode.getName(); this.lineNumber = functionNode.getLineNumber(); - this.isDeclared = functionNode.isDeclared(); - this.needsCallee = functionNode.needsCallee(); - this.isAnonymous = functionNode.isAnonymous(); + this.functionFlags = functionNode.getFlags() | (functionNode.needsCallee() ? FunctionNode.NEEDS_CALLEE : 0); this.functionNodeId = functionNode.getId(); this.source = functionNode.getSource(); + this.endParserState = functionNode.getEndParserState(); this.token = tokenFor(functionNode); this.installer = installer; this.allocatorClassName = allocatorClassName; @@ -202,6 +206,24 @@ } /** + * Returns the names of all external symbols this function uses. + * @return the names of all external symbols this function uses. + */ + public Set getExternalSymbolNames() { + return externalScopeDepths == null ? Collections.emptySet() : + Collections.unmodifiableSet(externalScopeDepths.keySet()); + } + + /** + * Returns the opaque object representing the parser state at the end of this function's body, used to + * skip parsing this function when reparsing its containing outer function. + * @return the object representing the end parser state + */ + public Object getEndParserState() { + return endParserState; + } + + /** * Get the parent of this RecompilableScriptFunctionData. If we are * a nested function, we have a parent. Note that "null" return value * can also mean that we have a parent but it is unknown, so this can @@ -269,7 +291,7 @@ @Override public boolean inDynamicContext() { - return (flags & IN_DYNAMIC_CONTEXT) != 0; + return getFunctionFlag(FunctionNode.IN_DYNAMIC_CONTEXT); } private static String functionName(final FunctionNode fn) { @@ -293,7 +315,7 @@ return Token.toDesc(TokenType.FUNCTION, position, length); } - private static int getFlags(final FunctionNode functionNode) { + private static int getDataFlags(final FunctionNode functionNode) { int flags = IS_CONSTRUCTOR; if (functionNode.isStrict()) { flags |= IS_STRICT; @@ -307,9 +329,6 @@ if (functionNode.isVarArg()) { flags |= IS_VARIABLE_ARITY; } - if (functionNode.inDynamicContext()) { - flags |= IN_DYNAMIC_CONTEXT; - } return flags; } @@ -337,7 +356,6 @@ } FunctionNode reparse() { - final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID; // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node final int descPosition = Token.descPosition(token); final Context context = Context.getContextTrusted(); @@ -346,18 +364,27 @@ source, new Context.ThrowErrorManager(), isStrict(), - functionNodeId - (isProgram ? 0 : 1), lineNumber - 1, context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive - if (isAnonymous) { + if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) { parser.setFunctionName(functionName); } + parser.setReparsedFunction(this); - final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true); - // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a - // single function, extract it from the program. - return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName); + final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, + Token.descLength(token), true); + // Parser generates a program AST even if we're recompiling a single function, so when we are only + // recompiling a single function, extract it from the program. + return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); + } + + private boolean getFunctionFlag(final int flag) { + return (functionFlags & flag) != 0; + } + + private boolean isProgram() { + return getFunctionFlag(FunctionNode.IS_PROGRAM); } TypeMap typeMap(final MethodType fnCallSiteType) { @@ -546,7 +573,7 @@ assert fns.size() == 1 : "got back more than one method in recompilation"; final FunctionNode f = fns.iterator().next(); assert f.getId() == functionNodeId; - if (!isDeclared && f.isDeclared()) { + if (!getFunctionFlag(FunctionNode.IS_DECLARED) && f.isDeclared()) { return f.clearFlag(null, FunctionNode.IS_DECLARED); } return f; @@ -669,7 +696,15 @@ @Override public boolean needsCallee() { - return needsCallee; + return getFunctionFlag(FunctionNode.NEEDS_CALLEE); + } + + /** + * Returns the {@link FunctionNode} flags associated with this function data. + * @return the {@link FunctionNode} flags associated with this function data. + */ + public int getFunctionFlags() { + return functionFlags; } @Override diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunctionData.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Sep 10 19:37:52 2014 -0700 @@ -90,8 +90,6 @@ public static final int USES_THIS = 1 << 4; /** Is this a variable arity function? */ public static final int IS_VARIABLE_ARITY = 1 << 5; - /** Is this declared in a dynamic context */ - public static final int IN_DYNAMIC_CONTEXT = 1 << 6; /** Flag for strict or built-in functions */ public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN; diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Wed Sep 10 19:37:52 2014 -0700 @@ -1050,7 +1050,7 @@ } private static int getIntValue(final FindProperty find, final int programPoint) { - final MethodHandle getter = find.getGetter(int.class, programPoint); + final MethodHandle getter = find.getGetter(int.class, programPoint, null); if (getter != null) { try { return (int)getter.invokeExact((Object)find.getGetterReceiver()); @@ -1065,7 +1065,7 @@ } private static long getLongValue(final FindProperty find, final int programPoint) { - final MethodHandle getter = find.getGetter(long.class, programPoint); + final MethodHandle getter = find.getGetter(long.class, programPoint, null); if (getter != null) { try { return (long)getter.invokeExact((Object)find.getGetterReceiver()); @@ -1080,7 +1080,7 @@ } private static double getDoubleValue(final FindProperty find, final int programPoint) { - final MethodHandle getter = find.getGetter(double.class, programPoint); + final MethodHandle getter = find.getGetter(double.class, programPoint, null); if (getter != null) { try { return (double)getter.invokeExact((Object)find.getGetterReceiver()); @@ -1983,7 +1983,7 @@ NashornCallSiteDescriptor.getProgramPoint(desc) : UnwarrantedOptimismException.INVALID_PROGRAM_POINT; - mh = find.getGetter(returnType, programPoint); + mh = find.getGetter(returnType, programPoint, request); // Get the appropriate guard for this callsite and property. final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck); final ScriptObject owner = find.getOwner(); @@ -1995,8 +1995,9 @@ mh = Lookup.emptyGetter(returnType); protoSwitchPoint = getProtoSwitchPoint(name, owner); } else if (!find.isSelf()) { - assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType; - if (!property.hasGetterFunction(owner)) { + assert mh.type().returnType().equals(returnType) : + "return type mismatch for getter " + mh.type().returnType() + " != " + returnType; + if (!(property instanceof UserAccessorProperty)) { // Add a filter that replaces the self object with the prototype owning the property. mh = addProtoFilter(mh, find.getProtoChainLength()); } @@ -2167,7 +2168,7 @@ } } - final GuardedInvocation inv = new SetMethodCreator(this, find, desc, explicitInstanceOfCheck).createGuardedInvocation(); + final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(); final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request); if (cinv != null) { @@ -2320,13 +2321,13 @@ find.isSelf()? getKnownFunctionPropertyGuardSelf( getMap(), - find.getGetter(Object.class, INVALID_PROGRAM_POINT), + find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), func) : //TODO this always does a scriptobject check getKnownFunctionPropertyGuardProto( getMap(), - find.getGetter(Object.class, INVALID_PROGRAM_POINT), + find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), find.getProtoChainLength(), func), getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()), diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java Wed Sep 10 19:37:52 2014 -0700 @@ -33,6 +33,7 @@ import java.lang.invoke.SwitchPoint; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornGuards; @@ -48,7 +49,7 @@ private final FindProperty find; private final CallSiteDescriptor desc; private final Class type; - private final boolean explicitInstanceOfCheck; + private final LinkRequest request; /** * Creates a new property setter method creator. @@ -56,14 +57,15 @@ * @param find a result of a {@link ScriptObject#findProperty(String, boolean)} on the object for the property we * want to create a setter for. Can be null if the property does not yet exist on the object. * @param desc the descriptor of the call site that triggered the property setter lookup + * @param request the link request */ - SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck) { - this.sobj = sobj; - this.map = sobj.getMap(); - this.find = find; - this.desc = desc; - this.type = desc.getMethodType().parameterType(1); - this.explicitInstanceOfCheck = explicitInstanceOfCheck; + SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final LinkRequest request) { + this.sobj = sobj; + this.map = sobj.getMap(); + this.find = find; + this.desc = desc; + this.type = desc.getMethodType().parameterType(1); + this.request = request; } @@ -111,6 +113,7 @@ // getGuard() and getException() either both return null, or neither does. The reason for that is that now // getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to // relink on ClassCastException. + final boolean explicitInstanceOfCheck = NashornGuards.explicitInstanceOfCheck(desc, request); return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); } @@ -140,6 +143,7 @@ private SetMethod createExistingPropertySetter() { final Property property = find.getProperty(); + final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); final MethodHandle methodHandle; if (NashornCallSiteDescriptor.isDeclaration(desc)) { @@ -152,7 +156,7 @@ final PropertyMap oldMap = getMap(); final Property newProperty = property.removeFlags(Property.NEEDS_DECLARATION); final PropertyMap newMap = oldMap.replaceProperty(property, newProperty); - final MethodHandle fastSetter = find.replaceProperty(newProperty).getSetter(type, NashornCallSiteDescriptor.isStrict(desc)); + final MethodHandle fastSetter = find.replaceProperty(newProperty).getSetter(type, isStrict, request); final MethodHandle slowSetter = MH.insertArguments(ScriptObject.DECLARE_AND_SET, 1, getName()).asType(fastSetter.type()); // cas map used as guard, if true that means we can do the set fast @@ -161,14 +165,14 @@ casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class)); methodHandle = MH.guardWithTest(casMap, fastSetter, slowSetter); } else { - methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc)); + methodHandle = find.getSetter(type, isStrict, request); } assert methodHandle != null; assert property != null; final MethodHandle boundHandle; - if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) { + if (!(property instanceof UserAccessorProperty) && find.isInherited()) { boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength()); } else { boundHandle = methodHandle; diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java Wed Sep 10 19:37:52 2014 -0700 @@ -33,6 +33,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; + +import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; @@ -189,7 +191,7 @@ maxKeyLength++; final StringBuilder sb = new StringBuilder(); - sb.append("Accumulated complation phase Timings:\n\n"); + sb.append("Accumulated compilation phase timings:\n\n"); for (final Map.Entry entry : timings.entrySet()) { int len; @@ -224,6 +226,9 @@ append((int)(knownTime * 100.0 / total)). append("%])"); + sb.append("\n\nEmitted compile units: "). + append(CompileUnit.getEmittedUnitCount()); + return sb.toString(); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java Wed Sep 10 19:37:52 2014 -0700 @@ -34,8 +34,8 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.concurrent.Callable; -import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -48,7 +48,7 @@ private static final long serialVersionUID = -5928687246526840321L; - static class Accessors { + static final class Accessors { Object getter; Object setter; @@ -67,20 +67,20 @@ } } + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + /** Getter method handle */ - private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, - "userAccessorGetter", Object.class, Accessors.class, Object.class); + private final static MethodHandle INVOKE_GETTER_ACCESSOR = findOwnMH_S("invokeGetterAccessor", Object.class, Accessors.class, Object.class); /** Setter method handle */ - private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, - "userAccessorSetter", void.class, Accessors.class, String.class, Object.class, Object.class); + private final static MethodHandle INVOKE_SETTER_ACCESSOR = findOwnMH_S("invokeSetterAccessor", void.class, Accessors.class, String.class, Object.class, Object.class); /** Dynamic invoker for getter */ - private static final Object INVOKE_UA_GETTER = new Object(); + private static final Object GETTER_INVOKER_KEY = new Object(); private static MethodHandle getINVOKE_UA_GETTER() { - return Context.getGlobal().getDynamicInvoker(INVOKE_UA_GETTER, + return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY, new Callable() { @Override public MethodHandle call() { @@ -91,10 +91,10 @@ } /** Dynamic invoker for setter */ - private static Object INVOKE_UA_SETTER = new Object(); + private static Object SETTER_INVOKER_KEY = new Object(); private static MethodHandle getINVOKE_UA_SETTER() { - return Context.getGlobal().getDynamicInvoker(INVOKE_UA_SETTER, + return Context.getGlobal().getDynamicInvoker(SETTER_INVOKER_KEY, new Callable() { @Override public MethodHandle call() { @@ -190,7 +190,7 @@ @Override public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { - return userAccessorGetter(getAccessors((owner != null) ? owner : self), self); + return invokeGetterAccessor(getAccessors((owner != null) ? owner : self), self); } @Override @@ -210,13 +210,13 @@ @Override public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { - userAccessorSetter(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); + invokeSetterAccessor(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); } @Override public MethodHandle getGetter(final Class type) { //this returns a getter on the format (Accessors, Object receiver) - return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type); + return Lookup.filterReturnType(INVOKE_GETTER_ACCESSOR, type); } @Override @@ -260,7 +260,7 @@ @Override public MethodHandle getSetter(final Class type, final PropertyMap currentMap) { - return USER_ACCESSOR_SETTER.methodHandle(); + return INVOKE_SETTER_ACCESSOR; } @Override @@ -269,11 +269,21 @@ return (value instanceof ScriptFunction) ? (ScriptFunction)value : null; } + /** + * Get the getter for the {@code Accessors} object. + * This is the the super {@code Object} type getter with {@code Accessors} return type. + * + * @return The getter handle for the Accessors + */ + MethodHandle getAccessorsGetter() { + return super.getGetter(Object.class).asType(MethodType.methodType(Accessors.class, Object.class)); + } + // User defined getter and setter are always called by "dyn:call". Note that the user // getter/setter may be inherited. If so, proto is bound during lookup. In either // inherited or self case, slot is also bound during lookup. Actual ScriptFunction // to be called is retrieved everytime and applied. - static Object userAccessorGetter(final Accessors gs, final Object self) { + private static Object invokeGetterAccessor(final Accessors gs, final Object self) { final Object func = gs.getter; if (func instanceof ScriptFunction) { try { @@ -288,7 +298,7 @@ return UNDEFINED; } - static void userAccessorSetter(final Accessors gs, final String name, final Object self, final Object value) { + private static void invokeSetterAccessor(final Accessors gs, final String name, final Object self, final Object value) { final Object func = gs.setter; if (func instanceof ScriptFunction) { try { @@ -303,4 +313,8 @@ } } + private static MethodHandle findOwnMH_S(final String name, final Class rtype, final Class... types) { + return MH.findStatic(LOOKUP, UserAccessorProperty.class, name, MH.type(rtype, types)); + } + } diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Wed Sep 10 19:37:52 2014 -0700 @@ -32,11 +32,10 @@ import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; -import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.internal.dynalink.support.Guards; -import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.UserAccessorProperty; /** * Implements lookup of methods to link for dynamic operations on JavaScript primitive values (booleans, strings, and @@ -86,15 +85,6 @@ final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, final MethodHandle protoFilter) { final CallSiteDescriptor desc = request.getCallSiteDescriptor(); - final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); - if ("setProp".equals(operator) || "setElem".equals(operator)) { - final MethodType type = desc.getMethodType(); - MethodHandle method = MH.asType(Lookup.EMPTY_SETTER, MH.type(void.class, Object.class, type.parameterType(1))); - if (type.parameterCount() == 3) { - method = MH.dropArguments(method, 2, type.parameterType(2)); - } - return new GuardedInvocation(method, guard); - } if(desc.getNameTokenCount() > 2) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); @@ -102,7 +92,7 @@ if(find == null) { // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. return null; - } else if (find.isInherited() && !find.getProperty().hasGetterFunction(find.getOwner())) { + } else if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) { // If property is found in the prototype object bind the method handle directly to // the proto filter instead of going through wrapper instantiation below. final ScriptObject proto = wrappedReceiver.getProto(); diff -r b2287cac7813 -r 00cc73c2862d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java Wed Sep 10 19:37:52 2014 -0700 @@ -246,7 +246,7 @@ // For each file on the command line. for (final String fileName : files) { - final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, FunctionNode.FIRST_FUNCTION_ID, 0, context.getLogger(Parser.class)).parse(); + final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, 0, context.getLogger(Parser.class)).parse(); if (errors.getNumberOfErrors() != 0) { return COMPILATION_ERROR; diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8030182_2.js --- a/nashorn/test/script/basic/JDK-8030182_2.js Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/JDK-8030182_2.js Wed Sep 10 19:37:52 2014 -0700 @@ -41,6 +41,6 @@ try { eval(str); } catch (e) { - print(e.stack.replace(/\\/g, '/').replace(/@[0-9]+/, '@')); + print(e.stack.replace(/\\/g, '/')); } diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8030182_2.js.EXPECTED --- a/nashorn/test/script/basic/JDK-8030182_2.js.EXPECTED Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/JDK-8030182_2.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -1,3 +1,3 @@ ReferenceError: "g" is not defined - at (test/script/basic/JDK-8030182_2.js#42:4@:-1) + at (test/script/basic/JDK-8030182_2.js#42:4:-1) at (test/script/basic/JDK-8030182_2.js:42) diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_1.js --- a/nashorn/test/script/basic/JDK-8048079_1.js Wed Jul 05 20:00:07 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * JDK-8048079: Persistent code store is broken after optimistic types merge - * - * @test - * @run - * @option -pcc - * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache - * @fork - */ - -load(__DIR__ + 'prototype.js'); -load(__DIR__ + 'yui.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_1.js.EXPECTED --- a/nashorn/test/script/basic/JDK-8048079_1.js.EXPECTED Wed Jul 05 20:00:07 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -parsed and compiled ok prototype.js -parsed and compiled ok yui-min.js -parsed and compiled ok yui.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_1a.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_1a.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge + * + * @test + * @runif external.prototype + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'prototype.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_1a.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_1a.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,1 @@ +parsed and compiled ok prototype.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_1b.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_1b.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge + * + * @test + * @runif external.yui + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'yui.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_1b.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_1b.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,2 @@ +parsed and compiled ok yui-min.js +parsed and compiled ok yui.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_2.js --- a/nashorn/test/script/basic/JDK-8048079_2.js Wed Jul 05 20:00:07 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * JDK-8048079: Persistent code store is broken after optimistic types merge - * - * @test - * @run - * @option -pcc - * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache - * @fork - */ - -load(__DIR__ + 'prototype.js'); -load(__DIR__ + 'yui.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_2.js.EXPECTED --- a/nashorn/test/script/basic/JDK-8048079_2.js.EXPECTED Wed Jul 05 20:00:07 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -parsed and compiled ok prototype.js -parsed and compiled ok yui-min.js -parsed and compiled ok yui.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_2a.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_2a.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge. + * Same script as JDK-8048079_1a.js to exercise code cache. + * @test + * @runif external.prototype + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'prototype.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_2a.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_2a.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,1 @@ +parsed and compiled ok prototype.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_2b.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_2b.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge + * Same script as JDK-8048079_1b.js to exercise code cache again. + * @test + * @runif external.yui + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'yui.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/JDK-8048079_2b.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8048079_2b.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,2 @@ +parsed and compiled ok yui-min.js +parsed and compiled ok yui.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/const-empty.js.EXPECTED --- a/nashorn/test/script/basic/es6/const-empty.js.EXPECTED Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/es6/const-empty.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -1,3 +1,3 @@ -SyntaxError: test/script/basic/es6/const-empty.js#33:4@1:2:7 Missing assignment to constant "x" +SyntaxError: test/script/basic/es6/const-empty.js#33:4:2:7 Missing assignment to constant "x" const x; ^ diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/const-redeclare-extra.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/const-redeclare-extra.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(<:3:8 Variable "x" has already been declared + var x = {}; + ^ +SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8:2:8 Variable "x" has already been declared + var x = 2; + ^ +SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8:2:13 Variable "x" has already been declared + function x () {} + ^ diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/const-redeclare.js.EXPECTED --- a/nashorn/test/script/basic/es6/const-redeclare.js.EXPECTED Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/es6/const-redeclare.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -1,3 +1,3 @@ -SyntaxError: test/script/basic/es6/const-redeclare.js#33:4@1:2:6 Variable "x" has already been declared +SyntaxError: test/script/basic/es6/const-redeclare.js#33:4:2:6 Variable "x" has already been declared const x = 2; ^ diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/let-redeclare-extra.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let-redeclare-extra.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(<:2:8 Variable "x" has already been declared + let x = 2; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8:3:8 Variable "x" has already been declared + var x = 2; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8:2:8 Variable "x" has already been declared + var x = 2; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8:2:10 Variable "x" has already been declared + const x = function (){}; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8:3:13 Variable "a" has already been declared + function a () {}; + ^ diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/let-redeclare.js.EXPECTED --- a/nashorn/test/script/basic/es6/let-redeclare.js.EXPECTED Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/es6/let-redeclare.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -1,3 +1,3 @@ -SyntaxError: test/script/basic/es6/let-redeclare.js#33:4@1:2:4 Variable "x" has already been declared +SyntaxError: test/script/basic/es6/let-redeclare.js#33:4:2:4 Variable "x" has already been declared let x = 2; ^ diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/let_const_closure.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let_const_closure.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + +function tryIt(code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + + +tryIt(<:3:8 Assignment to constant "a" + a-- + ^ +SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9:3:8 Assignment to constant "a" + a-- + ^ +ReferenceError: "a" is not defined diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/let_different_types.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let_different_types.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(< 0); + print(a) + print(b) +CODE) + +tryIt(< 0) { + a-- + let b = a + } + print(a) + print(b) +CODE) + +tryIt(< 0) { + a-- + const b = a + } + print(a) + print(b) +CODE) + +tryIt(< 0); + print(a) + print(b) +CODE) diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/es6/let_loops.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let_loops.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,8 @@ +0 +ReferenceError: "b" is not defined +0 +ReferenceError: "b" is not defined +0 +ReferenceError: "b" is not defined +0 +ReferenceError: "b" is not defined diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/optimistic_check_type.js --- a/nashorn/test/script/basic/optimistic_check_type.js Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/optimistic_check_type.js Wed Sep 10 19:37:52 2014 -0700 @@ -36,13 +36,18 @@ // Testing conditional operator print(inspect("" ? b : x.a, "ternary operator")) -print(inspect(x.b ? b : x.a, "ternary operator")) -print(inspect(c ? b : a, "ternary operator")) -print(inspect(!c ? b : a, "ternary operator")) -print(inspect(d ? b : x.c, "ternary operator")) +var b1 = b; +print(inspect(x.b ? b1 : x.a, "ternary operator")) +var b2 = b; +print(inspect(c ? b2 : a, "ternary operator")) +var b3 = b; +print(inspect(!c ? b3 : a, "ternary operator")) +var b4 = b; +print(inspect(d ? b4 : x.c, "ternary operator")) print(inspect(x.c ? a : c, "ternary operator")) print(inspect(c ? d : a, "ternary operator")) -print(inspect(c ? +a : b, "ternary operator")) +var b5 = b; +print(inspect(c ? +a : b5, "ternary operator")) // Testing format methods print(inspect(b.toFixed(2), "global double toFixed()")) @@ -53,11 +58,14 @@ print(inspect(trees[1], "member object")) trees[1] = undefined; print(inspect(trees[1], "member undefined")) -print(inspect(1 in trees ? b : a, "conditional on array member")) +var b6=b; +print(inspect(1 in trees ? b6 : a, "conditional on array member")) delete trees[2] -print(inspect(2 in trees ? b : a, "conditional on array member")) +var b7=b; +print(inspect(2 in trees ? b7 : a, "conditional on array member")) print(inspect(3 in trees ? trees[2]="bay" : a, "conditional on array member")) -print(inspect("oak" in trees ? b : a, "conditional on array member")) +var b8=b; +print(inspect("oak" in trees ? b8 : a, "conditional on array member")) // Testing nested functions and return value function f1() { diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/splitter.js --- a/nashorn/test/script/basic/splitter.js Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/splitter.js Wed Sep 10 19:37:52 2014 -0700 @@ -30,7 +30,5 @@ * @fork */ -load(__DIR__ + 'prototype.js'); -load(__DIR__ + 'yui.js'); load(__DIR__ + 'NASHORN-689.js'); load(__DIR__ + 'NASHORN-58.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/splitter.js.EXPECTED --- a/nashorn/test/script/basic/splitter.js.EXPECTED Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/script/basic/splitter.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -1,6 +1,3 @@ -parsed and compiled ok prototype.js -parsed and compiled ok yui-min.js -parsed and compiled ok yui.js a=10 a=9 a=8 diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/splitter_prototype.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/splitter_prototype.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Test various scripts with low splitter threshold + * + * @test + * @option -Dnashorn.compiler.splitter.threshold=200 + * @runif external.prototype + * @fork + */ + +load(__DIR__ + 'prototype.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/splitter_prototype.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/splitter_prototype.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,1 @@ +parsed and compiled ok prototype.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/splitter_yui.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/splitter_yui.js Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Test various scripts with low splitter threshold + * + * @test + * @option -Dnashorn.compiler.splitter.threshold=200 + * @runif external.yui + * @fork + */ + +load(__DIR__ + 'yui.js'); diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/script/basic/splitter_yui.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/splitter_yui.js.EXPECTED Wed Sep 10 19:37:52 2014 -0700 @@ -0,0 +1,2 @@ +parsed and compiled ok yui-min.js +parsed and compiled ok yui.js diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/src/jdk/nashorn/api/scripting/ScopeTest.java --- a/nashorn/test/src/jdk/nashorn/api/scripting/ScopeTest.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScopeTest.java Wed Sep 10 19:37:52 2014 -0700 @@ -407,6 +407,75 @@ Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context"); } + + /** + * Test multi-threaded access to prototype user accessor properties for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedAccessorTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'original context' })", origContext); + e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'new context', configurable: true })", newCtxt); + final String sharedScript = "({}).foo"; + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + final Object obj3 = e.eval("delete Object.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); + assertEquals(obj3, "newer context"); + final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); + + t3.start(); + t4.start(); + t3.join(); + t4.join(); + } + + /** + * Test multi-threaded access to primitive prototype user accessor properties for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedPrimitiveAccessorTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'original context' })", origContext); + e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'new context' })", newCtxt); + final String sharedScript = "''.foo"; + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + final Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); + assertEquals(obj3, "newer context"); + final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); + + t3.start(); + t4.start(); + t3.join(); + t4.join(); + } + /** * Test multi-threaded scope function invocation for shared script classes with multiple globals. */ diff -r b2287cac7813 -r 00cc73c2862d nashorn/test/src/jdk/nashorn/internal/test/framework/TestFinder.java --- a/nashorn/test/src/jdk/nashorn/internal/test/framework/TestFinder.java Wed Jul 05 20:00:07 2017 +0200 +++ b/nashorn/test/src/jdk/nashorn/internal/test/framework/TestFinder.java Wed Sep 10 19:37:52 2014 -0700 @@ -261,14 +261,17 @@ isTest = false; isNotTest = true; break; - case "@runif": - if (System.getProperty(scanner.next()) != null) { + case "@runif": { + final String prop = scanner.next(); + if (System.getProperty(prop) != null) { shouldRun = true; } else { + factory.log("WARNING: (" + prop + ") skipping " + testFile); isTest = false; isNotTest = true; } break; + } case "@run": shouldRun = true; break;