8051889: Implement block scoping in symbol assignment and scope computation

Reviewed-by: attila, lagergren
This commit is contained in:
Hannes Wallnöfer 2014-09-04 15:37:14 +02:00
parent 917f13e5db
commit 7ecdd47620
60 changed files with 1722 additions and 182 deletions

View file

@ -341,6 +341,13 @@ grant codeBase "file:/${basedir}/test/script/basic/parser/*" {
permission java.util.PropertyPermission "nashorn.test.*", "read"; 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" { grant codeBase "file:/${basedir}/test/script/basic/JDK-8010946-privileged.js" {
permission java.util.PropertyPermission "java.security.policy", "read"; permission java.util.PropertyPermission "java.security.policy", "read";
}; };

View file

@ -36,6 +36,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 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.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_FUNCTION_SELF;
import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
@ -83,11 +84,13 @@ import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.PropertyMap; 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.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger; import jdk.nashorn.internal.runtime.logging.Logger;
@ -101,7 +104,7 @@ import jdk.nashorn.internal.runtime.logging.Logger;
* visitor. * visitor.
*/ */
@Logger(name="symbols") @Logger(name="symbols")
final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements Loggable { final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable {
private final DebugLogger log; private final DebugLogger log;
private final boolean debug; private final boolean debug;
@ -190,8 +193,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
* @param body the body of the FunctionNode we are entering * @param body the body of the FunctionNode we are entering
*/ */
private void acceptDeclarations(final FunctionNode functionNode, final Block body) { 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 // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers.
// in a separate step above) and "var" declarations in for loop initializers.
// //
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override @Override
@ -204,8 +206,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
public Node leaveVarNode(final VarNode varNode) { public Node leaveVarNode(final VarNode varNode) {
if (varNode.isStatement()) { if (varNode.isStatement()) {
final IdentNode ident = varNode.getName(); final IdentNode ident = varNode.getName();
final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR); final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body;
functionNode.addDeclaredSymbol(symbol); final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
if (varNode.isFunctionDeclaration()) { if (varNode.isFunctionDeclaration()) {
symbol.setIsFunctionDeclaration(); symbol.setIsFunctionDeclaration();
} }
@ -303,23 +305,31 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return functionNode.setBody(lc, body.setStatements(lc, newStatements)); 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. * Defines a new symbol in the given block.
* *
* @param block the block in which to define the symbol * @param block the block in which to define the symbol
* @param name name of symbol. * @param name name of symbol.
* @param origin origin node
* @param symbolFlags Symbol flags. * @param symbolFlags Symbol flags.
* *
* @return Symbol for given name or null for redefinition. * @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; int flags = symbolFlags;
Symbol symbol = findSymbol(block, name); // Locate symbol. final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0;
final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL; 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. // Global variables are implicitly always scope variables too.
if (isGlobal) { if (isGlobal) {
@ -333,7 +343,6 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
final boolean isParam = (flags & KINDMASK) == IS_PARAM; final boolean isParam = (flags & KINDMASK) == IS_PARAM;
final boolean isVar = (flags & KINDMASK) == IS_VAR; final boolean isVar = (flags & KINDMASK) == IS_VAR;
final FunctionNode function = lc.getFunction(block);
if (symbol != null) { if (symbol != null) {
// Symbol was already defined. Check if it needs to be redefined. // Symbol was already defined. Check if it needs to be redefined.
if (isParam) { if (isParam) {
@ -345,10 +354,21 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
throw new AssertionError("duplicate parameter"); throw new AssertionError("duplicate parameter");
} }
} else if (isVar) { } 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. // Always create a new definition.
symbol = null; symbol = null;
} else { } 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. // Not defined in this function. Create a new definition.
if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
symbol = null; symbol = null;
@ -359,10 +379,10 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
if (symbol == null) { if (symbol == null) {
// If not found, then create a new one. // If not found, then create a new one.
Block symbolBlock; final Block symbolBlock;
// Determine where to create it. // 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 symbolBlock = block; //internal vars are always defined in the block closest to them
} else if (isGlobal) { } else if (isGlobal) {
symbolBlock = lc.getOutermostFunction().getBody(); symbolBlock = lc.getOutermostFunction().getBody();
@ -420,9 +440,9 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
@Override @Override
public boolean enterBlock(final Block block) { public boolean enterBlock(final Block block) {
start(block); start(block);
block.clearSymbols();
if (lc.isFunctionBody()) { if (lc.isFunctionBody()) {
block.clearSymbols();
enterFunctionBody(); enterFunctionBody();
} }
@ -441,7 +461,10 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
// If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its // 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. // symbol is naturally internal, and should be treated as such.
final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName()); 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; return true;
} }
@ -452,15 +475,13 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
initFunctionWideVariables(functionNode, body); initFunctionWideVariables(functionNode, body);
if (functionNode.isProgram()) { if (!functionNode.isProgram() && !functionNode.isDeclared() && !functionNode.isAnonymous()) {
initGlobalSymbols(body);
} else if (!functionNode.isDeclared() && !functionNode.isAnonymous()) {
// It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's // It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's
// anonymous. // anonymous.
final String name = functionNode.getIdent().getName(); final String name = functionNode.getIdent().getName();
assert name != null; assert name != null;
assert body.getExistingSymbol(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 if(functionNode.allVarsInScope()) { // basically, has deep eval
lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL); lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
} }
@ -485,7 +506,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
if (functionNode.isDeclared()) { if (functionNode.isDeclared()) {
final Iterator<Block> blocks = lc.getBlocks(); final Iterator<Block> blocks = lc.getBlocks();
if (blocks.hasNext()) { 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 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
@Override @Override
public boolean enterVarNode(final VarNode varNode) { public boolean enterVarNode(final VarNode varNode) {
start(varNode); start(varNode);
defineSymbol(lc.getCurrentBlock(), varNode.getName().getName(), IS_VAR | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0));
return true; 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() { private Symbol exceptionSymbol() {
return newObjectInternal(EXCEPTION_PREFIX); return newObjectInternal(EXCEPTION_PREFIX);
} }
@ -597,7 +625,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
} }
private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) { 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) { private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
@ -608,7 +636,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE); initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
if (functionNode.needsArguments()) { if (functionNode.needsArguments()) {
initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE); 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 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL); 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. * Initialize parameters for function node.
* @param functionNode the function node * @param functionNode the function node
@ -639,7 +653,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
final boolean isVarArg = functionNode.isVarArg(); final boolean isVarArg = functionNode.isVarArg();
final boolean scopeParams = functionNode.allVarsInScope() || isVarArg; final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
for (final IdentNode param : functionNode.getParameters()) { 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) { if(scopeParams) {
// NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored. // 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 // It will force creation of scopes where they would otherwise not necessarily be needed (functions
@ -665,10 +679,29 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return definingFn == function; return definingFn == function;
} }
@Override private void checkConstAssignment(final IdentNode ident) {
public Node leaveASSIGN(final BinaryNode binaryNode) { // Check for reassignment of constant
// If we're assigning a property of the this object ("this.foo = ..."), record it. final Symbol symbol = ident.getSymbol();
if (symbol.isConst()) {
throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident);
}
}
@Override
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(); final Expression lhs = binaryNode.lhs();
if (lhs instanceof AccessNode) { if (lhs instanceof AccessNode) {
final AccessNode accessNode = (AccessNode) lhs; final AccessNode accessNode = (AccessNode) lhs;
@ -683,6 +716,21 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return binaryNode; return binaryNode;
} }
@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 @Override
public Node leaveBlock(final Block block) { 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 // It's not necessary to guard the marking of symbols as locals with this "if"condition for correctness, it's
@ -699,8 +747,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return block; return block;
} }
@Override private Node leaveDELETE(final UnaryNode unaryNode) {
public Node leaveDELETE(final UnaryNode unaryNode) {
final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final FunctionNode currentFunctionNode = lc.getCurrentFunction();
final boolean strictMode = currentFunctionNode.isStrict(); final boolean strictMode = currentFunctionNode.isStrict();
final Expression rhs = unaryNode.getExpression(); final Expression rhs = unaryNode.getExpression();
@ -799,9 +846,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
// if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) // 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); maybeForceScope(symbol);
} else { } else {
log.info("No symbol exists. Declare as global: ", symbol); log.info("No symbol exists. Declare as global: ", name);
symbol = defineGlobalSymbol(block, name); symbol = defineSymbol(block, name, identNode, IS_GLOBAL | IS_SCOPE);
Symbol.setSymbolIsScope(lc, symbol);
} }
functionUsesSymbol(symbol); functionUsesSymbol(symbol);
@ -810,7 +856,15 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
symbol.increaseUseCount(); 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 @Override
@ -834,8 +888,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return tryNode; return tryNode;
} }
@Override private Node leaveTYPEOF(final UnaryNode unaryNode) {
public Node leaveTYPEOF(final UnaryNode unaryNode) {
final Expression rhs = unaryNode.getExpression(); final Expression rhs = unaryNode.getExpression();
final List<Expression> args = new ArrayList<>(); final List<Expression> args = new ArrayList<>();
@ -875,7 +928,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
} }
private Symbol newInternal(final CompilerConstants cc, final int flags) { 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) { private Symbol newObjectInternal(final CompilerConstants cc) {
@ -915,7 +968,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return false; return false;
} }
if (lc.getCurrentFunction().allVarsInScope()) { final FunctionNode func = lc.getCurrentFunction();
if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) {
return true; return true;
} }
@ -955,4 +1009,16 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits();
return !(units == null || units.isEmpty()); 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);
}
} }

View file

@ -52,6 +52,7 @@ import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 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_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_FAST_SCOPE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
@ -302,6 +303,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @return the method generator used * @return the method generator used
*/ */
private MethodEmitter loadIdent(final IdentNode identNode, final TypeBounds resultBounds) { private MethodEmitter loadIdent(final IdentNode identNode, final TypeBounds resultBounds) {
checkTemporalDeadZone(identNode);
final Symbol symbol = identNode.getSymbol(); final Symbol symbol = identNode.getSymbol();
if (!symbol.isScope()) { if (!symbol.isScope()) {
@ -334,6 +336,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return method; 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() { private boolean isRestOf() {
return continuationEntryPoints != null; return continuationEntryPoints != null;
} }
@ -3216,27 +3227,34 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return false; return false;
} }
final Expression init = varNode.getInit(); final Expression init = varNode.getInit();
final IdentNode identNode = varNode.getName();
final Symbol identSymbol = identNode.getSymbol();
assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol";
final boolean needsScope = identSymbol.isScope();
if (init == null) { if (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; return false;
} }
enterStatement(varNode); enterStatement(varNode);
final IdentNode identNode = varNode.getName();
final Symbol identSymbol = identNode.getSymbol();
assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol";
assert method != null; assert method != null;
final boolean needsScope = identSymbol.isScope();
if (needsScope) { if (needsScope) {
method.loadCompilerConstant(SCOPE); method.loadCompilerConstant(SCOPE);
} }
if (needsScope) { if (needsScope) {
loadExpressionUnbounded(init); 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)) { if (isFastScope(identSymbol)) {
storeFastScopeVar(identSymbol, flags); storeFastScopeVar(identSymbol, flags);
} else { } else {
@ -4343,6 +4361,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
protected abstract void evaluate(); protected abstract void evaluate();
void store() { void store() {
if (target instanceof IdentNode) {
checkTemporalDeadZone((IdentNode)target);
}
prologue(); prologue();
evaluate(); // leaves an operation of whatever the operationType was on the stack evaluate(); // leaves an operation of whatever the operationType was on the stack
storeNonDiscard(); storeNonDiscard();

View file

@ -59,7 +59,9 @@ import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator; import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.FunctionInitializer; import jdk.nashorn.internal.runtime.FunctionInitializer;
import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptObject;
@ -89,6 +91,8 @@ public final class Compiler implements Loggable {
private final String sourceName; private final String sourceName;
private final ErrorManager errors;
private final boolean optimistic; private final boolean optimistic;
private final Map<String, byte[]> bytecode; private final Map<String, byte[]> bytecode;
@ -311,6 +315,7 @@ public final class Compiler implements Loggable {
* @param env script environment * @param env script environment
* @param installer code installer * @param installer code installer
* @param source source to compile * @param source source to compile
* @param errors error manager
* @param isStrict is this a strict compilation * @param isStrict is this a strict compilation
*/ */
public Compiler( public Compiler(
@ -318,8 +323,9 @@ public final class Compiler implements Loggable {
final ScriptEnvironment env, final ScriptEnvironment env,
final CodeInstaller<ScriptEnvironment> installer, final CodeInstaller<ScriptEnvironment> installer,
final Source source, final Source source,
final ErrorManager errors,
final boolean isStrict) { 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 @@ public final class Compiler implements Loggable {
* @param env script environment * @param env script environment
* @param installer code installer * @param installer code installer
* @param source source to compile * @param source source to compile
* @param errors error manager
* @param isStrict is this a strict compilation * @param isStrict is this a strict compilation
* @param isOnDemand is this an on demand compilation * @param isOnDemand is this an on demand compilation
* @param compiledFunction compiled function, if any * @param compiledFunction compiled function, if any
@ -343,6 +350,7 @@ public final class Compiler implements Loggable {
final ScriptEnvironment env, final ScriptEnvironment env,
final CodeInstaller<ScriptEnvironment> installer, final CodeInstaller<ScriptEnvironment> installer,
final Source source, final Source source,
final ErrorManager errors,
final boolean isStrict, final boolean isStrict,
final boolean isOnDemand, final boolean isOnDemand,
final RecompilableScriptFunctionData compiledFunction, final RecompilableScriptFunctionData compiledFunction,
@ -359,6 +367,7 @@ public final class Compiler implements Loggable {
this.bytecode = new LinkedHashMap<>(); this.bytecode = new LinkedHashMap<>();
this.log = initLogger(context); this.log = initLogger(context);
this.source = source; this.source = source;
this.errors = errors;
this.sourceName = FunctionNode.getSourceName(source); this.sourceName = FunctionNode.getSourceName(source);
this.onDemand = isOnDemand; this.onDemand = isOnDemand;
this.compiledFunction = compiledFunction; this.compiledFunction = compiledFunction;
@ -524,7 +533,17 @@ public final class Compiler implements Loggable {
for (final CompilationPhase phase : phases) { for (final CompilationPhase phase : phases) {
log.fine(phase, " starting for ", quote(name)); 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)); log.fine(phase, " done for function ", quote(name));
if (env._print_mem_usage) { if (env._print_mem_usage) {

View file

@ -69,9 +69,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
* Constructor * Constructor
* *
* @param codegen code generator * @param codegen code generator
* @param keys keys for fields in object * @param tuples tuples for fields in object
* @param symbols symbols for fields in object
* @param values list of values corresponding to keys
*/ */
FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples) { FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples) {
this(codegen, tuples, false, false); this(codegen, tuples, false, false);
@ -81,9 +79,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
* Constructor * Constructor
* *
* @param codegen code generator * @param codegen code generator
* @param keys keys for fields in object * @param tuples tuples 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 isScope is this a scope object * @param isScope is this a scope object
* @param hasArguments does the created object have an "arguments" property * @param hasArguments does the created object have an "arguments" property
*/ */
@ -165,7 +161,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
* @param method Script method. * @param method Script method.
* @param key Property key. * @param key Property key.
* @param fieldIndex Field number. * @param fieldIndex Field number.
* @param value Value to store. * @param tuple Tuple to store.
*/ */
private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) { private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) {
method.dup(); method.dup();
@ -188,7 +184,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
* *
* @param method Script method. * @param method Script method.
* @param index Slot index. * @param index Slot index.
* @param value Value to store. * @param tuple Tuple to store.
*/ */
private void putSlot(final MethodEmitter method, final long index, final MapTuple<T> tuple) { private void putSlot(final MethodEmitter method, final long index, final MapTuple<T> tuple) {
method.dup(); method.dup();

View file

@ -52,8 +52,7 @@ public class MapCreator<T> {
* Constructor * Constructor
* *
* @param structure structure to generate map for (a JO subclass) * @param structure structure to generate map for (a JO subclass)
* @param keys list of keys for map * @param tuples list of tuples for map
* @param symbols list of symbols for map
*/ */
MapCreator(final Class<? extends ScriptObject> structure, final List<MapTuple<T>> tuples) { MapCreator(final Class<? extends ScriptObject> structure, final List<MapTuple<T>> tuples) {
this.structure = structure; this.structure = structure;
@ -149,6 +148,15 @@ public class MapCreator<T> {
flags |= Property.IS_FUNCTION_DECLARATION; 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; return flags;
} }
} }

View file

@ -2233,9 +2233,8 @@ public class MethodEmitter implements Emitter {
/** /**
* Generate dynamic setter. Pop receiver and property from stack. * Generate dynamic setter. Pop receiver and property from stack.
* *
* @param valueType the type of the value to set * @param name name of property
* @param name name of property * @param flags call site flags
* @param flags call site flags
*/ */
void dynamicSet(final String name, final int flags) { void dynamicSet(final String name, final int flags) {
assert !isOptimistic(flags); assert !isOptimistic(flags);
@ -2462,7 +2461,6 @@ public class MethodEmitter implements Emitter {
* Register line number at a label * Register line number at a label
* *
* @param line line number * @param line line number
* @param label label
*/ */
void lineNumber(final int line) { void lineNumber(final int line) {
if (context.getEnv()._debug_lines) { if (context.getEnv()._debug_lines) {

View file

@ -108,7 +108,7 @@ final class TypeEvaluator {
// Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed // Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed
// integer). // integer).
final Object value = property.getObjectValue(owner, owner); final Object value = property.needsDeclaration() ? ScriptRuntime.UNDEFINED : property.getObjectValue(owner, owner);
if (value == ScriptRuntime.UNDEFINED) { if (value == ScriptRuntime.UNDEFINED) {
return null; return null;
} }

View file

@ -138,10 +138,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Last token of function. **/ /** Last token of function. **/
private final long lastToken; private final long lastToken;
/** Declared symbols in this function node */
@Ignore
private final Set<Symbol> declaredSymbols;
/** Method's namespace. */ /** Method's namespace. */
private final Namespace namespace; private final Namespace namespace;
@ -330,7 +326,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.lastToken = token; this.lastToken = token;
this.namespace = namespace; this.namespace = namespace;
this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
this.declaredSymbols = new HashSet<>();
this.flags = flags; this.flags = flags;
this.compileUnit = null; this.compileUnit = null;
this.body = null; this.body = null;
@ -369,7 +364,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.id = functionNode.id; this.id = functionNode.id;
this.ident = functionNode.ident; this.ident = functionNode.ident;
this.namespace = functionNode.namespace; this.namespace = functionNode.namespace;
this.declaredSymbols = functionNode.declaredSymbols;
this.kind = functionNode.kind; this.kind = functionNode.kind;
this.firstToken = functionNode.firstToken; this.firstToken = functionNode.firstToken;
} }
@ -723,24 +717,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return ident; return ident;
} }
/**
* Return a set of symbols declared in this function node. This
* is only relevant after Attr, otherwise it will be an empty
* set as no symbols have been introduced
* @return set of declared symbols in function
*/
public Set<Symbol> getDeclaredSymbols() {
return Collections.unmodifiableSet(declaredSymbols);
}
/**
* Add a declared symbol to this function node
* @param symbol symbol that is declared
*/
public void addDeclaredSymbol(final Symbol symbol) {
declaredSymbols.add(symbol);
}
/** /**
* Get the function body * Get the function body
* @return the function body * @return the function body
@ -970,13 +946,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
} }
/** /**
* 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. * functions having with and/or eval blocks are such.
* *
* @return true if all variables should be in scope * @return true if all variables should be in scope
*/ */
public boolean allVarsInScope() { public boolean allVarsInScope() {
return isProgram() || getFlag(HAS_ALL_VARS_IN_SCOPE); return getFlag(HAS_ALL_VARS_IN_SCOPE);
} }
/** /**

View file

@ -46,6 +46,8 @@ public final class IdentNode extends Expression implements PropertyKey, Function
private static final int INITIALIZED_HERE = 1 << 1; private static final int INITIALIZED_HERE = 1 << 1;
private static final int FUNCTION = 1 << 2; private static final int FUNCTION = 1 << 2;
private static final int FUTURESTRICT_NAME = 1 << 3; 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. */ /** Identifier. */
private final String name; private final String name;
@ -246,6 +248,45 @@ public final class IdentNode extends Expression implements PropertyKey, Function
return new IdentNode(this, name, type, flags | INITIALIZED_HERE, programPoint, conversion); return new IdentNode(this, name, type, flags | INITIALIZED_HERE, programPoint, conversion);
} }
/**
* 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 * Check if the name of this IdentNode is same as that of a compile-time property (currently __DIR__, __FILE__, and
* __LINE__). * __LINE__).

View file

@ -54,17 +54,17 @@ public final class Symbol implements Comparable<Symbol> {
public static final int IS_VAR = 2; public static final int IS_VAR = 2;
/** Is this a parameter */ /** Is this a parameter */
public static final int IS_PARAM = 3; public static final int IS_PARAM = 3;
/** Is this a constant */
public static final int IS_CONSTANT = 4;
/** Mask for kind flags */ /** 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 */ /** 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 */ /** 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 */ /** 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 */ /** Is this an internal symbol, never represented explicitly in source code */
public static final int IS_INTERNAL = 1 << 6; public static final int IS_INTERNAL = 1 << 6;
/** Is this a function self-reference symbol */ /** Is this a function self-reference symbol */
@ -83,6 +83,8 @@ public final class Symbol implements Comparable<Symbol> {
public static final int HAS_DOUBLE_VALUE = 1 << 13; public static final int HAS_DOUBLE_VALUE = 1 << 13;
/** Is this symbol known to store an object value ? */ /** Is this symbol known to store an object value ? */
public static final int HAS_OBJECT_VALUE = 1 << 14; 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. */ /** Null or name identifying symbol. */
private final String name; private final String name;
@ -184,14 +186,17 @@ public final class Symbol implements Comparable<Symbol> {
sb.append(" global"); sb.append(" global");
break; break;
case IS_VAR: case IS_VAR:
sb.append(" var"); if (isConst()) {
sb.append(" const");
} else if (isLet()) {
sb.append(" let");
} else {
sb.append(" var");
}
break; break;
case IS_PARAM: case IS_PARAM:
sb.append(" param"); sb.append(" param");
break; break;
case IS_CONSTANT:
sb.append(" const");
break;
default: default:
break; break;
} }
@ -204,10 +209,6 @@ public final class Symbol implements Comparable<Symbol> {
sb.append(" internal"); sb.append(" internal");
} }
if (isLet()) {
sb.append(" let");
}
if (isThis()) { if (isThis()) {
sb.append(" this"); sb.append(" this");
} }
@ -410,8 +411,8 @@ public final class Symbol implements Comparable<Symbol> {
* Check if this symbol is a constant * Check if this symbol is a constant
* @return true if a constant * @return true if a constant
*/ */
public boolean isConstant() { public boolean isConst() {
return (flags & KINDMASK) == IS_CONSTANT; return (flags & IS_CONST) != 0;
} }
/** /**
@ -439,15 +440,6 @@ public final class Symbol implements Comparable<Symbol> {
return (flags & IS_LET) != 0; return (flags & IS_LET) != 0;
} }
/**
* Flag this symbol as a let
*/
public void setIsLet() {
if (!isLet()) {
flags |= IS_LET;
}
}
/** /**
* Flag this symbol as a function's self-referencing symbol. * Flag this symbol as a function's self-referencing symbol.
* @return true if 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 @@ public final class Symbol implements Comparable<Symbol> {
return (flags & IS_FUNCTION_SELF) != 0; 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 * 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. * and get allocated in a JO-prefixed ScriptObject subclass.

View file

@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
/** /**
* Node represents a var/let declaration. * Node represents a var/let declaration.
@ -43,12 +44,18 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
private final int flags; private final int flags;
/** Flag that determines if this function node is a statement */ /** 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 /** 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 * This is used to micro optimize the placement of return value assignments for
* a program node */ * a program node */
public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 1; public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 3;
/** /**
* Constructor * Constructor
@ -108,6 +115,43 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
return isAssignment() ? getInit() : null; return isAssignment() ? getInit() : null;
} }
/**
* 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 * Does this variable declaration have an init value
* @return true if an init exists, false otherwise * @return true if an init exists, false otherwise
@ -139,7 +183,7 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
@Override @Override
public void toString(final StringBuilder sb, final boolean printType) { public void toString(final StringBuilder sb, final boolean printType) {
sb.append("var "); sb.append(Token.descType(getToken()).getName()).append(' ');
name.toString(sb, printType); name.toString(sb, printType);
if (init != null) { if (init != null) {

View file

@ -45,6 +45,7 @@ import static jdk.nashorn.internal.parser.TokenType.IDENT;
import static jdk.nashorn.internal.parser.TokenType.IF; import static jdk.nashorn.internal.parser.TokenType.IF;
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
import static jdk.nashorn.internal.parser.TokenType.LBRACE; 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.LPAREN;
import static jdk.nashorn.internal.parser.TokenType.RBRACE; import static jdk.nashorn.internal.parser.TokenType.RBRACE;
import static jdk.nashorn.internal.parser.TokenType.RBRACKET; import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
@ -579,6 +580,10 @@ loop:
} }
} }
private boolean useBlockScope() {
return env._es6;
}
private static boolean isArguments(final String name) { private static boolean isArguments(final String name) {
return ARGUMENTS_NAME.equals(name); return ARGUMENTS_NAME.equals(name);
} }
@ -694,9 +699,20 @@ loop:
FunctionNode.Kind.SCRIPT, FunctionNode.Kind.SCRIPT,
functionLine); 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<>(); 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; functionDeclarations = null;
expect(EOF); expect(EOF);
@ -868,7 +884,7 @@ loop:
block(); block();
break; break;
case VAR: case VAR:
variableStatement(true); variableStatement(type, true);
break; break;
case SEMICOLON: case SEMICOLON:
emptyStatement(); emptyStatement();
@ -918,8 +934,12 @@ loop:
expect(SEMICOLON); expect(SEMICOLON);
break; break;
default: default:
if (useBlockScope() && (type == LET || type == CONST)) {
variableStatement(type, true);
break;
}
if (env._const_as_var && type == CONST) { if (env._const_as_var && type == CONST) {
variableStatement(true); variableStatement(TokenType.VAR, true);
break; break;
} }
@ -1035,11 +1055,17 @@ loop:
* Parse a VAR statement. * Parse a VAR statement.
* @param isStatement True if a statement (not used in a FOR.) * @param isStatement True if a statement (not used in a FOR.)
*/ */
private List<VarNode> variableStatement(final boolean isStatement) { private List<VarNode> variableStatement(final TokenType varType, final boolean isStatement) {
// VAR tested in caller. // VAR tested in caller.
next(); next();
final List<VarNode> vars = new ArrayList<>(); final List<VarNode> vars = new ArrayList<>();
int varFlags = VarNode.IS_STATEMENT;
if (varType == LET) {
varFlags |= VarNode.IS_LET;
} else if (varType == CONST) {
varFlags |= VarNode.IS_CONST;
}
while (true) { while (true) {
// Get starting token. // Get starting token.
@ -1063,10 +1089,12 @@ loop:
} finally { } finally {
defaultNames.pop(); defaultNames.pop();
} }
} else if (varType == CONST) {
throw error(AbstractParser.message("missing.const.assignment", name.getName()));
} }
// Allocate var node. // 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); vars.add(var);
appendStatement(var); appendStatement(var);
@ -1180,9 +1208,12 @@ loop:
* Parse a FOR statement. * Parse a FOR statement.
*/ */
private void forStatement() { 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. // Create FOR node, capturing FOR token.
ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR); ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR);
lc.push(forNode); lc.push(forNode);
try { try {
@ -1203,14 +1234,19 @@ loop:
switch (type) { switch (type) {
case VAR: case VAR:
// Var statements captured in for outer block. // Var statements captured in for outer block.
vars = variableStatement(false); vars = variableStatement(type, false);
break; break;
case SEMICOLON: case SEMICOLON:
break; break;
default: 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) { if (env._const_as_var && type == CONST) {
// Var statements captured in for outer block. // Var statements captured in for outer block.
vars = variableStatement(false); vars = variableStatement(TokenType.VAR, false);
break; break;
} }
@ -1290,8 +1326,13 @@ loop:
appendStatement(forNode); appendStatement(forNode);
} finally { } finally {
lc.pop(forNode); lc.pop(forNode);
if (outer != null) {
outer.setFinish(forNode.getFinish());
outer = restoreBlock(outer);
appendStatement(new BlockStatement(startLine, outer));
}
} }
} }
/** /**
* ... IterationStatement : * ... IterationStatement :
@ -1722,7 +1763,7 @@ loop:
} }
} }
/** /**
* ThrowStatement : * ThrowStatement :
* throw Expression ; // [no LineTerminator here] * throw Expression ; // [no LineTerminator here]
* *
@ -2609,7 +2650,7 @@ loop:
FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine); FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine);
if (isStatement) { if (isStatement) {
if (topLevel) { if (topLevel || useBlockScope()) {
functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED); functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED);
} else if (isStrictMode) { } else if (isStrictMode) {
throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken); throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken);
@ -2661,9 +2702,16 @@ loop:
} }
if (isStatement) { 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) { if (topLevel) {
functionDeclarations.add(varNode); functionDeclarations.add(varNode);
} else if (useBlockScope()) {
prependStatement(varNode); // Hoist to beginning of current block
} else { } else {
appendStatement(varNode); appendStatement(varNode);
} }
@ -2838,7 +2886,6 @@ loop:
} }
private void addFunctionDeclarations(final FunctionNode functionNode) { private void addFunctionDeclarations(final FunctionNode functionNode) {
assert lc.peek() == lc.getFunctionBody(functionNode);
VarNode lastDecl = null; VarNode lastDecl = null;
for (int i = functionDeclarations.size() - 1; i >= 0; i--) { for (int i = functionDeclarations.size() - 1; i >= 0; i--) {
Statement decl = functionDeclarations.get(i); Statement decl = functionDeclarations.get(i);

View file

@ -549,6 +549,8 @@ public class AccessorProperty extends Property {
type == Object.class : type == Object.class :
"invalid getter type " + type + " for " + getKey(); "invalid getter type " + type + " for " + getKey();
checkUndeclared();
//all this does is add a return value filter for object fields only //all this does is add a return value filter for object fields only
final MethodHandle[] getterCache = GETTER_CACHE; final MethodHandle[] getterCache = GETTER_CACHE;
final MethodHandle cachedGetter = getterCache[i]; final MethodHandle cachedGetter = getterCache[i];
@ -579,6 +581,8 @@ public class AccessorProperty extends Property {
return getOptimisticPrimitiveGetter(type, programPoint); return getOptimisticPrimitiveGetter(type, programPoint);
} }
checkUndeclared();
return debug( return debug(
createGetter( createGetter(
getCurrentType(), getCurrentType(),
@ -608,6 +612,13 @@ public class AccessorProperty extends Property {
return newMap; 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 // the final three arguments are for debug printout purposes only
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static Object replaceMap(final Object sobj, final PropertyMap newMap) { private static Object replaceMap(final Object sobj, final PropertyMap newMap) {
@ -635,13 +646,14 @@ public class AccessorProperty extends Property {
@Override @Override
public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
final int i = getAccessorTypeIndex(type); checkUndeclared();
final int ci = isUndefined() ? -1 : getAccessorTypeIndex(getCurrentType());
final Class<?> forType = isUndefined() ? type : getCurrentType(); 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 //if we are asking for an object setter, but are still a primitive type, we might try to box it
MethodHandle mh; MethodHandle mh;
if (needsInvalidator(i, ci)) { if (needsInvalidator(typeIndex, currentTypeIndex)) {
final Property newProperty = getWiderProperty(type); final Property newProperty = getWiderProperty(type);
final PropertyMap newMap = getWiderMap(currentMap, newProperty); final PropertyMap newMap = getWiderMap(currentMap, newProperty);
@ -652,6 +664,7 @@ public class AccessorProperty extends Property {
mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh);
} }
} else { } else {
final Class<?> forType = isUndefined() ? type : getCurrentType();
mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
} }
@ -692,11 +705,12 @@ public class AccessorProperty extends Property {
if (OBJECT_FIELDS_ONLY) { if (OBJECT_FIELDS_ONLY) {
return false; 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) { private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) {
return canChangeType() && ti > fti; return canChangeType() && typeIndex > currentTypeIndex;
} }
@Override @Override

View file

@ -1132,7 +1132,7 @@ public final class Context {
if (storedScript == null) { if (storedScript == null) {
functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
if (errors.hasErrors()) { if (errMan.hasErrors()) {
return null; return null;
} }
@ -1162,9 +1162,13 @@ public final class Context {
env, env,
installer, installer,
source, source,
errMan,
strict | functionNode.isStrict()); strict | functionNode.isStrict());
final FunctionNode compiledFunction = compiler.compile(functionNode, phases); final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
if (errMan.hasErrors()) {
return null;
}
script = compiledFunction.getRootClass(); script = compiledFunction.getRootClass();
compiler.persistClassInfo(cacheKey, compiledFunction); compiler.persistClassInfo(cacheKey, compiledFunction);
} else { } else {

View file

@ -57,6 +57,18 @@ public final class FindProperty {
this.property = property; this.property = property;
} }
/**
* 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 * 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 * internal representation of the property. It may be an Object (boxing primitives) or

View file

@ -82,11 +82,14 @@ public abstract class Property implements Serializable {
* is narrower than object, e.g. Math.PI which is declared * is narrower than object, e.g. Math.PI which is declared
* as a double * 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 /** 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. */ * 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. */ /** Property key. */
private final String key; private final String key;
@ -286,6 +289,15 @@ public abstract class Property implements Serializable {
return (flags & IS_BOUND) == IS_BOUND; return (flags & IS_BOUND) == IS_BOUND;
} }
/**
* 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, * 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 * so any property change that results in a larger flag set results in the

View file

@ -394,6 +394,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
context.getEnv(), context.getEnv(),
installer, installer,
functionNode.getSource(), // source functionNode.getSource(), // source
context.getErrorManager(),
isStrict() | functionNode.isStrict(), // is strict isStrict() | functionNode.isStrict(), // is strict
true, // is on demand true, // is on demand
this, // compiledFunction, i.e. this RecompilableScriptFunctionData this, // compiledFunction, i.e. this RecompilableScriptFunctionData

View file

@ -94,6 +94,9 @@ public final class ScriptEnvironment {
/** Use single Global instance per jsr223 engine instance. */ /** Use single Global instance per jsr223 engine instance. */
public final boolean _global_per_engine; 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 */ /** Argument passed to compile only if optimistic compilation should take place */
public static final String COMPILE_ONLY_OPTIMISTIC_ARG = "optimistic"; public static final String COMPILE_ONLY_OPTIMISTIC_ARG = "optimistic";
@ -258,6 +261,15 @@ public final class ScriptEnvironment {
_version = options.getBoolean("version"); _version = options.getBoolean("version");
_verify_code = options.getBoolean("verify.code"); _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 dir = null;
String func = null; String func = null;
final String pc = options.getString("print.code"); final String pc = options.getString("print.code");

View file

@ -158,6 +158,7 @@ public abstract class ScriptObject implements PropertyAccess {
static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class); 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 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 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); private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class);
@ -2027,6 +2028,22 @@ public abstract class ScriptObject implements PropertyAccess {
return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); 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. * Find the appropriate GETINDEX method for an invoke dynamic call.
* *
@ -2140,7 +2157,7 @@ public abstract class ScriptObject implements PropertyAccess {
} }
if (find != null) { if (find != null) {
if (!find.getProperty().isWritable()) { if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) {
// Existing, non-writable property // Existing, non-writable property
return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
} }

View file

@ -107,6 +107,11 @@ public final class ScriptRuntime {
*/ */
public static final Call APPLY = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "apply", Object.class, ScriptFunction.class, Object.class, Object[].class); 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. * Converts a switch tag value to a simple integer. deflt value if it can't.
* *
@ -381,6 +386,15 @@ public final class ScriptRuntime {
} }
} }
/**
* 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. * Call a script function as a constructor with given args.
* *

View file

@ -140,7 +140,29 @@ final class SetMethodCreator {
private SetMethod createExistingPropertySetter() { private SetMethod createExistingPropertySetter() {
final Property property = find.getProperty(); 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 methodHandle != null;
assert property != null; assert property != null;

View file

@ -54,23 +54,25 @@ public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor
public static final int CALLSITE_OPTIMISTIC = 1 << 3; public static final int CALLSITE_OPTIMISTIC = 1 << 3;
/** Is this really an apply that we try to call as a call? */ /** Is this really an apply that we try to call as a call? */
public static final int CALLSITE_APPLY_TO_CALL = 1 << 4; 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 /** 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. */ * 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 /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
* call sites have this flag set. */ * 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 /** 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. */ * {@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 /** 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. */ * {@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 /** 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 * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
* have this flag set. */ * 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 //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
//right now given the program points //right now given the program points
@ -82,10 +84,10 @@ public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor
* TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
* trace/profile settings. * 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; public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
@ -123,6 +125,9 @@ public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor
assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope"; assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope";
sb.append("scope "); sb.append("scope ");
} }
if ((flags & CALLSITE_DECLARE) != 0) {
sb.append("declare ");
}
} }
if ((flags & CALLSITE_APPLY_TO_CALL) != 0) { if ((flags & CALLSITE_APPLY_TO_CALL) != 0) {
sb.append("apply2call "); sb.append("apply2call ");
@ -328,6 +333,15 @@ public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor
return isFlag(desc, CALLSITE_OPTIMISTIC); return isFlag(desc, CALLSITE_OPTIMISTIC);
} }
/**
* 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) * Get a program point from a descriptor (must be optimistic)
* @param desc descriptor * @param desc descriptor

View file

@ -58,6 +58,7 @@ parser.error.regex.unsupported.flag=Unsupported RegExp flag: {0}
parser.error.regex.repeated.flag=Repeated RegExp flag: {0} parser.error.regex.repeated.flag=Repeated RegExp flag: {0}
parser.error.regex.syntax={0} parser.error.regex.syntax={0}
parser.error.trailing.comma.in.json=Trailing comma is not allowed in JSON 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 # strict mode error messages
parser.error.strict.no.with="with" statement cannot be used in strict mode parser.error.strict.no.with="with" statement cannot be used in strict mode
@ -162,6 +163,8 @@ reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side
syntax.error.invalid.json=Invalid JSON: {0} syntax.error.invalid.json=Invalid JSON: {0}
syntax.error.strict.cant.delete=cannot delete "{0}" in strict mode 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}" io.error.cant.write=cannot write "{0}"
config.error.no.dest=no destination directory supplied config.error.no.dest=no destination directory supplied

View file

@ -329,6 +329,14 @@ nashorn.option.scripting = { \
desc="Enable scripting features." \ desc="Enable scripting features." \
} }
nashorn.option.language = { \
name="--language", \
type=String, \
params=[es5|es6], \
default=es5, \
desc="Specify ECMAScript language version." \
}
nashorn.option.stdout = { \ nashorn.option.stdout = { \
name="--stdout", \ name="--stdout", \
is_undocumented=true, \ is_undocumented=true, \

View file

@ -252,6 +252,15 @@ public class Shell {
return COMPILATION_ERROR; 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) { if (env._print_ast) {
context.getErr().println(new ASTWriter(functionNode)); context.getErr().println(new ASTWriter(functionNode));
} }
@ -260,14 +269,9 @@ public class Shell {
context.getErr().println(new PrintVisitor(functionNode)); context.getErr().println(new PrintVisitor(functionNode));
} }
//null - pass no code installer - this is compile only if (errors.getNumberOfErrors() != 0) {
new Compiler( return COMPILATION_ERROR;
context, }
env,
null,
functionNode.getSource(),
env._strict | functionNode.isStrict()).
compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL);
} }
} finally { } finally {
env.getOut().flush(); env.getOut().flush();

View file

@ -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);
}

View file

@ -0,0 +1,10 @@
function f() {
print("in f");
}
in f
function f() {
print("in f");
}
in f
undefined
ReferenceError: "f" is not defined

View file

@ -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);
}

View file

@ -0,0 +1,3 @@
SyntaxError: test/script/basic/es6/const-empty.js#33:4<eval>@1:2:7 Missing assignment to constant "x"
const x;
^

View file

@ -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);
}

View file

@ -0,0 +1,16 @@
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError

View file

@ -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);
}

View file

@ -0,0 +1,3 @@
SyntaxError: test/script/basic/es6/const-redeclare.js#33:4<eval>@1:2:6 Variable "x" has already been declared
const x = 2;
^

View file

@ -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);
}

View file

@ -0,0 +1,2 @@
1 1
ReferenceError: "a" is not defined

View file

@ -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);
}
}

View file

@ -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

View file

@ -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();

View file

@ -0,0 +1,10 @@
2 2
5 10
42 43
5 10
5 10
5 10
5 10
5 10
2 2
2 2

View file

@ -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);
}

View file

@ -0,0 +1,11 @@
0
1
2
3
4
5
6
7
8
9
ReferenceError: "i" is not defined

View file

@ -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);

View file

@ -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

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}

View file

@ -0,0 +1,4 @@
2
ReferenceError: "x" is not defined
2
ReferenceError: "x" is not defined

View file

@ -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);
}

View file

@ -0,0 +1,3 @@
SyntaxError: test/script/basic/es6/let-redeclare.js#33:4<eval>@1:2:4 Variable "x" has already been declared
let x = 2;
^

View file

@ -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);
}

View file

@ -0,0 +1,2 @@
undefined undefined
ReferenceError: "a" is not defined

View file

@ -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);
}
}

View file

@ -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

View file

@ -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();

View file

@ -0,0 +1,10 @@
2 2
5 10
42 43
5 10
5 10
5 10
5 10
5 10
2 2
2 2

View file

@ -120,7 +120,7 @@ var getEnvMethod = Context.class.getMethod("getEnv")
var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class) 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 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 // compile(script) -- compiles a script specified as a string with its
// source code, returns a jdk.nashorn.internal.ir.FunctionNode object // source code, returns a jdk.nashorn.internal.ir.FunctionNode object
@ -134,7 +134,7 @@ function compile(source, phases) {
var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance()); var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance());
var func = parseMethod.invoke(parser); 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); return compileMethod.invoke(compiler, func, phases);
}; };

View file

@ -98,11 +98,16 @@ public class CompilerTest {
compileTestSet(new File(TEST262_SUITE_DIR), new TestFilter() { compileTestSet(new File(TEST262_SUITE_DIR), new TestFilter() {
@Override @Override
public boolean exclude(final File file, final String content) { 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, "node"), null);
compileTestSet(new File(TEST_NODE_DIR, "src"), null); compileTestSet(new File(TEST_NODE_DIR, "src"), null);
} }
@ -136,6 +141,9 @@ public class CompilerTest {
private int skipped; private int skipped;
private void compileJSDirectory(final File dir, final TestFilter filter) { private void compileJSDirectory(final File dir, final TestFilter filter) {
if (filter != null && filter.exclude(dir, null)) {
return;
}
for (final File f : dir.listFiles()) { for (final File f : dir.listFiles()) {
if (f.isDirectory()) { if (f.isDirectory()) {
compileJSDirectory(f, filter); compileJSDirectory(f, filter);

View file

@ -82,11 +82,16 @@ public class ParserTest {
parseTestSet(TEST262_SUITE_DIR, new TestFilter() { parseTestSet(TEST262_SUITE_DIR, new TestFilter() {
@Override @Override
public boolean exclude(final File file, final String content) { 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) { private void parseTestSet(final String testSet, final TestFilter filter) {
@ -120,6 +125,9 @@ public class ParserTest {
private int skipped; private int skipped;
private void parseJSDirectory(final File dir, final TestFilter filter) { private void parseJSDirectory(final File dir, final TestFilter filter) {
if (filter != null && filter.exclude(dir, null)) {
return;
}
for (final File f : dir.listFiles()) { for (final File f : dir.listFiles()) {
if (f.isDirectory()) { if (f.isDirectory()) {
parseJSDirectory(f, filter); parseJSDirectory(f, filter);