package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
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;
import static jdk.nashorn.internal.ir.Symbol.IS_LET;
import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
import static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL;
import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
import jdk.nashorn.internal.ir.Splittable;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
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.SimpleNodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
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;
@Logger(name="symbols")
final class AssignSymbols extends SimpleNodeVisitor implements Loggable {
private final DebugLogger log;
private final boolean debug;
private static boolean isParamOrVar(final IdentNode identNode) {
final Symbol symbol = identNode.getSymbol();
return symbol.isParam() || symbol.isVar();
}
private static String name(final Node node) {
final String cn = node.getClass().getName();
final int lastDot = cn.lastIndexOf('.');
if (lastDot == -1) {
return cn;
}
return cn.substring(lastDot + 1);
}
private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) {
if (!functionNode.needsCallee()) {
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
if(functionNode.isNamedFunctionExpression() && !functionNode.usesSelfSymbol()) {
final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
if(selfSymbol != null && selfSymbol.isFunctionSelf()) {
selfSymbol.setNeedsSlot(false);
selfSymbol.clearFlag(Symbol.IS_VAR);
}
}
return functionNode;
}
private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
private final Map<String, Symbol> globalSymbols = new HashMap<>();
private final Compiler compiler;
private final boolean isOnDemand;
public AssignSymbols(final Compiler compiler) {
this.compiler = compiler;
this.log = initLogger(compiler.getContext());
this.debug = log.isEnabled();
this.isOnDemand = compiler.isOnDemandCompilation();
}
@Override
public DebugLogger getLogger() {
return log;
}
@Override
public DebugLogger initLogger(final Context context) {
return context.getLogger(this.getClass());
}
private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
body.accept(new SimpleNodeVisitor() {
@Override
protected boolean enterDefault(final Node node) {
return !(node instanceof Expression);
}
@Override
public Node leaveVarNode(final VarNode varNode) {
final IdentNode ident = varNode.getName();
final boolean blockScoped = varNode.isBlockScoped();
if (blockScoped && lc.inUnprotectedSwitchContext()) {
throwUnprotectedSwitchError(varNode);
}
final Block block = blockScoped ? lc.getCurrentBlock() : body;
final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
if (varNode.isFunctionDeclaration()) {
symbol.setIsFunctionDeclaration();
}
return varNode.setName(ident.setSymbol(symbol));
}
});
}
private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
}
private IdentNode createImplicitIdentifier(final String name) {
final FunctionNode fn = lc.getCurrentFunction();
return new IdentNode(fn.getToken(), fn.getFinish(), name);
}
private Symbol createSymbol(final String name, final int flags) {
if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
Symbol global = globalSymbols.get(name);
if (global == null) {
global = new Symbol(name, flags);
globalSymbols.put(name, global);
}
return global;
}
return new Symbol(name, flags);
}
private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
final IdentNode init = compilerConstantIdentifier(initConstant);
assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal();
final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
assert nameSymbol != null;
return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this);
}
private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
final List<VarNode> syntheticInitializers = new ArrayList<>(2);
final Block body = functionNode.getBody();
lc.push(body);
try {
if (functionNode.usesSelfSymbol()) {
syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode));
}
if (functionNode.needsArguments()) {
syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
ARGUMENTS, functionNode));
}
if (syntheticInitializers.isEmpty()) {
return functionNode;
}
for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) {
it.set((VarNode)it.next().accept(this));
}
} finally {
lc.pop(body);
}
final List<Statement> stmts = body.getStatements();
final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
newStatements.addAll(syntheticInitializers);
newStatements.addAll(stmts);
return functionNode.setBody(lc, body.setStatements(lc, newStatements));
}
private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) {
int flags = symbolFlags;
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) {
symbol = block.getExistingSymbol(name);
function = lc.getCurrentFunction();
} else {
symbol = findSymbol(block, name);
function = lc.getFunction(block);
}
if (isGlobal) {
flags |= IS_SCOPE;
}
if (lc.getCurrentFunction().isProgram()) {
flags |= IS_PROGRAM_LEVEL;
}
final boolean isParam = (flags & KINDMASK) == IS_PARAM;
final boolean isVar = (flags & KINDMASK) == IS_VAR;
if (symbol != null) {
if (isParam) {
if (!isLocal(function, symbol)) {
symbol = null;
} else if (symbol.isParam()) {
throwParserException(ECMAErrors.getMessage("syntax.error.duplicate.parameter", name), origin);
}
} else if (isVar) {
if (isBlockScope) {
if (symbol.hasBeenDeclared()) {
throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
} else {
symbol.setHasBeenDeclared();
if (function.isProgram() && function.getBody() == block) {
symbol.setIsScope();
}
}
} else if ((flags & IS_INTERNAL) != 0) {
symbol = null;
} else {
if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) {
throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
}
if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
symbol = null;
}
}
}
}
if (symbol == null) {
final Block symbolBlock;
if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) {
symbolBlock = block;
} else if (isGlobal) {
symbolBlock = lc.getOutermostFunction().getBody();
} else {
symbolBlock = lc.getFunctionBody(function);
}
symbol = createSymbol(name, flags);
symbolBlock.putSymbol(symbol);
if ((flags & IS_SCOPE) == 0) {
symbol.setNeedsSlot(true);
}
} else if (symbol.less(flags)) {
symbol.setFlags(flags);
}
return symbol;
}
private <T extends Node> T end(final T node) {
return end(node, true);
}
private <T extends Node> T end(final T node, final boolean printNode) {
if (debug) {
final StringBuilder sb = new StringBuilder();
sb.append("[LEAVE ").
append(name(node)).
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
append(lc.getCurrentFunction().getName()).
append('\'');
if (node instanceof IdentNode) {
final Symbol symbol = ((IdentNode)node).getSymbol();
if (symbol == null) {
sb.append(" <NO SYMBOL>");
} else {
sb.append(" <symbol=").append(symbol).append('>');
}
}
log.unindent();
log.info(sb);
}
return node;
}
@Override
public boolean enterBlock(final Block block) {
start(block);
if (lc.isFunctionBody()) {
assert !block.hasSymbols();
final FunctionNode fn = lc.getCurrentFunction();
if (isUnparsedFunction(fn)) {
for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) {
nameIsUsed(name, null);
}
assert block.getStatements().isEmpty();
return false;
}
enterFunctionBody();
}
return true;
}
private boolean isUnparsedFunction(final FunctionNode fn) {
return isOnDemand && fn != lc.getOutermostFunction();
}
@Override
public boolean enterCatchNode(final CatchNode catchNode) {
final IdentNode exception = catchNode.getExceptionIdentifier();
final Block block = lc.getCurrentBlock();
start(catchNode);
final String exname = exception.getName();
final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
symbol.clearFlag(IS_LET);
return true;
}
private void enterFunctionBody() {
final FunctionNode functionNode = lc.getCurrentFunction();
final Block body = lc.getCurrentBlock();
initFunctionWideVariables(functionNode, body);
acceptDeclarations(functionNode, body);
defineFunctionSelfSymbol(functionNode, body);
}
private void defineFunctionSelfSymbol(final FunctionNode functionNode, final Block body) {
if (!functionNode.isNamedFunctionExpression()) {
return;
}
final String name = functionNode.getIdent().getName();
assert name != null;
if (body.getExistingSymbol(name) != null) {
return;
}
defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
if(functionNode.allVarsInScope()) {
lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
}
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
start(functionNode, false);
thisProperties.push(new HashSet<String>());
assert functionNode.getBody() != null;
return true;
}
@Override
public boolean enterVarNode(final VarNode varNode) {
start(varNode);
if (varNode.isFunctionDeclaration()) {
defineVarIdent(varNode);
}
return true;
}
@Override
public Node leaveVarNode(final VarNode varNode) {
if (!varNode.isFunctionDeclaration()) {
defineVarIdent(varNode);
}
return super.leaveVarNode(varNode);
}
private void defineVarIdent(final VarNode varNode) {
final IdentNode ident = varNode.getName();
final int flags;
if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) {
flags = IS_SCOPE;
} else {
flags = 0;
}
defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags);
}
private Symbol exceptionSymbol() {
return newObjectInternal(EXCEPTION_PREFIX);
}
private FunctionNode finalizeParameters(final FunctionNode functionNode) {
final List<IdentNode> newParams = new ArrayList<>();
final boolean isVarArg = functionNode.isVarArg();
final Block body = functionNode.getBody();
for (final IdentNode param : functionNode.getParameters()) {
final Symbol paramSymbol = body.getExistingSymbol(param.getName());
assert paramSymbol != null;
assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
newParams.add(param.setSymbol(paramSymbol));
if (isVarArg) {
paramSymbol.setNeedsSlot(false);
}
}
return functionNode.setParameters(lc, newParams);
}
private Symbol findSymbol(final Block block, final String name) {
for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
final Symbol symbol = blocks.next().getExistingSymbol(name);
if (symbol != null) {
return symbol;
}
}
return null;
}
private void functionUsesGlobalSymbol() {
for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
}
}
private void functionUsesScopeSymbol(final Symbol symbol) {
final String name = symbol.getName();
for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
final LexicalContextNode node = contextNodeIter.next();
if (node instanceof Block) {
final Block block = (Block)node;
if (block.getExistingSymbol(name) != null) {
assert lc.contains(block);
lc.setBlockNeedsScope(block);
break;
}
} else if (node instanceof FunctionNode) {
lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
}
}
}
private void functionUsesSymbol(final Symbol symbol) {
assert symbol != null;
if (symbol.isScope()) {
if (symbol.isGlobal()) {
functionUsesGlobalSymbol();
} else {
functionUsesScopeSymbol(symbol);
}
} else {
assert !symbol.isGlobal();
}
}
private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true);
}
private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE);
if (functionNode.isVarArg()) {
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(), null, IS_VAR | HAS_OBJECT_VALUE);
}
}
initParameters(functionNode, body);
initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
}
private void initParameters(final FunctionNode functionNode, final Block body) {
final boolean isVarArg = functionNode.isVarArg();
final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
for (final IdentNode param : functionNode.getParameters()) {
final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM);
if(scopeParams) {
symbol.setIsScope();
assert symbol.hasSlot();
if(isVarArg) {
symbol.setNeedsSlot(false);
}
}
}
}
private boolean isLocal(final FunctionNode function, final Symbol symbol) {
final FunctionNode definingFn = lc.getDefiningFunction(symbol);
assert definingFn != null;
return definingFn == function;
}
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
if (binaryNode.isTokenType(TokenType.ASSIGN)) {
return leaveASSIGN(binaryNode);
}
return super.leaveBinaryNode(binaryNode);
}
private Node leaveASSIGN(final BinaryNode binaryNode) {
final Expression lhs = binaryNode.lhs();
if (lhs instanceof AccessNode) {
final AccessNode accessNode = (AccessNode) lhs;
final Expression base = accessNode.getBase();
if (base instanceof IdentNode) {
final Symbol symbol = ((IdentNode)base).getSymbol();
if(symbol.isThis()) {
thisProperties.peek().add(accessNode.getProperty());
}
}
}
return binaryNode;
}
@Override
public Node leaveUnaryNode(final UnaryNode unaryNode) {
switch (unaryNode.tokenType()) {
case DELETE:
return leaveDELETE(unaryNode);
case TYPEOF:
return leaveTYPEOF(unaryNode);
default:
return super.leaveUnaryNode(unaryNode);
}
}
private Node leaveDELETE(final UnaryNode unaryNode) {
final FunctionNode currentFunctionNode = lc.getCurrentFunction();
final boolean strictMode = currentFunctionNode.isStrict();
final Expression rhs = unaryNode.getExpression();
final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
Request request = Request.DELETE;
final List<Expression> args = new ArrayList<>();
if (rhs instanceof IdentNode) {
final IdentNode ident = (IdentNode)rhs;
final String name = ident.getName();
final Symbol symbol = ident.getSymbol();
if (symbol.isThis()) {
return LiteralNode.newInstance(unaryNode, true);
}
final Expression literalNode = LiteralNode.newInstance(unaryNode, name);
final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
if (!failDelete) {
args.add(compilerConstantIdentifier(SCOPE));
}
args.add(literalNode);
args.add(strictFlagNode);
if (failDelete) {
request = Request.FAIL_DELETE;
} else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) {
request = Request.SLOW_DELETE;
}
} else if (rhs instanceof AccessNode) {
final Expression base = ((AccessNode)rhs).getBase();
final String property = ((AccessNode)rhs).getProperty();
args.add(base);
args.add(LiteralNode.newInstance(unaryNode, property));
args.add(strictFlagNode);
} else if (rhs instanceof IndexNode) {
final IndexNode indexNode = (IndexNode)rhs;
final Expression base = indexNode.getBase();
final Expression index = indexNode.getIndex();
args.add(base);
args.add(index);
args.add(strictFlagNode);
} else {
return LiteralNode.newInstance(unaryNode, true);
}
return new RuntimeNode(unaryNode, request, args);
}
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForInOrOf()) {
return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX));
}
return end(forNode);
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
final FunctionNode finalizedFunction;
if (isUnparsedFunction(functionNode)) {
finalizedFunction = functionNode;
} else {
finalizedFunction =
markProgramBlock(
removeUnusedSlots(
createSyntheticInitializers(
finalizeParameters(
lc.applyTopFlags(functionNode))))
.setThisProperties(lc, thisProperties.pop().size()));
}
return finalizedFunction;
}
@Override
public Node leaveIdentNode(final IdentNode identNode) {
if (identNode.isPropertyName()) {
return identNode;
}
final Symbol symbol = nameIsUsed(identNode.getName(), identNode);
if (!identNode.isInitializedHere()) {
symbol.increaseUseCount();
}
IdentNode newIdentNode = identNode.setSymbol(symbol);
if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
newIdentNode = newIdentNode.markDead();
}
return end(newIdentNode);
}
private Symbol nameIsUsed(final String name, final IdentNode origin) {
final Block block = lc.getCurrentBlock();
Symbol symbol = findSymbol(block, name);
if (symbol != null) {
log.info("Existing symbol = ", symbol);
if (symbol.isFunctionSelf()) {
final FunctionNode functionNode = lc.getDefiningFunction(symbol);
assert functionNode != null;
assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
}
maybeForceScope(symbol);
} else {
log.info("No symbol exists. Declare as global: ", name);
symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE);
}
functionUsesSymbol(symbol);
return symbol;
}
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
if(!switchNode.isUniqueInteger()) {
return switchNode.setTag(lc, newObjectInternal(SWITCH_TAG_PREFIX));
}
return switchNode;
}
@Override
public Node leaveTryNode(final TryNode tryNode) {
assert tryNode.getFinallyBody() == null;
end(tryNode);
return tryNode.setException(lc, exceptionSymbol());
}
private Node leaveTYPEOF(final UnaryNode unaryNode) {
final Expression rhs = unaryNode.getExpression();
final List<Expression> args = new ArrayList<>();
if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
args.add(compilerConstantIdentifier(SCOPE));
args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()));
} else {
args.add(rhs);
args.add(LiteralNode.newInstance(unaryNode));
}
final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
end(unaryNode);
return runtimeNode;
}
private FunctionNode markProgramBlock(final FunctionNode functionNode) {
if (isOnDemand || !functionNode.isProgram()) {
return functionNode;
}
return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
}
private void maybeForceScope(final Symbol symbol) {
if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
Symbol.setSymbolIsScope(lc, symbol);
}
}
private Symbol newInternal(final CompilerConstants cc, final int flags) {
return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags);
}
private Symbol newObjectInternal(final CompilerConstants cc) {
return newInternal(cc, HAS_OBJECT_VALUE);
}
private boolean start(final Node node) {
return start(node, true);
}
private boolean start(final Node node, final boolean printNode) {
if (debug) {
final StringBuilder sb = new StringBuilder();
sb.append("[ENTER ").
append(name(node)).
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
append(lc.getCurrentFunction().getName()).
append("'");
log.info(sb);
log.indent();
}
return true;
}
private boolean symbolNeedsToBeScope(final Symbol symbol) {
if (symbol.isThis() || symbol.isInternal()) {
return false;
}
final FunctionNode func = lc.getCurrentFunction();
if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) {
return true;
}
boolean previousWasBlock = false;
for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
final LexicalContextNode node = it.next();
if (node instanceof FunctionNode || isSplitLiteral(node)) {
return true;
} else if (node instanceof WithNode) {
if (previousWasBlock) {
return true;
}
previousWasBlock = false;
} else if (node instanceof Block) {
if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
return false;
}
previousWasBlock = true;
} else {
previousWasBlock = false;
}
}
throw new AssertionError();
}
private static boolean isSplitLiteral(final LexicalContextNode expr) {
return expr instanceof Splittable && ((Splittable) expr).getSplitRanges() != null;
}
private void throwUnprotectedSwitchError(final VarNode varNode) {
final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const");
throwParserException(msg, varNode);
}
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);
}
}