30 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; |
30 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; |
31 |
31 |
32 import java.util.ArrayList; |
32 import java.util.ArrayList; |
33 import java.util.Arrays; |
33 import java.util.Arrays; |
34 import java.util.List; |
34 import java.util.List; |
|
35 |
35 import jdk.nashorn.internal.ir.BaseNode; |
36 import jdk.nashorn.internal.ir.BaseNode; |
36 import jdk.nashorn.internal.ir.BinaryNode; |
37 import jdk.nashorn.internal.ir.BinaryNode; |
37 import jdk.nashorn.internal.ir.Block; |
38 import jdk.nashorn.internal.ir.Block; |
38 import jdk.nashorn.internal.ir.BlockLexicalContext; |
39 import jdk.nashorn.internal.ir.BlockLexicalContext; |
39 import jdk.nashorn.internal.ir.BreakNode; |
40 import jdk.nashorn.internal.ir.BreakNode; |
47 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; |
48 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; |
48 import jdk.nashorn.internal.ir.IdentNode; |
49 import jdk.nashorn.internal.ir.IdentNode; |
49 import jdk.nashorn.internal.ir.IfNode; |
50 import jdk.nashorn.internal.ir.IfNode; |
50 import jdk.nashorn.internal.ir.LabelNode; |
51 import jdk.nashorn.internal.ir.LabelNode; |
51 import jdk.nashorn.internal.ir.LexicalContext; |
52 import jdk.nashorn.internal.ir.LexicalContext; |
52 import jdk.nashorn.internal.ir.LineNumberNode; |
|
53 import jdk.nashorn.internal.ir.LiteralNode; |
53 import jdk.nashorn.internal.ir.LiteralNode; |
54 import jdk.nashorn.internal.ir.LoopNode; |
54 import jdk.nashorn.internal.ir.LoopNode; |
55 import jdk.nashorn.internal.ir.Node; |
55 import jdk.nashorn.internal.ir.Node; |
56 import jdk.nashorn.internal.ir.ReturnNode; |
56 import jdk.nashorn.internal.ir.ReturnNode; |
|
57 import jdk.nashorn.internal.ir.Statement; |
57 import jdk.nashorn.internal.ir.SwitchNode; |
58 import jdk.nashorn.internal.ir.SwitchNode; |
58 import jdk.nashorn.internal.ir.Symbol; |
59 import jdk.nashorn.internal.ir.Symbol; |
59 import jdk.nashorn.internal.ir.ThrowNode; |
60 import jdk.nashorn.internal.ir.ThrowNode; |
60 import jdk.nashorn.internal.ir.TryNode; |
61 import jdk.nashorn.internal.ir.TryNode; |
61 import jdk.nashorn.internal.ir.UnaryNode; |
|
62 import jdk.nashorn.internal.ir.VarNode; |
62 import jdk.nashorn.internal.ir.VarNode; |
63 import jdk.nashorn.internal.ir.WhileNode; |
63 import jdk.nashorn.internal.ir.WhileNode; |
64 import jdk.nashorn.internal.ir.WithNode; |
64 import jdk.nashorn.internal.ir.WithNode; |
65 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; |
65 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; |
66 import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
66 import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
91 */ |
91 */ |
92 Lower() { |
92 Lower() { |
93 super(new BlockLexicalContext() { |
93 super(new BlockLexicalContext() { |
94 |
94 |
95 @Override |
95 @Override |
96 public List<Node> popStatements() { |
96 public List<Statement> popStatements() { |
97 List<Node> newStatements = new ArrayList<>(); |
97 final List<Statement> newStatements = new ArrayList<>(); |
98 boolean terminated = false; |
98 boolean terminated = false; |
99 |
99 |
100 final List<Node> statements = super.popStatements(); |
100 final List<Statement> statements = super.popStatements(); |
101 for (final Node statement : statements) { |
101 for (final Statement statement : statements) { |
102 if (!terminated) { |
102 if (!terminated) { |
103 newStatements.add(statement); |
103 newStatements.add(statement); |
104 if (statement.isTerminal()) { |
104 if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why? |
105 terminated = true; |
105 terminated = true; |
106 } |
106 } |
107 } else { |
107 } else { |
108 if (statement instanceof VarNode) { |
108 statement.accept(new NodeVisitor() { |
109 newStatements.add(((VarNode)statement).setInit(null)); |
109 @Override |
110 } |
110 public boolean enterVarNode(final VarNode varNode) { |
|
111 newStatements.add(varNode.setInit(null)); |
|
112 return false; |
|
113 } |
|
114 }); |
111 } |
115 } |
112 } |
116 } |
113 return newStatements; |
117 return newStatements; |
114 } |
118 } |
115 }); |
119 }); |
118 @Override |
122 @Override |
119 public boolean enterBlock(final Block block) { |
123 public boolean enterBlock(final Block block) { |
120 final LexicalContext lc = getLexicalContext(); |
124 final LexicalContext lc = getLexicalContext(); |
121 final FunctionNode function = lc.getCurrentFunction(); |
125 final FunctionNode function = lc.getCurrentFunction(); |
122 if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) { |
126 if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) { |
123 new ExecuteNode(block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this); |
127 new ExecuteNode(block.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this); |
124 } |
128 } |
125 return true; |
129 return true; |
126 } |
130 } |
127 |
131 |
128 @Override |
132 @Override |
130 //now we have committed the entire statement list to the block, but we need to truncate |
134 //now we have committed the entire statement list to the block, but we need to truncate |
131 //whatever is after the last terminal. block append won't append past it |
135 //whatever is after the last terminal. block append won't append past it |
132 |
136 |
133 final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext(); |
137 final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext(); |
134 |
138 |
135 Node last = lc.getLastStatement(); |
139 Statement last = lc.getLastStatement(); |
136 |
140 |
137 if (lc.isFunctionBody()) { |
141 if (lc.isFunctionBody()) { |
138 final FunctionNode currentFunction = getLexicalContext().getCurrentFunction(); |
142 final FunctionNode currentFunction = getLexicalContext().getCurrentFunction(); |
139 final boolean isProgram = currentFunction.isProgram(); |
143 final boolean isProgram = currentFunction.isProgram(); |
140 final ReturnNode returnNode = new ReturnNode( |
144 final ReturnNode returnNode = new ReturnNode( |
|
145 last == null ? block.getLineNumber() : last.getLineNumber(), //TODO? |
141 currentFunction.getToken(), |
146 currentFunction.getToken(), |
142 currentFunction.getFinish(), |
147 currentFunction.getFinish(), |
143 isProgram ? |
148 isProgram ? |
144 compilerConstant(RETURN) : |
149 compilerConstant(RETURN) : |
145 LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)); |
150 LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)); |
146 |
151 |
147 last = returnNode.accept(this); |
152 last = (Statement)returnNode.accept(this); |
148 } |
153 } |
149 |
154 |
150 if (last != null && last.isTerminal()) { |
155 if (last != null && last.isTerminal()) { |
151 return block.setIsTerminal(lc, true); |
156 return block.setIsTerminal(lc, true); |
152 } |
157 } |
269 return labelledNode.ensureUniqueLabels(getLexicalContext()); |
268 return labelledNode.ensureUniqueLabels(getLexicalContext()); |
270 } |
269 } |
271 }); |
270 }); |
272 } |
271 } |
273 |
272 |
274 private static List<Node> copyFinally(final Block finallyBody) { |
273 private static List<Statement> copyFinally(final Block finallyBody) { |
275 final List<Node> newStatements = new ArrayList<>(); |
274 final List<Statement> newStatements = new ArrayList<>(); |
276 for (final Node statement : finallyBody.getStatements()) { |
275 for (final Statement statement : finallyBody.getStatements()) { |
277 newStatements.add(ensureUniqueLabelsIn(statement)); |
276 newStatements.add((Statement)ensureUniqueLabelsIn(statement)); |
278 if (statement.hasTerminalFlags()) { |
277 if (statement.hasTerminalFlags()) { |
279 return newStatements; |
278 return newStatements; |
280 } |
279 } |
281 } |
280 } |
282 return newStatements; |
281 return newStatements; |
283 } |
282 } |
284 |
283 |
285 private Block catchAllBlock(final TryNode tryNode) { |
284 private Block catchAllBlock(final TryNode tryNode) { |
286 final long token = tryNode.getToken(); |
285 final int lineNumber = tryNode.getLineNumber(); |
287 final int finish = tryNode.getFinish(); |
286 final long token = tryNode.getToken(); |
|
287 final int finish = tryNode.getFinish(); |
288 |
288 |
289 final IdentNode exception = new IdentNode(token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all")); |
289 final IdentNode exception = new IdentNode(token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all")); |
290 |
290 |
291 final Block catchBody = new Block(token, finish, new ThrowNode(token, finish, new IdentNode(exception))). |
291 final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception))). |
292 setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal |
292 setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal |
293 |
293 |
294 final CatchNode catchAllNode = new CatchNode(token, finish, new IdentNode(exception), null, catchBody); |
294 final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody); |
295 final Block catchAllBlock = new Block(token, finish, catchAllNode); |
295 final Block catchAllBlock = new Block(lineNumber, token, finish, catchAllNode); |
296 |
296 |
297 //catchallblock -> catchallnode (catchnode) -> exception -> throw |
297 //catchallblock -> catchallnode (catchnode) -> exception -> throw |
298 |
298 |
299 return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower |
299 return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower |
300 } |
300 } |
302 private IdentNode compilerConstant(final CompilerConstants cc) { |
302 private IdentNode compilerConstant(final CompilerConstants cc) { |
303 final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); |
303 final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); |
304 return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); |
304 return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); |
305 } |
305 } |
306 |
306 |
307 private static boolean isTerminal(final List<Node> statements) { |
307 private static boolean isTerminal(final List<Statement> statements) { |
308 return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags(); |
308 return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags(); |
309 } |
309 } |
310 |
310 |
311 /** |
311 /** |
312 * Splice finally code into all endpoints of a trynode |
312 * Splice finally code into all endpoints of a trynode |
314 * @param list of rethrowing throw nodes from synthetic catch blocks |
314 * @param list of rethrowing throw nodes from synthetic catch blocks |
315 * @param finallyBody the code in the original finally block |
315 * @param finallyBody the code in the original finally block |
316 * @return new try node after splicing finally code (same if nop) |
316 * @return new try node after splicing finally code (same if nop) |
317 */ |
317 */ |
318 private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) { |
318 private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) { |
319 final int finish = tryNode.getFinish(); |
319 final int finish = tryNode.getFinish(); |
320 |
320 |
321 assert tryNode.getFinallyBody() == null; |
321 assert tryNode.getFinallyBody() == null; |
322 |
322 |
323 final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() { |
323 final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() { |
324 final List<Node> insideTry = new ArrayList<>(); |
324 final List<Node> insideTry = new ArrayList<>(); |
336 } |
336 } |
337 |
337 |
338 @Override |
338 @Override |
339 public Node leaveThrowNode(final ThrowNode throwNode) { |
339 public Node leaveThrowNode(final ThrowNode throwNode) { |
340 if (rethrows.contains(throwNode)) { |
340 if (rethrows.contains(throwNode)) { |
341 final List<Node> newStatements = copyFinally(finallyBody); |
341 final List<Statement> newStatements = copyFinally(finallyBody); |
342 if (!isTerminal(newStatements)) { |
342 if (!isTerminal(newStatements)) { |
343 newStatements.add(throwNode); |
343 newStatements.add(throwNode); |
344 } |
344 } |
345 return new Block(throwNode.getToken(), throwNode.getFinish(), newStatements); |
345 return new Block(throwNode.getLineNumber(), throwNode.getToken(), throwNode.getFinish(), newStatements); |
346 } |
346 } |
347 return throwNode; |
347 return throwNode; |
348 } |
348 } |
349 |
349 |
350 @Override |
350 @Override |
358 } |
358 } |
359 |
359 |
360 @Override |
360 @Override |
361 public Node leaveReturnNode(final ReturnNode returnNode) { |
361 public Node leaveReturnNode(final ReturnNode returnNode) { |
362 final Node expr = returnNode.getExpression(); |
362 final Node expr = returnNode.getExpression(); |
363 final List<Node> newStatements = new ArrayList<>(); |
363 final List<Statement> newStatements = new ArrayList<>(); |
364 |
364 |
365 final Node resultNode; |
365 final Node resultNode; |
366 if (expr != null) { |
366 if (expr != null) { |
367 //we need to evaluate the result of the return in case it is complex while |
367 //we need to evaluate the result of the return in case it is complex while |
368 //still in the try block, store it in a result value and return it afterwards |
368 //still in the try block, store it in a result value and return it afterwards |
369 resultNode = new IdentNode(Lower.this.compilerConstant(RETURN)); |
369 resultNode = new IdentNode(Lower.this.compilerConstant(RETURN)); |
370 newStatements.add(new ExecuteNode(new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); |
370 newStatements.add(new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); |
371 } else { |
371 } else { |
372 resultNode = null; |
372 resultNode = null; |
373 } |
373 } |
374 |
374 |
375 newStatements.addAll(copyFinally(finallyBody)); |
375 newStatements.addAll(copyFinally(finallyBody)); |
376 if (!isTerminal(newStatements)) { |
376 if (!isTerminal(newStatements)) { |
377 newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode)); |
377 newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode)); |
378 } |
378 } |
379 |
379 |
380 return new ExecuteNode(new Block(returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements)); |
380 return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements)); |
381 } |
381 } |
382 |
382 |
383 private Node copy(final Node endpoint, final Node targetNode) { |
383 private Node copy(final Statement endpoint, final Node targetNode) { |
384 if (!insideTry.contains(targetNode)) { |
384 if (!insideTry.contains(targetNode)) { |
385 final List<Node> newStatements = copyFinally(finallyBody); |
385 final List<Statement> newStatements = copyFinally(finallyBody); |
386 if (!isTerminal(newStatements)) { |
386 if (!isTerminal(newStatements)) { |
387 newStatements.add(endpoint); |
387 newStatements.add(endpoint); |
388 } |
388 } |
389 return new ExecuteNode(new Block(endpoint.getToken(), finish, newStatements)); |
389 return new ExecuteNode(endpoint.getLineNumber(), endpoint.getToken(), endpoint.getFinish(), new Block(endpoint.getLineNumber(), endpoint.getToken(), finish, newStatements)); |
390 } |
390 } |
391 return endpoint; |
391 return endpoint; |
392 } |
392 } |
393 }); |
393 }); |
394 |
394 |
395 addStatement(newTryNode); |
395 addStatement(newTryNode); |
396 for (final Node statement : finallyBody.getStatements()) { |
396 for (final Node statement : finallyBody.getStatements()) { |
397 addStatement(statement); |
397 addStatement((Statement)statement); |
398 } |
398 } |
399 |
399 |
400 return newTryNode; |
400 return newTryNode; |
401 } |
401 } |
402 |
402 |
446 assert rethrows.size() == 1; |
446 assert rethrows.size() == 1; |
447 |
447 |
448 if (tryNode.getCatchBlocks().isEmpty()) { |
448 if (tryNode.getCatchBlocks().isEmpty()) { |
449 newTryNode = tryNode.setFinallyBody(null); |
449 newTryNode = tryNode.setFinallyBody(null); |
450 } else { |
450 } else { |
451 Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), new ArrayList<Node>(Arrays.asList(tryNode.setFinallyBody(null)))); |
451 Block outerBody = new Block(tryNode.getLineNumber(), tryNode.getToken(), tryNode.getFinish(), new ArrayList<Statement>(Arrays.asList(tryNode.setFinallyBody(null)))); |
452 newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null); |
452 newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null); |
453 } |
453 } |
454 |
454 |
455 newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null); |
455 newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null); |
456 |
456 |
475 final Node test = whileNode.getTest(); |
475 final Node test = whileNode.getTest(); |
476 final Block body = whileNode.getBody(); |
476 final Block body = whileNode.getBody(); |
477 |
477 |
478 if (conservativeAlwaysTrue(test)) { |
478 if (conservativeAlwaysTrue(test)) { |
479 //turn it into a for node without a test. |
479 //turn it into a for node without a test. |
480 final ForNode forNode = (ForNode)new ForNode(whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this); |
480 final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this); |
481 getLexicalContext().replace(whileNode, forNode); |
481 getLexicalContext().replace(whileNode, forNode); |
482 return forNode; |
482 return forNode; |
483 } |
483 } |
484 |
484 |
485 return addStatement(checkEscape(whileNode)); |
485 return addStatement(checkEscape(whileNode)); |
486 } |
486 } |
487 |
487 |
488 @Override |
488 @Override |
489 public Node leaveWithNode(final WithNode withNode) { |
489 public Node leaveWithNode(final WithNode withNode) { |
490 return addStatement(withNode); |
490 return addStatement(withNode); |
491 } |
|
492 |
|
493 @Override |
|
494 public Node leaveDELETE(final UnaryNode unaryNode) { |
|
495 final Node rhs = unaryNode.rhs(); |
|
496 if (rhs instanceof IdentNode || rhs instanceof BaseNode) { |
|
497 return unaryNode; |
|
498 } |
|
499 addStatement(new ExecuteNode(rhs)); |
|
500 return LiteralNode.newInstance(unaryNode, true); |
|
501 } |
491 } |
502 |
492 |
503 /** |
493 /** |
504 * Given a function node that is a callee in a CallNode, replace it with |
494 * Given a function node that is a callee in a CallNode, replace it with |
505 * the appropriate marker function. This is used by {@link CodeGenerator} |
495 * the appropriate marker function. This is used by {@link CodeGenerator} |