1.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java Tue Jan 29 14:25:39 2013 -0400 1.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Wed Jan 30 12:26:45 2013 +0100 1.3 @@ -28,47 +28,19 @@ 1.4 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; 1.5 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 1.6 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL; 1.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; 1.8 -import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; 1.9 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 1.10 import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN; 1.11 -import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX; 1.12 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 1.13 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 1.14 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.ADD; 1.15 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.DELETE; 1.16 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ; 1.17 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ_STRICT; 1.18 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.FAIL_DELETE; 1.19 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.GE; 1.20 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.GT; 1.21 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.IN; 1.22 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.INSTANCEOF; 1.23 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.LE; 1.24 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.LT; 1.25 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE; 1.26 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE_STRICT; 1.27 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.TYPEOF; 1.28 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.VOID; 1.29 -import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; 1.30 -import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 1.31 -import static jdk.nashorn.internal.ir.Symbol.IS_LET; 1.32 -import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; 1.33 -import static jdk.nashorn.internal.ir.Symbol.IS_THIS; 1.34 -import static jdk.nashorn.internal.ir.Symbol.IS_VAR; 1.35 -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 1.36 1.37 import java.util.ArrayDeque; 1.38 import java.util.ArrayList; 1.39 import java.util.Arrays; 1.40 import java.util.Deque; 1.41 -import java.util.HashSet; 1.42 -import java.util.LinkedList; 1.43 import java.util.List; 1.44 -import java.util.Set; 1.45 -import jdk.nashorn.internal.codegen.types.Type; 1.46 + 1.47 import jdk.nashorn.internal.ir.AccessNode; 1.48 -import jdk.nashorn.internal.ir.Assignment; 1.49 +import jdk.nashorn.internal.ir.BaseNode; 1.50 import jdk.nashorn.internal.ir.BinaryNode; 1.51 import jdk.nashorn.internal.ir.Block; 1.52 import jdk.nashorn.internal.ir.BreakNode; 1.53 @@ -85,19 +57,13 @@ 1.54 import jdk.nashorn.internal.ir.IfNode; 1.55 import jdk.nashorn.internal.ir.IndexNode; 1.56 import jdk.nashorn.internal.ir.LabelNode; 1.57 +import jdk.nashorn.internal.ir.LabeledNode; 1.58 import jdk.nashorn.internal.ir.LineNumberNode; 1.59 import jdk.nashorn.internal.ir.LiteralNode; 1.60 -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 1.61 import jdk.nashorn.internal.ir.Node; 1.62 -import jdk.nashorn.internal.ir.ObjectNode; 1.63 -import jdk.nashorn.internal.ir.PropertyNode; 1.64 -import jdk.nashorn.internal.ir.ReferenceNode; 1.65 import jdk.nashorn.internal.ir.ReturnNode; 1.66 -import jdk.nashorn.internal.ir.RuntimeNode; 1.67 -import jdk.nashorn.internal.ir.RuntimeNode.Request; 1.68 import jdk.nashorn.internal.ir.SwitchNode; 1.69 import jdk.nashorn.internal.ir.Symbol; 1.70 -import jdk.nashorn.internal.ir.TernaryNode; 1.71 import jdk.nashorn.internal.ir.ThrowNode; 1.72 import jdk.nashorn.internal.ir.TryNode; 1.73 import jdk.nashorn.internal.ir.UnaryNode; 1.74 @@ -108,54 +74,26 @@ 1.75 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 1.76 import jdk.nashorn.internal.parser.Token; 1.77 import jdk.nashorn.internal.parser.TokenType; 1.78 -import jdk.nashorn.internal.runtime.Context; 1.79 import jdk.nashorn.internal.runtime.DebugLogger; 1.80 -import jdk.nashorn.internal.runtime.ECMAException; 1.81 -import jdk.nashorn.internal.runtime.JSType; 1.82 -import jdk.nashorn.internal.runtime.Property; 1.83 -import jdk.nashorn.internal.runtime.PropertyMap; 1.84 -import jdk.nashorn.internal.runtime.ScriptFunction; 1.85 -import jdk.nashorn.internal.runtime.ScriptObject; 1.86 import jdk.nashorn.internal.runtime.ScriptRuntime; 1.87 import jdk.nashorn.internal.runtime.Source; 1.88 -import jdk.nashorn.internal.runtime.Undefined; 1.89 1.90 /** 1.91 - * Lower to more primitive operations. After lowering, an AST has symbols and 1.92 - * types. Lowering may also add specialized versions of methods to the script if 1.93 - * the optimizer is turned on. 1.94 + * Lower to more primitive operations. After lowering, an AST still has no symbols 1.95 + * and types, but several nodes have been turned into more low level constructs 1.96 + * and control flow termination criteria have been computed. 1.97 * 1.98 - * Any expression that requires temporary storage as part of computation will 1.99 - * also be detected here and give a temporary symbol 1.100 + * We do things like code copying/inlining of finallies here, as it is much 1.101 + * harder and context dependent to do any code copying after symbols have been 1.102 + * finalized. 1.103 */ 1.104 1.105 final class Lower extends NodeOperatorVisitor { 1.106 - /** Current compiler. */ 1.107 + 1.108 private final Compiler compiler; 1.109 1.110 - /** Current source. */ 1.111 private final Source source; 1.112 1.113 - /** List of lowered statements */ 1.114 - private List<Node> statements; 1.115 - 1.116 - /** All symbols that are declared locally in a function node */ 1.117 - private List<Symbol> declaredSymbolsLocal; 1.118 - 1.119 - /** 1.120 - * Local definitions in current block (to discriminate from function 1.121 - * declarations always defined in the function scope. This is for 1.122 - * "can be undefined" analysis. 1.123 - */ 1.124 - private Set<String> localDefs; 1.125 - 1.126 - /** 1.127 - * Local definitions in current block to guard against cases like 1.128 - * NASHORN-467 when things can be undefined as they are used before 1.129 - * their local var definition. *sigh* JavaScript... 1.130 - */ 1.131 - private Set<String> localUses; 1.132 - 1.133 /** 1.134 * Nesting level stack. Currently just used for loops to avoid the problem 1.135 * with terminal bodies that end with throw/return but still do continues to 1.136 @@ -163,8 +101,11 @@ 1.137 */ 1.138 private final Deque<Node> nesting; 1.139 1.140 - private static final DebugLogger LOG = new DebugLogger("lower"); 1.141 - private static final boolean DEBUG = LOG.isEnabled(); 1.142 + private static final DebugLogger LOG = new DebugLogger("lower"); 1.143 + 1.144 + private Node lastStatement; 1.145 + 1.146 + private List<Node> statements; 1.147 1.148 /** 1.149 * Constructor. 1.150 @@ -174,52 +115,17 @@ 1.151 Lower(final Compiler compiler) { 1.152 this.compiler = compiler; 1.153 this.source = compiler.getSource(); 1.154 + this.nesting = new ArrayDeque<>(); 1.155 this.statements = new ArrayList<>(); 1.156 - this.nesting = new ArrayDeque<>(); 1.157 - } 1.158 - 1.159 - private void nest(final Node node) { 1.160 - nesting.push(node); 1.161 - } 1.162 - 1.163 - private void unnest() { 1.164 - nesting.pop(); 1.165 - } 1.166 - 1.167 - static void debug(final String str) { 1.168 - if (DEBUG) { 1.169 - LOG.info(str); 1.170 - } 1.171 - } 1.172 - 1.173 - @Override 1.174 - public Node leave(final AccessNode accessNode) { 1.175 - //accessNode.setBase(convert(accessNode.getBase(), Type.OBJECT)); 1.176 - getCurrentFunctionNode().newTemporary(Type.OBJECT, accessNode); //This is not always an object per se, but currently the narrowing logic resides in AccessSpecializer. @see AccessSpecializer! 1.177 - 1.178 - return accessNode; 1.179 } 1.180 1.181 @Override 1.182 public Node enter(final Block block) { 1.183 - /* 1.184 - * Save the statement list from the outer construct we are currently 1.185 - * generating and push frame 1.186 - */ 1.187 - final List<Node> savedStatements = statements; 1.188 - final Set<String> savedDefs = localDefs; 1.189 - final Set<String> savedUses = localUses; 1.190 - 1.191 - block.setFrame(getCurrentFunctionNode().pushFrame()); 1.192 + final Node savedLastStatement = lastStatement; 1.193 + final List<Node> savedStatements = statements; 1.194 1.195 try { 1.196 - /* 1.197 - * Reset the statement instance var, new block 1.198 - */ 1.199 - statements = new ArrayList<>(); 1.200 - localDefs = new HashSet<>(savedDefs); 1.201 - localUses = new HashSet<>(savedUses); 1.202 - 1.203 + this.statements = new ArrayList<>(); 1.204 for (final Node statement : block.getStatements()) { 1.205 statement.accept(this); 1.206 /* 1.207 @@ -231,28 +137,715 @@ 1.208 * 1.209 * @see NASHORN-285 1.210 */ 1.211 - final Node lastStatement = Node.lastStatement(statements); 1.212 if (lastStatement != null && lastStatement.isTerminal()) { 1.213 - block.copyTerminalFlags(lastStatement); 1.214 + copyTerminal(block, lastStatement); 1.215 + break; 1.216 + } 1.217 + } 1.218 + block.setStatements(statements); 1.219 + 1.220 + } finally { 1.221 + this.statements = savedStatements; 1.222 + this.lastStatement = savedLastStatement; 1.223 + } 1.224 + 1.225 + return null; 1.226 + } 1.227 + 1.228 + @Override 1.229 + public Node enter(final BreakNode breakNode) { 1.230 + return enterBreakOrContinue(breakNode); 1.231 + } 1.232 + 1.233 + @Override 1.234 + public Node enter(final CallNode callNode) { 1.235 + final Node function = markerFunction(callNode.getFunction()); 1.236 + callNode.setFunction(function); 1.237 + checkEval(callNode); //check if this is an eval call and store the information 1.238 + return callNode; 1.239 + } 1.240 + 1.241 + @Override 1.242 + public Node leave(final CaseNode caseNode) { 1.243 + caseNode.copyTerminalFlags(caseNode.getBody()); 1.244 + return caseNode; 1.245 + } 1.246 + 1.247 + @Override 1.248 + public Node leave(final CatchNode catchNode) { 1.249 + catchNode.copyTerminalFlags(catchNode.getBody()); 1.250 + addStatement(catchNode); 1.251 + return catchNode; 1.252 + } 1.253 + 1.254 + @Override 1.255 + public Node enter(final ContinueNode continueNode) { 1.256 + return enterBreakOrContinue(continueNode); 1.257 + } 1.258 + 1.259 + @Override 1.260 + public Node enter(final DoWhileNode doWhileNode) { 1.261 + return enter((WhileNode)doWhileNode); 1.262 + } 1.263 + 1.264 + @Override 1.265 + public Node leave(final DoWhileNode doWhileNode) { 1.266 + return leave((WhileNode)doWhileNode); 1.267 + } 1.268 + 1.269 + @Override 1.270 + public Node enter(final EmptyNode emptyNode) { 1.271 + return null; 1.272 + } 1.273 + 1.274 + @Override 1.275 + public Node leave(final ExecuteNode executeNode) { 1.276 + final Node expr = executeNode.getExpression(); 1.277 + 1.278 + if (getCurrentFunctionNode().isScript()) { 1.279 + if (!(expr instanceof Block)) { 1.280 + if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) { 1.281 + executeNode.setExpression(new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN), 1.282 + getCurrentFunctionNode().getResultNode(), 1.283 + expr)); 1.284 + } 1.285 + } 1.286 + } 1.287 + 1.288 + copyTerminal(executeNode, executeNode.getExpression()); 1.289 + addStatement(executeNode); 1.290 + 1.291 + return executeNode; 1.292 + } 1.293 + 1.294 + @Override 1.295 + public Node enter(final ForNode forNode) { 1.296 + nest(forNode); 1.297 + return forNode; 1.298 + } 1.299 + 1.300 + @Override 1.301 + public Node leave(final ForNode forNode) { 1.302 + final Node test = forNode.getTest(); 1.303 + final Block body = forNode.getBody(); 1.304 + 1.305 + if (!forNode.isForIn() && test == null) { 1.306 + setHasGoto(forNode); 1.307 + } 1.308 + 1.309 + final boolean escapes = controlFlowEscapes(body); 1.310 + if (escapes) { 1.311 + setTerminal(body, false); 1.312 + } 1.313 + 1.314 + // pop the loop from the loop context 1.315 + unnest(forNode); 1.316 + 1.317 + if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { 1.318 + forNode.setTest(null); 1.319 + setTerminal(forNode, !escapes); 1.320 + } 1.321 + 1.322 + addStatement(forNode); 1.323 + 1.324 + return forNode; 1.325 + } 1.326 + 1.327 + @Override 1.328 + public Node enter(final FunctionNode functionNode) { 1.329 + LOG.info("START FunctionNode: " + functionNode.getName()); 1.330 + 1.331 + initFunctionNode(functionNode); 1.332 + 1.333 + Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED); 1.334 + 1.335 + nest(functionNode); 1.336 + 1.337 + /* 1.338 + * As we are evaluating a nested structure, we need to store the 1.339 + * statement list for the surrounding block and restore it when the 1.340 + * function is done 1.341 + */ 1.342 + final List<Node> savedStatements = statements; 1.343 + final Node savedLastStatement = lastStatement; 1.344 + 1.345 + statements = new ArrayList<>(); 1.346 + lastStatement = null; 1.347 + 1.348 + // for initial eval result is the last declared function 1.349 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 1.350 + final IdentNode ident = nestedFunction.getIdent(); 1.351 + if (ident != null && nestedFunction.isStatement()) { 1.352 + initialEvalResult = new IdentNode(ident); 1.353 + } 1.354 + } 1.355 + 1.356 + if (functionNode.needsSelfSymbol()) { 1.357 + //function needs to start with var funcIdent = __callee_; 1.358 + statements.add(functionNode.getSelfSymbolInit().accept(this)); 1.359 + } 1.360 + 1.361 + try { 1.362 + // Every nested function needs a definition in the outer function with its name. Add these. 1.363 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 1.364 + final VarNode varNode = nestedFunction.getFunctionVarNode(); 1.365 + if (varNode != null) { 1.366 + final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode(); 1.367 + if (lineNumberNode != null) { 1.368 + lineNumberNode.accept(this); 1.369 + } 1.370 + varNode.accept(this); 1.371 + varNode.setIsFunctionVarNode(); 1.372 + } 1.373 + } 1.374 + 1.375 + if (functionNode.isScript()) { 1.376 + new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this); 1.377 + } 1.378 + 1.379 + //do the statements - this fills the block with code 1.380 + for (final Node statement : functionNode.getStatements()) { 1.381 + statement.accept(this); 1.382 + //If there are unused terminated endpoints in the function, we need 1.383 + // to add a "return undefined" in those places for correct semantics 1.384 + LOG.info("Checking lastStatement="+lastStatement+" for terminal flags"); 1.385 + if (lastStatement != null && lastStatement.hasTerminalFlags()) { 1.386 + copyTerminal(functionNode, lastStatement); 1.387 break; 1.388 } 1.389 } 1.390 1.391 - block.setStatements(statements); 1.392 + functionNode.setStatements(statements); 1.393 + 1.394 + if (!functionNode.isTerminal()) { 1.395 + guaranteeReturn(functionNode); 1.396 + } 1.397 + 1.398 + //lower all nested functions 1.399 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 1.400 + nestedFunction.accept(this); 1.401 + } 1.402 + 1.403 } finally { 1.404 - /* 1.405 - * Restore the saved statements after block ends and pop frame 1.406 - */ 1.407 - statements = savedStatements; 1.408 - localDefs = savedDefs; 1.409 - localUses = savedUses; 1.410 - 1.411 - getCurrentFunctionNode().popFrame(); 1.412 + statements = savedStatements; 1.413 + lastStatement = savedLastStatement; 1.414 } 1.415 1.416 + LOG.info("END FunctionNode: " + functionNode.getName()); 1.417 + unnest(functionNode); 1.418 + 1.419 return null; 1.420 } 1.421 1.422 + @Override 1.423 + public Node enter(final IfNode ifNode) { 1.424 + return nest(ifNode); 1.425 + } 1.426 + 1.427 + @Override 1.428 + public Node leave(final IfNode ifNode) { 1.429 + final Node pass = ifNode.getPass(); 1.430 + final Node fail = ifNode.getFail(); 1.431 + 1.432 + if (pass.isTerminal() && fail != null && fail.isTerminal()) { 1.433 + setTerminal(ifNode, true); 1.434 + } 1.435 + 1.436 + addStatement(ifNode); 1.437 + unnest(ifNode); 1.438 + 1.439 + return ifNode; 1.440 + } 1.441 + 1.442 + @Override 1.443 + public Node enter(LabelNode labelNode) { 1.444 + final Block body = labelNode.getBody(); 1.445 + body.accept(this); 1.446 + copyTerminal(labelNode, body); 1.447 + addStatement(labelNode); 1.448 + return null; 1.449 + } 1.450 + 1.451 + @Override 1.452 + public Node enter(final LineNumberNode lineNumberNode) { 1.453 + addStatement(lineNumberNode, false); // don't put it in lastStatement cache 1.454 + return null; 1.455 + } 1.456 + 1.457 + @Override 1.458 + public Node enter(final ReturnNode returnNode) { 1.459 + final TryNode tryNode = returnNode.getTryChain(); 1.460 + final Node expr = returnNode.getExpression(); 1.461 + 1.462 + if (tryNode != null) { 1.463 + //we are inside a try block - we don't necessarily have a result node yet. attr will do that. 1.464 + if (expr != null) { 1.465 + //we need to evaluate the result of the return in case it is complex while 1.466 + //still in the try block, store it in a result value and return it afterwards 1.467 + final long token = returnNode.getToken(); 1.468 + final Node resultNode = new IdentNode(getCurrentFunctionNode().getResultNode()); 1.469 + final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr); 1.470 + 1.471 + //add return_in_try = expr; to try block 1.472 + new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this); 1.473 + 1.474 + //splice in the finally code, inlining it here 1.475 + if (copyFinally(tryNode, null)) { 1.476 + return null; 1.477 + } 1.478 + 1.479 + //make sure that the return node now returns 'return_in_try' 1.480 + returnNode.setExpression(resultNode); 1.481 + } else if (copyFinally(tryNode, null)) { 1.482 + return null; 1.483 + } 1.484 + } else if (expr != null) { 1.485 + returnNode.setExpression(expr.accept(this)); 1.486 + } 1.487 + 1.488 + addStatement(returnNode); 1.489 + 1.490 + return null; 1.491 + } 1.492 + 1.493 + @Override 1.494 + public Node leave(final ReturnNode returnNode) { 1.495 + addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor 1.496 + return returnNode; 1.497 + } 1.498 + 1.499 + @Override 1.500 + public Node enter(final SwitchNode switchNode) { 1.501 + nest(switchNode); 1.502 + return switchNode; 1.503 + } 1.504 + 1.505 + @Override 1.506 + public Node leave(final SwitchNode switchNode) { 1.507 + unnest(switchNode); 1.508 + 1.509 + final List<CaseNode> cases = switchNode.getCases(); 1.510 + final CaseNode defaultCase = switchNode.getDefaultCase(); 1.511 + 1.512 + boolean allTerminal = !cases.isEmpty(); 1.513 + for (final CaseNode caseNode : switchNode.getCases()) { 1.514 + allTerminal &= caseNode.isTerminal(); 1.515 + } 1.516 + 1.517 + if (allTerminal && defaultCase != null && defaultCase.isTerminal()) { 1.518 + setTerminal(switchNode, true); 1.519 + } 1.520 + 1.521 + addStatement(switchNode); 1.522 + 1.523 + return switchNode; 1.524 + } 1.525 + 1.526 + @Override 1.527 + public Node leave(final ThrowNode throwNode) { 1.528 + addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor 1.529 + return throwNode; 1.530 + } 1.531 + 1.532 + @Override 1.533 + public Node enter(final TryNode tryNode) { 1.534 + final Block finallyBody = tryNode.getFinallyBody(); 1.535 + final long token = tryNode.getToken(); 1.536 + final int finish = tryNode.getFinish(); 1.537 + 1.538 + nest(tryNode); 1.539 + 1.540 + if (finallyBody == null) { 1.541 + //do nothing if no finally exists 1.542 + return tryNode; 1.543 + } 1.544 + 1.545 + /* 1.546 + * We have a finally clause. 1.547 + * 1.548 + * Transform to do finally tail duplication as follows: 1.549 + * 1.550 + * <pre> 1.551 + * try { 1.552 + * try_body 1.553 + * } catch e1 { 1.554 + * catchbody_1 1.555 + * } 1.556 + * ... 1.557 + * } catch en { 1.558 + * catchbody_n 1.559 + * } finally { 1.560 + * finally_body 1.561 + * } 1.562 + * 1.563 + * (where e1 ... en are optional) 1.564 + * 1.565 + * turns into 1.566 + * 1.567 + * try { 1.568 + * try { 1.569 + * try_body 1.570 + * } catch e1 { 1.571 + * catchbody1 1.572 + * //nothing inlined explicitly here, return, break other 1.573 + * //terminals may inline the finally body 1.574 + * ... 1.575 + * } catch en { 1.576 + * catchbody2 1.577 + * //nothing inlined explicitly here, return, break other 1.578 + * //terminals may inline the finally body 1.579 + * } 1.580 + * } catch all ex { 1.581 + * finally_body_inlined 1.582 + * rethrow ex 1.583 + * } 1.584 + * finally_body_inlined 1.585 + * </pre> 1.586 + * 1.587 + * If tries are catches are terminal, visitors for return, break & 1.588 + * continue will handle the tail duplications. Throw needs to be 1.589 + * treated specially with the catchall as described in the above 1.590 + * ASCII art. 1.591 + * 1.592 + * If the try isn't terminal we do the finally_body_inlined at the 1.593 + * end. If the try is terminated with continue/break/return the 1.594 + * existing visitor logic will inline the finally before that 1.595 + * operation. if the try is terminated with a throw, the catches e1 1.596 + * ... en will have a chance to process the exception. If the 1.597 + * appropriate catch e1..en is non terminal we fall through to the 1.598 + * last finally_body_inlined. if the catch e1...en IS terminal with 1.599 + * continue/break/return existing visitor logic will fix it. If they 1.600 + * are terminal with another throw it goes to the catchall and the 1.601 + * finally_body_inlined marked (*) will fix it before rethrowing 1.602 + * whatever problem there was for identical semantic. 1.603 + */ 1.604 + 1.605 + // if try node does not contain a catch we can skip creation of a new 1.606 + // try node and just append our synthetic catch to the existing try node. 1.607 + if (!tryNode.getCatchBlocks().isEmpty()) { 1.608 + // insert an intermediate try-catch* node, where we move the body and all catch blocks. 1.609 + // the original try node become a try-finally container for the new try-catch* node. 1.610 + // because we don't clone (to avoid deep copy), we have to fix the block chain in the end. 1.611 + final TryNode innerTryNode; 1.612 + innerTryNode = new TryNode(source, token, finish, tryNode.getNext()); 1.613 + innerTryNode.setBody(tryNode.getBody()); 1.614 + innerTryNode.setCatchBlocks(tryNode.getCatchBlocks()); 1.615 + 1.616 + // set outer tryNode's body to innerTryNode 1.617 + final Block outerBody; 1.618 + outerBody = new Block(source, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode()); 1.619 + outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode))); 1.620 + tryNode.setBody(outerBody); 1.621 + tryNode.setCatchBlocks(null); 1.622 + 1.623 + // now before we go on, we have to fix the block parents 1.624 + // (we repair the block tree after the insertion so that all references are intact) 1.625 + innerTryNode.getBody().setParent(tryNode.getBody()); 1.626 + for (final Block block : innerTryNode.getCatchBlocks()) { 1.627 + block.setParent(tryNode.getBody()); 1.628 + } 1.629 + } 1.630 + 1.631 + // create a catch-all that inlines finally and rethrows 1.632 + 1.633 + final Block catchBlock = new Block(source, token, finish, getCurrentBlock(), getCurrentFunctionNode()); 1.634 + //this catch block should get define symbol 1.635 + 1.636 + final Block catchBody = new Block(source, token, finish, catchBlock, getCurrentFunctionNode()); 1.637 + final Node catchAllFinally = finallyBody.clone(); 1.638 + 1.639 + catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally)); 1.640 + setTerminal(catchBody, true); 1.641 + 1.642 + final CatchNode catchAllNode; 1.643 + final IdentNode exception; 1.644 + 1.645 + exception = new IdentNode(source, token, finish, compiler.uniqueName("catch_all")); 1.646 + catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody); 1.647 + catchAllNode.setIsSyntheticRethrow(); 1.648 + 1.649 + catchBlock.addStatement(catchAllNode); 1.650 + 1.651 + // replace all catches of outer tryNode with the catch-all 1.652 + tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock))); 1.653 + 1.654 + /* 1.655 + * We leave the finally block for the original try in place for now 1.656 + * so that children visitations will work. It is removed and placed 1.657 + * afterwards in the else case below, after all children are visited 1.658 + */ 1.659 + 1.660 + return tryNode; 1.661 + } 1.662 + 1.663 + @Override 1.664 + public Node leave(final TryNode tryNode) { 1.665 + final Block finallyBody = tryNode.getFinallyBody(); 1.666 + 1.667 + boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal()); 1.668 + 1.669 + for (final Block catchBlock : tryNode.getCatchBlocks()) { 1.670 + allTerminal &= catchBlock.isTerminal(); 1.671 + } 1.672 + 1.673 + tryNode.setIsTerminal(allTerminal); 1.674 + 1.675 + addStatement(tryNode); 1.676 + unnest(tryNode); 1.677 + 1.678 + // if finally body is present, place it after the tryNode 1.679 + if (finallyBody != null) { 1.680 + tryNode.setFinallyBody(null); 1.681 + addStatement(finallyBody); 1.682 + } 1.683 + 1.684 + return tryNode; 1.685 + } 1.686 + 1.687 + @Override 1.688 + public Node leave(final VarNode varNode) { 1.689 + addStatement(varNode); 1.690 + return varNode; 1.691 + } 1.692 + 1.693 + @Override 1.694 + public Node enter(final WhileNode whileNode) { 1.695 + return nest(whileNode); 1.696 + } 1.697 + 1.698 + @Override 1.699 + public Node leave(final WhileNode whileNode) { 1.700 + final Node test = whileNode.getTest(); 1.701 + 1.702 + if (test == null) { 1.703 + setHasGoto(whileNode); 1.704 + } 1.705 + 1.706 + final Block body = whileNode.getBody(); 1.707 + final boolean escapes = controlFlowEscapes(body); 1.708 + if (escapes) { 1.709 + setTerminal(body, false); 1.710 + } 1.711 + 1.712 + Node node = whileNode; 1.713 + 1.714 + if (body.isTerminal()) { 1.715 + if (whileNode instanceof DoWhileNode) { 1.716 + setTerminal(whileNode, true); 1.717 + } else if (conservativeAlwaysTrue(test)) { 1.718 + node = new ForNode(source, whileNode.getToken(), whileNode.getFinish()); 1.719 + ((ForNode)node).setBody(body); 1.720 + ((ForNode)node).accept(this); 1.721 + setTerminal(node, !escapes); 1.722 + } 1.723 + } 1.724 + 1.725 + // pop the loop from the loop context 1.726 + unnest(whileNode); 1.727 + addStatement(node); 1.728 + 1.729 + return node; 1.730 + } 1.731 + 1.732 + @Override 1.733 + public Node leave(final WithNode withNode) { 1.734 + if (withNode.getBody().isTerminal()) { 1.735 + setTerminal(withNode, true); 1.736 + } 1.737 + addStatement(withNode); 1.738 + 1.739 + return withNode; 1.740 + } 1.741 + 1.742 + @Override 1.743 + public Node leaveDELETE(final UnaryNode unaryNode) { 1.744 + final Node rhs = unaryNode.rhs(); 1.745 + if (rhs instanceof IdentNode || rhs instanceof BaseNode) { 1.746 + return unaryNode; 1.747 + } 1.748 + addStatement(new ExecuteNode(rhs)); 1.749 + return LiteralNode.newInstance(unaryNode, true); 1.750 + } 1.751 + 1.752 + /** 1.753 + * Given a function node that is a callee in a CallNode, replace it with 1.754 + * the appropriate marker function. This is used by {@link CodeGenerator} 1.755 + * for fast scope calls 1.756 + * 1.757 + * @param function function called by a CallNode 1.758 + * @return transformed node to marker function or identity if not ident/access/indexnode 1.759 + */ 1.760 + private static Node markerFunction(final Node function) { 1.761 + if (function instanceof IdentNode) { 1.762 + return new IdentNode((IdentNode)function) { 1.763 + @Override 1.764 + public boolean isFunction() { 1.765 + return true; 1.766 + } 1.767 + }; 1.768 + } else if (function instanceof AccessNode) { 1.769 + return new AccessNode((AccessNode)function) { 1.770 + @Override 1.771 + public boolean isFunction() { 1.772 + return true; 1.773 + } 1.774 + }; 1.775 + } else if (function instanceof IndexNode) { 1.776 + return new IndexNode((IndexNode)function) { 1.777 + @Override 1.778 + public boolean isFunction() { 1.779 + return true; 1.780 + } 1.781 + }; 1.782 + } 1.783 + 1.784 + return function; 1.785 + } 1.786 + 1.787 + /** 1.788 + * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval> 1.789 + * @param node a node 1.790 + * @return eval location 1.791 + */ 1.792 + private static String evalLocation(final IdentNode node) { 1.793 + //final StringBuilder sb = new StringBuilder(node.getSource().getName()); 1.794 + return new StringBuilder(). 1.795 + append(node.getSource().getName()). 1.796 + append('#'). 1.797 + append(node.getSource().getLine(node.position())). 1.798 + append("<eval>"). 1.799 + toString(); 1.800 + } 1.801 + 1.802 + /** 1.803 + * Check whether a call node may be a call to eval. In that case we 1.804 + * clone the args in order to create the following construct in 1.805 + * {@link CodeGenerator} 1.806 + * 1.807 + * <pre> 1.808 + * if (calledFuntion == buildInEval) { 1.809 + * eval(cloned arg); 1.810 + * } else { 1.811 + * cloned arg; 1.812 + * } 1.813 + * </pre> 1.814 + * 1.815 + * @param callNode call node to check if it's an eval 1.816 + */ 1.817 + private void checkEval(final CallNode callNode) { 1.818 + if (callNode.getFunction() instanceof IdentNode) { 1.819 + 1.820 + final List<Node> args = callNode.getArgs(); 1.821 + final IdentNode callee = (IdentNode)callNode.getFunction(); 1.822 + 1.823 + // 'eval' call with at least one argument 1.824 + if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) { 1.825 + final CallNode.EvalArgs evalArgs = 1.826 + new CallNode.EvalArgs( 1.827 + args.get(0).clone().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case" 1.828 + getCurrentFunctionNode().getThisNode(), 1.829 + evalLocation(callee), 1.830 + getCurrentFunctionNode().isStrictMode()); 1.831 + callNode.setEvalArgs(evalArgs); 1.832 + } 1.833 + } 1.834 + } 1.835 + 1.836 + private static boolean conservativeAlwaysTrue(final Node node) { 1.837 + return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue())); 1.838 + } 1.839 + 1.840 + /** 1.841 + * Helper that given a loop body makes sure that it is not terminal if it 1.842 + * has a continue that leads to the loop header or to outer loops' loop 1.843 + * headers. This means that, even if the body ends with a terminal 1.844 + * statement, we cannot tag it as terminal 1.845 + * 1.846 + * @param loopBody the loop body to check 1.847 + * @return true if control flow may escape the loop 1.848 + */ 1.849 + private boolean controlFlowEscapes(final Node loopBody) { 1.850 + final List<Node> escapes = new ArrayList<>(); 1.851 + 1.852 + loopBody.accept(new NodeVisitor() { 1.853 + @Override 1.854 + public Node leave(final BreakNode node) { 1.855 + escapes.add(node); 1.856 + return node; 1.857 + } 1.858 + 1.859 + @Override 1.860 + public Node leave(final ContinueNode node) { 1.861 + // all inner loops have been popped. 1.862 + if (nesting.contains(node.getTargetNode())) { 1.863 + escapes.add(node); 1.864 + } 1.865 + return node; 1.866 + } 1.867 + }); 1.868 + 1.869 + return !escapes.isEmpty(); 1.870 + } 1.871 + 1.872 + private void guaranteeReturn(final FunctionNode functionNode) { 1.873 + Node resultNode; 1.874 + 1.875 + if (functionNode.isScript()) { 1.876 + resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr 1.877 + } else { 1.878 + if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) { 1.879 + return; //already in place or not needed, as it should be for a non-undefined returning function 1.880 + } 1.881 + resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED); 1.882 + } 1.883 + 1.884 + //create a return statement 1.885 + final Node returnNode = new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null); 1.886 + returnNode.accept(this); 1.887 + } 1.888 + 1.889 + 1.890 + private Node nest(final Node node) { 1.891 + LOG.info("Nesting: " + node); 1.892 + LOG.indent(); 1.893 + nesting.push(node); 1.894 + return node; 1.895 + } 1.896 + 1.897 + private void unnest(final Node node) { 1.898 + LOG.unindent(); 1.899 + assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node; 1.900 + LOG.info("Unnesting: " + nesting); 1.901 + nesting.pop(); 1.902 + } 1.903 + 1.904 + private static void setTerminal(final Node node, final boolean isTerminal) { 1.905 + LOG.info("terminal = " + isTerminal + " for " + node); 1.906 + node.setIsTerminal(isTerminal); 1.907 + } 1.908 + 1.909 + private static void setHasGoto(final Node node) { //, final boolean hasGoto) { 1.910 + LOG.info("hasGoto = true for " + node); 1.911 + node.setHasGoto(); 1.912 + } 1.913 + 1.914 + private static void copyTerminal(final Node node, final Node sourceNode) { 1.915 + LOG.info("copy terminal flags " + sourceNode + " -> " + node); 1.916 + node.copyTerminalFlags(sourceNode); 1.917 + } 1.918 + 1.919 + private void addStatement(final Node statement, final boolean storeInLastStatement) { 1.920 + LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")"); 1.921 + statements.add(statement); 1.922 + if (storeInLastStatement) { 1.923 + lastStatement = statement; 1.924 + } 1.925 + } 1.926 + 1.927 + private void addStatement(final Node statement) { 1.928 + addStatement(statement, true); 1.929 + } 1.930 + 1.931 /** 1.932 * Determine if Try block is inside target block. 1.933 * 1.934 @@ -316,271 +909,15 @@ 1.935 return false; 1.936 } 1.937 1.938 - @Override 1.939 - public Node enter(final BreakNode breakNode) { 1.940 - final TryNode tryNode = breakNode.getTryChain(); 1.941 - 1.942 - if (tryNode != null && copyFinally(tryNode, breakNode.getTargetNode())) { 1.943 + private Node enterBreakOrContinue(final LabeledNode labeledNode) { 1.944 + final TryNode tryNode = labeledNode.getTryChain(); 1.945 + if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) { 1.946 return null; 1.947 } 1.948 - 1.949 - statements.add(breakNode); 1.950 - 1.951 + addStatement(labeledNode); 1.952 return null; 1.953 } 1.954 1.955 - /** 1.956 - * Blesses nodes with an expectant type, converting if necessary. 1.957 - * 1.958 - * @param node Node to be blest. 1.959 - * @param type Type class expected. 1.960 - * 1.961 - * @return Converted node or original node if no conversion is needed. 1.962 - */ 1.963 - private Node convert(final Node node, final Type type) { 1.964 - 1.965 - final Symbol symbol = node.getSymbol(); 1.966 - final FunctionNode functionNode = getCurrentFunctionNode(); 1.967 - 1.968 - assert !type.isUnknown() : "unknown"; 1.969 - 1.970 - /* 1.971 - * Conversions are now mandatory, have to be placed at code time and 1.972 - * cannot be removed as there might be cases like: 1.973 - * 1.974 - * var x = 17; if (x + 1) { ... } 1.975 - * 1.976 - * x = Number(4711); if (x + 1) { ... } 1.977 - * 1.978 - * In the old world this behaved as follows: 1.979 - * 1.980 - * Here, x is originally inferred type to a number, then the if does a 1.981 - * double add without a cast. However, the next statement turns x into 1.982 - * an object, and this messes up the original. If we would now suddenly 1.983 - * need an explicit cast for the first addition, given that we don't do 1.984 - * fancy stuff like splitting live range. 1.985 - * 1.986 - * Even worse is the case where we have an eval that modifies local 1.987 - * variables in the middle of a function and may widen a type suspected 1.988 - * to be at most e.g. NUMBER to e.g. OBJECT. 1.989 - * 1.990 - * There are a few local optimizations we can do. If we want to do an 1.991 - * OBJECT to OBJECT cast, for example, we can skip it as already is 1.992 - * maximally wide, except if we are in a method with an eval where 1.993 - * everything is possible... 1.994 - * 1.995 - * @see test/test262/test/suite/ch07/7.2/S7.2_A1.4_T2.js for an example 1.996 - * of this. 1.997 - */ 1.998 - 1.999 - assert symbol != null : "no symbol for " + node; 1.1000 - 1.1001 - /* check object to object cast */ 1.1002 - if (!functionNode.hasEval() && node.getType().isEquivalentTo(Type.OBJECT) && type.isEquivalentTo(Type.OBJECT)) { 1.1003 - return node; 1.1004 - } 1.1005 - 1.1006 - Node resultNode = node; 1.1007 - 1.1008 - // Literal nodes may be converted directly 1.1009 - 1.1010 - if (node instanceof LiteralNode) { 1.1011 - final LiteralNode<?> convertedLiteral = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, type).eval(); 1.1012 - if (convertedLiteral != null) { 1.1013 - resultNode = newLiteral(convertedLiteral); 1.1014 - } 1.1015 - // object literals still need the cast 1.1016 - if (type.isObject()) { 1.1017 - resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node); 1.1018 - } 1.1019 - } else { 1.1020 - if (resultNode.getSymbol().isParam()) { 1.1021 - resultNode.getSymbol().setType(type); 1.1022 - } 1.1023 - resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), resultNode); 1.1024 - } 1.1025 - 1.1026 - functionNode.newTemporary(type, resultNode); 1.1027 - resultNode.copyTerminalFlags(node); 1.1028 - 1.1029 - return resultNode; 1.1030 - } 1.1031 - 1.1032 - /** 1.1033 - * Accept and convert all arguments to type Object. If we have a 1.1034 - * specialization profile for this function, we instead try to specialize 1.1035 - * the arguments before the casts based on their current types and values. 1.1036 - * 1.1037 - * @param callNode function call 1.1038 - * @return return type for call 1.1039 - */ 1.1040 - private Type acceptArgs(final CallNode callNode) { 1.1041 - final List<Node> oldArgs = callNode.getArgs(); 1.1042 - final List<Node> acceptedArgs = new ArrayList<>(oldArgs.size()); 1.1043 - 1.1044 - for (final Node arg : oldArgs) { 1.1045 - //acceptedArgs.add(convert(arg.accept(this), OBJECT)); 1.1046 - acceptedArgs.add(arg.accept(this)); 1.1047 - } 1.1048 - callNode.setArgs(acceptedArgs); 1.1049 - 1.1050 - return Type.OBJECT; 1.1051 - } 1.1052 - 1.1053 - private static String evalLocation(final IdentNode node) { 1.1054 - final StringBuilder sb = new StringBuilder(node.getSource().getName()); 1.1055 - 1.1056 - sb.append('#'); 1.1057 - sb.append(node.getSource().getLine(node.position())); 1.1058 - sb.append("<eval>"); 1.1059 - 1.1060 - return sb.toString(); 1.1061 - } 1.1062 - 1.1063 - private void checkEval(final CallNode callNode) { 1.1064 - if (callNode.getFunction() instanceof IdentNode) { 1.1065 - 1.1066 - final List<Node> args = callNode.getArgs(); 1.1067 - final IdentNode callee = (IdentNode)callNode.getFunction(); 1.1068 - 1.1069 - // 'eval' call with atleast one argument 1.1070 - if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) { 1.1071 - final CallNode.EvalArgs evalArgs = new CallNode.EvalArgs(); 1.1072 - // code that is evaluated 1.1073 - evalArgs.code = args.get(0).clone(); 1.1074 - evalArgs.code.accept(this); 1.1075 - // 'this' to be passed to evaluated code 1.1076 - evalArgs.evalThis = new IdentNode(getCurrentFunctionNode().getThisNode()); 1.1077 - // location string of the eval call 1.1078 - evalArgs.location = evalLocation(callee); 1.1079 - // strict mode context or not? 1.1080 - evalArgs.strictMode = getCurrentFunctionNode().isStrictMode(); 1.1081 - callNode.setEvalArgs(evalArgs); 1.1082 - } 1.1083 - } 1.1084 - } 1.1085 - 1.1086 - private static Node markerFunction(final Node function) { 1.1087 - if (function instanceof IdentNode) { 1.1088 - return new IdentNode((IdentNode)function) { 1.1089 - @Override 1.1090 - public boolean isFunction() { 1.1091 - return true; 1.1092 - } 1.1093 - }; 1.1094 - } else if (function instanceof AccessNode) { 1.1095 - return new AccessNode((AccessNode)function) { 1.1096 - @Override 1.1097 - public boolean isFunction() { 1.1098 - return true; 1.1099 - } 1.1100 - }; 1.1101 - } else if (function instanceof IndexNode) { 1.1102 - return new IndexNode((IndexNode)function) { 1.1103 - @Override 1.1104 - public boolean isFunction() { 1.1105 - return true; 1.1106 - } 1.1107 - }; 1.1108 - } 1.1109 - 1.1110 - return function; 1.1111 - } 1.1112 - 1.1113 - @Override 1.1114 - public Node enter(final CallNode callNode) { 1.1115 - final Node function = callNode.getFunction(); 1.1116 - final Node markedFunction = markerFunction(function); 1.1117 - 1.1118 - callNode.setFunction(markedFunction.accept(this)); 1.1119 - 1.1120 - checkEval(callNode); 1.1121 - 1.1122 - final Type returnType = acceptArgs(callNode); 1.1123 - getCurrentFunctionNode().newTemporary(returnType, callNode); 1.1124 - callNode.getFunction().getSymbol().setType(returnType); 1.1125 - 1.1126 - return null; 1.1127 - } 1.1128 - 1.1129 - @Override 1.1130 - public Node leave(final CaseNode caseNode) { 1.1131 - caseNode.copyTerminalFlags(caseNode.getBody()); 1.1132 - 1.1133 - return caseNode; 1.1134 - } 1.1135 - 1.1136 - @Override 1.1137 - public Node enter(final CatchNode catchNode) { 1.1138 - final IdentNode ident = catchNode.getException(); 1.1139 - final Block block = getCurrentBlock(); 1.1140 - 1.1141 - // define block-local exception variable 1.1142 - block.defineSymbol(ident.getName(), IS_VAR | IS_LET, ident).setType(Type.OBJECT); 1.1143 - localDefs.add(ident.getName()); 1.1144 - 1.1145 - return catchNode; 1.1146 - } 1.1147 - 1.1148 - @Override 1.1149 - public Node leave(final CatchNode catchNode) { 1.1150 - final Node exceptionCondition = catchNode.getExceptionCondition(); 1.1151 - if (exceptionCondition != null) { 1.1152 - catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); 1.1153 - } 1.1154 - 1.1155 - catchNode.copyTerminalFlags(catchNode.getBody()); 1.1156 - 1.1157 - statements.add(catchNode); 1.1158 - 1.1159 - return catchNode; 1.1160 - } 1.1161 - 1.1162 - @Override 1.1163 - public Node enter(final ContinueNode continueNode) { 1.1164 - final TryNode tryNode = continueNode.getTryChain(); 1.1165 - final Node target = continueNode.getTargetNode(); 1.1166 - 1.1167 - if (tryNode != null && copyFinally(tryNode, target)) { 1.1168 - return null; 1.1169 - } 1.1170 - 1.1171 - statements.add(continueNode); 1.1172 - 1.1173 - return null; 1.1174 - } 1.1175 - 1.1176 - @Override 1.1177 - public Node enter(final DoWhileNode whileNode) { 1.1178 - return enter((WhileNode)whileNode); 1.1179 - } 1.1180 - 1.1181 - @Override 1.1182 - public Node leave(final DoWhileNode whileNode) { 1.1183 - return leave((WhileNode)whileNode); 1.1184 - } 1.1185 - 1.1186 - @Override 1.1187 - public Node enter(final EmptyNode emptyNode) { 1.1188 - return null; 1.1189 - } 1.1190 - 1.1191 - /** 1.1192 - * Is this an assignment to the special variable that hosts scripting eval 1.1193 - * results? 1.1194 - * 1.1195 - * @param expression expression to check whether it is $evalresult = X 1.1196 - * 1.1197 - * @return true if an assignment to eval result, false otherwise 1.1198 - */ 1.1199 - private boolean isEvalResultAssignment(final Node expression) { 1.1200 - Node e = expression; 1.1201 - if (e.tokenType() == TokenType.DISCARD) { 1.1202 - e = ((UnaryNode)expression).rhs(); 1.1203 - } 1.1204 - final Node resultNode = getCurrentFunctionNode().getResultNode(); 1.1205 - return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode); 1.1206 - } 1.1207 1.1208 /** 1.1209 * An internal expression has a symbol that is tagged internal. Check if 1.1210 @@ -595,2543 +932,41 @@ 1.1211 } 1.1212 1.1213 /** 1.1214 - * Discard the result of the expression. 1.1215 + * Is this an assignment to the special variable that hosts scripting eval 1.1216 + * results? 1.1217 * 1.1218 - * @param expression Expression to discard. 1.1219 - * 1.1220 - * @return Discard node. 1.1221 + * @param expression expression to check whether it is $evalresult = X 1.1222 + * @return true if an assignment to eval result, false otherwise 1.1223 */ 1.1224 - private Node discard(final Node expression) { 1.1225 - expression.setDiscard(true); 1.1226 - 1.1227 - if (expression.getSymbol() != null) { 1.1228 - final Node discard = new UnaryNode(source, Token.recast(expression.getToken(), TokenType.DISCARD), expression); 1.1229 - discard.copyTerminalFlags(expression); 1.1230 - 1.1231 - return discard; 1.1232 + private boolean isEvalResultAssignment(final Node expression) { 1.1233 + Node e = expression; 1.1234 + if (e.tokenType() == TokenType.DISCARD) { 1.1235 + e = ((UnaryNode)expression).rhs(); 1.1236 } 1.1237 - 1.1238 - return expression; 1.1239 + final Node resultNode = getCurrentFunctionNode().getResultNode(); 1.1240 + return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode); 1.1241 } 1.1242 1.1243 /** 1.1244 - * ExecuteNodes are needed to actually generate code, with a few exceptions 1.1245 - * such as ReturnNodes and ThrowNodes and various control flow that can 1.1246 - * standalone. Every other kind of statement needs to be wrapped in an 1.1247 - * ExecuteNode in order to become code 1.1248 - * 1.1249 - * @param executeNode the execute node to visit 1.1250 + * Prepare special function nodes. 1.1251 + * TODO : only create those that are needed. 1.1252 + * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant 1.1253 */ 1.1254 - @Override 1.1255 - public Node leave(final ExecuteNode executeNode) { 1.1256 - 1.1257 - Node expression = executeNode.getExpression(); 1.1258 - 1.1259 - /* 1.1260 - * Handle the eval result for scripts. Every statement has to write its 1.1261 - * return value to a special variable that is the result of the script. 1.1262 - */ 1.1263 - if (getCurrentFunctionNode().isScript() && !(expression instanceof Block) && !isEvalResultAssignment(expression) && !isInternalExpression(expression)) { 1.1264 - final Node resultNode = getCurrentFunctionNode().getResultNode(); 1.1265 - expression = new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN), resultNode, convert(expression, resultNode.getType())); 1.1266 - 1.1267 - getCurrentFunctionNode().newTemporary(Type.OBJECT, expression); 1.1268 - } 1.1269 - 1.1270 - expression = discard(expression); 1.1271 - executeNode.setExpression(expression); 1.1272 - executeNode.copyTerminalFlags(expression); 1.1273 - 1.1274 - statements.add(executeNode); 1.1275 - 1.1276 - return executeNode; 1.1277 - } 1.1278 - 1.1279 - /** 1.1280 - * Helper that given a loop body makes sure that it is not terminal if it 1.1281 - * has a continue that leads to the loop header or to outer loops' loop 1.1282 - * headers. This means that, even if the body ends with a terminal 1.1283 - * statement, we cannot tag it as terminal 1.1284 - * 1.1285 - * @param loopBody the loop body to check 1.1286 - */ 1.1287 - private boolean controlFlowEscapes(final Node loopBody) { 1.1288 - final List<Node> escapes = new ArrayList<>(); 1.1289 - 1.1290 - loopBody.accept(new NodeVisitor() { 1.1291 - @Override 1.1292 - public Node leave(final BreakNode node) { 1.1293 - escapes.add(node); 1.1294 - return node; 1.1295 - } 1.1296 - 1.1297 - @Override 1.1298 - public Node leave(final ContinueNode node) { 1.1299 - // all inner loops have been popped. 1.1300 - if (nesting.contains(node.getTargetNode())) { 1.1301 - escapes.add(node); 1.1302 - } 1.1303 - return node; 1.1304 - } 1.1305 - }); 1.1306 - 1.1307 - return !escapes.isEmpty(); 1.1308 - } 1.1309 - 1.1310 - @Override 1.1311 - public Node enter(final ForNode forNode) { 1.1312 - // push the loop to the loop context 1.1313 - nest(forNode); 1.1314 - return forNode; 1.1315 - } 1.1316 - 1.1317 - private static boolean conservativeAlwaysTrue(final Node node) { 1.1318 - return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue())); 1.1319 - } 1.1320 - 1.1321 - @Override 1.1322 - public Node leave(final ForNode forNode) { 1.1323 - final Node init = forNode.getInit(); 1.1324 - final Node test = forNode.getTest(); 1.1325 - final Node modify = forNode.getModify(); 1.1326 - 1.1327 - if (forNode.isForIn()) { 1.1328 - final String name = compiler.uniqueName(ITERATOR_PREFIX.tag()); 1.1329 - // DEFINE SYMBOL: may be any type, not local var 1.1330 - final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); 1.1331 - iter.setType(Type.OBJECT); // NASHORN-73 1.1332 - forNode.setIterator(iter); 1.1333 - forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 1.1334 - 1.1335 - /* 1.1336 - * Iterators return objects, so we need to widen the scope of the 1.1337 - * init variable if it, for example, has been assigned double type 1.1338 - * see NASHORN-50 1.1339 - */ 1.1340 - forNode.getInit().getSymbol().setType(Type.OBJECT); 1.1341 - } else { 1.1342 - /* Normal for node, not for in */ 1.1343 - 1.1344 - if (init != null) { 1.1345 - forNode.setInit(discard(init)); 1.1346 - } 1.1347 - 1.1348 - if (test != null) { 1.1349 - forNode.setTest(convert(test, Type.BOOLEAN)); 1.1350 - } else { 1.1351 - forNode.setHasGoto(); 1.1352 - } 1.1353 - 1.1354 - if (modify != null) { 1.1355 - forNode.setModify(discard(modify)); 1.1356 - } 1.1357 - } 1.1358 - 1.1359 - final Block body = forNode.getBody(); 1.1360 - 1.1361 - final boolean escapes = controlFlowEscapes(body); 1.1362 - if (escapes) { 1.1363 - body.setIsTerminal(false); 1.1364 - } 1.1365 - 1.1366 - // pop the loop from the loop context 1.1367 - unnest(); 1.1368 - 1.1369 - if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { 1.1370 - forNode.setTest(null); 1.1371 - forNode.setIsTerminal(!escapes); 1.1372 - } 1.1373 - 1.1374 - statements.add(forNode); 1.1375 - 1.1376 - return forNode; 1.1377 - } 1.1378 - 1.1379 - /** 1.1380 - * Initialize this symbol and variable for function node 1.1381 - * 1.1382 - * @param functionNode the function node 1.1383 - */ 1.1384 - private void initThis(final FunctionNode functionNode) { 1.1385 - final long token = functionNode.getToken(); 1.1386 - final int finish = functionNode.getFinish(); 1.1387 - 1.1388 - final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null); 1.1389 - final IdentNode thisNode = new IdentNode(source, token, finish, thisSymbol.getName()); 1.1390 - 1.1391 - thisSymbol.setType(Type.OBJECT); 1.1392 - thisSymbol.setNeedsSlot(true); 1.1393 - 1.1394 - thisNode.setSymbol(thisSymbol); 1.1395 - 1.1396 - functionNode.setThisNode(thisNode); 1.1397 - } 1.1398 - 1.1399 - /** 1.1400 - * Initialize scope symbol and variable for function node 1.1401 - * 1.1402 - * @param functionNode the function node 1.1403 - */ 1.1404 - private void initScope(final FunctionNode functionNode) { 1.1405 - final long token = functionNode.getToken(); 1.1406 - final int finish = functionNode.getFinish(); 1.1407 - 1.1408 - final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null); 1.1409 - final IdentNode scopeNode = new IdentNode(source, token, finish, scopeSymbol.getName()); 1.1410 - 1.1411 - scopeSymbol.setType(ScriptObject.class); 1.1412 - scopeSymbol.setNeedsSlot(true); 1.1413 - 1.1414 - scopeNode.setSymbol(scopeSymbol); 1.1415 - 1.1416 - functionNode.setScopeNode(scopeNode); 1.1417 - } 1.1418 - 1.1419 - /** 1.1420 - * Initialize return symbol and variable for function node 1.1421 - * 1.1422 - * @param functionNode the function node 1.1423 - */ 1.1424 - private void initReturn(final FunctionNode functionNode) { 1.1425 - final long token = functionNode.getToken(); 1.1426 - final int finish = functionNode.getFinish(); 1.1427 - 1.1428 - final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); 1.1429 - final IdentNode returnNode = new IdentNode(source, token, finish, returnSymbol.getName()); 1.1430 - 1.1431 - returnSymbol.setType(Object.class); 1.1432 - returnSymbol.setNeedsSlot(true); 1.1433 - 1.1434 - returnNode.setSymbol(returnSymbol); 1.1435 - 1.1436 - functionNode.setResultNode(returnNode); 1.1437 - } 1.1438 - 1.1439 - /** 1.1440 - * Initialize varargs for function node, if applicable 1.1441 - * 1.1442 - * @param functionNode 1.1443 - */ 1.1444 - private void initVarArg(final FunctionNode functionNode) { 1.1445 + private void initFunctionNode(final FunctionNode functionNode) { 1.1446 final long token = functionNode.getToken(); 1.1447 final int finish = functionNode.getFinish(); 1.1448 1.1449 - assert functionNode.getCalleeNode() != null; 1.1450 + LOG.info("Init this, scope, result, callee, varargs and argument for " + functionNode.getName()); 1.1451 1.1452 - final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); 1.1453 - final IdentNode varArgsNode = new IdentNode(source, token, finish, varArgsSymbol.getName()); 1.1454 - 1.1455 - varArgsSymbol.setType(Type.OBJECT_ARRAY); 1.1456 - varArgsSymbol.setNeedsSlot(true); 1.1457 - 1.1458 - varArgsNode.setSymbol(varArgsSymbol); 1.1459 - 1.1460 - functionNode.setVarArgsNode(varArgsNode); 1.1461 - 1.1462 - final String argumentsName = ARGUMENTS.tag(); 1.1463 - final String name = functionNode.hideArguments() ? functionNode.uniqueName("$" + argumentsName) : argumentsName; 1.1464 - final Symbol argumentsSymbol = functionNode.defineSymbol(name, IS_PARAM | IS_INTERNAL, null); 1.1465 - final IdentNode argumentsNode = new IdentNode(source, token, finish, argumentsSymbol.getName()); 1.1466 - 1.1467 - argumentsSymbol.setType(Type.OBJECT); 1.1468 - argumentsSymbol.setNeedsSlot(true); 1.1469 - 1.1470 - argumentsNode.setSymbol(argumentsSymbol); 1.1471 - 1.1472 - functionNode.setArgumentsNode(argumentsNode); 1.1473 + functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag())); 1.1474 + functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag())); 1.1475 + functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag())); 1.1476 + functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag())); 1.1477 + functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag())); 1.1478 + functionNode.setArgumentsNode(new IdentNode(source, token, finish, functionNode.hideArguments() ? compiler.uniqueName('$' + ARGUMENTS.tag()) : ARGUMENTS.tag())); 1.1479 } 1.1480 1.1481 - private void initCallee(final FunctionNode functionNode) { 1.1482 - if (functionNode.getCalleeNode() != null) { 1.1483 - return; 1.1484 - } 1.1485 +} 1.1486 1.1487 - final long token = functionNode.getToken(); 1.1488 - final int finish = functionNode.getFinish(); 1.1489 1.1490 - final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); 1.1491 - final IdentNode calleeNode = new IdentNode(source, token, finish, calleeSymbol.getName()); 1.1492 1.1493 - calleeSymbol.setType(ScriptFunction.class); 1.1494 - calleeSymbol.setNeedsSlot(true); 1.1495 - 1.1496 - calleeNode.setSymbol(calleeSymbol); 1.1497 - 1.1498 - functionNode.setCalleeNode(calleeNode); 1.1499 - } 1.1500 - 1.1501 - /** 1.1502 - * Initialize parameters for function node. This may require specializing 1.1503 - * types if a specialization profile is known 1.1504 - * 1.1505 - * @param functionNode the function node 1.1506 - */ 1.1507 - private void initParameters(final FunctionNode functionNode) { 1.1508 - /* 1.1509 - * If a function is specialized, we don't need to tag either it return 1.1510 - * type or its parameters with the widest (OBJECT) type for safety. 1.1511 - */ 1.1512 - functionNode.setReturnType(Type.UNKNOWN); 1.1513 - 1.1514 - for (final IdentNode ident : functionNode.getParameters()) { 1.1515 - localDefs.add(ident.getName()); 1.1516 - final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident); 1.1517 - if (paramSymbol != null) { 1.1518 - paramSymbol.setType(Type.UNKNOWN); 1.1519 - } 1.1520 - } 1.1521 - } 1.1522 - 1.1523 - /** 1.1524 - * This has to run before fix assignment types, store any type specializations for 1.1525 - * paramters, then turn then to objects for the generic version of this method 1.1526 - * 1.1527 - * @param functionNode functionNode 1.1528 - */ 1.1529 - private static void fixParameters(final FunctionNode functionNode) { 1.1530 - boolean nonObjectParams = false; 1.1531 - List<Type> paramSpecializations = new ArrayList<>(); 1.1532 - 1.1533 - for (final IdentNode ident : functionNode.getParameters()) { 1.1534 - final Symbol paramSymbol = ident.getSymbol(); 1.1535 - if (paramSymbol != null) { 1.1536 - Type type = paramSymbol.getSymbolType(); 1.1537 - if (type.isUnknown()) { 1.1538 - type = Type.OBJECT; 1.1539 - } 1.1540 - paramSpecializations.add(type); 1.1541 - if (!type.isObject()) { 1.1542 - nonObjectParams = true; 1.1543 - } 1.1544 - paramSymbol.setType(Type.OBJECT); 1.1545 - } 1.1546 - } 1.1547 - 1.1548 - if (!nonObjectParams) { 1.1549 - paramSpecializations = null; 1.1550 - /* 1.1551 - * Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters 1.1552 - * here. If the callee has parameter specializations, we can regenerate it with those particular types for speed. 1.1553 - */ 1.1554 - } else { 1.1555 - LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations); 1.1556 - } 1.1557 - 1.1558 - // parameters should not be slots for a vararg function, make sure this is the case 1.1559 - if (functionNode.isVarArg()) { 1.1560 - for (final IdentNode param : functionNode.getParameters()) { 1.1561 - param.getSymbol().setNeedsSlot(false); 1.1562 - } 1.1563 - } 1.1564 - } 1.1565 - 1.1566 - private LiteralNode<Undefined> undefined() { 1.1567 - return LiteralNode.newInstance(source, 0, 0, UNDEFINED); 1.1568 - } 1.1569 - 1.1570 - private void guaranteeReturn(final FunctionNode functionNode) { 1.1571 - Node resultNode; 1.1572 - 1.1573 - if (functionNode.isScript()) { 1.1574 - resultNode = functionNode.getResultNode(); 1.1575 - } else { 1.1576 - final Node lastStatement = Node.lastStatement(functionNode.getStatements()); 1.1577 - if (lastStatement != null && (lastStatement.isTerminal() || lastStatement instanceof ReturnNode)) { 1.1578 - return; // return already in place, as it should be for a non-undefined returning function 1.1579 - } 1.1580 - resultNode = undefined().accept(this); 1.1581 - } 1.1582 - 1.1583 - new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null).accept(this); 1.1584 - 1.1585 - functionNode.setReturnType(Type.OBJECT); 1.1586 - } 1.1587 - 1.1588 - /** 1.1589 - * Fix return types for a node. The return type is the widest of all known 1.1590 - * return expressions in the function, and has to be the same for all return 1.1591 - * nodes, thus we need to traverse all of them in case some of them must be 1.1592 - * upcast 1.1593 - * 1.1594 - * @param node function node to scan return types for 1.1595 - */ 1.1596 - private void fixReturnTypes(final FunctionNode node) { 1.1597 - 1.1598 - if (node.getReturnType().isUnknown()) { 1.1599 - node.setReturnType(Type.OBJECT); // for example, infinite loops 1.1600 - } 1.1601 - 1.1602 - node.accept(new NodeVisitor() { 1.1603 - @Override 1.1604 - public Node enter(final FunctionNode subFunction) { 1.1605 - //leave subfunctions alone. their return values are already specialized and should not 1.1606 - //be touched. 1.1607 - return null; 1.1608 - } 1.1609 - @Override 1.1610 - public Node leave(final ReturnNode returnNode) { 1.1611 - if (returnNode.hasExpression()) { 1.1612 - returnNode.setExpression(convert(returnNode.getExpression(), node.getReturnType())); 1.1613 - } 1.1614 - return returnNode; 1.1615 - } 1.1616 - }); 1.1617 - } 1.1618 - 1.1619 - /** 1.1620 - * Augment assignments with casts after traversing a function node. 1.1621 - * Assignment nodes are special, as they require entire scope for type 1.1622 - * inference. 1.1623 - * 1.1624 - * Consider e.g. 1.1625 - * 1.1626 - * var x = 18.5; //double 1.1627 - * for (...) { 1.1628 - * var y = x; //initially y is inferred to be double y = func(x) //y changes to object, but above assignment is unaware that it needs to be var y = (object)x instaed 1.1629 - */ 1.1630 - 1.1631 - private void fixAssignmentTypes(final FunctionNode node, final List<Symbol> declaredSymbols) { 1.1632 - 1.1633 - // Make sure all unknown symbols are OBJECT types now. No UNKNOWN may survive lower. 1.1634 - for (final Symbol symbol : declaredSymbols) { 1.1635 - if (symbol.getSymbolType().isUnknown()) { 1.1636 - LOG.finest("fixAssignmentTypes: widening " + symbol + " to object " + symbol + " in " + getCurrentFunctionNode()); 1.1637 - symbol.setType(Type.OBJECT); 1.1638 - symbol.setCanBeUndefined(); 1.1639 - } 1.1640 - 1.1641 - if (symbol.canBeUndefined()) { 1.1642 - /* 1.1643 - * Ideally we'd like to only widen to the narrowest 1.1644 - * possible type that supports local variables, i.e. doubles for 1.1645 - * numerics, but 1.1646 - * 1.1647 - * var x; 1.1648 - * 1.1649 - * if (x !== undefined) do something 1.1650 - * 1.1651 - * x++; 1.1652 - * 1.1653 - * Screws this up, as x is determined to be a double even though 1.1654 - * the use is undefined This can be fixed with better use 1.1655 - * analysis in the IdentNode visitor. 1.1656 - * 1.1657 - * This actually seems to be superseded with calls to ensureTypeNotUnknown 1.1658 - */ 1.1659 - if (!Type.areEquivalent(symbol.getSymbolType(), Type.OBJECT)) { 1.1660 - debug(symbol + " in " + getCurrentFunctionNode() + " can be undefined. Widening to object"); 1.1661 - symbol.setType(Type.OBJECT); 1.1662 - } 1.1663 - } 1.1664 - } 1.1665 - 1.1666 - node.accept(new NodeVisitor() { 1.1667 - 1.1668 - private void fix(final Assignment<? extends Node> assignment) { 1.1669 - final Node src = assignment.getAssignmentSource(); 1.1670 - final Node dest = assignment.getAssignmentDest(); 1.1671 - 1.1672 - if (src == null) { 1.1673 - return; 1.1674 - } 1.1675 - 1.1676 - //we don't know about scope yet so this is too conservative. AccessSpecialized will remove casts that are not required 1.1677 - 1.1678 - final Type srcType = src.getType(); 1.1679 - Type destType = dest.getType(); 1.1680 - 1.1681 - //we can only narrow an operation type if the variable doesn't have a slot 1.1682 - if (!dest.getSymbol().hasSlot() && !node.hasDeepWithOrEval()) { 1.1683 - destType = Type.narrowest(((Node)assignment).getWidestOperationType(), destType); 1.1684 - } 1.1685 - 1.1686 - if (!Type.areEquivalent(destType, srcType)) { 1.1687 - LOG.finest("fixAssignment " + assignment + " type = " + src.getType() + "=>" + dest.getType()); 1.1688 - assignment.setAssignmentSource(convert(src, destType)); 1.1689 - } 1.1690 - } 1.1691 - 1.1692 - private Node checkAssignment(final Node assignNode) { 1.1693 - if (!assignNode.isAssignment()) { 1.1694 - return assignNode; 1.1695 - } 1.1696 - fix((Assignment<?>)assignNode); 1.1697 - return assignNode; 1.1698 - } 1.1699 - 1.1700 - 1.1701 - @Override 1.1702 - public Node leave(final UnaryNode unaryNode) { 1.1703 - return checkAssignment(unaryNode); 1.1704 - } 1.1705 - 1.1706 - @Override 1.1707 - public Node leave(final BinaryNode binaryNode) { 1.1708 - return checkAssignment(binaryNode); 1.1709 - } 1.1710 - 1.1711 - @Override 1.1712 - public Node leave(final VarNode varNode) { 1.1713 - return checkAssignment(varNode); 1.1714 - } 1.1715 - }); 1.1716 - } 1.1717 - 1.1718 - /* 1.1719 - * For a script, add scope symbols as defined in the property map 1.1720 - */ 1.1721 - private static void initFromPropertyMap(final Context context, final FunctionNode functionNode) { 1.1722 - assert functionNode.isScript(); 1.1723 - 1.1724 - final PropertyMap map = context.getGlobalMap(); 1.1725 - 1.1726 - for (final Property property : map.getProperties()) { 1.1727 - final String key = property.getKey(); 1.1728 - functionNode.defineSymbol(key, IS_GLOBAL, null).setType(Type.OBJECT); 1.1729 - } 1.1730 - } 1.1731 - 1.1732 - @Override 1.1733 - public Node enter(final FunctionNode functionNode) { 1.1734 - LOG.info("START FunctionNode: " + functionNode.getName()); 1.1735 - 1.1736 - Node initialEvalResult = undefined(); 1.1737 - 1.1738 - nest(functionNode); 1.1739 - 1.1740 - localDefs = new HashSet<>(); 1.1741 - localUses = new HashSet<>(); 1.1742 - 1.1743 - functionNode.setFrame(functionNode.pushFrame()); 1.1744 - 1.1745 - initThis(functionNode); 1.1746 - initCallee(functionNode); 1.1747 - if (functionNode.isVarArg()) { 1.1748 - initVarArg(functionNode); 1.1749 - } 1.1750 - 1.1751 - initParameters(functionNode); 1.1752 - initScope(functionNode); 1.1753 - initReturn(functionNode); 1.1754 - 1.1755 - /* 1.1756 - * Add all nested functions as symbols in this function 1.1757 - */ 1.1758 - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 1.1759 - final IdentNode ident = nestedFunction.getIdent(); 1.1760 - 1.1761 - // for initial eval result is the last declared function 1.1762 - if (ident != null && nestedFunction.isStatement()) { 1.1763 - final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction); 1.1764 - 1.1765 - functionSymbol.setType(ScriptFunction.class); 1.1766 - initialEvalResult = new IdentNode(ident); 1.1767 - } 1.1768 - } 1.1769 - 1.1770 - if (functionNode.isScript()) { 1.1771 - initFromPropertyMap(compiler.getContext(), functionNode); 1.1772 - } 1.1773 - 1.1774 - // Add function name as local symbol 1.1775 - if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) { 1.1776 - final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode); 1.1777 - selfSymbol.setType(Type.OBJECT); 1.1778 - selfSymbol.setNode(functionNode); 1.1779 - } 1.1780 - 1.1781 - /* 1.1782 - * As we are evaluating a nested structure, we need to store the 1.1783 - * statement list for the surrounding block and restore it when the 1.1784 - * function is done 1.1785 - */ 1.1786 - final List<Node> savedStatements = statements; 1.1787 - statements = new ArrayList<>(); 1.1788 - 1.1789 - /* 1.1790 - * This pushes all declarations (except for non-statements, i.e. for 1.1791 - * node temporaries) to the top of the function scope. This way we can 1.1792 - * get around problems like 1.1793 - * 1.1794 - * while (true) { 1.1795 - * break; 1.1796 - * if (true) { 1.1797 - * var s; 1.1798 - * } 1.1799 - * } 1.1800 - * 1.1801 - * to an arbitrary nesting depth. 1.1802 - * 1.1803 - * @see NASHORN-73 1.1804 - */ 1.1805 - 1.1806 - final List<Symbol> declaredSymbols = new ArrayList<>(); 1.1807 - for (final VarNode decl : functionNode.getDeclarations()) { 1.1808 - final IdentNode ident = decl.getName(); 1.1809 - // any declared symbols that aren't visited need to be typed as 1.1810 - // well, hence the list 1.1811 - declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident))); 1.1812 - } 1.1813 - 1.1814 - declaredSymbolsLocal = new ArrayList<>(); 1.1815 - 1.1816 - /* 1.1817 - * Main function code lowering 1.1818 - */ 1.1819 - try { 1.1820 - /* 1.1821 - * Every nested function needs a definition in the outer function 1.1822 - * with its name. Add these. 1.1823 - */ 1.1824 - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 1.1825 - final VarNode varNode = nestedFunction.getFunctionVarNode(); 1.1826 - if (varNode != null) { 1.1827 - final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode(); 1.1828 - if (lineNumberNode != null) { 1.1829 - lineNumberNode.accept(this); 1.1830 - } 1.1831 - varNode.accept(this); 1.1832 - varNode.setIsFunctionVarNode(); 1.1833 - } 1.1834 - } 1.1835 - 1.1836 - if (functionNode.isScript()) { 1.1837 - new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this); 1.1838 - } 1.1839 - 1.1840 - /* 1.1841 - * Now we do the statements. This fills the block with code 1.1842 - */ 1.1843 - for (final Node statement : functionNode.getStatements()) { 1.1844 - statement.accept(this); 1.1845 - 1.1846 - final Node lastStatement = Node.lastStatement(statements); 1.1847 - if (lastStatement != null && lastStatement.hasTerminalFlags()) { 1.1848 - functionNode.copyTerminalFlags(lastStatement); 1.1849 - break; 1.1850 - } 1.1851 - } 1.1852 - 1.1853 - functionNode.setStatements(statements); 1.1854 - 1.1855 - /* 1.1856 - * If there are unusedterminated enpoints in the function, we need 1.1857 - * to add a "return undefined" in those places for correct semantics 1.1858 - */ 1.1859 - if (!functionNode.isTerminal()) { 1.1860 - guaranteeReturn(functionNode); 1.1861 - } 1.1862 - 1.1863 - /* 1.1864 - * Emit all nested functions 1.1865 - */ 1.1866 - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 1.1867 - nestedFunction.accept(this); 1.1868 - } 1.1869 - 1.1870 - fixReturnTypes(functionNode); 1.1871 - 1.1872 - if (functionNode.needsSelfSymbol()) { 1.1873 - final IdentNode selfSlotNode = new IdentNode(functionNode.getIdent()); 1.1874 - selfSlotNode.setSymbol(functionNode.findSymbol(functionNode.getIdent().getName())); 1.1875 - final Node functionRef = new IdentNode(functionNode.getCalleeNode()); 1.1876 - statements.add(0, new VarNode(source, functionNode.getToken(), functionNode.getFinish(), selfSlotNode, functionRef, false).accept(this)); 1.1877 - } 1.1878 - } finally { 1.1879 - /* 1.1880 - * Restore statement for outer block that we were working on 1.1881 - */ 1.1882 - statements = savedStatements; 1.1883 - } 1.1884 - 1.1885 - unnest(); 1.1886 - 1.1887 - fixParameters(functionNode); 1.1888 - 1.1889 - /* 1.1890 - * Fix assignment issues. assignments are troublesome as they can be on 1.1891 - * the form "dest = dest op source" where the type is different for the 1.1892 - * two dest:s: right now this is a post pass that handles putting casts 1.1893 - * in the correct places, but it can be implemented as a bottom up AST 1.1894 - * traversal on the fly. TODO. 1.1895 - */ 1.1896 - fixAssignmentTypes(functionNode, declaredSymbols); 1.1897 - 1.1898 - /* 1.1899 - * Set state of function to lowered and pop the frame 1.1900 - */ 1.1901 - functionNode.setIsLowered(true); 1.1902 - functionNode.popFrame(); 1.1903 - 1.1904 - LOG.info("END FunctionNode: " + functionNode.getName()); 1.1905 - 1.1906 - return null; 1.1907 - } 1.1908 - 1.1909 - @Override 1.1910 - public Node enter(final IdentNode identNode) { 1.1911 - final String name = identNode.getName(); 1.1912 - 1.1913 - if (identNode.isPropertyName()) { 1.1914 - // assign a pseudo symbol to property name 1.1915 - identNode.setSymbol(new Symbol(name, 0, Type.OBJECT)); 1.1916 - return null; 1.1917 - } 1.1918 - 1.1919 - final Block block = getCurrentBlock(); 1.1920 - final Symbol oldSymbol = identNode.getSymbol(); 1.1921 - Symbol symbol = block.findSymbol(name); 1.1922 - 1.1923 - /* 1.1924 - * If an existing symbol with the name is found, use that otherwise, 1.1925 - * declare a new one 1.1926 - */ 1.1927 - 1.1928 - if (symbol != null) { 1.1929 - if (isFunctionExpressionSelfReference(symbol)) { 1.1930 - ((FunctionNode) symbol.getNode()).setNeedsSelfSymbol(); 1.1931 - } 1.1932 - 1.1933 - if (!identNode.isInitializedHere()) { // NASHORN-448 1.1934 - // here is a use outside the local def scope 1.1935 - if (!localDefs.contains(name)) { 1.1936 - symbol.setType(Type.OBJECT); 1.1937 - symbol.setCanBeUndefined(); 1.1938 - } 1.1939 - } 1.1940 - 1.1941 - identNode.setSymbol(symbol); 1.1942 - if (!getCurrentFunctionNode().isLocal(symbol)) { 1.1943 - // non-local: we need to put symbol in scope (if it isn't already) 1.1944 - if (!symbol.isScope()) { 1.1945 - final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction()); 1.1946 - for (final Block lookupBlock : lookupBlocks) { 1.1947 - final Symbol refSymbol = lookupBlock.findSymbol(name); 1.1948 - if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f() 1.1949 - LOG.finest("Found a ref symbol that must be scope " + refSymbol); 1.1950 - refSymbol.setIsScope(); 1.1951 - } 1.1952 - } 1.1953 - } 1.1954 - } 1.1955 - } else { 1.1956 - symbol = block.useSymbol(name, identNode); 1.1957 - // we have never seen this before, it can be undefined 1.1958 - symbol.setType(Type.OBJECT); // TODO unknown -we have explicit casts anyway? 1.1959 - symbol.setCanBeUndefined(); 1.1960 - } 1.1961 - 1.1962 - if (symbol != oldSymbol && !identNode.isInitializedHere()) { 1.1963 - symbol.increaseUseCount(); 1.1964 - } 1.1965 - localUses.add(identNode.getName()); 1.1966 - 1.1967 - return null; 1.1968 - } 1.1969 - 1.1970 - private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) { 1.1971 - if (currentFunction.findParentFunction() == topFunction) { 1.1972 - final List<Block> blocks = new LinkedList<>(); 1.1973 - 1.1974 - blocks.add(currentFunction.getParent()); 1.1975 - blocks.addAll(currentFunction.getReferencingParentBlocks()); 1.1976 - return blocks; 1.1977 - } 1.1978 - /** 1.1979 - * assumption: all parent blocks of an inner function will always be in the same outer function; 1.1980 - * therefore we can simply skip through intermediate functions. 1.1981 - * @see FunctionNode#addReferencingParentBlock(Block) 1.1982 - */ 1.1983 - return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction); 1.1984 - } 1.1985 - 1.1986 - private static boolean isFunctionExpressionSelfReference(final Symbol symbol) { 1.1987 - if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) { 1.1988 - return ((FunctionNode) symbol.getNode()).getIdent().getName().equals(symbol.getName()); 1.1989 - } 1.1990 - return false; 1.1991 - } 1.1992 - 1.1993 - @Override 1.1994 - public Node enter(final IfNode ifNode) { 1.1995 - nest(ifNode); 1.1996 - return ifNode; 1.1997 - } 1.1998 - 1.1999 - @Override 1.2000 - public Node leave(final IfNode ifNode) { 1.2001 - 1.2002 - final Node test = convert(ifNode.getTest(), Type.BOOLEAN); 1.2003 - 1.2004 - /* 1.2005 - * constant if checks should short circuit into just one of the 1.2006 - * pass/fail blocks 1.2007 - */ 1.2008 - if (test.getSymbol().isConstant()) { 1.2009 - assert test instanceof LiteralNode<?>; 1.2010 - final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); 1.2011 - if (shortCut != null) { 1.2012 - for (final Node statement : shortCut.getStatements()) { 1.2013 - statements.add(statement); 1.2014 - } 1.2015 - } 1.2016 - return null; 1.2017 - } 1.2018 - 1.2019 - final Node pass = ifNode.getPass(); 1.2020 - final Node fail = ifNode.getFail(); 1.2021 - 1.2022 - if (pass.isTerminal() && fail != null && fail.isTerminal()) { 1.2023 - ifNode.setIsTerminal(true); 1.2024 - } 1.2025 - 1.2026 - ifNode.setTest(test); 1.2027 - statements.add(ifNode); 1.2028 - 1.2029 - unnest(); 1.2030 - 1.2031 - return ifNode; 1.2032 - } 1.2033 - 1.2034 - @Override 1.2035 - public Node leave(final IndexNode indexNode) { 1.2036 - getCurrentFunctionNode().newTemporary(Type.OBJECT, indexNode); 1.2037 - 1.2038 - return indexNode; 1.2039 - } 1.2040 - 1.2041 - @Override 1.2042 - public Node enter(final LabelNode labeledNode) { 1.2043 - // Don't want a symbol for label. 1.2044 - final Block body = labeledNode.getBody(); 1.2045 - body.accept(this); 1.2046 - labeledNode.copyTerminalFlags(body); 1.2047 - statements.add(labeledNode); 1.2048 - 1.2049 - return null; 1.2050 - } 1.2051 - 1.2052 - @Override 1.2053 - public Node enter(final LineNumberNode lineNumberNode) { 1.2054 - statements.add(lineNumberNode); 1.2055 - return null; 1.2056 - } 1.2057 - 1.2058 - /** 1.2059 - * Generate a new literal symbol. 1.2060 - * 1.2061 - * @param literalNode LiteralNode needing symbol. 1.2062 - * @return The literal node augmented with the symbol 1.2063 - */ 1.2064 - private LiteralNode<?> newLiteral(final LiteralNode<?> literalNode) { 1.2065 - return newLiteral(getCurrentFunctionNode(), literalNode); 1.2066 - } 1.2067 - 1.2068 - private static LiteralNode<?> newLiteral(final FunctionNode functionNode, final LiteralNode<?> literalNode) { 1.2069 - functionNode.newLiteral(literalNode); 1.2070 - return literalNode; 1.2071 - } 1.2072 - 1.2073 - @SuppressWarnings("rawtypes") 1.2074 - @Override 1.2075 - public Node enter(final LiteralNode literalNode) { 1.2076 - if (literalNode.isTokenType(TokenType.THIS)) { 1.2077 - literalNode.setSymbol(getCurrentFunctionNode().getThisNode().getSymbol()); 1.2078 - return null; 1.2079 - } 1.2080 - 1.2081 - if (literalNode instanceof ArrayLiteralNode) { 1.2082 - final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; 1.2083 - final Node[] array = arrayLiteralNode.getValue(); 1.2084 - 1.2085 - for (int i = 0; i < array.length; i++) { 1.2086 - final Node element = array[i]; 1.2087 - if (element != null) { 1.2088 - array[i] = array[i].accept(this); 1.2089 - } 1.2090 - } 1.2091 - 1.2092 - arrayLiteralNode.analyze(); 1.2093 - final Type elementType = arrayLiteralNode.getElementType(); 1.2094 - 1.2095 - // we only have types after all elements are accepted 1.2096 - for (int i = 0; i < array.length; i++) { 1.2097 - final Node element = array[i]; 1.2098 - if (element != null) { 1.2099 - array[i] = convert(element, elementType); 1.2100 - } 1.2101 - } 1.2102 - } else { 1.2103 - final Object value = literalNode.getValue(); 1.2104 - 1.2105 - if (value instanceof Node) { 1.2106 - final Node node = ((Node)value).accept(this); 1.2107 - if (node != null) { 1.2108 - return newLiteral(LiteralNode.newInstance(source, node.getToken(), node.getFinish(), node)); 1.2109 - } 1.2110 - } 1.2111 - } 1.2112 - 1.2113 - newLiteral(literalNode); 1.2114 - 1.2115 - return null; 1.2116 - } 1.2117 - 1.2118 - @Override 1.2119 - public Node leave(final ObjectNode objectNode) { 1.2120 - getCurrentFunctionNode().newTemporary(Type.OBJECT, objectNode); 1.2121 - return objectNode; 1.2122 - } 1.2123 - 1.2124 - @Override 1.2125 - public Node enter(final PropertyNode propertyNode) { 1.2126 - // assign a pseudo symbol to property name, see NASHORN-710 1.2127 - propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); 1.2128 - return propertyNode; 1.2129 - } 1.2130 - 1.2131 - @Override 1.2132 - public Node enter(final ReferenceNode referenceNode) { 1.2133 - final FunctionNode functionNode = referenceNode.getReference(); 1.2134 - if (functionNode != null) { 1.2135 - functionNode.addReferencingParentBlock(getCurrentBlock()); 1.2136 - } 1.2137 - return referenceNode; 1.2138 - } 1.2139 - 1.2140 - @Override 1.2141 - public Node leave(final ReferenceNode referenceNode) { 1.2142 - getCurrentFunctionNode().newTemporary(Type.OBJECT, referenceNode); 1.2143 - return referenceNode; 1.2144 - } 1.2145 - 1.2146 - /** 1.2147 - * For each return node we need to set a return expression of the correct 1.2148 - * type. In the non-specializing world, we can always upcast to object and 1.2149 - * ignore the rest, but in the specializing world we have to keep track of 1.2150 - * the widest return type so far, which is the common one for this method. 1.2151 - * 1.2152 - * @param returnNode the return node we are generating 1.2153 - * @param expression the expression to return from this code 1.2154 - */ 1.2155 - private void setReturnExpression(final ReturnNode returnNode, final Node expression) { 1.2156 - final FunctionNode functionNode = getCurrentFunctionNode(); 1.2157 - 1.2158 - returnNode.setExpression(expression); 1.2159 - 1.2160 - final Symbol symbol = expression.getSymbol(); 1.2161 - if (expression.getType().isUnknown() && symbol.isParam()) { 1.2162 - symbol.setType(Type.OBJECT); 1.2163 - } 1.2164 - functionNode.setReturnType(Type.widest(expression.getType(), functionNode.getReturnType())); 1.2165 - } 1.2166 - 1.2167 - @Override 1.2168 - public Node enter(final ReturnNode returnNode) { 1.2169 - final TryNode tryNode = returnNode.getTryChain(); 1.2170 - final Node expression = returnNode.getExpression(); 1.2171 - 1.2172 - if (tryNode != null) { 1.2173 - if (expression != null) { 1.2174 - final long token = returnNode.getToken(); 1.2175 - final Node resultNode = getCurrentFunctionNode().getResultNode(); 1.2176 - new ExecuteNode(source, token, Token.descPosition(token), new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expression)).accept(this); 1.2177 - 1.2178 - if (copyFinally(tryNode, null)) { 1.2179 - return null; 1.2180 - } 1.2181 - 1.2182 - setReturnExpression(returnNode, resultNode); 1.2183 - } else if (copyFinally(tryNode, null)) { 1.2184 - return null; 1.2185 - } 1.2186 - } else if (expression != null) { 1.2187 - setReturnExpression(returnNode, expression.accept(this)); 1.2188 - } 1.2189 - 1.2190 - statements.add(returnNode); 1.2191 - 1.2192 - return null; 1.2193 - } 1.2194 - 1.2195 - @Override 1.2196 - public Node leave(final RuntimeNode runtimeNode) { 1.2197 - for (final Node arg : runtimeNode.getArgs()) { 1.2198 - ensureTypeNotUnknown(arg); 1.2199 - } 1.2200 - 1.2201 - getCurrentFunctionNode().newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode); 1.2202 - 1.2203 - return runtimeNode; 1.2204 - } 1.2205 - 1.2206 - @Override 1.2207 - public Node enter(final SwitchNode switchNode) { 1.2208 - nest(switchNode); 1.2209 - return switchNode; 1.2210 - } 1.2211 - 1.2212 - @Override 1.2213 - public Node leave(final SwitchNode switchNode) { 1.2214 - unnest(); 1.2215 - 1.2216 - final Node expression = switchNode.getExpression(); 1.2217 - final List<CaseNode> cases = switchNode.getCases(); 1.2218 - final CaseNode defaultCase = switchNode.getDefaultCase(); 1.2219 - final boolean hasDefault = defaultCase != null; 1.2220 - final int n = cases.size() + (hasDefault ? -1 : 0); 1.2221 - 1.2222 - boolean allTerminal = !cases.isEmpty(); 1.2223 - boolean allInteger = n > 1; 1.2224 - 1.2225 - for (final CaseNode caseNode : cases) { 1.2226 - allTerminal &= caseNode.isTerminal(); 1.2227 - final Node test = caseNode.getTest(); 1.2228 - 1.2229 - // Skip default. 1.2230 - if (test == null) { 1.2231 - continue; 1.2232 - } 1.2233 - 1.2234 - // If not a number. 1.2235 - if (!(test instanceof LiteralNode) || !test.getType().isNumeric()) { 1.2236 - allInteger = false; 1.2237 - continue; 1.2238 - } 1.2239 - 1.2240 - final LiteralNode<?> testLiteral = (LiteralNode<?>)test; 1.2241 - 1.2242 - // If a potential integer. 1.2243 - if (!(testLiteral.getValue() instanceof Integer)) { 1.2244 - // If not an integer value. 1.2245 - if (!JSType.isRepresentableAsInt(testLiteral.getNumber())) { 1.2246 - allInteger = false; 1.2247 - continue; 1.2248 - } 1.2249 - 1.2250 - // Guarantee all case literals are Integers (simplifies sorting in code gen.) 1.2251 - caseNode.setTest(newLiteral(LiteralNode.newInstance(source, testLiteral.getToken(), testLiteral.getFinish(), testLiteral.getInt32()))); 1.2252 - } 1.2253 - } 1.2254 - 1.2255 - if (allTerminal && defaultCase != null && defaultCase.isTerminal()) { 1.2256 - switchNode.setIsTerminal(true); 1.2257 - } 1.2258 - 1.2259 - if (!allInteger) { 1.2260 - switchNode.setExpression(convert(expression, Type.OBJECT)); 1.2261 - 1.2262 - for (final CaseNode caseNode : cases) { 1.2263 - final Node test = caseNode.getTest(); 1.2264 - 1.2265 - if (test != null) { 1.2266 - caseNode.setTest(convert(test, Type.OBJECT)); 1.2267 - } 1.2268 - } 1.2269 - } 1.2270 - 1.2271 - final String name = compiler.uniqueName(SWITCH_TAG_PREFIX.tag()); 1.2272 - final Symbol tag = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); 1.2273 - 1.2274 - tag.setType(allInteger ? Type.INT : Type.OBJECT); 1.2275 - switchNode.setTag(tag); 1.2276 - 1.2277 - statements.add(switchNode); 1.2278 - 1.2279 - return switchNode; 1.2280 - } 1.2281 - 1.2282 - @Override 1.2283 - public Node leave(final ThrowNode throwNode) { 1.2284 - throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT)); 1.2285 - statements.add(throwNode); 1.2286 - 1.2287 - return throwNode; 1.2288 - } 1.2289 - 1.2290 - @Override 1.2291 - public Node enter(final TryNode tryNode) { 1.2292 - final Block finallyBody = tryNode.getFinallyBody(); 1.2293 - final Source tryNodeSource = tryNode.getSource(); 1.2294 - final long token = tryNode.getToken(); 1.2295 - final int finish = tryNode.getFinish(); 1.2296 - 1.2297 - nest(tryNode); 1.2298 - 1.2299 - if (finallyBody == null) { 1.2300 - return tryNode; 1.2301 - } 1.2302 - 1.2303 - /** 1.2304 - * We have a finally clause. 1.2305 - * 1.2306 - * Transform to do finally tail duplication as follows: 1.2307 - * 1.2308 - * <pre> 1.2309 - * try { 1.2310 - * try_body 1.2311 - * } catch e1 { 1.2312 - * catchbody_1 1.2313 - * } 1.2314 - * ... 1.2315 - * } catch en { 1.2316 - * catchbody_n 1.2317 - * } finally { 1.2318 - * finally_body 1.2319 - * } 1.2320 - * 1.2321 - * (where e1 ... en are optional) 1.2322 - * 1.2323 - * turns into 1.2324 - * 1.2325 - * try { 1.2326 - * try { 1.2327 - * try_body 1.2328 - * } catch e1 { 1.2329 - * catchbody1 1.2330 - * //nothing inlined explicitly here, return, break other 1.2331 - * //terminals may inline the finally body 1.2332 - * ... 1.2333 - * } catch en { 1.2334 - * catchbody2 1.2335 - * //nothing inlined explicitly here, return, break other 1.2336 - * //terminals may inline the finally body 1.2337 - * } 1.2338 - * } catch all ex { 1.2339 - * finally_body_inlined 1.2340 - * rethrow ex 1.2341 - * } 1.2342 - * finally_body_inlined 1.2343 - * </pre> 1.2344 - * 1.2345 - * If tries are catches are terminal, visitors for return, break & 1.2346 - * continue will handle the tail duplications. Throw needs to be 1.2347 - * treated specially with the catchall as described in the above 1.2348 - * ASCII art. 1.2349 - * 1.2350 - * If the try isn't terminal we do the finally_body_inlined at the 1.2351 - * end. If the try is terminated with continue/break/return the 1.2352 - * existing visitor logic will inline the finally before that 1.2353 - * operation. if the try is terminated with a throw, the catches e1 1.2354 - * ... en will have a chance to process the exception. If the 1.2355 - * appropriate catch e1..en is non terminal we fall through to the 1.2356 - * last finally_body_inlined. if the catch e1...en IS terminal with 1.2357 - * continue/break/return existing visitor logic will fix it. If they 1.2358 - * are terminal with another throw it goes to the catchall and the 1.2359 - * finally_body_inlined marked (*) will fix it before rethrowing 1.2360 - * whatever problem there was for identical semantic. 1.2361 - */ 1.2362 - 1.2363 - // if try node does not contain a catch we can skip creation of a new 1.2364 - // try node and just append our synthetic catch to the existing try node. 1.2365 - if (!tryNode.getCatchBlocks().isEmpty()) { 1.2366 - // insert an intermediate try-catch* node, where we move the body and all catch blocks. 1.2367 - // the original try node become a try-finally container for the new try-catch* node. 1.2368 - // because we don't clone (to avoid deep copy), we have to fix the block chain in the end. 1.2369 - final TryNode innerTryNode = new TryNode(tryNodeSource, token, finish, tryNode.getNext()); 1.2370 - innerTryNode.setBody(tryNode.getBody()); 1.2371 - innerTryNode.setCatchBlocks(tryNode.getCatchBlocks()); 1.2372 - 1.2373 - // set outer tryNode's body to innerTryNode 1.2374 - final Block outerBody = new Block(tryNodeSource, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode()); 1.2375 - outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode))); 1.2376 - tryNode.setBody(outerBody); 1.2377 - tryNode.setCatchBlocks(null); 1.2378 - 1.2379 - // now before we go on, we have to fix the block parents 1.2380 - // (we repair the block tree after the insertion so that all references are intact) 1.2381 - innerTryNode.getBody().setParent(tryNode.getBody()); 1.2382 - for (final Block block : innerTryNode.getCatchBlocks()) { 1.2383 - block.setParent(tryNode.getBody()); 1.2384 - } 1.2385 - } 1.2386 - 1.2387 - // create a catch-all that inlines finally and rethrows 1.2388 - final String name = compiler.uniqueName(EXCEPTION_PREFIX.tag()); 1.2389 - 1.2390 - final IdentNode exception = new IdentNode(tryNodeSource, token, finish, name); 1.2391 - exception.accept(this); 1.2392 - 1.2393 - final Block catchBlock = new Block(tryNodeSource, token, finish, getCurrentBlock(), getCurrentFunctionNode()); 1.2394 - final Block catchBody = new Block(tryNodeSource, token, finish, catchBlock, getCurrentFunctionNode()); 1.2395 - final Node catchAllFinally = finallyBody.clone(); 1.2396 - 1.2397 - catchBody.addStatement(new ExecuteNode(tryNodeSource, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally)); 1.2398 - catchBody.setIsTerminal(true); 1.2399 - 1.2400 - final CatchNode catchNode = new CatchNode(tryNodeSource, token, finish, new IdentNode(exception), null, catchBody); 1.2401 - catchNode.setIsSyntheticRethrow(); 1.2402 - catchBlock.addStatement(catchNode); 1.2403 - 1.2404 - // replace all catches of outer tryNode with the catch-all 1.2405 - tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock))); 1.2406 - 1.2407 - /* 1.2408 - * We leave the finally block for the original try in place for now 1.2409 - * so that children visitations will work. It is removed and placed 1.2410 - * afterwards in the else case below, after all children are visited 1.2411 - */ 1.2412 - 1.2413 - return tryNode; 1.2414 - } 1.2415 - 1.2416 - @Override 1.2417 - public Node leave(final TryNode tryNode) { 1.2418 - final Block finallyBody = tryNode.getFinallyBody(); 1.2419 - 1.2420 - boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal()); 1.2421 - 1.2422 - for (final Block catchBlock : tryNode.getCatchBlocks()) { 1.2423 - allTerminal &= catchBlock.isTerminal(); 1.2424 - } 1.2425 - 1.2426 - tryNode.setIsTerminal(allTerminal); 1.2427 - 1.2428 - final String name = compiler.uniqueName(EXCEPTION_PREFIX.tag()); 1.2429 - final Symbol exception = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); 1.2430 - 1.2431 - exception.setType(ECMAException.class); 1.2432 - tryNode.setException(exception); 1.2433 - 1.2434 - statements.add(tryNode); 1.2435 - 1.2436 - unnest(); 1.2437 - 1.2438 - // if finally body is present, place it after the tryNode 1.2439 - if (finallyBody != null) { 1.2440 - tryNode.setFinallyBody(null); 1.2441 - statements.add(finallyBody); 1.2442 - } 1.2443 - 1.2444 - return tryNode; 1.2445 - } 1.2446 - 1.2447 - @Override 1.2448 - public Node enter(final VarNode varNode) { 1.2449 - final IdentNode ident = varNode.getName(); 1.2450 - final String name = ident.getName(); 1.2451 - 1.2452 - final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident); 1.2453 - assert symbol != null; 1.2454 - 1.2455 - varNode.setSymbol(symbol); 1.2456 - declaredSymbolsLocal.add(symbol); 1.2457 - 1.2458 - // NASHORN-467 - use before definition of vars - conservative 1.2459 - if (localUses.contains(ident.getName())) { 1.2460 - symbol.setType(Type.OBJECT); 1.2461 - symbol.setCanBeUndefined(); 1.2462 - } 1.2463 - 1.2464 - return varNode; 1.2465 - } 1.2466 - 1.2467 - private static boolean isScopeOrGlobal(final Symbol symbol) { 1.2468 - return symbol.getBlock().getFunction().isScript(); //we can't do explicit isScope checks as early as lower, which is why AccessSpecializer is afterwards as well. 1.2469 - } 1.2470 - 1.2471 - @Override 1.2472 - public Node leave(final VarNode varNode) { 1.2473 - final Node init = varNode.getInit(); 1.2474 - final IdentNode ident = varNode.getName(); 1.2475 - final String name = ident.getName(); 1.2476 - 1.2477 - if (init != null) { 1.2478 - localDefs.add(name); 1.2479 - } 1.2480 - 1.2481 - if (varNode.shouldAppend()) { 1.2482 - statements.add(varNode); 1.2483 - } 1.2484 - 1.2485 - if (init == null) { 1.2486 - // var x; with no init will be treated like a use of x by 1.2487 - // visit(IdentNode) unless we remove the name 1.2488 - // from the localdef list. 1.2489 - localDefs.remove(name); 1.2490 - return varNode; 1.2491 - } 1.2492 - 1.2493 - final Symbol symbol = varNode.getSymbol(); 1.2494 - if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScopeOrGlobal(symbol)) { //see NASHORN-56 1.2495 - // Forbid integers as local vars for now as we have no way to treat them as undefined 1.2496 - final Type type = Compiler.shouldUseIntegers() ? init.getType() : Type.NUMBER; 1.2497 - varNode.setInit(convert(init, type)); 1.2498 - symbol.setType(type); 1.2499 - } else { 1.2500 - symbol.setType(Type.OBJECT); // var x = obj;, x is an "object" //TODO too conservative now? 1.2501 - } 1.2502 - 1.2503 - return varNode; 1.2504 - } 1.2505 - 1.2506 - @Override 1.2507 - public Node enter(final WhileNode whileNode) { 1.2508 - // push the loop to the loop context 1.2509 - nest(whileNode); 1.2510 - 1.2511 - return whileNode; 1.2512 - } 1.2513 - 1.2514 - @Override 1.2515 - public Node leave(final WhileNode whileNode) { 1.2516 - final Node test = whileNode.getTest(); 1.2517 - 1.2518 - if (test != null) { 1.2519 - whileNode.setTest(convert(test, Type.BOOLEAN)); 1.2520 - } else { 1.2521 - whileNode.setHasGoto(); 1.2522 - } 1.2523 - 1.2524 - Node returnNode = whileNode; 1.2525 - 1.2526 - final Block body = whileNode.getBody(); 1.2527 - final boolean escapes = controlFlowEscapes(body); 1.2528 - if (escapes) { 1.2529 - body.setIsTerminal(false); 1.2530 - } 1.2531 - 1.2532 - if (body.isTerminal()) { 1.2533 - if (whileNode instanceof DoWhileNode) { 1.2534 - whileNode.setIsTerminal(true); 1.2535 - } else if (conservativeAlwaysTrue(test)) { 1.2536 - returnNode = new ForNode(source, whileNode.getToken(), whileNode.getFinish()); 1.2537 - ((ForNode)returnNode).setBody(body); 1.2538 - returnNode.setIsTerminal(!escapes); 1.2539 - } 1.2540 - } 1.2541 - 1.2542 - // pop the loop from the loop context 1.2543 - unnest(); 1.2544 - statements.add(returnNode); 1.2545 - 1.2546 - return returnNode; 1.2547 - } 1.2548 - 1.2549 - @Override 1.2550 - public Node leave(final WithNode withNode) { 1.2551 - withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT)); 1.2552 - 1.2553 - if (withNode.getBody().isTerminal()) { 1.2554 - withNode.setIsTerminal(true); 1.2555 - } 1.2556 - 1.2557 - statements.add(withNode); 1.2558 - 1.2559 - return withNode; 1.2560 - } 1.2561 - 1.2562 - /* 1.2563 - * Unary visits. 1.2564 - */ 1.2565 - 1.2566 - /** 1.2567 - * Visit unary node and set symbols and inferred type 1.2568 - * 1.2569 - * @param unaryNode unary node to visit 1.2570 - * @param type The narrowest allowed type for this node 1.2571 - * 1.2572 - * @return the final node, or replacement thereof 1.2573 - */ 1.2574 - public Node leaveUnary(final UnaryNode unaryNode, final Type type) { 1.2575 - /* Try to eliminate the unary node if the expression is constant */ 1.2576 - final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval(); 1.2577 - if (literalNode != null) { 1.2578 - return newLiteral(literalNode); 1.2579 - } 1.2580 - 1.2581 - /* Add explicit conversion */ 1.2582 - unaryNode.setRHS(convert(unaryNode.rhs(), type)); 1.2583 - getCurrentFunctionNode().newTemporary(type, unaryNode); 1.2584 - 1.2585 - return unaryNode; 1.2586 - } 1.2587 - 1.2588 - @Override 1.2589 - public Node leaveADD(final UnaryNode unaryNode) { 1.2590 - return leaveUnary(unaryNode, Type.NUMBER); 1.2591 - } 1.2592 - 1.2593 - @Override 1.2594 - public Node leaveBIT_NOT(final UnaryNode unaryNode) { 1.2595 - return leaveUnary(unaryNode, Type.INT); 1.2596 - } 1.2597 - 1.2598 - @Override 1.2599 - public Node leaveCONVERT(final UnaryNode unaryNode) { 1.2600 - final Node rhs = unaryNode.rhs(); 1.2601 - 1.2602 - if (rhs.isTokenType(TokenType.CONVERT)) { 1.2603 - rhs.setSymbol(unaryNode.getSymbol()); 1.2604 - return rhs; 1.2605 - } 1.2606 - 1.2607 - return unaryNode; 1.2608 - } 1.2609 - 1.2610 - /** 1.2611 - * In an assignment, recursively make sure that there are slots for 1.2612 - * everything that has to be laid out as temporary storage, which is the 1.2613 - * case if we are assign-op:ing a BaseNode subclass. This has to be 1.2614 - * recursive to handle things like multi dimensional arrays as lhs 1.2615 - * 1.2616 - * see NASHORN-258 1.2617 - * 1.2618 - * @param functionNode the current function node (has to be passed as it changes in the visitor below) 1.2619 - * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes 1.2620 - */ 1.2621 - private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) { 1.2622 - 1.2623 - assignmentDest.accept(new NodeVisitor() { 1.2624 - 1.2625 - @Override 1.2626 - public Node leave(final AccessNode accessNode) { 1.2627 - final Node base = accessNode.getBase(); 1.2628 - if (base.getSymbol().isThis()) { 1.2629 - functionNode.addThisProperty(accessNode.getProperty().getName(), accessNode); 1.2630 - } 1.2631 - 1.2632 - return accessNode; 1.2633 - } 1.2634 - 1.2635 - @Override 1.2636 - public Node leave(final IndexNode indexNode) { 1.2637 - final Node index = indexNode.getIndex(); 1.2638 - index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant()); 1.2639 - 1.2640 - return indexNode; 1.2641 - } 1.2642 - 1.2643 - }); 1.2644 - } 1.2645 - 1.2646 - @Override 1.2647 - public Node leaveDECINC(final UnaryNode unaryNode) { 1.2648 - // @see assignOffset 1.2649 - ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs()); 1.2650 - final Type type = Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER; 1.2651 - 1.2652 - unaryNode.rhs().getSymbol().setType(type); 1.2653 - getCurrentFunctionNode().newTemporary(type, unaryNode); 1.2654 - 1.2655 - return unaryNode; 1.2656 - } 1.2657 - 1.2658 - /** 1.2659 - * Helper class for wrapping a result 1.2660 - */ 1.2661 - private abstract static class ResultNodeVisitor extends NodeVisitor { 1.2662 - 1.2663 - private Node resultNode; 1.2664 - 1.2665 - ResultNodeVisitor(final Node resultNode) { 1.2666 - this.resultNode = resultNode; 1.2667 - } 1.2668 - 1.2669 - Node getResultNode() { 1.2670 - return resultNode; 1.2671 - } 1.2672 - 1.2673 - void setResultNode(final Node resultNode) { 1.2674 - this.resultNode = resultNode; 1.2675 - } 1.2676 - } 1.2677 - 1.2678 - @Override 1.2679 - public Node leaveDELETE(final UnaryNode unaryNode) { 1.2680 - final boolean strictMode = getCurrentFunctionNode().isStrictMode(); 1.2681 - final Node rhs = unaryNode.rhs(); 1.2682 - final long token = unaryNode.getToken(); 1.2683 - final int finish = unaryNode.getFinish(); 1.2684 - final LiteralNode<Boolean> trueNode = LiteralNode.newInstance(source, token, finish, true); 1.2685 - final LiteralNode<Boolean> strictFlagNode = LiteralNode.newInstance(source, token, finish, strictMode); 1.2686 - 1.2687 - newLiteral(trueNode); 1.2688 - newLiteral(strictFlagNode); 1.2689 - 1.2690 - final NodeVisitor lower = this; 1.2691 - final FunctionNode currentFunctionNode = getCurrentFunctionNode(); 1.2692 - 1.2693 - final ResultNodeVisitor visitor = new ResultNodeVisitor(trueNode) { 1.2694 - 1.2695 - private void initRuntimeNode(final RuntimeNode node) { 1.2696 - node.accept(lower); 1.2697 - currentFunctionNode.newTemporary(Type.BOOLEAN, unaryNode); 1.2698 - node.setSymbol(unaryNode.getSymbol()); 1.2699 - 1.2700 - setResultNode(node); 1.2701 - } 1.2702 - 1.2703 - @Override 1.2704 - public Node enter(final IdentNode node) { 1.2705 - // If this is a declared variable or a function parameter, delete always fails (except for globals). 1.2706 - final boolean failDelete = 1.2707 - strictMode || 1.2708 - node.getSymbol().isParam() || 1.2709 - (node.getSymbol().isVar() && !node.getSymbol().isTopLevel()); 1.2710 - 1.2711 - final Node literalNode = 1.2712 - newLiteral( 1.2713 - currentFunctionNode, 1.2714 - LiteralNode.newInstance( 1.2715 - source, 1.2716 - node.getToken(), 1.2717 - node.getFinish(), 1.2718 - node.getName())); 1.2719 - 1.2720 - if (failDelete) { 1.2721 - if (THIS.tag().equals(node.getName())) { 1.2722 - statements.add(new ExecuteNode(source, token, finish, discard(node))); 1.2723 - currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode); 1.2724 - return null; //trueNode it is 1.2725 - } 1.2726 - } 1.2727 - 1.2728 - final List<Node> args = new ArrayList<>(); 1.2729 - args.add(convert(currentFunctionNode.getScopeNode(), Type.OBJECT)); 1.2730 - args.add(convert(literalNode, Type.OBJECT)); 1.2731 - args.add(strictFlagNode); 1.2732 - 1.2733 - initRuntimeNode( 1.2734 - new RuntimeNode( 1.2735 - source, 1.2736 - token, 1.2737 - finish, 1.2738 - failDelete ? FAIL_DELETE : DELETE, 1.2739 - args)); 1.2740 - 1.2741 - return null; 1.2742 - } 1.2743 - 1.2744 - @Override 1.2745 - public Node enter(final AccessNode node) { 1.2746 - final IdentNode property = node.getProperty(); 1.2747 - 1.2748 - initRuntimeNode( 1.2749 - new RuntimeNode( 1.2750 - source, 1.2751 - token, 1.2752 - finish, 1.2753 - DELETE, 1.2754 - convert(node.getBase(), Type.OBJECT), 1.2755 - convert( 1.2756 - newLiteral( 1.2757 - currentFunctionNode, 1.2758 - LiteralNode.newInstance( 1.2759 - source, 1.2760 - property.getToken(), 1.2761 - property.getFinish(), 1.2762 - property.getName())), 1.2763 - Type.OBJECT), 1.2764 - strictFlagNode)); 1.2765 - 1.2766 - return null; 1.2767 - } 1.2768 - 1.2769 - @Override 1.2770 - public Node enter(final IndexNode node) { 1.2771 - initRuntimeNode( 1.2772 - new RuntimeNode( 1.2773 - source, 1.2774 - token, 1.2775 - finish, 1.2776 - DELETE, 1.2777 - convert(node.getBase(), Type.OBJECT), 1.2778 - convert(node.getIndex(), Type.OBJECT), 1.2779 - strictFlagNode)); 1.2780 - 1.2781 - return null; 1.2782 - } 1.2783 - 1.2784 - @Override 1.2785 - public Node enterDefault(final Node node) { 1.2786 - statements.add(new ExecuteNode(source, token, finish, discard(node))); 1.2787 - currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode); 1.2788 - //just return trueNode which is the default 1.2789 - return null; 1.2790 - } 1.2791 - }; 1.2792 - 1.2793 - rhs.accept(visitor); 1.2794 - 1.2795 - return visitor.getResultNode(); 1.2796 - } 1.2797 - 1.2798 - @Override 1.2799 - public Node enterNEW(final UnaryNode unaryNode) { 1.2800 - final CallNode callNode = (CallNode)unaryNode.rhs(); 1.2801 - 1.2802 - callNode.setIsNew(); 1.2803 - 1.2804 - final Node function = callNode.getFunction(); 1.2805 - final Node markedFunction = markerFunction(function); 1.2806 - 1.2807 - callNode.setFunction(markedFunction.accept(this)); 1.2808 - 1.2809 - acceptArgs(callNode); 1.2810 - 1.2811 - getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode); 1.2812 - 1.2813 - return null; 1.2814 - } 1.2815 - 1.2816 - @Override 1.2817 - public Node leaveNOT(final UnaryNode unaryNode) { 1.2818 - return leaveUnary(unaryNode, Type.BOOLEAN); 1.2819 - } 1.2820 - 1.2821 - /** 1.2822 - * Create a null literal 1.2823 - * 1.2824 - * @param parent node to inherit token from. 1.2825 - * @return the new null literal 1.2826 - */ 1.2827 - private LiteralNode<?> newNullLiteral(final Node parent) { 1.2828 - return newLiteral(LiteralNode.newInstance(parent.getSource(), parent.getToken(), parent.getFinish())); 1.2829 - } 1.2830 - 1.2831 - @Override 1.2832 - public Node leaveTYPEOF(final UnaryNode unaryNode) { 1.2833 - final Node rhs = unaryNode.rhs(); 1.2834 - final long token = unaryNode.getToken(); 1.2835 - final int finish = unaryNode.getFinish(); 1.2836 - 1.2837 - RuntimeNode runtimeNode; 1.2838 - 1.2839 - if (rhs instanceof IdentNode) { 1.2840 - final IdentNode ident = (IdentNode)rhs; 1.2841 - 1.2842 - if (ident.getSymbol().isParam() || ident.getSymbol().isVar()) { 1.2843 - runtimeNode = new RuntimeNode( 1.2844 - source, 1.2845 - token, 1.2846 - finish, 1.2847 - TYPEOF, 1.2848 - convert( 1.2849 - rhs, 1.2850 - Type.OBJECT), 1.2851 - newNullLiteral(unaryNode)); 1.2852 - } else { 1.2853 - runtimeNode = new RuntimeNode( 1.2854 - source, 1.2855 - token, 1.2856 - finish, 1.2857 - TYPEOF, 1.2858 - convert( 1.2859 - getCurrentFunctionNode().getScopeNode(), 1.2860 - Type.OBJECT), 1.2861 - convert( 1.2862 - newLiteral( 1.2863 - LiteralNode.newInstance( 1.2864 - source, 1.2865 - ident.getToken(), 1.2866 - ident.getFinish(), 1.2867 - ident.getName())), 1.2868 - Type.OBJECT)); 1.2869 - } 1.2870 - } else { 1.2871 - runtimeNode = new RuntimeNode( 1.2872 - source, 1.2873 - token, 1.2874 - finish, 1.2875 - TYPEOF, 1.2876 - convert( 1.2877 - rhs, 1.2878 - Type.OBJECT), 1.2879 - newNullLiteral(unaryNode)); 1.2880 - } 1.2881 - 1.2882 - runtimeNode.accept(this); 1.2883 - 1.2884 - getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode); 1.2885 - runtimeNode.setSymbol(unaryNode.getSymbol()); 1.2886 - 1.2887 - return runtimeNode; 1.2888 - } 1.2889 - 1.2890 - @Override 1.2891 - public Node leaveSUB(final UnaryNode unaryNode) { 1.2892 - return leaveUnary(unaryNode, Type.NUMBER); 1.2893 - } 1.2894 - 1.2895 - @Override 1.2896 - public Node leaveVOID(final UnaryNode unaryNode) { 1.2897 - final Node rhs = unaryNode.rhs(); 1.2898 - getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode); 1.2899 - 1.2900 - // Set up request. 1.2901 - final RuntimeNode runtimeNode = new RuntimeNode(source, unaryNode.getToken(), unaryNode.getFinish(), VOID, convert(rhs, Type.OBJECT)); 1.2902 - 1.2903 - // Use same symbol as unary node. 1.2904 - runtimeNode.setSymbol(unaryNode.getSymbol()); 1.2905 - 1.2906 - return runtimeNode; 1.2907 - } 1.2908 - 1.2909 - /** 1.2910 - * Compute the narrowest possible type for a binaryNode, based on its 1.2911 - * contents. 1.2912 - * 1.2913 - * @param binaryNode the binary node 1.2914 - * @return the narrowest possible type 1.2915 - */ 1.2916 - private static Type binaryType(final BinaryNode binaryNode) { 1.2917 - final Node lhs = binaryNode.lhs(); 1.2918 - assert lhs.hasType(); 1.2919 - 1.2920 - final Node rhs = binaryNode.rhs(); 1.2921 - assert rhs.hasType(); 1.2922 - 1.2923 - // actually bitwise assignments are ok for ints. TODO 1.2924 - switch (binaryNode.tokenType()) { 1.2925 - case ASSIGN_BIT_AND: 1.2926 - case ASSIGN_BIT_OR: 1.2927 - case ASSIGN_BIT_XOR: 1.2928 - case ASSIGN_SHL: 1.2929 - case ASSIGN_SAR: 1.2930 - return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.INT) : Type.INT; 1.2931 - case ASSIGN_SHR: 1.2932 - return Type.LONG; 1.2933 - case ASSIGN: 1.2934 - return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.NUMBER) : Type.NUMBER; 1.2935 - default: 1.2936 - return binaryArithType(lhs.getType(), rhs.getType()); 1.2937 - } 1.2938 - } 1.2939 - 1.2940 - private static Type binaryArithType(final Type lhsType, final Type rhsType) { 1.2941 - if (!Compiler.shouldUseIntegerArithmetic()) { 1.2942 - return Type.NUMBER; 1.2943 - } 1.2944 - return Type.widest(lhsType, rhsType, Type.NUMBER); 1.2945 - } 1.2946 - 1.2947 - /** 1.2948 - * Visit binary node and set symbols and inferred type 1.2949 - * 1.2950 - * @param binaryNode unary node to visit 1.2951 - * @param type The narrowest allowed type for this node 1.2952 - * 1.2953 - * @return the final node, or replacement thereof 1.2954 - */ 1.2955 - private Node leaveBinary(final BinaryNode binaryNode, final Type type) { 1.2956 - return leaveBinary(binaryNode, type, type, type); 1.2957 - } 1.2958 - 1.2959 - /** 1.2960 - * Visit a binary node, specifying types for rhs, rhs and destType. 1.2961 - * 1.2962 - * @param binaryNode the binary node 1.2963 - * @param destType destination type 1.2964 - * @param lhsType type for left hand side 1.2965 - * @param rhsType type for right hand side 1.2966 - * 1.2967 - * @return resulting binary node 1.2968 - */ 1.2969 - private Node leaveBinary(final BinaryNode binaryNode, final Type destType, final Type lhsType, final Type rhsType) { 1.2970 - /* Attempt to turn this binary expression into a constant */ 1.2971 - final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); 1.2972 - if (literalNode != null) { 1.2973 - return newLiteral(literalNode); 1.2974 - } 1.2975 - 1.2976 - if (lhsType != null) { 1.2977 - binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)); 1.2978 - } 1.2979 - 1.2980 - if (rhsType != null) { 1.2981 - binaryNode.setRHS(convert(binaryNode.rhs(), rhsType)); 1.2982 - } 1.2983 - 1.2984 - getCurrentFunctionNode().newTemporary(destType, binaryNode); 1.2985 - 1.2986 - return binaryNode; 1.2987 - } 1.2988 - 1.2989 - /** 1.2990 - * Determine if the outcome of + operator is a string. 1.2991 - * 1.2992 - * @param node 1.2993 - * Node to test. 1.2994 - * @return true if a string result. 1.2995 - */ 1.2996 - private boolean isAddString(final Node node) { 1.2997 - if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) { 1.2998 - final BinaryNode binaryNode = (BinaryNode)node; 1.2999 - final Node lhs = binaryNode.lhs(); 1.3000 - final Node rhs = binaryNode.rhs(); 1.3001 - 1.3002 - return isAddString(lhs) || isAddString(rhs); 1.3003 - } 1.3004 - 1.3005 - return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).getObject() instanceof String; 1.3006 - } 1.3007 - 1.3008 - /** 1.3009 - * Helper for creating a new runtime node from a parent node, inheriting its 1.3010 - * types and tokens 1.3011 - * 1.3012 - * @param parent Parent node. 1.3013 - * @param args Runtime request arguments. 1.3014 - * @param request Runtime request type. 1.3015 - * @return New {@link RuntimeNode}. 1.3016 - */ 1.3017 - 1.3018 - private RuntimeNode newRuntime(final Node parent, final List<Node> args, final Request request) { 1.3019 - final RuntimeNode runtimeNode = new RuntimeNode(source, parent.getToken(), parent.getFinish(), request, args); 1.3020 - runtimeNode.setStart(parent.getStart()); 1.3021 - runtimeNode.setFinish(parent.getFinish()); 1.3022 - 1.3023 - // Use same symbol as parent node. 1.3024 - runtimeNode.accept(this); 1.3025 - runtimeNode.setSymbol(parent.getSymbol()); 1.3026 - 1.3027 - return runtimeNode; 1.3028 - } 1.3029 - 1.3030 - /** 1.3031 - * Helper for creating a new runtime node from a binary node 1.3032 - * 1.3033 - * @param binaryNode {@link RuntimeNode} expression. 1.3034 - * @param request Runtime request type. 1.3035 - * @return New {@link RuntimeNode}. 1.3036 - */ 1.3037 - private RuntimeNode newRuntime(final BinaryNode binaryNode, final Request request) { 1.3038 - return newRuntime(binaryNode, Arrays.asList(new Node[] { binaryNode.lhs(), binaryNode.rhs() }), request); 1.3039 - } 1.3040 - 1.3041 - /** 1.3042 - * Add is a special binary, as it works not only on arithmetic, but for 1.3043 - * strings etc as well. 1.3044 - */ 1.3045 - @Override 1.3046 - public Node leaveADD(final BinaryNode binaryNode) { 1.3047 - final Node lhs = binaryNode.lhs(); 1.3048 - final Node rhs = binaryNode.rhs(); 1.3049 - 1.3050 - //parameters must be blown up to objects 1.3051 - ensureTypeNotUnknown(lhs); 1.3052 - ensureTypeNotUnknown(rhs); 1.3053 - 1.3054 - if ((lhs.getType().isNumeric() || lhs.getType().isBoolean()) && (rhs.getType().isNumeric() || rhs.getType().isBoolean())) { 1.3055 - return leaveBinary(binaryNode, binaryType(binaryNode)); 1.3056 - } else if (isAddString(binaryNode)) { 1.3057 - binaryNode.setLHS(convert(lhs, Type.OBJECT)); 1.3058 - binaryNode.setRHS(convert(rhs, Type.OBJECT)); 1.3059 - getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode); 1.3060 - } else { 1.3061 - getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode); 1.3062 - return newRuntime(binaryNode, ADD); 1.3063 - } 1.3064 - 1.3065 - return binaryNode; 1.3066 - } 1.3067 - 1.3068 - @Override 1.3069 - public Node leaveAND(final BinaryNode binaryNode) { 1.3070 - return leaveBinary(binaryNode, Type.OBJECT, null, null); 1.3071 - } 1.3072 - 1.3073 - /** 1.3074 - * This is a helper called before an assignment. 1.3075 - * @param binaryNode assignment node 1.3076 - */ 1.3077 - private Node enterAssign(final BinaryNode binaryNode) { 1.3078 - final Node lhs = binaryNode.lhs(); 1.3079 - 1.3080 - if (!(lhs instanceof IdentNode)) { 1.3081 - return binaryNode; 1.3082 - } 1.3083 - 1.3084 - final Block block = getCurrentBlock(); 1.3085 - final IdentNode ident = (IdentNode)lhs; 1.3086 - final String name = ident.getName(); 1.3087 - 1.3088 - Symbol symbol = getCurrentBlock().findSymbol(name); 1.3089 - 1.3090 - if (symbol == null) { 1.3091 - symbol = block.defineSymbol(name, IS_GLOBAL, ident); 1.3092 - binaryNode.setSymbol(symbol); 1.3093 - } else if (!getCurrentFunctionNode().isLocal(symbol)) { 1.3094 - symbol.setIsScope(); 1.3095 - } 1.3096 - 1.3097 - localDefs.add(name); 1.3098 - 1.3099 - return binaryNode; 1.3100 - } 1.3101 - 1.3102 - /** 1.3103 - * This assign helper is called after an assignment, when all children of 1.3104 - * the assign has been processed. It fixes the types and recursively makes 1.3105 - * sure that everyhing has slots that should have them in the chain. 1.3106 - * 1.3107 - * @param binaryNode assignment node 1.3108 - * @param destType destination type of assignment 1.3109 - */ 1.3110 - private Node leaveAssign(final BinaryNode binaryNode, final Type destType) { 1.3111 - binaryNode.lhs().getSymbol().setType(destType); // lhs inherits dest type 1.3112 - getCurrentFunctionNode().newTemporary(destType, binaryNode); // as does destination 1.3113 - 1.3114 - ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode.lhs()); 1.3115 - return binaryNode; 1.3116 - } 1.3117 - 1.3118 - @Override 1.3119 - public Node enterASSIGN(final BinaryNode binaryNode) { 1.3120 - return enterAssign(binaryNode); 1.3121 - } 1.3122 - 1.3123 - @Override 1.3124 - public Node leaveASSIGN(final BinaryNode binaryNode) { 1.3125 - final Node lhs = binaryNode.lhs(); 1.3126 - final Node rhs = binaryNode.rhs(); 1.3127 - 1.3128 - if (rhs.getType().isNumeric()) { 1.3129 - final Symbol lhsSymbol = lhs.getSymbol(); 1.3130 - final Type lhsType = Type.widest(lhs.getType(), binaryType(binaryNode)); 1.3131 - 1.3132 - // for index nodes, we can set anything 1.3133 - lhsSymbol.setType(lhsType); 1.3134 - getCurrentFunctionNode().newTemporary(lhs.getType(), binaryNode); 1.3135 - if (!(lhs instanceof IndexNode)) { 1.3136 - binaryNode.setRHS(convert(rhs, lhsType)); 1.3137 - } 1.3138 - } else { 1.3139 - // Force symbol to be object if not numeric assignment. 1.3140 - binaryNode.setRHS(convert(rhs, Type.OBJECT)); 1.3141 - getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode); 1.3142 - 1.3143 - if (lhs instanceof IdentNode) { 1.3144 - lhs.getSymbol().setType(Type.OBJECT); 1.3145 - } 1.3146 - } 1.3147 - 1.3148 - if (lhs instanceof AccessNode) { 1.3149 - final Node baseNode = ((AccessNode)lhs).getBase(); 1.3150 - 1.3151 - if (baseNode.getSymbol().isThis()) { 1.3152 - final IdentNode property = ((AccessNode)lhs).getProperty(); 1.3153 - getCurrentFunctionNode().addThisProperty(property.getName(), property); 1.3154 - } 1.3155 - } 1.3156 - 1.3157 - return binaryNode; 1.3158 - } 1.3159 - 1.3160 - @Override 1.3161 - public Node enterASSIGN_ADD(final BinaryNode binaryNode) { 1.3162 - return enterAssign(binaryNode); 1.3163 - } 1.3164 - 1.3165 - @Override 1.3166 - public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { 1.3167 - final Node lhs = binaryNode.lhs(); 1.3168 - final Node rhs = binaryNode.rhs(); 1.3169 - final boolean bothNumeric = lhs.getType().isNumeric() && rhs.getType().isNumeric(); 1.3170 - 1.3171 - /* 1.3172 - * In the case of bothNumeric, 1.3173 - * compute type for lhs += rhs. Assign type to lhs. Assign type to 1.3174 - * temporary for this node. Convert rhs from whatever it was to this 1.3175 - * type. Legacy wise dest has always been narrowed. It should 1.3176 - * actually only be the lhs that is the double, or other narrower 1.3177 - * type than OBJECT 1.3178 - */ 1.3179 - return leaveAssign(binaryNode, bothNumeric ? binaryType(binaryNode) : Type.OBJECT); 1.3180 - } 1.3181 - 1.3182 - @Override 1.3183 - public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) { 1.3184 - return enterAssign(binaryNode); 1.3185 - } 1.3186 - 1.3187 - @Override 1.3188 - public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { 1.3189 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3190 - } 1.3191 - 1.3192 - @Override 1.3193 - public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) { 1.3194 - return enterAssign(binaryNode); 1.3195 - } 1.3196 - 1.3197 - @Override 1.3198 - public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { 1.3199 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3200 - } 1.3201 - 1.3202 - @Override 1.3203 - public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { 1.3204 - return enterAssign(binaryNode); 1.3205 - } 1.3206 - 1.3207 - @Override 1.3208 - public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { 1.3209 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3210 - } 1.3211 - 1.3212 - @Override 1.3213 - public Node enterASSIGN_DIV(final BinaryNode binaryNode) { 1.3214 - return enterAssign(binaryNode); 1.3215 - } 1.3216 - 1.3217 - @Override 1.3218 - public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { 1.3219 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3220 - } 1.3221 - 1.3222 - @Override 1.3223 - public Node enterASSIGN_MOD(final BinaryNode binaryNode) { 1.3224 - return enterAssign(binaryNode); 1.3225 - } 1.3226 - 1.3227 - @Override 1.3228 - public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { 1.3229 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3230 - } 1.3231 - 1.3232 - @Override 1.3233 - public Node enterASSIGN_MUL(final BinaryNode binaryNode) { 1.3234 - return enterAssign(binaryNode); 1.3235 - } 1.3236 - 1.3237 - @Override 1.3238 - public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { 1.3239 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3240 - } 1.3241 - 1.3242 - @Override 1.3243 - public Node enterASSIGN_SAR(final BinaryNode binaryNode) { 1.3244 - return enterAssign(binaryNode); 1.3245 - } 1.3246 - 1.3247 - @Override 1.3248 - public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { 1.3249 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3250 - } 1.3251 - 1.3252 - @Override 1.3253 - public Node enterASSIGN_SHL(final BinaryNode binaryNode) { 1.3254 - return enterAssign(binaryNode); 1.3255 - } 1.3256 - 1.3257 - @Override 1.3258 - public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { 1.3259 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3260 - } 1.3261 - 1.3262 - @Override 1.3263 - public Node enterASSIGN_SHR(final BinaryNode binaryNode) { 1.3264 - return enterAssign(binaryNode); 1.3265 - } 1.3266 - 1.3267 - @Override 1.3268 - public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { 1.3269 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3270 - } 1.3271 - 1.3272 - @Override 1.3273 - public Node enterASSIGN_SUB(final BinaryNode binaryNode) { 1.3274 - return enterAssign(binaryNode); 1.3275 - } 1.3276 - 1.3277 - @Override 1.3278 - public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { 1.3279 - return leaveAssign(binaryNode, binaryType(binaryNode)); 1.3280 - } 1.3281 - 1.3282 - @Override 1.3283 - public Node leaveBIT_AND(final BinaryNode binaryNode) { 1.3284 - return leaveBinary(binaryNode, Type.INT); 1.3285 - } 1.3286 - 1.3287 - @Override 1.3288 - public Node leaveBIT_OR(final BinaryNode binaryNode) { 1.3289 - return leaveBinary(binaryNode, Type.INT); 1.3290 - } 1.3291 - 1.3292 - @Override 1.3293 - public Node leaveBIT_XOR(final BinaryNode binaryNode) { 1.3294 - return leaveBinary(binaryNode, Type.INT); 1.3295 - } 1.3296 - 1.3297 - @Override 1.3298 - public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { 1.3299 - binaryNode.setLHS(discard(binaryNode.lhs())); 1.3300 - getCurrentFunctionNode().newTemporary(binaryNode.rhs().getType(), binaryNode); 1.3301 - 1.3302 - return binaryNode; 1.3303 - } 1.3304 - 1.3305 - @Override 1.3306 - public Node leaveCOMMALEFT(final BinaryNode binaryNode) { 1.3307 - binaryNode.setRHS(discard(binaryNode.rhs())); 1.3308 - getCurrentFunctionNode().newTemporary(binaryNode.lhs().getType(), binaryNode); 1.3309 - 1.3310 - return binaryNode; 1.3311 - } 1.3312 - 1.3313 - @Override 1.3314 - public Node leaveDIV(final BinaryNode binaryNode) { 1.3315 - return leaveBinary(binaryNode, binaryType(binaryNode)); 1.3316 - } 1.3317 - 1.3318 - @Override 1.3319 - public Node leaveEQ(final BinaryNode binaryNode) { 1.3320 - return leaveCmp(binaryNode, EQ); 1.3321 - } 1.3322 - 1.3323 - @Override 1.3324 - public Node leaveEQ_STRICT(final BinaryNode binaryNode) { 1.3325 - return leaveCmp(binaryNode, EQ_STRICT); 1.3326 - } 1.3327 - 1.3328 - private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { 1.3329 - final Node lhs = binaryNode.lhs(); 1.3330 - final Node rhs = binaryNode.rhs(); 1.3331 - 1.3332 - /* Attempt to turn this comparison into a constant and collapse it */ 1.3333 - final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); 1.3334 - if (literalNode != null) { 1.3335 - return newLiteral(literalNode); 1.3336 - } 1.3337 - 1.3338 - // another case where dest != source operand types always a boolean 1.3339 - getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode); 1.3340 - 1.3341 - ensureTypeNotUnknown(lhs); 1.3342 - ensureTypeNotUnknown(rhs); 1.3343 - final Type type = Type.widest(lhs.getType(), rhs.getType()); 1.3344 - 1.3345 - if (type.isObject()) { 1.3346 - return newRuntime(binaryNode, request); 1.3347 - } 1.3348 - 1.3349 - if ((request.equals(EQ_STRICT) || request.equals(NE_STRICT)) && lhs.getType().isBoolean() != rhs.getType().isBoolean()) { 1.3350 - // special case: number compared against boolean => never equal. must not convert! 1.3351 - final boolean result = request.equals(NE_STRICT); 1.3352 - final LiteralNode<Boolean> resultNode = LiteralNode.newInstance(source, 0, 0, result); 1.3353 - final boolean canSkipLHS = (lhs instanceof LiteralNode); 1.3354 - final boolean canSkipRHS = (rhs instanceof LiteralNode); 1.3355 - 1.3356 - if (canSkipLHS && canSkipRHS) { 1.3357 - return resultNode.accept(this); 1.3358 - } 1.3359 - 1.3360 - final Node argNode; 1.3361 - 1.3362 - if (!canSkipLHS && !canSkipRHS) { 1.3363 - argNode = new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), lhs, rhs); 1.3364 - } else { 1.3365 - argNode = !canSkipLHS ? lhs : rhs; 1.3366 - } 1.3367 - 1.3368 - return new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), argNode, resultNode).accept(this); 1.3369 - } 1.3370 - 1.3371 - binaryNode.setLHS(convert(lhs, type)); 1.3372 - binaryNode.setRHS(convert(rhs, type)); 1.3373 - 1.3374 - return binaryNode; 1.3375 - } 1.3376 - 1.3377 - @Override 1.3378 - public Node leaveGE(final BinaryNode binaryNode) { 1.3379 - return leaveCmp(binaryNode, GE); 1.3380 - } 1.3381 - 1.3382 - @Override 1.3383 - public Node leaveGT(final BinaryNode binaryNode) { 1.3384 - return leaveCmp(binaryNode, GT); 1.3385 - } 1.3386 - 1.3387 - private Node exitIN_INSTANCEOF(final BinaryNode binaryNode, final Request request) { 1.3388 - getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode); 1.3389 - return newRuntime(binaryNode, request); 1.3390 - } 1.3391 - 1.3392 - @Override 1.3393 - public Node leaveIN(final BinaryNode binaryNode) { 1.3394 - return exitIN_INSTANCEOF(binaryNode, IN); 1.3395 - } 1.3396 - 1.3397 - @Override 1.3398 - public Node leaveINSTANCEOF(final BinaryNode binaryNode) { 1.3399 - return exitIN_INSTANCEOF(binaryNode, INSTANCEOF); 1.3400 - } 1.3401 - 1.3402 - @Override 1.3403 - public Node leaveLE(final BinaryNode binaryNode) { 1.3404 - return leaveCmp(binaryNode, LE); 1.3405 - } 1.3406 - 1.3407 - @Override 1.3408 - public Node leaveLT(final BinaryNode binaryNode) { 1.3409 - return leaveCmp(binaryNode, LT); 1.3410 - } 1.3411 - 1.3412 - @Override 1.3413 - public Node leaveMOD(final BinaryNode binaryNode) { 1.3414 - return leaveBinary(binaryNode, binaryType(binaryNode)); 1.3415 - } 1.3416 - 1.3417 - @Override 1.3418 - public Node leaveMUL(final BinaryNode binaryNode) { 1.3419 - return leaveBinary(binaryNode, binaryType(binaryNode)); 1.3420 - } 1.3421 - 1.3422 - @Override 1.3423 - public Node leaveNE(final BinaryNode binaryNode) { 1.3424 - return leaveCmp(binaryNode, NE); 1.3425 - } 1.3426 - 1.3427 - @Override 1.3428 - public Node leaveNE_STRICT(final BinaryNode binaryNode) { 1.3429 - return leaveCmp(binaryNode, NE_STRICT); 1.3430 - } 1.3431 - 1.3432 - @Override 1.3433 - public Node leaveOR(final BinaryNode binaryNode) { 1.3434 - return leaveBinary(binaryNode, Type.OBJECT, null, null); 1.3435 - } 1.3436 - 1.3437 - @Override 1.3438 - public Node leaveSAR(final BinaryNode binaryNode) { 1.3439 - return leaveBinary(binaryNode, Type.INT); 1.3440 - } 1.3441 - 1.3442 - @Override 1.3443 - public Node leaveSHL(final BinaryNode binaryNode) { 1.3444 - return leaveBinary(binaryNode, Type.INT); 1.3445 - } 1.3446 - 1.3447 - @Override 1.3448 - public Node leaveSHR(final BinaryNode binaryNode) { 1.3449 - return leaveBinary(binaryNode, Type.LONG, Type.INT, Type.INT); 1.3450 - } 1.3451 - 1.3452 - @Override 1.3453 - public Node leaveSUB(final BinaryNode binaryNode) { 1.3454 - return leaveBinary(binaryNode, binaryType(binaryNode)); 1.3455 - } 1.3456 - 1.3457 - @Override 1.3458 - public Node leave(final TernaryNode ternaryNode) { 1.3459 - final Node test = ternaryNode.lhs(); 1.3460 - final Node lhs = ternaryNode.rhs(); 1.3461 - final Node rhs = ternaryNode.third(); 1.3462 - 1.3463 - ensureTypeNotUnknown(lhs); 1.3464 - ensureTypeNotUnknown(rhs); 1.3465 - final Type type = Type.widest(lhs.getType(), rhs.getType()); 1.3466 - 1.3467 - ternaryNode.setRHS(convert(lhs, type)); 1.3468 - ternaryNode.setThird(convert(rhs, type)); 1.3469 - 1.3470 - // optimize away the ternary if the test is constant. 1.3471 - if (test.getSymbol().isConstant()) { 1.3472 - return ((LiteralNode<?>)test).isTrue() ? lhs : rhs; 1.3473 - } 1.3474 - 1.3475 - ternaryNode.setLHS(convert(test, Type.BOOLEAN)); 1.3476 - 1.3477 - getCurrentFunctionNode().newTemporary(type, ternaryNode); 1.3478 - 1.3479 - return ternaryNode; 1.3480 - } 1.3481 - 1.3482 - private static void ensureTypeNotUnknown(final Node node) { 1.3483 - final Symbol symbol = node.getSymbol(); 1.3484 - 1.3485 - /* 1.3486 - * Note that not just unknowns, but params need to be blown 1.3487 - * up to objects, because we can have something like 1.3488 - * 1.3489 - * function f(a) { 1.3490 - * var b = ~a; //b and a are inferred to be int 1.3491 - * return b; 1.3492 - * } 1.3493 - * 1.3494 - * In this case, it would be correct to say that "if you have 1.3495 - * an int at the callsite, just pass it". 1.3496 - * 1.3497 - * However 1.3498 - * 1.3499 - * function f(a) { 1.3500 - * var b = ~a; //b and a are inferred to be int 1.3501 - * return b == 17; //b is still inferred to be int. 1.3502 - * } 1.3503 - * 1.3504 - * can be called with f("17") and if we assume that b is an 1.3505 - * int and don't blow it up to an object in the comparison, we 1.3506 - * are screwed. I hate JavaScript. 1.3507 - * 1.3508 - * This check has to be done for any operation that might take 1.3509 - * objects as parameters, for example +, but not *, which is known 1.3510 - * to coerce types into doubles 1.3511 - */ 1.3512 - if (node.getType().isUnknown() || symbol.isParam()) { 1.3513 - symbol.setType(Type.OBJECT); 1.3514 - symbol.setCanBeUndefined(); 1.3515 - } 1.3516 - } 1.3517 - 1.3518 - /** 1.3519 - * A simple node visitor that ensure that scope and slot information is correct. 1.3520 - * This is run as a post pass after we know all scope changing information about 1.3521 - * the Lowering. This is also called after potential mutations like splitting 1.3522 - * have taken place, as splitting forces scope. 1.3523 - * 1.3524 - * This was previously done on a per functionNode basis in {@link CodeGenerator}, 1.3525 - * but this is too late for type information to be used in {@link AccessSpecializer} 1.3526 - */ 1.3527 - static class FinalizeSymbols extends NodeVisitor { 1.3528 - @Override 1.3529 - public Node leave(final Block block) { 1.3530 - return updateSymbols(block); 1.3531 - } 1.3532 - 1.3533 - @Override 1.3534 - public Node leave(final FunctionNode function) { 1.3535 - return updateSymbols(function); 1.3536 - } 1.3537 - 1.3538 - private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { 1.3539 - if (!symbol.isScope()) { 1.3540 - LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope"); 1.3541 - } 1.3542 - if (loseSlot && symbol.hasSlot()) { 1.3543 - LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope"); 1.3544 - } 1.3545 - } 1.3546 - 1.3547 - // called after a block or function node (subclass of block) is finished 1.3548 - // to correct scope and slot assignment for variables 1.3549 - private static Block updateSymbols(final Block block) { 1.3550 - 1.3551 - if (!block.needsScope()) { 1.3552 - return block; // nothing to do 1.3553 - } 1.3554 - 1.3555 - assert !(block instanceof FunctionNode) || block.getFunction() == block; 1.3556 - 1.3557 - final FunctionNode functionNode = block.getFunction(); 1.3558 - final List<Symbol> symbols = block.getFrame().getSymbols(); 1.3559 - final boolean allVarsInScope = functionNode.varsInScope(); 1.3560 - final boolean isVarArg = functionNode.isVarArg(); 1.3561 - 1.3562 - for (final Symbol symbol : symbols) { 1.3563 - if (symbol.isInternal() || symbol.isThis()) { 1.3564 - continue; 1.3565 - } 1.3566 - 1.3567 - if (symbol.isVar()) { 1.3568 - if (allVarsInScope || symbol.isScope()) { 1.3569 - updateSymbolsLog(functionNode, symbol, true); 1.3570 - symbol.setIsScope(); 1.3571 - symbol.setNeedsSlot(false); 1.3572 - } else { 1.3573 - assert symbol.hasSlot() : symbol + " should have a slot only, no scope"; 1.3574 - } 1.3575 - } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) { 1.3576 - updateSymbolsLog(functionNode, symbol, isVarArg); 1.3577 - symbol.setIsScope(); 1.3578 - symbol.setNeedsSlot(!isVarArg); 1.3579 - } 1.3580 - } 1.3581 - 1.3582 - return block; 1.3583 - } 1.3584 - } 1.3585 - 1.3586 - /** 1.3587 - * Helper class to evaluate constant expressions at compile time This is 1.3588 - * also a simplifier used by BinaryNode visits, UnaryNode visits and 1.3589 - * conversions. 1.3590 - */ 1.3591 - private abstract static class ConstantEvaluator<T extends Node> { 1.3592 - protected T parent; 1.3593 - protected final Source source; 1.3594 - protected final long token; 1.3595 - protected final int finish; 1.3596 - 1.3597 - protected ConstantEvaluator(final T parent) { 1.3598 - this.parent = parent; 1.3599 - this.source = parent.getSource(); 1.3600 - this.token = parent.getToken(); 1.3601 - this.finish = parent.getFinish(); 1.3602 - } 1.3603 - 1.3604 - /** 1.3605 - * Returns a literal node that replaces the given parent node, or null if replacement 1.3606 - * is impossible 1.3607 - * @return the literal node 1.3608 - */ 1.3609 - protected abstract LiteralNode<?> eval(); 1.3610 - } 1.3611 - 1.3612 - static class LiteralNodeConstantEvaluator extends ConstantEvaluator<LiteralNode<?>> { 1.3613 - private final Type type; 1.3614 - 1.3615 - LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) { 1.3616 - super(parent); 1.3617 - this.type = type; 1.3618 - } 1.3619 - 1.3620 - @Override 1.3621 - protected LiteralNode<?> eval() { 1.3622 - final Object value = ((LiteralNode<?>)parent).getValue(); 1.3623 - 1.3624 - LiteralNode<?> literalNode = null; 1.3625 - 1.3626 - if (type.isString()) { 1.3627 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value)); 1.3628 - } else if (type.isBoolean()) { 1.3629 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value)); 1.3630 - } else if (type.isInteger()) { 1.3631 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); 1.3632 - } else if (type.isLong()) { 1.3633 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); 1.3634 - } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) { 1.3635 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value)); 1.3636 - } 1.3637 - 1.3638 - return literalNode; 1.3639 - } 1.3640 - } 1.3641 - 1.3642 - private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> { 1.3643 - UnaryNodeConstantEvaluator(final UnaryNode parent) { 1.3644 - super(parent); 1.3645 - } 1.3646 - 1.3647 - @Override 1.3648 - protected LiteralNode<?> eval() { 1.3649 - final Node rhsNode = parent.rhs(); 1.3650 - 1.3651 - if (!rhsNode.getSymbol().isConstant()) { 1.3652 - return null; 1.3653 - } 1.3654 - 1.3655 - final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode; 1.3656 - final boolean rhsInteger = rhs.getType().isInteger(); 1.3657 - 1.3658 - LiteralNode<?> literalNode; 1.3659 - 1.3660 - switch (parent.tokenType()) { 1.3661 - case ADD: 1.3662 - if (rhsInteger) { 1.3663 - literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32()); 1.3664 - } else { 1.3665 - literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber()); 1.3666 - } 1.3667 - break; 1.3668 - case SUB: 1.3669 - if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js 1.3670 - literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32()); 1.3671 - } else { 1.3672 - literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber()); 1.3673 - } 1.3674 - break; 1.3675 - case NOT: 1.3676 - literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean()); 1.3677 - break; 1.3678 - case BIT_NOT: 1.3679 - literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32()); 1.3680 - break; 1.3681 - default: 1.3682 - return null; 1.3683 - } 1.3684 - 1.3685 - return literalNode; 1.3686 - } 1.3687 - } 1.3688 - 1.3689 - private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> { 1.3690 - BinaryNodeConstantEvaluator(final BinaryNode parent) { 1.3691 - super(parent); 1.3692 - } 1.3693 - 1.3694 - @Override 1.3695 - protected LiteralNode<?> eval() { 1.3696 - 1.3697 - if (!parent.lhs().getSymbol().isConstant() || !parent.rhs().getSymbol().isConstant()) { 1.3698 - return null; 1.3699 - } 1.3700 - 1.3701 - final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs(); 1.3702 - final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs(); 1.3703 - 1.3704 - final Type widest = Type.widest(lhs.getType(), rhs.getType()); 1.3705 - 1.3706 - boolean isInteger = widest.isInteger(); 1.3707 - boolean isLong = widest.isLong(); 1.3708 - 1.3709 - double value; 1.3710 - 1.3711 - switch (parent.tokenType()) { 1.3712 - case AND: 1.3713 - return JSType.toBoolean(lhs.getObject()) ? rhs : lhs; 1.3714 - case OR: 1.3715 - return JSType.toBoolean(lhs.getObject()) ? lhs : rhs; 1.3716 - case DIV: 1.3717 - value = lhs.getNumber() / rhs.getNumber(); 1.3718 - break; 1.3719 - case ADD: 1.3720 - value = lhs.getNumber() + rhs.getNumber(); 1.3721 - break; 1.3722 - case MUL: 1.3723 - value = lhs.getNumber() * rhs.getNumber(); 1.3724 - break; 1.3725 - case MOD: 1.3726 - value = lhs.getNumber() % rhs.getNumber(); 1.3727 - break; 1.3728 - case SUB: 1.3729 - value = lhs.getNumber() - rhs.getNumber(); 1.3730 - break; 1.3731 - case SHR: 1.3732 - return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL); 1.3733 - case SAR: 1.3734 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32()); 1.3735 - case SHL: 1.3736 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32()); 1.3737 - case BIT_XOR: 1.3738 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32()); 1.3739 - case BIT_AND: 1.3740 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32()); 1.3741 - case BIT_OR: 1.3742 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32()); 1.3743 - case GE: 1.3744 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject())); 1.3745 - case LE: 1.3746 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject())); 1.3747 - case GT: 1.3748 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject())); 1.3749 - case LT: 1.3750 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject())); 1.3751 - case NE: 1.3752 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject())); 1.3753 - case NE_STRICT: 1.3754 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject())); 1.3755 - case EQ: 1.3756 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject())); 1.3757 - case EQ_STRICT: 1.3758 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject())); 1.3759 - default: 1.3760 - return null; 1.3761 - } 1.3762 - 1.3763 - isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value); 1.3764 - isLong &= value != 0.0 && JSType.isRepresentableAsLong(value); 1.3765 - 1.3766 - if (isInteger) { 1.3767 - return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); 1.3768 - } else if (isLong) { 1.3769 - return LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); 1.3770 - } 1.3771 - 1.3772 - return LiteralNode.newInstance(source, token, finish, value); 1.3773 - } 1.3774 - } 1.3775 -}