# HG changeset patch # User lana # Date 1441322087 25200 # Node ID 42d8ed4651b62572b39e6fed3fafcb7ee93f9dc2 # Parent c16cb85c1aec375a83444729a8bb790b9611421b# Parent e32cac18203541f8bb0f3d382c19191f5880f902 Merge diff -r c16cb85c1aec -r 42d8ed4651b6 make/project.properties --- a/make/project.properties Thu Sep 03 14:24:47 2015 -0700 +++ b/make/project.properties Thu Sep 03 16:14:47 2015 -0700 @@ -302,7 +302,7 @@ -XX:+HeapDumpOnOutOfMemoryError # turn on assertions for tests -run.test.jvmargs.main=${run.test.jvmargs.common} -esa -ea +run.test.jvmargs.main=${run.test.jvmargs.common} -esa -ea -da:java.lang.invoke.LambdaFormEditor # Extra jvmargs that might be useful for debugging # and performance improvements/monitoring diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Thu Sep 03 16:14:47 2015 -0700 @@ -107,7 +107,7 @@ private final AccessibleObject target; private final MethodType type; - public CallerSensitiveDynamicMethod(final AccessibleObject target) { + CallerSensitiveDynamicMethod(final AccessibleObject target) { super(getName(target)); this.target = target; this.type = getMethodType(target); @@ -115,8 +115,9 @@ private static String getName(final AccessibleObject target) { final Member m = (Member)target; - return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(), - m.getName())); + final boolean constructor = m instanceof Constructor; + return getMethodNameWithSignature(getMethodType(target), constructor ? m.getName() : + getClassAndMethodName(m.getDeclaringClass(), m.getName()), !constructor); } @Override diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Thu Sep 03 16:14:47 2015 -0700 @@ -86,7 +86,9 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.text.Collator; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -242,6 +244,35 @@ return methods.getFirst().isConstructor(); } + @Override + public String toString() { + // First gather the names and sort them. This makes it consistent and easier to read. + final List names = new ArrayList<>(methods.size()); + int len = 0; + for (final SingleDynamicMethod m: methods) { + final String name = m.getName(); + len += name.length(); + names.add(name); + } + // Case insensitive sorting, so e.g. "Object" doesn't come before "boolean". + final Collator collator = Collator.getInstance(); + collator.setStrength(Collator.SECONDARY); + Collections.sort(names, collator); + + final String className = getClass().getName(); + // Class name length + length of signatures + 2 chars/per signature for indentation and newline + + // 3 for brackets and initial newline + final int totalLength = className.length() + len + 2 * names.size() + 3; + final StringBuilder b = new StringBuilder(totalLength); + b.append('[').append(className).append('\n'); + for(final String name: names) { + b.append(' ').append(name).append('\n'); + } + b.append(']'); + assert b.length() == totalLength; + return b.toString(); + }; + ClassLoader getClassLoader() { return classLoader; } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SimpleDynamicMethod.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Thu Sep 03 16:14:47 2015 -0700 @@ -122,13 +122,13 @@ * @param constructor does this represent a constructor? */ SimpleDynamicMethod(final MethodHandle target, final Class clazz, final String name, final boolean constructor) { - super(getName(target, clazz, name)); + super(getName(target, clazz, name, constructor)); this.target = target; this.constructor = constructor; } - private static String getName(final MethodHandle target, final Class clazz, final String name) { - return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name)); + private static String getName(final MethodHandle target, final Class clazz, final String name, final boolean constructor) { + return getMethodNameWithSignature(target.type(), constructor ? name : getClassAndMethodName(clazz, name), !constructor); } @Override diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java Thu Sep 03 16:14:47 2015 -0700 @@ -143,14 +143,18 @@ return getMethodType().parameterList().equals(method.getMethodType().parameterList()); } - static String getMethodNameWithSignature(final MethodType type, final String methodName) { + static String getMethodNameWithSignature(final MethodType type, final String methodName, final boolean withReturnType) { final String typeStr = type.toString(); final int retTypeIndex = typeStr.lastIndexOf(')') + 1; int secondParamIndex = typeStr.indexOf(',') + 1; if(secondParamIndex == 0) { secondParamIndex = retTypeIndex - 1; } - return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex); + final StringBuilder b = new StringBuilder(); + if (withReturnType) { + b.append(typeStr, retTypeIndex, typeStr.length()).append(' '); + } + return b.append(methodName).append('(').append(typeStr, secondParamIndex, retTypeIndex).toString(); } /** diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornException.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornException.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornException.java Thu Sep 03 16:14:47 2015 -0700 @@ -51,6 +51,8 @@ private String fileName; // script line number private int line; + // are the line and fileName unknown? + private boolean lineAndFileNameUnknown; // script column number private int column; // underlying ECMA error object - lazily initialized @@ -92,27 +94,10 @@ */ protected NashornException(final String msg, final Throwable cause) { super(msg, cause == null ? null : cause); - // This is not so pretty - but it gets the job done. Note that the stack - // trace has been already filled by "fillInStackTrace" call from - // Throwable - // constructor and so we don't pay additional cost for it. - // Hard luck - no column number info this.column = -1; - - // Find the first JavaScript frame by walking and set file, line from it - // Usually, we should be able to find it in just few frames depth. - for (final StackTraceElement ste : getStackTrace()) { - if (ECMAErrors.isScriptFrame(ste)) { - // Whatever here is compiled from JavaScript code - this.fileName = ste.getFileName(); - this.line = ste.getLineNumber(); - return; - } - } - - this.fileName = null; - this.line = 0; + // We can retrieve the line number and file name from the stack trace if needed + this.lineAndFileNameUnknown = true; } /** @@ -121,6 +106,7 @@ * @return the file name */ public final String getFileName() { + ensureLineAndFileName(); return fileName; } @@ -131,6 +117,7 @@ */ public final void setFileName(final String fileName) { this.fileName = fileName; + lineAndFileNameUnknown = false; } /** @@ -139,6 +126,7 @@ * @return the line number */ public final int getLineNumber() { + ensureLineAndFileName(); return line; } @@ -148,6 +136,7 @@ * @param line the line number */ public final void setLineNumber(final int line) { + lineAndFileNameUnknown = false; this.line = line; } @@ -274,4 +263,19 @@ public void setEcmaError(final Object ecmaError) { this.ecmaError = ecmaError; } + + private void ensureLineAndFileName() { + if (lineAndFileNameUnknown) { + for (final StackTraceElement ste : getStackTrace()) { + if (ECMAErrors.isScriptFrame(ste)) { + // Whatever here is compiled from JavaScript code + fileName = ste.getFileName(); + line = ste.getLineNumber(); + return; + } + } + + lineAndFileNameUnknown = false; + } + } } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ApplySpecialization.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu Sep 03 16:14:47 2015 -0700 @@ -283,17 +283,13 @@ start++; } - start++; //we always uses this + start++; // we always use this - final List params = functionNode.getParameters(); + assert functionNode.getNumOfParams() == 0 : "apply2call on function with named paramaters!"; final List newParams = new ArrayList<>(); - final long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start); + final long to = actualCallSiteType.parameterCount() - start; for (int i = 0; i < to; i++) { - if (i >= params.size()) { - newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i))); - } else { - newParams.add(params.get(i)); - } + newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i))); } callSiteTypes.push(actualCallSiteType); @@ -316,6 +312,10 @@ return false; } + if (functionNode.getNumOfParams() != 0) { + return false; + } + if (functionNode.hasEval()) { return false; } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Sep 03 16:14:47 2015 -0700 @@ -149,12 +149,14 @@ private final Deque> thisProperties = new ArrayDeque<>(); private final Map globalSymbols = new HashMap<>(); //reuse the same global symbol private final Compiler compiler; + private final boolean isOnDemand; public AssignSymbols(final Compiler compiler) { super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); this.debug = log.isEnabled(); + this.isOnDemand = compiler.isOnDemandCompilation(); } @Override @@ -390,7 +392,7 @@ // Create and add to appropriate block. symbol = createSymbol(name, flags); - symbolBlock.putSymbol(lc, symbol); + symbolBlock.putSymbol(symbol); if ((flags & IS_SCOPE) == 0) { // Initial assumption; symbol can lose its slot later @@ -440,7 +442,7 @@ start(block); if (lc.isFunctionBody()) { - block.clearSymbols(); + assert !block.hasSymbols(); 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. @@ -459,7 +461,7 @@ } private boolean isUnparsedFunction(final FunctionNode fn) { - return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); + return isOnDemand && fn != lc.getOutermostFunction(); } @Override @@ -747,28 +749,6 @@ } } - @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. - if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { - // 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()); - } - } - } - } - return block; - } - private Node leaveDELETE(final UnaryNode unaryNode) { final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final boolean strictMode = currentFunctionNode.isStrict(); @@ -786,9 +766,9 @@ if (symbol.isThis()) { // Can't delete "this", ignore and return true - return LiteralNode.newInstance(unaryNode, true).accept(this); + return LiteralNode.newInstance(unaryNode, true); } - final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); + final Expression literalNode = LiteralNode.newInstance(unaryNode, name); final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))); if (!failDelete) { @@ -799,7 +779,7 @@ if (failDelete) { request = Request.FAIL_DELETE; - } else if (symbol.isGlobal() && !symbol.isFunctionDeclaration()) { + } else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) { request = Request.SLOW_DELETE; } } else if (rhs instanceof AccessNode) { @@ -807,7 +787,7 @@ final String property = ((AccessNode)rhs).getProperty(); args.add(base); - args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this)); + args.add(LiteralNode.newInstance(unaryNode, property)); args.add(strictFlagNode); } else if (rhs instanceof IndexNode) { @@ -820,15 +800,15 @@ args.add(strictFlagNode); } else { - return LiteralNode.newInstance(unaryNode, true).accept(this); + return LiteralNode.newInstance(unaryNode, true); } - return new RuntimeNode(unaryNode, request, args).accept(this); + return new RuntimeNode(unaryNode, request, args); } @Override public Node leaveForNode(final ForNode forNode) { if (forNode.isForIn()) { - forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 + return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 } return end(forNode); @@ -904,19 +884,18 @@ public Node leaveSwitchNode(final SwitchNode switchNode) { // We only need a symbol for the tag if it's not an integer switch node if(!switchNode.isUniqueInteger()) { - switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); + return switchNode.setTag(lc, newObjectInternal(SWITCH_TAG_PREFIX)); } return switchNode; } @Override public Node leaveTryNode(final TryNode tryNode) { - tryNode.setException(exceptionSymbol()); assert tryNode.getFinallyBody() == null; end(tryNode); - return tryNode; + return tryNode.setException(lc, exceptionSymbol()); } private Node leaveTYPEOF(final UnaryNode unaryNode) { @@ -925,13 +904,13 @@ final List args = new ArrayList<>(); if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { args.add(compilerConstantIdentifier(SCOPE)); - args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null + args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName())); //null } else { args.add(rhs); - args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' + args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } - final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); + final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); end(unaryNode); @@ -939,7 +918,7 @@ } private FunctionNode markProgramBlock(final FunctionNode functionNode) { - if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) { + if (isOnDemand || !functionNode.isProgram()) { return functionNode; } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AstSerializer.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AstSerializer.java Thu Sep 03 14:24:47 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +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. 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.codegen; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.util.Collections; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; -import jdk.nashorn.internal.ir.Block; -import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.LexicalContext; -import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.Statement; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; -import jdk.nashorn.internal.runtime.options.Options; - -/** - * This static utility class performs serialization of FunctionNode ASTs to a byte array. - * The format is a standard Java serialization stream, deflated. - */ -final class AstSerializer { - // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed - // and size. - private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); - static byte[] serialize(final FunctionNode fn) { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final Deflater deflater = new Deflater(COMPRESSION_LEVEL); - try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { - oout.writeObject(removeInnerFunctionBodies(fn)); - } catch (final IOException e) { - throw new AssertionError("Unexpected exception serializing function", e); - } finally { - deflater.end(); - } - return out.toByteArray(); - } - - private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) { - return (FunctionNode)fn.accept(new NodeVisitor(new LexicalContext()) { - @Override - public Node leaveBlock(final Block block) { - if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { - return block.setStatements(lc, Collections.emptyList()); - } - return super.leaveBlock(block); - } - }); - } -} diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CacheAst.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CacheAst.java Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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.codegen; + +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; + +class CacheAst extends NodeVisitor { + private final Deque dataStack = new ArrayDeque<>(); + + private final Compiler compiler; + + CacheAst(final Compiler compiler) { + super(new LexicalContext()); + this.compiler = compiler; + assert !compiler.isOnDemandCompilation(); + } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + final int id = functionNode.getId(); + // It isn't necessary to keep a stack of RecompilableScriptFunctionData, but then we'd need to do a + // potentially transitive lookup with compiler.getScriptFunctionData(id) for deeper functions; this way + // we keep it constant time. + dataStack.push(dataStack.isEmpty() ? compiler.getScriptFunctionData(id) : dataStack.peek().getScriptFunctionData(id)); + return true; + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + final RecompilableScriptFunctionData data = dataStack.pop(); + if (functionNode.isSplit()) { + // NOTE: cache only split function ASTs from eager pass. Caching non-split functions would require + // some additional work, namely creating the concept of "uncacheable" function and reworking + // ApplySpecialization to ensure that functions undergoing apply-to-call transformations are not + // cacheable as well as recomputing Symbol.useCount when caching the eagerly parsed AST. + // Recomputing Symbol.useCount would be needed so it will only reflect uses from within the + // function being cached (and not reflect uses from its own nested functions or functions it is + // nested in). This is consistent with the count an on-demand recompilation of the function would + // produce. This is important as the decision to emit shared scope calls is based on this count, + // and if it is not matched between a previous version of the code and its deoptimizing rest-of + // compilation, it can result in rest-of not emitting a shared scope call where a previous version + // of the code (compiled from a cached eager pre-pass seeing higher (global) useCount) would emit + // it, causing a mismatch in stack shapes between previous code and its rest-of. + data.setCachedAst(functionNode); + } + + if (!dataStack.isEmpty() && ((dataStack.peek().getFunctionFlags() & FunctionNode.IS_SPLIT) != 0)) { + // Return a function node with no body so that caching outer functions doesn't hold on to nested + // functions' bodies. Note we're doing this only for functions directly nested inside split + // functions, since we're only caching the split ones. It is not necessary to limit body removal + // to just these functions, but it's a cheap way to prevent unnecessary AST mutations. + return functionNode.setBody(lc, functionNode.getBody().setStatements(null, Collections.emptyList())); + } + return functionNode; + } +} diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Sep 03 16:14:47 2015 -0700 @@ -48,11 +48,13 @@ import java.util.Set; import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; +import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -189,7 +191,7 @@ } }, - SERIALIZE_SPLIT_PHASE( + CACHE_AST( EnumSet.of( INITIALIZED, PARSED, @@ -199,20 +201,21 @@ SPLIT)) { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return transformFunction(fn, new NodeVisitor(new LexicalContext()) { - @Override - public boolean enterFunctionNode(final FunctionNode functionNode) { - if (functionNode.isSplit()) { - compiler.serializeAst(functionNode); - } - return true; - } - }); + if (!compiler.isOnDemandCompilation()) { + // Only do this on initial preprocessing of the source code. For on-demand compilations from + // source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function + // being compiled. + transformFunction(fn, new CacheAst(compiler)); + } + // NOTE: we're returning the original fn as we have destructively modified the cached functions by + // removing their bodies. This step is associating FunctionNode objects with + // RecompilableScriptFunctionData; it's not really modifying the AST. + return fn; } @Override public String toString() { - return "'Serialize Split Functions'"; + return "'Cache ASTs'"; } }, @@ -255,6 +258,51 @@ } }, + DECLARE_LOCAL_SYMBOLS_TO_COMPILER( + EnumSet.of( + INITIALIZED, + PARSED, + CONSTANT_FOLDED, + LOWERED, + BUILTINS_TRANSFORMED, + SPLIT, + SYMBOLS_ASSIGNED, + SCOPE_DEPTHS_COMPUTED)) { + @Override + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { + // 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()) { + fn.getBody().accept(new NodeVisitor(new LexicalContext()) { + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + // 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. + return false; + }; + @Override + public boolean enterBlock(final Block block) { + for (final Symbol symbol: block.getSymbols()) { + if (!symbol.isScope()) { + compiler.declareLocalSymbol(symbol.getName()); + } + } + return true; + }; + }); + } + return fn; + } + + @Override + public String toString() { + return "'Local Symbols Declaration'"; + } + }, + OPTIMISTIC_TYPE_ASSIGNMENT_PHASE( EnumSet.of( INITIALIZED, @@ -382,7 +430,7 @@ } }, - REINITIALIZE_SERIALIZED( + REINITIALIZE_CACHED( EnumSet.of( INITIALIZED, PARSED, @@ -430,7 +478,7 @@ @Override public String toString() { - return "'Deserialize'"; + return "'Reinitialize cached'"; } }, diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Thu Sep 03 16:14:47 2015 -0700 @@ -160,42 +160,41 @@ */ private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; - private final Map serializedAsts = new HashMap<>(); - /** * Compilation phases that a compilation goes through */ public static class CompilationPhases implements Iterable { /** - * Singleton that describes compilation up to the phase where a function can be serialized. + * Singleton that describes compilation up to the phase where a function can be cached. */ - private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases( + private final static CompilationPhases COMPILE_UPTO_CACHED = new CompilationPhases( "Common initial phases", CompilationPhase.CONSTANT_FOLDING_PHASE, CompilationPhase.LOWERING_PHASE, CompilationPhase.TRANSFORM_BUILTINS_PHASE, CompilationPhase.SPLITTING_PHASE, CompilationPhase.PROGRAM_POINT_PHASE, - CompilationPhase.SERIALIZE_SPLIT_PHASE + CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, + CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, + CompilationPhase.CACHE_AST ); - private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases( + private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( "After common phases, before bytecode generator", - CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, - CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, + CompilationPhase.DECLARE_LOCAL_SYMBOLS_TO_COMPILER, CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE ); /** - * Singleton that describes additional steps to be taken after deserializing, all the way up to (but not - * including) generating and installing code. + * Singleton that describes additional steps to be taken after retrieving a cached function, all the + * way up to (but not including) generating and installing code. */ - public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases( - "Recompile serialized function up to bytecode", - CompilationPhase.REINITIALIZE_SERIALIZED, - COMPILE_SERIALIZABLE_UPTO_BYTECODE + public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( + "Recompile cached function up to bytecode", + CompilationPhase.REINITIALIZE_CACHED, + COMPILE_CACHED_UPTO_BYTECODE ); /** @@ -211,8 +210,8 @@ /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( "Compile upto bytecode", - COMPILE_UPTO_SERIALIZABLE, - COMPILE_SERIALIZABLE_UPTO_BYTECODE); + COMPILE_UPTO_CACHED, + COMPILE_CACHED_UPTO_BYTECODE); /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( @@ -227,9 +226,9 @@ GENERATE_BYTECODE_AND_INSTALL); /** Singleton that describes a full compilation - this includes code installation - from serialized state*/ - public final static CompilationPhases COMPILE_ALL_SERIALIZED = new CompilationPhases( + public final static CompilationPhases COMPILE_ALL_CACHED = new CompilationPhases( "Eager compilation from serializaed state", - RECOMPILE_SERIALIZED_UPTO_BYTECODE, + RECOMPILE_CACHED_UPTO_BYTECODE, GENERATE_BYTECODE_AND_INSTALL); /** @@ -248,9 +247,9 @@ GENERATE_BYTECODE_AND_INSTALL_RESTOF); /** Compile from serialized for a rest of method */ - public final static CompilationPhases COMPILE_SERIALIZED_RESTOF = new CompilationPhases( + public final static CompilationPhases COMPILE_CACHED_RESTOF = new CompilationPhases( "Compile serialized, rest of", - RECOMPILE_SERIALIZED_UPTO_BYTECODE, + RECOMPILE_CACHED_UPTO_BYTECODE, GENERATE_BYTECODE_AND_INSTALL_RESTOF); private final List phases; @@ -313,7 +312,7 @@ } boolean isRestOfCompilation() { - return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_SERIALIZED_RESTOF; + return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_CACHED_RESTOF; } String getDesc() { @@ -766,14 +765,6 @@ compileUnits.addAll(newUnits); } - void serializeAst(final FunctionNode fn) { - serializedAsts.put(fn.getId(), AstSerializer.serialize(fn)); - } - - byte[] removeSerializedAst(final int fnId) { - return serializedAsts.remove(fnId); - } - CompileUnit findUnit(final long weight) { for (final CompileUnit unit : compileUnits) { if (unit.canHold(weight)) { diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Sep 03 16:14:47 2015 -0700 @@ -188,6 +188,9 @@ log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope."); newFunctionNode = newFunctionNode.setInDynamicContext(lc); } + if (newFunctionNode == lc.getOutermostFunction() && !newFunctionNode.hasApplyToCallSpecialization()) { + data.setCachedAst(newFunctionNode); + } return newFunctionNode; } @@ -208,8 +211,7 @@ ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()), nestedFunctions, externalSymbolDepths.get(fnId), - internalSymbols.get(fnId), - compiler.removeSerializedAst(fnId)); + internalSymbols.get(fnId)); if (lc.getOutermostFunction() != newFunctionNode) { final FunctionNode parentFn = lc.getParentFunction(newFunctionNode); diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Label.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Label.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Label.java Thu Sep 03 16:14:47 2015 -0700 @@ -497,7 +497,7 @@ private transient Label.Stack stack; /** ASM representation of this label */ - private jdk.internal.org.objectweb.asm.Label label; + private transient jdk.internal.org.objectweb.asm.Label label; /** Id for debugging purposes, remove if footprint becomes unmanageable */ private final int id; diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeMap.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeMap.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeMap.java Thu Sep 03 16:14:47 2015 -0700 @@ -27,21 +27,18 @@ import java.lang.invoke.MethodType; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; import java.util.NoSuchElementException; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.runtime.ScriptFunction; /** - * A data structure that maps one or several function nodes (by their unique id:s, not by - * the FunctionNode object itself, due to copy on write changing it several times through - * code generation. + * A tuple containing function id, parameter types, return type and needsCallee flag. */ -public class TypeMap { - private final Map paramTypeMap = new HashMap<>(); - private final Map returnTypeMap = new HashMap<>(); +public final class TypeMap { + private final int functionNodeId; + private final Type[] paramTypes; + private final Type returnType; private final boolean needsCallee; /** @@ -56,9 +53,10 @@ for (final Class p : type.parameterArray()) { types[pos++] = Type.typeFor(p); } - paramTypeMap.put(functionNodeId, types); - returnTypeMap.put(functionNodeId, Type.typeFor(type.returnType())); + this.functionNodeId = functionNodeId; + this.paramTypes = types; + this.returnType = Type.typeFor(type.returnType()); this.needsCallee = needsCallee; } @@ -69,20 +67,14 @@ * @throws NoSuchElementException if the type map has no mapping for the requested function */ public Type[] getParameterTypes(final int functionNodeId) { - final Type[] paramTypes = paramTypeMap.get(functionNodeId); - if (paramTypes == null) { - throw new NoSuchElementException(Integer.toString(functionNodeId)); - } + assert this.functionNodeId == functionNodeId; return paramTypes.clone(); } MethodType getCallSiteType(final FunctionNode functionNode) { - final Type[] types = paramTypeMap.get(functionNode.getId()); - if (types == null) { - return null; - } - - MethodType mt = MethodType.methodType(returnTypeMap.get(functionNode.getId()).getTypeClass()); + assert this.functionNodeId == functionNode.getId(); + final Type[] types = paramTypes; + MethodType mt = MethodType.methodType(returnType.getTypeClass()); if (needsCallee) { mt = mt.appendParameterTypes(ScriptFunction.class); } @@ -116,7 +108,8 @@ * @return parameter type for this callsite if known */ Type get(final FunctionNode functionNode, final int pos) { - final Type[] types = paramTypeMap.get(functionNode.getId()); + assert this.functionNodeId == functionNode.getId(); + final Type[] types = paramTypes; assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this; if (types != null && pos < types.length) { return types[pos]; @@ -124,13 +117,6 @@ return null; } - boolean has(final FunctionNode functionNode) { - final int id = functionNode.getId(); - final Type[] paramTypes = paramTypeMap.get(id); - assert (paramTypes == null) == (returnTypeMap.get(id) == null) : "inconsistent param and return types in param map"; - return paramTypes != null; - } - @Override public String toString() { return toString(""); @@ -139,27 +125,16 @@ String toString(final String prefix) { final StringBuilder sb = new StringBuilder(); - if (paramTypeMap.isEmpty()) { - sb.append(prefix).append("\t"); - return sb.toString(); - } - - for (final Map.Entry entry : paramTypeMap.entrySet()) { - final int id = entry.getKey(); - sb.append(prefix).append('\t'); - sb.append("function ").append(id).append('\n'); - sb.append(prefix).append("\t\tparamTypes="); - if (entry.getValue() == null) { - sb.append("[]"); - } else { - sb.append(Arrays.toString(entry.getValue())); - } - sb.append('\n'); - sb.append(prefix).append("\t\treturnType="); - final Type ret = returnTypeMap.get(id); - sb.append(ret == null ? "N/A" : ret); - sb.append('\n'); - } + final int id = functionNodeId; + sb.append(prefix).append('\t'); + sb.append("function ").append(id).append('\n'); + sb.append(prefix).append("\t\tparamTypes="); + sb.append(Arrays.toString(paramTypes)); + sb.append('\n'); + sb.append(prefix).append("\t\treturnType="); + final Type ret = returnType; + sb.append(ret == null ? "N/A" : ret); + sb.append('\n'); return sb.toString(); } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Thu Sep 03 16:14:47 2015 -0700 @@ -159,11 +159,42 @@ } /** - * Clear the symbols in the block. - * TODO: make this immutable. + * Returns true if this block defines any symbols. + * @return true if this block defines any symbols. + */ + public boolean hasSymbols() { + return !symbols.isEmpty(); + } + + /** + * Replaces symbols defined in this block with different symbols. Used to ensure symbol tables are + * immutable upon construction and have copy-on-write semantics. Note that this method only replaces the + * symbols in the symbol table, it does not act on any contained AST nodes that might reference the symbols. + * Those should be updated separately as this method is meant to be used as part of such an update pass. + * @param lc the current lexical context + * @param replacements the map of symbol replacements + * @return a new block with replaced symbols, or this block if none of the replacements modified the symbol + * table. */ - public void clearSymbols() { - symbols.clear(); + public Block replaceSymbols(final LexicalContext lc, final Map replacements) { + if (symbols.isEmpty()) { + return this; + } + final LinkedHashMap newSymbols = new LinkedHashMap<>(symbols); + for (final Map.Entry entry: newSymbols.entrySet()) { + final Symbol newSymbol = replacements.get(entry.getValue()); + assert newSymbol != null : "Missing replacement for " + entry.getKey(); + entry.setValue(newSymbol); + } + return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, newSymbols, conversion)); + } + + /** + * Returns a copy of this block with a shallow copy of the symbol table. + * @return a copy of this block with a shallow copy of the symbol table. + */ + public Block copyWithNewSymbols() { + return new Block(this, finish, statements, flags, new LinkedHashMap<>(symbols), conversion); } @Override @@ -191,7 +222,7 @@ * @return symbol iterator */ public List getSymbols() { - return Collections.unmodifiableList(new ArrayList<>(symbols.values())); + return symbols.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(new ArrayList<>(symbols.values())); } /** @@ -355,10 +386,9 @@ /** * Add or overwrite an existing symbol in the block * - * @param lc get lexical context * @param symbol symbol */ - public void putSymbol(final LexicalContext lc, final Symbol symbol) { + public void putSymbol(final Symbol symbol) { symbols.put(symbol.getName(), symbol); } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Thu Sep 03 16:14:47 2015 -0700 @@ -43,7 +43,7 @@ private final JoinPredecessorExpression modify; /** Iterator symbol. */ - private Symbol iterator; + private final Symbol iterator; /** Is this a normal for in loop? */ public static final int IS_FOR_IN = 1 << 0; @@ -86,23 +86,23 @@ this.flags = flags; this.init = init; this.modify = modify; + this.iterator = null; } private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, - final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion) { + final Block body, final JoinPredecessorExpression modify, final int flags, + final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) { super(forNode, test, body, controlFlowEscapes, conversion); this.init = init; this.modify = modify; this.flags = flags; - // Even if the for node gets cloned in try/finally, the symbol can be shared as only one branch of the finally - // is executed. - this.iterator = forNode.iterator; + this.iterator = iterator; } @Override public Node ensureUniqueLabels(final LexicalContext lc) { - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -175,7 +175,7 @@ if (this.init == init) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } /** @@ -204,10 +204,15 @@ /** * Assign an iterator symbol to this ForNode. Used for for in and for each constructs + * @param lc the current lexical context * @param iterator the iterator symbol + * @return a ForNode with the iterator set */ - public void setIterator(final Symbol iterator) { - this.iterator = iterator; + public ForNode setIterator(final LexicalContext lc, final Symbol iterator) { + if (this.iterator == iterator) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } /** @@ -228,7 +233,7 @@ if (this.modify == modify) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -236,7 +241,7 @@ if (this.test == test) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -249,7 +254,7 @@ if (this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -257,12 +262,12 @@ if (this.controlFlowEscapes == controlFlowEscapes) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Thu Sep 03 16:14:47 2015 -0700 @@ -264,6 +264,11 @@ */ public static final int NEEDS_CALLEE = 1 << 26; + /** + * Is the function node cached? + */ + public static final int IS_CACHED = 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 | @@ -353,7 +358,7 @@ final List parameters, final int thisProperties, final Class rootClass, - final Source source, Namespace namespace) { + final Source source, final Namespace namespace) { super(functionNode); this.endParserState = endParserState; @@ -757,7 +762,7 @@ */ public boolean needsCallee() { // NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units. - return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall(); + return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasApplyToCallSpecialization(); } /** @@ -774,7 +779,7 @@ * Return true if function contains an apply to call transform * @return true if this function has transformed apply to call */ - public boolean hasOptimisticApplyToCall() { + public boolean hasApplyToCallSpecialization() { return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION); } @@ -1026,6 +1031,14 @@ } /** + * Return the number of parameters to this function + * @return the number of parameters + */ + public int getNumOfParams() { + return parameters.size(); + } + + /** * Returns the identifier for a named parameter at the specified position in this function's parameter list. * @param index the parameter's position. * @return the identifier for the requested named parameter. @@ -1162,6 +1175,24 @@ } /** + * Returns true if this function node has been cached. + * @return true if this function node has been cached. + */ + public boolean isCached() { + return getFlag(IS_CACHED); + } + + /** + * Mark this function node as having been cached. + * @param lc the current lexical context + * @return a function node equivalent to this one, with the flag set. + */ + public FunctionNode setCached(final LexicalContext lc) { + return setFlag(lc, IS_CACHED); + } + + + /** * Get the compile unit used to compile this function * @see Compiler * @return the compile unit diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SwitchNode.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SwitchNode.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SwitchNode.java Thu Sep 03 16:14:47 2015 -0700 @@ -53,7 +53,7 @@ private final boolean uniqueInteger; /** Tag symbol. */ - private Symbol tag; + private final Symbol tag; /** * Constructor @@ -71,15 +71,16 @@ this.cases = cases; this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase); this.uniqueInteger = false; + this.tag = null; } private SwitchNode(final SwitchNode switchNode, final Expression expression, final List cases, - final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger) { + final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger, final Symbol tag) { super(switchNode, conversion); this.expression = expression; this.cases = cases; this.defaultCaseIndex = defaultCaseIndex; - this.tag = switchNode.getTag(); //TODO are symbols inherited as references? + this.tag = tag; this.uniqueInteger = uniqueInteger; } @@ -89,7 +90,7 @@ for (final CaseNode caseNode : cases) { newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion())); } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger, tag)); } @Override @@ -157,7 +158,7 @@ if (this.cases == cases) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -189,7 +190,7 @@ if (this.expression == expression) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -204,10 +205,15 @@ /** * Set the tag symbol for this switch. The tag symbol is where * the switch expression result is stored + * @param lc lexical context * @param tag a symbol + * @return a switch node with the symbol set */ - public void setTag(final Symbol tag) { - this.tag = tag; + public SwitchNode setTag(final LexicalContext lc, final Symbol tag) { + if (this.tag == tag) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -229,12 +235,12 @@ if(this.uniqueInteger == uniqueInteger) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Symbol.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Symbol.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Symbol.java Thu Sep 03 16:14:47 2015 -0700 @@ -25,7 +25,10 @@ package jdk.nashorn.internal.ir; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.PrintWriter; +import java.io.Serializable; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; @@ -47,7 +50,9 @@ * refer to their location. */ -public final class Symbol implements Comparable { +public final class Symbol implements Comparable, Cloneable, Serializable { + private static final long serialVersionUID = 1L; + /** Is this Global */ public static final int IS_GLOBAL = 1; /** Is this a variable */ @@ -94,10 +99,10 @@ /** First bytecode method local variable slot for storing the value(s) of this variable. -1 indicates the variable * is not stored in local variable slots or it is not yet known. */ - private int firstSlot = -1; + private transient int firstSlot = -1; /** Field number in scope or property; array index in varargs when not using arguments object. */ - private int fieldIndex = -1; + private transient int fieldIndex = -1; /** Number of times this symbol is used in code */ private int useCount; @@ -144,6 +149,15 @@ } } + @Override + public Symbol clone() { + try { + return (Symbol)super.clone(); + } catch (final CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + private static String align(final String string, final int max) { final StringBuilder sb = new StringBuilder(); sb.append(string.substring(0, Math.min(string.length(), max))); @@ -337,7 +351,7 @@ * Flag this symbol as scope as described in {@link Symbol#isScope()} * @return the symbol */ - public Symbol setIsScope() { + public Symbol setIsScope() { if (!isScope()) { if(shouldTrace()) { trace("SET IS SCOPE"); @@ -609,11 +623,11 @@ /** * Increase the symbol's use count by one. - * @return the symbol */ - public Symbol increaseUseCount() { - useCount++; - return this; + public void increaseUseCount() { + if (isScope()) { // Avoid dirtying a cache line; we only need the use count for scoped symbols + useCount++; + } } /** @@ -669,4 +683,10 @@ new Throwable().printStackTrace(Context.getCurrentErr()); } } + + private void readObject(final ObjectInputStream in) throws ClassNotFoundException, IOException { + in.defaultReadObject(); + firstSlot = -1; + fieldIndex = -1; + } } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java Thu Sep 03 16:14:47 2015 -0700 @@ -65,7 +65,7 @@ private final List inlinedFinallies; /** Exception symbol. */ - private Symbol exception; + private final Symbol exception; private final LocalVariableConversion conversion; @@ -86,22 +86,23 @@ this.finallyBody = finallyBody; this.conversion = null; this.inlinedFinallies = Collections.emptyList(); + this.exception = null; } - private TryNode(final TryNode tryNode, final Block body, final List catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List inlinedFinallies) { + private TryNode(final TryNode tryNode, final Block body, final List catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List inlinedFinallies, final Symbol exception) { super(tryNode); this.body = body; this.catchBlocks = catchBlocks; this.finallyBody = finallyBody; this.conversion = conversion; this.inlinedFinallies = inlinedFinallies; - this.exception = tryNode.exception; + this.exception = exception; } @Override public Node ensureUniqueLabels(final LexicalContext lc) { //try nodes are never in lex context - return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); } @Override @@ -160,7 +161,7 @@ if (this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -197,7 +198,7 @@ if (this.catchBlocks == catchBlocks) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -209,12 +210,15 @@ } /** * Set the exception symbol for this try block + * @param lc lexical context * @param exception a symbol for the compiler to store the exception in * @return new TryNode or same if unchanged */ - public TryNode setException(final Symbol exception) { - this.exception = exception; - return this; + public TryNode setException(final LexicalContext lc, final Symbol exception) { + if (this.exception == exception) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -277,7 +281,7 @@ if (this.finallyBody == finallyBody) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -293,7 +297,7 @@ return this; } assert checkInlinedFinallies(inlinedFinallies); - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } private static boolean checkInlinedFinallies(final List inlinedFinallies) { @@ -314,7 +318,7 @@ if(this.conversion == conversion) { return this; } - return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); } @Override diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java Thu Sep 03 16:14:47 2015 -0700 @@ -2419,7 +2419,7 @@ } private void initScripting(final ScriptEnvironment scriptEnv) { - Object value; + ScriptObject value; value = ScriptFunctionImpl.makeFunction("readLine", ScriptingFunctions.READLINE); addOwnProperty("readLine", Attribute.NOT_ENUMERABLE, value); @@ -2428,11 +2428,13 @@ final String execName = ScriptingFunctions.EXEC_NAME; value = ScriptFunctionImpl.makeFunction(execName, ScriptingFunctions.EXEC); + value.addOwnProperty(ScriptingFunctions.THROW_ON_ERROR_NAME, Attribute.NOT_ENUMERABLE, false); + addOwnProperty(execName, Attribute.NOT_ENUMERABLE, value); // Nashorn extension: global.echo (scripting-mode-only) // alias for "print" - value = get("print"); + value = (ScriptObject)get("print"); addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value); // Nashorn extension: global.$OPTIONS (scripting-mode-only) diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java Thu Sep 03 16:14:47 2015 -0700 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.parser; import static jdk.nashorn.internal.parser.TokenType.ADD; +import static jdk.nashorn.internal.parser.TokenType.BINARY_NUMBER; import static jdk.nashorn.internal.parser.TokenType.COMMENT; import static jdk.nashorn.internal.parser.TokenType.DECIMAL; import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT; @@ -40,6 +41,7 @@ import static jdk.nashorn.internal.parser.TokenType.LBRACE; import static jdk.nashorn.internal.parser.TokenType.LPAREN; import static jdk.nashorn.internal.parser.TokenType.OCTAL; +import static jdk.nashorn.internal.parser.TokenType.OCTAL_LEGACY; import static jdk.nashorn.internal.parser.TokenType.RBRACE; import static jdk.nashorn.internal.parser.TokenType.REGEX; import static jdk.nashorn.internal.parser.TokenType.RPAREN; @@ -47,6 +49,7 @@ import static jdk.nashorn.internal.parser.TokenType.XML; import java.io.Serializable; + import jdk.nashorn.internal.runtime.ECMAErrors; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSErrorType; @@ -75,6 +78,9 @@ /** True if here and edit strings are supported. */ private final boolean scripting; + /** True if parsing in ECMAScript 6 mode. */ + private final boolean es6; + /** True if a nested scan. (scan to completion, no EOF.) */ private final boolean nested; @@ -173,7 +179,7 @@ * @param stream the token stream to lex */ public Lexer(final Source source, final TokenStream stream) { - this(source, stream, false); + this(source, stream, false, false); } /** @@ -182,9 +188,10 @@ * @param source the source * @param stream the token stream to lex * @param scripting are we in scripting mode + * @param es6 are we in ECMAScript 6 mode */ - public Lexer(final Source source, final TokenStream stream, final boolean scripting) { - this(source, 0, source.getLength(), stream, scripting, false); + public Lexer(final Source source, final TokenStream stream, final boolean scripting, final boolean es6) { + this(source, 0, source.getLength(), stream, scripting, es6, false); } /** @@ -195,16 +202,18 @@ * @param len length of source segment to lex * @param stream token stream to lex * @param scripting are we in scripting mode + * @param es6 are we in ECMAScript 6 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, final boolean pauseOnFunctionBody) { + public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean es6, final boolean pauseOnFunctionBody) { super(source.getContent(), 1, start, len); this.source = source; this.stream = stream; this.scripting = scripting; + this.es6 = es6; this.nested = false; this.pendingLine = 1; this.last = EOL; @@ -218,6 +227,7 @@ source = lexer.source; stream = lexer.stream; scripting = lexer.scripting; + es6 = lexer.es6; nested = true; pendingLine = state.pendingLine; @@ -1088,6 +1098,24 @@ } type = HEXADECIMAL; + } else if (digit == 0 && es6 && (ch1 == 'o' || ch1 == 'O') && convertDigit(ch2, 8) != -1) { + // Skip over 0oN. + skip(3); + // Skip over remaining digits. + while (convertDigit(ch0, 8) != -1) { + skip(1); + } + + type = OCTAL; + } else if (digit == 0 && es6 && (ch1 == 'b' || ch1 == 'B') && convertDigit(ch2, 2) != -1) { + // Skip over 0bN. + skip(3); + // Skip over remaining digits. + while (convertDigit(ch0, 2) != -1) { + skip(1); + } + + type = BINARY_NUMBER; } else { // Check for possible octal constant. boolean octal = digit == 0; @@ -1105,7 +1133,7 @@ } if (octal && position - start > 1) { - type = OCTAL; + type = OCTAL_LEGACY; } else if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') { // Must be a double. if (ch0 == '.') { @@ -1637,10 +1665,14 @@ switch (Token.descType(token)) { case DECIMAL: return Lexer.valueOf(source.getString(start, len), 10); // number - case OCTAL: - return Lexer.valueOf(source.getString(start, len), 8); // number case HEXADECIMAL: return Lexer.valueOf(source.getString(start + 2, len - 2), 16); // number + case OCTAL_LEGACY: + return Lexer.valueOf(source.getString(start, len), 8); // number + case OCTAL: + return Lexer.valueOf(source.getString(start + 2, len - 2), 8); // number + case BINARY_NUMBER: + return Lexer.valueOf(source.getString(start + 2, len - 2), 2); // number case FLOATING: final String str = source.getString(start, len); final double value = Double.valueOf(str); diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Thu Sep 03 16:14:47 2015 -0700 @@ -273,7 +273,7 @@ try { stream = new TokenStream(); - lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, reparsedFunction != null); + lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, env._es6, reparsedFunction != null); lexer.line = lexer.pendingLine = lineOffset + 1; line = lineOffset; @@ -309,7 +309,7 @@ public List parseFormalParameterList() { try { stream = new TokenStream(); - lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions); + lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); // Set up first token (skips opening EOL.) k = -1; @@ -333,7 +333,7 @@ public FunctionNode parseFunctionBody() { try { stream = new TokenStream(); - lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions); + lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); final int functionLine = line; // Set up first token (skips opening EOL.) @@ -716,7 +716,7 @@ restoreBlock(body); body.setFlag(Block.NEEDS_SCOPE); - final Block programBody = new Block(functionToken, functionLine, body.getFlags() | Block.IS_SYNTHETIC, body.getStatements()); + final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC, body.getStatements()); lc.pop(script); script.setLastToken(token); @@ -1216,11 +1216,10 @@ final long forToken = token; final int forLine = line; // start position of this for statement. This is used - // for sort order for variables declared in the initialzer + // for sort order for variables declared in the initializer // part of this 'for' statement (if any). final int forStart = Token.descPosition(forToken); // When ES6 for-let is enabled we create a container block to capture the LET. - final int startLine = start; final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null; // Create FOR node, capturing FOR token. @@ -1341,22 +1340,24 @@ body = getStatement(); } finally { lc.pop(forNode); - } - - if (vars != null) { - for (final VarNode var : vars) { - appendStatement(var); + + if (vars != null) { + for (final VarNode var : vars) { + appendStatement(var); + } + } + if (body != null) { + appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); } - } - if (body != null) { - appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); - } - if (outer != null) { - restoreBlock(outer); - appendStatement(new BlockStatement(startLine, new Block( - outer.getToken(), - body.getFinish(), - outer.getStatements()))); + if (outer != null) { + restoreBlock(outer); + if (body != null) { + appendStatement(new BlockStatement(forLine, new Block( + outer.getToken(), + body.getFinish(), + outer.getStatements()))); + } + } } } @@ -1954,7 +1955,7 @@ } detectSpecialProperty(ident); return ident; - case OCTAL: + case OCTAL_LEGACY: if (isStrictMode) { throw error(AbstractParser.message("strict.no.octal"), token); } @@ -1962,6 +1963,8 @@ case ESCSTRING: case DECIMAL: case HEXADECIMAL: + case OCTAL: + case BINARY_NUMBER: case FLOATING: case REGEX: case XML: @@ -2053,13 +2056,13 @@ // LBRACKET tested in caller. next(); - // Prepare to accummulating elements. + // Prepare to accumulate elements. final List elements = new ArrayList<>(); // Track elisions. boolean elision = true; loop: while (true) { - switch (type) { + switch (type) { case RBRACKET: next(); @@ -2223,7 +2226,7 @@ switch (type) { case IDENT: return getIdent().setIsPropertyName(); - case OCTAL: + case OCTAL_LEGACY: if (isStrictMode) { throw error(AbstractParser.message("strict.no.octal"), token); } @@ -2231,6 +2234,8 @@ case ESCSTRING: case DECIMAL: case HEXADECIMAL: + case OCTAL: + case BINARY_NUMBER: case FLOATING: return getLiteral(); default: @@ -2284,7 +2289,7 @@ } } - propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); + propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); } else { propertyName = propertyName(); } @@ -2553,7 +2558,7 @@ final long callToken = token; switch (type) { - case LBRACKET: + case LBRACKET: { next(); // Get array index. @@ -2565,8 +2570,8 @@ lhs = new IndexNode(callToken, finish, lhs, index); break; - - case PERIOD: + } + case PERIOD: { if (lhs == null) { throw error(AbstractParser.message("expected.operand", type.getNameOrType())); } @@ -2579,7 +2584,7 @@ lhs = new AccessNode(callToken, finish, lhs, property.getName()); break; - + } default: break loop; } @@ -3034,7 +3039,7 @@ assert parserState != null; stream.reset(); - lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions); + lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions, env._es6); line = parserState.line; linePosition = parserState.linePosition; // Doesn't really matter, but it's safe to treat it as if there were a semicolon before @@ -3063,8 +3068,8 @@ 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); + Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting, final boolean es6) { + final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, es6, true); newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON)); return newLexer; } @@ -3107,15 +3112,6 @@ return new RuntimeNode(lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args); } - /* - * parse LHS [a, b, ..., c]. - * - * JavaScript 1.8. - */ - //private Node destructureExpression() { - // return null; - //} - /** * PostfixExpression : * LeftHandSideExpression @@ -3127,7 +3123,7 @@ * UnaryExpression : * PostfixExpression * delete UnaryExpression - * Node UnaryExpression + * void UnaryExpression * typeof UnaryExpression * ++ UnaryExpression * -- UnaryExpression @@ -3333,7 +3329,6 @@ // This method is protected so that subclass can get details // at expression start point! - // TODO - Destructuring array. // Include commas in expression parsing. return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false); } @@ -3407,7 +3402,6 @@ // This method is protected so that subclass can get details // at assignment expression start point! - // TODO - Handle decompose. // Exclude commas in expression parsing. return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn); } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenType.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenType.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenType.java Thu Sep 03 16:14:47 2015 -0700 @@ -170,8 +170,10 @@ YIELD (FUTURESTRICT, "yield"), DECIMAL (LITERAL, null), + HEXADECIMAL (LITERAL, null), + OCTAL_LEGACY (LITERAL, null), OCTAL (LITERAL, null), - HEXADECIMAL (LITERAL, null), + BINARY_NUMBER (LITERAL, null), FLOATING (LITERAL, null), STRING (LITERAL, null), ESCSTRING (LITERAL, null), diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AstSerializer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AstSerializer.java Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,55 @@ +/* + * 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. 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.runtime; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.runtime.options.Options; + +/** + * This static utility class performs serialization of FunctionNode ASTs to a byte array. + * The format is a standard Java serialization stream, deflated. + */ +final class AstSerializer { + // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed + // and size. + private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); + static byte[] serialize(final FunctionNode fn) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Deflater deflater = new Deflater(COMPRESSION_LEVEL); + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { + oout.writeObject(fn); + } catch (final IOException e) { + throw new AssertionError("Unexpected exception serializing function", e); + } finally { + deflater.end(); + } + return out.toByteArray(); + } +} diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Sep 03 16:14:47 2015 -0700 @@ -27,6 +27,7 @@ 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; @@ -820,7 +821,7 @@ // isn't available, we'll use the old one bound into the call site. final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo; FunctionNode fn = effectiveOptInfo.reparse(); - final boolean serialized = effectiveOptInfo.isSerialized(); + final boolean cached = fn.isCached(); final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of if (!shouldRecompile) { @@ -828,11 +829,11 @@ // recompiled a deoptimized version for an inner invocation. // We still need to do the rest of from the beginning logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); - return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); + return restOfHandle(effectiveOptInfo, compiler.compile(fn, cached ? CompilationPhases.COMPILE_CACHED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); } logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); - fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); + fn = compiler.compile(fn, cached ? CompilationPhases.RECOMPILE_CACHED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); log.fine("Reusable IR generated"); // compile the rest of the function, and install it @@ -956,10 +957,6 @@ FunctionNode reparse() { return data.reparse(); } - - boolean isSerialized() { - return data.isSerialized(); - } } @SuppressWarnings("unused") diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Sep 03 16:14:47 2015 -0700 @@ -26,16 +26,24 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; + import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; @@ -45,8 +53,15 @@ import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; import jdk.nashorn.internal.codegen.TypeMap; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.SwitchNode; +import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.ir.TryNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Parser; @@ -55,6 +70,7 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; +import jdk.nashorn.internal.runtime.options.Options; /** * This is a subclass that represents a script function that may be regenerated, * for example with specialization based on call site types, or lazily generated. @@ -66,6 +82,8 @@ /** Prefix used for all recompiled script classes */ public static final String RECOMPILATION_PREFIX = "Recompilation$"; + private static final ExecutorService astSerializerExecutorService = createAstSerializerExecutorService(); + /** Unique function node id for this function node */ private final int functionNodeId; @@ -77,8 +95,12 @@ /** Source from which FunctionNode was parsed. */ private transient Source source; - /** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */ - private final byte[] serializedAst; + /** + * Cached form of the AST. Either a {@code SerializedAst} object used by split functions as they can't be + * reparsed from source, or a soft reference to a {@code FunctionNode} for other functions (it is safe + * to be cleared as they can be reparsed). + */ + private volatile Object cachedAst; /** Token of this function within the source. */ private final long token; @@ -128,7 +150,6 @@ * @param nestedFunctions nested function map * @param externalScopeDepths external scope depths * @param internalSymbols internal symbols to method, defined in its scope - * @param serializedAst a serialized AST representation. Normally only used for split functions. */ public RecompilableScriptFunctionData( final FunctionNode functionNode, @@ -136,8 +157,7 @@ final AllocationStrategy allocationStrategy, final Map nestedFunctions, final Map externalScopeDepths, - final Set internalSymbols, - final byte[] serializedAst) { + final Set internalSymbols) { super(functionName(functionNode), Math.min(functionNode.getParameters().size(), MAX_ARITY), @@ -161,7 +181,6 @@ nfn.setParent(this); } - this.serializedAst = serializedAst; createLogger(); } @@ -244,7 +263,7 @@ * @return parent data, or null if non exists and also null IF UNKNOWN. */ public RecompilableScriptFunctionData getParent() { - return parent; + return parent; } void setParent(final RecompilableScriptFunctionData parent) { @@ -358,13 +377,11 @@ return allocationStrategy.allocate(map); } - boolean isSerialized() { - return serializedAst != null; - } - FunctionNode reparse() { - if (isSerialized()) { - return deserialize(); + final FunctionNode cachedFunction = getCachedAst(); + if (cachedFunction != null) { + assert cachedFunction.isCached(); + return cachedFunction; } final int descPosition = Token.descPosition(token); @@ -391,7 +408,98 @@ return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); } - private FunctionNode deserialize() { + private FunctionNode getCachedAst() { + final Object lCachedAst = cachedAst; + // Are we softly caching the AST? + if (lCachedAst instanceof Reference) { + final FunctionNode fn = (FunctionNode)((Reference)lCachedAst).get(); + if (fn != null) { + // Yes we are - this is fast + return cloneSymbols(fn); + } + // Are we strongly caching a serialized AST (for split functions only)? + } else if (lCachedAst instanceof SerializedAst) { + final SerializedAst serializedAst = (SerializedAst)lCachedAst; + // Even so, are we also softly caching the AST? + final FunctionNode cachedFn = serializedAst.cachedAst.get(); + if (cachedFn != null) { + // Yes we are - this is fast + return cloneSymbols(cachedFn); + } + final FunctionNode deserializedFn = deserialize(serializedAst.serializedAst); + // Softly cache after deserialization, maybe next time we won't need to deserialize + serializedAst.cachedAst = new SoftReference<>(deserializedFn); + return deserializedFn; + } + // No cached representation; return null for reparsing + return null; + } + + /** + * Sets the AST to cache in this function + * @param astToCache the new AST to cache + */ + public void setCachedAst(final FunctionNode astToCache) { + assert astToCache.getId() == functionNodeId; // same function + assert !(cachedAst instanceof SerializedAst); // Can't overwrite serialized AST + + final boolean isSplit = astToCache.isSplit(); + // If we're caching a split function, we're doing it in the eager pass, hence there can be no other + // cached representation already. In other words, isSplit implies cachedAst == null. + assert !isSplit || cachedAst == null; // + + final FunctionNode symbolClonedAst = cloneSymbols(astToCache); + final Reference ref = new SoftReference<>(symbolClonedAst); + cachedAst = ref; + + // Asynchronously serialize split functions. + if (isSplit) { + astSerializerExecutorService.execute(() -> { + cachedAst = new SerializedAst(symbolClonedAst, ref); + }); + } + } + + /** + * Creates the AST serializer executor service used for in-memory serialization of split functions' ASTs. + * It is created with an unbounded queue (so it can queue any number of pending tasks). Its core and max + * threads is the same, but they are all allowed to time out so when there's no work, they can all go + * away. The threads will be daemons, and they will time out if idle for a minute. Their priority is also + * slightly lower than normal priority as we'd prefer the CPU to keep running the program; serializing + * split function is a memory conservation measure (it allows us to release the AST), it can wait a bit. + * @return an executor service with above described characteristics. + */ + private static ExecutorService createAstSerializerExecutorService() { + final int threads = Math.max(1, Options.getIntProperty("nashorn.serialize.threads", Runtime.getRuntime().availableProcessors() / 2)); + final ThreadPoolExecutor service = new ThreadPoolExecutor(threads, threads, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<>(), + (r) -> { + final Thread t = new Thread(r, "Nashorn AST Serializer"); + t.setDaemon(true); + t.setPriority(Thread.NORM_PRIORITY - 1); + return t; + }); + service.allowCoreThreadTimeOut(true); + return service; + } + + /** + * A tuple of a serialized AST and a soft reference to a deserialized AST. This is used to cache split + * functions. Since split functions are altered from their source form, they can't be reparsed from + * source. While we could just use the {@code byte[]} representation in {@link RecompilableScriptFunctionData#cachedAst} + * we're using this tuple instead to also keep a deserialized AST around in memory to cut down on + * deserialization costs. + */ + private static class SerializedAst { + private final byte[] serializedAst; + private volatile Reference cachedAst; + + SerializedAst(final FunctionNode fn, final Reference cachedAst) { + this.serializedAst = AstSerializer.serialize(fn); + this.cachedAst = cachedAst; + } + } + + private FunctionNode deserialize(final byte[] serializedAst) { final ScriptEnvironment env = installer.getOwner(); final Timing timing = env._timing; final long t1 = System.nanoTime(); @@ -402,6 +510,107 @@ } } + private FunctionNode cloneSymbols(final FunctionNode fn) { + final IdentityHashMap symbolReplacements = new IdentityHashMap<>(); + final boolean cached = fn.isCached(); + // blockDefinedSymbols is used to re-mark symbols defined outside the function as global. We only + // need to do this when we cache an eagerly parsed function (which currently means a split one, as we + // don't cache non-split functions from the eager pass); those already cached, or those not split + // don't need this step. + final Set blockDefinedSymbols = fn.isSplit() && !cached ? Collections.newSetFromMap(new IdentityHashMap<>()) : null; + FunctionNode newFn = (FunctionNode)fn.accept(new NodeVisitor(new LexicalContext()) { + + private Symbol getReplacement(final Symbol original) { + if (original == null) { + return null; + } + final Symbol existingReplacement = symbolReplacements.get(original); + if (existingReplacement != null) { + return existingReplacement; + } + final Symbol newReplacement = original.clone(); + symbolReplacements.put(original, newReplacement); + return newReplacement; + } + + @Override + public Node leaveIdentNode(final IdentNode identNode) { + final Symbol oldSymbol = identNode.getSymbol(); + if (oldSymbol != null) { + final Symbol replacement = getReplacement(oldSymbol); + return identNode.setSymbol(replacement); + } + return identNode; + } + + @Override + public Node leaveForNode(final ForNode forNode) { + return ensureUniqueLabels(forNode.setIterator(lc, getReplacement(forNode.getIterator()))); + } + + @Override + public Node leaveSwitchNode(final SwitchNode switchNode) { + return ensureUniqueLabels(switchNode.setTag(lc, getReplacement(switchNode.getTag()))); + } + + @Override + public Node leaveTryNode(final TryNode tryNode) { + return ensureUniqueLabels(tryNode.setException(lc, getReplacement(tryNode.getException()))); + } + + @Override + public boolean enterBlock(final Block block) { + for(final Symbol symbol: block.getSymbols()) { + final Symbol replacement = getReplacement(symbol); + if (blockDefinedSymbols != null) { + blockDefinedSymbols.add(replacement); + } + } + return true; + } + + @Override + public Node leaveBlock(final Block block) { + return ensureUniqueLabels(block.replaceSymbols(lc, symbolReplacements)); + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + return functionNode.setParameters(lc, functionNode.visitParameters(this)); + } + + @Override + protected Node leaveDefault(final Node node) { + return ensureUniqueLabels(node); + }; + + private Node ensureUniqueLabels(final Node node) { + // If we're returning a cached AST, we must also ensure unique labels + return cached ? node.ensureUniqueLabels(lc) : node; + } + }); + + if (blockDefinedSymbols != null) { + // Mark all symbols not defined in blocks as globals + Block newBody = null; + for(final Symbol symbol: symbolReplacements.values()) { + if(!blockDefinedSymbols.contains(symbol)) { + assert symbol.isScope(); // must be scope + assert externalScopeDepths.containsKey(symbol.getName()); // must be known to us as an external + // Register it in the function body symbol table as a new global symbol + symbol.setFlags((symbol.getFlags() & ~Symbol.KINDMASK) | Symbol.IS_GLOBAL); + if (newBody == null) { + newBody = newFn.getBody().copyWithNewSymbols(); + newFn = newFn.setBody(null, newBody); + } + assert newBody.getExistingSymbol(symbol.getName()) == null; // must not be defined in the body already + newBody.putSymbol(symbol); + } + } + } + return newFn.setCached(null); + } + private boolean getFunctionFlag(final int flag) { return (functionFlags & flag) != 0; } @@ -512,9 +721,9 @@ final FunctionNode fn = reparse(); final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope); final FunctionNode compiledFn = compiler.compile(fn, - isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL); + fn.isCached() ? CompilationPhases.COMPILE_ALL_CACHED : CompilationPhases.COMPILE_ALL); - if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) { + if (persist && !compiledFn.hasApplyToCallSpecialization()) { compiler.persistClassInfo(cacheKey, compiledFn); } return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints()); diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptingFunctions.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Sep 03 16:14:47 2015 -0700 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; @@ -70,6 +71,9 @@ /** EXIT name - special property used by $EXEC API. */ public static final String EXIT_NAME = "$EXIT"; + /** THROW_ON_ERROR name - special property of the $EXEC function used by $EXEC API. */ + public static final String THROW_ON_ERROR_NAME = "throwOnError"; + /** Names of special properties used by $ENV API. */ public static final String ENV_NAME = "$ENV"; @@ -247,6 +251,19 @@ } } + // if we got a non-zero exit code ("failure"), then we have to decide to throw error or not + if (exit != 0) { + // get the $EXEC function object from the global object + final Object exec = global.get(EXEC_NAME); + assert exec instanceof ScriptObject : EXEC_NAME + " is not a script object!"; + + // Check if the user has set $EXEC.throwOnError property to true. If so, throw RangeError + // If that property is not set or set to false, then silently proceed with the rest. + if (JSType.toBoolean(((ScriptObject)exec).get(THROW_ON_ERROR_NAME))) { + throw rangeError("exec.returned.non.zero", ScriptRuntime.safeToString(exit)); + } + } + // Return the result from stdout. return out; } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/InvalidArrayIndexException.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/InvalidArrayIndexException.java Thu Sep 03 14:24:47 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. 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.runtime.arrays; - -/** - * Mechanism for communicating that something isn't a plain - * numeric integer array index. This enables things like - * array getters for the fast case in a try, basically - * just consisting of an "array[index]" access without - * any checks of boundary conditions that rarely happen - */ -@SuppressWarnings("serial") -class InvalidArrayIndexException extends Exception { - - private final Object index; - - InvalidArrayIndexException(final Object index) { - super(index == null ? "null" : index.toString()); - this.index = index; - } - - InvalidArrayIndexException(final int index) { - this(Integer.valueOf(index)); - } - - InvalidArrayIndexException(final long index) { - this(Long.valueOf(index)); - } - - InvalidArrayIndexException(final double index) { - this(Double.valueOf(index)); - } - - @Override - public String toString() { - return index.toString(); - } - - Object getIndex() { - return index; - } - -} diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/AdaptationException.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/AdaptationException.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/AdaptationException.java Thu Sep 03 16:14:47 2015 -0700 @@ -30,6 +30,7 @@ private final AdaptationResult adaptationResult; AdaptationException(final AdaptationResult.Outcome outcome, final String classList) { + super(null, null, false, false); this.adaptationResult = new AdaptationResult(outcome, classList); } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Sep 03 16:14:47 2015 -0700 @@ -189,7 +189,7 @@ * @return true if the obj is an instance of @FunctionalInterface interface */ public static boolean isFunctionalInterfaceObject(final Object obj) { - return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethod(obj.getClass()) != null); + return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethodName(obj.getClass()) != null); } /** diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Thu Sep 03 16:14:47 2015 -0700 @@ -79,10 +79,10 @@ } // cache of @FunctionalInterface method of implementor classes - private static final ClassValue FUNCTIONAL_IFACE_METHOD = new ClassValue() { + private static final ClassValue FUNCTIONAL_IFACE_METHOD_NAME = new ClassValue() { @Override - protected Method computeValue(final Class type) { - return findFunctionalInterfaceMethod(type); + protected String computeValue(final Class type) { + return findFunctionalInterfaceMethodName(type); } }; @@ -107,19 +107,21 @@ // annotated interface. This way Java method, constructor references or // implementations of java.util.function.* interfaces can be called as though // those are script functions. - final Method m = getFunctionalInterfaceMethod(self.getClass()); - if (m != null) { + final String name = getFunctionalInterfaceMethodName(self.getClass()); + if (name != null) { final MethodType callType = desc.getMethodType(); - // 'callee' and 'thiz' passed from script + actual arguments - if (callType.parameterCount() != m.getParameterCount() + 2) { - throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); - } - return new GuardedInvocation( - // drop 'thiz' passed from the script. - MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1, - callType.parameterType(1)), Guards.getInstanceOfGuard( - m.getDeclaringClass())).asTypeSafeReturn( - new NashornBeansLinkerServices(linkerServices), callType); + // drop callee (Undefined ScriptFunction) and change the request to be dyn:callMethod: + final NashornCallSiteDescriptor newDesc = NashornCallSiteDescriptor.get(desc.getLookup(), + "dyn:callMethod:" + name, desc.getMethodType().dropParameterTypes(1, 2), + NashornCallSiteDescriptor.getFlags(desc)); + final GuardedInvocation gi = getGuardedInvocation(beansLinker, + linkRequest.replaceArguments(newDesc, linkRequest.getArguments()), + new NashornBeansLinkerServices(linkerServices)); + + // drop 'thiz' passed from the script. + return gi.replaceMethods( + MH.dropArguments(linkerServices.filterInternalObjects(gi.getInvocation()), 1, callType.parameterType(1)), + gi.getGuard()); } } return getGuardedInvocation(beansLinker, linkRequest, linkerServices); @@ -163,7 +165,7 @@ return arg instanceof ConsString ? arg.toString() : arg; } - private static Method findFunctionalInterfaceMethod(final Class clazz) { + private static String findFunctionalInterfaceMethodName(final Class clazz) { if (clazz == null) { return null; } @@ -179,20 +181,20 @@ // return the first abstract method for (final Method m : iface.getMethods()) { if (Modifier.isAbstract(m.getModifiers())) { - return m; + return m.getName(); } } } } // did not find here, try super class - return findFunctionalInterfaceMethod(clazz.getSuperclass()); + return findFunctionalInterfaceMethodName(clazz.getSuperclass()); } // Returns @FunctionalInterface annotated interface's single abstract - // method. If not found, returns null. - static Method getFunctionalInterfaceMethod(final Class clazz) { - return FUNCTIONAL_IFACE_METHOD.get(clazz); + // method name. If not found, returns null. + static String getFunctionalInterfaceMethodName(final Class clazz) { + return FUNCTIONAL_IFACE_METHOD_NAME.get(clazz); } static MethodHandleTransformer createHiddenObjectFilter() { diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/regexp/joni/exception/JOniException.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/regexp/joni/exception/JOniException.java Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/regexp/joni/exception/JOniException.java Thu Sep 03 16:14:47 2015 -0700 @@ -24,6 +24,6 @@ private static final long serialVersionUID = -6027192180014164667L; public JOniException(final String message) { - super(message); + super(message, null, false, false); } } diff -r c16cb85c1aec -r 42d8ed4651b6 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Sep 03 14:24:47 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Sep 03 16:14:47 2015 -0700 @@ -163,6 +163,7 @@ range.error.invalid.date=Invalid Date range.error.too.many.errors=Script contains too many errors: {0} errors range.error.concat.string.too.big=Concatenated String is too big +range.error.exec.returned.non.zero=$EXEC returned non-zero exit code: {0} reference.error.not.defined="{0}" is not defined reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side of assignment diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8043232.js.EXPECTED --- a/test/script/basic/JDK-8043232.js.EXPECTED Thu Sep 03 14:24:47 2015 -0700 +++ b/test/script/basic/JDK-8043232.js.EXPECTED Thu Sep 03 16:14:47 2015 -0700 @@ -1,14 +1,28 @@ bcd -[jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] +[jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] red TypeError: No such Java class: java.lang.NonExistent TypeError: No such Java constructor: Object(String) TypeError: Java constructor signature invalid: Object()xxxxx TypeError: Java constructor signature invalid: Object( TypeError: Java constructor signature invalid: Object) -TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.lang.System.getProperty] cannot be used as a constructor. -TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] cannot be used as a constructor. -TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] requires "new". +TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod + String java.lang.System.getProperty(String,String) + String java.lang.System.getProperty(String) +] cannot be used as a constructor. +TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod + void java.io.PrintStream.println() + void java.io.PrintStream.println(boolean) + void java.io.PrintStream.println(char) + void java.io.PrintStream.println(char[]) + void java.io.PrintStream.println(double) + void java.io.PrintStream.println(float) + void java.io.PrintStream.println(int) + void java.io.PrintStream.println(long) + void java.io.PrintStream.println(Object) + void java.io.PrintStream.println(String) +] cannot be used as a constructor. +TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new". TypeError: No such Java constructor: Runnable() TypeError: No such Java constructor: Runnable(int) java.lang.InstantiationException: java.io.InputStream diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8049242.js.EXPECTED --- a/test/script/basic/JDK-8049242.js.EXPECTED Thu Sep 03 14:24:47 2015 -0700 +++ b/test/script/basic/JDK-8049242.js.EXPECTED Thu Sep 03 16:14:47 2015 -0700 @@ -1,10 +1,10 @@ abc -[jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] +[jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] ava TypeError: null is not a function TypeError: null is not a function TypeError: null is not a function -TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.java.lang.String(char[],int,int)] requires "new". +TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new". TypeError: null is not a function TypeError: null is not a function java.lang.InstantiationException: java.io.InputStream diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8068901.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8068901.js Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 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-8068901: Surprising behavior with more than one functional interface on a class + * + * @test + * @run + */ + +var Consumer = java.util.function.Consumer; +var JFunction = java.util.function.Function; + +var fc = new (Java.extend(JFunction, Consumer))({ + apply: function(x) { print("fc invoked as a function") }, + accept: function(x) { print("fc invoked as a consumer") } +}); + +var c = new Consumer(function(x) { print("c invoked as a consumer") }); + +var cf = new (Java.extend(Consumer, JFunction))({ + apply: function(x) { print("cf invoked as a function") }, + accept: function(x) { print("cf invoked as a consumer") } +}); + +var f = new JFunction(function(x) { print("f invoked as a function") }); + +for each(x in [fc, c, fc, cf, f, cf, c, fc, f, cf]) { x(null); } + diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8068901.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8068901.js.EXPECTED Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,10 @@ +fc invoked as a function +c invoked as a consumer +fc invoked as a function +cf invoked as a consumer +f invoked as a function +cf invoked as a consumer +c invoked as a consumer +fc invoked as a function +f invoked as a function +cf invoked as a consumer diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8068903.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8068903.js Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, 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-8068903: Can't invoke vararg @FunctionalInterface methods + * + * @test + * @run + */ + +var vc = new (Java.type("jdk.nashorn.test.models.VarArgConsumer"))( + function(x) { + Assert.assertTrue(x.length == 3); + Assert.assertTrue(x[0] == 1); + Assert.assertTrue(x[1] == 2); + Assert.assertTrue(x[2] == 3); + } +); + +vc(1, 2, 3); diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8079470.js.EXPECTED --- a/test/script/basic/JDK-8079470.js.EXPECTED Thu Sep 03 14:24:47 2015 -0700 +++ b/test/script/basic/JDK-8079470.js.EXPECTED Thu Sep 03 16:14:47 2015 -0700 @@ -1,2 +1,2 @@ -TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod File java.io.File.java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures. -TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures. +TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures. +TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures. diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8134731.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134731.js Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, 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-8134731: `Function.prototype.apply` interacts incorrectly with `arguments` + * + * @test + * @run + */ + +function func() { + return (function(f){ + return function(a1, a2, a3, a4){ + return (f.apply(this, arguments)); + } + })(function(){ + return arguments.length; + }) +} + +Assert.assertTrue(func()() == 0); +Assert.assertTrue(func()(33) == 1); +Assert.assertTrue(func()(33, true) == 2); +Assert.assertTrue(func()(33, true, "hello") == 3); +Assert.assertTrue(func()(33, true, "hello", "world") == 4); +Assert.assertTrue(func()(33, true, "hello", "world", 42) == 5); diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8134865.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134865.js Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015, 2015, 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-8134865: Need to restore for container block from lexical context in finally + * + * @test + * @option --language=es6 + */ + +try { + eval("function f() { for (x : y) { } }"); + throw "should not reach here"; +} catch (e) { + if (!(e instanceof SyntaxError)) throw e; +} diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/JDK-8134939.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134939.js Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 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-8134939: Improve toString method of Dynalink OverloadedDynamicMethod + * + * @test + * @run + */ + +var overloadedSetter = new (Java.type("jdk.nashorn.test.models.OverloadedSetter")); + +Assert.assertEquals(String(overloadedSetter.foo), + "[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" + + " String jdk.nashorn.test.models.OverloadedSetter.foo(String)\n" + + " void jdk.nashorn.test.models.OverloadedSetter.foo(int)\n" + + "]"); + +Assert.assertEquals(String(overloadedSetter.setColor), + "[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" + + " void jdk.nashorn.test.models.OverloadedSetter.setColor(int)\n" + + " void jdk.nashorn.test.models.OverloadedSetter.setColor(String)\n" + + "]"); diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/basic/es6/numeric-literals.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/numeric-literals.js Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 2015, 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-8134873: ECMAScript 6 Numeric Literals + * + * @test + * @option --language=es6 + */ + +function assertEquals(expected, actual) { + if (expected !== actual) { + throw new Error("expected: " + expected + ", actual: " + actual); + } +} + +assertEquals(0b0, 0); +assertEquals(0B0, 0); +assertEquals(0b01, 1); +assertEquals(0B10, 2); +assertEquals(0b11111111, 255); +assertEquals(0b11111111111111111111111111111111, 4294967295); + +assertEquals(0o0, 0); +assertEquals(0O0, 0); +assertEquals(0o01, 1); +assertEquals(0O10, 8); +assertEquals(0o777, 511); + diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/trusted/JDK-8087292.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/trusted/JDK-8087292.js Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 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-8087292: nashorn should have a "fail-fast" option for scripting, analog to bash "set -e" + * + * @test + * @option -scripting + * @run + */ + +function tryExec() { + try { + `java` + } catch (e) { + print(e); + } + + // make sure we got non-zero ("failure") exit code! + if ($EXIT == 0) { + print("Error: expected $EXIT code to be non-zero"); + } +} + +// no exception now! +tryExec(); + +// turn on error with non-zero exit code +$EXEC.throwOnError = true; +tryExec(); + +// no exception after this +$EXEC.throwOnError = false; +tryExec(); diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/trusted/JDK-8087292.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/trusted/JDK-8087292.js.EXPECTED Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,1 @@ +RangeError: $EXEC returned non-zero exit code: 1 diff -r c16cb85c1aec -r 42d8ed4651b6 test/script/trusted/classfilter.js.EXPECTED --- a/test/script/trusted/classfilter.js.EXPECTED Thu Sep 03 14:24:47 2015 -0700 +++ b/test/script/trusted/classfilter.js.EXPECTED Thu Sep 03 16:14:47 2015 -0700 @@ -4,7 +4,18 @@ typeof java.util.Map evalutes to function typeof java.util.HashMap evalutes to function var m = new java.util.HashMap(); m.put('foo', 42); m evalutes to {foo=42} -java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] +java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod + void java.io.PrintStream.println() + void java.io.PrintStream.println(boolean) + void java.io.PrintStream.println(char) + void java.io.PrintStream.println(char[]) + void java.io.PrintStream.println(double) + void java.io.PrintStream.println(float) + void java.io.PrintStream.println(int) + void java.io.PrintStream.println(long) + void java.io.PrintStream.println(Object) + void java.io.PrintStream.println(String) +] java.lang.System.exit evalutes to [jdk.internal.dynalink.beans.SimpleDynamicMethod void java.lang.System.exit(int)] new javax.script.SimpleBindings throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.SimpleBindings Java.type('javax.script.ScriptContext') throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.ScriptContext diff -r c16cb85c1aec -r 42d8ed4651b6 test/src/jdk/nashorn/test/models/VarArgConsumer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/VarArgConsumer.java Thu Sep 03 16:14:47 2015 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 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.test.models; + +/** + * Simple function interface with a varargs SAM method. + */ +@FunctionalInterface +public interface VarArgConsumer { + public void apply(Object... o); +} +