src/jdk/nashorn/internal/codegen/Lower.java

Wed, 05 Jun 2013 12:17:10 +0200

author
attila
date
Wed, 05 Jun 2013 12:17:10 +0200
changeset 325
9374c04f38fe
parent 290
6fc7b51e83d6
child 411
ad6b18ee4666
permissions
-rw-r--r--

8015961: Several small code-gardening fixes
Reviewed-by: lagergren, sundar

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.internal.codegen;
jlaskey@3 27
jlaskey@3 28 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
lagergren@211 29 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
jlaskey@3 30 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
jlaskey@3 31
jlaskey@3 32 import java.util.ArrayList;
jlaskey@3 33 import java.util.Arrays;
jlaskey@3 34 import java.util.List;
lagergren@57 35 import jdk.nashorn.internal.ir.BaseNode;
jlaskey@3 36 import jdk.nashorn.internal.ir.BinaryNode;
jlaskey@3 37 import jdk.nashorn.internal.ir.Block;
lagergren@211 38 import jdk.nashorn.internal.ir.BlockLexicalContext;
jlaskey@3 39 import jdk.nashorn.internal.ir.BreakNode;
jlaskey@3 40 import jdk.nashorn.internal.ir.CallNode;
jlaskey@3 41 import jdk.nashorn.internal.ir.CatchNode;
jlaskey@3 42 import jdk.nashorn.internal.ir.ContinueNode;
jlaskey@3 43 import jdk.nashorn.internal.ir.EmptyNode;
jlaskey@3 44 import jdk.nashorn.internal.ir.ExecuteNode;
jlaskey@3 45 import jdk.nashorn.internal.ir.ForNode;
jlaskey@3 46 import jdk.nashorn.internal.ir.FunctionNode;
attila@144 47 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
jlaskey@3 48 import jdk.nashorn.internal.ir.IdentNode;
jlaskey@3 49 import jdk.nashorn.internal.ir.IfNode;
jlaskey@3 50 import jdk.nashorn.internal.ir.LabelNode;
attila@144 51 import jdk.nashorn.internal.ir.LexicalContext;
jlaskey@3 52 import jdk.nashorn.internal.ir.LiteralNode;
lagergren@211 53 import jdk.nashorn.internal.ir.LoopNode;
jlaskey@3 54 import jdk.nashorn.internal.ir.Node;
jlaskey@3 55 import jdk.nashorn.internal.ir.ReturnNode;
lagergren@253 56 import jdk.nashorn.internal.ir.Statement;
jlaskey@3 57 import jdk.nashorn.internal.ir.SwitchNode;
jlaskey@3 58 import jdk.nashorn.internal.ir.Symbol;
jlaskey@3 59 import jdk.nashorn.internal.ir.ThrowNode;
jlaskey@3 60 import jdk.nashorn.internal.ir.TryNode;
jlaskey@3 61 import jdk.nashorn.internal.ir.VarNode;
jlaskey@3 62 import jdk.nashorn.internal.ir.WhileNode;
jlaskey@3 63 import jdk.nashorn.internal.ir.WithNode;
jlaskey@3 64 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
jlaskey@3 65 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
jlaskey@3 66 import jdk.nashorn.internal.parser.Token;
jlaskey@3 67 import jdk.nashorn.internal.parser.TokenType;
jlaskey@3 68 import jdk.nashorn.internal.runtime.DebugLogger;
jlaskey@3 69 import jdk.nashorn.internal.runtime.ScriptRuntime;
jlaskey@3 70 import jdk.nashorn.internal.runtime.Source;
jlaskey@3 71
jlaskey@3 72 /**
lagergren@57 73 * Lower to more primitive operations. After lowering, an AST still has no symbols
lagergren@57 74 * and types, but several nodes have been turned into more low level constructs
lagergren@57 75 * and control flow termination criteria have been computed.
jlaskey@3 76 *
lagergren@57 77 * We do things like code copying/inlining of finallies here, as it is much
lagergren@57 78 * harder and context dependent to do any code copying after symbols have been
lagergren@57 79 * finalized.
jlaskey@3 80 */
jlaskey@3 81
lagergren@290 82 final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
lagergren@57 83
lagergren@57 84 private static final DebugLogger LOG = new DebugLogger("lower");
lagergren@57 85
jlaskey@3 86 /**
jlaskey@3 87 * Constructor.
jlaskey@3 88 *
jlaskey@3 89 * @param compiler the compiler
jlaskey@3 90 */
lagergren@89 91 Lower() {
lagergren@211 92 super(new BlockLexicalContext() {
lagergren@211 93
lagergren@211 94 @Override
lagergren@253 95 public List<Statement> popStatements() {
lagergren@253 96 final List<Statement> newStatements = new ArrayList<>();
lagergren@211 97 boolean terminated = false;
lagergren@211 98
lagergren@253 99 final List<Statement> statements = super.popStatements();
lagergren@253 100 for (final Statement statement : statements) {
lagergren@211 101 if (!terminated) {
lagergren@211 102 newStatements.add(statement);
lagergren@253 103 if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why?
lagergren@211 104 terminated = true;
lagergren@211 105 }
lagergren@211 106 } else {
lagergren@290 107 statement.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
lagergren@253 108 @Override
lagergren@253 109 public boolean enterVarNode(final VarNode varNode) {
lagergren@253 110 newStatements.add(varNode.setInit(null));
lagergren@253 111 return false;
lagergren@253 112 }
lagergren@253 113 });
lagergren@211 114 }
lagergren@211 115 }
lagergren@211 116 return newStatements;
lagergren@211 117 }
lagergren@211 118 });
jlaskey@3 119 }
jlaskey@3 120
jlaskey@3 121 @Override
lagergren@211 122 public boolean enterBlock(final Block block) {
lagergren@252 123 final FunctionNode function = lc.getCurrentFunction();
lagergren@252 124 if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
lagergren@253 125 new ExecuteNode(block.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
lagergren@57 126 }
lagergren@211 127 return true;
lagergren@57 128 }
lagergren@57 129
lagergren@57 130 @Override
lagergren@211 131 public Node leaveBlock(final Block block) {
lagergren@211 132 //now we have committed the entire statement list to the block, but we need to truncate
lagergren@211 133 //whatever is after the last terminal. block append won't append past it
lagergren@211 134
lagergren@253 135 Statement last = lc.getLastStatement();
lagergren@211 136
lagergren@211 137 if (lc.isFunctionBody()) {
lagergren@290 138 final FunctionNode currentFunction = lc.getCurrentFunction();
lagergren@211 139 final boolean isProgram = currentFunction.isProgram();
lagergren@211 140 final ReturnNode returnNode = new ReturnNode(
lagergren@253 141 last == null ? block.getLineNumber() : last.getLineNumber(), //TODO?
lagergren@211 142 currentFunction.getToken(),
lagergren@211 143 currentFunction.getFinish(),
lagergren@211 144 isProgram ?
lagergren@211 145 compilerConstant(RETURN) :
lagergren@211 146 LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
lagergren@211 147
lagergren@253 148 last = (Statement)returnNode.accept(this);
lagergren@211 149 }
lagergren@211 150
lagergren@211 151 if (last != null && last.isTerminal()) {
lagergren@211 152 return block.setIsTerminal(lc, true);
lagergren@211 153 }
lagergren@211 154
lagergren@211 155 return block;
lagergren@57 156 }
lagergren@57 157
lagergren@57 158 @Override
lagergren@211 159 public boolean enterBreakNode(final BreakNode breakNode) {
lagergren@211 160 addStatement(breakNode);
lagergren@211 161 return false;
lagergren@57 162 }
lagergren@57 163
lagergren@57 164 @Override
lagergren@211 165 public Node leaveCallNode(final CallNode callNode) {
lagergren@211 166 return checkEval(callNode.setFunction(markerFunction(callNode.getFunction())));
lagergren@57 167 }
lagergren@57 168
lagergren@57 169 @Override
attila@144 170 public Node leaveCatchNode(final CatchNode catchNode) {
lagergren@211 171 return addStatement(catchNode);
lagergren@57 172 }
lagergren@57 173
lagergren@57 174 @Override
lagergren@211 175 public boolean enterContinueNode(final ContinueNode continueNode) {
lagergren@211 176 addStatement(continueNode);
lagergren@211 177 return false;
lagergren@57 178 }
lagergren@57 179
lagergren@57 180 @Override
lagergren@211 181 public boolean enterEmptyNode(final EmptyNode emptyNode) {
lagergren@211 182 return false;
lagergren@57 183 }
lagergren@57 184
lagergren@57 185 @Override
attila@144 186 public Node leaveExecuteNode(final ExecuteNode executeNode) {
lagergren@57 187 final Node expr = executeNode.getExpression();
lagergren@211 188 ExecuteNode node = executeNode;
lagergren@57 189
lagergren@290 190 final FunctionNode currentFunction = lc.getCurrentFunction();
lagergren@211 191
lagergren@211 192 if (currentFunction.isProgram()) {
attila@144 193 if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
lagergren@57 194 if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
lagergren@211 195 node = executeNode.setExpression(
lagergren@211 196 new BinaryNode(
lagergren@211 197 Token.recast(
lagergren@211 198 executeNode.getToken(),
lagergren@211 199 TokenType.ASSIGN),
lagergren@211 200 compilerConstant(RETURN),
lagergren@211 201 expr));
lagergren@57 202 }
lagergren@57 203 }
lagergren@57 204 }
lagergren@57 205
lagergren@211 206 return addStatement(node);
lagergren@57 207 }
lagergren@57 208
lagergren@57 209 @Override
attila@144 210 public Node leaveForNode(final ForNode forNode) {
lagergren@211 211 ForNode newForNode = forNode;
lagergren@211 212
lagergren@57 213 final Node test = forNode.getTest();
lagergren@211 214 if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
lagergren@290 215 newForNode = forNode.setTest(lc, null);
lagergren@57 216 }
lagergren@57 217
lagergren@211 218 return addStatement(checkEscape(newForNode));
lagergren@57 219 }
lagergren@57 220
lagergren@57 221 @Override
lagergren@211 222 public boolean enterFunctionNode(final FunctionNode functionNode) {
lagergren@211 223 return !functionNode.isLazy();
attila@144 224 }
attila@144 225
lagergren@57 226 @Override
lagergren@211 227 public Node leaveFunctionNode(final FunctionNode functionNode) {
lagergren@211 228 LOG.info("END FunctionNode: ", functionNode.getName());
lagergren@290 229 return functionNode.setState(lc, CompilationState.LOWERED);
lagergren@57 230 }
lagergren@57 231
lagergren@57 232 @Override
attila@144 233 public Node leaveIfNode(final IfNode ifNode) {
lagergren@211 234 return addStatement(ifNode);
lagergren@57 235 }
lagergren@57 236
lagergren@57 237 @Override
lagergren@211 238 public Node leaveLabelNode(final LabelNode labelNode) {
lagergren@211 239 return addStatement(labelNode);
lagergren@57 240 }
lagergren@57 241
lagergren@57 242 @Override
attila@144 243 public Node leaveReturnNode(final ReturnNode returnNode) {
lagergren@57 244 addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
lagergren@57 245 return returnNode;
lagergren@57 246 }
lagergren@57 247
lagergren@57 248
lagergren@57 249 @Override
attila@144 250 public Node leaveSwitchNode(final SwitchNode switchNode) {
lagergren@211 251 return addStatement(switchNode);
lagergren@57 252 }
lagergren@57 253
lagergren@57 254 @Override
attila@144 255 public Node leaveThrowNode(final ThrowNode throwNode) {
lagergren@57 256 addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
lagergren@57 257 return throwNode;
lagergren@57 258 }
lagergren@57 259
attila@325 260 private static Node ensureUniqueNamesIn(final Node node) {
lagergren@290 261 return node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
lagergren@272 262 @Override
lagergren@272 263 public Node leaveFunctionNode(final FunctionNode functionNode) {
lagergren@272 264 final String name = functionNode.getName();
lagergren@290 265 return functionNode.setName(lc, lc.getCurrentFunction().uniqueName(name));
lagergren@272 266 }
lagergren@272 267
lagergren@272 268 @Override
lagergren@272 269 public Node leaveDefault(final Node labelledNode) {
lagergren@290 270 return labelledNode.ensureUniqueLabels(lc);
lagergren@272 271 }
lagergren@211 272 });
lagergren@211 273 }
lagergren@57 274
attila@325 275 private static List<Statement> copyFinally(final Block finallyBody) {
lagergren@253 276 final List<Statement> newStatements = new ArrayList<>();
lagergren@253 277 for (final Statement statement : finallyBody.getStatements()) {
attila@325 278 newStatements.add((Statement)ensureUniqueNamesIn(statement));
lagergren@211 279 if (statement.hasTerminalFlags()) {
lagergren@211 280 return newStatements;
lagergren@211 281 }
lagergren@211 282 }
lagergren@211 283 return newStatements;
lagergren@211 284 }
lagergren@57 285
lagergren@211 286 private Block catchAllBlock(final TryNode tryNode) {
lagergren@253 287 final int lineNumber = tryNode.getLineNumber();
lagergren@253 288 final long token = tryNode.getToken();
lagergren@253 289 final int finish = tryNode.getFinish();
lagergren@211 290
lagergren@290 291 final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName("catch_all"));
lagergren@211 292
lagergren@265 293 final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW)).
lagergren@290 294 setIsTerminal(lc, true); //ends with throw, so terminal
lagergren@211 295
lagergren@265 296 final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, CatchNode.IS_SYNTHETIC_RETHROW);
lagergren@253 297 final Block catchAllBlock = new Block(lineNumber, token, finish, catchAllNode);
lagergren@211 298
lagergren@211 299 //catchallblock -> catchallnode (catchnode) -> exception -> throw
lagergren@211 300
lagergren@211 301 return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower
lagergren@211 302 }
lagergren@211 303
lagergren@211 304 private IdentNode compilerConstant(final CompilerConstants cc) {
lagergren@290 305 final FunctionNode functionNode = lc.getCurrentFunction();
lagergren@252 306 return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
lagergren@211 307 }
lagergren@211 308
lagergren@253 309 private static boolean isTerminal(final List<Statement> statements) {
lagergren@211 310 return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
lagergren@211 311 }
lagergren@211 312
lagergren@211 313 /**
lagergren@211 314 * Splice finally code into all endpoints of a trynode
lagergren@211 315 * @param tryNode the try node
lagergren@211 316 * @param list of rethrowing throw nodes from synthetic catch blocks
lagergren@211 317 * @param finallyBody the code in the original finally block
lagergren@211 318 * @return new try node after splicing finally code (same if nop)
lagergren@211 319 */
lagergren@211 320 private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
lagergren@211 321 assert tryNode.getFinallyBody() == null;
lagergren@272 322 final int finish = tryNode.getFinish();
lagergren@211 323
lagergren@290 324 final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
lagergren@211 325 final List<Node> insideTry = new ArrayList<>();
lagergren@211 326
lagergren@211 327 @Override
lagergren@211 328 public boolean enterDefault(final Node node) {
lagergren@211 329 insideTry.add(node);
lagergren@211 330 return true;
lagergren@211 331 }
lagergren@211 332
lagergren@211 333 @Override
lagergren@211 334 public boolean enterFunctionNode(final FunctionNode functionNode) {
lagergren@211 335 // do not enter function nodes - finally code should not be inlined into them
lagergren@211 336 return false;
lagergren@211 337 }
lagergren@211 338
lagergren@211 339 @Override
lagergren@211 340 public Node leaveThrowNode(final ThrowNode throwNode) {
lagergren@211 341 if (rethrows.contains(throwNode)) {
attila@325 342 final List<Statement> newStatements = copyFinally(finallyBody);
lagergren@211 343 if (!isTerminal(newStatements)) {
lagergren@211 344 newStatements.add(throwNode);
lagergren@211 345 }
lagergren@253 346 return new Block(throwNode.getLineNumber(), throwNode.getToken(), throwNode.getFinish(), newStatements);
lagergren@211 347 }
lagergren@211 348 return throwNode;
lagergren@211 349 }
lagergren@211 350
lagergren@211 351 @Override
lagergren@211 352 public Node leaveBreakNode(final BreakNode breakNode) {
lagergren@290 353 return copy(breakNode, Lower.this.lc.getBreakable(breakNode.getLabel()));
lagergren@211 354 }
lagergren@211 355
lagergren@211 356 @Override
lagergren@211 357 public Node leaveContinueNode(final ContinueNode continueNode) {
lagergren@290 358 return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabel()));
lagergren@211 359 }
lagergren@211 360
lagergren@211 361 @Override
lagergren@211 362 public Node leaveReturnNode(final ReturnNode returnNode) {
lagergren@211 363 final Node expr = returnNode.getExpression();
lagergren@253 364 final List<Statement> newStatements = new ArrayList<>();
lagergren@211 365
lagergren@211 366 final Node resultNode;
lagergren@211 367 if (expr != null) {
lagergren@211 368 //we need to evaluate the result of the return in case it is complex while
lagergren@211 369 //still in the try block, store it in a result value and return it afterwards
lagergren@211 370 resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
lagergren@253 371 newStatements.add(new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
lagergren@211 372 } else {
lagergren@211 373 resultNode = null;
lagergren@211 374 }
lagergren@211 375
attila@325 376 newStatements.addAll(copyFinally(finallyBody));
lagergren@211 377 if (!isTerminal(newStatements)) {
lagergren@211 378 newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
lagergren@211 379 }
lagergren@211 380
lagergren@290 381 return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), lc.getCurrentBlock().getFinish(), newStatements));
lagergren@211 382 }
lagergren@211 383
lagergren@253 384 private Node copy(final Statement endpoint, final Node targetNode) {
lagergren@211 385 if (!insideTry.contains(targetNode)) {
attila@325 386 final List<Statement> newStatements = copyFinally(finallyBody);
lagergren@211 387 if (!isTerminal(newStatements)) {
lagergren@211 388 newStatements.add(endpoint);
lagergren@211 389 }
lagergren@253 390 return new ExecuteNode(endpoint.getLineNumber(), endpoint.getToken(), endpoint.getFinish(), new Block(endpoint.getLineNumber(), endpoint.getToken(), finish, newStatements));
lagergren@211 391 }
lagergren@211 392 return endpoint;
lagergren@211 393 }
lagergren@211 394 });
lagergren@211 395
lagergren@211 396 addStatement(newTryNode);
lagergren@211 397 for (final Node statement : finallyBody.getStatements()) {
lagergren@253 398 addStatement((Statement)statement);
lagergren@57 399 }
lagergren@57 400
lagergren@211 401 return newTryNode;
lagergren@57 402 }
lagergren@57 403
lagergren@57 404 @Override
attila@144 405 public Node leaveTryNode(final TryNode tryNode) {
lagergren@211 406 final Block finallyBody = tryNode.getFinallyBody();
lagergren@57 407
lagergren@211 408 if (finallyBody == null) {
lagergren@211 409 return addStatement(tryNode);
lagergren@57 410 }
lagergren@57 411
lagergren@211 412 /*
lagergren@211 413 * create a new trynode
lagergren@211 414 * if we have catches:
lagergren@211 415 *
lagergren@211 416 * try try
lagergren@211 417 * x try
lagergren@211 418 * catch x
lagergren@211 419 * y catch
lagergren@211 420 * finally z y
lagergren@211 421 * catchall
lagergren@211 422 * rethrow
lagergren@211 423 *
lagergren@211 424 * otheriwse
lagergren@211 425 *
lagergren@211 426 * try try
lagergren@211 427 * x x
lagergren@211 428 * finally catchall
lagergren@211 429 * y rethrow
lagergren@211 430 *
lagergren@211 431 *
lagergren@211 432 * now splice in finally code wherever needed
lagergren@211 433 *
lagergren@211 434 */
lagergren@211 435 TryNode newTryNode;
lagergren@57 436
lagergren@211 437 final Block catchAll = catchAllBlock(tryNode);
lagergren@57 438
lagergren@211 439 final List<ThrowNode> rethrows = new ArrayList<>();
lagergren@290 440 catchAll.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
lagergren@211 441 @Override
lagergren@211 442 public boolean enterThrowNode(final ThrowNode throwNode) {
lagergren@211 443 rethrows.add(throwNode);
lagergren@211 444 return true;
lagergren@211 445 }
lagergren@211 446 });
lagergren@211 447 assert rethrows.size() == 1;
lagergren@211 448
lagergren@211 449 if (tryNode.getCatchBlocks().isEmpty()) {
lagergren@211 450 newTryNode = tryNode.setFinallyBody(null);
lagergren@211 451 } else {
lagergren@253 452 Block outerBody = new Block(tryNode.getLineNumber(), tryNode.getToken(), tryNode.getFinish(), new ArrayList<Statement>(Arrays.asList(tryNode.setFinallyBody(null))));
lagergren@211 453 newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
lagergren@57 454 }
lagergren@57 455
lagergren@211 456 newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
lagergren@211 457
lagergren@211 458 /*
lagergren@211 459 * Now that the transform is done, we have to go into the try and splice
lagergren@211 460 * the finally block in front of any statement that is outside the try
lagergren@211 461 */
lagergren@211 462 return spliceFinally(newTryNode, rethrows, finallyBody);
lagergren@57 463 }
lagergren@57 464
lagergren@57 465 @Override
attila@144 466 public Node leaveVarNode(final VarNode varNode) {
lagergren@57 467 addStatement(varNode);
lagergren@290 468 if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && lc.getCurrentFunction().isProgram()) {
lagergren@253 469 new ExecuteNode(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
lagergren@211 470 }
lagergren@57 471 return varNode;
lagergren@57 472 }
lagergren@57 473
lagergren@57 474 @Override
attila@144 475 public Node leaveWhileNode(final WhileNode whileNode) {
lagergren@252 476 final Node test = whileNode.getTest();
lagergren@211 477 final Block body = whileNode.getBody();
lagergren@57 478
lagergren@211 479 if (conservativeAlwaysTrue(test)) {
lagergren@211 480 //turn it into a for node without a test.
lagergren@253 481 final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
lagergren@290 482 lc.replace(whileNode, forNode);
lagergren@211 483 return forNode;
lagergren@57 484 }
lagergren@57 485
lagergren@211 486 return addStatement(checkEscape(whileNode));
lagergren@57 487 }
lagergren@57 488
lagergren@57 489 @Override
attila@144 490 public Node leaveWithNode(final WithNode withNode) {
lagergren@211 491 return addStatement(withNode);
lagergren@57 492 }
lagergren@57 493
lagergren@57 494 /**
lagergren@57 495 * Given a function node that is a callee in a CallNode, replace it with
lagergren@57 496 * the appropriate marker function. This is used by {@link CodeGenerator}
lagergren@57 497 * for fast scope calls
lagergren@57 498 *
lagergren@57 499 * @param function function called by a CallNode
lagergren@57 500 * @return transformed node to marker function or identity if not ident/access/indexnode
lagergren@57 501 */
lagergren@57 502 private static Node markerFunction(final Node function) {
lagergren@57 503 if (function instanceof IdentNode) {
attila@144 504 return ((IdentNode)function).setIsFunction();
attila@144 505 } else if (function instanceof BaseNode) {
attila@144 506 return ((BaseNode)function).setIsFunction();
lagergren@57 507 }
lagergren@57 508 return function;
lagergren@57 509 }
lagergren@57 510
lagergren@57 511 /**
lagergren@57 512 * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval>
lagergren@57 513 * @param node a node
lagergren@57 514 * @return eval location
lagergren@57 515 */
lagergren@252 516 private String evalLocation(final IdentNode node) {
lagergren@290 517 final Source source = lc.getCurrentFunction().getSource();
lagergren@57 518 return new StringBuilder().
lagergren@252 519 append(source.getName()).
lagergren@57 520 append('#').
lagergren@252 521 append(source.getLine(node.position())).
lagergren@57 522 append("<eval>").
lagergren@57 523 toString();
lagergren@57 524 }
lagergren@57 525
lagergren@57 526 /**
lagergren@57 527 * Check whether a call node may be a call to eval. In that case we
lagergren@57 528 * clone the args in order to create the following construct in
lagergren@57 529 * {@link CodeGenerator}
lagergren@57 530 *
lagergren@57 531 * <pre>
lagergren@57 532 * if (calledFuntion == buildInEval) {
lagergren@57 533 * eval(cloned arg);
lagergren@57 534 * } else {
lagergren@57 535 * cloned arg;
lagergren@57 536 * }
lagergren@57 537 * </pre>
lagergren@57 538 *
lagergren@57 539 * @param callNode call node to check if it's an eval
lagergren@57 540 */
lagergren@211 541 private CallNode checkEval(final CallNode callNode) {
lagergren@57 542 if (callNode.getFunction() instanceof IdentNode) {
lagergren@57 543
lagergren@57 544 final List<Node> args = callNode.getArgs();
lagergren@57 545 final IdentNode callee = (IdentNode)callNode.getFunction();
lagergren@57 546
lagergren@57 547 // 'eval' call with at least one argument
lagergren@211 548 if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) {
lagergren@290 549 final FunctionNode currentFunction = lc.getCurrentFunction();
lagergren@211 550 return callNode.setEvalArgs(
lagergren@57 551 new CallNode.EvalArgs(
attila@325 552 ensureUniqueNamesIn(args.get(0)).accept(this),
lagergren@211 553 compilerConstant(THIS),
lagergren@57 554 evalLocation(callee),
lagergren@211 555 currentFunction.isStrict()));
lagergren@57 556 }
lagergren@57 557 }
lagergren@211 558
lagergren@211 559 return callNode;
lagergren@57 560 }
lagergren@57 561
lagergren@57 562 private static boolean conservativeAlwaysTrue(final Node node) {
lagergren@57 563 return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue()));
lagergren@57 564 }
lagergren@57 565
lagergren@57 566 /**
lagergren@57 567 * Helper that given a loop body makes sure that it is not terminal if it
lagergren@57 568 * has a continue that leads to the loop header or to outer loops' loop
lagergren@57 569 * headers. This means that, even if the body ends with a terminal
lagergren@57 570 * statement, we cannot tag it as terminal
lagergren@57 571 *
lagergren@57 572 * @param loopBody the loop body to check
lagergren@57 573 * @return true if control flow may escape the loop
lagergren@57 574 */
lagergren@211 575 private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
lagergren@57 576 final List<Node> escapes = new ArrayList<>();
lagergren@57 577
lagergren@290 578 loopBody.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
lagergren@57 579 @Override
attila@144 580 public Node leaveBreakNode(final BreakNode node) {
lagergren@57 581 escapes.add(node);
lagergren@57 582 return node;
lagergren@57 583 }
lagergren@57 584
lagergren@57 585 @Override
attila@144 586 public Node leaveContinueNode(final ContinueNode node) {
lagergren@57 587 // all inner loops have been popped.
lagergren@211 588 if (lex.contains(lex.getContinueTo(node.getLabel()))) {
lagergren@57 589 escapes.add(node);
lagergren@57 590 }
lagergren@57 591 return node;
lagergren@57 592 }
lagergren@57 593 });
lagergren@57 594
lagergren@57 595 return !escapes.isEmpty();
lagergren@57 596 }
lagergren@57 597
lagergren@211 598 private LoopNode checkEscape(final LoopNode loopNode) {
lagergren@211 599 final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
lagergren@211 600 if (escapes) {
lagergren@211 601 return loopNode.
lagergren@211 602 setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
lagergren@211 603 setControlFlowEscapes(lc, escapes);
lagergren@57 604 }
lagergren@211 605 return loopNode;
lagergren@57 606 }
lagergren@57 607
lagergren@57 608
lagergren@253 609 private Node addStatement(final Statement statement) {
lagergren@290 610 lc.appendStatement(statement);
lagergren@211 611 return statement;
lagergren@57 612 }
lagergren@57 613
jlaskey@3 614 /**
jlaskey@3 615 * An internal expression has a symbol that is tagged internal. Check if
jlaskey@3 616 * this is such a node
jlaskey@3 617 *
jlaskey@3 618 * @param expression expression to check for internal symbol
jlaskey@3 619 * @return true if internal, false otherwise
jlaskey@3 620 */
jlaskey@3 621 private static boolean isInternalExpression(final Node expression) {
jlaskey@3 622 final Symbol symbol = expression.getSymbol();
jlaskey@3 623 return symbol != null && symbol.isInternal();
jlaskey@3 624 }
jlaskey@3 625
jlaskey@3 626 /**
lagergren@57 627 * Is this an assignment to the special variable that hosts scripting eval
lagergren@211 628 * results, i.e. __return__?
jlaskey@3 629 *
lagergren@57 630 * @param expression expression to check whether it is $evalresult = X
lagergren@57 631 * @return true if an assignment to eval result, false otherwise
jlaskey@3 632 */
lagergren@211 633 private static boolean isEvalResultAssignment(final Node expression) {
lagergren@57 634 Node e = expression;
lagergren@211 635 assert e.tokenType() != TokenType.DISCARD; //there are no discards this early anymore
lagergren@211 636 if (e instanceof BinaryNode) {
lagergren@211 637 final Node lhs = ((BinaryNode)e).lhs();
lagergren@211 638 if (lhs instanceof IdentNode) {
lagergren@211 639 return ((IdentNode)lhs).getName().equals(RETURN.symbolName());
attila@62 640 }
attila@62 641 }
lagergren@211 642 return false;
jlaskey@3 643 }
jlaskey@3 644
lagergren@57 645 }

mercurial