This commit is contained in:
Chris Hegarty 2016-03-22 15:26:07 +00:00
commit 30266d79fb
9 changed files with 220 additions and 15 deletions
nashorn
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal
test/script/basic/es6

@ -257,6 +257,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
//is this a rest of compilation
private final int[] continuationEntryPoints;
// Scope object creators needed for for-of and for-in loops
private Deque<FieldObjectCreator<?>> scopeObjectCreators = new ArrayDeque<>();
/**
* Constructor.
*
@ -1297,6 +1300,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private void popBlockScope(final Block block) {
final Label breakLabel = block.getBreakLabel();
if (block.providesScopeCreator()) {
scopeObjectCreators.pop();
}
if(!block.needsScope() || lc.isFunctionBody()) {
emitBlockBreakLabel(breakLabel);
return;
@ -1812,6 +1818,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}.store();
body.accept(this);
if (forNode.needsScopeCreator() && lc.getCurrentBlock().providesScopeCreator()) {
// for-in loops with lexical declaration need a new scope for each iteration.
final FieldObjectCreator<?> creator = scopeObjectCreators.peek();
assert creator != null;
creator.createForInIterationScope(method);
method.storeCompilerConstant(SCOPE);
}
if(method.isReachable()) {
method._goto(continueLabel);
}
@ -1923,12 +1937,16 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* Create a new object based on the symbols and values, generate
* bootstrap code for object
*/
new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
final FieldObjectCreator<Symbol> creator = new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
@Override
protected void loadValue(final Symbol value, final Type type) {
method.load(value, type);
}
}.makeObject(method);
};
creator.makeObject(method);
if (block.providesScopeCreator()) {
scopeObjectCreators.push(creator);
}
// program function: merge scope into global
if (isFunctionBody && function.isProgram()) {
method.invoke(ScriptRuntime.MERGE_SCOPE);
@ -3294,8 +3312,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
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)
// Block-scoped variables need a DECLARE flag to signal end of temporal dead zone (TDZ).
// However, don't do this for CONST which always has an initializer except in the special case of
// for-in/of loops, in which it is initialized in the loop header and should be left untouched here.
if (needsScope && varNode.isLet()) {
method.loadCompilerConstant(SCOPE);
method.loadUndefined(Type.OBJECT);
final int flags = getScopeCallSiteFlags(identSymbol) | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0);
@ -4480,7 +4500,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
final Symbol symbol = node.getSymbol();
assert symbol != null;
if (symbol.isScope()) {
final int flags = getScopeCallSiteFlags(symbol);
final int flags = getScopeCallSiteFlags(symbol) | (node.isDeclaredHere() ? CALLSITE_DECLARE : 0);
if (isFastScope(symbol)) {
storeFastScopeVar(symbol, flags);
} else {

@ -120,6 +120,25 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
}
}
/**
* Create a scope for a for-in/of loop as defined in ES6 13.7.5.13 step 5.g.iii
*
* @param method the method emitter
*/
void createForInIterationScope(final MethodEmitter method) {
assert fieldObjectClass != null;
assert isScope();
assert getMap() != null;
final String className = getClassName();
method._new(fieldObjectClass).dup();
loadMap(method); //load the map
loadScope(method);
// We create a scope identical to the currently active one, so use its parent as our parent
method.invoke(ScriptObject.GET_PROTO);
method.invoke(constructorNoLookup(className, PropertyMap.class, ScriptObject.class));
}
@Override
public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) {
method.load(objectType, objectSlot);

@ -97,6 +97,7 @@ import jdk.nashorn.internal.runtime.logging.Logger;
final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Loggable {
private final DebugLogger log;
private final boolean es6;
// Conservative pattern to test if element names consist of characters valid for identifiers.
// This matches any non-zero length alphanumeric string including _ and $ and not starting with a digit.
@ -144,6 +145,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
});
this.log = initLogger(compiler.getContext());
this.es6 = compiler.getScriptEnvironment()._es6;
}
@Override
@ -257,8 +259,9 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
}
newForNode = checkEscape(newForNode);
if(newForNode.isForIn()) {
// Wrap it in a block so its internally created iterator is restricted in scope
if(!es6 && newForNode.isForIn()) {
// Wrap it in a block so its internally created iterator is restricted in scope, unless we are running
// in ES6 mode, in which case the parser already created a block to capture let/const declarations.
addStatementEnclosedInBlock(newForNode);
} else {
addStatement(newForNode);

@ -462,6 +462,19 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
return next;
}
/**
* Determine whether this block needs to provide its scope object creator for use by its child nodes.
* This is only necessary for synthetic parent blocks of for-in loops with lexical declarations.
*
* @see ForNode#needsScopeCreator()
* @return true if child nodes need access to this block's scope creator
*/
public boolean providesScopeCreator() {
return needsScope() && isSynthetic()
&& (getLastStatement() instanceof ForNode)
&& ((ForNode) getLastStatement()).needsScopeCreator();
}
@Override
public boolean isBreakableWithoutLabel() {
return false;

@ -274,4 +274,15 @@ public final class ForNode extends LoopNode {
public boolean hasPerIterationScope() {
return (flags & PER_ITERATION_SCOPE) != 0;
}
/**
* Returns true if this for-node needs the scope creator of its containing block to create
* per-iteration scope. This is only true for for-in loops with lexical declarations.
*
* @see Block#providesScopeCreator()
* @return true if the containing block's scope object creator is required in codegen
*/
public boolean needsScopeCreator() {
return isForIn() && hasPerIterationScope();
}
}

@ -1104,12 +1104,14 @@ loop:
} finally {
defaultNames.pop();
}
} else if (varType == CONST) {
} else if (varType == CONST && isStatement) {
throw error(AbstractParser.message("missing.const.assignment", name.getName()));
}
// Only set declaration flag on lexically scoped let/const as it adds runtime overhead.
final IdentNode actualName = varType == LET || varType == CONST ? name.setIsDeclaredHere() : name;
// Allocate var node.
final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, name.setIsDeclaredHere(), init, varFlags);
final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, actualName, init, varFlags);
vars.add(var);
appendStatement(var);
@ -1247,7 +1249,6 @@ loop:
expect(LPAREN);
switch (type) {
case VAR:
// Var declaration captured in for outer block.
@ -1257,9 +1258,7 @@ loop:
break;
default:
if (useBlockScope() && (type == LET || type == CONST)) {
if (type == LET) {
flags |= ForNode.PER_ITERATION_SCOPE;
}
flags |= ForNode.PER_ITERATION_SCOPE;
// LET/CONST declaration captured in container block created above.
vars = variableStatement(type, false, forStart);
break;

@ -145,8 +145,7 @@ final class SetMethodCreator {
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
final MethodHandle methodHandle;
if (NashornCallSiteDescriptor.isDeclaration(desc)) {
assert property.needsDeclaration();
if (NashornCallSiteDescriptor.isDeclaration(desc) && 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

@ -0,0 +1,69 @@
/*
* Copyright (c) 2016, 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-8151810: for-in iteration does not provide per-iteration scope
*
* @test
* @run
* @option --language=es6
*/
"use strict";
let array = ["a", "b", "c"];
let funcs = [];
for (let i in array) {
funcs.push(function() { return array[i]; });
}
Assert.assertEquals(funcs.length, 3);
for (let i = 0; i < 3; i++) {
Assert.assertEquals(funcs[i](), array[i]);
}
funcs = [];
for (let i in array) {
for (let j in array) {
for (let k in array) {
funcs.push(function () {
return array[i] + array[j] + array[k];
});
}
}
}
Assert.assertEquals(funcs.length, 3 * 3 * 3);
let count = 0;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
for (let k = 0; k < 3; k++) {
Assert.assertEquals(funcs[count++](), array[i] + array[j] + array[k]);
}
}
}

@ -0,0 +1,72 @@
/*
* Copyright (c) 2016, 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-8151811: Const declarations do not work in for..in loops
*
* @test
* @run
* @option --language=es6
*/
let array = ["a", "b", "c"];
let count = 0;
for (const i in array) {
try {
eval("i = 5");
fail("const assignment should have thrown")
} catch (e) {
Assert.assertTrue(e instanceof TypeError);
}
Assert.assertTrue(i == count++);
}
let funcs = [];
for (const i in array) {
try {
eval("i = 5");
fail("const assignment should have thrown")
} catch (e) {
Assert.assertTrue(e instanceof TypeError);
}
for (const j in array) {
for (const k in array) {
funcs.push(function () {
return array[i] + array[j] + array[k];
});
}
}
}
Assert.assertEquals(funcs.length, 3 * 3 * 3);
count = 0;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
for (let k = 0; k < 3; k++) {
Assert.assertEquals(funcs[count++](), array[i] + array[j] + array[k]);
}
}
}