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

@ -341,6 +341,13 @@ grant codeBase "file:/${basedir}/test/script/basic/parser/*" {
permission java.util.PropertyPermission "nashorn.test.*", "read";
};
grant codeBase "file:/${basedir}/test/script/basic/es6/*" {
permission java.io.FilePermission "${basedir}/test/script/-", "read";
permission java.io.FilePermission "$${user.dir}", "read";
permission java.util.PropertyPermission "user.dir", "read";
permission java.util.PropertyPermission "nashorn.test.*", "read";
};
grant codeBase "file:/${basedir}/test/script/basic/JDK-8010946-privileged.js" {
permission java.util.PropertyPermission "java.security.policy", "read";
};

@ -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.VARARGS;
import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE;
import static jdk.nashorn.internal.ir.Symbol.IS_CONST;
import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
@ -83,11 +84,13 @@ import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
@ -101,7 +104,7 @@ import jdk.nashorn.internal.runtime.logging.Logger;
* visitor.
*/
@Logger(name="symbols")
final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements Loggable {
final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable {
private final DebugLogger log;
private final boolean debug;
@ -190,8 +193,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
* @param body the body of the FunctionNode we are entering
*/
private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
// This visitor will assign symbol to all declared variables, except function declarations (which are taken care
// in a separate step above) and "var" declarations in for loop initializers.
// This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers.
//
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
@ -204,8 +206,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
public Node leaveVarNode(final VarNode varNode) {
if (varNode.isStatement()) {
final IdentNode ident = varNode.getName();
final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR);
functionNode.addDeclaredSymbol(symbol);
final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body;
final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
if (varNode.isFunctionDeclaration()) {
symbol.setIsFunctionDeclaration();
}
@ -303,23 +305,31 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return functionNode.setBody(lc, body.setStatements(lc, newStatements));
}
private Symbol defineGlobalSymbol(final Block block, final String name) {
return defineSymbol(block, name, IS_GLOBAL);
}
/**
* Defines a new symbol in the given block.
*
* @param block the block in which to define the symbol
* @param name name of symbol.
* @param origin origin node
* @param symbolFlags Symbol flags.
*
* @return Symbol for given name or null for redefinition.
*/
private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) {
int flags = symbolFlags;
Symbol symbol = findSymbol(block, name); // Locate symbol.
final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL;
final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0;
final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL;
Symbol symbol;
final FunctionNode function;
if (isBlockScope) {
// block scoped variables always live in current block, no need to look for existing symbols in parent blocks.
symbol = block.getExistingSymbol(name);
function = lc.getCurrentFunction();
} else {
symbol = findSymbol(block, name);
function = lc.getFunction(block);
}
// Global variables are implicitly always scope variables too.
if (isGlobal) {
@ -333,7 +343,6 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
final boolean isParam = (flags & KINDMASK) == IS_PARAM;
final boolean isVar = (flags & KINDMASK) == IS_VAR;
final FunctionNode function = lc.getFunction(block);
if (symbol != null) {
// Symbol was already defined. Check if it needs to be redefined.
if (isParam) {
@ -345,10 +354,21 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
throw new AssertionError("duplicate parameter");
}
} else if (isVar) {
if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
if (isBlockScope) {
// Check redeclaration in same block
if (symbol.hasBeenDeclared()) {
throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
} else {
symbol.setHasBeenDeclared();
}
} else if ((flags & IS_INTERNAL) != 0) {
// Always create a new definition.
symbol = null;
} else {
// Found LET or CONST in parent scope of same function - s SyntaxError
if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) {
throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
}
// Not defined in this function. Create a new definition.
if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
symbol = null;
@ -359,10 +379,10 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
if (symbol == null) {
// If not found, then create a new one.
Block symbolBlock;
final Block symbolBlock;
// Determine where to create it.
if (isVar && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) {
symbolBlock = block; //internal vars are always defined in the block closest to them
} else if (isGlobal) {
symbolBlock = lc.getOutermostFunction().getBody();
@ -420,9 +440,9 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
@Override
public boolean enterBlock(final Block block) {
start(block);
block.clearSymbols();
if (lc.isFunctionBody()) {
block.clearSymbols();
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
// symbol is naturally internal, and should be treated as such.
final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
defineSymbol(block, exname, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
// IS_LET flag is required to make sure symbol is not visible outside catch block. However, we need to
// clear the IS_LET flag after creation to allow redefinition of symbol inside the catch block.
final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
symbol.clearFlag(IS_LET);
return true;
}
@ -452,15 +475,13 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
initFunctionWideVariables(functionNode, body);
if (functionNode.isProgram()) {
initGlobalSymbols(body);
} else if (!functionNode.isDeclared() && !functionNode.isAnonymous()) {
if (!functionNode.isProgram() && !functionNode.isDeclared() && !functionNode.isAnonymous()) {
// It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's
// anonymous.
final String name = functionNode.getIdent().getName();
assert name != null;
assert body.getExistingSymbol(name) == null;
defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
if(functionNode.allVarsInScope()) { // basically, has deep eval
lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
}
@ -485,7 +506,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
if (functionNode.isDeclared()) {
final Iterator<Block> blocks = lc.getBlocks();
if (blocks.hasNext()) {
defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR | (functionNode.isAnonymous()? IS_INTERNAL : 0));
final IdentNode ident = functionNode.getIdent();
defineSymbol(blocks.next(), ident.getName(), ident, IS_VAR | (functionNode.isAnonymous()? IS_INTERNAL : 0));
}
}
@ -495,10 +517,16 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
@Override
public boolean enterVarNode(final VarNode varNode) {
start(varNode);
defineSymbol(lc.getCurrentBlock(), varNode.getName().getName(), IS_VAR | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0));
return true;
}
@Override
public Node leaveVarNode(final VarNode varNode) {
final IdentNode ident = varNode.getName();
defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0));
return super.leaveVarNode(varNode);
}
private Symbol exceptionSymbol() {
return newObjectInternal(EXCEPTION_PREFIX);
}
@ -597,7 +625,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
}
private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
defineSymbol(block, cc.symbolName(), flags).setNeedsSlot(true);
defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true);
}
private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
@ -608,7 +636,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
if (functionNode.needsArguments()) {
initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
defineSymbol(body, ARGUMENTS_VAR.symbolName(), IS_VAR | HAS_OBJECT_VALUE);
defineSymbol(body, ARGUMENTS_VAR.symbolName(), null, IS_VAR | HAS_OBJECT_VALUE);
}
}
@ -617,20 +645,6 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
}
/**
* Move any properties from the global map into the scope of this function (which must be a program function).
* @param block the function node body for which to init scope vars
*/
private void initGlobalSymbols(final Block block) {
final PropertyMap map = Context.getGlobalMap();
for (final Property property : map.getProperties()) {
final Symbol symbol = defineGlobalSymbol(block, property.getKey());
log.info("Added global symbol from property map ", symbol);
}
}
/**
* Initialize parameters for function node.
* @param functionNode the function node
@ -639,7 +653,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
final boolean isVarArg = functionNode.isVarArg();
final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
for (final IdentNode param : functionNode.getParameters()) {
final Symbol symbol = defineSymbol(body, param.getName(), IS_PARAM);
final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM);
if(scopeParams) {
// NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored.
// It will force creation of scopes where they would otherwise not necessarily be needed (functions
@ -665,10 +679,29 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return definingFn == function;
}
@Override
public Node leaveASSIGN(final BinaryNode binaryNode) {
// If we're assigning a property of the this object ("this.foo = ..."), record it.
private void checkConstAssignment(final IdentNode ident) {
// Check for reassignment of constant
final Symbol symbol = ident.getSymbol();
if (symbol.isConst()) {
throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident);
}
}
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
if (binaryNode.isAssignment() && binaryNode.lhs() instanceof IdentNode) {
checkConstAssignment((IdentNode) binaryNode.lhs());
}
switch (binaryNode.tokenType()) {
case ASSIGN:
return leaveASSIGN(binaryNode);
default:
return super.leaveBinaryNode(binaryNode);
}
}
private Node leaveASSIGN(final BinaryNode binaryNode) {
// If we're assigning a property of the this object ("this.foo = ..."), record it.
final Expression lhs = binaryNode.lhs();
if (lhs instanceof AccessNode) {
final AccessNode accessNode = (AccessNode) lhs;
@ -683,6 +716,21 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
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
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
@ -699,8 +747,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return block;
}
@Override
public Node leaveDELETE(final UnaryNode unaryNode) {
private Node leaveDELETE(final UnaryNode unaryNode) {
final FunctionNode currentFunctionNode = lc.getCurrentFunction();
final boolean strictMode = currentFunctionNode.isStrict();
final Expression rhs = unaryNode.getExpression();
@ -799,9 +846,8 @@ 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)
maybeForceScope(symbol);
} else {
log.info("No symbol exists. Declare as global: ", symbol);
symbol = defineGlobalSymbol(block, name);
Symbol.setSymbolIsScope(lc, symbol);
log.info("No symbol exists. Declare as global: ", name);
symbol = defineSymbol(block, name, identNode, IS_GLOBAL | IS_SCOPE);
}
functionUsesSymbol(symbol);
@ -810,7 +856,15 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
symbol.increaseUseCount();
}
return end(identNode.setSymbol(symbol));
IdentNode newIdentNode = identNode.setSymbol(symbol);
// If a block-scoped var is used before its declaration mark it as dead.
// We can only statically detect this for local vars, cross-function symbols require runtime checks.
if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
newIdentNode = newIdentNode.markDead();
}
return end(newIdentNode);
}
@Override
@ -834,8 +888,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return tryNode;
}
@Override
public Node leaveTYPEOF(final UnaryNode unaryNode) {
private Node leaveTYPEOF(final UnaryNode unaryNode) {
final Expression rhs = unaryNode.getExpression();
final List<Expression> args = new ArrayList<>();
@ -875,7 +928,7 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
}
private Symbol newInternal(final CompilerConstants cc, final int flags) {
return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), IS_VAR | IS_INTERNAL | flags); //NASHORN-73
return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73
}
private Symbol newObjectInternal(final CompilerConstants cc) {
@ -915,7 +968,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
return false;
}
if (lc.getCurrentFunction().allVarsInScope()) {
final FunctionNode func = lc.getCurrentFunction();
if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) {
return true;
}
@ -955,4 +1009,16 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits();
return !(units == null || units.isEmpty());
}
private void throwParserException(final String message, final Node origin) {
if (origin == null) {
throw new ParserException(message);
}
final Source source = compiler.getSource();
final long token = origin.getToken();
final int line = source.getLine(origin.getStart());
final int column = source.getColumn(origin.getStart());
final String formatted = ErrorManager.format(message, source, line, column, token);
throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token);
}
}

@ -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.isValid;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_APPLY_TO_CALL;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_DECLARE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
@ -302,6 +303,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @return the method generator used
*/
private MethodEmitter loadIdent(final IdentNode identNode, final TypeBounds resultBounds) {
checkTemporalDeadZone(identNode);
final Symbol symbol = identNode.getSymbol();
if (!symbol.isScope()) {
@ -334,6 +336,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return method;
}
// Any access to LET and CONST variables before their declaration must throw ReferenceError.
// This is called the temporal dead zone (TDZ). See https://gist.github.com/rwaldron/f0807a758aa03bcdd58a
private void checkTemporalDeadZone(final IdentNode identNode) {
if (identNode.isDead()) {
method.load(identNode.getSymbol().getName());
method.invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
}
}
private boolean isRestOf() {
return continuationEntryPoints != null;
}
@ -3216,27 +3227,34 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return false;
}
final Expression init = varNode.getInit();
final IdentNode identNode = varNode.getName();
final Symbol identSymbol = identNode.getSymbol();
assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol";
final boolean needsScope = identSymbol.isScope();
if (init == null) {
if (needsScope && varNode.isBlockScoped()) {
// block scoped variables need a DECLARE flag to signal end of temporal dead zone (TDZ)
method.loadCompilerConstant(SCOPE);
method.loadUndefined(Type.OBJECT);
final int flags = CALLSITE_SCOPE | getCallSiteFlags() | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0);
assert isFastScope(identSymbol);
storeFastScopeVar(identSymbol, flags);
}
return false;
}
enterStatement(varNode);
final IdentNode identNode = varNode.getName();
final Symbol identSymbol = identNode.getSymbol();
assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol";
assert method != null;
final boolean needsScope = identSymbol.isScope();
if (needsScope) {
method.loadCompilerConstant(SCOPE);
}
if (needsScope) {
loadExpressionUnbounded(init);
final int flags = CALLSITE_SCOPE | getCallSiteFlags();
// block scoped variables need a DECLARE flag to signal end of temporal dead zone (TDZ)
final int flags = CALLSITE_SCOPE | getCallSiteFlags() | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0);
if (isFastScope(identSymbol)) {
storeFastScopeVar(identSymbol, flags);
} else {
@ -4343,6 +4361,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
protected abstract void evaluate();
void store() {
if (target instanceof IdentNode) {
checkTemporalDeadZone((IdentNode)target);
}
prologue();
evaluate(); // leaves an operation of whatever the operationType was on the stack
storeNonDiscard();

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

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

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

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

@ -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
// integer).
final Object value = property.getObjectValue(owner, owner);
final Object value = property.needsDeclaration() ? ScriptRuntime.UNDEFINED : property.getObjectValue(owner, owner);
if (value == ScriptRuntime.UNDEFINED) {
return null;
}

@ -138,10 +138,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Last token of function. **/
private final long lastToken;
/** Declared symbols in this function node */
@Ignore
private final Set<Symbol> declaredSymbols;
/** Method's namespace. */
private final Namespace namespace;
@ -330,7 +326,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.lastToken = token;
this.namespace = namespace;
this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
this.declaredSymbols = new HashSet<>();
this.flags = flags;
this.compileUnit = null;
this.body = null;
@ -369,7 +364,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.id = functionNode.id;
this.ident = functionNode.ident;
this.namespace = functionNode.namespace;
this.declaredSymbols = functionNode.declaredSymbols;
this.kind = functionNode.kind;
this.firstToken = functionNode.firstToken;
}
@ -723,24 +717,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
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
* @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.
*
* @return true if all variables should be in scope
*/
public boolean allVarsInScope() {
return isProgram() || getFlag(HAS_ALL_VARS_IN_SCOPE);
return getFlag(HAS_ALL_VARS_IN_SCOPE);
}
/**

@ -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 FUNCTION = 1 << 2;
private static final int FUTURESTRICT_NAME = 1 << 3;
private static final int IS_DECLARED_HERE = 1 << 4;
private static final int IS_DEAD = 1 << 5;
/** Identifier. */
private final String name;
@ -246,6 +248,45 @@ public final class IdentNode extends Expression implements PropertyKey, Function
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
* __LINE__).

@ -54,17 +54,17 @@ public final class Symbol implements Comparable<Symbol> {
public static final int IS_VAR = 2;
/** Is this a parameter */
public static final int IS_PARAM = 3;
/** Is this a constant */
public static final int IS_CONSTANT = 4;
/** Mask for kind flags */
public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
public static final int KINDMASK = (1 << 2) - 1; // Kinds are represented by lower two bits
/** Is this symbol in scope */
public static final int IS_SCOPE = 1 << 3;
public static final int IS_SCOPE = 1 << 2;
/** Is this a this symbol */
public static final int IS_THIS = 1 << 4;
public static final int IS_THIS = 1 << 3;
/** Is this a let */
public static final int IS_LET = 1 << 5;
public static final int IS_LET = 1 << 4;
/** Is this a const */
public static final int IS_CONST = 1 << 5;
/** Is this an internal symbol, never represented explicitly in source code */
public static final int IS_INTERNAL = 1 << 6;
/** Is this a function self-reference symbol */
@ -83,6 +83,8 @@ public final class Symbol implements Comparable<Symbol> {
public static final int HAS_DOUBLE_VALUE = 1 << 13;
/** Is this symbol known to store an object value ? */
public static final int HAS_OBJECT_VALUE = 1 << 14;
/** Is this symbol seen a declaration? Used for block scoped LET and CONST symbols only. */
public static final int HAS_BEEN_DECLARED = 1 << 15;
/** Null or name identifying symbol. */
private final String name;
@ -184,14 +186,17 @@ public final class Symbol implements Comparable<Symbol> {
sb.append(" global");
break;
case IS_VAR:
sb.append(" var");
if (isConst()) {
sb.append(" const");
} else if (isLet()) {
sb.append(" let");
} else {
sb.append(" var");
}
break;
case IS_PARAM:
sb.append(" param");
break;
case IS_CONSTANT:
sb.append(" const");
break;
default:
break;
}
@ -204,10 +209,6 @@ public final class Symbol implements Comparable<Symbol> {
sb.append(" internal");
}
if (isLet()) {
sb.append(" let");
}
if (isThis()) {
sb.append(" this");
}
@ -410,8 +411,8 @@ public final class Symbol implements Comparable<Symbol> {
* Check if this symbol is a constant
* @return true if a constant
*/
public boolean isConstant() {
return (flags & KINDMASK) == IS_CONSTANT;
public boolean isConst() {
return (flags & IS_CONST) != 0;
}
/**
@ -439,15 +440,6 @@ public final class Symbol implements Comparable<Symbol> {
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.
* @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;
}
public boolean isBlockScoped() {
return isLet() || isConst();
}
public boolean hasBeenDeclared() {
return (flags & HAS_BEEN_DECLARED) != 0;
}
public void setHasBeenDeclared() {
if (!hasBeenDeclared()) {
flags |= HAS_BEEN_DECLARED;
}
}
/**
* Get the index of the field used to store this symbol, should it be an AccessorProperty
* and get allocated in a JO-prefixed ScriptObject subclass.

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

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

@ -549,6 +549,8 @@ public class AccessorProperty extends Property {
type == Object.class :
"invalid getter type " + type + " for " + getKey();
checkUndeclared();
//all this does is add a return value filter for object fields only
final MethodHandle[] getterCache = GETTER_CACHE;
final MethodHandle cachedGetter = getterCache[i];
@ -579,6 +581,8 @@ public class AccessorProperty extends Property {
return getOptimisticPrimitiveGetter(type, programPoint);
}
checkUndeclared();
return debug(
createGetter(
getCurrentType(),
@ -608,6 +612,13 @@ public class AccessorProperty extends Property {
return newMap;
}
private void checkUndeclared() {
if ((getFlags() & NEEDS_DECLARATION) != 0) {
// a lexically defined variable that hasn't seen its declaration - throw ReferenceError
throw ECMAErrors.referenceError("not.defined", getKey());
}
}
// the final three arguments are for debug printout purposes only
@SuppressWarnings("unused")
private static Object replaceMap(final Object sobj, final PropertyMap newMap) {
@ -635,13 +646,14 @@ public class AccessorProperty extends Property {
@Override
public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
final int i = getAccessorTypeIndex(type);
final int ci = isUndefined() ? -1 : getAccessorTypeIndex(getCurrentType());
final Class<?> forType = isUndefined() ? type : getCurrentType();
checkUndeclared();
final int typeIndex = getAccessorTypeIndex(type);
final int currentTypeIndex = getAccessorTypeIndex(getCurrentType());
//if we are asking for an object setter, but are still a primitive type, we might try to box it
MethodHandle mh;
if (needsInvalidator(i, ci)) {
if (needsInvalidator(typeIndex, currentTypeIndex)) {
final Property newProperty = getWiderProperty(type);
final PropertyMap newMap = getWiderMap(currentMap, newProperty);
@ -652,6 +664,7 @@ public class AccessorProperty extends Property {
mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh);
}
} else {
final Class<?> forType = isUndefined() ? type : getCurrentType();
mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
}
@ -692,11 +705,12 @@ public class AccessorProperty extends Property {
if (OBJECT_FIELDS_ONLY) {
return false;
}
return getCurrentType() != Object.class && (isConfigurable() || isWritable());
// Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST.
return getCurrentType() == null || (getCurrentType() != Object.class && (isConfigurable() || isWritable()));
}
private boolean needsInvalidator(final int ti, final int fti) {
return canChangeType() && ti > fti;
private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) {
return canChangeType() && typeIndex > currentTypeIndex;
}
@Override

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

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

@ -82,11 +82,14 @@ public abstract class Property implements Serializable {
* is narrower than object, e.g. Math.PI which is declared
* as a double
*/
public static final int IS_NASGEN_PRIMITIVE = 1 << 6;
public static final int IS_NASGEN_PRIMITIVE = 1 << 6;
/** Is this property bound to a receiver? This means get/set operations will be delegated to
* a statically defined object instead of the object passed as callsite parameter. */
public static final int IS_BOUND = 1 << 8;
public static final int IS_BOUND = 1 << 7;
/** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */
public static final int NEEDS_DECLARATION = 1 << 8;
/** Property key. */
private final String key;
@ -286,6 +289,15 @@ public abstract class Property implements Serializable {
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,
* so any property change that results in a larger flag set results in the

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

@ -94,6 +94,9 @@ public final class ScriptEnvironment {
/** Use single Global instance per jsr223 engine instance. */
public final boolean _global_per_engine;
/** Enable experimental ECMAScript 6 features. */
public final boolean _es6;
/** Argument passed to compile only if optimistic compilation should take place */
public static final String COMPILE_ONLY_OPTIMISTIC_ARG = "optimistic";
@ -258,6 +261,15 @@ public final class ScriptEnvironment {
_version = options.getBoolean("version");
_verify_code = options.getBoolean("verify.code");
final String language = options.getString("language");
if (language == null || language.equals("es5")) {
_es6 = false;
} else if (language.equals("es6")) {
_es6 = true;
} else {
throw new RuntimeException("Unsupported language: " + language);
}
String dir = null;
String func = null;
final String pc = options.getString("print.code");

@ -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 GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class);
static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class);
private static final MethodHandle TRUNCATINGFILTER = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class);
private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class);
@ -2027,6 +2028,22 @@ public abstract class ScriptObject implements PropertyAccess {
return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
}
// Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST
@SuppressWarnings("unused")
private void declareAndSet(final String key, final Object value) {
final PropertyMap map = getMap();
final FindProperty find = findProperty(key, false);
assert find != null;
final Property property = find.getProperty();
assert property != null;
assert property.needsDeclaration();
final PropertyMap newMap = map.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
setMap(newMap);
set(key, value, true);
}
/**
* Find the appropriate GETINDEX method for an invoke dynamic call.
*
@ -2140,7 +2157,7 @@ public abstract class ScriptObject implements PropertyAccess {
}
if (find != null) {
if (!find.getProperty().isWritable()) {
if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) {
// Existing, non-writable property
return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
}

@ -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);
/**
* 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.
*
@ -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.
*

@ -140,7 +140,29 @@ final class SetMethodCreator {
private SetMethod createExistingPropertySetter() {
final Property property = find.getProperty();
final MethodHandle methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc));
final MethodHandle methodHandle;
if (NashornCallSiteDescriptor.isDeclaration(desc)) {
assert property.needsDeclaration();
// This is a LET or CONST being declared. The property is already there but flagged as needing declaration.
// We create a new PropertyMap with the flag removed. The map is installed with a fast compare-and-set
// method if the pre-callsite map is stable (which should be the case for function scopes except for
// non-strict functions containing eval() with var). Otherwise we have to use a slow setter that creates
// a new PropertyMap on the fly.
final PropertyMap oldMap = getMap();
final Property newProperty = property.removeFlags(Property.NEEDS_DECLARATION);
final PropertyMap newMap = oldMap.replaceProperty(property, newProperty);
final MethodHandle fastSetter = find.replaceProperty(newProperty).getSetter(type, NashornCallSiteDescriptor.isStrict(desc));
final MethodHandle slowSetter = MH.insertArguments(ScriptObject.DECLARE_AND_SET, 1, getName()).asType(fastSetter.type());
// cas map used as guard, if true that means we can do the set fast
MethodHandle casMap = MH.insertArguments(ScriptObject.CAS_MAP, 1, oldMap, newMap);
casMap = MH.dropArguments(casMap, 1, type);
casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class));
methodHandle = MH.guardWithTest(casMap, fastSetter, slowSetter);
} else {
methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc));
}
assert methodHandle != null;
assert property != null;

@ -54,23 +54,25 @@ public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor
public static final int CALLSITE_OPTIMISTIC = 1 << 3;
/** Is this really an apply that we try to call as a call? */
public static final int CALLSITE_APPLY_TO_CALL = 1 << 4;
/** Does this a callsite for a variable declaration? */
public static final int CALLSITE_DECLARE = 1 << 5;
/** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
* code where call sites have this flag set. */
public static final int CALLSITE_PROFILE = 1 << 5;
public static final int CALLSITE_PROFILE = 1 << 6;
/** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
* call sites have this flag set. */
public static final int CALLSITE_TRACE = 1 << 6;
public static final int CALLSITE_TRACE = 1 << 7;
/** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
* {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
public static final int CALLSITE_TRACE_MISSES = 1 << 7;
public static final int CALLSITE_TRACE_MISSES = 1 << 8;
/** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
* {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 8;
public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 9;
/** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
* that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
* have this flag set. */
public static final int CALLSITE_TRACE_VALUES = 1 << 9;
public static final int CALLSITE_TRACE_VALUES = 1 << 10;
//we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
//right now given the program points
@ -82,10 +84,10 @@ 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
* trace/profile settings.
*/
public static final int CALLSITE_PROGRAM_POINT_SHIFT = 10;
public static final int CALLSITE_PROGRAM_POINT_SHIFT = 11;
/**
* Maximum program point value. 22 bits should be enough for anyone
* Maximum program point value. 21 bits should be enough for anyone
*/
public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
@ -123,6 +125,9 @@ public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor
assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope";
sb.append("scope ");
}
if ((flags & CALLSITE_DECLARE) != 0) {
sb.append("declare ");
}
}
if ((flags & CALLSITE_APPLY_TO_CALL) != 0) {
sb.append("apply2call ");
@ -328,6 +333,15 @@ public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor
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)
* @param desc descriptor

@ -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.syntax={0}
parser.error.trailing.comma.in.json=Trailing comma is not allowed in JSON
parser.error.missing.const.assignment=Missing assignment to constant "{0}"
# strict mode error messages
parser.error.strict.no.with="with" statement cannot be used in strict mode
@ -162,6 +163,8 @@ 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.strict.cant.delete=cannot delete "{0}" in strict mode
syntax.error.redeclare.variable=Variable "{0}" has already been declared
syntax.error.assign.constant=Assignment to constant "{0}"
io.error.cant.write=cannot write "{0}"
config.error.no.dest=no destination directory supplied

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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 ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, boolean.class);
var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, ErrorManager.class, boolean.class);
// compile(script) -- compiles a script specified as a string with its
// source code, returns a jdk.nashorn.internal.ir.FunctionNode object
@ -134,7 +134,7 @@ function compile(source, phases) {
var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance());
var func = parseMethod.invoke(parser);
var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, false);
var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false);
return compileMethod.invoke(compiler, func, phases);
};

@ -98,11 +98,16 @@ public class CompilerTest {
compileTestSet(new File(TEST262_SUITE_DIR), new TestFilter() {
@Override
public boolean exclude(final File file, final String content) {
return content.indexOf("@negative") != -1;
return content != null && content.contains("@negative");
}
});
}
compileTestSet(new File(TEST_BASIC_DIR), null);
compileTestSet(new File(TEST_BASIC_DIR), new TestFilter() {
@Override
public boolean exclude(final File file, final String content) {
return file.getName().equals("es6");
}
});
compileTestSet(new File(TEST_NODE_DIR, "node"), null);
compileTestSet(new File(TEST_NODE_DIR, "src"), null);
}
@ -136,6 +141,9 @@ public class CompilerTest {
private int skipped;
private void compileJSDirectory(final File dir, final TestFilter filter) {
if (filter != null && filter.exclude(dir, null)) {
return;
}
for (final File f : dir.listFiles()) {
if (f.isDirectory()) {
compileJSDirectory(f, filter);

@ -82,11 +82,16 @@ public class ParserTest {
parseTestSet(TEST262_SUITE_DIR, new TestFilter() {
@Override
public boolean exclude(final File file, final String content) {
return content.indexOf("@negative") != -1;
return content != null && content.contains("@negative");
}
});
}
parseTestSet(TEST_BASIC_DIR, null);
parseTestSet(TEST_BASIC_DIR, new TestFilter() {
@Override
public boolean exclude(final File file, final String content) {
return file.getName().equals("es6");
}
});
}
private void parseTestSet(final String testSet, final TestFilter filter) {
@ -120,6 +125,9 @@ public class ParserTest {
private int skipped;
private void parseJSDirectory(final File dir, final TestFilter filter) {
if (filter != null && filter.exclude(dir, null)) {
return;
}
for (final File f : dir.listFiles()) {
if (f.isDirectory()) {
parseJSDirectory(f, filter);