private List<IdentNode> convertArrowFunctionParameterList(final Expression paramListExpr, final int functionLine) { final List<IdentNode> parameters; if (paramListExpr == null) { // empty parameter list, i.e. () => parameters = Collections.emptyList(); } else if (paramListExpr instanceof IdentNode || paramListExpr.isTokenType(ASSIGN) || isDestructuringLhs(paramListExpr)) { parameters = Collections.singletonList(verifyArrowParameter(paramListExpr, 0, functionLine)); } else if (paramListExpr instanceof BinaryNode && Token.descType(paramListExpr.getToken()) == COMMARIGHT) { parameters = new ArrayList<>(); Expression car = paramListExpr; do { final Expression cdr = ((BinaryNode) car).rhs(); parameters.add(0, verifyArrowParameter(cdr, parameters.size(), functionLine)); car = ((BinaryNode) car).lhs(); } while (car instanceof BinaryNode && Token.descType(car.getToken()) == COMMARIGHT); parameters.add(0, verifyArrowParameter(car, parameters.size(), functionLine)); } else { throw error(AbstractParser.message("expected.arrow.parameter"), paramListExpr.getToken()); } return parameters; }
/** * Parse a property assignment from the token stream * @return the property assignment as a Node */ private PropertyNode propertyAssignment() { // Capture firstToken. final long propertyToken = token; LiteralNode<?> name = null; if (type == STRING) { name = getStringLiteral(); } else if (type == ESCSTRING) { name = getLiteral(); } if (name != null) { expect(COLON); final Expression value = jsonLiteral(); return new PropertyNode(propertyToken, value.getFinish(), name, value, null, null); } // Raise an error. throw error(AbstractParser.message("expected", "string", type.getNameOrType())); }
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())); //null } else { args.add(rhs); args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); end(unaryNode); return runtimeNode; }
/** * Convert execString to a call to $EXEC. * * @param primaryToken Original string token. * @return callNode to $EXEC. */ CallNode execString(final int primaryLine, final long primaryToken) { // Synthesize an ident to call $EXEC. final IdentNode execIdent = new IdentNode(primaryToken, finish, ScriptingFunctions.EXEC_NAME); // Skip over EXECSTRING. next(); // Set up argument list for call. // Skip beginning of edit string expression. expect(LBRACE); // Add the following expression to arguments. final List<Expression> arguments = Collections.singletonList(expression()); // Skip ending of edit string expression. expect(RBRACE); return new CallNode(primaryLine, primaryToken, finish, execIdent, arguments, false); }
/** * Arguments : * ( ) * ( ArgumentList ) * * ArgumentList : * AssignmentExpression * ArgumentList , AssignmentExpression * * See 11.2 * * Parse function call arguments. * @return Argument list. */ private ArrayList<Expression> argumentList() { // Prepare to accumulate list of arguments. final ArrayList<Expression> nodeList = new ArrayList<>(); // LPAREN tested in caller. next(); // Track commas. boolean first = true; while (type != RPAREN) { // Comma prior to every argument except the first. if (!first) { expect(COMMARIGHT); } else { first = false; } // Get argument expression. nodeList.add(assignmentExpression(false)); } expect(RPAREN); return nodeList; }
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((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null } else { args.add(rhs); args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); end(unaryNode); return runtimeNode; }
private Expression verifyIncDecExpression(final long unaryToken, final TokenType opType, final Expression lhs, final boolean isPostfix) { assert lhs != null; if (!(lhs instanceof AccessNode || lhs instanceof IndexNode || lhs instanceof IdentNode)) { return referenceError(lhs, null, env._early_lvalue_error); } if (lhs instanceof IdentNode) { if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, null, false); } verifyIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); } return incDecExpression(unaryToken, opType, lhs, isPostfix); }
@Override public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { final Expression expr = expressionStatement.getExpression(); ExpressionStatement node = expressionStatement; final FunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction.isProgram()) { if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) { node = expressionStatement.setExpression( new BinaryNode( Token.recast( expressionStatement.getToken(), TokenType.ASSIGN), compilerConstant(RETURN), expr)); } } return addStatement(node); }
@Override public Node leaveForNode(final ForNode forNode) { ForNode newForNode = forNode; final Expression test = forNode.getTest(); if (!forNode.isForIn() && isAlwaysTrue(test)) { newForNode = forNode.setTest(lc, null); } newForNode = checkEscape(newForNode); if(newForNode.isForIn()) { // Wrap it in a block so its internally created iterator is restricted in scope addStatementEnclosedInBlock(newForNode); } else { addStatement(newForNode); } return newForNode; }
private void loadNOT(final UnaryNode unaryNode) { final Expression expr = unaryNode.getExpression(); if(expr instanceof UnaryNode && expr.isTokenType(TokenType.NOT)) { // !!x is idiomatic boolean cast in JavaScript loadExpressionAsBoolean(((UnaryNode)expr).getExpression()); } else { final Label trueLabel = new Label("true"); final Label afterLabel = new Label("after"); emitBranch(expr, trueLabel, true); method.load(true); method._goto(afterLabel); method.label(trueLabel); method.load(false); method.label(afterLabel); } }
/** * Check whether a call node may be a call to eval. In that case we * clone the args in order to create the following construct in * {@link CodeGenerator} * * <pre> * if (calledFuntion == buildInEval) { * eval(cloned arg); * } else { * cloned arg; * } * </pre> * * @param callNode call node to check if it's an eval */ private CallNode checkEval(final CallNode callNode) { if (callNode.getFunction() instanceof IdentNode) { final List<Expression> args = callNode.getArgs(); final IdentNode callee = (IdentNode)callNode.getFunction(); // 'eval' call with at least one argument if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) { final List<Expression> evalArgs = new ArrayList<>(args.size()); for(final Expression arg: args) { evalArgs.add((Expression)ensureUniqueNamesIn(arg).accept(this)); } return callNode.setEvalArgs(new CallNode.EvalArgs(evalArgs, evalLocation(callee))); } } return callNode; }
private Object evaluateSafely(final Expression expr) { if (expr instanceof IdentNode) { return runtimeScope == null ? null : evaluatePropertySafely(runtimeScope, ((IdentNode)expr).getName()); } if (expr instanceof AccessNode) { final AccessNode accessNode = (AccessNode)expr; final Object base = evaluateSafely(accessNode.getBase()); if (!(base instanceof ScriptObject)) { return null; } return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty()); } return null; }
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) { final Expression rhs = unaryNode.getExpression(); switch (unaryNode.tokenType()) { case NOT: branchOptimizer(rhs, label, !state); return; default: if (unaryNode.getType().isBoolean()) { branchOptimizer(rhs, label, state); return; } break; } loadTestAndJump(unaryNode, label, state); }
@Override public boolean enterReturnNode(final ReturnNode returnNode) { if(!reachable) { return false; } final Expression returnExpr = returnNode.getExpression(); final Type returnExprType; if(returnExpr != null) { returnExpr.accept(this); returnExprType = getType(returnExpr); } else { returnExprType = Type.UNDEFINED; } returnType = Type.widestReturnType(returnType, returnExprType); doesNotContinueSequentially(); return false; }
private boolean checkValidLValue(final Expression init, final String contextString) { if (init instanceof IdentNode) { if (!checkIdentLValue((IdentNode)init)) { return false; } verifyIdent((IdentNode)init, contextString); return true; } else if (init instanceof AccessNode || init instanceof IndexNode) { return true; } else if (isDestructuringLhs(init)) { verifyDestructuringAssignmentPattern(init, contextString); return true; } else { return false; } }
@Override public boolean enterReturnNode(final ReturnNode returnNode) { if(!method.isReachable()) { return false; } enterStatement(returnNode); method.registerReturn(); final Type returnType = lc.getCurrentFunction().getReturnType(); final Expression expression = returnNode.getExpression(); if (expression != null) { loadExpressionUnbounded(expression); } else { method.loadUndefined(returnType); } method._return(returnType); return false; }
private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine, final String contextString) { verifyDestructuringBindingPattern(pattern, new Consumer<IdentNode>() { public void accept(final IdentNode identNode) { verifyIdent(identNode, contextString); final ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { // declare function-scope variables for destructuring bindings if (!env._parse_only) { lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); } // detect duplicate bounds names in parameter list currentFunction.addParameterBinding(identNode); currentFunction.setSimpleParameterList(false); } } }); }
private void loadAndDiscard(final Expression expr) { // TODO: move checks for discarding to actual expression load code (e.g. as we do with void). That way we might // be able to eliminate even more checks. if(expr instanceof PrimitiveLiteralNode | isLocalVariable(expr)) { assert lc.getCurrentDiscard() != expr; // Don't bother evaluating expressions without side effects. Typical usage is "void 0" for reliably generating // undefined. return; } lc.pushDiscard(expr); loadExpression(expr, TypeBounds.UNBOUNDED); if (lc.getCurrentDiscard() == expr) { assert !expr.isAssignment(); // NOTE: if we had a way to load with type void, we could avoid popping method.pop(); lc.popDiscard(); } }
private void loadASSIGN(final BinaryNode binaryNode) { final Expression lhs = binaryNode.lhs(); final Expression rhs = binaryNode.rhs(); final Type rhsType = rhs.getType(); // Detect dead assignments if(lhs instanceof IdentNode) { final Symbol symbol = ((IdentNode)lhs).getSymbol(); if(!symbol.isScope() && !symbol.hasSlotFor(rhsType) && lc.getCurrentDiscard() == binaryNode) { loadAndDiscard(rhs); lc.popDiscard(); method.markDeadLocalVariable(symbol); return; } } new Store<BinaryNode>(binaryNode, lhs) { @Override protected void evaluate() { // NOTE: we're loading with "at least as wide as" so optimistic operations on the right hand side // remain optimistic, and then explicitly convert to the required type if needed. loadExpressionAsType(rhs, rhsType); } }.store(); }
@Override protected void evaluate() { final Expression lhs = assignNode.lhs(); final Expression rhs = assignNode.rhs(); final Type widestOperationType = assignNode.getWidestOperationType(); final TypeBounds bounds = new TypeBounds(assignNode.getType(), widestOperationType); new OptimisticOperation(assignNode, bounds) { @Override void loadStack() { final boolean forceConversionSeparation; if (isValid(getProgramPoint()) || widestOperationType == Type.NUMBER) { forceConversionSeparation = false; } else { final Type operandType = Type.widest(booleanToInt(objectToNumber(lhs.getType())), booleanToInt(objectToNumber(rhs.getType()))); forceConversionSeparation = operandType.narrowerThan(widestOperationType); } loadBinaryOperands(lhs, rhs, bounds, true, forceConversionSeparation); } @Override void consumeStack() { op(this); } }.emit(getOptimisticIgnoreCountForSelfModifyingExpression(lhs)); method.convert(assignNode.getType()); }
/** * WithStatement : * with ( Expression ) Statement * * See 12.10 * * Parse WITH statement. */ private void withStatement() { // Capture WITH token. final int withLine = line; final long withToken = token; // WITH tested in caller. next(); // ECMA 12.10.1 strict mode restrictions if (isStrictMode) { throw error(AbstractParser.message("strict.no.with"), withToken); } expect(LPAREN); final Expression expression = expression(); expect(RPAREN); final Block body = getStatement(); appendStatement(new WithNode(withLine, withToken, finish, expression, body)); }
@Override public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) { final Expression expr = joinExpr.getExpression(); if (expr != null) { expr.accept(this); } else { typeStack.push(LvarType.UNDEFINED); } return false; }
/** * YieldStatement : * yield Expression? ; // [no LineTerminator here] * * JavaScript 1.8 * * Parse YIELD statement. */ private void yieldStatement() { // Capture YIELD token. final int yieldLine = line; final long yieldToken = token; // YIELD tested in caller. nextOrEOL(); Expression expression = null; // SEMICOLON or expression. switch (type) { case RBRACE: case SEMICOLON: case EOL: case EOF: break; default: expression = expression(); break; } endOfLine(); // Construct and add YIELD node. appendStatement(new ReturnNode(yieldLine, yieldToken, finish, expression)); }
/** * ReturnStatement : * return Expression? ; // [no LineTerminator here] * * See 12.9 * * Parse RETURN statement. */ private void returnStatement() { // check for return outside function if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT || lc.getCurrentFunction().getKind() == FunctionNode.Kind.MODULE) { throw error(AbstractParser.message("invalid.return")); } // Capture RETURN token. final int returnLine = line; final long returnToken = token; // RETURN tested in caller. nextOrEOL(); Expression expression = null; // SEMICOLON or expression. switch (type) { case RBRACE: case SEMICOLON: case EOL: case EOF: break; default: expression = expression(); break; } endOfLine(); // Construct and add RETURN node. appendStatement(new ReturnNode(returnLine, returnToken, finish, expression)); }
private void addTemplateLiteralString(final ArrayList<Expression> rawStrings, final ArrayList<Expression> cookedStrings) { final long stringToken = token; final String rawString = lexer.valueOfRawString(stringToken); final String cookedString = (String) getValue(); next(); rawStrings.add(LiteralNode.newInstance(stringToken, finish, rawString)); cookedStrings.add(LiteralNode.newInstance(stringToken, finish, cookedString)); }
private static boolean isUniqueIntegerLiteral(final Expression expr, final Set<Integer> alreadySeen) { if (expr instanceof LiteralNode) { final Object value = ((LiteralNode<?>)expr).getValue(); if (value instanceof Integer) { return alreadySeen.add((Integer)value); } } return false; }
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; 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; }
private Expression bindingPattern() { if (type == LBRACKET) { return arrayLiteral(); } else if (type == LBRACE) { return objectLiteral(); } else { throw error(AbstractParser.message("expected.binding")); } }
@Override public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinPredecessorExpression) { final Expression expr = joinPredecessorExpression.getExpression(); if(expr != null) { expr.accept(this); } else { nullValue(); } return false; }
/** * Define symbols for all variable declarations at the top of the function scope. This way we can get around * problems like * * while (true) { * break; * if (true) { * var s; * } * } * * to an arbitrary nesting depth. * * see NASHORN-73 * * @param functionNode the FunctionNode we are entering * @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. body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override protected boolean enterDefault(final Node node) { // Don't bother visiting expressions; var is a statement, it can't be inside an expression. // This will also prevent visiting nested functions (as FunctionNode is an expression). 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)); } }); }
/** * ThrowStatement : * throw Expression ; // [no LineTerminator here] * * See 12.13 * * Parse throw statement. */ private void throwStatement() { // Capture THROW token. final int throwLine = line; final long throwToken = token; // THROW tested in caller. nextOrEOL(); Expression expression = null; // SEMICOLON or expression. switch (type) { case RBRACE: case SEMICOLON: case EOL: break; default: expression = expression(); break; } if (expression == null) { throw error(AbstractParser.message("expected.operand", type.getNameOrType())); } endOfLine(); appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, false)); }
private static String getConstantPropertyName(final Expression expression) { if (expression instanceof LiteralNode.PrimitiveLiteralNode) { final Object value = ((LiteralNode) expression).getValue(); if (value instanceof String && SAFE_PROPERTY_NAME.matcher((String) value).matches()) { return (String) value; } } return null; }
@Override public boolean enterObjectNode(final ObjectNode objectNode) { for(final PropertyNode propertyNode: objectNode.getElements()) { // Avoid falsely adding property keys to the control flow graph final Expression value = propertyNode.getValue(); if (value != null) { visitExpression(value); } } return pushExpressionType(objectNode); }
/** * Check if a property value contains a particular program point * @param value value * @param pp program point * @return true if it's there. */ private static boolean propertyValueContains(final Expression value, final int pp) { return new Supplier<Boolean>() { boolean contains; @Override public Boolean get() { value.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { return false; } @Override public boolean enterDefault(final Node node) { if (contains) { return false; } if (node instanceof Optimistic && ((Optimistic)node).getProgramPoint() == pp) { contains = true; return false; } return true; } }); return contains; } }.get(); }
private static boolean isOptimistic(final Optimistic optimistic) { if(!optimistic.canBeOptimistic()) { return false; } final Expression expr = (Expression)optimistic; return expr.getType().narrowerThan(expr.getWidestOperationType()); }
@Override public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { final Expression expr = expressionStatement.getExpression(); if(!expr.isSelfModifying()) { tagNeverOptimistic(expr); } return true; }
private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine, final int flags) { final boolean computed = type == LBRACKET; final Expression propertyName = propertyName(); final String getterName = propertyName instanceof PropertyKey ? ((PropertyKey) propertyName).getPropertyName() : getDefaultValidFunctionName(functionLine, false); final IdentNode getNameNode = createIdentNode((propertyName).getToken(), finish, NameCodec.encode("get " + getterName)); expect(LPAREN); expect(RPAREN); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getNameNode, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.<IdentNode>emptyList()); functionNode.setFlag(flags); if (computed) { functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } lc.push(functionNode); Block functionBody; try { functionBody = functionBody(functionNode); } finally { lc.pop(functionNode); } final FunctionNode function = createFunctionNode( functionNode, getSetToken, getNameNode, Collections.<IdentNode>emptyList(), FunctionNode.Kind.GETTER, functionLine, functionBody); return new PropertyFunction(propertyName, function, computed); }
@Override public boolean enterTernaryNode(final TernaryNode ternaryNode) { final Expression test = ternaryNode.getTest(); final Expression trueExpr = ternaryNode.getTrueExpression(); final Expression falseExpr = ternaryNode.getFalseExpression(); visitExpression(test); final Map<Symbol, LvarType> testExitLvarTypes = localVariableTypes; final LvarType trueType; if(!isAlwaysFalse(test)) { trueType = visitExpression(trueExpr); } else { trueType = null; } final Map<Symbol, LvarType> trueExitLvarTypes = localVariableTypes; localVariableTypes = testExitLvarTypes; final LvarType falseType; if(!isAlwaysTrue(test)) { falseType = visitExpression(falseExpr); } else { falseType = null; } final Map<Symbol, LvarType> falseExitLvarTypes = localVariableTypes; localVariableTypes = getUnionTypes(trueExitLvarTypes, falseExitLvarTypes); setConversion((JoinPredecessor)trueExpr, trueExitLvarTypes, localVariableTypes); setConversion((JoinPredecessor)falseExpr, falseExitLvarTypes, localVariableTypes); typeStack.push(trueType != null ? falseType != null ? widestLvarType(trueType, falseType) : trueType : assertNotNull(falseType)); return false; }
private void optimizeLogicalOperand(final Expression expr, final Label label, final boolean state, final boolean isRhs) { final JoinPredecessorExpression jpexpr = (JoinPredecessorExpression)expr; if(LocalVariableConversion.hasLiveConversion(jpexpr)) { final Label after = new Label("after"); branchOptimizer(jpexpr.getExpression(), after, !state); method.beforeJoinPoint(jpexpr); method._goto(label); method.label(after); if(isRhs) { method.beforeJoinPoint(jpexpr); } } else { branchOptimizer(jpexpr.getExpression(), label, state); } }