Mercurial > hg > shenandoah-preopenjdk-archive > openjdk8 > nashorn
changeset 1036:4af4491477eb
Merge
author | asaha |
---|---|
date | Tue, 16 Sep 2014 13:59:37 -0700 |
parents | 0bcc64d0d193 (current diff) 1196f17cf7bc (diff) |
children | 83e53aa5acf2 |
files | bin/fixorphantests.sh bin/fixwhitespace.sh bin/jjsdebug.sh bin/rm-non-tracked.sh bin/run_octane.sh test/script/basic/JDK-8048079_1.js test/script/basic/JDK-8048079_1.js.EXPECTED test/script/basic/JDK-8048079_2.js test/script/basic/JDK-8048079_2.js.EXPECTED |
diffstat | 146 files changed, 4099 insertions(+), 845 deletions(-) [+] |
line wrap: on
line diff
--- a/bin/fixorphantests.sh Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -#ensure that all tests tagged with @test are also tagged with @run - -for f in $(find test/script/basic/*.js); do - grep @test $f >/dev/null - TEST=$? - grep @run $f >/dev/null - RUN=$? - - if [ $TEST -eq 0 ] && [ ! $RUN -eq 0 ]; then - echo "repairing ${f}..." - TEMP=$(mktemp /tmp/scratch.XXXXXX) - - #IFS='', -raw flag to preserve white space - while IFS='' read -r line; do - echo $line | grep @test >/dev/null - TEST=$? - printf "%s\n" "$line" - if [ $TEST -eq 0 ]; then - printf "%s\n" "$line" | sed s/@test/@run/g - fi - done < $f >$TEMP - - cp $TEMP $f - - rm -fr $TEMP - fi - -done
--- a/bin/fixwhitespace.sh Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -fix() { - #convert tabs to spaces - find . -name $1 -exec sed -i "" 's/ / /g' {} \; - #remove trailing whitespace - find . -name $1 -exec sed -i "" 's/[ ]*$//' \{} \; -} - -if [ ! -z $1 ]; then - fix $1; -else - fix "*.java" - fix "*.js" -fi
--- a/bin/jjsdebug.sh Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -$JAVA_HOME/bin/jjs -J-Djava.ext.dirs=`dirname $0`/../dist -J-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=y $*
--- a/bin/rm-non-tracked.sh Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -hg status|grep ^\?|awk '{print $2}'|xargs rm
--- a/bin/run_octane.sh Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -LOG="./octane_$(date|sed "s/ /_/g"|sed "s/:/_/g").log" - -run_one() { - sh ../bin/runopt.sh -scripting ../test/script/basic/run-octane.js -- $1 --verbose --iterations 25 | tee -a $LOG -} - -if [ -z $1 ]; then - - run_one "box2d" - run_one "code-load" - run_one "crypto" - run_one "deltablue" - run_one "earley-boyer" - run_one "gbemu" - run_one "mandreel" - run_one "navier-stokes" - run_one "pdfjs" - run_one "raytrace" - run_one "regexp" - run_one "richards" - run_one "splay" - run_one "typescript" - run_one "zlib" - -else - run_one $1 -fi
--- a/make/build.xml Thu Sep 11 15:34:13 2014 -0700 +++ b/make/build.xml Tue Sep 16 13:59:37 2014 -0700 @@ -340,6 +340,13 @@ permission java.util.PropertyPermission "nashorn.test.*", "read"; }; +grant codeBase "file:/${basedir}/test/script/basic/es6/*" { + permission java.io.FilePermission "${basedir}/test/script/-", "read"; + permission java.io.FilePermission "$${user.dir}", "read"; + permission java.util.PropertyPermission "user.dir", "read"; + permission java.util.PropertyPermission "nashorn.test.*", "read"; +}; + grant codeBase "file:/${basedir}/test/script/basic/JDK-8010946-privileged.js" { permission java.util.PropertyPermission "java.security.policy", "read"; };
--- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Tue Sep 16 13:59:37 2014 -0700 @@ -36,6 +36,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE; +import static jdk.nashorn.internal.ir.Symbol.IS_CONST; import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF; import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; @@ -83,11 +84,13 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.Property; -import jdk.nashorn.internal.runtime.PropertyMap; +import jdk.nashorn.internal.runtime.ECMAErrors; +import jdk.nashorn.internal.runtime.ErrorManager; +import jdk.nashorn.internal.runtime.JSErrorType; +import jdk.nashorn.internal.runtime.ParserException; +import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; @@ -101,7 +104,7 @@ * visitor. */ @Logger(name="symbols") -final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements Loggable { +final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable { private final DebugLogger log; private final boolean debug; @@ -190,22 +193,21 @@ * @param body the body of the FunctionNode we are entering */ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { - // This visitor will assign symbol to all declared variables, except function declarations (which are taken care - // in a separate step above) and "var" declarations in for loop initializers. - // + // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers. body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override - public boolean enterFunctionNode(final FunctionNode nestedFn) { - // Don't descend into nested functions - return false; + protected boolean enterDefault(final Node node) { + // Don't bother visiting expressions; var is a statement, it can't be inside an expression. + // This will also prevent visiting nested functions (as FunctionNode is an expression). + return !(node instanceof Expression); } @Override public Node leaveVarNode(final VarNode varNode) { if (varNode.isStatement()) { final IdentNode ident = varNode.getName(); - final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR); - functionNode.addDeclaredSymbol(symbol); + final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body; + final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); if (varNode.isFunctionDeclaration()) { symbol.setIsFunctionDeclaration(); } @@ -303,23 +305,31 @@ return functionNode.setBody(lc, body.setStatements(lc, newStatements)); } - private Symbol defineGlobalSymbol(final Block block, final String name) { - return defineSymbol(block, name, IS_GLOBAL); - } - /** * Defines a new symbol in the given block. * * @param block the block in which to define the symbol * @param name name of symbol. + * @param origin origin node * @param symbolFlags Symbol flags. * * @return Symbol for given name or null for redefinition. */ - private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) { + private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) { int flags = symbolFlags; - Symbol symbol = findSymbol(block, name); // Locate symbol. - final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL; + final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0; + final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL; + + Symbol symbol; + final FunctionNode function; + if (isBlockScope) { + // block scoped variables always live in current block, no need to look for existing symbols in parent blocks. + symbol = block.getExistingSymbol(name); + function = lc.getCurrentFunction(); + } else { + symbol = findSymbol(block, name); + function = lc.getFunction(block); + } // Global variables are implicitly always scope variables too. if (isGlobal) { @@ -333,7 +343,6 @@ final boolean isParam = (flags & KINDMASK) == IS_PARAM; final boolean isVar = (flags & KINDMASK) == IS_VAR; - final FunctionNode function = lc.getFunction(block); if (symbol != null) { // Symbol was already defined. Check if it needs to be redefined. if (isParam) { @@ -345,10 +354,21 @@ throw new AssertionError("duplicate parameter"); } } else if (isVar) { - if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) { + if (isBlockScope) { + // Check redeclaration in same block + if (symbol.hasBeenDeclared()) { + throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin); + } else { + symbol.setHasBeenDeclared(); + } + } else if ((flags & IS_INTERNAL) != 0) { // Always create a new definition. symbol = null; } else { + // Found LET or CONST in parent scope of same function - s SyntaxError + if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) { + throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin); + } // Not defined in this function. Create a new definition. if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { symbol = null; @@ -359,10 +379,10 @@ if (symbol == null) { // If not found, then create a new one. - Block symbolBlock; + final Block symbolBlock; // Determine where to create it. - if (isVar && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { + if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) { symbolBlock = block; //internal vars are always defined in the block closest to them } else if (isGlobal) { symbolBlock = lc.getOutermostFunction().getBody(); @@ -420,15 +440,30 @@ @Override public boolean enterBlock(final Block block) { start(block); - block.clearSymbols(); if (lc.isFunctionBody()) { + block.clearSymbols(); + final FunctionNode fn = lc.getCurrentFunction(); + if (isUnparsedFunction(fn)) { + // It's a skipped nested function. Just mark the symbols being used by it as being in use. + for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) { + nameIsUsed(name, null); + } + // Don't bother descending into it, it must be empty anyway. + assert block.getStatements().isEmpty(); + return false; + } + enterFunctionBody(); } return true; } + private boolean isUnparsedFunction(final FunctionNode fn) { + return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); + } + @Override public boolean enterCatchNode(final CatchNode catchNode) { final IdentNode exception = catchNode.getException(); @@ -441,7 +476,10 @@ // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its // symbol is naturally internal, and should be treated as such. final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName()); - defineSymbol(block, exname, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE); + // IS_LET flag is required to make sure symbol is not visible outside catch block. However, we need to + // clear the IS_LET flag after creation to allow redefinition of symbol inside the catch block. + final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE); + symbol.clearFlag(IS_LET); return true; } @@ -452,15 +490,13 @@ initFunctionWideVariables(functionNode, body); - if (functionNode.isProgram()) { - initGlobalSymbols(body); - } else if (!functionNode.isDeclared() && !functionNode.isAnonymous()) { + if (!functionNode.isProgram() && !functionNode.isDeclared() && !functionNode.isAnonymous()) { // It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's // anonymous. final String name = functionNode.getIdent().getName(); assert name != null; assert body.getExistingSymbol(name) == null; - defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE); + defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE); if(functionNode.allVarsInScope()) { // basically, has deep eval lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL); } @@ -471,41 +507,48 @@ @Override public boolean enterFunctionNode(final FunctionNode functionNode) { - // TODO: once we have information on symbols used by nested functions, we can stop descending into nested - // functions with on-demand compilation, e.g. add - // if(!thisProperties.isEmpty() && env.isOnDemandCompilation()) { - // return false; - // } start(functionNode, false); thisProperties.push(new HashSet<String>()); - //an outermost function in our lexical context that is not a program - //is possible - it is a function being compiled lazily if (functionNode.isDeclared()) { + // Can't use lc.getCurrentBlock() as we can have an outermost function in our lexical context that + // is not a program - it is a function being compiled on-demand. final Iterator<Block> blocks = lc.getBlocks(); if (blocks.hasNext()) { - defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR | (functionNode.isAnonymous()? IS_INTERNAL : 0)); + final IdentNode ident = functionNode.getIdent(); + defineSymbol(blocks.next(), ident.getName(), ident, IS_VAR | (functionNode.isAnonymous()? IS_INTERNAL : 0)); } } + // Every function has a body, even the ones skipped on reparse (they have an empty one). We're + // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that + // are used in them. + assert functionNode.getBody() != null; + return true; } @Override public boolean enterVarNode(final VarNode varNode) { start(varNode); - defineSymbol(lc.getCurrentBlock(), varNode.getName().getName(), IS_VAR | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0)); return true; } + @Override + public Node leaveVarNode(final VarNode varNode) { + final IdentNode ident = varNode.getName(); + defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0)); + return super.leaveVarNode(varNode); + } + private Symbol exceptionSymbol() { return newObjectInternal(EXCEPTION_PREFIX); } /** * This has to run before fix assignment types, store any type specializations for - * paramters, then turn then to objects for the generic version of this method + * parameters, then turn them into objects for the generic version of this method. * * @param functionNode functionNode */ @@ -597,7 +640,7 @@ } private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) { - defineSymbol(block, cc.symbolName(), flags).setNeedsSlot(true); + defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true); } private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) { @@ -608,7 +651,7 @@ initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE); if (functionNode.needsArguments()) { initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE); - defineSymbol(body, ARGUMENTS_VAR.symbolName(), IS_VAR | HAS_OBJECT_VALUE); + defineSymbol(body, ARGUMENTS_VAR.symbolName(), null, IS_VAR | HAS_OBJECT_VALUE); } } @@ -617,20 +660,6 @@ initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL); } - - /** - * Move any properties from the global map into the scope of this function (which must be a program function). - * @param block the function node body for which to init scope vars - */ - private void initGlobalSymbols(final Block block) { - final PropertyMap map = Context.getGlobalMap(); - - for (final Property property : map.getProperties()) { - final Symbol symbol = defineGlobalSymbol(block, property.getKey()); - log.info("Added global symbol from property map ", symbol); - } - } - /** * Initialize parameters for function node. * @param functionNode the function node @@ -639,7 +668,7 @@ final boolean isVarArg = functionNode.isVarArg(); final boolean scopeParams = functionNode.allVarsInScope() || isVarArg; for (final IdentNode param : functionNode.getParameters()) { - final Symbol symbol = defineSymbol(body, param.getName(), IS_PARAM); + final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM); if(scopeParams) { // NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored. // It will force creation of scopes where they would otherwise not necessarily be needed (functions @@ -665,10 +694,29 @@ return definingFn == function; } + private void checkConstAssignment(final IdentNode ident) { + // Check for reassignment of constant + final Symbol symbol = ident.getSymbol(); + if (symbol.isConst()) { + throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident); + } + } + @Override - public Node leaveASSIGN(final BinaryNode binaryNode) { + public Node leaveBinaryNode(final BinaryNode binaryNode) { + if (binaryNode.isAssignment() && binaryNode.lhs() instanceof IdentNode) { + checkConstAssignment((IdentNode) binaryNode.lhs()); + } + switch (binaryNode.tokenType()) { + case ASSIGN: + return leaveASSIGN(binaryNode); + default: + return super.leaveBinaryNode(binaryNode); + } + } + + private Node leaveASSIGN(final BinaryNode binaryNode) { // If we're assigning a property of the this object ("this.foo = ..."), record it. - final Expression lhs = binaryNode.lhs(); if (lhs instanceof AccessNode) { final AccessNode accessNode = (AccessNode) lhs; @@ -684,23 +732,43 @@ } @Override + public Node leaveUnaryNode(final UnaryNode unaryNode) { + if (unaryNode.isAssignment() && unaryNode.getExpression() instanceof IdentNode) { + checkConstAssignment((IdentNode) unaryNode.getExpression()); + } + switch (unaryNode.tokenType()) { + case DELETE: + return leaveDELETE(unaryNode); + case TYPEOF: + return leaveTYPEOF(unaryNode); + default: + return super.leaveUnaryNode(unaryNode); + } + } + + @Override public Node leaveBlock(final Block block) { - // It's not necessary to guard the marking of symbols as locals with this "if"condition for correctness, it's - // just an optimization -- runtime type calculation is not used when the compilation is not an on-demand - // optimistic compilation, so we can skip locals marking then. + // It's not necessary to guard the marking of symbols as locals with this "if" condition for + // correctness, it's just an optimization -- runtime type calculation is not used when the compilation + // is not an on-demand optimistic compilation, so we can skip locals marking then. if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { - for (final Symbol symbol: block.getSymbols()) { - if (!symbol.isScope()) { - assert symbol.isVar() || symbol.isParam(); - compiler.declareLocalSymbol(symbol.getName()); + // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand + // compilation, and we're skipping parsing the function bodies for nested functions, this + // basically only means their parameters. It'd be enough to mistakenly declare to be a local a + // symbol in the outer function named the same as one of the parameters, though. + if (lc.getFunction(block) == lc.getOutermostFunction()) { + for (final Symbol symbol: block.getSymbols()) { + if (!symbol.isScope()) { + assert symbol.isVar() || symbol.isParam(); + compiler.declareLocalSymbol(symbol.getName()); + } } } } return block; } - @Override - public Node leaveDELETE(final UnaryNode unaryNode) { + private Node leaveDELETE(final UnaryNode unaryNode) { final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final boolean strictMode = currentFunctionNode.isStrict(); final Expression rhs = unaryNode.getExpression(); @@ -764,24 +832,45 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { - - return markProgramBlock( + final FunctionNode finalizedFunction; + if (isUnparsedFunction(functionNode)) { + finalizedFunction = functionNode; + } else { + finalizedFunction = + markProgramBlock( removeUnusedSlots( createSyntheticInitializers( finalizeParameters( lc.applyTopFlags(functionNode)))) - .setThisProperties(lc, thisProperties.pop().size()) - .setState(lc, CompilationState.SYMBOLS_ASSIGNED)); + .setThisProperties(lc, thisProperties.pop().size())); + } + return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED); } @Override public Node leaveIdentNode(final IdentNode identNode) { - final String name = identNode.getName(); - if (identNode.isPropertyName()) { return identNode; } + final Symbol symbol = nameIsUsed(identNode.getName(), identNode); + + if (!identNode.isInitializedHere()) { + symbol.increaseUseCount(); + } + + IdentNode newIdentNode = identNode.setSymbol(symbol); + + // If a block-scoped var is used before its declaration mark it as dead. + // We can only statically detect this for local vars, cross-function symbols require runtime checks. + if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) { + newIdentNode = newIdentNode.markDead(); + } + + return end(newIdentNode); + } + + private Symbol nameIsUsed(final String name, final IdentNode origin) { final Block block = lc.getCurrentBlock(); Symbol symbol = findSymbol(block, name); @@ -799,18 +888,12 @@ // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) maybeForceScope(symbol); } else { - log.info("No symbol exists. Declare as global: ", symbol); - symbol = defineGlobalSymbol(block, name); - Symbol.setSymbolIsScope(lc, symbol); + log.info("No symbol exists. Declare as global: ", name); + symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE); } functionUsesSymbol(symbol); - - if (!identNode.isInitializedHere()) { - symbol.increaseUseCount(); - } - - return end(identNode.setSymbol(symbol)); + return symbol; } @Override @@ -834,8 +917,7 @@ return tryNode; } - @Override - public Node leaveTYPEOF(final UnaryNode unaryNode) { + private Node leaveTYPEOF(final UnaryNode unaryNode) { final Expression rhs = unaryNode.getExpression(); final List<Expression> args = new ArrayList<>(); @@ -859,7 +941,6 @@ return functionNode; } - assert functionNode.getId() == 1; return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE)); } @@ -875,7 +956,7 @@ } private Symbol newInternal(final CompilerConstants cc, final int flags) { - return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), IS_VAR | IS_INTERNAL | flags); //NASHORN-73 + return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73 } private Symbol newObjectInternal(final CompilerConstants cc) { @@ -915,7 +996,8 @@ return false; } - if (lc.getCurrentFunction().allVarsInScope()) { + final FunctionNode func = lc.getCurrentFunction(); + if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) { return true; } @@ -955,4 +1037,16 @@ final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); return !(units == null || units.isEmpty()); } + + private void throwParserException(final String message, final Node origin) { + if (origin == null) { + throw new ParserException(message); + } + final Source source = compiler.getSource(); + final long token = origin.getToken(); + final int line = source.getLine(origin.getStart()); + final int column = source.getColumn(origin.getStart()); + final String formatted = ErrorManager.format(message, source, line, column, token); + throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token); + } }
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java Tue Sep 16 13:59:37 2014 -0700 @@ -59,6 +59,7 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.Set; + import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; @@ -135,6 +136,16 @@ /** Set of constants access methods required. */ private Set<Class<?>> constantMethodNeeded; + private int methodCount; + + private int initCount; + + private int clinitCount; + + private int fieldCount; + + private final Set<String> methodNames; + /** * Constructor - only used internally in this class as it breaks * abstraction towards ASM or other code generator below @@ -146,6 +157,11 @@ this.context = context; this.cw = cw; this.methodsStarted = new HashSet<>(); + this.methodNames = new HashSet<>(); + } + + public Set<String> getMethodNames() { + return methodNames; } /** @@ -209,6 +225,38 @@ } /** + * Get the method count, including init and clinit methods + * @return method count + */ + public int getMethodCount() { + return methodCount; + } + + /** + * Get the clinit count + * @return clinit count + */ + public int getClinitCount() { + return clinitCount; + } + + /** + * Get the init count + * @return init count + */ + public int getInitCount() { + return initCount; + } + + /** + * Get the field count + * @return field count + */ + public int getFieldCount() { + return fieldCount; + } + + /** * Convert a binary name to a package/class name. * * @param name Binary name. @@ -359,9 +407,16 @@ */ @Override public void end() { - assert classStarted; + assert classStarted : "class not started for " + unitClassName; if (unitClassName != null) { + final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE)); + initMethod.begin(); + initMethod.load(Type.OBJECT, 0); + initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class); + initMethod.returnVoid(); + initMethod.end(); + defineCommonUtilities(); } @@ -419,6 +474,8 @@ } SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { + methodCount++; + methodNames.add(methodName); return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); } @@ -446,6 +503,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { + methodCount++; + methodNames.add(methodName); return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes)); } @@ -471,6 +530,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) { + methodCount++; + methodNames.add(methodName); return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null)); } @@ -481,6 +542,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter method(final FunctionNode functionNode) { + methodCount++; + methodNames.add(functionNode.getName()); final FunctionSignature signature = new FunctionSignature(functionNode); final MethodVisitor mv = cw.visitMethod( ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0), @@ -499,6 +562,8 @@ * @return method emitter to use for weaving this method */ MethodEmitter restOfMethod(final FunctionNode functionNode) { + methodCount++; + methodNames.add(functionNode.getName()); final MethodVisitor mv = cw.visitMethod( ACC_PUBLIC | ACC_STATIC, functionNode.getName(), @@ -516,6 +581,7 @@ * @return method emitter to use for weaving <clinit> */ MethodEmitter clinit() { + clinitCount++; return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class); } @@ -525,6 +591,7 @@ * @return method emitter to use for weaving <init>()V */ MethodEmitter init() { + initCount++; return method(INIT.symbolName(), void.class); } @@ -535,6 +602,7 @@ * @return method emitter to use for weaving <init>()V */ MethodEmitter init(final Class<?>... ptypes) { + initCount++; return method(INIT.symbolName(), void.class, ptypes); } @@ -547,6 +615,7 @@ * @return method emitter to use for weaving <init>(...)V */ MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) { + initCount++; return method(flags, INIT.symbolName(), void.class, ptypes); } @@ -561,6 +630,7 @@ * @see ClassEmitter.Flag */ final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) { + fieldCount++; cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd(); }
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Sep 16 13:59:37 2014 -0700 @@ -52,6 +52,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_APPLY_TO_CALL; +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_DECLARE; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; @@ -302,6 +303,7 @@ * @return the method generator used */ private MethodEmitter loadIdent(final IdentNode identNode, final TypeBounds resultBounds) { + checkTemporalDeadZone(identNode); final Symbol symbol = identNode.getSymbol(); if (!symbol.isScope()) { @@ -334,6 +336,15 @@ return method; } + // Any access to LET and CONST variables before their declaration must throw ReferenceError. + // This is called the temporal dead zone (TDZ). See https://gist.github.com/rwaldron/f0807a758aa03bcdd58a + private void checkTemporalDeadZone(final IdentNode identNode) { + if (identNode.isDead()) { + method.load(identNode.getSymbol().getName()); + method.invoke(ScriptRuntime.THROW_REFERENCE_ERROR); + } + } + private boolean isRestOf() { return continuationEntryPoints != null; } @@ -1611,9 +1622,18 @@ @Override protected void evaluate() { - method.load(ITERATOR_TYPE, iterSlot); - // TODO: optimistic for-in iteration - method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class)); + new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) { + @Override + void loadStack() { + method.load(ITERATOR_TYPE, iterSlot); + } + + @Override + void consumeStack() { + method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class)); + convertOptimisticReturnValue(); + } + }.emit(); } }.store(); body.accept(this); @@ -3216,27 +3236,34 @@ return false; } final Expression init = varNode.getInit(); + final IdentNode identNode = varNode.getName(); + final Symbol identSymbol = identNode.getSymbol(); + assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol"; + final boolean needsScope = identSymbol.isScope(); if (init == null) { + if (needsScope && varNode.isBlockScoped()) { + // block scoped variables need a DECLARE flag to signal end of temporal dead zone (TDZ) + method.loadCompilerConstant(SCOPE); + method.loadUndefined(Type.OBJECT); + final int flags = CALLSITE_SCOPE | getCallSiteFlags() | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0); + assert isFastScope(identSymbol); + storeFastScopeVar(identSymbol, flags); + } return false; } enterStatement(varNode); - - final IdentNode identNode = varNode.getName(); - final Symbol identSymbol = identNode.getSymbol(); - assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol"; - assert method != null; - final boolean needsScope = identSymbol.isScope(); if (needsScope) { method.loadCompilerConstant(SCOPE); } if (needsScope) { loadExpressionUnbounded(init); - final int flags = CALLSITE_SCOPE | getCallSiteFlags(); + // block scoped variables need a DECLARE flag to signal end of temporal dead zone (TDZ) + final int flags = CALLSITE_SCOPE | getCallSiteFlags() | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0); if (isFastScope(identSymbol)) { storeFastScopeVar(identSymbol, flags); } else { @@ -4343,6 +4370,9 @@ protected abstract void evaluate(); void store() { + if (target instanceof IdentNode) { + checkTemporalDeadZone((IdentNode)target); + } prologue(); evaluate(); // leaves an operation of whatever the operationType was on the stack storeNonDiscard();
--- a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Tue Sep 16 13:59:37 2014 -0700 @@ -31,6 +31,7 @@ import java.util.Deque; import java.util.HashMap; import java.util.Map; + import jdk.nashorn.internal.IntDeque; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Block; @@ -158,7 +159,9 @@ CompileUnit popCompileUnit(final CompileUnit oldUnit) { assert compileUnits.peek() == oldUnit; - compileUnits.pop(); + final CompileUnit unit = compileUnits.pop(); + assert unit.hasCode() : "compile unit popped without code"; + unit.setUsed(); return compileUnits.isEmpty() ? null : compileUnits.peek(); }
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue Sep 16 13:59:37 2014 -0700 @@ -48,6 +48,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; + import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.ir.FunctionNode; @@ -300,6 +301,7 @@ } }, + /** * Reuse compile units, if they are already present. We are using the same compiler * to recompile stuff @@ -334,6 +336,8 @@ if (phases.isRestOfCompilation()) { sb.append("$restOf"); } + //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what + //fills those out anyway. Thus no need for a copy constructor final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); map.put(oldUnit, newUnit); @@ -430,8 +434,14 @@ FunctionNode newFunctionNode = fn; + //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped + //in CodeGeneration - the rest can be used as a working "is compile unit used" metric + fn.getCompileUnit().setUsed(); + compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); + final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); + try { // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program // in the lazy + optimistic world. See CodeGenerator.skipFunction(). @@ -455,12 +465,18 @@ final ClassEmitter classEmitter = compileUnit.getClassEmitter(); classEmitter.end(); + if (!compileUnit.isUsed()) { + compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); + continue; + } + final byte[] bytecode = classEmitter.toByteArray(); assert bytecode != null; final String className = compileUnit.getUnitClassName(); + compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used - compiler.addClass(className, bytecode); + CompileUnit.increaseEmitCount(); // should we verify the generated code? if (senv._verify_code) { @@ -536,6 +552,9 @@ // initialize function in the compile units for (final CompileUnit unit : compiler.getCompileUnits()) { + if (!unit.isUsed()) { + continue; + } unit.setCode(installedClasses.get(unit.getUnitClassName())); }
--- a/src/jdk/nashorn/internal/codegen/CompileUnit.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java Tue Sep 16 13:59:37 2014 -0700 @@ -42,6 +42,10 @@ private Class<?> clazz; + private boolean isUsed; + + private static int emittedUnitCount; + CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) { this.className = className; this.weight = initialWeight; @@ -52,6 +56,33 @@ return new TreeSet<>(); } + static void increaseEmitCount() { + emittedUnitCount++; + } + + public static int getEmittedUnitCount() { + return emittedUnitCount; + } + + /** + * Check if this compile unit is used + * @return true if tagged as in use - i.e active code that needs to be generated + */ + public boolean isUsed() { + return isUsed; + } + + public boolean hasCode() { + return (classEmitter.getMethodCount() - classEmitter.getInitCount() - classEmitter.getClinitCount()) > 0; + } + + /** + * Tag this compile unit as used + */ + public void setUsed() { + this.isUsed = true; + } + /** * Return the class that contains the code for this unit, null if not * generated yet @@ -121,7 +152,8 @@ @Override public String toString() { - return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']'; + final String methods = classEmitter != null ? classEmitter.getMethodNames().toString() : "<anon>"; + return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + " hasCode=" + methods + ']'; } @Override
--- a/src/jdk/nashorn/internal/codegen/Compiler.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Tue Sep 16 13:59:37 2014 -0700 @@ -38,7 +38,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -51,18 +50,21 @@ import java.util.function.Consumer; import java.util.logging.Level; import jdk.internal.dynalink.support.NameCodec; -import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.Optimistic; import jdk.nashorn.internal.ir.debug.ClassHistogramElement; import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator; import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.FunctionInitializer; +import jdk.nashorn.internal.runtime.ParserException; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; @@ -89,6 +91,8 @@ private final String sourceName; + private final ErrorManager errors; + private final boolean optimistic; private final Map<String, byte[]> bytecode; @@ -244,6 +248,15 @@ return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); } + @SuppressWarnings("unused") //TODO I'll use this soon + private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) { + final LinkedList<CompilationPhase> list = new LinkedList<>(); + for (final CompilationPhase p : phases) { + list.add(p == phase ? newPhase : p); + } + return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); + } + private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) { final LinkedList<CompilationPhase> list = new LinkedList<>(); for (final CompilationPhase p : phases) { @@ -311,6 +324,7 @@ * @param env script environment * @param installer code installer * @param source source to compile + * @param errors error manager * @param isStrict is this a strict compilation */ public Compiler( @@ -318,8 +332,9 @@ final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source, + final ErrorManager errors, final boolean isStrict) { - this(context, env, installer, source, isStrict, false, null, null, null, null, null, null); + this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null); } /** @@ -329,6 +344,7 @@ * @param env script environment * @param installer code installer * @param source source to compile + * @param errors error manager * @param isStrict is this a strict compilation * @param isOnDemand is this an on demand compilation * @param compiledFunction compiled function, if any @@ -343,6 +359,7 @@ final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source, + final ErrorManager errors, final boolean isStrict, final boolean isOnDemand, final RecompilableScriptFunctionData compiledFunction, @@ -359,6 +376,7 @@ this.bytecode = new LinkedHashMap<>(); this.log = initLogger(context); this.source = source; + this.errors = errors; this.sourceName = FunctionNode.getSourceName(source); this.onDemand = isOnDemand; this.compiledFunction = compiledFunction; @@ -464,6 +482,19 @@ return typeEvaluator.getOptimisticType(node); } + /** + * Returns true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property names retrieved through + * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its + * property name types. + * @param expr the expression to test + * @return true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property iterators. + */ + boolean hasStringPropertyIterator(final Expression expr) { + return typeEvaluator.hasStringPropertyIterator(expr); + } + void addInvalidatedProgramPoint(final int programPoint, final Type type) { invalidatedProgramPoints.put(programPoint, type); } @@ -524,7 +555,17 @@ for (final CompilationPhase phase : phases) { log.fine(phase, " starting for ", quote(name)); - newFunctionNode = phase.apply(this, phases, newFunctionNode); + + try { + newFunctionNode = phase.apply(this, phases, newFunctionNode); + } catch (final ParserException error) { + errors.error(error); + if (env._dump_on_error) { + error.printStackTrace(env.getErr()); + } + return null; + } + log.fine(phase, " done for function ", quote(name)); if (env._print_mem_usage) { @@ -656,16 +697,8 @@ CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) { final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict()); final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); - classEmitter.begin(); - final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE)); - initMethod.begin(); - initMethod.load(Type.OBJECT, 0); - initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class); - initMethod.returnVoid(); - initMethod.end(); - return compileUnit; } @@ -703,13 +736,6 @@ return name.replace('/', '.'); } - RecompilableScriptFunctionData getProgram() { - if (compiledFunction == null) { - return null; - } - return compiledFunction.getProgram(); - } - RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId); }
--- a/src/jdk/nashorn/internal/codegen/DumpBytecode.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/DumpBytecode.java Tue Sep 16 13:59:37 2014 -0700 @@ -89,7 +89,7 @@ // should code be dumped to disk - only valid in compile_only mode? - if (env._dest_dir != null && env._compile_only) { + if (env._dest_dir != null) { final String fileName = className.replace('.', File.separatorChar) + ".class"; final int index = fileName.lastIndexOf(File.separatorChar);
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Tue Sep 16 13:59:37 2014 -0700 @@ -69,9 +69,7 @@ * Constructor * * @param codegen code generator - * @param keys keys for fields in object - * @param symbols symbols for fields in object - * @param values list of values corresponding to keys + * @param tuples tuples for fields in object */ FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples) { this(codegen, tuples, false, false); @@ -81,9 +79,7 @@ * Constructor * * @param codegen code generator - * @param keys keys for fields in object - * @param symbols symbols for fields in object - * @param values values (or null where no value) to be written to the fields + * @param tuples tuples for fields in object * @param isScope is this a scope object * @param hasArguments does the created object have an "arguments" property */ @@ -165,7 +161,7 @@ * @param method Script method. * @param key Property key. * @param fieldIndex Field number. - * @param value Value to store. + * @param tuple Tuple to store. */ private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) { method.dup(); @@ -188,7 +184,7 @@ * * @param method Script method. * @param index Slot index. - * @param value Value to store. + * @param tuple Tuple to store. */ private void putSlot(final MethodEmitter method, final long index, final MapTuple<T> tuple) { method.dup();
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Tue Sep 16 13:59:37 2014 -0700 @@ -25,8 +25,6 @@ package jdk.nashorn.internal.codegen; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getClassName; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; import java.util.HashMap; @@ -34,6 +32,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; +import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; @@ -44,7 +43,6 @@ import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; @@ -208,14 +206,10 @@ assert nestedFunctions != null; // Generate the object class and property map in case this function is ever used as constructor - final int fieldCount = getPaddedFieldCount(newFunctionNode.getThisProperties()); - final String allocatorClassName = Compiler.binaryName(getClassName(fieldCount)); - final PropertyMap allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, fieldCount, 0); final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData( newFunctionNode, compiler.getCodeInstaller(), - allocatorClassName, - allocatorMap, + new AllocatorDescriptor(newFunctionNode.getThisProperties()), nestedFunctions, externalSymbolDepths.get(fnId), internalSymbols.get(fnId)
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Tue Sep 16 13:59:37 2014 -0700 @@ -551,13 +551,19 @@ final Expression init = forNode.getInit(); if(forNode.isForIn()) { - forNode.getModify().accept(this); - enterTestFirstLoop(forNode, null, init); + final JoinPredecessorExpression iterable = forNode.getModify(); + iterable.accept(this); + enterTestFirstLoop(forNode, null, init, + // If we're iterating over property names, and we can discern from the runtime environment + // of the compilation that the object being iterated over must use strings for property + // names (e.g., it is a native JS object or array), then we'll not bother trying to treat + // the property names optimistically. + !forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression())); } else { if(init != null) { init.accept(this); } - enterTestFirstLoop(forNode, forNode.getModify(), null); + enterTestFirstLoop(forNode, forNode.getModify(), null, false); } return false; } @@ -792,7 +798,8 @@ return false; } - private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, final Expression iteratorValues) { + private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, + final Expression iteratorValues, final boolean iteratorValuesAreObject) { final JoinPredecessorExpression test = loopNode.getTest(); if(isAlwaysFalse(test)) { test.accept(this); @@ -814,8 +821,12 @@ jumpToLabel(test, breakLabel); } if(iteratorValues instanceof IdentNode) { - // Receives iterator values; they're currently all objects (JDK-8034954). - onAssignment((IdentNode)iteratorValues, LvarType.OBJECT); + final IdentNode ident = (IdentNode)iteratorValues; + // Receives iterator values; the optimistic type of the iterator values is tracked on the + // identifier, but we override optimism if it's known that the object being iterated over will + // never have primitive property names. + onAssignment(ident, iteratorValuesAreObject ? LvarType.OBJECT : + toLvarType(compiler.getOptimisticType(ident))); } final Block body = loopNode.getBody(); body.accept(this); @@ -955,7 +966,7 @@ if(whileNode.isDoWhile()) { enterDoWhileLoop(whileNode); } else { - enterTestFirstLoop(whileNode, null, null); + enterTestFirstLoop(whileNode, null, null, false); } return false; }
--- a/src/jdk/nashorn/internal/codegen/Lower.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Tue Sep 16 13:59:37 2014 -0700 @@ -71,7 +71,6 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; -import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Source; @@ -93,9 +92,6 @@ private final DebugLogger log; - // needed only to get unique eval id - private final CodeInstaller<?> installer; - /** * Constructor. */ @@ -143,7 +139,6 @@ } }); - this.installer = compiler.getCodeInstaller(); this.log = initLogger(compiler.getContext()); } @@ -566,16 +561,13 @@ private String evalLocation(final IdentNode node) { final Source source = lc.getCurrentFunction().getSource(); final int pos = node.position(); - // Code installer is null when running with --compile-only, use 0 as id in that case - final long id = installer == null ? 0 : installer.getUniqueEvalId(); return new StringBuilder(). append(source.getName()). append('#'). append(source.getLine(pos)). append(':'). append(source.getColumn(pos)). - append("<eval>@"). - append(id). + append("<eval>"). toString(); }
--- a/src/jdk/nashorn/internal/codegen/MapCreator.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/MapCreator.java Tue Sep 16 13:59:37 2014 -0700 @@ -52,8 +52,7 @@ * Constructor * * @param structure structure to generate map for (a JO subclass) - * @param keys list of keys for map - * @param symbols list of symbols for map + * @param tuples list of tuples for map */ MapCreator(final Class<? extends ScriptObject> structure, final List<MapTuple<T>> tuples) { this.structure = structure; @@ -149,6 +148,15 @@ flags |= Property.IS_FUNCTION_DECLARATION; } + if (symbol.isConst()) { + flags |= Property.NOT_WRITABLE; + } + + // Mark symbol as needing declaration. Access before declaration will throw a ReferenceError. + if (symbol.isBlockScoped() && symbol.isScope()) { + flags |= Property.NEEDS_DECLARATION; + } + return flags; } }
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Tue Sep 16 13:59:37 2014 -0700 @@ -2233,9 +2233,8 @@ /** * Generate dynamic setter. Pop receiver and property from stack. * - * @param valueType the type of the value to set - * @param name name of property - * @param flags call site flags + * @param name name of property + * @param flags call site flags */ void dynamicSet(final String name, final int flags) { assert !isOptimistic(flags); @@ -2462,7 +2461,6 @@ * Register line number at a label * * @param line line number - * @param label label */ void lineNumber(final int line) { if (context.getEnv()._debug_lines) {
--- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Tue Sep 16 13:59:37 2014 -0700 @@ -308,7 +308,7 @@ newEmptyInit(className, classEmitter); newAllocate(className, classEmitter); - return toByteArray(classEmitter); + return toByteArray(className, classEmitter); } /** @@ -341,7 +341,7 @@ initWithArguments.returnVoid(); initWithArguments.end(); - return toByteArray(classEmitter); + return toByteArray(className, classEmitter); } /** @@ -484,15 +484,13 @@ * @param classEmitter Open class emitter. * @return Byte codes for the class. */ - private byte[] toByteArray(final ClassEmitter classEmitter) { + private byte[] toByteArray(final String className, final ClassEmitter classEmitter) { classEmitter.end(); final byte[] code = classEmitter.toByteArray(); final ScriptEnvironment env = context.getEnv(); - if (env._print_code && env._print_code_dir == null) { - env.getErr().println(ClassEmitter.disassemble(code)); - } + DumpBytecode.dumpBytecode(env, log, code, className); if (env._verify_code) { context.verify(code); @@ -827,5 +825,45 @@ return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types)); } + /** + * Describes the allocator class name and property map for a constructor function with the specified + * number of "this" properties that it initializes. + * + */ + public static class AllocatorDescriptor { + private final String allocatorClassName; + private final PropertyMap allocatorMap; + /** + * Creates a new allocator descriptor + * @param thisProperties the number of "this" properties that the function initializes + */ + public AllocatorDescriptor(final int thisProperties) { + final int paddedFieldCount = getPaddedFieldCount(thisProperties); + this.allocatorClassName = Compiler.binaryName(getClassName(paddedFieldCount)); + this.allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, paddedFieldCount, 0); + } + + /** + * Returns the name of the class that the function allocates + * @return the name of the class that the function allocates + */ + public String getAllocatorClassName() { + return allocatorClassName; + } + + /** + * Returns the allocator map for the function. + * @return the allocator map for the function. + */ + public PropertyMap getAllocatorMap() { + return allocatorMap; + } + + @Override + public String toString() { + return "AllocatorDescriptor[allocatorClassName=" + allocatorClassName + ", allocatorMap.size=" + + allocatorMap.size() + "]"; + } + } }
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Tue Sep 16 13:59:37 2014 -0700 @@ -31,9 +31,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Path; import java.security.AccessController; import java.security.MessageDigest; import java.security.PrivilegedAction; @@ -41,6 +43,14 @@ import java.util.Base64; import java.util.Date; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.stream.Stream; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; @@ -49,30 +59,66 @@ import jdk.nashorn.internal.runtime.options.Options; /** - * Static utility that encapsulates persistence of decompilation information for functions. Normally, the type info - * persistence feature is enabled and operates in an operating-system specific per-user cache directory. You can - * override the directory by specifying it in the {@code nashorn.typeInfo.cacheDir} directory. Also, you can disable the - * type info persistence altogether by specifying the {@code nashorn.typeInfo.disabled} system property. + * Static utility that encapsulates persistence of type information for functions compiled with optimistic + * typing. With this feature enabled, when a JavaScript function is recompiled because it gets deoptimized, + * the type information for deoptimization is stored in a cache file. If the same function is compiled in a + * subsequent JVM invocation, the type information is used for initial compilation, thus allowing the system + * to skip a lot of intermediate recompilations and immediately emit a version of the code that has its + * optimistic types at (or near) the steady state. + * </p><p> + * Normally, the type info persistence feature is disabled. When the {@code nashorn.typeInfo.maxFiles} system + * property is specified with a value greater than 0, it is enabled and operates in an operating-system + * specific per-user cache directory. You can override the directory by specifying it in the + * {@code nashorn.typeInfo.cacheDir} directory. The maximum number of files is softly enforced by a task that + * cleans up the directory periodically on a separate thread. It is run after some delay after a new file is + * added to the cache. The default delay is 20 seconds, and can be set using the + * {@code nashorn.typeInfo.cleanupDelaySeconds} system property. You can also specify the word + * {@code unlimited} as the value for {@code nashorn.typeInfo.maxFiles} in which case the type info cache is + * allowed to grow without limits. */ public final class OptimisticTypesPersistence { + // Default is 0, for disabling the feature when not specified. A reasonable default when enabled is + // dependent on the application; setting it to e.g. 20000 is probably good enough for most uses and will + // usually cap the cache directory to about 80MB presuming a 4kB filesystem allocation unit. There is one + // file per JavaScript function. + private static final int DEFAULT_MAX_FILES = 0; + // Constants for signifying that the cache should not be limited + private static final int UNLIMITED_FILES = -1; + // Maximum number of files that should be cached on disk. The maximum will be softly enforced. + private static final int MAX_FILES = getMaxFiles(); + // Number of seconds to wait between adding a new file to the cache and running a cleanup process + private static final int DEFAULT_CLEANUP_DELAY = 20; + private static final int CLEANUP_DELAY = Math.max(0, Options.getIntProperty( + "nashorn.typeInfo.cleanupDelaySeconds", DEFAULT_CLEANUP_DELAY)); // The name of the default subdirectory within the system cache directory where we store type info. private static final String DEFAULT_CACHE_SUBDIR_NAME = "com.oracle.java.NashornTypeInfo"; // The directory where we cache type info - private static final File cacheDir = createCacheDir(); + private static final File baseCacheDir = createBaseCacheDir(); + private static final File cacheDir = createCacheDir(baseCacheDir); // In-process locks to make sure we don't have a cross-thread race condition manipulating any file. private static final Object[] locks = cacheDir == null ? null : createLockArray(); - // Only report one read/write error every minute private static final long ERROR_REPORT_THRESHOLD = 60000L; private static volatile long lastReportedError; - + private static final AtomicBoolean scheduledCleanup; + private static final Timer cleanupTimer; + static { + if (baseCacheDir == null || MAX_FILES == UNLIMITED_FILES) { + scheduledCleanup = null; + cleanupTimer = null; + } else { + scheduledCleanup = new AtomicBoolean(); + cleanupTimer = new Timer(true); + } + } /** - * Retrieves an opaque descriptor for the persistence location for a given function. It should be passed to - * {@link #load(Object)} and {@link #store(Object, Map)} methods. + * Retrieves an opaque descriptor for the persistence location for a given function. It should be passed + * to {@link #load(Object)} and {@link #store(Object, Map)} methods. * @param source the source where the function comes from * @param functionId the unique ID number of the function within the source - * @param paramTypes the types of the function parameters (as persistence is per parameter type specialization). + * @param paramTypes the types of the function parameters (as persistence is per parameter type + * specialization). * @return an opaque descriptor for the persistence location. Can be null if persistence is disabled. */ public static Object getLocationDescriptor(final Source source, final int functionId, final Type[] paramTypes) { @@ -82,7 +128,8 @@ final StringBuilder b = new StringBuilder(48); // Base64-encode the digest of the source, and append the function id. b.append(source.getDigest()).append('-').append(functionId); - // Finally, if this is a parameter-type specialized version of the function, add the parameter types to the file name. + // Finally, if this is a parameter-type specialized version of the function, add the parameter types + // to the file name. if(paramTypes != null && paramTypes.length > 0) { b.append('-'); for(final Type t: paramTypes) { @@ -118,6 +165,11 @@ @Override public Void run() { synchronized(getFileLock(file)) { + if (!file.exists()) { + // If the file already exists, we aren't increasing the number of cached files, so + // don't schedule cleanup. + scheduleCleanup(); + } try (final FileOutputStream out = new FileOutputStream(file)) { out.getChannel().lock(); // lock exclusive final DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(out)); @@ -174,19 +226,19 @@ } } - private static File createCacheDir() { - if(Options.getBooleanProperty("nashorn.typeInfo.disabled")) { + private static File createBaseCacheDir() { + if(MAX_FILES == 0 || Options.getBooleanProperty("nashorn.typeInfo.disabled")) { return null; } try { - return createCacheDirPrivileged(); + return createBaseCacheDirPrivileged(); } catch(final Exception e) { getLogger().warning("Failed to create cache dir", e); return null; } } - private static File createCacheDirPrivileged() { + private static File createBaseCacheDirPrivileged() { return AccessController.doPrivileged(new PrivilegedAction<File>() { @Override public File run() { @@ -195,14 +247,35 @@ if(explicitDir != null) { dir = new File(explicitDir); } else { - // When no directory is explicitly specified, get an operating system specific cache directory, - // and create "com.oracle.java.NashornTypeInfo" in it. + // When no directory is explicitly specified, get an operating system specific cache + // directory, and create "com.oracle.java.NashornTypeInfo" in it. final File systemCacheDir = getSystemCacheDir(); dir = new File(systemCacheDir, DEFAULT_CACHE_SUBDIR_NAME); if (isSymbolicLink(dir)) { return null; } } + return dir; + } + }); + } + + private static File createCacheDir(final File baseDir) { + if (baseDir == null) { + return null; + } + try { + return createCacheDirPrivileged(baseDir); + } catch(final Exception e) { + getLogger().warning("Failed to create cache dir", e); + return null; + } + } + + private static File createCacheDirPrivileged(final File baseDir) { + return AccessController.doPrivileged(new PrivilegedAction<File>() { + @Override + public File run() { final String versionDirName; try { versionDirName = getVersionDirName(); @@ -210,12 +283,12 @@ getLogger().warning("Failed to calculate version dir name", e); return null; } - final File versionDir = new File(dir, versionDirName); + final File versionDir = new File(baseDir, versionDirName); if (isSymbolicLink(versionDir)) { return null; } versionDir.mkdirs(); - if(versionDir.isDirectory()) { + if (versionDir.isDirectory()) { getLogger().info("Optimistic type persistence directory is " + versionDir); return versionDir; } @@ -235,12 +308,12 @@ // Mac OS X stores caches in ~/Library/Caches return new File(new File(System.getProperty("user.home"), "Library"), "Caches"); } else if(os.startsWith("Windows")) { - // On Windows, temp directory is the best approximation of a cache directory, as its contents persist across - // reboots and various cleanup utilities know about it. java.io.tmpdir normally points to a user-specific - // temp directory, %HOME%\LocalSettings\Temp. + // On Windows, temp directory is the best approximation of a cache directory, as its contents + // persist across reboots and various cleanup utilities know about it. java.io.tmpdir normally + // points to a user-specific temp directory, %HOME%\LocalSettings\Temp. return new File(System.getProperty("java.io.tmpdir")); } else { - // In all other cases we're presumably dealing with a UNIX flavor (Linux, Solaris, etc.); "~/.cache" + // In other cases we're presumably dealing with a UNIX flavor (Linux, Solaris, etc.); "~/.cache" return new File(System.getProperty("user.home"), ".cache"); } } @@ -278,7 +351,8 @@ final int packageNameLen = className.lastIndexOf('.'); final String dirStr = fileStr.substring(0, fileStr.length() - packageNameLen - 1); final File dir = new File(dirStr); - return "dev-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(getLastModifiedClassFile(dir, 0L))); + return "dev-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(getLastModifiedClassFile( + dir, 0L))); } else { throw new AssertionError(); } @@ -335,4 +409,108 @@ return DebugLogger.DISABLED_LOGGER; } } + + private static void scheduleCleanup() { + if (MAX_FILES != UNLIMITED_FILES && scheduledCleanup.compareAndSet(false, true)) { + cleanupTimer.schedule(new TimerTask() { + @Override + public void run() { + scheduledCleanup.set(false); + try { + doCleanup(); + } catch (final IOException e) { + // Ignore it. While this is unfortunate, we don't have good facility for reporting + // this, as we're running in a thread that has no access to Context, so we can't grab + // a DebugLogger. + } + } + }, TimeUnit.SECONDS.toMillis(CLEANUP_DELAY)); + } + } + + private static void doCleanup() throws IOException { + final long start = System.nanoTime(); + final Path[] files = getAllRegularFilesInLastModifiedOrder(); + final int nFiles = files.length; + final int filesToDelete = Math.max(0, nFiles - MAX_FILES); + int filesDeleted = 0; + for (int i = 0; i < nFiles && filesDeleted < filesToDelete; ++i) { + try { + Files.deleteIfExists(files[i]); + // Even if it didn't exist, we increment filesDeleted; it existed a moment earlier; something + // else deleted it for us; that's okay with us. + filesDeleted++; + } catch (final Exception e) { + // does not increase filesDeleted + } + files[i] = null; // gc eligible + }; + final long duration = System.nanoTime() - start; + } + + private static Path[] getAllRegularFilesInLastModifiedOrder() throws IOException { + try (final Stream<Path> filesStream = Files.walk(baseCacheDir.toPath())) { + // TODO: rewrite below once we can use JDK8 syntactic constructs + return filesStream + .filter(new Predicate<Path>() { + @Override + public boolean test(final Path path) { + return !Files.isDirectory(path); + }; + }) + .map(new Function<Path, PathAndTime>() { + @Override + public PathAndTime apply(final Path path) { + return new PathAndTime(path); + } + }) + .sorted() + .map(new Function<PathAndTime, Path>() { + @Override + public Path apply(final PathAndTime pathAndTime) { + return pathAndTime.path; + } + }) + .toArray(new IntFunction<Path[]>() { // Replace with Path::new + @Override + public Path[] apply(final int length) { + return new Path[length]; + } + }); + } + } + + private static class PathAndTime implements Comparable<PathAndTime> { + private final Path path; + private final long time; + + PathAndTime(final Path path) { + this.path = path; + this.time = getTime(path); + } + + @Override + public int compareTo(final PathAndTime other) { + return Long.compare(time, other.time); + } + + private static long getTime(final Path path) { + try { + return Files.getLastModifiedTime(path).toMillis(); + } catch (IOException e) { + // All files for which we can't retrieve the last modified date will be considered oldest. + return -1L; + } + } + } + + private static int getMaxFiles() { + final String str = Options.getStringProperty("nashorn.typeInfo.maxFiles", null); + if (str == null) { + return DEFAULT_MAX_FILES; + } else if ("unlimited".equals(str)) { + return UNLIMITED_FILES; + } + return Math.max(0, Integer.parseInt(str)); + } }
--- a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Tue Sep 16 13:59:37 2014 -0700 @@ -55,6 +55,19 @@ this.runtimeScope = runtimeScope; } + /** + * Returns true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property names retrieved through + * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its + * property name types. + * @param expr the expression to test + * @return true if the expression can be safely evaluated, and its value is an object known to always use + * String as the type of its property iterators. + */ + boolean hasStringPropertyIterator(final Expression expr) { + return evaluateSafely(expr) instanceof ScriptObject; + } + Type getOptimisticType(final Optimistic node) { assert compiler.useOptimisticTypes(); @@ -108,7 +121,7 @@ // Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed // integer). - final Object value = property.getObjectValue(owner, owner); + final Object value = property.needsDeclaration() ? ScriptRuntime.UNDEFINED : property.getObjectValue(owner, owner); if (value == ScriptRuntime.UNDEFINED) { return null; }
--- a/src/jdk/nashorn/internal/codegen/types/Type.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/types/Type.java Tue Sep 16 13:59:37 2014 -0700 @@ -333,7 +333,7 @@ */ public static Map<Integer, Type> readTypeMap(final DataInput input) throws IOException { final int size = input.readInt(); - if (size == 0) { + if (size <= 0) { return null; } final Map<Integer, Type> map = new TreeMap<>(); @@ -345,7 +345,7 @@ case 'L': type = Type.OBJECT; break; case 'D': type = Type.NUMBER; break; case 'J': type = Type.LONG; break; - default: throw new AssertionError(); + default: continue; } map.put(pp, type); }
--- a/src/jdk/nashorn/internal/ir/Block.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/Block.java Tue Sep 16 13:59:37 2014 -0700 @@ -277,6 +277,14 @@ } /** + * Returns the number of statements in the block. + * @return the number of statements in the block. + */ + public int getStatementCount() { + return statements.size(); + } + + /** * Returns the line number of the first statement in the block. * @return the line number of the first statement in the block, or -1 if the block has no statements. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/ir/CompileUnitHolder.java Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.ir; + +import jdk.nashorn.internal.codegen.CompileUnit; + +/** + * Marker interface for things in the IR that can hold compile units. + * {@link CompileUnit} + */ +public interface CompileUnitHolder { + /** + * Return the compile unit held by this instance + * @return compile unit + */ + public CompileUnit getCompileUnit(); +}
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Tue Sep 16 13:59:37 2014 -0700 @@ -34,10 +34,8 @@ import java.util.Collections; import java.util.EnumSet; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; import java.util.function.Function; import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.CompileUnit; @@ -48,6 +46,7 @@ import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.UserAccessorProperty; @@ -57,7 +56,7 @@ * IR representation for function (or script.) */ @Immutable -public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode> { +public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode>, CompileUnitHolder { /** Type used for all FunctionNodes */ public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class); @@ -110,8 +109,11 @@ /** Source of entity. */ private final Source source; - /** Unique ID used for recompilation among other things */ - private final int id; + /** + * Opaque object representing parser state at the end of the function. Used when reparsing outer functions + * to skip parsing inner functions. + */ + private final Object endParserState; /** External function identifier. */ @Ignore @@ -138,10 +140,6 @@ /** Last token of function. **/ private final long lastToken; - /** Declared symbols in this function node */ - @Ignore - private final Set<Symbol> declaredSymbols; - /** Method's namespace. */ private final Namespace namespace; @@ -260,6 +258,14 @@ /** trace callsite values in this function? */ public static final int IS_TRACE_VALUES = 1 << 26; + /** + * Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a + * parameter on invocation. Note that we aren't, in fact using this flag in function nodes. + * Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData} + * will, however, cache the value of this flag. + */ + public static final int NEEDS_CALLEE = 1 << 27; + /** extension callsite flags mask */ public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST | @@ -275,16 +281,9 @@ /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; - /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. - * We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */ + /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. */ private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL; - /** Used to signify "null", e.g. if someone asks for the parent of the program node */ - public static final int NO_FUNCTION_ID = 0; - - /** Where to start assigning global and unique function node ids */ - public static final int FIRST_FUNCTION_ID = NO_FUNCTION_ID + 1; - /** What is the return type of this function? */ private Type returnType = Type.UNKNOWN; @@ -292,11 +291,10 @@ * Constructor * * @param source the source - * @param id unique id * @param lineNumber line number * @param token token * @param finish finish - * @param firstToken first token of the funtion node (including the function declaration) + * @param firstToken first token of the function node (including the function declaration) * @param namespace the namespace * @param ident the identifier * @param name the name of the function @@ -306,7 +304,6 @@ */ public FunctionNode( final Source source, - final int id, final int lineNumber, final long token, final int finish, @@ -320,7 +317,6 @@ super(token, finish); this.source = source; - this.id = id; this.lineNumber = lineNumber; this.ident = ident; this.name = name; @@ -330,17 +326,18 @@ this.lastToken = token; this.namespace = namespace; this.compilationState = EnumSet.of(CompilationState.INITIALIZED); - this.declaredSymbols = new HashSet<>(); this.flags = flags; this.compileUnit = null; this.body = null; this.thisProperties = 0; this.rootClass = null; + this.endParserState = null; } private FunctionNode( final FunctionNode functionNode, final long lastToken, + Object endParserState, final int flags, final String name, final Type returnType, @@ -352,6 +349,7 @@ final Class<?> rootClass) { super(functionNode); + this.endParserState = endParserState; this.lineNumber = functionNode.lineNumber; this.flags = flags; this.name = name; @@ -366,10 +364,8 @@ // the fields below never change - they are final and assigned in constructor this.source = functionNode.source; - this.id = functionNode.id; this.ident = functionNode.ident; this.namespace = functionNode.namespace; - this.declaredSymbols = functionNode.declaredSymbols; this.kind = functionNode.kind; this.firstToken = functionNode.firstToken; } @@ -435,11 +431,11 @@ } /** - * Get the unique ID for this function + * Get the unique ID for this function within the script file. * @return the id */ public int getId() { - return id; + return position(); } /** @@ -541,6 +537,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -612,6 +609,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -650,15 +648,24 @@ } /** - * Check if the {@code eval} keyword is used in this function + * Check if this function has a call expression for the identifier "eval" (that is, {@code eval(...)}). * - * @return true if {@code eval} is used + * @return true if {@code eval} is called. */ public boolean hasEval() { return getFlag(HAS_EVAL); } /** + * Returns true if a function nested (directly or transitively) within this function {@link #hasEval()}. + * + * @return true if a nested function calls {@code eval}. + */ + public boolean hasNestedEval() { + return getFlag(HAS_NESTED_EVAL); + } + + /** * Get the first token for this function * @return the first token */ @@ -724,24 +731,6 @@ } /** - * Return a set of symbols declared in this function node. This - * is only relevant after Attr, otherwise it will be an empty - * set as no symbols have been introduced - * @return set of declared symbols in function - */ - public Set<Symbol> getDeclaredSymbols() { - return Collections.unmodifiableSet(declaredSymbols); - } - - /** - * Add a declared symbol to this function node - * @param symbol symbol that is declared - */ - public void addDeclaredSymbol(final Symbol symbol) { - declaredSymbols.add(symbol); - } - - /** * Get the function body * @return the function body */ @@ -765,6 +754,7 @@ new FunctionNode( this, lastToken, + endParserState, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : @@ -863,6 +853,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -923,6 +914,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -935,6 +927,41 @@ } /** + * Returns the end parser state for this function. + * @return the end parser state for this function. + */ + public Object getEndParserState() { + return endParserState; + } + + /** + * Set the end parser state for this function. + * @param lc lexical context + * @param endParserState the parser state to set + * @return function node or a new one if state was changed + */ + public FunctionNode setEndParserState(final LexicalContext lc, final Object endParserState) { + if (this.endParserState == endParserState) { + return this; + } + return Node.replaceInLexicalContext( + lc, + this, + new FunctionNode( + this, + lastToken, + endParserState, + flags, + name, + returnType, + compileUnit, + compilationState, + body, + parameters, + thisProperties, rootClass)); + } + + /** * Get the name of this function * @return the name */ @@ -958,6 +985,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -970,13 +998,13 @@ } /** - * Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and + * Check if this function should have all its variables in its own scope. Split sub-functions, and * functions having with and/or eval blocks are such. * * @return true if all variables should be in scope */ public boolean allVarsInScope() { - return isProgram() || getFlag(HAS_ALL_VARS_IN_SCOPE); + return getFlag(HAS_ALL_VARS_IN_SCOPE); } /** @@ -1023,6 +1051,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -1101,6 +1130,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, type, @@ -1126,6 +1156,7 @@ * @see Compiler * @return the compile unit */ + @Override public CompileUnit getCompileUnit() { return compileUnit; } @@ -1147,6 +1178,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType, @@ -1202,6 +1234,7 @@ new FunctionNode( this, lastToken, + endParserState, flags, name, returnType,
--- a/src/jdk/nashorn/internal/ir/IdentNode.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/IdentNode.java Tue Sep 16 13:59:37 2014 -0700 @@ -46,6 +46,8 @@ private static final int INITIALIZED_HERE = 1 << 1; private static final int FUNCTION = 1 << 2; private static final int FUTURESTRICT_NAME = 1 << 3; + private static final int IS_DECLARED_HERE = 1 << 4; + private static final int IS_DEAD = 1 << 5; /** Identifier. */ private final String name; @@ -247,6 +249,45 @@ } /** + * Is this a LET or CONST identifier used before its declaration? + * + * @return true if identifier is dead + */ + public boolean isDead() { + return (flags & IS_DEAD) != 0; + } + + /** + * Flag this IdentNode as a LET or CONST identifier used before its declaration. + * + * @return a new IdentNode equivalent to this but marked as dead. + */ + public IdentNode markDead() { + return new IdentNode(this, name, type, flags | IS_DEAD, programPoint, conversion); + } + + /** + * Is this IdentNode declared here? + * + * @return true if identifier is declared here + */ + public boolean isDeclaredHere() { + return (flags & IS_DECLARED_HERE) != 0; + } + + /** + * Flag this IdentNode as being declared here. + * + * @return a new IdentNode equivalent to this but marked as declared here. + */ + public IdentNode setIsDeclaredHere() { + if (isDeclaredHere()) { + return this; + } + return new IdentNode(this, name, type, flags | IS_DECLARED_HERE, programPoint, conversion); + } + + /** * Check if the name of this IdentNode is same as that of a compile-time property (currently __DIR__, __FILE__, and * __LINE__). *
--- a/src/jdk/nashorn/internal/ir/LexicalContext.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java Tue Sep 16 13:59:37 2014 -0700 @@ -351,8 +351,7 @@ } /** - * Get the function for this block. If the block is itself a function - * this returns identity + * Get the function for this block. * @param block block for which to get function * @return function for block */
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Tue Sep 16 13:59:37 2014 -0700 @@ -603,7 +603,7 @@ * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can * be split if they are too large, for bytecode generation reasons */ - public static final class ArrayUnit { + public static final class ArrayUnit implements CompileUnitHolder { /** Compile unit associated with the postsets range. */ private final CompileUnit compileUnit; @@ -642,6 +642,7 @@ * The array compile unit * @return array compile unit */ + @Override public CompileUnit getCompileUnit() { return compileUnit; }
--- a/src/jdk/nashorn/internal/ir/SplitNode.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/SplitNode.java Tue Sep 16 13:59:37 2014 -0700 @@ -39,7 +39,7 @@ * Node indicating code is split across classes. */ @Immutable -public class SplitNode extends LexicalContextStatement implements Labels { +public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder { /** Split node method name. */ private final String name; @@ -116,6 +116,7 @@ * Get the compile unit for this split node * @return compile unit */ + @Override public CompileUnit getCompileUnit() { return compileUnit; }
--- a/src/jdk/nashorn/internal/ir/Symbol.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Tue Sep 16 13:59:37 2014 -0700 @@ -54,17 +54,17 @@ public static final int IS_VAR = 2; /** Is this a parameter */ public static final int IS_PARAM = 3; - /** Is this a constant */ - public static final int IS_CONSTANT = 4; /** Mask for kind flags */ - public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits + public static final int KINDMASK = (1 << 2) - 1; // Kinds are represented by lower two bits /** Is this symbol in scope */ - public static final int IS_SCOPE = 1 << 3; + public static final int IS_SCOPE = 1 << 2; /** Is this a this symbol */ - public static final int IS_THIS = 1 << 4; + public static final int IS_THIS = 1 << 3; /** Is this a let */ - public static final int IS_LET = 1 << 5; + public static final int IS_LET = 1 << 4; + /** Is this a const */ + public static final int IS_CONST = 1 << 5; /** Is this an internal symbol, never represented explicitly in source code */ public static final int IS_INTERNAL = 1 << 6; /** Is this a function self-reference symbol */ @@ -83,6 +83,8 @@ public static final int HAS_DOUBLE_VALUE = 1 << 13; /** Is this symbol known to store an object value ? */ public static final int HAS_OBJECT_VALUE = 1 << 14; + /** Is this symbol seen a declaration? Used for block scoped LET and CONST symbols only. */ + public static final int HAS_BEEN_DECLARED = 1 << 15; /** Null or name identifying symbol. */ private final String name; @@ -184,14 +186,17 @@ sb.append(" global"); break; case IS_VAR: - sb.append(" var"); + if (isConst()) { + sb.append(" const"); + } else if (isLet()) { + sb.append(" let"); + } else { + sb.append(" var"); + } break; case IS_PARAM: sb.append(" param"); break; - case IS_CONSTANT: - sb.append(" const"); - break; default: break; } @@ -204,10 +209,6 @@ sb.append(" internal"); } - if (isLet()) { - sb.append(" let"); - } - if (isThis()) { sb.append(" this"); } @@ -410,8 +411,8 @@ * Check if this symbol is a constant * @return true if a constant */ - public boolean isConstant() { - return (flags & KINDMASK) == IS_CONSTANT; + public boolean isConst() { + return (flags & IS_CONST) != 0; } /** @@ -440,15 +441,6 @@ } /** - * Flag this symbol as a let - */ - public void setIsLet() { - if (!isLet()) { - flags |= IS_LET; - } - } - - /** * Flag this symbol as a function's self-referencing symbol. * @return true if this symbol as a function's self-referencing symbol. */ @@ -456,6 +448,20 @@ return (flags & IS_FUNCTION_SELF) != 0; } + public boolean isBlockScoped() { + return isLet() || isConst(); + } + + public boolean hasBeenDeclared() { + return (flags & HAS_BEEN_DECLARED) != 0; + } + + public void setHasBeenDeclared() { + if (!hasBeenDeclared()) { + flags |= HAS_BEEN_DECLARED; + } + } + /** * Get the index of the field used to store this symbol, should it be an AccessorProperty * and get allocated in a JO-prefixed ScriptObject subclass.
--- a/src/jdk/nashorn/internal/ir/VarNode.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/ir/VarNode.java Tue Sep 16 13:59:37 2014 -0700 @@ -27,6 +27,7 @@ import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.parser.Token; /** * Node represents a var/let declaration. @@ -43,12 +44,18 @@ private final int flags; /** Flag that determines if this function node is a statement */ - public static final int IS_STATEMENT = 1 << 0; + public static final int IS_STATEMENT = 1 << 0; + + /** Flag for ES6 LET declaration */ + public static final int IS_LET = 1 << 1; + + /** Flag for ES6 CONST declaration */ + public static final int IS_CONST = 1 << 2; /** Flag that determines if this is the last function declaration in a function * This is used to micro optimize the placement of return value assignments for * a program node */ - public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 1; + public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 3; /** * Constructor @@ -109,6 +116,43 @@ } /** + * Is this a VAR node block scoped? This returns true for ECMAScript 6 LET and CONST nodes. + * @return true if an ES6 LET or CONST node + */ + public boolean isBlockScoped() { + return getFlag(IS_LET) || getFlag(IS_CONST); + } + + /** + * Is this an ECMAScript 6 LET node? + * @return true if LET node + */ + public boolean isLet() { + return getFlag(IS_LET); + } + + /** + * Is this an ECMAScript 6 CONST node? + * @return true if CONST node + */ + public boolean isConst() { + return getFlag(IS_CONST); + } + + /** + * Return the flags to use for symbols for this declaration. + * @return the symbol flags + */ + public int getSymbolFlags() { + if (isLet()) { + return Symbol.IS_VAR | Symbol.IS_LET; + } else if (isConst()) { + return Symbol.IS_VAR | Symbol.IS_CONST; + } + return Symbol.IS_VAR; + } + + /** * Does this variable declaration have an init value * @return true if an init exists, false otherwise */ @@ -139,7 +183,7 @@ @Override public void toString(final StringBuilder sb, final boolean printType) { - sb.append("var "); + sb.append(Token.descType(getToken()).getName()).append(' '); name.toString(sb, printType); if (init != null) {
--- a/src/jdk/nashorn/internal/objects/Global.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/objects/Global.java Tue Sep 16 13:59:37 2014 -0700 @@ -631,6 +631,24 @@ } /** + * Returns a method handle that creates a wrapper object for a JS primitive value. + * + * @param self receiver object + * @return method handle to create wrapper objects for primitive receiver + */ + public static MethodHandle getPrimitiveWrapFilter(final Object self) { + if (self instanceof String || self instanceof ConsString) { + return NativeString.WRAPFILTER; + } else if (self instanceof Number) { + return NativeNumber.WRAPFILTER; + } else if (self instanceof Boolean) { + return NativeBoolean.WRAPFILTER; + } + throw new IllegalArgumentException("Unsupported primitive: " + self); + } + + + /** * Create a new empty script object * * @return the new ScriptObject
--- a/src/jdk/nashorn/internal/objects/NativeBoolean.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeBoolean.java Tue Sep 16 13:59:37 2014 -0700 @@ -51,9 +51,9 @@ public final class NativeBoolean extends ScriptObject { private final boolean value; - // Method handle to create an object wrapper for a primitive boolean - private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class)); - // Method handle to retrieve the Boolean prototype object + /** Method handle to create an object wrapper for a primitive boolean. */ + static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class)); + /** Method handle to retrieve the Boolean prototype object. */ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); // initialized by nasgen
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Tue Sep 16 13:59:37 2014 -0700 @@ -28,6 +28,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -697,7 +698,7 @@ if (methodHandle != null) { return new GuardedInvocation( methodHandle, - testJSAdaptor(adaptee, findData.getGetter(Object.class, UnwarrantedOptimismException.INVALID_PROGRAM_POINT), findData.getOwner(), func), + testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func), adaptee.getProtoSwitchPoint(hook, findData.getOwner())); } }
--- a/src/jdk/nashorn/internal/objects/NativeNumber.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeNumber.java Tue Sep 16 13:59:37 2014 -0700 @@ -57,9 +57,9 @@ @ScriptClass("Number") public final class NativeNumber extends ScriptObject { - // Method handle to create an object wrapper for a primitive number - private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class)); - // Method handle to retrieve the Number prototype object + /** Method handle to create an object wrapper for a primitive number. */ + static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class)); + /** Method handle to retrieve the Number prototype object. */ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); /** ECMA 15.7.3.2 largest positive finite value */
--- a/src/jdk/nashorn/internal/objects/NativeString.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeString.java Tue Sep 16 13:59:37 2014 -0700 @@ -71,9 +71,9 @@ private final CharSequence value; - // Method handle to create an object wrapper for a primitive string - private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); - // Method handle to retrieve the String prototype object + /** Method handle to create an object wrapper for a primitive string */ + static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); + /** Method handle to retrieve the String prototype object */ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); // initialized by nasgen
--- a/src/jdk/nashorn/internal/parser/AbstractParser.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/parser/AbstractParser.java Tue Sep 16 13:59:37 2014 -0700 @@ -326,18 +326,28 @@ } /** - * Check next token and advance. + * Check current token and advance to the next token. * * @param expected Expected tokenType. * * @throws ParserException on unexpected token type */ protected final void expect(final TokenType expected) throws ParserException { + expectDontAdvance(expected); + next(); + } + + /** + * Check current token, but don't advance to the next token. + * + * @param expected Expected tokenType. + * + * @throws ParserException on unexpected token type + */ + protected final void expectDontAdvance(final TokenType expected) throws ParserException { if (type != expected) { throw error(expectMessage(expected)); } - - next(); } /**
--- a/src/jdk/nashorn/internal/parser/Lexer.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/parser/Lexer.java Tue Sep 16 13:59:37 2014 -0700 @@ -35,6 +35,7 @@ import static jdk.nashorn.internal.parser.TokenType.ESCSTRING; import static jdk.nashorn.internal.parser.TokenType.EXECSTRING; import static jdk.nashorn.internal.parser.TokenType.FLOATING; +import static jdk.nashorn.internal.parser.TokenType.FUNCTION; import static jdk.nashorn.internal.parser.TokenType.HEXADECIMAL; import static jdk.nashorn.internal.parser.TokenType.LBRACE; import static jdk.nashorn.internal.parser.TokenType.LPAREN; @@ -85,6 +86,9 @@ /** Type of last token added. */ private TokenType last; + private final boolean pauseOnFunctionBody; + private boolean pauseOnNextLeftBrace; + private static final String SPACETAB = " \t"; // ASCII space and tab private static final String LFCR = "\n\r"; // line feed and carriage return (ctrl-m) @@ -182,20 +186,23 @@ * @param scripting are we in scripting mode */ public Lexer(final Source source, final TokenStream stream, final boolean scripting) { - this(source, 0, source.getLength(), stream, scripting); + this(source, 0, source.getLength(), stream, scripting, false); } /** - * Contructor + * Constructor * * @param source the source * @param start start position in source from which to start lexing * @param len length of source segment to lex * @param stream token stream to lex * @param scripting are we in scripting mode + * @param pauseOnFunctionBody if true, lexer will return from {@link #lexify()} when it encounters a + * function body. This is used with the feature where the parser is skipping nested function bodies to + * avoid reading ahead unnecessarily when we skip the function bodies. */ - public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting) { + public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean pauseOnFunctionBody) { super(source.getContent(), 1, start, len); this.source = source; this.stream = stream; @@ -203,6 +210,8 @@ this.nested = false; this.pendingLine = 1; this.last = EOL; + + this.pauseOnFunctionBody = pauseOnFunctionBody; } private Lexer(final Lexer lexer, final State state) { @@ -216,6 +225,7 @@ pendingLine = state.pendingLine; linePosition = state.linePosition; last = EOL; + pauseOnFunctionBody = false; } static class State extends Scanner.State { @@ -810,6 +820,9 @@ final int length = scanIdentifier(); // Check to see if it is a keyword. final TokenType type = TokenLookup.lookupKeyword(content, start, length); + if (type == FUNCTION && pauseOnFunctionBody) { + pauseOnNextLeftBrace = true; + } // Add keyword or identifier token. add(type, start); } @@ -1597,6 +1610,9 @@ // We break to let the parser decide what it is. if (canStartLiteral(type)) { break; + } else if (type == LBRACE && pauseOnNextLeftBrace) { + pauseOnNextLeftBrace = false; + break; } } else if (Character.isJavaIdentifierStart(ch0) || ch0 == '\\' && ch1 == 'u') { // Scan and add identifier or keyword.
--- a/src/jdk/nashorn/internal/parser/Parser.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/parser/Parser.java Tue Sep 16 13:59:37 2014 -0700 @@ -45,6 +45,7 @@ import static jdk.nashorn.internal.parser.TokenType.IF; import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.LBRACE; +import static jdk.nashorn.internal.parser.TokenType.LET; import static jdk.nashorn.internal.parser.TokenType.LPAREN; import static jdk.nashorn.internal.parser.TokenType.RBRACE; import static jdk.nashorn.internal.parser.TokenType.RBRACKET; @@ -147,7 +148,7 @@ /** to receive line information from Lexer when scanning multine literals. */ protected final Lexer.LineInfoReceiver lineInfoReceiver; - private int nextFunctionId; + private RecompilableScriptFunctionData reparsedFunction; /** * Constructor @@ -170,7 +171,7 @@ * @param log debug logger if one is needed */ public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final DebugLogger log) { - this(env, source, errors, strict, FunctionNode.FIRST_FUNCTION_ID, 0, log); + this(env, source, errors, strict, 0, log); } /** @@ -180,15 +181,13 @@ * @param source source to parse * @param errors error manager * @param strict parser created with strict mode enabled. - * @param nextFunctionId starting value for assigning new unique ids to function nodes * @param lineOffset line offset to start counting lines from * @param log debug logger if one is needed */ - public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int nextFunctionId, final int lineOffset, final DebugLogger log) { + public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) { super(source, errors, strict, lineOffset); this.env = env; this.namespace = new Namespace(env.getNamespace()); - this.nextFunctionId = nextFunctionId; this.scripting = env._scripting; if (this.scripting) { this.lineInfoReceiver = new Lexer.LineInfoReceiver() { @@ -227,6 +226,16 @@ } /** + * Sets the {@link RecompilableScriptFunctionData} representing the function being reparsed (when this + * parser instance is used to reparse a previously parsed function, as part of its on-demand compilation). + * This will trigger various special behaviors, such as skipping nested function bodies. + * @param reparsedFunction the function being reparsed. + */ + public void setReparsedFunction(final RecompilableScriptFunctionData reparsedFunction) { + this.reparsedFunction = reparsedFunction; + } + + /** * Execute parse and return the resulting function node. * Errors will be thrown and the error manager will contain information * if parsing should fail @@ -263,7 +272,7 @@ try { stream = new TokenStream(); - lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions); + lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, reparsedFunction != null); lexer.line = lexer.pendingLine = lineOffset + 1; line = lineOffset; @@ -471,7 +480,6 @@ final FunctionNode functionNode = new FunctionNode( source, - nextFunctionId++, functionLine, token, Token.descPosition(token), @@ -579,6 +587,10 @@ } } + private boolean useBlockScope() { + return env._es6; + } + private static boolean isArguments(final String name) { return ARGUMENTS_NAME.equals(name); } @@ -694,9 +706,20 @@ FunctionNode.Kind.SCRIPT, functionLine); + // If ES6 block scope is enabled add a per-script block for top-level LET and CONST declarations. + final int startLine = start; + Block outer = useBlockScope() ? newBlock() : null; functionDeclarations = new ArrayList<>(); - sourceElements(allowPropertyFunction); - addFunctionDeclarations(script); + + try { + sourceElements(allowPropertyFunction); + addFunctionDeclarations(script); + } finally { + if (outer != null) { + outer = restoreBlock(outer); + appendStatement(new BlockStatement(startLine, outer)); + } + } functionDeclarations = null; expect(EOF); @@ -868,7 +891,7 @@ block(); break; case VAR: - variableStatement(true); + variableStatement(type, true); break; case SEMICOLON: emptyStatement(); @@ -918,8 +941,12 @@ expect(SEMICOLON); break; default: + if (useBlockScope() && (type == LET || type == CONST)) { + variableStatement(type, true); + break; + } if (env._const_as_var && type == CONST) { - variableStatement(true); + variableStatement(TokenType.VAR, true); break; } @@ -1035,11 +1062,17 @@ * Parse a VAR statement. * @param isStatement True if a statement (not used in a FOR.) */ - private List<VarNode> variableStatement(final boolean isStatement) { + private List<VarNode> variableStatement(final TokenType varType, final boolean isStatement) { // VAR tested in caller. next(); final List<VarNode> vars = new ArrayList<>(); + int varFlags = VarNode.IS_STATEMENT; + if (varType == LET) { + varFlags |= VarNode.IS_LET; + } else if (varType == CONST) { + varFlags |= VarNode.IS_CONST; + } while (true) { // Get starting token. @@ -1063,10 +1096,12 @@ } finally { defaultNames.pop(); } + } else if (varType == CONST) { + throw error(AbstractParser.message("missing.const.assignment", name.getName())); } // Allocate var node. - final VarNode var = new VarNode(varLine, varToken, finish, name, init); + final VarNode var = new VarNode(varLine, varToken, finish, name.setIsDeclaredHere(), init, varFlags); vars.add(var); appendStatement(var); @@ -1180,9 +1215,12 @@ * Parse a FOR statement. */ private void forStatement() { + // When ES6 for-let is enabled we create a container block to capture the LET. + final int startLine = start; + Block outer = useBlockScope() ? newBlock() : null; + // Create FOR node, capturing FOR token. ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR); - lc.push(forNode); try { @@ -1203,14 +1241,19 @@ switch (type) { case VAR: // Var statements captured in for outer block. - vars = variableStatement(false); + vars = variableStatement(type, false); break; case SEMICOLON: break; default: + if (useBlockScope() && (type == LET || type == CONST)) { + // LET/CONST captured in container block created above. + vars = variableStatement(type, false); + break; + } if (env._const_as_var && type == CONST) { // Var statements captured in for outer block. - vars = variableStatement(false); + vars = variableStatement(TokenType.VAR, false); break; } @@ -1290,8 +1333,13 @@ appendStatement(forNode); } finally { lc.pop(forNode); + if (outer != null) { + outer.setFinish(forNode.getFinish()); + outer = restoreBlock(outer); + appendStatement(new BlockStatement(startLine, outer)); + } } - } + } /** * ... IterationStatement : @@ -1722,7 +1770,7 @@ } } - /** + /** * ThrowStatement : * throw Expression ; // [no LineTerminator here] * @@ -2609,7 +2657,7 @@ FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine); if (isStatement) { - if (topLevel) { + if (topLevel || useBlockScope()) { functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED); } else if (isStrictMode) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken); @@ -2661,9 +2709,16 @@ } if (isStatement) { - final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, VarNode.IS_STATEMENT); + int varFlags = VarNode.IS_STATEMENT; + if (!topLevel && useBlockScope()) { + // mark ES6 block functions as lexically scoped + varFlags |= VarNode.IS_LET; + } + final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, varFlags); if (topLevel) { functionDeclarations.add(varNode); + } else if (useBlockScope()) { + prependStatement(varNode); // Hoist to beginning of current block } else { appendStatement(varNode); } @@ -2780,10 +2835,14 @@ FunctionNode functionNode = null; long lastToken = 0L; + final boolean parseBody; + Object endParserState = null; try { // Create a new function block. functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine); - + assert functionNode != null; + final int functionId = functionNode.getId(); + parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); // Nashorn extension: expression closures if (!env._no_syntax_extensions && type != LBRACE) { /* @@ -2799,34 +2858,143 @@ assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); // EOL uses length field to store the line number final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken)); - final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); - appendStatement(returnNode); + // Only create the return node if we aren't skipping nested functions. Note that we aren't + // skipping parsing of these extended functions; they're considered to be small anyway. Also, + // they don't end with a single well known token, so it'd be very hard to get correctly (see + // the note below for reasoning on skipping happening before instead of after RBRACE for + // details). + if (parseBody) { + final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); + appendStatement(returnNode); + } functionNode.setFinish(lastFinish); - } else { - expect(LBRACE); - - // Gather the function elements. - final List<Statement> prevFunctionDecls = functionDeclarations; - functionDeclarations = new ArrayList<>(); - try { - sourceElements(false); - addFunctionDeclarations(functionNode); - } finally { - functionDeclarations = prevFunctionDecls; + expectDontAdvance(LBRACE); + if (parseBody || !skipFunctionBody(functionNode)) { + next(); + // Gather the function elements. + final List<Statement> prevFunctionDecls = functionDeclarations; + functionDeclarations = new ArrayList<>(); + try { + sourceElements(false); + addFunctionDeclarations(functionNode); + } finally { + functionDeclarations = prevFunctionDecls; + } + + lastToken = token; + if (parseBody) { + // Since the lexer can read ahead and lexify some number of tokens in advance and have + // them buffered in the TokenStream, we need to produce a lexer state as it was just + // before it lexified RBRACE, and not whatever is its current (quite possibly well read + // ahead) state. + endParserState = new ParserState(Token.descPosition(token), line, linePosition); + + // NOTE: you might wonder why do we capture/restore parser state before RBRACE instead of + // after RBRACE; after all, we could skip the below "expect(RBRACE);" if we captured the + // state after it. The reason is that RBRACE is a well-known token that we can expect and + // will never involve us getting into a weird lexer state, and as such is a great reparse + // point. Typical example of a weird lexer state after RBRACE would be: + // function this_is_skipped() { ... } "use strict"; + // because lexer is doing weird off-by-one maneuvers around string literal quotes. Instead + // of compensating for the possibility of a string literal (or similar) after RBRACE, + // we'll rather just restart parsing from this well-known, friendly token instead. + } } - - lastToken = token; expect(RBRACE); functionNode.setFinish(finish); } } finally { functionNode = restoreFunctionNode(functionNode, lastToken); } + + // NOTE: we can only do alterations to the function node after restoreFunctionNode. + + if (parseBody) { + functionNode = functionNode.setEndParserState(lc, endParserState); + } else if (functionNode.getBody().getStatementCount() > 0){ + // This is to ensure the body is empty when !parseBody but we couldn't skip parsing it (see + // skipFunctionBody() for possible reasons). While it is not strictly necessary for correctness to + // enforce empty bodies in nested functions that were supposed to be skipped, we do assert it as + // an invariant in few places in the compiler pipeline, so for consistency's sake we'll throw away + // nested bodies early if we were supposed to skip 'em. + functionNode = functionNode.setBody(null, functionNode.getBody().setStatements(null, + Collections.<Statement>emptyList())); + } + + if (reparsedFunction != null) { + // We restore the flags stored in the function's ScriptFunctionData that we got when we first + // eagerly parsed the code. We're doing it because some flags would be set based on the + // content of the function, or even content of its nested functions, most of which are normally + // skipped during an on-demand compilation. + final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId()); + if (data != null) { + // Data can be null if when we originally parsed the file, we removed the function declaration + // as it was dead code. + functionNode = functionNode.setFlags(lc, data.getFunctionFlags()); + // This compensates for missing markEval() in case the function contains an inner function + // that contains eval(), that now we didn't discover since we skipped the inner function. + if (functionNode.hasNestedEval()) { + assert functionNode.hasScopeBlock(); + functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(null)); + } + } + } printAST(functionNode); return functionNode; } + private boolean skipFunctionBody(final FunctionNode functionNode) { + if (reparsedFunction == null) { + // Not reparsing, so don't skip any function body. + return false; + } + // Skip to the RBRACE of this function, and continue parsing from there. + final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId()); + if (data == null) { + // Nested function is not known to the reparsed function. This can happen if the FunctionNode was + // in dead code that was removed. Both FoldConstants and Lower prune dead code. In that case, the + // FunctionNode was dropped before a RecompilableScriptFunctionData could've been created for it. + return false; + } + final ParserState parserState = (ParserState)data.getEndParserState(); + assert parserState != null; + + stream.reset(); + lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions); + line = parserState.line; + linePosition = parserState.linePosition; + // Doesn't really matter, but it's safe to treat it as if there were a semicolon before + // the RBRACE. + type = SEMICOLON; + k = -1; + next(); + + return true; + } + + /** + * Encapsulates part of the state of the parser, enough to reconstruct the state of both parser and lexer + * for resuming parsing after skipping a function body. + */ + private static class ParserState { + private final int position; + private final int line; + private final int linePosition; + + ParserState(final int position, final int line, final int linePosition) { + this.position = position; + this.line = line; + this.linePosition = linePosition; + } + + Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting) { + final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, true); + newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON)); + return newLexer; + } + } + private void printAST(final FunctionNode functionNode) { if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { env.getErr().println(new ASTWriter(functionNode)); @@ -2838,7 +3006,6 @@ } private void addFunctionDeclarations(final FunctionNode functionNode) { - assert lc.peek() == lc.getFunctionBody(functionNode); VarNode lastDecl = null; for (int i = functionDeclarations.size() - 1; i >= 0; i--) { Statement decl = functionDeclarations.get(i); @@ -3200,6 +3367,9 @@ } else { lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL); } + // NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip + // parsing a nested function. functionBody() contains code to compensate for the lack of invoking + // this method when the parser skips a nested function. lc.setBlockNeedsScope(lc.getFunctionBody(fn)); } }
--- a/src/jdk/nashorn/internal/parser/TokenStream.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/parser/TokenStream.java Tue Sep 16 13:59:37 2014 -0700 @@ -209,4 +209,8 @@ in = count; buffer = newBuffer; } + + void reset() { + in = out = count = base = 0; + } }
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java Tue Sep 16 13:59:37 2014 -0700 @@ -549,6 +549,8 @@ type == Object.class : "invalid getter type " + type + " for " + getKey(); + checkUndeclared(); + //all this does is add a return value filter for object fields only final MethodHandle[] getterCache = GETTER_CACHE; final MethodHandle cachedGetter = getterCache[i]; @@ -579,6 +581,8 @@ return getOptimisticPrimitiveGetter(type, programPoint); } + checkUndeclared(); + return debug( createGetter( getCurrentType(), @@ -608,6 +612,13 @@ return newMap; } + private void checkUndeclared() { + if ((getFlags() & NEEDS_DECLARATION) != 0) { + // a lexically defined variable that hasn't seen its declaration - throw ReferenceError + throw ECMAErrors.referenceError("not.defined", getKey()); + } + } + // the final three arguments are for debug printout purposes only @SuppressWarnings("unused") private static Object replaceMap(final Object sobj, final PropertyMap newMap) { @@ -635,13 +646,14 @@ @Override public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { - final int i = getAccessorTypeIndex(type); - final int ci = isUndefined() ? -1 : getAccessorTypeIndex(getCurrentType()); - final Class<?> forType = isUndefined() ? type : getCurrentType(); + checkUndeclared(); + + final int typeIndex = getAccessorTypeIndex(type); + final int currentTypeIndex = getAccessorTypeIndex(getCurrentType()); //if we are asking for an object setter, but are still a primitive type, we might try to box it MethodHandle mh; - if (needsInvalidator(i, ci)) { + if (needsInvalidator(typeIndex, currentTypeIndex)) { final Property newProperty = getWiderProperty(type); final PropertyMap newMap = getWiderMap(currentMap, newProperty); @@ -652,6 +664,7 @@ mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); } } else { + final Class<?> forType = isUndefined() ? type : getCurrentType(); mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); } @@ -692,11 +705,12 @@ if (OBJECT_FIELDS_ONLY) { return false; } - return getCurrentType() != Object.class && (isConfigurable() || isWritable()); + // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. + return getCurrentType() == null || (getCurrentType() != Object.class && (isConfigurable() || isWritable())); } - private boolean needsInvalidator(final int ti, final int fti) { - return canChangeType() && ti > fti; + private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) { + return canChangeType() && typeIndex > currentTypeIndex; } @Override
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/AllocationStrategy.java Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 static jdk.nashorn.internal.lookup.Lookup.MH; + +import java.io.Serializable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import jdk.nashorn.internal.codegen.CompilerConstants; +import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor; + +/** + * Encapsulates the allocation strategy for a function when used as a constructor. Basically the same as + * {@link AllocatorDescriptor}, but with an additionally cached resolved method handle. There is also a + * canonical default allocation strategy for functions that don't assign any "this" properties (vast majority + * of all functions), therefore saving some storage space in {@link RecompilableScriptFunctionData} that would + * otherwise be lost to identical tuples of (map, className, handle) fields. + */ +final class AllocationStrategy implements Serializable { + private static final long serialVersionUID = 1L; + + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + + private static final AllocationStrategy DEFAULT_STRATEGY = new AllocationStrategy(new AllocatorDescriptor(0)); + + /** Allocator map from allocator descriptor */ + private final PropertyMap allocatorMap; + + /** Name of class where allocator function resides */ + private final String allocatorClassName; + + /** lazily generated allocator */ + private transient MethodHandle allocator; + + private AllocationStrategy(final AllocatorDescriptor desc) { + this.allocatorMap = desc.getAllocatorMap(); + // These classes get loaded, so an interned variant of their name is most likely around anyway. + this.allocatorClassName = desc.getAllocatorClassName().intern(); + } + + private boolean matches(final AllocatorDescriptor desc) { + return desc.getAllocatorMap().size() == allocatorMap.size() && + desc.getAllocatorClassName().equals(allocatorClassName); + } + + static AllocationStrategy get(final AllocatorDescriptor desc) { + return DEFAULT_STRATEGY.matches(desc) ? DEFAULT_STRATEGY : new AllocationStrategy(desc); + } + + PropertyMap getAllocatorMap() { + return allocatorMap; + } + + ScriptObject allocate(final PropertyMap map) { + try { + if (allocator == null) { + allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), + CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class)); + } + return (ScriptObject)allocator.invokeExact(map); + } catch (final RuntimeException | Error e) { + throw e; + } catch (final Throwable t) { + throw new RuntimeException(t); + } + } + + private Object readResolve() { + if(allocatorMap.size() == DEFAULT_STRATEGY.allocatorMap.size() && + allocatorClassName.equals(DEFAULT_STRATEGY.allocatorClassName)) { + return DEFAULT_STRATEGY; + } + return this; + } + + @Override + public String toString() { + return "AllocationStrategy[allocatorClassName=" + allocatorClassName + ", allocatorMap.size=" + + allocatorMap.size() + "]"; + } +}
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java Tue Sep 16 13:59:37 2014 -0700 @@ -80,12 +80,6 @@ public long getUniqueScriptId(); /** - * Get next unique eval id - * @return unique eval id - */ - public long getUniqueEvalId(); - - /** * Store a compiled script for later reuse * @param source the script source * @param mainClassName the main class name
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java Tue Sep 16 13:59:37 2014 -0700 @@ -27,7 +27,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; - import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -39,6 +38,7 @@ import java.util.TreeMap; import java.util.function.Supplier; import java.util.logging.Level; + import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
--- a/src/jdk/nashorn/internal/runtime/Context.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/Context.java Tue Sep 16 13:59:37 2014 -0700 @@ -196,16 +196,11 @@ } @Override - public long getUniqueEvalId() { - return context.getUniqueEvalId(); - } - - @Override - public void storeScript(final String classInfoFile, final Source source, final String mainClassName, + public void storeScript(final String cacheKey, final Source source, final String mainClassName, final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, final Object[] constants, final int compilationId) { if (context.codeStore != null) { - context.codeStore.storeScript(classInfoFile, source, mainClassName, classBytes, initializers, constants, compilationId); + context.codeStore.storeScript(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId); } } @@ -334,9 +329,6 @@ /** Unique id for script. Used only when --loader-per-compile=false */ private final AtomicLong uniqueScriptId; - /** Unique id for 'eval' */ - private final AtomicLong uniqueEvalId; - /** Optional class filter to use for Java classes. Can be null. */ private final ClassFilter classFilter; @@ -450,7 +442,6 @@ this.uniqueScriptId = new AtomicLong(); } this.errors = errors; - this.uniqueEvalId = new AtomicLong(); // if user passed -classpath option, make a class loader with that and set it as // thread context class loader so that script can access classes from that path. @@ -1132,7 +1123,7 @@ if (storedScript == null) { functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); - if (errors.hasErrors()) { + if (errMan.hasErrors()) { return null; } @@ -1162,9 +1153,13 @@ env, installer, source, + errMan, strict | functionNode.isStrict()); final FunctionNode compiledFunction = compiler.compile(functionNode, phases); + if (errMan.hasErrors()) { + return null; + } script = compiledFunction.getRootClass(); compiler.persistClassInfo(cacheKey, compiledFunction); } else { @@ -1186,10 +1181,6 @@ }, CREATE_LOADER_ACC_CTXT); } - private long getUniqueEvalId() { - return uniqueEvalId.getAndIncrement(); - } - private long getUniqueScriptId() { return uniqueScriptId.getAndIncrement(); }
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java Tue Sep 16 13:59:37 2014 -0700 @@ -29,7 +29,9 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import java.lang.invoke.MethodHandle; +import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.codegen.ObjectClassGenerator; +import jdk.nashorn.internal.objects.Global; /** * This class represents the result from a find property search. @@ -58,6 +60,18 @@ } /** + * Return a copy of this FindProperty with a different property. + * + * @param newProperty the new property + * @return the new FindProperty instance + */ + public FindProperty replaceProperty(final Property newProperty) { + assert this.property.getKey().equals(newProperty.getKey()); + assert this.property.getSlot() == newProperty.getSlot(); + return new FindProperty(self, prototype, newProperty); + } + + /** * Ask for a getter that returns the given type. The type has nothing to do with the * internal representation of the property. It may be an Object (boxing primitives) or * a primitive (primitive fields with -Dnashorn.fields.dual=true) @@ -67,25 +81,17 @@ * @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic * @return method handle for the getter */ - public MethodHandle getGetter(final Class<?> type, final int programPoint) { + public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) { final MethodHandle getter; if (isValid(programPoint)) { getter = property.getOptimisticGetter(type, programPoint); } else { getter = property.getGetter(type); } - return getGetterInner(getter); - } - - private MethodHandle getGetterInner(final MethodHandle getter) { if (property instanceof UserAccessorProperty) { - final UserAccessorProperty uc = (UserAccessorProperty)property; - final ScriptObject owner = getOwner(); - final ScriptObject container = (owner != null) ? owner : self; - return MH.insertArguments(getter, 0, uc.getAccessors(container)); + return insertAccessorsGetter((UserAccessorProperty) property, request, getter); } return getter; - } /** @@ -99,18 +105,31 @@ * * @return method handle for the getter */ - public MethodHandle getSetter(final Class<?> type, final boolean strict) { - final MethodHandle setter = property.getSetter(type, getOwner().getMap()); + public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) { + MethodHandle setter = property.getSetter(type, getOwner().getMap()); if (property instanceof UserAccessorProperty) { - final UserAccessorProperty uc = (UserAccessorProperty)property; - final ScriptObject owner = getOwner(); - final ScriptObject container = (owner != null) ? owner : self; - return MH.insertArguments(setter, 0, uc.getAccessors(container), strict ? property.getKey() : null); + setter = MH.insertArguments(setter, 1, strict ? property.getKey() : null); + return insertAccessorsGetter((UserAccessorProperty) property, request, setter); } return setter; } + // Fold an accessor getter into the method handle of a user accessor property. + private MethodHandle insertAccessorsGetter(final UserAccessorProperty uap, final LinkRequest request, final MethodHandle mh) { + MethodHandle superGetter = uap.getAccessorsGetter(); + if (isInherited()) { + superGetter = ScriptObject.addProtoFilter(superGetter, getProtoChainLength()); + } + if (request != null && !(request.getReceiver() instanceof ScriptObject)) { + final MethodHandle wrapFilter = Global.getPrimitiveWrapFilter(request.getReceiver()); + superGetter = MH.filterArguments(superGetter, 0, wrapFilter.asType(wrapFilter.type().changeReturnType(superGetter.type().parameterType(0)))); + } + superGetter = MH.asType(superGetter, superGetter.type().changeParameterType(0, Object.class)); + + return MH.foldArguments(mh, superGetter); + } + /** * Return the {@code ScriptObject} owning of the property: this means the prototype. * @return owner of property @@ -124,7 +143,7 @@ * @return appropriate receiver */ public ScriptObject getGetterReceiver() { - return property != null && property.hasGetterFunction(prototype) ? self : prototype; + return property != null && property instanceof UserAccessorProperty ? self : prototype; } /**
--- a/src/jdk/nashorn/internal/runtime/GlobalConstants.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/GlobalConstants.java Tue Sep 16 13:59:37 2014 -0700 @@ -28,6 +28,8 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; import java.lang.invoke.MethodHandle; @@ -370,22 +372,19 @@ * @param find property lookup * @param receiver receiver * @param desc callsite descriptor - * @param request link request - * @param operator operator * * @return resulting getter, or null if failed to create constant */ - synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc, final LinkRequest request, final String operator) { - if (GLOBAL_ONLY && !find.getOwner().isGlobal()) { + synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) { + // Also return null if property may have side effects + if ((GLOBAL_ONLY && !find.getOwner().isGlobal()) || find.getProperty() instanceof UserAccessorProperty) { return null; } - final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? - NashornCallSiteDescriptor.getProgramPoint(desc) : - UnwarrantedOptimismException.INVALID_PROGRAM_POINT; - final boolean isOptimistic = programPoint != UnwarrantedOptimismException.INVALID_PROGRAM_POINT; - final Class<?> retType = desc.getMethodType().returnType(); - final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); + final boolean isOptimistic = NashornCallSiteDescriptor.isOptimistic(desc); + final int programPoint = isOptimistic ? getProgramPoint(desc) : INVALID_PROGRAM_POINT; + final Class<?> retType = desc.getMethodType().returnType(); + final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); final Access acc = getOrCreateSwitchPoint(name);
--- a/src/jdk/nashorn/internal/runtime/JSType.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Tue Sep 16 13:59:37 2014 -0700 @@ -938,11 +938,8 @@ * @return double */ public static int toInt32Optimistic(final Object obj, final int programPoint) { - if (obj != null) { - final Class<?> clz = obj.getClass(); - if (clz == Integer.class) { - return ((Integer)obj).intValue(); - } + if (obj != null && obj.getClass() == Integer.class) { + return ((Integer)obj).intValue(); } throw new UnwarrantedOptimismException(obj, programPoint); }
--- a/src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java Tue Sep 16 13:59:37 2014 -0700 @@ -191,19 +191,20 @@ } /** - * Returns the argument value as an int. If the argument is not a Number object that can be exactly represented as - * an int, throw an {@link UnwarrantedOptimismException}.This method is only public so that generated script code - * can use it. See {code CodeGenerator.ENSURE_INT}. + * Returns the argument value as an int. If the argument is not a wrapper for a primitive numeric type + * with a value that can be exactly represented as an int, throw an {@link UnwarrantedOptimismException}. + * This method is only public so that generated script code can use it. See {code CodeGenerator.ENSURE_INT}. * @param arg the original argument. * @param programPoint the program point used in the exception * @return the value of the argument as an int. - * @throws UnwarrantedOptimismException if the argument is not a Number that can be exactly represented as an int. + * @throws UnwarrantedOptimismException if the argument is not a wrapper for a primitive numeric type with + * a value that can be exactly represented as an int. */ public static int ensureInt(final Object arg, final int programPoint) { // NOTE: this doesn't delegate to ensureInt(double, int) as in that case if arg were a Long, it would throw a // (potentially imprecise) Double in the UnwarrantedOptimismException. This way, it will put the correct valued // Long into the exception. - if (arg instanceof Number) { + if (isPrimitiveNumberWrapper(arg)) { final double d = ((Number)arg).doubleValue(); if (JSType.isRepresentableAsInt(d) && !JSType.isNegativeZero(d)) { return (int)d; @@ -212,6 +213,15 @@ throw new UnwarrantedOptimismException(arg, programPoint); } + private static boolean isPrimitiveNumberWrapper(final Object obj) { + if (obj == null) { + return false; + } + final Class<?> c = obj.getClass(); + return c == Integer.class || c == Double.class || c == Long.class || + c == Float.class || c == Short.class || c == Byte.class; + } + @SuppressWarnings("unused") private static int ensureInt(final boolean arg, final int programPoint) { throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT); @@ -236,35 +246,40 @@ } /** - * Returns the argument value as a long. If the argument is not a Number object that can be exactly represented as - * a long, throw an {@link UnwarrantedOptimismException}.This method is only public so that generated script code - * can use it. See {code CodeGenerator.ENSURE_LONG}. + * Returns the argument value as a long. If the argument is not a wrapper for a primitive numeric type + * with a value that can be exactly represented as a long, throw an {@link UnwarrantedOptimismException}. + * This method is only public so that generated script code can use it. See {code CodeGenerator.ENSURE_LONG}. * @param arg the original argument. * @param programPoint the program point used in the exception * @return the value of the argument as a long. - * @throws UnwarrantedOptimismException if the argument is not a Number that can be exactly represented as a long. + * @throws UnwarrantedOptimismException if the argument is not a wrapper for a primitive numeric type with + * a value that can be exactly represented as a long */ public static long ensureLong(final Object arg, final int programPoint) { - if (arg instanceof Long) { - // Must check for Long separately, as Long.doubleValue() isn't precise. - return ((Long)arg).longValue(); - } else if (arg instanceof Number) { - return ensureLong(((Number)arg).doubleValue(), programPoint); + if (arg != null) { + final Class<?> c = arg.getClass(); + if (c == Long.class) { + // Must check for Long separately, as Long.doubleValue() isn't precise. + return ((Long)arg).longValue(); + } else if (c == Integer.class || c == Double.class || c == Float.class || c == Short.class || + c == Byte.class) { + return ensureLong(((Number)arg).doubleValue(), programPoint); + } } throw new UnwarrantedOptimismException(arg, programPoint); } /** - * Returns the argument value as a double. If the argument is not a Number object, throw an - * {@link UnwarrantedOptimismException}.This method is only public so that generated script code can use it. See - * {code CodeGenerator.ENSURE_NUMBER}. + * Returns the argument value as a double. If the argument is not a a wrapper for a primitive numeric type + * throw an {@link UnwarrantedOptimismException}.This method is only public so that generated script code + * can use it. See {code CodeGenerator.ENSURE_NUMBER}. * @param arg the original argument. * @param programPoint the program point used in the exception * @return the value of the argument as a double. - * @throws UnwarrantedOptimismException if the argument is not a Number that can be exactly represented as a long. + * @throws UnwarrantedOptimismException if the argument is not a wrapper for a primitive numeric type. */ public static double ensureNumber(final Object arg, final int programPoint) { - if (arg instanceof Number) { // arg == null -> false + if (isPrimitiveNumberWrapper(arg)) { return ((Number)arg).doubleValue(); } throw new UnwarrantedOptimismException(arg, programPoint);
--- a/src/jdk/nashorn/internal/runtime/Property.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/Property.java Tue Sep 16 13:59:37 2014 -0700 @@ -82,11 +82,14 @@ * is narrower than object, e.g. Math.PI which is declared * as a double */ - public static final int IS_NASGEN_PRIMITIVE = 1 << 6; + public static final int IS_NASGEN_PRIMITIVE = 1 << 6; /** Is this property bound to a receiver? This means get/set operations will be delegated to * a statically defined object instead of the object passed as callsite parameter. */ - public static final int IS_BOUND = 1 << 8; + public static final int IS_BOUND = 1 << 7; + + /** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */ + public static final int NEEDS_DECLARATION = 1 << 8; /** Property key. */ private final String key; @@ -287,6 +290,15 @@ } /** + * Is this a LET or CONST property that needs to see its declaration before being usable? + * + * @return true if this is a block-scoped variable + */ + public boolean needsDeclaration() { + return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION; + } + + /** * Add more property flags to the property. Properties are immutable here, * so any property change that results in a larger flag set results in the * property being cloned. Use only the return value
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Tue Sep 16 13:59:37 2014 -0700 @@ -42,6 +42,7 @@ import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.FunctionSignature; +import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor; import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; import jdk.nashorn.internal.codegen.TypeMap; import jdk.nashorn.internal.codegen.types.Type; @@ -55,7 +56,6 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; - /** * 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. @@ -81,26 +81,29 @@ /** Token of this function within the source. */ private final long token; - /** Allocator map from makeMap() */ - private final PropertyMap allocatorMap; + /** + * Represents the allocation strategy (property map, script object class, and method handle) for when + * this function is used as a constructor. Note that majority of functions (those not setting any this.* + * properties) will share a single canonical "default strategy" instance. + */ + private final AllocationStrategy allocationStrategy; + + /** + * Opaque object representing parser state at the end of the function. Used when reparsing outer function + * to help with skipping parsing inner functions. + */ + private final Object endParserState; /** Code installer used for all further recompilation/specialization of this ScriptFunction */ private transient CodeInstaller<ScriptEnvironment> installer; - /** Name of class where allocator function resides */ - private final String allocatorClassName; - - /** lazily generated allocator */ - private transient MethodHandle allocator; - private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions; /** Id to parent function if one exists */ private RecompilableScriptFunctionData parent; - private final boolean isDeclared; - private final boolean isAnonymous; - private final boolean needsCallee; + /** Copy of the {@link FunctionNode} flags. */ + private final int functionFlags; private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); @@ -119,8 +122,7 @@ * * @param functionNode functionNode that represents this function code * @param installer installer for code regeneration versions of this function - * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor - * @param allocatorMap allocator map to seed instances with, when constructing + * @param allocationDescriptor descriptor for the allocation behavior when this function is used as a constructor * @param nestedFunctions nested function map * @param externalScopeDepths external scope depths * @param internalSymbols internal symbols to method, defined in its scope @@ -128,30 +130,27 @@ public RecompilableScriptFunctionData( final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, - final String allocatorClassName, - final PropertyMap allocatorMap, + final AllocatorDescriptor allocationDescriptor, final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, final Map<String, Integer> externalScopeDepths, final Set<String> internalSymbols) { super(functionName(functionNode), Math.min(functionNode.getParameters().size(), MAX_ARITY), - getFlags(functionNode)); + getDataFlags(functionNode)); this.functionName = functionNode.getName(); this.lineNumber = functionNode.getLineNumber(); - this.isDeclared = functionNode.isDeclared(); - this.needsCallee = functionNode.needsCallee(); - this.isAnonymous = functionNode.isAnonymous(); + this.functionFlags = functionNode.getFlags() | (functionNode.needsCallee() ? FunctionNode.NEEDS_CALLEE : 0); this.functionNodeId = functionNode.getId(); this.source = functionNode.getSource(); + this.endParserState = functionNode.getEndParserState(); this.token = tokenFor(functionNode); this.installer = installer; - this.allocatorClassName = allocatorClassName; - this.allocatorMap = allocatorMap; - this.nestedFunctions = nestedFunctions; - this.externalScopeDepths = externalScopeDepths; - this.internalSymbols = new HashSet<>(internalSymbols); + this.allocationStrategy = AllocationStrategy.get(allocationDescriptor); + this.nestedFunctions = smallMap(nestedFunctions); + this.externalScopeDepths = smallMap(externalScopeDepths); + this.internalSymbols = smallSet(new HashSet<>(internalSymbols)); for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) { assert nfn.getParent() == null; @@ -161,6 +160,27 @@ createLogger(); } + private static <K, V> Map<K, V> smallMap(final Map<K, V> map) { + if (map == null || map.isEmpty()) { + return Collections.emptyMap(); + } else if (map.size() == 1) { + final Map.Entry<K, V> entry = map.entrySet().iterator().next(); + return Collections.singletonMap(entry.getKey(), entry.getValue()); + } else { + return map; + } + } + + private static <T> Set<T> smallSet(final Set<T> set) { + if (set == null || set.isEmpty()) { + return Collections.emptySet(); + } else if (set.size() == 1) { + return Collections.singleton(set.iterator().next()); + } else { + return set; + } + } + @Override public DebugLogger getLogger() { return log; @@ -190,11 +210,7 @@ * @return the external symbol table with proto depths */ public int getExternalSymbolDepth(final String symbolName) { - final Map<String, Integer> map = externalScopeDepths; - if (map == null) { - return -1; - } - final Integer depth = map.get(symbolName); + final Integer depth = externalScopeDepths.get(symbolName); if (depth == null) { return -1; } @@ -202,6 +218,23 @@ } /** + * Returns the names of all external symbols this function uses. + * @return the names of all external symbols this function uses. + */ + public Set<String> getExternalSymbolNames() { + return Collections.unmodifiableSet(externalScopeDepths.keySet()); + } + + /** + * Returns the opaque object representing the parser state at the end of this function's body, used to + * skip parsing this function when reparsing its containing outer function. + * @return the object representing the end parser state + */ + public Object getEndParserState() { + return endParserState; + } + + /** * Get the parent of this RecompilableScriptFunctionData. If we are * a nested function, we have a parent. Note that "null" return value * can also mean that we have a parent but it is unknown, so this can @@ -269,7 +302,7 @@ @Override public boolean inDynamicContext() { - return (flags & IN_DYNAMIC_CONTEXT) != 0; + return getFunctionFlag(FunctionNode.IN_DYNAMIC_CONTEXT); } private static String functionName(final FunctionNode fn) { @@ -293,7 +326,7 @@ return Token.toDesc(TokenType.FUNCTION, position, length); } - private static int getFlags(final FunctionNode functionNode) { + private static int getDataFlags(final FunctionNode functionNode) { int flags = IS_CONSTRUCTOR; if (functionNode.isStrict()) { flags |= IS_STRICT; @@ -307,37 +340,20 @@ if (functionNode.isVarArg()) { flags |= IS_VARIABLE_ARITY; } - if (functionNode.inDynamicContext()) { - flags |= IN_DYNAMIC_CONTEXT; - } return flags; } @Override PropertyMap getAllocatorMap() { - return allocatorMap; + return allocationStrategy.getAllocatorMap(); } @Override ScriptObject allocate(final PropertyMap map) { - try { - ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try - return allocator == null ? null : (ScriptObject)allocator.invokeExact(map); - } catch (final RuntimeException | Error e) { - throw e; - } catch (final Throwable t) { - throw new RuntimeException(t); - } - } - - private void ensureHasAllocator() throws ClassNotFoundException { - if (allocator == null && allocatorClassName != null) { - this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class)); - } + return allocationStrategy.allocate(map); } FunctionNode reparse() { - final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID; // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node final int descPosition = Token.descPosition(token); final Context context = Context.getContextTrusted(); @@ -346,18 +362,27 @@ source, new Context.ThrowErrorManager(), isStrict(), - functionNodeId - (isProgram ? 0 : 1), lineNumber - 1, context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive - if (isAnonymous) { + if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) { parser.setFunctionName(functionName); } + parser.setReparsedFunction(this); - final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true); - // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a - // single function, extract it from the program. - return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName); + final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, + Token.descLength(token), true); + // Parser generates a program AST even if we're recompiling a single function, so when we are only + // recompiling a single function, extract it from the program. + return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); + } + + private boolean getFunctionFlag(final int flag) { + return (functionFlags & flag) != 0; + } + + private boolean isProgram() { + return getFunctionFlag(FunctionNode.IS_PROGRAM); } TypeMap typeMap(final MethodType fnCallSiteType) { @@ -394,6 +419,7 @@ context.getEnv(), installer, functionNode.getSource(), // source + context.getErrorManager(), isStrict() | functionNode.isStrict(), // is strict true, // is on demand this, // compiledFunction, i.e. this RecompilableScriptFunctionData @@ -545,7 +571,7 @@ assert fns.size() == 1 : "got back more than one method in recompilation"; final FunctionNode f = fns.iterator().next(); assert f.getId() == functionNodeId; - if (!isDeclared && f.isDeclared()) { + if (!getFunctionFlag(FunctionNode.IS_DECLARED) && f.isDeclared()) { return f.clearFlag(null, FunctionNode.IS_DECLARED); } return f; @@ -668,7 +694,15 @@ @Override public boolean needsCallee() { - return needsCallee; + return getFunctionFlag(FunctionNode.NEEDS_CALLEE); + } + + /** + * Returns the {@link FunctionNode} flags associated with this function data. + * @return the {@link FunctionNode} flags associated with this function data. + */ + public int getFunctionFlags() { + return functionFlags; } @Override @@ -720,21 +754,6 @@ } /** - * Get the uppermost parent, the program, for this data - * @return program - */ - public RecompilableScriptFunctionData getProgram() { - RecompilableScriptFunctionData program = this; - while (true) { - final RecompilableScriptFunctionData p = program.getParent(); - if (p == null) { - return program; - } - program = p; - } - } - - /** * Check whether a certain name is a global symbol, i.e. only exists as defined * in outermost scope and not shadowed by being parameter or assignment in inner * scopes
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Tue Sep 16 13:59:37 2014 -0700 @@ -94,6 +94,9 @@ /** Use single Global instance per jsr223 engine instance. */ public final boolean _global_per_engine; + /** Enable experimental ECMAScript 6 features. */ + public final boolean _es6; + /** Argument passed to compile only if optimistic compilation should take place */ public static final String COMPILE_ONLY_OPTIMISTIC_ARG = "optimistic"; @@ -258,6 +261,15 @@ _version = options.getBoolean("version"); _verify_code = options.getBoolean("verify.code"); + final String language = options.getString("language"); + if (language == null || language.equals("es5")) { + _es6 = false; + } else if (language.equals("es6")) { + _es6 = true; + } else { + throw new RuntimeException("Unsupported language: " + language); + } + String dir = null; String func = null; final String pc = options.getString("print.code");
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Sep 16 13:59:37 2014 -0700 @@ -36,6 +36,7 @@ import java.lang.invoke.MethodType; import java.lang.invoke.SwitchPoint; import java.util.Collections; + import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -44,6 +45,9 @@ import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.objects.NativeFunction; +import jdk.nashorn.internal.runtime.ScriptFunctionData; +import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; @@ -621,6 +625,23 @@ appliedType = appliedType.changeParameterType(1, Object.class); } + /* + * dropArgs is a synthetic method handle that contains any args that we need to + * get rid of that come after the arguments array in the apply case. We adapt + * the callsite to ask for 3 args only and then dropArguments on the method handle + * to make it fit the extraneous args. + */ + MethodType dropArgs = MH.type(void.class); + if (isApply && !isFailedApplyToCall) { + final int pc = appliedType.parameterCount(); + for (int i = 3; i < pc; i++) { + dropArgs = dropArgs.appendParameterTypes(appliedType.parameterType(i)); + } + if (pc > 3) { + appliedType = appliedType.dropParameterTypes(3, pc); + } + } + if (isApply || isFailedApplyToCall) { if (passesArgs) { // R(this, args) => R(this, Object[]) @@ -702,6 +723,15 @@ } inv = MH.dropArguments(inv, 0, applyFnType); + /* + * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which + * is when we need to add arguments to the callsite to catch and ignore the synthetic + * extra args that someone has added to the command line. + */ + for (int i = 0; i < dropArgs.parameterCount(); i++) { + inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i)); + } + MethodHandle guard = appliedInvocation.getGuard(); // If the guard checks the value of "this" but we aren't passing thisArg, insert the default one if (!passesThis && guard.type().parameterCount() > 1) {
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Tue Sep 16 13:59:37 2014 -0700 @@ -90,8 +90,6 @@ public static final int USES_THIS = 1 << 4; /** Is this a variable arity function? */ public static final int IS_VARIABLE_ARITY = 1 << 5; - /** Is this declared in a dynamic context */ - public static final int IN_DYNAMIC_CONTEXT = 1 << 6; /** Flag for strict or built-in functions */ public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN;
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Sep 16 13:59:37 2014 -0700 @@ -158,6 +158,7 @@ static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class); static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); + static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class); private static final MethodHandle TRUNCATINGFILTER = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class); private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class); @@ -1049,7 +1050,7 @@ } private static int getIntValue(final FindProperty find, final int programPoint) { - final MethodHandle getter = find.getGetter(int.class, programPoint); + final MethodHandle getter = find.getGetter(int.class, programPoint, null); if (getter != null) { try { return (int)getter.invokeExact((Object)find.getGetterReceiver()); @@ -1064,7 +1065,7 @@ } private static long getLongValue(final FindProperty find, final int programPoint) { - final MethodHandle getter = find.getGetter(long.class, programPoint); + final MethodHandle getter = find.getGetter(long.class, programPoint, null); if (getter != null) { try { return (long)getter.invokeExact((Object)find.getGetterReceiver()); @@ -1079,7 +1080,7 @@ } private static double getDoubleValue(final FindProperty find, final int programPoint) { - final MethodHandle getter = find.getGetter(double.class, programPoint); + final MethodHandle getter = find.getGetter(double.class, programPoint, null); if (getter != null) { try { return (double)getter.invokeExact((Object)find.getGetterReceiver()); @@ -1970,7 +1971,7 @@ } } - final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc, request, operator); + final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc); if (cinv != null) { return cinv; } @@ -1982,7 +1983,7 @@ NashornCallSiteDescriptor.getProgramPoint(desc) : UnwarrantedOptimismException.INVALID_PROGRAM_POINT; - mh = find.getGetter(returnType, programPoint); + mh = find.getGetter(returnType, programPoint, request); // Get the appropriate guard for this callsite and property. final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck); final ScriptObject owner = find.getOwner(); @@ -1994,8 +1995,9 @@ mh = Lookup.emptyGetter(returnType); protoSwitchPoint = getProtoSwitchPoint(name, owner); } else if (!find.isSelf()) { - assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType; - if (!property.hasGetterFunction(owner)) { + assert mh.type().returnType().equals(returnType) : + "return type mismatch for getter " + mh.type().returnType() + " != " + returnType; + if (!(property instanceof UserAccessorProperty)) { // Add a filter that replaces the self object with the prototype owning the property. mh = addProtoFilter(mh, find.getProtoChainLength()); } @@ -2027,6 +2029,22 @@ return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); } + // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST + @SuppressWarnings("unused") + private void declareAndSet(final String key, final Object value) { + final PropertyMap map = getMap(); + final FindProperty find = findProperty(key, false); + assert find != null; + + final Property property = find.getProperty(); + assert property != null; + assert property.needsDeclaration(); + + final PropertyMap newMap = map.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION)); + setMap(newMap); + set(key, value, true); + } + /** * Find the appropriate GETINDEX method for an invoke dynamic call. * @@ -2140,7 +2158,7 @@ } if (find != null) { - if (!find.getProperty().isWritable()) { + if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) { // Existing, non-writable property return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); } @@ -2150,7 +2168,7 @@ } } - final GuardedInvocation inv = new SetMethodCreator(this, find, desc, explicitInstanceOfCheck).createGuardedInvocation(); + final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(); final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request); if (cinv != null) { @@ -2303,13 +2321,13 @@ find.isSelf()? getKnownFunctionPropertyGuardSelf( getMap(), - find.getGetter(Object.class, INVALID_PROGRAM_POINT), + find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), func) : //TODO this always does a scriptobject check getKnownFunctionPropertyGuardProto( getMap(), - find.getGetter(Object.class, INVALID_PROGRAM_POINT), + find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), find.getProtoChainLength(), func), getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Tue Sep 16 13:59:37 2014 -0700 @@ -108,6 +108,11 @@ public static final Call APPLY = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "apply", Object.class, ScriptFunction.class, Object.class, Object[].class); /** + * Throws a reference error for an undefined variable. + */ + public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class); + + /** * Converts a switch tag value to a simple integer. deflt value if it can't. * * @param tag Switch statement tag value. @@ -382,6 +387,15 @@ } /** + * Throws a reference error for an undefined variable. + * + * @param name the variable name + */ + public static void throwReferenceError(final String name) { + throw referenceError("not.defined", name); + } + + /** * Call a script function as a constructor with given args. * * @param target ScriptFunction object.
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Tue Sep 16 13:59:37 2014 -0700 @@ -33,6 +33,7 @@ import java.lang.invoke.SwitchPoint; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornGuards; @@ -48,7 +49,7 @@ private final FindProperty find; private final CallSiteDescriptor desc; private final Class<?> type; - private final boolean explicitInstanceOfCheck; + private final LinkRequest request; /** * Creates a new property setter method creator. @@ -56,14 +57,15 @@ * @param find a result of a {@link ScriptObject#findProperty(String, boolean)} on the object for the property we * want to create a setter for. Can be null if the property does not yet exist on the object. * @param desc the descriptor of the call site that triggered the property setter lookup + * @param request the link request */ - SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck) { - this.sobj = sobj; - this.map = sobj.getMap(); - this.find = find; - this.desc = desc; - this.type = desc.getMethodType().parameterType(1); - this.explicitInstanceOfCheck = explicitInstanceOfCheck; + SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final LinkRequest request) { + this.sobj = sobj; + this.map = sobj.getMap(); + this.find = find; + this.desc = desc; + this.type = desc.getMethodType().parameterType(1); + this.request = request; } @@ -111,6 +113,7 @@ // getGuard() and getException() either both return null, or neither does. The reason for that is that now // getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to // relink on ClassCastException. + final boolean explicitInstanceOfCheck = NashornGuards.explicitInstanceOfCheck(desc, request); return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); } @@ -140,13 +143,36 @@ private SetMethod createExistingPropertySetter() { final Property property = find.getProperty(); - final MethodHandle methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc)); + final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); + final MethodHandle methodHandle; + + if (NashornCallSiteDescriptor.isDeclaration(desc)) { + assert property.needsDeclaration(); + // This is a LET or CONST being declared. The property is already there but flagged as needing declaration. + // We create a new PropertyMap with the flag removed. The map is installed with a fast compare-and-set + // method if the pre-callsite map is stable (which should be the case for function scopes except for + // non-strict functions containing eval() with var). Otherwise we have to use a slow setter that creates + // a new PropertyMap on the fly. + final PropertyMap oldMap = getMap(); + final Property newProperty = property.removeFlags(Property.NEEDS_DECLARATION); + final PropertyMap newMap = oldMap.replaceProperty(property, newProperty); + final MethodHandle fastSetter = find.replaceProperty(newProperty).getSetter(type, isStrict, request); + final MethodHandle slowSetter = MH.insertArguments(ScriptObject.DECLARE_AND_SET, 1, getName()).asType(fastSetter.type()); + + // cas map used as guard, if true that means we can do the set fast + MethodHandle casMap = MH.insertArguments(ScriptObject.CAS_MAP, 1, oldMap, newMap); + casMap = MH.dropArguments(casMap, 1, type); + casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class)); + methodHandle = MH.guardWithTest(casMap, fastSetter, slowSetter); + } else { + methodHandle = find.getSetter(type, isStrict, request); + } assert methodHandle != null; assert property != null; final MethodHandle boundHandle; - if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) { + if (!(property instanceof UserAccessorProperty) && find.isInherited()) { boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength()); } else { boundHandle = methodHandle;
--- a/src/jdk/nashorn/internal/runtime/Timing.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/Timing.java Tue Sep 16 13:59:37 2014 -0700 @@ -33,6 +33,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; + +import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; @@ -189,7 +191,7 @@ maxKeyLength++; final StringBuilder sb = new StringBuilder(); - sb.append("Accumulated complation phase Timings:\n\n"); + sb.append("Accumulated compilation phase timings:\n\n"); for (final Map.Entry<String, Long> entry : timings.entrySet()) { int len; @@ -224,6 +226,9 @@ append((int)(knownTime * 100.0 / total)). append("%])"); + sb.append("\n\nEmitted compile units: "). + append(CompileUnit.getEmittedUnitCount()); + return sb.toString(); }
--- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Tue Sep 16 13:59:37 2014 -0700 @@ -34,8 +34,8 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.concurrent.Callable; -import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -48,7 +48,7 @@ private static final long serialVersionUID = -5928687246526840321L; - static class Accessors { + static final class Accessors { Object getter; Object setter; @@ -67,20 +67,20 @@ } } + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + /** Getter method handle */ - private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, - "userAccessorGetter", Object.class, Accessors.class, Object.class); + private final static MethodHandle INVOKE_GETTER_ACCESSOR = findOwnMH_S("invokeGetterAccessor", Object.class, Accessors.class, Object.class); /** Setter method handle */ - private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, - "userAccessorSetter", void.class, Accessors.class, String.class, Object.class, Object.class); + private final static MethodHandle INVOKE_SETTER_ACCESSOR = findOwnMH_S("invokeSetterAccessor", void.class, Accessors.class, String.class, Object.class, Object.class); /** Dynamic invoker for getter */ - private static final Object INVOKE_UA_GETTER = new Object(); + private static final Object GETTER_INVOKER_KEY = new Object(); private static MethodHandle getINVOKE_UA_GETTER() { - return Context.getGlobal().getDynamicInvoker(INVOKE_UA_GETTER, + return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY, new Callable<MethodHandle>() { @Override public MethodHandle call() { @@ -91,10 +91,10 @@ } /** Dynamic invoker for setter */ - private static Object INVOKE_UA_SETTER = new Object(); + private static Object SETTER_INVOKER_KEY = new Object(); private static MethodHandle getINVOKE_UA_SETTER() { - return Context.getGlobal().getDynamicInvoker(INVOKE_UA_SETTER, + return Context.getGlobal().getDynamicInvoker(SETTER_INVOKER_KEY, new Callable<MethodHandle>() { @Override public MethodHandle call() { @@ -190,7 +190,7 @@ @Override public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { - return userAccessorGetter(getAccessors((owner != null) ? owner : self), self); + return invokeGetterAccessor(getAccessors((owner != null) ? owner : self), self); } @Override @@ -210,13 +210,13 @@ @Override public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { - userAccessorSetter(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); + invokeSetterAccessor(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); } @Override public MethodHandle getGetter(final Class<?> type) { //this returns a getter on the format (Accessors, Object receiver) - return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type); + return Lookup.filterReturnType(INVOKE_GETTER_ACCESSOR, type); } @Override @@ -260,7 +260,7 @@ @Override public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { - return USER_ACCESSOR_SETTER.methodHandle(); + return INVOKE_SETTER_ACCESSOR; } @Override @@ -269,11 +269,21 @@ return (value instanceof ScriptFunction) ? (ScriptFunction)value : null; } + /** + * Get the getter for the {@code Accessors} object. + * This is the the super {@code Object} type getter with {@code Accessors} return type. + * + * @return The getter handle for the Accessors + */ + MethodHandle getAccessorsGetter() { + return super.getGetter(Object.class).asType(MethodType.methodType(Accessors.class, Object.class)); + } + // User defined getter and setter are always called by "dyn:call". Note that the user // getter/setter may be inherited. If so, proto is bound during lookup. In either // inherited or self case, slot is also bound during lookup. Actual ScriptFunction // to be called is retrieved everytime and applied. - static Object userAccessorGetter(final Accessors gs, final Object self) { + private static Object invokeGetterAccessor(final Accessors gs, final Object self) { final Object func = gs.getter; if (func instanceof ScriptFunction) { try { @@ -288,7 +298,7 @@ return UNDEFINED; } - static void userAccessorSetter(final Accessors gs, final String name, final Object self, final Object value) { + private static void invokeSetterAccessor(final Accessors gs, final String name, final Object self, final Object value) { final Object func = gs.setter; if (func instanceof ScriptFunction) { try { @@ -303,4 +313,8 @@ } } + private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { + return MH.findStatic(LOOKUP, UserAccessorProperty.class, name, MH.type(rtype, types)); + } + }
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Tue Sep 16 13:59:37 2014 -0700 @@ -628,18 +628,20 @@ Class<?> widest = Integer.class; for (final Object item : items) { - final Class<?> itemClass = item == null ? null : item.getClass(); + if (item == null) { + return Object.class; + } + final Class<?> itemClass = item.getClass(); if (itemClass == Long.class) { if (widest == Integer.class) { widest = Long.class; } - } else if (itemClass == Double.class) { + } else if (itemClass == Double.class || itemClass == Float.class) { if (widest == Integer.class || widest == Long.class) { widest = Double.class; } - } else if (!(item instanceof Number)) { - widest = Object.class; - break; + } else if (itemClass != Integer.class && itemClass != Short.class && itemClass != Byte.class) { + return Object.class; } }
--- a/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Tue Sep 16 13:59:37 2014 -0700 @@ -54,23 +54,25 @@ public static final int CALLSITE_OPTIMISTIC = 1 << 3; /** Is this really an apply that we try to call as a call? */ public static final int CALLSITE_APPLY_TO_CALL = 1 << 4; + /** Does this a callsite for a variable declaration? */ + public static final int CALLSITE_DECLARE = 1 << 5; /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit * code where call sites have this flag set. */ - public static final int CALLSITE_PROFILE = 1 << 5; + public static final int CALLSITE_PROFILE = 1 << 6; /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where * call sites have this flag set. */ - public static final int CALLSITE_TRACE = 1 << 6; + public static final int CALLSITE_TRACE = 1 << 7; /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ - public static final int CALLSITE_TRACE_MISSES = 1 << 7; + public static final int CALLSITE_TRACE_MISSES = 1 << 8; /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ - public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 8; + public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 9; /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites * have this flag set. */ - public static final int CALLSITE_TRACE_VALUES = 1 << 9; + public static final int CALLSITE_TRACE_VALUES = 1 << 10; //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious //right now given the program points @@ -82,10 +84,10 @@ * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its * trace/profile settings. */ - public static final int CALLSITE_PROGRAM_POINT_SHIFT = 10; + public static final int CALLSITE_PROGRAM_POINT_SHIFT = 11; /** - * Maximum program point value. 22 bits should be enough for anyone + * Maximum program point value. 21 bits should be enough for anyone */ public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1; @@ -123,6 +125,9 @@ assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope"; sb.append("scope "); } + if ((flags & CALLSITE_DECLARE) != 0) { + sb.append("declare "); + } } if ((flags & CALLSITE_APPLY_TO_CALL) != 0) { sb.append("apply2call "); @@ -329,6 +334,15 @@ } /** + * Does this callsite contain a declaration for its target? + * @param desc descriptor + * @return true if contains declaration + */ + public static boolean isDeclaration(final CallSiteDescriptor desc) { + return isFlag(desc, CALLSITE_DECLARE); + } + + /** * Get a program point from a descriptor (must be optimistic) * @param desc descriptor * @return program point
--- a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Tue Sep 16 13:59:37 2014 -0700 @@ -32,11 +32,10 @@ import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; -import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.internal.dynalink.support.Guards; -import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.UserAccessorProperty; /** * Implements lookup of methods to link for dynamic operations on JavaScript primitive values (booleans, strings, and @@ -86,15 +85,6 @@ final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, final MethodHandle protoFilter) { final CallSiteDescriptor desc = request.getCallSiteDescriptor(); - final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); - if ("setProp".equals(operator) || "setElem".equals(operator)) { - final MethodType type = desc.getMethodType(); - MethodHandle method = MH.asType(Lookup.EMPTY_SETTER, MH.type(void.class, Object.class, type.parameterType(1))); - if (type.parameterCount() == 3) { - method = MH.dropArguments(method, 2, type.parameterType(2)); - } - return new GuardedInvocation(method, guard); - } if(desc.getNameTokenCount() > 2) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); @@ -102,7 +92,7 @@ if(find == null) { // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. return null; - } else if (find.isInherited() && !find.getProperty().hasGetterFunction(find.getOwner())) { + } else if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) { // If property is found in the prototype object bind the method handle directly to // the proto filter instead of going through wrapper instantiation below. final ScriptObject proto = wrappedReceiver.getProto();
--- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties Tue Sep 16 13:59:37 2014 -0700 @@ -58,6 +58,7 @@ parser.error.regex.repeated.flag=Repeated RegExp flag: {0} parser.error.regex.syntax={0} parser.error.trailing.comma.in.json=Trailing comma is not allowed in JSON +parser.error.missing.const.assignment=Missing assignment to constant "{0}" # strict mode error messages parser.error.strict.no.with="with" statement cannot be used in strict mode @@ -162,6 +163,8 @@ syntax.error.invalid.json=Invalid JSON: {0} syntax.error.strict.cant.delete=cannot delete "{0}" in strict mode +syntax.error.redeclare.variable=Variable "{0}" has already been declared +syntax.error.assign.constant=Assignment to constant "{0}" io.error.cant.write=cannot write "{0}" config.error.no.dest=no destination directory supplied
--- a/src/jdk/nashorn/internal/runtime/resources/Options.properties Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties Tue Sep 16 13:59:37 2014 -0700 @@ -329,6 +329,14 @@ desc="Enable scripting features." \ } +nashorn.option.language = { \ + name="--language", \ + type=String, \ + params=[es5|es6], \ + default=es5, \ + desc="Specify ECMAScript language version." \ +} + nashorn.option.stdout = { \ name="--stdout", \ is_undocumented=true, \
--- a/src/jdk/nashorn/tools/Shell.java Thu Sep 11 15:34:13 2014 -0700 +++ b/src/jdk/nashorn/tools/Shell.java Tue Sep 16 13:59:37 2014 -0700 @@ -246,12 +246,21 @@ // For each file on the command line. for (final String fileName : files) { - final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, FunctionNode.FIRST_FUNCTION_ID, 0, context.getLogger(Parser.class)).parse(); + final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, 0, context.getLogger(Parser.class)).parse(); if (errors.getNumberOfErrors() != 0) { return COMPILATION_ERROR; } + new Compiler( + context, + env, + null, //null - pass no code installer - this is compile only + functionNode.getSource(), + context.getErrorManager(), + env._strict | functionNode.isStrict()). + compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL); + if (env._print_ast) { context.getErr().println(new ASTWriter(functionNode)); } @@ -260,14 +269,9 @@ context.getErr().println(new PrintVisitor(functionNode)); } - //null - pass no code installer - this is compile only - new Compiler( - context, - env, - null, - functionNode.getSource(), - env._strict | functionNode.isStrict()). - compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL); + if (errors.getNumberOfErrors() != 0) { + return COMPILATION_ERROR; + } } } finally { env.getOut().flush();
--- a/test/script/basic/JDK-8030182_2.js Thu Sep 11 15:34:13 2014 -0700 +++ b/test/script/basic/JDK-8030182_2.js Tue Sep 16 13:59:37 2014 -0700 @@ -41,6 +41,6 @@ try { eval(str); } catch (e) { - print(e.stack.replace(/\\/g, '/').replace(/<eval>@[0-9]+/, '<eval>@<id>')); + print(e.stack.replace(/\\/g, '/')); }
--- a/test/script/basic/JDK-8030182_2.js.EXPECTED Thu Sep 11 15:34:13 2014 -0700 +++ b/test/script/basic/JDK-8030182_2.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -1,3 +1,3 @@ ReferenceError: "g" is not defined - at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>@<id>:-1) + at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>:-1) at <program> (test/script/basic/JDK-8030182_2.js:42)
--- a/test/script/basic/JDK-8048079_1.js Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * JDK-8048079: Persistent code store is broken after optimistic types merge - * - * @test - * @run - * @option -pcc - * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache - * @fork - */ - -load(__DIR__ + 'prototype.js'); -load(__DIR__ + 'yui.js');
--- a/test/script/basic/JDK-8048079_1.js.EXPECTED Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -parsed and compiled ok prototype.js -parsed and compiled ok yui-min.js -parsed and compiled ok yui.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_1a.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge + * + * @test + * @runif external.prototype + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'prototype.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_1a.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,1 @@ +parsed and compiled ok prototype.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_1b.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge + * + * @test + * @runif external.yui + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'yui.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_1b.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,2 @@ +parsed and compiled ok yui-min.js +parsed and compiled ok yui.js
--- a/test/script/basic/JDK-8048079_2.js Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * JDK-8048079: Persistent code store is broken after optimistic types merge - * - * @test - * @run - * @option -pcc - * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache - * @fork - */ - -load(__DIR__ + 'prototype.js'); -load(__DIR__ + 'yui.js');
--- a/test/script/basic/JDK-8048079_2.js.EXPECTED Thu Sep 11 15:34:13 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -parsed and compiled ok prototype.js -parsed and compiled ok yui-min.js -parsed and compiled ok yui.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_2a.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge. + * Same script as JDK-8048079_1a.js to exercise code cache. + * @test + * @runif external.prototype + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'prototype.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_2a.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,1 @@ +parsed and compiled ok prototype.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_2b.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8048079: Persistent code store is broken after optimistic types merge + * Same script as JDK-8048079_1b.js to exercise code cache again. + * @test + * @runif external.yui + * @option -pcc + * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @fork + */ + +load(__DIR__ + 'yui.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8048079_2b.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,2 @@ +parsed and compiled ok yui-min.js +parsed and compiled ok yui.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8056129.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8056129: AtomicInteger is treated as primitive number with optimistic compilation + * + * @test + * @run + */ + +var AtomicInteger = java.util.concurrent.atomic.AtomicInteger; + +function getAtomic() { + return new AtomicInteger() +} +var x = getAtomic() +print(x instanceof AtomicInteger) + +var a = [] +a.push(x) +var y = a[0] +print(y instanceof AtomicInteger)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8056129.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,2 @@ +true +true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8057019-2.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * this apply with extra arguments + * (with apply to call enabled) + * + * @test + * @run + */ + +load(__DIR__ + 'JDK-8057019-payload.js'); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8057019-2.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,24 @@ +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +23 apa gorilla +23 apa gorilla +23 apa gorilla +23 apa gorilla +23 apa gorilla +23 apa gorilla +TypeError: Function.prototype.apply expects an Array for second argument +TypeError: Function.prototype.apply expects an Array for second argument +TypeError: Function.prototype.apply expects an Array for second argument
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8057019-payload.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * this apply with extra arguments + * + * @subtest + */ + +function func(x, y, z) { + print(x, y, z); +} + +function g() { + func.apply(this, arguments); +} +function h() { + func.apply(this, arguments, 23); +} +function i() { + func.apply(this, arguments, 23, 4711); +} +function j() { + func.apply(this, arguments, 23, 4711, "apa", "dingo", "gorilla"); +} +function k() { + func.apply(this, arguments, 23); +} +function l() { + func.apply(this, [23, "apa", "gorilla", "dingo"], 17); +} +function m() { + func.apply(this, [23, "apa", "gorilla", "dingo"]); +} +function n() { + func.apply(this, "significant"); +} + +g(1,2); +g(1,2,3); +g(1,2,3,4); + +h(1,2); +h(1,2,3); +h(1,2,3,4); + +i(1,2); +i(1,2,3); +i(1,2,3,4); + +j(1,2); +j(1,2,3); +j(1,2,3,4); + +k(1,2); +k(1,2,3); +k(1,2,3,4); + +l(1,2); +l(1,2,3); +l(1,2,3,4); + +m(1,2); +m(1,2,3); +m(1,2,3,4); + +try { + n(1,2); +} catch (e) { + print(e); +} +try { + n(1,2,3); +} catch (e) { + print(e); +} + +try { + n(1,2,3,4); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8057019.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * this apply with extra arguments + * (turning off apply to call) + * + * @fork + * @option -Dnashorn.apply2call=false + * @test + * @run + */ + +load(__DIR__ + 'JDK-8057019-payload.js'); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8057019.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,24 @@ +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +1 2 undefined +1 2 3 +1 2 3 +23 apa gorilla +23 apa gorilla +23 apa gorilla +23 apa gorilla +23 apa gorilla +23 apa gorilla +TypeError: Function.prototype.apply expects an Array for second argument +TypeError: Function.prototype.apply expects an Array for second argument +TypeError: Function.prototype.apply expects an Array for second argument
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8058179.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8058179: Global constants get in the way of self-modifying properties + * + * @test + * @run + */ + +var global = this; + +Object.defineProperty(global, "value", { + get: function() { + print("getting value"); + global["value"] = "value 2"; + return "value 1"; + }, + set: function(value) { + print("setting value: " + value); + delete global["value"]; + global["value"] = value; + }, + configurable: true +}); + +print(value); +print(value);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8058179.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,4 @@ +getting value +setting value: value 2 +value 1 +value 2
--- a/test/script/basic/apply_to_call/apply_to_call4.js.EXPECTED Thu Sep 11 15:34:13 2014 -0700 +++ b/test/script/basic/apply_to_call/apply_to_call4.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -15,8 +15,8 @@ 2.3 3.4 test 5 done -a=object -17 +a=number +22 undefined Now it's time for transforms 19
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/block-function-decl.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 + */ + +"use strict"; + +{ + // f is defined on block level + print(f); + f(); + function f() { + print("in f"); + } + print(f); + f(); +} + +try { + print(typeof f); + f(); +} catch (e) { + print(e); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/block-function-decl.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,10 @@ +function f() { + print("in f"); + } +in f +function f() { + print("in f"); + } +in f +undefined +ReferenceError: "f" is not defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-empty.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 + */ + +try { + eval('"use strict";\n' + + 'const x;\n'); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-empty.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,3 @@ +SyntaxError: test/script/basic/es6/const-empty.js#33:4<eval>:2:7 Missing assignment to constant "x" +const x; + ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-reassign.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x = 1;\n'); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x++;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x--;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + '++x;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + '--x;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x += 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x *= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x /= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x %= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x |= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x &= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x ^= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x <<= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x >>= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'x >>>= 1;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +} + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'delete x;\n'); + fail("const assignment didn't throw"); +} catch (e) { + print(e.name); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-reassign.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,16 @@ +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-redeclare-extra.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(<<CODE + "use strict"; + const x = 2; + var x = {}; +CODE) + +tryIt(<<CODE + "use strict"; + var x = 2; + const x = {}; +CODE) + +tryIt(<<CODE + "use strict"; + function x () {} + const x = 5; +CODE) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-redeclare-extra.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,9 @@ +SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:3:8 Variable "x" has already been declared + var x = {}; + ^ +SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:2:8 Variable "x" has already been declared + var x = 2; + ^ +SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:2:13 Variable "x" has already been declared + function x () {} + ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-redeclare.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 + */ + +try { + eval('"use strict";\n' + + 'const x = 2;\n' + + 'const x = 2;\n'); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-redeclare.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,3 @@ +SyntaxError: test/script/basic/es6/const-redeclare.js#33:4<eval>:2:6 Variable "x" has already been declared +const x = 2; + ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-self.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +const a = 1, b = a; + +print(a, b); + +try { + eval('"use strict";\n' + + 'const a = a;\n'); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-self.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,2 @@ +1 1 +ReferenceError: "a" is not defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-tdz.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +{ + print("test 1"); + + function f() { + try { + print(a); + } catch (a) { + print(a); + } + } + + f(); + const a = 1; + f(); +} + +{ + print("test 2"); + + function f() { + try { + print(a); + } catch (a) { + print(a); + } + } + + f(); + const a = 2; + f(); +} + +{ + print("test 3"); + { + try { + print(a); + } catch (a) { + print(a); + } + } + + const a = 3; + + { + print(a); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-tdz.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,9 @@ +test 1 +ReferenceError: "a" is not defined +1 +test 2 +ReferenceError: "a" is not defined +2 +test 3 +ReferenceError: "a" is not defined +3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 + */ + +"use strict"; + +const a = 2; +const c = 2; +print(a, c); + +function f(x) { + const a = 5; + const c = 10; + print(a, c); + if (x) { + const a = 42; + const c = 43; + print(a, c); + } + print(a, c); + + function inner() { + (function() { + print(a, c); + })(); + } + inner(); +} + +f(true); +f(false); + +(function() { + (function() { + print(a, c); + })(); +})(); + +function outer() { + print(a, c); +} +outer();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,10 @@ +2 2 +5 10 +42 43 +5 10 +5 10 +5 10 +5 10 +5 10 +2 2 +2 2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/for-let.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +for (let i = 0; i < 10; i++) { + print(i); +} + +try { + print(i); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/for-let.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,11 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +ReferenceError: "i" is not defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-eval.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +function f() { + var a; + let b; + const c = 0; + + print(a, b, c); + + try { + eval("x = 1; print('x: ' + x);"); + print("assignment to x succeeded"); + } catch (e) { + print(e); + } + try { + eval("'use strict'; let z = 1; print('z: ' + z);"); + print("assignment to z succeeded"); + eval("print('z: ' + z);"); + } catch (e) { + print(e); + } + + try { + eval("a = 1; print(a);"); + print("assignment to a succeeded"); + } catch (e) { + print(e); + } + print("a: " + a); + + try { + eval("b = 1; print('b: ' + b);"); + print("assignment to b succeeded"); + } catch (e) { + print(e); + } + print("b: " + b); + + try { + eval("c = 1; print('c: ' + c);"); + print("assignment to c succeeded"); + } catch (e) { + print(e); + } + print("c: " + c); + + eval("a = 2; let b = 3;"); + + try { + print(a, b, c); + } catch (e) { + print(e); + } + + let x; + + try { + print(a, b, c, x); + } catch (e) { + print(e); + } + +} + +f(); + +print(typeof a, typeof b, typeof c, typeof x, typeof z);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-eval.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,16 @@ +undefined undefined 0 +ReferenceError: "x" is not defined +z: 1 +assignment to z succeeded +ReferenceError: "z" is not defined +1 +assignment to a succeeded +a: 1 +b: 1 +assignment to b succeeded +b: 1 +TypeError: "c" is not a writable property of [object Object] +c: 0 +2 1 0 +2 1 0 undefined +undefined undefined undefined undefined undefined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-load-lib.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @subtest + */ + +"use strict"; + +// var should be visible in other script, let and const not +var a = 1; +let b = 2; +const c = 3; + +// top level function should be visible +function top() { + print("top level function"); +} + +// block level function not visible outside script +{ + function block() { + print("block function"); + } + + top(); + block(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-load.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +load(__DIR__ + "let-load-lib.js"); + +{ + let a = 20; + const c = 30; + print("print local defs: " + a, c); +} + +print("imported var: " + a); +try { + print("imported let: " + b); +} catch (e) { + print(e); +} + +try { + print("imported const: " + c); +} catch (e) { + print(e); +} + +top(); + +try { + block(); +} catch (e) { + print(e); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-load.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,8 @@ +top level function +block function +print local defs: 20 30 +imported var: 1 +ReferenceError: "b" is not defined +ReferenceError: "c" is not defined +top level function +ReferenceError: "block" is not defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-nodeclare.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +try { + if (true) { + let x = 2; + print(x); + } + print(x); +} catch (e) { + print(e); +} + + +try { + if (true) { + const x = 2; + print(x); + } + print(x); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-nodeclare.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,4 @@ +2 +ReferenceError: "x" is not defined +2 +ReferenceError: "x" is not defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-redeclare-extra.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(<<CODE + "use strict"; + let x = 2; + const x = function (a,b,c) {}; +CODE) + +tryIt(<<CODE + "use strict"; + let x = {}; + var x = 2; +CODE) + +tryIt(<<CODE + "use strict"; + var x = 2; + let x = undefined; +CODE) + +tryIt(<<CODE + "use strict"; + const x = function (){}; + let x = {}; +CODE) + + +tryIt(<<CODE + "use strict"; + let a = 2; + function a () {}; +CODE) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-redeclare-extra.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,15 @@ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:8 Variable "x" has already been declared + let x = 2; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:3:8 Variable "x" has already been declared + var x = 2; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:8 Variable "x" has already been declared + var x = 2; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:10 Variable "x" has already been declared + const x = function (){}; + ^ +SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:3:13 Variable "a" has already been declared + function a () {}; + ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-redeclare.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 + */ + +try { + eval('"use strict";\n' + + 'let x = 2;\n' + + 'let x = 2;\n'); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-redeclare.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,3 @@ +SyntaxError: test/script/basic/es6/let-redeclare.js#33:4<eval>:2:4 Variable "x" has already been declared +let x = 2; + ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-self.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +let a, b = a; + +print(a, b); + +try { + eval('"use strict";\n' + + 'let a = a;\n'); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-self.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,2 @@ +undefined undefined +ReferenceError: "a" is not defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-tdz.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +{ + print("test 1"); + + function f() { + try { + print(a); + } catch (a) { + print(a); + } + } + + f(); + let a = 1; + f(); +} + +{ + print("test 2"); + + function f() { + try { + print(a); + } catch (a) { + print(a); + } + } + + f(); + let a = 2; + f(); +} + +{ + print("test 3"); + + { + try { + print(a); + } catch (a) { + print(a); + } + } + + let a = 3; + + { + print(a); + } +} + +{ + print("test 4"); + let a; + + { + print(a); + } + + a = 4; + + { + print(a); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-tdz.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,12 @@ +test 1 +ReferenceError: "a" is not defined +1 +test 2 +ReferenceError: "a" is not defined +2 +test 3 +ReferenceError: "a" is not defined +3 +test 4 +undefined +4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8051889: Implement block scoping in symbol assignment and scope computation + * + * @test + * @run + * @option --language=es6 */ + +"use strict"; + +let a = 2; +let c = 2; +print(a, c); + +function f(x) { + let a = 5; + const c = 10; + print(a, c); + if (x) { + let a = 42; + const c = 43; + print(a, c); + } + print(a, c); + + function inner() { + (function() { + print(a, c); + })(); + } + inner(); +} + +f(true); +f(false); + +(function() { + (function() { + print(a, c); + })(); +})(); + +function outer() { + print(a, c); +} +outer(); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,10 @@ +2 2 +5 10 +42 43 +5 10 +5 10 +5 10 +5 10 +5 10 +2 2 +2 2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_const_closure.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + +function tryIt(code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + + +tryIt(<<CODE + function a () { + this.val = 41 + let self = this + this.b = function () { + return self.val; + } + } + c = new a() + print(c.b.call(null)) +CODE) + + +tryIt(<<CODE + function a () { + this.val = 42 + let self = this + this.b = function () { + return this.val; + }.bind(self) + } + c = new a() + print(c.b.call(null)) +CODE) + +tryIt(<<CODE + function a () { + this.val = 43 + const self = this + this.b = function () { + return self.val; + } + } + c = new a() + print(c.b.call(null)) +CODE) + +tryIt(<<CODE + function a () { + this.val = 44 + const self = this + this.b = function () { + return this.val; + }.bind(self) + } + c = new a() + print(c.b.call(null)) +CODE) + +tryIt(<<CODE + let a = {name : 'test'} + let f = function () { + print(this.name) + } + let nf = f.bind(a) + nf() + if (true) { + let a = null + nf() + } + nf() +CODE) + + +tryIt(<<CODE + let arr = [] + for (let i = 0; i < 3; i++) { + arr[i] = function(){return i;} + } + for (let i in arr) { + print(arr[i]()) + } + arr = [] + for (var i = 0; i < 3; i++) { + (function(i){ + arr[i] = function(){return i;} + })(i) + } + for (let i in arr) { + print(arr[i]()) + } +CODE) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_const_closure.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,13 @@ +41 +42 +43 +44 +test +test +test +3 +3 +3 +0 +1 +2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_const_reuse.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(<<CODE + let a = 23 + if (true) { + a-- + let a = 43; + } +CODE) + +tryIt(<<CODE + const a = 23 + if (true) { + a-- + const a = 43; + } +CODE) + +tryIt(<<CODE + let a = 23 + if (true) { + a-- + const a = 43; + } +CODE) + +tryIt(<<CODE + const a = 23 + if (true) { + a-- + let a = 43; + } +CODE)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_const_reuse.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,8 @@ +ReferenceError: "a" is not defined +SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a" + a-- + ^ +SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a" + a-- + ^ +ReferenceError: "a" is not defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_different_types.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(<<CODE + let a = function () { var a = "Hello World!"; return a; } + print(typeof a) + { + let a = 34; + print(typeof a) + if (true) { + let c = 54.7 + var d = c + print(typeof c) + print(typeof d) + } + } + print(typeof a) + print(typeof a()) + print(typeof c) + print(typeof d) + print(d) +CODE) + +tryIt(<<CODE + let a = {} + if (true) { + function a () { + print (typeof a) + return 'Hello World!' + } + print(typeof a) + print(a()) + } + print(typeof a) +CODE) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_different_types.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,13 @@ +function +number +number +number +function +string +undefined +number +54.7 +function +function +Hello World! +object
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_loops.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057678: Tests for let&const keywords in Nashorn + * + * @test + * @run + * @option --language=es6 + * @option -scripting + */ + + +function tryIt (code) { + try { + eval(code) + } catch (e) { + print(e) + } +} + +tryIt(<<CODE + let a = 2; + do { + a--; + let b = a; + } while (a > 0); + print(a) + print(b) +CODE) + +tryIt(<<CODE + let a = 2 + while(a > 0) { + a-- + let b = a + } + print(a) + print(b) +CODE) + +tryIt(<<CODE + let a = 2 + while(a > 0) { + a-- + const b = a + } + print(a) + print(b) +CODE) + +tryIt(<<CODE + let a = 2; + do { + a--; + const b = a; + } while (a > 0); + print(a) + print(b) +CODE)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let_loops.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,8 @@ +0 +ReferenceError: "b" is not defined +0 +ReferenceError: "b" is not defined +0 +ReferenceError: "b" is not defined +0 +ReferenceError: "b" is not defined
--- a/test/script/basic/optimistic_check_type.js Thu Sep 11 15:34:13 2014 -0700 +++ b/test/script/basic/optimistic_check_type.js Tue Sep 16 13:59:37 2014 -0700 @@ -36,13 +36,18 @@ // Testing conditional operator print(inspect("" ? b : x.a, "ternary operator")) -print(inspect(x.b ? b : x.a, "ternary operator")) -print(inspect(c ? b : a, "ternary operator")) -print(inspect(!c ? b : a, "ternary operator")) -print(inspect(d ? b : x.c, "ternary operator")) +var b1 = b; +print(inspect(x.b ? b1 : x.a, "ternary operator")) +var b2 = b; +print(inspect(c ? b2 : a, "ternary operator")) +var b3 = b; +print(inspect(!c ? b3 : a, "ternary operator")) +var b4 = b; +print(inspect(d ? b4 : x.c, "ternary operator")) print(inspect(x.c ? a : c, "ternary operator")) print(inspect(c ? d : a, "ternary operator")) -print(inspect(c ? +a : b, "ternary operator")) +var b5 = b; +print(inspect(c ? +a : b5, "ternary operator")) // Testing format methods print(inspect(b.toFixed(2), "global double toFixed()")) @@ -53,11 +58,14 @@ print(inspect(trees[1], "member object")) trees[1] = undefined; print(inspect(trees[1], "member undefined")) -print(inspect(1 in trees ? b : a, "conditional on array member")) +var b6=b; +print(inspect(1 in trees ? b6 : a, "conditional on array member")) delete trees[2] -print(inspect(2 in trees ? b : a, "conditional on array member")) +var b7=b; +print(inspect(2 in trees ? b7 : a, "conditional on array member")) print(inspect(3 in trees ? trees[2]="bay" : a, "conditional on array member")) -print(inspect("oak" in trees ? b : a, "conditional on array member")) +var b8=b; +print(inspect("oak" in trees ? b8 : a, "conditional on array member")) // Testing nested functions and return value function f1() {
--- a/test/script/basic/splitter.js Thu Sep 11 15:34:13 2014 -0700 +++ b/test/script/basic/splitter.js Tue Sep 16 13:59:37 2014 -0700 @@ -30,7 +30,5 @@ * @fork */ -load(__DIR__ + 'prototype.js'); -load(__DIR__ + 'yui.js'); load(__DIR__ + 'NASHORN-689.js'); load(__DIR__ + 'NASHORN-58.js');
--- a/test/script/basic/splitter.js.EXPECTED Thu Sep 11 15:34:13 2014 -0700 +++ b/test/script/basic/splitter.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -1,6 +1,3 @@ -parsed and compiled ok prototype.js -parsed and compiled ok yui-min.js -parsed and compiled ok yui.js a=10 a=9 a=8
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/splitter_prototype.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Test various scripts with low splitter threshold + * + * @test + * @option -Dnashorn.compiler.splitter.threshold=200 + * @runif external.prototype + * @fork + */ + +load(__DIR__ + 'prototype.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/splitter_prototype.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,1 @@ +parsed and compiled ok prototype.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/splitter_yui.js Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Test various scripts with low splitter threshold + * + * @test + * @option -Dnashorn.compiler.splitter.threshold=200 + * @runif external.yui + * @fork + */ + +load(__DIR__ + 'yui.js');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/splitter_yui.js.EXPECTED Tue Sep 16 13:59:37 2014 -0700 @@ -0,0 +1,2 @@ +parsed and compiled ok yui-min.js +parsed and compiled ok yui.js
--- a/test/script/trusted/JDK-8006529.js Thu Sep 11 15:34:13 2014 -0700 +++ b/test/script/trusted/JDK-8006529.js Tue Sep 16 13:59:37 2014 -0700 @@ -120,7 +120,7 @@ var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class) var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class) -var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, boolean.class); +var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, ErrorManager.class, boolean.class); // compile(script) -- compiles a script specified as a string with its // source code, returns a jdk.nashorn.internal.ir.FunctionNode object @@ -134,7 +134,7 @@ var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance()); var func = parseMethod.invoke(parser); - var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, false); + var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false); return compileMethod.invoke(compiler, func, phases); };
--- a/test/src/jdk/nashorn/api/scripting/ScopeTest.java Thu Sep 11 15:34:13 2014 -0700 +++ b/test/src/jdk/nashorn/api/scripting/ScopeTest.java Tue Sep 16 13:59:37 2014 -0700 @@ -407,6 +407,75 @@ Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context"); } + + /** + * Test multi-threaded access to prototype user accessor properties for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedAccessorTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'original context' })", origContext); + e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'new context', configurable: true })", newCtxt); + final String sharedScript = "({}).foo"; + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + final Object obj3 = e.eval("delete Object.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); + assertEquals(obj3, "newer context"); + final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); + + t3.start(); + t4.start(); + t3.join(); + t4.join(); + } + + /** + * Test multi-threaded access to primitive prototype user accessor properties for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedPrimitiveAccessorTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'original context' })", origContext); + e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'new context' })", newCtxt); + final String sharedScript = "''.foo"; + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + final Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); + assertEquals(obj3, "newer context"); + final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); + + t3.start(); + t4.start(); + t3.join(); + t4.join(); + } + /** * Test multi-threaded scope function invocation for shared script classes with multiple globals. */
--- a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java Thu Sep 11 15:34:13 2014 -0700 +++ b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java Tue Sep 16 13:59:37 2014 -0700 @@ -98,11 +98,16 @@ compileTestSet(new File(TEST262_SUITE_DIR), new TestFilter() { @Override public boolean exclude(final File file, final String content) { - return content.indexOf("@negative") != -1; + return content != null && content.contains("@negative"); } }); } - compileTestSet(new File(TEST_BASIC_DIR), null); + compileTestSet(new File(TEST_BASIC_DIR), new TestFilter() { + @Override + public boolean exclude(final File file, final String content) { + return file.getName().equals("es6"); + } + }); compileTestSet(new File(TEST_NODE_DIR, "node"), null); compileTestSet(new File(TEST_NODE_DIR, "src"), null); } @@ -136,6 +141,9 @@ private int skipped; private void compileJSDirectory(final File dir, final TestFilter filter) { + if (filter != null && filter.exclude(dir, null)) { + return; + } for (final File f : dir.listFiles()) { if (f.isDirectory()) { compileJSDirectory(f, filter);
--- a/test/src/jdk/nashorn/internal/parser/ParserTest.java Thu Sep 11 15:34:13 2014 -0700 +++ b/test/src/jdk/nashorn/internal/parser/ParserTest.java Tue Sep 16 13:59:37 2014 -0700 @@ -82,11 +82,16 @@ parseTestSet(TEST262_SUITE_DIR, new TestFilter() { @Override public boolean exclude(final File file, final String content) { - return content.indexOf("@negative") != -1; + return content != null && content.contains("@negative"); } }); } - parseTestSet(TEST_BASIC_DIR, null); + parseTestSet(TEST_BASIC_DIR, new TestFilter() { + @Override + public boolean exclude(final File file, final String content) { + return file.getName().equals("es6"); + } + }); } private void parseTestSet(final String testSet, final TestFilter filter) { @@ -120,6 +125,9 @@ private int skipped; private void parseJSDirectory(final File dir, final TestFilter filter) { + if (filter != null && filter.exclude(dir, null)) { + return; + } for (final File f : dir.listFiles()) { if (f.isDirectory()) { parseJSDirectory(f, filter);
--- a/test/src/jdk/nashorn/internal/test/framework/TestFinder.java Thu Sep 11 15:34:13 2014 -0700 +++ b/test/src/jdk/nashorn/internal/test/framework/TestFinder.java Tue Sep 16 13:59:37 2014 -0700 @@ -261,14 +261,17 @@ isTest = false; isNotTest = true; break; - case "@runif": - if (System.getProperty(scanner.next()) != null) { + case "@runif": { + final String prop = scanner.next(); + if (System.getProperty(prop) != null) { shouldRun = true; } else { + factory.log("WARNING: (" + prop + ") skipping " + testFile); isTest = false; isNotTest = true; } break; + } case "@run": shouldRun = true; break;