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 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); }
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); } }
private void loadSUB(final UnaryNode unaryNode, final TypeBounds resultBounds) { final Type type = unaryNode.getType(); assert type.isNumeric(); final TypeBounds numericBounds = resultBounds.booleanToInt(); new OptimisticOperation(unaryNode, numericBounds) { @Override void loadStack() { final Expression expr = unaryNode.getExpression(); loadExpression(expr, numericBounds.notWiderThan(Type.NUMBER)); } @Override void consumeStack() { // Must do an explicit conversion to the operation's type when it's double so that we correctly handle // negation of an int 0 to a double -0. With this, we get the correct negation of a local variable after // it deoptimized, e.g. "iload_2; i2d; dneg". Without this, we get "iload_2; ineg; i2d". if(type.isNumber()) { method.convert(type); } method.neg(getProgramPoint()); } }.emit(); }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { if (unaryNode.isTokenType(TokenType.NEW)) { curExpr = new NewTreeImpl(unaryNode, translateExpr(unaryNode.getExpression())); } else if (unaryNode.isTokenType(TokenType.YIELD) || unaryNode.isTokenType(TokenType.YIELD_STAR)) { curExpr = new YieldTreeImpl(unaryNode, translateExpr(unaryNode.getExpression())); } else if (unaryNode.isTokenType(TokenType.SPREAD_ARGUMENT) || unaryNode.isTokenType(TokenType.SPREAD_ARRAY)) { curExpr = new SpreadTreeImpl(unaryNode, translateExpr(unaryNode.getExpression())); } else { curExpr = new UnaryTreeImpl(unaryNode, translateExpr(unaryNode.getExpression())); } return false; }
@Override public boolean enterLiteralNode(final LiteralNode<?> literalNode) { if (literalNode.isArray()) { if (((LiteralNode.ArrayLiteralNode)literalNode).hasSpread() && ((LiteralNode.ArrayLiteralNode)literalNode).hasTrailingComma()) { throw error("Rest element must be last", literalNode.getElementExpressions().get(literalNode.getElementExpressions().size() - 1).getToken()); } boolean restElement = false; for (final Expression element : literalNode.getElementExpressions()) { if (element != null) { if (restElement) { throw error("Unexpected element after rest element", element.getToken()); } if (element.isTokenType(SPREAD_ARRAY)) { restElement = true; final Expression lvalue = ((UnaryNode) element).getExpression(); verifySpreadElement(lvalue); } element.accept(this); } } return false; } else { return enterDefault(literalNode); } }
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; }
/** * NewExpression : * MemberExpression * new NewExpression * * See 11.2 * * Parse new expression. * @return Expression node. */ private Expression newExpression() { final long newToken = token; // NEW is tested in caller. next(); // Get function base. final int callLine = line; final Expression constructor = memberExpression(); if (constructor == null) { return null; } // Get arguments. ArrayList<Expression> arguments; // Allow for missing arguments. if (type == LPAREN) { arguments = argumentList(); } else { arguments = new ArrayList<>(); } // Nashorn extension: This is to support the following interface implementation // syntax: // // var r = new java.lang.Runnable() { // run: function() { println("run"); } // }; // // The object literal following the "new Constructor()" expresssion // is passed as an additional (last) argument to the constructor. if (!env._no_syntax_extensions && type == LBRACE) { arguments.add(objectLiteral()); } final CallNode callNode = new CallNode(callLine, constructor.getToken(), finish, constructor, optimizeList(arguments), true); return new UnaryNode(newToken, callNode); }
@Override public final boolean enterUnaryNode(final UnaryNode unaryNode) { switch (unaryNode.tokenType()) { case ADD: return enterADD(unaryNode); case BIT_NOT: return enterBIT_NOT(unaryNode); case DELETE: return enterDELETE(unaryNode); case NEW: return enterNEW(unaryNode); case NOT: return enterNOT(unaryNode); case SUB: return enterSUB(unaryNode); case TYPEOF: return enterTYPEOF(unaryNode); case VOID: return enterVOID(unaryNode); case DECPREFIX: case DECPOSTFIX: case INCPREFIX: case INCPOSTFIX: return enterDECINC(unaryNode); default: return super.enterUnaryNode(unaryNode); } }
@Override public final Node leaveUnaryNode(final UnaryNode unaryNode) { switch (unaryNode.tokenType()) { case ADD: return leaveADD(unaryNode); case BIT_NOT: return leaveBIT_NOT(unaryNode); case DELETE: return leaveDELETE(unaryNode); case NEW: return leaveNEW(unaryNode); case NOT: return leaveNOT(unaryNode); case SUB: return leaveSUB(unaryNode); case TYPEOF: return leaveTYPEOF(unaryNode); case VOID: return leaveVOID(unaryNode); case DECPREFIX: case DECPOSTFIX: case INCPREFIX: case INCPOSTFIX: return leaveDECINC(unaryNode); default: return super.leaveUnaryNode(unaryNode); } }
@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 leaveUnaryNode(final UnaryNode unaryNode) { final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval(); if (literalNode != null) { log.info("Unary constant folded ", unaryNode, " to ", literalNode); return literalNode; } return unaryNode; }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { if(unaryNode.isTokenType(TokenType.NOT) || unaryNode.isTokenType(TokenType.NEW)) { // Operand of boolean negation is never optimistic (always coerced to boolean). // Operand of "new" is never optimistic (always coerced to Object). tagNeverOptimistic(unaryNode.getExpression()); } return true; }
private void branchOptimizer(final Expression node, final Label label, final boolean state) { if (node instanceof BinaryNode) { branchOptimizer((BinaryNode)node, label, state); return; } if (node instanceof UnaryNode) { branchOptimizer((UnaryNode)node, label, state); return; } loadTestAndJump(node, label, state); }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { final Expression expr = unaryNode.getExpression(); expr.accept(this); if(unaryNode.isSelfModifying()) { if(expr instanceof IdentNode) { final IdentNode ident = (IdentNode)expr; onSelfAssignment(ident, unaryNode, getLocalVariableTypeIfBytecode(ident.getSymbol())); } } return false; }
private void loadADD(final UnaryNode unaryNode, final TypeBounds resultBounds) { loadExpression(unaryNode.getExpression(), resultBounds.booleanToInt().notWiderThan(Type.NUMBER)); if(method.peekType() == Type.BOOLEAN) { // It's a no-op in bytecode, but we must make sure it is treated as an int for purposes of type signatures method.convert(Type.INT); } }
private void loadNEW(final UnaryNode unaryNode) { final CallNode callNode = (CallNode)unaryNode.getExpression(); final List<Expression> args = callNode.getArgs(); // Load function reference. loadExpressionAsObject(callNode.getFunction()); // must detect type error method.dynamicNew(1 + loadArgs(args), getCallSiteFlags()); }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { if (unaryNode.isTokenType(SPREAD_ARRAY)) { // rest element return true; } else { return enterDefault(unaryNode); } }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { unaryNode.toString(sb, new Runnable() { @Override public void run() { unaryNode.getExpression().accept(PrintVisitor.this); } }, printTypes); return false; }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { switch (unaryNode.tokenType()) { case POS: return enterPOS(unaryNode); case BIT_NOT: return enterBIT_NOT(unaryNode); case DELETE: return enterDELETE(unaryNode); case NEW: return enterNEW(unaryNode); case NOT: return enterNOT(unaryNode); case NEG: return enterNEG(unaryNode); case TYPEOF: return enterTYPEOF(unaryNode); case VOID: return enterVOID(unaryNode); case DECPREFIX: case DECPOSTFIX: case INCPREFIX: case INCPOSTFIX: return enterDECINC(unaryNode); default: return super.enterUnaryNode(unaryNode); } }
@Override public final Node leaveUnaryNode(final UnaryNode unaryNode) { switch (unaryNode.tokenType()) { case POS: return leavePOS(unaryNode); case BIT_NOT: return leaveBIT_NOT(unaryNode); case DELETE: return leaveDELETE(unaryNode); case NEW: return leaveNEW(unaryNode); case NOT: return leaveNOT(unaryNode); case NEG: return leaveNEG(unaryNode); case TYPEOF: return leaveTYPEOF(unaryNode); case VOID: return leaveVOID(unaryNode); case DECPREFIX: case DECPOSTFIX: case INCPREFIX: case INCPOSTFIX: return leaveDECINC(unaryNode); default: return super.leaveUnaryNode(unaryNode); } }
@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); } }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { if (es6) { if (unaryNode.isTokenType(TokenType.YIELD) || unaryNode.isTokenType(TokenType.YIELD_STAR)) { throwNotImplementedYet("es6.yield", unaryNode); } else if (unaryNode.isTokenType(TokenType.SPREAD_ARGUMENT) || unaryNode.isTokenType(TokenType.SPREAD_ARRAY)) { throwNotImplementedYet("es6.spread", unaryNode); } } return super.enterUnaryNode(unaryNode); }
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) { if (unaryNode.isTokenType(NOT)) { branchOptimizer(unaryNode.getExpression(), label, !state); } else { loadTestAndJump(unaryNode, label, state); } }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { final Expression expr = unaryNode.getExpression(); final LvarType unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType()); if(unaryNode.isSelfModifying() && expr instanceof IdentNode) { onSelfAssignment((IdentNode)expr, unaryType); } typeStack.push(unaryType); return false; }
/** * Parse a JSON literal from the token stream * @return the JSON literal as a Node */ private Expression jsonLiteral() { final long literalToken = token; switch (type) { case STRING: return getStringLiteral(); case ESCSTRING: case DECIMAL: case FLOATING: return getLiteral(); case FALSE: next(); return LiteralNode.newInstance(literalToken, finish, false); case TRUE: next(); return LiteralNode.newInstance(literalToken, finish, true); case NULL: next(); return LiteralNode.newInstance(literalToken, finish); case LBRACKET: return arrayLiteral(); case LBRACE: return objectLiteral(); /* * A.8.1 JSON Lexical Grammar * * JSONNumber :: See 15.12.1.1 * -opt DecimalIntegerLiteral JSONFractionopt ExponentPartopt */ case SUB: next(); final long realToken = token; final Object value = getValue(); if (value instanceof Number) { next(); return new UnaryNode(literalToken, LiteralNode.newInstance(realToken, finish, (Number)value)); } throw error(AbstractParser.message("expected", "number", type.getNameOrType())); default: break; } throw error(AbstractParser.message("expected", "json literal", type.getNameOrType())); }
@Override public boolean enterUnaryNode(final UnaryNode unaryNode) { enterDefault(unaryNode); final TokenType tokenType = unaryNode.tokenType(); if (tokenType == TokenType.NEW) { type("NewExpression"); comma(); final CallNode callNode = (CallNode)unaryNode.getExpression(); property("callee"); callNode.getFunction().accept(this); comma(); array("arguments", callNode.getArgs()); } else { final String operator; final boolean prefix; switch (tokenType) { case INCPOSTFIX: prefix = false; operator = "++"; break; case DECPOSTFIX: prefix = false; operator = "--"; break; case INCPREFIX: operator = "++"; prefix = true; break; case DECPREFIX: operator = "--"; prefix = true; break; default: prefix = true; operator = tokenType.getName(); break; } type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression"); comma(); property("operator", operator); comma(); property("prefix", prefix); comma(); property("argument"); unaryNode.getExpression().accept(this); } return leave(); }
@Override public Node leaveNEG(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); }
@Override public Node leaveDELETE(final UnaryNode unaryNode) { return runtimeNodeWeight(unaryNode); }
private Node runtimeNodeWeight(final UnaryNode unaryNode) { weight += CALL_WEIGHT; return unaryNode; }
UnaryNodeConstantEvaluator(final UnaryNode parent) { super(parent); }
private void loadDECINC(final UnaryNode unaryNode) { final Expression operand = unaryNode.getExpression(); final Type type = unaryNode.getType(); final TypeBounds typeBounds = new TypeBounds(type, Type.NUMBER); final TokenType tokenType = unaryNode.tokenType(); final boolean isPostfix = tokenType == TokenType.DECPOSTFIX || tokenType == TokenType.INCPOSTFIX; final boolean isIncrement = tokenType == TokenType.INCPREFIX || tokenType == TokenType.INCPOSTFIX; assert !type.isObject(); new SelfModifyingStore<UnaryNode>(unaryNode, operand) { private void loadRhs() { loadExpression(operand, typeBounds, true); } @Override protected void evaluate() { if(isPostfix) { loadRhs(); } else { new OptimisticOperation(unaryNode, typeBounds) { @Override void loadStack() { loadRhs(); loadMinusOne(); } @Override void consumeStack() { doDecInc(getProgramPoint()); } }.emit(getOptimisticIgnoreCountForSelfModifyingExpression(operand)); } } @Override protected void storeNonDiscard() { super.storeNonDiscard(); if (isPostfix) { new OptimisticOperation(unaryNode, typeBounds) { @Override void loadStack() { loadMinusOne(); } @Override void consumeStack() { doDecInc(getProgramPoint()); } }.emit(1); // 1 for non-incremented result on the top of the stack pushed in evaluate() } } private void loadMinusOne() { if (type.isInteger()) { method.load(isIncrement ? 1 : -1); } else { method.load(isIncrement ? 1.0 : -1.0); } } private void doDecInc(final int programPoint) { method.add(programPoint); } }.store(); }
private void loadDECINC(final UnaryNode unaryNode) { final Expression operand = unaryNode.getExpression(); final Type type = unaryNode.getType(); final TypeBounds typeBounds = new TypeBounds(type, Type.NUMBER); final TokenType tokenType = unaryNode.tokenType(); final boolean isPostfix = tokenType == TokenType.DECPOSTFIX || tokenType == TokenType.INCPOSTFIX; final boolean isIncrement = tokenType == TokenType.INCPREFIX || tokenType == TokenType.INCPOSTFIX; assert !type.isObject(); new SelfModifyingStore<UnaryNode>(unaryNode, operand) { private void loadRhs() { loadExpression(operand, typeBounds, true); } @Override protected void evaluate() { if(isPostfix) { loadRhs(); } else { new OptimisticOperation(unaryNode, typeBounds) { @Override void loadStack() { loadRhs(); loadMinusOne(); } @Override void consumeStack() { doDecInc(getProgramPoint()); } }.emit(getOptimisticIgnoreCountForSelfModifyingExpression(operand)); } } @Override protected void storeNonDiscard() { super.storeNonDiscard(); if (isPostfix) { new OptimisticOperation(unaryNode, typeBounds) { @Override void loadStack() { loadMinusOne(); } @Override void consumeStack() { doDecInc(getProgramPoint()); } }.emit(1); // 1 for non-incremented result on the top of the stack pushed in evaluate() } } private void loadMinusOne() { if (type.isInteger()) { method.load(isIncrement ? 1 : -1); } else if (type.isLong()) { method.load(isIncrement ? 1L : -1L); } else { method.load(isIncrement ? 1.0 : -1.0); } } private void doDecInc(final int programPoint) { method.add(programPoint); } }.store(); }
public void loadVOID(final UnaryNode unaryNode, final TypeBounds resultBounds) { loadAndDiscard(unaryNode.getExpression()); if (!lc.popDiscardIfCurrent(unaryNode)) { method.loadUndefined(resultBounds.widest); } }
@Override public Node leaveADD(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); }