# HG changeset patch # User hannesw # Date 1409849238 -7200 # Node ID b7a2db4de2545740ff31f50cee090ef2461c4c11 # Parent 46647c4943ffb2db97324b457a567c44430c9ec2 8051889: Implement block scoping in symbol assignment and scope computation Reviewed-by: attila, lagergren diff -r 46647c4943ff -r b7a2db4de254 make/build.xml --- a/make/build.xml Wed Sep 03 14:33:34 2014 +0200 +++ b/make/build.xml Thu Sep 04 18:47:18 2014 +0200 @@ -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"; }; diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Sep 04 18:47:18 2014 +0200 @@ -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 implements Loggable { +final class AssignSymbols extends NodeVisitor implements Loggable { private final DebugLogger log; private final boolean debug; @@ -190,8 +193,7 @@ * @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(new LexicalContext()) { @Override @@ -204,8 +206,8 @@ 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,9 +440,9 @@ @Override public boolean enterBlock(final Block block) { start(block); - block.clearSymbols(); if (lc.isFunctionBody()) { + block.clearSymbols(); enterFunctionBody(); } @@ -441,7 +461,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 +475,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); } @@ -485,7 +506,8 @@ if (functionNode.isDeclared()) { final Iterator 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)); } } @@ -495,10 +517,16 @@ @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); } @@ -597,7 +625,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 +636,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 +645,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 +653,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 +679,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,6 +717,21 @@ } @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 @@ -699,8 +747,7 @@ 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(); @@ -799,9 +846,8 @@ // 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, identNode, IS_GLOBAL | IS_SCOPE); } functionUsesSymbol(symbol); @@ -810,7 +856,15 @@ symbol.increaseUseCount(); } - return end(identNode.setSymbol(symbol)); + 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); } @Override @@ -834,8 +888,7 @@ return tryNode; } - @Override - public Node leaveTYPEOF(final UnaryNode unaryNode) { + private Node leaveTYPEOF(final UnaryNode unaryNode) { final Expression rhs = unaryNode.getExpression(); final List args = new ArrayList<>(); @@ -875,7 +928,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 +968,8 @@ return false; } - if (lc.getCurrentFunction().allVarsInScope()) { + final FunctionNode func = lc.getCurrentFunction(); + if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) { return true; } @@ -955,4 +1009,16 @@ final List 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); + } } diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Sep 04 18:47:18 2014 +0200 @@ -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; } @@ -3216,27 +3227,34 @@ return false; } final Expression init = varNode.getInit(); - - if (init == null) { - 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"; - + 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); 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 +4361,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(); diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/codegen/Compiler.java --- a/src/jdk/nashorn/internal/codegen/Compiler.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Thu Sep 04 18:47:18 2014 +0200 @@ -59,7 +59,9 @@ 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; @@ -89,6 +91,8 @@ private final String sourceName; + private final ErrorManager errors; + private final boolean optimistic; private final Map bytecode; @@ -311,6 +315,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 +323,9 @@ final ScriptEnvironment env, final CodeInstaller 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 +335,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 +350,7 @@ final ScriptEnvironment env, final CodeInstaller installer, final Source source, + final ErrorManager errors, final boolean isStrict, final boolean isOnDemand, final RecompilableScriptFunctionData compiledFunction, @@ -359,6 +367,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; @@ -524,7 +533,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) { diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/codegen/FieldObjectCreator.java --- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Thu Sep 04 18:47:18 2014 +0200 @@ -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> 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 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 tuple) { method.dup(); diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/codegen/MapCreator.java --- a/src/jdk/nashorn/internal/codegen/MapCreator.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/MapCreator.java Thu Sep 04 18:47:18 2014 +0200 @@ -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 structure, final List> 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; } } diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/codegen/MethodEmitter.java --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Sep 04 18:47:18 2014 +0200 @@ -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) { diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/codegen/TypeEvaluator.java --- a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Thu Sep 04 18:47:18 2014 +0200 @@ -108,7 +108,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; } diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/ir/FunctionNode.java --- a/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Sep 04 18:47:18 2014 +0200 @@ -138,10 +138,6 @@ /** Last token of function. **/ private final long lastToken; - /** Declared symbols in this function node */ - @Ignore - private final Set declaredSymbols; - /** Method's namespace. */ private final Namespace namespace; @@ -330,7 +326,6 @@ 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; @@ -369,7 +364,6 @@ this.id = functionNode.id; this.ident = functionNode.ident; this.namespace = functionNode.namespace; - this.declaredSymbols = functionNode.declaredSymbols; this.kind = functionNode.kind; this.firstToken = functionNode.firstToken; } @@ -724,24 +718,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 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 */ @@ -970,13 +946,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); } /** diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/ir/IdentNode.java --- a/src/jdk/nashorn/internal/ir/IdentNode.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/ir/IdentNode.java Thu Sep 04 18:47:18 2014 +0200 @@ -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__). * diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/ir/Symbol.java --- a/src/jdk/nashorn/internal/ir/Symbol.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Thu Sep 04 18:47:18 2014 +0200 @@ -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. diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/ir/VarNode.java --- a/src/jdk/nashorn/internal/ir/VarNode.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/ir/VarNode.java Thu Sep 04 18:47:18 2014 +0200 @@ -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) { diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/parser/Parser.java --- a/src/jdk/nashorn/internal/parser/Parser.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/parser/Parser.java Thu Sep 04 18:47:18 2014 +0200 @@ -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; @@ -579,6 +580,10 @@ } } + private boolean useBlockScope() { + return env._es6; + } + private static boolean isArguments(final String name) { return ARGUMENTS_NAME.equals(name); } @@ -694,9 +699,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 +884,7 @@ block(); break; case VAR: - variableStatement(true); + variableStatement(type, true); break; case SEMICOLON: emptyStatement(); @@ -918,8 +934,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 +1055,17 @@ * Parse a VAR statement. * @param isStatement True if a statement (not used in a FOR.) */ - private List variableStatement(final boolean isStatement) { + private List variableStatement(final TokenType varType, final boolean isStatement) { // VAR tested in caller. next(); final List 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 +1089,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 +1208,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 +1234,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 +1326,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 +1763,7 @@ } } - /** + /** * ThrowStatement : * throw Expression ; // [no LineTerminator here] * @@ -2609,7 +2650,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 +2702,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); } @@ -2838,7 +2886,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); diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/AccessorProperty.java --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/Context.java --- a/src/jdk/nashorn/internal/runtime/Context.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/Context.java Thu Sep 04 18:47:18 2014 +0200 @@ -1132,7 +1132,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 +1162,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 { diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/FindProperty.java --- a/src/jdk/nashorn/internal/runtime/FindProperty.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java Thu Sep 04 18:47:18 2014 +0200 @@ -58,6 +58,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) diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/Property.java --- a/src/jdk/nashorn/internal/runtime/Property.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/Property.java Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Sep 04 18:47:18 2014 +0200 @@ -394,6 +394,7 @@ context.getEnv(), installer, functionNode.getSource(), // source + context.getErrorManager(), isStrict() | functionNode.isStrict(), // is strict true, // is on demand this, // compiledFunction, i.e. this RecompilableScriptFunctionData diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/ScriptEnvironment.java --- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Thu Sep 04 18:47:18 2014 +0200 @@ -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"); diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/ScriptObject.java --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu Sep 04 18:47:18 2014 +0200 @@ -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); @@ -2027,6 +2028,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 +2157,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); } diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/ScriptRuntime.java --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Thu Sep 04 18:47:18 2014 +0200 @@ -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. diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/SetMethodCreator.java --- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Thu Sep 04 18:47:18 2014 +0200 @@ -140,7 +140,29 @@ private SetMethod createExistingPropertySetter() { final Property property = find.getProperty(); - final MethodHandle methodHandle = find.getSetter(type, 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, NashornCallSiteDescriptor.isStrict(desc)); + 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, NashornCallSiteDescriptor.isStrict(desc)); + } assert methodHandle != null; assert property != null; diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/resources/Messages.properties --- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/internal/runtime/resources/Options.properties --- a/src/jdk/nashorn/internal/runtime/resources/Options.properties Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties Thu Sep 04 18:47:18 2014 +0200 @@ -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, \ diff -r 46647c4943ff -r b7a2db4de254 src/jdk/nashorn/tools/Shell.java --- a/src/jdk/nashorn/tools/Shell.java Wed Sep 03 14:33:34 2014 +0200 +++ b/src/jdk/nashorn/tools/Shell.java Thu Sep 04 18:47:18 2014 +0200 @@ -252,6 +252,15 @@ 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(); diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/block-function-decl.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/block-function-decl.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} + diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/block-function-decl.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/block-function-decl.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,10 @@ +function f() { + print("in f"); + } +in f +function f() { + print("in f"); + } +in f +undefined +ReferenceError: "f" is not defined diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-empty.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-empty.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-empty.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-empty.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,3 @@ +SyntaxError: test/script/basic/es6/const-empty.js#33:4@1:2:7 Missing assignment to constant "x" +const x; + ^ diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-reassign.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-reassign.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-reassign.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-reassign.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,16 @@ +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError +SyntaxError diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-redeclare.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-redeclare.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-redeclare.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-redeclare.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,3 @@ +SyntaxError: test/script/basic/es6/const-redeclare.js#33:4@1:2:6 Variable "x" has already been declared +const x = 2; + ^ diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-self.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-self.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-self.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-self.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,2 @@ +1 1 +ReferenceError: "a" is not defined diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-tdz.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-tdz.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); + } +} + diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const-tdz.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const-tdz.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const.js Thu Sep 04 18:47:18 2014 +0200 @@ -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(); diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/const.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/const.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,10 @@ +2 2 +5 10 +42 43 +5 10 +5 10 +5 10 +5 10 +5 10 +2 2 +2 2 diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/for-let.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/for-let.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/for-let.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/for-let.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,11 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +ReferenceError: "i" is not defined diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-eval.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-eval.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-eval.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-eval.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-load-lib.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-load-lib.js Thu Sep 04 18:47:18 2014 +0200 @@ -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(); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-load.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-load.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} + + diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-load.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-load.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-nodeclare.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-nodeclare.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-nodeclare.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-nodeclare.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,4 @@ +2 +ReferenceError: "x" is not defined +2 +ReferenceError: "x" is not defined diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-redeclare.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-redeclare.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-redeclare.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-redeclare.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,3 @@ +SyntaxError: test/script/basic/es6/let-redeclare.js#33:4@1:2:4 Variable "x" has already been declared +let x = 2; + ^ diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-self.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-self.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); +} diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-self.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-self.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,2 @@ +undefined undefined +ReferenceError: "a" is not defined diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-tdz.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-tdz.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); + } +} + diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let-tdz.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let-tdz.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -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 diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let.js Thu Sep 04 18:47:18 2014 +0200 @@ -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(); + diff -r 46647c4943ff -r b7a2db4de254 test/script/basic/es6/let.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/es6/let.js.EXPECTED Thu Sep 04 18:47:18 2014 +0200 @@ -0,0 +1,10 @@ +2 2 +5 10 +42 43 +5 10 +5 10 +5 10 +5 10 +5 10 +2 2 +2 2 diff -r 46647c4943ff -r b7a2db4de254 test/script/trusted/JDK-8006529.js --- a/test/script/trusted/JDK-8006529.js Wed Sep 03 14:33:34 2014 +0200 +++ b/test/script/trusted/JDK-8006529.js Thu Sep 04 18:47:18 2014 +0200 @@ -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); }; diff -r 46647c4943ff -r b7a2db4de254 test/src/jdk/nashorn/internal/codegen/CompilerTest.java --- a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java Wed Sep 03 14:33:34 2014 +0200 +++ b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java Thu Sep 04 18:47:18 2014 +0200 @@ -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); diff -r 46647c4943ff -r b7a2db4de254 test/src/jdk/nashorn/internal/parser/ParserTest.java --- a/test/src/jdk/nashorn/internal/parser/ParserTest.java Wed Sep 03 14:33:34 2014 +0200 +++ b/test/src/jdk/nashorn/internal/parser/ParserTest.java Thu Sep 04 18:47:18 2014 +0200 @@ -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);