28 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
30 import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
31
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.ListIterator;
37 import java.util.regex.Pattern;
38 import jdk.nashorn.internal.ir.AccessNode;
39 import jdk.nashorn.internal.ir.BaseNode;
40 import jdk.nashorn.internal.ir.BinaryNode;
41 import jdk.nashorn.internal.ir.Block;
42 import jdk.nashorn.internal.ir.BlockLexicalContext;
43 import jdk.nashorn.internal.ir.BlockStatement;
44 import jdk.nashorn.internal.ir.BreakNode;
45 import jdk.nashorn.internal.ir.CallNode;
46 import jdk.nashorn.internal.ir.CaseNode;
47 import jdk.nashorn.internal.ir.CatchNode;
48 import jdk.nashorn.internal.ir.ContinueNode;
49 import jdk.nashorn.internal.ir.DebuggerNode;
50 import jdk.nashorn.internal.ir.EmptyNode;
51 import jdk.nashorn.internal.ir.Expression;
52 import jdk.nashorn.internal.ir.ExpressionStatement;
53 import jdk.nashorn.internal.ir.ForNode;
54 import jdk.nashorn.internal.ir.FunctionNode;
55 import jdk.nashorn.internal.ir.IdentNode;
56 import jdk.nashorn.internal.ir.IfNode;
57 import jdk.nashorn.internal.ir.IndexNode;
58 import jdk.nashorn.internal.ir.JumpStatement;
59 import jdk.nashorn.internal.ir.JumpToInlinedFinally;
60 import jdk.nashorn.internal.ir.LabelNode;
61 import jdk.nashorn.internal.ir.LexicalContext;
62 import jdk.nashorn.internal.ir.LiteralNode;
63 import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode;
64 import jdk.nashorn.internal.ir.LoopNode;
65 import jdk.nashorn.internal.ir.Node;
66 import jdk.nashorn.internal.ir.ReturnNode;
67 import jdk.nashorn.internal.ir.RuntimeNode;
68 import jdk.nashorn.internal.ir.Statement;
69 import jdk.nashorn.internal.ir.SwitchNode;
70 import jdk.nashorn.internal.ir.Symbol;
71 import jdk.nashorn.internal.ir.ThrowNode;
72 import jdk.nashorn.internal.ir.TryNode;
73 import jdk.nashorn.internal.ir.VarNode;
74 import jdk.nashorn.internal.ir.WhileNode;
75 import jdk.nashorn.internal.ir.WithNode;
76 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
77 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
78 import jdk.nashorn.internal.parser.Token;
79 import jdk.nashorn.internal.parser.TokenType;
80 import jdk.nashorn.internal.runtime.Context;
81 import jdk.nashorn.internal.runtime.JSType;
82 import jdk.nashorn.internal.runtime.Source;
83 import jdk.nashorn.internal.runtime.logging.DebugLogger;
84 import jdk.nashorn.internal.runtime.logging.Loggable;
85 import jdk.nashorn.internal.runtime.logging.Logger;
86
87 /**
88 * Lower to more primitive operations. After lowering, an AST still has no symbols
89 * and types, but several nodes have been turned into more low level constructs
90 * and control flow termination criteria have been computed.
91 *
92 * We do things like code copying/inlining of finallies here, as it is much
93 * harder and context dependent to do any code copying after symbols have been
94 * finalized.
95 */
96 @Logger(name="lower")
97 final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Loggable {
98
99 private final DebugLogger log;
100 private final boolean es6;
101
102 // Conservative pattern to test if element names consist of characters valid for identifiers.
103 // This matches any non-zero length alphanumeric string including _ and $ and not starting with a digit.
104 private static final Pattern SAFE_PROPERTY_NAME = Pattern.compile("[a-zA-Z_$][\\w$]*");
105
106 /**
107 * Constructor.
108 */
109 Lower(final Compiler compiler) {
110 super(new BlockLexicalContext() {
111
112 @Override
113 public List<Statement> popStatements() {
114 final List<Statement> newStatements = new ArrayList<>();
115 boolean terminated = false;
116
117 final List<Statement> statements = super.popStatements();
118 for (final Statement statement : statements) {
119 if (!terminated) {
120 newStatements.add(statement);
129 }
130
131 @Override
132 protected Block afterSetStatements(final Block block) {
133 final List<Statement> stmts = block.getStatements();
134 for(final ListIterator<Statement> li = stmts.listIterator(stmts.size()); li.hasPrevious();) {
135 final Statement stmt = li.previous();
136 // popStatements() guarantees that the only thing after a terminal statement are uninitialized
137 // VarNodes. We skip past those, and set the terminal state of the block to the value of the
138 // terminal state of the first statement that is not an uninitialized VarNode.
139 if(!(stmt instanceof VarNode && ((VarNode)stmt).getInit() == null)) {
140 return block.setIsTerminal(this, stmt.isTerminal());
141 }
142 }
143 return block.setIsTerminal(this, false);
144 }
145 });
146
147 this.log = initLogger(compiler.getContext());
148 this.es6 = compiler.getScriptEnvironment()._es6;
149 }
150
151 @Override
152 public DebugLogger getLogger() {
153 return log;
154 }
155
156 @Override
157 public DebugLogger initLogger(final Context context) {
158 return context.getLogger(this.getClass());
159 }
160
161 @Override
162 public boolean enterBreakNode(final BreakNode breakNode) {
163 addStatement(breakNode);
164 return false;
165 }
166
167 @Override
168 public Node leaveCallNode(final CallNode callNode) {
224
225 @Override
226 public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
227 final Expression expr = expressionStatement.getExpression();
228 ExpressionStatement node = expressionStatement;
229
230 final FunctionNode currentFunction = lc.getCurrentFunction();
231
232 if (currentFunction.isProgram()) {
233 if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
234 node = expressionStatement.setExpression(
235 new BinaryNode(
236 Token.recast(
237 expressionStatement.getToken(),
238 TokenType.ASSIGN),
239 compilerConstant(RETURN),
240 expr));
241 }
242 }
243
244 return addStatement(node);
245 }
246
247 @Override
248 public Node leaveBlockStatement(final BlockStatement blockStatement) {
249 return addStatement(blockStatement);
250 }
251
252 @Override
253 public Node leaveForNode(final ForNode forNode) {
254 ForNode newForNode = forNode;
255
256 final Expression test = forNode.getTest();
257 if (!forNode.isForInOrOf() && isAlwaysTrue(test)) {
258 newForNode = forNode.setTest(lc, null);
259 }
260
261 newForNode = checkEscape(newForNode);
262 if(!es6 && newForNode.isForInOrOf()) {
263 // Wrap it in a block so its internally created iterator is restricted in scope, unless we are running
264 // in ES6 mode, in which case the parser already created a block to capture let/const declarations.
265 addStatementEnclosedInBlock(newForNode);
266 } else {
267 addStatement(newForNode);
268 }
269 return newForNode;
270 }
271
272 @Override
273 public Node leaveFunctionNode(final FunctionNode functionNode) {
274 log.info("END FunctionNode: ", functionNode.getName());
275 return functionNode;
276 }
277
278 @Override
279 public Node leaveIfNode(final IfNode ifNode) {
280 return addStatement(ifNode);
281 }
282
283 @Override
284 public Node leaveIN(final BinaryNode binaryNode) {
285 return new RuntimeNode(binaryNode);
286 }
287
288 @Override
289 public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
290 return new RuntimeNode(binaryNode);
291 }
292
561 /*
562 * Now that the transform is done, we have to go into the try and splice
563 * the finally block in front of any statement that is outside the try
564 */
565 return (TryNode)lc.replace(tryNode, spliceFinally(newTryNode, rethrows.get(0), finallyBody));
566 }
567
568 private TryNode ensureUnconditionalCatch(final TryNode tryNode) {
569 final List<CatchNode> catches = tryNode.getCatches();
570 if(catches == null || catches.isEmpty() || catches.get(catches.size() - 1).getExceptionCondition() == null) {
571 return tryNode;
572 }
573 // If the last catch block is conditional, add an unconditional rethrow block
574 final List<Block> newCatchBlocks = new ArrayList<>(tryNode.getCatchBlocks());
575
576 newCatchBlocks.add(catchAllBlock(tryNode));
577 return tryNode.setCatchBlocks(lc, newCatchBlocks);
578 }
579
580 @Override
581 public Node leaveVarNode(final VarNode varNode) {
582 addStatement(varNode);
583 if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION)
584 && lc.getCurrentFunction().isProgram()
585 && ((FunctionNode) varNode.getInit()).isAnonymous()) {
586 new ExpressionStatement(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
587 }
588 return varNode;
589 }
590
591 @Override
592 public Node leaveWhileNode(final WhileNode whileNode) {
593 final Expression test = whileNode.getTest();
594 final Block body = whileNode.getBody();
595
596 if (isAlwaysTrue(test)) {
597 //turn it into a for node without a test.
598 final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, 0).accept(this);
599 lc.replace(whileNode, forNode);
600 return forNode;
601 }
602
603 return addStatement(checkEscape(whileNode));
604 }
605
606 @Override
607 public Node leaveWithNode(final WithNode withNode) {
608 return addStatement(withNode);
609 }
610
611 /**
612 * Given a function node that is a callee in a CallNode, replace it with
613 * the appropriate marker function. This is used by {@link CodeGenerator}
614 * for fast scope calls
615 *
616 * @param function function called by a CallNode
617 * @return transformed node to marker function or identity if not ident/access/indexnode
618 */
619 private static Expression markerFunction(final Expression function) {
620 if (function instanceof IdentNode) {
621 return ((IdentNode)function).setIsFunction();
622 } else if (function instanceof BaseNode) {
623 return ((BaseNode)function).setIsFunction();
624 }
625 return function;
626 }
627
628 /**
629 * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval>
630 * @param node a node
748 final Symbol symbol = ((IdentNode)expression).getSymbol();
749 return symbol != null && symbol.isInternal();
750 }
751
752 /**
753 * Is this an assignment to the special variable that hosts scripting eval
754 * results, i.e. __return__?
755 *
756 * @param expression expression to check whether it is $evalresult = X
757 * @return true if an assignment to eval result, false otherwise
758 */
759 private static boolean isEvalResultAssignment(final Node expression) {
760 final Node e = expression;
761 if (e instanceof BinaryNode) {
762 final Node lhs = ((BinaryNode)e).lhs();
763 if (lhs instanceof IdentNode) {
764 return ((IdentNode)lhs).getName().equals(RETURN.symbolName());
765 }
766 }
767 return false;
768 }
769 }
|
28 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
30 import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
31
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.ListIterator;
37 import java.util.regex.Pattern;
38 import jdk.nashorn.internal.ir.AccessNode;
39 import jdk.nashorn.internal.ir.BaseNode;
40 import jdk.nashorn.internal.ir.BinaryNode;
41 import jdk.nashorn.internal.ir.Block;
42 import jdk.nashorn.internal.ir.BlockLexicalContext;
43 import jdk.nashorn.internal.ir.BlockStatement;
44 import jdk.nashorn.internal.ir.BreakNode;
45 import jdk.nashorn.internal.ir.CallNode;
46 import jdk.nashorn.internal.ir.CaseNode;
47 import jdk.nashorn.internal.ir.CatchNode;
48 import jdk.nashorn.internal.ir.ClassNode;
49 import jdk.nashorn.internal.ir.ContinueNode;
50 import jdk.nashorn.internal.ir.DebuggerNode;
51 import jdk.nashorn.internal.ir.EmptyNode;
52 import jdk.nashorn.internal.ir.Expression;
53 import jdk.nashorn.internal.ir.ExpressionStatement;
54 import jdk.nashorn.internal.ir.ForNode;
55 import jdk.nashorn.internal.ir.FunctionNode;
56 import jdk.nashorn.internal.ir.IdentNode;
57 import jdk.nashorn.internal.ir.IfNode;
58 import jdk.nashorn.internal.ir.IndexNode;
59 import jdk.nashorn.internal.ir.JumpStatement;
60 import jdk.nashorn.internal.ir.JumpToInlinedFinally;
61 import jdk.nashorn.internal.ir.LabelNode;
62 import jdk.nashorn.internal.ir.LexicalContext;
63 import jdk.nashorn.internal.ir.LiteralNode;
64 import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode;
65 import jdk.nashorn.internal.ir.LoopNode;
66 import jdk.nashorn.internal.ir.Node;
67 import jdk.nashorn.internal.ir.ReturnNode;
68 import jdk.nashorn.internal.ir.RuntimeNode;
69 import jdk.nashorn.internal.ir.Statement;
70 import jdk.nashorn.internal.ir.SwitchNode;
71 import jdk.nashorn.internal.ir.Symbol;
72 import jdk.nashorn.internal.ir.ThrowNode;
73 import jdk.nashorn.internal.ir.TryNode;
74 import jdk.nashorn.internal.ir.UnaryNode;
75 import jdk.nashorn.internal.ir.VarNode;
76 import jdk.nashorn.internal.ir.WhileNode;
77 import jdk.nashorn.internal.ir.WithNode;
78 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
79 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
80 import jdk.nashorn.internal.parser.Token;
81 import jdk.nashorn.internal.parser.TokenType;
82 import jdk.nashorn.internal.runtime.Context;
83 import jdk.nashorn.internal.runtime.ECMAErrors;
84 import jdk.nashorn.internal.runtime.ErrorManager;
85 import jdk.nashorn.internal.runtime.JSType;
86 import jdk.nashorn.internal.runtime.Source;
87 import jdk.nashorn.internal.runtime.logging.DebugLogger;
88 import jdk.nashorn.internal.runtime.logging.Loggable;
89 import jdk.nashorn.internal.runtime.logging.Logger;
90
91 /**
92 * Lower to more primitive operations. After lowering, an AST still has no symbols
93 * and types, but several nodes have been turned into more low level constructs
94 * and control flow termination criteria have been computed.
95 *
96 * We do things like code copying/inlining of finallies here, as it is much
97 * harder and context dependent to do any code copying after symbols have been
98 * finalized.
99 */
100 @Logger(name="lower")
101 final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Loggable {
102
103 private final DebugLogger log;
104 private final boolean es6;
105 private final Source source;
106
107 // Conservative pattern to test if element names consist of characters valid for identifiers.
108 // This matches any non-zero length alphanumeric string including _ and $ and not starting with a digit.
109 private static final Pattern SAFE_PROPERTY_NAME = Pattern.compile("[a-zA-Z_$][\\w$]*");
110
111 /**
112 * Constructor.
113 */
114 Lower(final Compiler compiler) {
115 super(new BlockLexicalContext() {
116
117 @Override
118 public List<Statement> popStatements() {
119 final List<Statement> newStatements = new ArrayList<>();
120 boolean terminated = false;
121
122 final List<Statement> statements = super.popStatements();
123 for (final Statement statement : statements) {
124 if (!terminated) {
125 newStatements.add(statement);
134 }
135
136 @Override
137 protected Block afterSetStatements(final Block block) {
138 final List<Statement> stmts = block.getStatements();
139 for(final ListIterator<Statement> li = stmts.listIterator(stmts.size()); li.hasPrevious();) {
140 final Statement stmt = li.previous();
141 // popStatements() guarantees that the only thing after a terminal statement are uninitialized
142 // VarNodes. We skip past those, and set the terminal state of the block to the value of the
143 // terminal state of the first statement that is not an uninitialized VarNode.
144 if(!(stmt instanceof VarNode && ((VarNode)stmt).getInit() == null)) {
145 return block.setIsTerminal(this, stmt.isTerminal());
146 }
147 }
148 return block.setIsTerminal(this, false);
149 }
150 });
151
152 this.log = initLogger(compiler.getContext());
153 this.es6 = compiler.getScriptEnvironment()._es6;
154 this.source = compiler.getSource();
155 }
156
157 @Override
158 public DebugLogger getLogger() {
159 return log;
160 }
161
162 @Override
163 public DebugLogger initLogger(final Context context) {
164 return context.getLogger(this.getClass());
165 }
166
167 @Override
168 public boolean enterBreakNode(final BreakNode breakNode) {
169 addStatement(breakNode);
170 return false;
171 }
172
173 @Override
174 public Node leaveCallNode(final CallNode callNode) {
230
231 @Override
232 public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
233 final Expression expr = expressionStatement.getExpression();
234 ExpressionStatement node = expressionStatement;
235
236 final FunctionNode currentFunction = lc.getCurrentFunction();
237
238 if (currentFunction.isProgram()) {
239 if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
240 node = expressionStatement.setExpression(
241 new BinaryNode(
242 Token.recast(
243 expressionStatement.getToken(),
244 TokenType.ASSIGN),
245 compilerConstant(RETURN),
246 expr));
247 }
248 }
249
250 if (es6 && expressionStatement.destructuringDeclarationType() != null) {
251 throwNotImplementedYet("es6.destructuring", expressionStatement);
252 }
253
254 return addStatement(node);
255 }
256
257 @Override
258 public Node leaveBlockStatement(final BlockStatement blockStatement) {
259 return addStatement(blockStatement);
260 }
261
262 @Override
263 public Node leaveForNode(final ForNode forNode) {
264 ForNode newForNode = forNode;
265
266 final Expression test = forNode.getTest();
267 if (!forNode.isForInOrOf() && isAlwaysTrue(test)) {
268 newForNode = forNode.setTest(lc, null);
269 }
270
271 newForNode = checkEscape(newForNode);
272 if(!es6 && newForNode.isForInOrOf()) {
273 // Wrap it in a block so its internally created iterator is restricted in scope, unless we are running
274 // in ES6 mode, in which case the parser already created a block to capture let/const declarations.
275 addStatementEnclosedInBlock(newForNode);
276 } else {
277 addStatement(newForNode);
278 }
279 return newForNode;
280 }
281
282 @Override
283 public boolean enterFunctionNode(final FunctionNode functionNode) {
284 if (es6) {
285 if (functionNode.getKind() == FunctionNode.Kind.MODULE) {
286 throwNotImplementedYet("es6.module", functionNode);
287 }
288
289 if (functionNode.getKind() == FunctionNode.Kind.GENERATOR) {
290 throwNotImplementedYet("es6.generator", functionNode);
291 }
292
293 int numParams = functionNode.getNumOfParams();
294 if (numParams > 1) {
295 final IdentNode lastParam = functionNode.getParameter(numParams - 1);
296 if (lastParam.isRestParameter()) {
297 throwNotImplementedYet("es6.rest.param", lastParam);
298 }
299 }
300 }
301
302 return super.enterFunctionNode(functionNode);
303 }
304
305 @Override
306 public Node leaveFunctionNode(final FunctionNode functionNode) {
307 log.info("END FunctionNode: ", functionNode.getName());
308 return functionNode;
309 }
310
311 @Override
312 public Node leaveIfNode(final IfNode ifNode) {
313 return addStatement(ifNode);
314 }
315
316 @Override
317 public Node leaveIN(final BinaryNode binaryNode) {
318 return new RuntimeNode(binaryNode);
319 }
320
321 @Override
322 public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
323 return new RuntimeNode(binaryNode);
324 }
325
594 /*
595 * Now that the transform is done, we have to go into the try and splice
596 * the finally block in front of any statement that is outside the try
597 */
598 return (TryNode)lc.replace(tryNode, spliceFinally(newTryNode, rethrows.get(0), finallyBody));
599 }
600
601 private TryNode ensureUnconditionalCatch(final TryNode tryNode) {
602 final List<CatchNode> catches = tryNode.getCatches();
603 if(catches == null || catches.isEmpty() || catches.get(catches.size() - 1).getExceptionCondition() == null) {
604 return tryNode;
605 }
606 // If the last catch block is conditional, add an unconditional rethrow block
607 final List<Block> newCatchBlocks = new ArrayList<>(tryNode.getCatchBlocks());
608
609 newCatchBlocks.add(catchAllBlock(tryNode));
610 return tryNode.setCatchBlocks(lc, newCatchBlocks);
611 }
612
613 @Override
614 public boolean enterUnaryNode(final UnaryNode unaryNode) {
615 if (es6) {
616 if (unaryNode.isTokenType(TokenType.YIELD) ||
617 unaryNode.isTokenType(TokenType.YIELD_STAR)) {
618 throwNotImplementedYet("es6.yield", unaryNode);
619 } else if (unaryNode.isTokenType(TokenType.SPREAD_ARGUMENT) ||
620 unaryNode.isTokenType(TokenType.SPREAD_ARRAY)) {
621 throwNotImplementedYet("es6.spread", unaryNode);
622 }
623 }
624
625 return super.enterUnaryNode(unaryNode);
626 }
627
628 @Override
629 public Node leaveVarNode(final VarNode varNode) {
630 addStatement(varNode);
631 if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION)
632 && lc.getCurrentFunction().isProgram()
633 && ((FunctionNode) varNode.getInit()).isAnonymous()) {
634 new ExpressionStatement(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
635 }
636 return varNode;
637 }
638
639 @Override
640 public Node leaveWhileNode(final WhileNode whileNode) {
641 final Expression test = whileNode.getTest();
642 final Block body = whileNode.getBody();
643
644 if (isAlwaysTrue(test)) {
645 //turn it into a for node without a test.
646 final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, 0).accept(this);
647 lc.replace(whileNode, forNode);
648 return forNode;
649 }
650
651 return addStatement(checkEscape(whileNode));
652 }
653
654 @Override
655 public Node leaveWithNode(final WithNode withNode) {
656 return addStatement(withNode);
657 }
658
659 @Override
660 public boolean enterClassNode(final ClassNode classNode) {
661 throwNotImplementedYet("es6.class", classNode);
662 return super.enterClassNode(classNode);
663 }
664
665 /**
666 * Given a function node that is a callee in a CallNode, replace it with
667 * the appropriate marker function. This is used by {@link CodeGenerator}
668 * for fast scope calls
669 *
670 * @param function function called by a CallNode
671 * @return transformed node to marker function or identity if not ident/access/indexnode
672 */
673 private static Expression markerFunction(final Expression function) {
674 if (function instanceof IdentNode) {
675 return ((IdentNode)function).setIsFunction();
676 } else if (function instanceof BaseNode) {
677 return ((BaseNode)function).setIsFunction();
678 }
679 return function;
680 }
681
682 /**
683 * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval>
684 * @param node a node
802 final Symbol symbol = ((IdentNode)expression).getSymbol();
803 return symbol != null && symbol.isInternal();
804 }
805
806 /**
807 * Is this an assignment to the special variable that hosts scripting eval
808 * results, i.e. __return__?
809 *
810 * @param expression expression to check whether it is $evalresult = X
811 * @return true if an assignment to eval result, false otherwise
812 */
813 private static boolean isEvalResultAssignment(final Node expression) {
814 final Node e = expression;
815 if (e instanceof BinaryNode) {
816 final Node lhs = ((BinaryNode)e).lhs();
817 if (lhs instanceof IdentNode) {
818 return ((IdentNode)lhs).getName().equals(RETURN.symbolName());
819 }
820 }
821 return false;
822 }
823
824 private void throwNotImplementedYet(final String msgId, final Node node) {
825 final long token = node.getToken();
826 final int line = source.getLine(node.getStart());
827 final int column = source.getColumn(node.getStart());
828 final String message = ECMAErrors.getMessage("unimplemented." + msgId);
829 final String formatted = ErrorManager.format(message, source, line, column, token);
830 throw new RuntimeException(formatted);
831 }
832 }
|