1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.codegen; 27 28 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; 29 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR; 30 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 31 import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; 32 import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; 33 import static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX; 34 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; 35 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 36 import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX; 37 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 38 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 39 import static jdk.nashorn.internal.ir.Symbol.IS_ALWAYS_DEFINED; 40 import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT; 41 import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF; 42 import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; 43 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 44 import static jdk.nashorn.internal.ir.Symbol.IS_LET; 45 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; 46 import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE; 47 import static jdk.nashorn.internal.ir.Symbol.IS_THIS; 48 import static jdk.nashorn.internal.ir.Symbol.IS_VAR; 49 import static jdk.nashorn.internal.ir.Symbol.KINDMASK; 50 51 import java.util.ArrayDeque; 52 import java.util.ArrayList; 53 import java.util.Deque; 54 import java.util.HashSet; 55 import java.util.Iterator; 56 import java.util.List; 57 import java.util.Set; 58 import jdk.nashorn.internal.codegen.types.Type; 59 import jdk.nashorn.internal.ir.AccessNode; 60 import jdk.nashorn.internal.ir.BinaryNode; 61 import jdk.nashorn.internal.ir.Block; 62 import jdk.nashorn.internal.ir.CallNode; 63 import jdk.nashorn.internal.ir.CaseNode; 64 import jdk.nashorn.internal.ir.CatchNode; 65 import jdk.nashorn.internal.ir.Expression; 66 import jdk.nashorn.internal.ir.ForNode; 67 import jdk.nashorn.internal.ir.FunctionNode; 68 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 69 import jdk.nashorn.internal.ir.IdentNode; 70 import jdk.nashorn.internal.ir.IndexNode; 71 import jdk.nashorn.internal.ir.LexicalContext; 72 import jdk.nashorn.internal.ir.LexicalContextNode; 73 import jdk.nashorn.internal.ir.LiteralNode; 74 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 75 import jdk.nashorn.internal.ir.Node; 76 import jdk.nashorn.internal.ir.ObjectNode; 77 import jdk.nashorn.internal.ir.ReturnNode; 78 import jdk.nashorn.internal.ir.RuntimeNode; 79 import jdk.nashorn.internal.ir.RuntimeNode.Request; 80 import jdk.nashorn.internal.ir.Statement; 81 import jdk.nashorn.internal.ir.SwitchNode; 82 import jdk.nashorn.internal.ir.Symbol; 83 import jdk.nashorn.internal.ir.TemporarySymbols; 84 import jdk.nashorn.internal.ir.TernaryNode; 85 import jdk.nashorn.internal.ir.TryNode; 86 import jdk.nashorn.internal.ir.UnaryNode; 87 import jdk.nashorn.internal.ir.VarNode; 88 import jdk.nashorn.internal.ir.WithNode; 89 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 90 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 91 import jdk.nashorn.internal.parser.TokenType; 92 import jdk.nashorn.internal.runtime.Context; 93 import jdk.nashorn.internal.runtime.Debug; 94 import jdk.nashorn.internal.runtime.DebugLogger; 95 import jdk.nashorn.internal.runtime.JSType; 96 import jdk.nashorn.internal.runtime.Property; 97 import jdk.nashorn.internal.runtime.PropertyMap; 98 99 /** 100 * This is the attribution pass of the code generator. Attr takes Lowered IR, 101 * that is, IR where control flow has been computed and high level to low level 102 * substitions for operations have been performed. 103 * 104 * After Attr, every symbol will have a conservative correct type. 105 * 106 * Any expression that requires temporary storage as part of computation will 107 * also be detected here and give a temporary symbol 108 * 109 * Types can be narrowed after Attr by Access Specialization in FinalizeTypes, 110 * but in general, this is where the main symbol type information is 111 * computed. 112 */ 113 114 final class Attr extends NodeOperatorVisitor<LexicalContext> { 115 116 /** 117 * Local definitions in current block (to discriminate from function 118 * declarations always defined in the function scope. This is for 119 * "can be undefined" analysis. 120 */ 121 private final Deque<Set<String>> localDefs; 122 123 /** 124 * Local definitions in current block to guard against cases like 125 * NASHORN-467 when things can be undefined as they are used before 126 * their local var definition. *sigh* JavaScript... 127 */ 128 private final Deque<Set<String>> localUses; 129 130 private final Deque<Type> returnTypes; 131 132 private int catchNestingLevel; 133 134 private static final DebugLogger LOG = new DebugLogger("attr"); 135 private static final boolean DEBUG = LOG.isEnabled(); 136 137 private final TemporarySymbols temporarySymbols; 138 139 /** 140 * Constructor. 141 */ 142 Attr(final TemporarySymbols temporarySymbols) { 143 super(new LexicalContext()); 144 this.temporarySymbols = temporarySymbols; 145 this.localDefs = new ArrayDeque<>(); 146 this.localUses = new ArrayDeque<>(); 147 this.returnTypes = new ArrayDeque<>(); 148 } 149 150 @Override 151 protected boolean enterDefault(final Node node) { 152 return start(node); 153 } 154 155 @Override 156 protected Node leaveDefault(final Node node) { 157 return end(node); 158 } 159 160 @Override 161 public Node leaveAccessNode(final AccessNode accessNode) { 162 //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this, that 163 //is why we can't set the access node base to be an object here, that will ruin access specialization 164 //for example for a.x | 17. 165 return end(ensureSymbol(Type.OBJECT, accessNode)); 166 } 167 168 private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) { 169 initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL); 170 initCompileConstant(THIS, body, IS_PARAM | IS_THIS, Type.OBJECT); 171 172 if (functionNode.isVarArg()) { 173 initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL); 174 if (functionNode.needsArguments()) { 175 initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED); 176 final String argumentsName = ARGUMENTS_VAR.symbolName(); 177 newType(defineSymbol(body, argumentsName, IS_VAR | IS_ALWAYS_DEFINED), Type.typeFor(ARGUMENTS_VAR.type())); 178 addLocalDef(argumentsName); 179 } 180 } 181 182 initParameters(functionNode, body); 183 initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED); 184 initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED, Type.OBJECT); 185 } 186 187 188 /** 189 * This pushes all declarations (except for non-statements, i.e. for 190 * node temporaries) to the top of the function scope. This way we can 191 * get around problems like 192 * 193 * while (true) { 194 * break; 195 * if (true) { 196 * var s; 197 * } 198 * } 199 * 200 * to an arbitrary nesting depth. 201 * 202 * see NASHORN-73 203 * 204 * @param functionNode the FunctionNode we are entering 205 * @param body the body of the FunctionNode we are entering 206 */ 207 private void acceptDeclarations(final FunctionNode functionNode, final Block body) { 208 // This visitor will assign symbol to all declared variables, except function declarations (which are taken care 209 // in a separate step above) and "var" declarations in for loop initializers. 210 // 211 // It also handles the case that a variable can be undefined, e.g 212 // if (cond) { 213 // x = x.y; 214 // } 215 // var x = 17; 216 // 217 // by making sure that no identifier has been found earlier in the body than the 218 // declaration - if such is the case the identifier is flagged as caBeUndefined to 219 // be safe if it turns into a local var. Otherwise corrupt bytecode results 220 221 body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 222 private final Set<String> uses = new HashSet<>(); 223 private final Set<String> canBeUndefined = new HashSet<>(); 224 225 @Override 226 public boolean enterFunctionNode(final FunctionNode nestedFn) { 227 return false; 228 } 229 230 @Override 231 public Node leaveIdentNode(final IdentNode identNode) { 232 uses.add(identNode.getName()); 233 return identNode; 234 } 235 236 @Override 237 public boolean enterVarNode(final VarNode varNode) { 238 final String name = varNode.getName().getName(); 239 //if this is used before the var node, the var node symbol needs to be tagged as can be undefined 240 if (uses.contains(name)) { 241 canBeUndefined.add(name); 242 } 243 244 // all uses of the declared varnode inside the var node are potentially undefined 245 // however this is a bit conservative as e.g. var x = 17; var x = 1 + x; does work 246 if (!varNode.isFunctionDeclaration() && varNode.getInit() != null) { 247 varNode.getInit().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 248 @Override 249 public boolean enterIdentNode(final IdentNode identNode) { 250 if (name.equals(identNode.getName())) { 251 canBeUndefined.add(name); 252 } 253 return false; 254 } 255 }); 256 } 257 258 return true; 259 } 260 261 @Override 262 public Node leaveVarNode(final VarNode varNode) { 263 // any declared symbols that aren't visited need to be typed as well, hence the list 264 if (varNode.isStatement()) { 265 final IdentNode ident = varNode.getName(); 266 final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR); 267 if (canBeUndefined.contains(ident.getName())) { 268 symbol.setType(Type.OBJECT); 269 symbol.setCanBeUndefined(); 270 } 271 functionNode.addDeclaredSymbol(symbol); 272 if (varNode.isFunctionDeclaration()) { 273 newType(symbol, FunctionNode.FUNCTION_TYPE); 274 symbol.setIsFunctionDeclaration(); 275 } 276 return varNode.setName((IdentNode)ident.setSymbol(lc, symbol)); 277 } 278 279 return varNode; 280 } 281 }); 282 } 283 284 private void enterFunctionBody() { 285 286 final FunctionNode functionNode = lc.getCurrentFunction(); 287 final Block body = lc.getCurrentBlock(); 288 289 initFunctionWideVariables(functionNode, body); 290 291 if (functionNode.isProgram()) { 292 initFromPropertyMap(body); 293 } else if (!functionNode.isDeclared()) { 294 // It's neither declared nor program - it's a function expression then; assign it a self-symbol. 295 assert functionNode.getSymbol() == null; 296 297 final boolean anonymous = functionNode.isAnonymous(); 298 final String name = anonymous ? null : functionNode.getIdent().getName(); 299 if (!(anonymous || body.getExistingSymbol(name) != null)) { 300 assert !anonymous && name != null; 301 newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT); 302 } 303 } 304 305 acceptDeclarations(functionNode, body); 306 } 307 308 @Override 309 public boolean enterBlock(final Block block) { 310 start(block); 311 //ensure that we don't use information from a previous compile. This is very ugly TODO 312 //the symbols in the block should really be stateless 313 block.clearSymbols(); 314 315 if (lc.isFunctionBody()) { 316 enterFunctionBody(); 317 } 318 pushLocalsBlock(); 319 320 return true; 321 } 322 323 @Override 324 public Node leaveBlock(final Block block) { 325 popLocals(); 326 return end(block); 327 } 328 329 @Override 330 public boolean enterCallNode(final CallNode callNode) { 331 return start(callNode); 332 } 333 334 @Override 335 public Node leaveCallNode(final CallNode callNode) { 336 return end(ensureSymbol(callNode.getType(), callNode)); 337 } 338 339 @Override 340 public boolean enterCatchNode(final CatchNode catchNode) { 341 final IdentNode exception = catchNode.getException(); 342 final Block block = lc.getCurrentBlock(); 343 344 start(catchNode); 345 catchNestingLevel++; 346 347 // define block-local exception variable 348 final String exname = exception.getName(); 349 final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED); 350 newType(def, Type.OBJECT); //we can catch anything, not just ecma exceptions 351 352 addLocalDef(exname); 353 354 return true; 355 } 356 357 @Override 358 public Node leaveCatchNode(final CatchNode catchNode) { 359 final IdentNode exception = catchNode.getException(); 360 final Block block = lc.getCurrentBlock(); 361 final Symbol symbol = findSymbol(block, exception.getName()); 362 363 catchNestingLevel--; 364 365 assert symbol != null; 366 return end(catchNode.setException((IdentNode)exception.setSymbol(lc, symbol))); 367 } 368 369 /** 370 * Declare the definition of a new symbol. 371 * 372 * @param name Name of symbol. 373 * @param symbolFlags Symbol flags. 374 * 375 * @return Symbol for given name or null for redefinition. 376 */ 377 private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) { 378 int flags = symbolFlags; 379 Symbol symbol = findSymbol(block, name); // Locate symbol. 380 boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL; 381 382 if (isGlobal) { 383 flags |= IS_SCOPE; 384 } 385 386 final FunctionNode function = lc.getFunction(block); 387 if (symbol != null) { 388 // Symbol was already defined. Check if it needs to be redefined. 389 if ((flags & KINDMASK) == IS_PARAM) { 390 if (!isLocal(function, symbol)) { 391 // Not defined in this function. Create a new definition. 392 symbol = null; 393 } else if (symbol.isParam()) { 394 // Duplicate parameter. Null return will force an error. 395 assert false : "duplicate parameter"; 396 return null; 397 } 398 } else if ((flags & KINDMASK) == IS_VAR) { 399 if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) { 400 // Always create a new definition. 401 symbol = null; 402 } else { 403 // Not defined in this function. Create a new definition. 404 if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { 405 symbol = null; 406 } 407 } 408 } 409 } 410 411 if (symbol == null) { 412 // If not found, then create a new one. 413 Block symbolBlock; 414 415 // Determine where to create it. 416 if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { 417 symbolBlock = block; //internal vars are always defined in the block closest to them 418 } else if (isGlobal) { 419 symbolBlock = lc.getOutermostFunction().getBody(); 420 } else { 421 symbolBlock = lc.getFunctionBody(function); 422 } 423 424 // Create and add to appropriate block. 425 symbol = new Symbol(name, flags); 426 symbolBlock.putSymbol(lc, symbol); 427 428 if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { 429 symbol.setNeedsSlot(true); 430 } 431 } else if (symbol.less(flags)) { 432 symbol.setFlags(flags); 433 } 434 435 return symbol; 436 } 437 438 @Override 439 public boolean enterFunctionNode(final FunctionNode functionNode) { 440 start(functionNode, false); 441 442 if (functionNode.isLazy()) { 443 return false; 444 } 445 446 //an outermost function in our lexical context that is not a program (runScript) 447 //is possible - it is a function being compiled lazily 448 if (functionNode.isDeclared()) { 449 final Iterator<Block> blocks = lc.getBlocks(); 450 if (blocks.hasNext()) { 451 defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR); 452 } 453 } 454 455 returnTypes.push(functionNode.getReturnType()); 456 pushLocalsFunction(); 457 458 return true; 459 } 460 461 @Override 462 public Node leaveFunctionNode(final FunctionNode functionNode) { 463 FunctionNode newFunctionNode = functionNode; 464 465 final Block body = newFunctionNode.getBody(); 466 467 //look for this function in the parent block 468 if (functionNode.isDeclared()) { 469 final Iterator<Block> blocks = lc.getBlocks(); 470 if (blocks.hasNext()) { 471 newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName())); 472 } 473 } else if (!functionNode.isProgram()) { 474 final boolean anonymous = functionNode.isAnonymous(); 475 final String name = anonymous ? null : functionNode.getIdent().getName(); 476 if (anonymous || body.getExistingSymbol(name) != null) { 477 newFunctionNode = (FunctionNode)ensureSymbol(FunctionNode.FUNCTION_TYPE, newFunctionNode); 478 } else { 479 assert name != null; 480 final Symbol self = body.getExistingSymbol(name); 481 assert self != null && self.isFunctionSelf(); 482 newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, body.getExistingSymbol(name)); 483 } 484 } 485 486 //unknown parameters are promoted to object type. 487 if (newFunctionNode.hasLazyChildren()) { 488 //the final body has already been assigned as we have left the function node block body by now 489 objectifySymbols(body); 490 } 491 newFunctionNode = finalizeParameters(newFunctionNode); 492 newFunctionNode = finalizeTypes(newFunctionNode); 493 for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) { 494 if (symbol.getSymbolType().isUnknown()) { 495 symbol.setType(Type.OBJECT); 496 symbol.setCanBeUndefined(); 497 } 498 } 499 500 List<VarNode> syntheticInitializers = null; 501 502 if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) { 503 syntheticInitializers = new ArrayList<>(2); 504 LOG.info("Accepting self symbol init for ", newFunctionNode.getName()); 505 // "var fn = :callee" 506 syntheticInitializers.add(createSyntheticInitializer(newFunctionNode.getIdent(), CALLEE, newFunctionNode)); 507 } 508 509 if (newFunctionNode.needsArguments()) { 510 if (syntheticInitializers == null) { 511 syntheticInitializers = new ArrayList<>(1); 512 } 513 // "var arguments = :arguments" 514 syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()), 515 ARGUMENTS, newFunctionNode)); 516 } 517 518 if (syntheticInitializers != null) { 519 final List<Statement> stmts = newFunctionNode.getBody().getStatements(); 520 final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size()); 521 newStatements.addAll(syntheticInitializers); 522 newStatements.addAll(stmts); 523 newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements)); 524 } 525 526 if (returnTypes.peek().isUnknown()) { 527 LOG.info("Unknown return type promoted to object"); 528 newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT); 529 } 530 final Type returnType = returnTypes.pop(); 531 newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType); 532 newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR); 533 534 popLocals(); 535 536 end(newFunctionNode, false); 537 538 return newFunctionNode; 539 } 540 541 /** 542 * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically 543 * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function 544 * expressions as well as for assignment of {@code :arguments} to {@code arguments}. 545 * 546 * @param name the ident node identifying the variable to initialize 547 * @param initConstant the compiler constant it is initialized to 548 * @param fn the function node the assignment is for 549 * @return a var node with the appropriate assignment 550 */ 551 private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) { 552 final IdentNode init = compilerConstant(initConstant); 553 assert init.getSymbol() != null && init.getSymbol().hasSlot(); 554 555 VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init); 556 557 final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName()); 558 assert nameSymbol != null; 559 560 return synthVar.setName((IdentNode)name.setSymbol(lc, nameSymbol)); 561 } 562 563 @Override 564 public Node leaveIdentNode(final IdentNode identNode) { 565 final String name = identNode.getName(); 566 567 if (identNode.isPropertyName()) { 568 // assign a pseudo symbol to property name 569 final Symbol pseudoSymbol = pseudoSymbol(name); 570 LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol); 571 LOG.unindent(); 572 return end(identNode.setSymbol(lc, pseudoSymbol)); 573 } 574 575 final Block block = lc.getCurrentBlock(); 576 577 Symbol symbol = findSymbol(block, name); 578 579 //If an existing symbol with the name is found, use that otherwise, declare a new one 580 if (symbol != null) { 581 LOG.info("Existing symbol = ", symbol); 582 if (symbol.isFunctionSelf()) { 583 final FunctionNode functionNode = lc.getDefiningFunction(symbol); 584 assert functionNode != null; 585 assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null; 586 lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL); 587 newType(symbol, FunctionNode.FUNCTION_TYPE); 588 } else if (!identNode.isInitializedHere()) { 589 /* 590 * See NASHORN-448, JDK-8016235 591 * 592 * Here is a use outside the local def scope 593 * the inCatch check is a conservative approach to handle things that might have only been 594 * defined in the try block, but with variable declarations, which due to JavaScript rules 595 * have to be lifted up into the function scope outside the try block anyway, but as the 596 * flow can fault at almost any place in the try block and get us to the catch block, all we 597 * know is that we have a declaration, not a definition. This can be made better and less 598 * conservative once we superimpose a CFG onto the AST. 599 */ 600 if (!isLocalDef(name) || inCatch()) { 601 newType(symbol, Type.OBJECT); 602 symbol.setCanBeUndefined(); 603 } 604 } 605 606 // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) 607 maybeForceScope(symbol); 608 } else { 609 LOG.info("No symbol exists. Declare undefined: ", symbol); 610 symbol = defineSymbol(block, name, IS_GLOBAL); 611 // we have never seen this before, it can be undefined 612 newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway? 613 symbol.setCanBeUndefined(); 614 Symbol.setSymbolIsScope(lc, symbol); 615 } 616 617 setBlockScope(name, symbol); 618 619 if (!identNode.isInitializedHere()) { 620 symbol.increaseUseCount(); 621 } 622 addLocalUse(identNode.getName()); 623 624 return end(identNode.setSymbol(lc, symbol)); 625 } 626 627 private boolean inCatch() { 628 return catchNestingLevel > 0; 629 } 630 631 /** 632 * If the symbol isn't already a scope symbol, and it is either not local to the current function, or it is being 633 * referenced from within a with block, we force it to be a scope symbol. 634 * @param symbol the symbol that might be scoped 635 */ 636 private void maybeForceScope(final Symbol symbol) { 637 if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) { 638 Symbol.setSymbolIsScope(lc, symbol); 639 } 640 } 641 642 private boolean symbolNeedsToBeScope(Symbol symbol) { 643 if (symbol.isThis() || symbol.isInternal()) { 644 return false; 645 } 646 boolean previousWasBlock = false; 647 for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { 648 final LexicalContextNode node = it.next(); 649 if (node instanceof FunctionNode) { 650 // We reached the function boundary without seeing a definition for the symbol - it needs to be in 651 // scope. 652 return true; 653 } else if (node instanceof WithNode) { 654 if (previousWasBlock) { 655 // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately 656 // preceded by a block, this means we're currently processing its expression, not its body, 657 // therefore it doesn't count. 658 return true; 659 } 660 previousWasBlock = false; 661 } else if (node instanceof Block) { 662 if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) { 663 // We reached the block that defines the symbol without reaching either the function boundary, or a 664 // WithNode. The symbol need not be scoped. 665 return false; 666 } 667 previousWasBlock = true; 668 } else { 669 previousWasBlock = false; 670 } 671 } 672 throw new AssertionError(); 673 } 674 675 private void setBlockScope(final String name, final Symbol symbol) { 676 assert symbol != null; 677 if (symbol.isGlobal()) { 678 setUsesGlobalSymbol(); 679 return; 680 } 681 682 if (symbol.isScope()) { 683 Block scopeBlock = null; 684 for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) { 685 final LexicalContextNode node = contextNodeIter.next(); 686 if (node instanceof Block) { 687 if (((Block)node).getExistingSymbol(name) != null) { 688 scopeBlock = (Block)node; 689 break; 690 } 691 } else if (node instanceof FunctionNode) { 692 lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE); 693 } 694 } 695 696 if (scopeBlock != null) { 697 assert lc.contains(scopeBlock); 698 lc.setBlockNeedsScope(scopeBlock); 699 } 700 } 701 } 702 703 /** 704 * Marks the current function as one using any global symbol. The function and all its parent functions will all be 705 * marked as needing parent scope. 706 * @see #needsParentScope() 707 */ 708 private void setUsesGlobalSymbol() { 709 for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) { 710 lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE); 711 } 712 } 713 714 /** 715 * Search for symbol in the lexical context starting from the given block. 716 * @param name Symbol name. 717 * @return Found symbol or null if not found. 718 */ 719 private Symbol findSymbol(final Block block, final String name) { 720 // Search up block chain to locate symbol. 721 722 for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) { 723 // Find name. 724 final Symbol symbol = blocks.next().getExistingSymbol(name); 725 // If found then we are good. 726 if (symbol != null) { 727 return symbol; 728 } 729 } 730 return null; 731 } 732 733 @Override 734 public Node leaveIndexNode(final IndexNode indexNode) { 735 return end(ensureSymbol(Type.OBJECT, indexNode)); 736 } 737 738 @SuppressWarnings("rawtypes") 739 @Override 740 public Node leaveLiteralNode(final LiteralNode literalNode) { 741 assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens 742 assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported"; 743 final Symbol symbol = new Symbol(lc.getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType()); 744 if (literalNode instanceof ArrayLiteralNode) { 745 ((ArrayLiteralNode)literalNode).analyze(); 746 } 747 return end(literalNode.setSymbol(lc, symbol)); 748 } 749 750 @Override 751 public boolean enterObjectNode(final ObjectNode objectNode) { 752 return start(objectNode); 753 } 754 755 @Override 756 public Node leaveObjectNode(final ObjectNode objectNode) { 757 return end(ensureSymbol(Type.OBJECT, objectNode)); 758 } 759 760 @Override 761 public Node leaveReturnNode(final ReturnNode returnNode) { 762 final Expression expr = returnNode.getExpression(); 763 final Type returnType; 764 765 if (expr != null) { 766 //we can't do parameter specialization if we return something that hasn't been typed yet 767 final Symbol symbol = expr.getSymbol(); 768 if (expr.getType().isUnknown() && symbol.isParam()) { 769 symbol.setType(Type.OBJECT); 770 } 771 772 returnType = widestReturnType(returnTypes.pop(), symbol.getSymbolType()); 773 } else { 774 returnType = Type.OBJECT; //undefined 775 } 776 LOG.info("Returntype is now ", returnType); 777 returnTypes.push(returnType); 778 779 end(returnNode); 780 781 return returnNode; 782 } 783 784 @Override 785 public Node leaveSwitchNode(final SwitchNode switchNode) { 786 Type type = Type.UNKNOWN; 787 788 final List<CaseNode> newCases = new ArrayList<>(); 789 for (final CaseNode caseNode : switchNode.getCases()) { 790 final Node test = caseNode.getTest(); 791 792 CaseNode newCaseNode = caseNode; 793 if (test != null) { 794 if (test instanceof LiteralNode) { 795 //go down to integers if we can 796 final LiteralNode<?> lit = (LiteralNode<?>)test; 797 if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) { 798 if (JSType.isRepresentableAsInt(lit.getNumber())) { 799 newCaseNode = caseNode.setTest((Expression)LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); 800 } 801 } 802 } else { 803 // the "all integer" case that CodeGenerator optimizes for currently assumes literals only 804 type = Type.OBJECT; 805 } 806 807 final Type newCaseType = newCaseNode.getTest().getType(); 808 if (newCaseType.isBoolean()) { 809 type = Type.OBJECT; //booleans and integers aren't assignment compatible 810 } else { 811 type = Type.widest(type, newCaseType); 812 } 813 } 814 815 newCases.add(newCaseNode); 816 } 817 818 //only optimize for all integers 819 if (!type.isInteger()) { 820 type = Type.OBJECT; 821 } 822 823 switchNode.setTag(newInternal(lc.getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type)); 824 825 end(switchNode); 826 827 return switchNode.setCases(lc, newCases); 828 } 829 830 @Override 831 public Node leaveTryNode(final TryNode tryNode) { 832 tryNode.setException(exceptionSymbol()); 833 834 if (tryNode.getFinallyBody() != null) { 835 tryNode.setFinallyCatchAll(exceptionSymbol()); 836 } 837 838 end(tryNode); 839 840 return tryNode; 841 } 842 843 @Override 844 public boolean enterVarNode(final VarNode varNode) { 845 start(varNode); 846 847 final IdentNode ident = varNode.getName(); 848 final String name = ident.getName(); 849 850 final Symbol symbol = defineSymbol(lc.getCurrentBlock(), name, IS_VAR); 851 assert symbol != null; 852 853 // NASHORN-467 - use before definition of vars - conservative 854 if (isLocalUse(ident.getName())) { 855 newType(symbol, Type.OBJECT); 856 symbol.setCanBeUndefined(); 857 } 858 859 return true; 860 } 861 862 @Override 863 public Node leaveVarNode(final VarNode varNode) { 864 final Expression init = varNode.getInit(); 865 final IdentNode ident = varNode.getName(); 866 final String name = ident.getName(); 867 868 final Symbol symbol = findSymbol(lc.getCurrentBlock(), name); 869 assert ident.getSymbol() == symbol; 870 871 if (init == null) { 872 // var x; with no init will be treated like a use of x by 873 // leaveIdentNode unless we remove the name from the localdef list. 874 removeLocalDef(name); 875 return end(varNode); 876 } 877 878 addLocalDef(name); 879 880 assert symbol != null; 881 882 final IdentNode newIdent = (IdentNode)ident.setSymbol(lc, symbol); 883 884 final VarNode newVarNode = varNode.setName(newIdent); 885 886 final boolean isScript = lc.getDefiningFunction(symbol).isProgram(); //see NASHORN-56 887 if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) { 888 // Forbid integers as local vars for now as we have no way to treat them as undefined 889 newType(symbol, init.getType()); 890 } else { 891 newType(symbol, Type.OBJECT); 892 } 893 894 assert newVarNode.getName().hasType() : newVarNode + " has no type"; 895 896 return end(newVarNode); 897 } 898 899 @Override 900 public Node leaveADD(final UnaryNode unaryNode) { 901 return end(ensureSymbol(arithType(), unaryNode)); 902 } 903 904 @Override 905 public Node leaveBIT_NOT(final UnaryNode unaryNode) { 906 return end(ensureSymbol(Type.INT, unaryNode)); 907 } 908 909 @Override 910 public Node leaveDECINC(final UnaryNode unaryNode) { 911 // @see assignOffset 912 final Type type = arithType(); 913 newType(unaryNode.rhs().getSymbol(), type); 914 return end(ensureSymbol(type, unaryNode)); 915 } 916 917 @Override 918 public Node leaveDELETE(final UnaryNode unaryNode) { 919 final FunctionNode currentFunctionNode = lc.getCurrentFunction(); 920 final boolean strictMode = currentFunctionNode.isStrict(); 921 final Expression rhs = unaryNode.rhs(); 922 final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this); 923 924 Request request = Request.DELETE; 925 final List<Expression> args = new ArrayList<>(); 926 927 if (rhs instanceof IdentNode) { 928 // If this is a declared variable or a function parameter, delete always fails (except for globals). 929 final String name = ((IdentNode)rhs).getName(); 930 931 final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name)); 932 933 if (failDelete && rhs.getSymbol().isThis()) { 934 return LiteralNode.newInstance(unaryNode, true).accept(this); 935 } 936 final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); 937 938 if (!failDelete) { 939 args.add(compilerConstant(SCOPE)); 940 } 941 args.add(literalNode); 942 args.add(strictFlagNode); 943 944 if (failDelete) { 945 request = Request.FAIL_DELETE; 946 } 947 } else if (rhs instanceof AccessNode) { 948 final Expression base = ((AccessNode)rhs).getBase(); 949 final IdentNode property = ((AccessNode)rhs).getProperty(); 950 951 args.add(base); 952 args.add((Expression)LiteralNode.newInstance(unaryNode, property.getName()).accept(this)); 953 args.add(strictFlagNode); 954 955 } else if (rhs instanceof IndexNode) { 956 final IndexNode indexNode = (IndexNode)rhs; 957 final Expression base = indexNode.getBase(); 958 final Expression index = indexNode.getIndex(); 959 960 args.add(base); 961 args.add(index); 962 args.add(strictFlagNode); 963 964 } else { 965 return LiteralNode.newInstance(unaryNode, true).accept(this); 966 } 967 968 final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args); 969 assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this 970 971 return leaveRuntimeNode(runtimeNode); 972 } 973 974 /** 975 * Is the symbol denoted by the specified name in the current lexical context defined in the program level 976 * @param name the name of the symbol 977 * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level. 978 */ 979 private boolean isProgramLevelSymbol(final String name) { 980 for(final Iterator<Block> it = lc.getBlocks(); it.hasNext();) { 981 final Block next = it.next(); 982 if(next.getExistingSymbol(name) != null) { 983 return next == lc.getFunctionBody(lc.getOutermostFunction()); 984 } 985 } 986 throw new AssertionError("Couldn't find symbol " + name + " in the context"); 987 } 988 989 @Override 990 public Node leaveNEW(final UnaryNode unaryNode) { 991 return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew()))); 992 } 993 994 @Override 995 public Node leaveNOT(final UnaryNode unaryNode) { 996 return end(ensureSymbol(Type.BOOLEAN, unaryNode)); 997 } 998 999 private IdentNode compilerConstant(CompilerConstants cc) { 1000 return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc, lc.getCurrentFunction().compilerConstant(cc)); 1001 } 1002 1003 /** 1004 * Creates an ident node for an implicit identifier within the function (one not declared in the script source 1005 * code). These identifiers are defined with function's token and finish. 1006 * @param name the name of the identifier 1007 * @return an ident node representing the implicit identifier. 1008 */ 1009 private IdentNode createImplicitIdentifier(final String name) { 1010 final FunctionNode fn = lc.getCurrentFunction(); 1011 return new IdentNode(fn.getToken(), fn.getFinish(), name); 1012 } 1013 1014 @Override 1015 public Node leaveTYPEOF(final UnaryNode unaryNode) { 1016 final Expression rhs = unaryNode.rhs(); 1017 1018 List<Expression> args = new ArrayList<>(); 1019 if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) { 1020 args.add(compilerConstant(SCOPE)); 1021 args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null 1022 } else { 1023 args.add(rhs); 1024 args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' 1025 } 1026 1027 RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); 1028 assert runtimeNode.getSymbol() == unaryNode.getSymbol(); 1029 1030 runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode); 1031 1032 end(unaryNode); 1033 1034 return runtimeNode; 1035 } 1036 1037 @Override 1038 public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { 1039 return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode)); 1040 } 1041 1042 @Override 1043 public Node leaveSUB(final UnaryNode unaryNode) { 1044 return end(ensureSymbol(arithType(), unaryNode)); 1045 } 1046 1047 @Override 1048 public Node leaveVOID(final UnaryNode unaryNode) { 1049 return end(ensureSymbol(Type.OBJECT, unaryNode)); 1050 } 1051 1052 /** 1053 * Add is a special binary, as it works not only on arithmetic, but for 1054 * strings etc as well. 1055 */ 1056 @Override 1057 public Node leaveADD(final BinaryNode binaryNode) { 1058 final Expression lhs = binaryNode.lhs(); 1059 final Expression rhs = binaryNode.rhs(); 1060 1061 ensureTypeNotUnknown(lhs); 1062 ensureTypeNotUnknown(rhs); 1063 //even if we are adding two known types, this can overflow. i.e. 1064 //int and number -> number. 1065 //int and int are also number though. 1066 //something and object is object 1067 return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode)); 1068 } 1069 1070 @Override 1071 public Node leaveAND(final BinaryNode binaryNode) { 1072 return end(ensureSymbol(Type.OBJECT, binaryNode)); 1073 } 1074 1075 /** 1076 * This is a helper called before an assignment. 1077 * @param binaryNode assignment node 1078 */ 1079 private boolean enterAssignmentNode(final BinaryNode binaryNode) { 1080 start(binaryNode); 1081 1082 return true; 1083 } 1084 1085 1086 /** 1087 * This assign helper is called after an assignment, when all children of 1088 * the assign has been processed. It fixes the types and recursively makes 1089 * sure that everyhing has slots that should have them in the chain. 1090 * 1091 * @param binaryNode assignment node 1092 */ 1093 private Node leaveAssignmentNode(final BinaryNode binaryNode) { 1094 final Expression lhs = binaryNode.lhs(); 1095 final Expression rhs = binaryNode.rhs(); 1096 final Type type; 1097 1098 if (lhs instanceof IdentNode) { 1099 final Block block = lc.getCurrentBlock(); 1100 final IdentNode ident = (IdentNode)lhs; 1101 final String name = ident.getName(); 1102 final Symbol symbol = findSymbol(block, name); 1103 1104 if (symbol == null) { 1105 defineSymbol(block, name, IS_GLOBAL); 1106 } else { 1107 maybeForceScope(symbol); 1108 } 1109 1110 addLocalDef(name); 1111 } 1112 1113 if (rhs.getType().isNumeric()) { 1114 type = Type.widest(lhs.getType(), rhs.getType()); 1115 } else { 1116 type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too. 1117 } 1118 1119 newType(lhs.getSymbol(), type); 1120 return end(ensureSymbol(type, binaryNode)); 1121 } 1122 1123 private boolean isLocal(FunctionNode function, Symbol symbol) { 1124 final FunctionNode definingFn = lc.getDefiningFunction(symbol); 1125 // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local 1126 return definingFn == null || definingFn == function; 1127 } 1128 1129 @Override 1130 public boolean enterASSIGN(final BinaryNode binaryNode) { 1131 return enterAssignmentNode(binaryNode); 1132 } 1133 1134 @Override 1135 public Node leaveASSIGN(final BinaryNode binaryNode) { 1136 return leaveAssignmentNode(binaryNode); 1137 } 1138 1139 @Override 1140 public boolean enterASSIGN_ADD(final BinaryNode binaryNode) { 1141 return enterAssignmentNode(binaryNode); 1142 } 1143 1144 @Override 1145 public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { 1146 final Expression lhs = binaryNode.lhs(); 1147 final Expression rhs = binaryNode.rhs(); 1148 1149 final Type widest = Type.widest(lhs.getType(), rhs.getType()); 1150 //Type.NUMBER if we can't prove that the add doesn't overflow. todo 1151 return leaveSelfModifyingAssignmentNode(binaryNode, widest.isNumeric() ? Type.NUMBER : Type.OBJECT); 1152 } 1153 1154 @Override 1155 public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) { 1156 return enterAssignmentNode(binaryNode); 1157 } 1158 1159 @Override 1160 public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { 1161 return leaveSelfModifyingAssignmentNode(binaryNode); 1162 } 1163 1164 @Override 1165 public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) { 1166 return enterAssignmentNode(binaryNode); 1167 } 1168 1169 @Override 1170 public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { 1171 return leaveSelfModifyingAssignmentNode(binaryNode); 1172 } 1173 1174 @Override 1175 public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { 1176 return enterAssignmentNode(binaryNode); 1177 } 1178 1179 @Override 1180 public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { 1181 return leaveSelfModifyingAssignmentNode(binaryNode); 1182 } 1183 1184 @Override 1185 public boolean enterASSIGN_DIV(final BinaryNode binaryNode) { 1186 return enterAssignmentNode(binaryNode); 1187 } 1188 1189 @Override 1190 public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { 1191 return leaveSelfModifyingAssignmentNode(binaryNode); 1192 } 1193 1194 @Override 1195 public boolean enterASSIGN_MOD(final BinaryNode binaryNode) { 1196 return enterAssignmentNode(binaryNode); 1197 } 1198 1199 @Override 1200 public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { 1201 return leaveSelfModifyingAssignmentNode(binaryNode); 1202 } 1203 1204 @Override 1205 public boolean enterASSIGN_MUL(final BinaryNode binaryNode) { 1206 return enterAssignmentNode(binaryNode); 1207 } 1208 1209 @Override 1210 public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { 1211 return leaveSelfModifyingAssignmentNode(binaryNode); 1212 } 1213 1214 @Override 1215 public boolean enterASSIGN_SAR(final BinaryNode binaryNode) { 1216 return enterAssignmentNode(binaryNode); 1217 } 1218 1219 @Override 1220 public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { 1221 return leaveSelfModifyingAssignmentNode(binaryNode); 1222 } 1223 1224 @Override 1225 public boolean enterASSIGN_SHL(final BinaryNode binaryNode) { 1226 return enterAssignmentNode(binaryNode); 1227 } 1228 1229 @Override 1230 public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { 1231 return leaveSelfModifyingAssignmentNode(binaryNode); 1232 } 1233 1234 @Override 1235 public boolean enterASSIGN_SHR(final BinaryNode binaryNode) { 1236 return enterAssignmentNode(binaryNode); 1237 } 1238 1239 @Override 1240 public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { 1241 return leaveSelfModifyingAssignmentNode(binaryNode); 1242 } 1243 1244 @Override 1245 public boolean enterASSIGN_SUB(final BinaryNode binaryNode) { 1246 return enterAssignmentNode(binaryNode); 1247 } 1248 1249 @Override 1250 public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { 1251 return leaveSelfModifyingAssignmentNode(binaryNode); 1252 } 1253 1254 @Override 1255 public Node leaveBIT_AND(final BinaryNode binaryNode) { 1256 return end(coerce(binaryNode, Type.INT)); 1257 } 1258 1259 @Override 1260 public Node leaveBIT_OR(final BinaryNode binaryNode) { 1261 return end(coerce(binaryNode, Type.INT)); 1262 } 1263 1264 @Override 1265 public Node leaveBIT_XOR(final BinaryNode binaryNode) { 1266 return end(coerce(binaryNode, Type.INT)); 1267 } 1268 1269 @Override 1270 public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { 1271 return leaveComma(binaryNode, binaryNode.rhs()); 1272 } 1273 1274 @Override 1275 public Node leaveCOMMALEFT(final BinaryNode binaryNode) { 1276 return leaveComma(binaryNode, binaryNode.lhs()); 1277 } 1278 1279 private Node leaveComma(final BinaryNode commaNode, final Expression effectiveExpr) { 1280 ensureTypeNotUnknown(effectiveExpr); 1281 return end(ensureSymbol(effectiveExpr.getType(), commaNode)); 1282 } 1283 1284 @Override 1285 public Node leaveDIV(final BinaryNode binaryNode) { 1286 return leaveBinaryArithmetic(binaryNode); 1287 } 1288 1289 private Node leaveCmp(final BinaryNode binaryNode) { 1290 ensureTypeNotUnknown(binaryNode.lhs()); 1291 ensureTypeNotUnknown(binaryNode.rhs()); 1292 Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); 1293 ensureSymbol(widest, binaryNode.lhs()); 1294 ensureSymbol(widest, binaryNode.rhs()); 1295 return end(ensureSymbol(Type.BOOLEAN, binaryNode)); 1296 } 1297 1298 private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) { 1299 // TODO we currently don't support changing inferred type based on uses, only on 1300 // definitions. we would need some additional logic. We probably want to do that 1301 // in the future, if e.g. a specialized method gets parameter that is only used 1302 // as, say, an int : function(x) { return x & 4711 }, and x is not defined in 1303 // the function. to make this work, uncomment the following two type inferences 1304 // and debug. 1305 //newType(binaryNode.lhs().getSymbol(), operandType); 1306 //newType(binaryNode.rhs().getSymbol(), operandType); 1307 return ensureSymbol(destType, binaryNode); 1308 } 1309 1310 private Node coerce(final BinaryNode binaryNode, final Type type) { 1311 return coerce(binaryNode, type, type); 1312 } 1313 1314 //leave a binary node and inherit the widest type of lhs , rhs 1315 private Node leaveBinaryArithmetic(final BinaryNode binaryNode) { 1316 assert !Compiler.shouldUseIntegerArithmetic(); 1317 return end(coerce(binaryNode, Type.NUMBER)); 1318 } 1319 1320 @Override 1321 public Node leaveEQ(final BinaryNode binaryNode) { 1322 return leaveCmp(binaryNode); 1323 } 1324 1325 @Override 1326 public Node leaveEQ_STRICT(final BinaryNode binaryNode) { 1327 return leaveCmp(binaryNode); 1328 } 1329 1330 @Override 1331 public Node leaveGE(final BinaryNode binaryNode) { 1332 return leaveCmp(binaryNode); 1333 } 1334 1335 @Override 1336 public Node leaveGT(final BinaryNode binaryNode) { 1337 return leaveCmp(binaryNode); 1338 } 1339 1340 @Override 1341 public Node leaveIN(final BinaryNode binaryNode) { 1342 return leaveBinaryRuntimeOperator(binaryNode, Request.IN); 1343 } 1344 1345 @Override 1346 public Node leaveINSTANCEOF(final BinaryNode binaryNode) { 1347 return leaveBinaryRuntimeOperator(binaryNode, Request.INSTANCEOF); 1348 } 1349 1350 private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) { 1351 try { 1352 // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands 1353 return leaveRuntimeNode(new RuntimeNode(binaryNode, request)); 1354 } finally { 1355 end(binaryNode); 1356 } 1357 } 1358 1359 @Override 1360 public Node leaveLE(final BinaryNode binaryNode) { 1361 return leaveCmp(binaryNode); 1362 } 1363 1364 @Override 1365 public Node leaveLT(final BinaryNode binaryNode) { 1366 return leaveCmp(binaryNode); 1367 } 1368 1369 @Override 1370 public Node leaveMOD(final BinaryNode binaryNode) { 1371 return leaveBinaryArithmetic(binaryNode); 1372 } 1373 1374 @Override 1375 public Node leaveMUL(final BinaryNode binaryNode) { 1376 return leaveBinaryArithmetic(binaryNode); 1377 } 1378 1379 @Override 1380 public Node leaveNE(final BinaryNode binaryNode) { 1381 return leaveCmp(binaryNode); 1382 } 1383 1384 @Override 1385 public Node leaveNE_STRICT(final BinaryNode binaryNode) { 1386 return leaveCmp(binaryNode); 1387 } 1388 1389 @Override 1390 public Node leaveOR(final BinaryNode binaryNode) { 1391 return end(ensureSymbol(Type.OBJECT, binaryNode)); 1392 } 1393 1394 @Override 1395 public Node leaveSAR(final BinaryNode binaryNode) { 1396 return end(coerce(binaryNode, Type.INT)); 1397 } 1398 1399 @Override 1400 public Node leaveSHL(final BinaryNode binaryNode) { 1401 return end(coerce(binaryNode, Type.INT)); 1402 } 1403 1404 @Override 1405 public Node leaveSHR(final BinaryNode binaryNode) { 1406 return end(coerce(binaryNode, Type.LONG)); 1407 } 1408 1409 @Override 1410 public Node leaveSUB(final BinaryNode binaryNode) { 1411 return leaveBinaryArithmetic(binaryNode); 1412 } 1413 1414 @Override 1415 public Node leaveForNode(final ForNode forNode) { 1416 if (forNode.isForIn()) { 1417 forNode.setIterator(newInternal(lc.getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.typeFor(ITERATOR_PREFIX.type()))); //NASHORN-73 1418 /* 1419 * Iterators return objects, so we need to widen the scope of the 1420 * init variable if it, for example, has been assigned double type 1421 * see NASHORN-50 1422 */ 1423 newType(forNode.getInit().getSymbol(), Type.OBJECT); 1424 } 1425 1426 end(forNode); 1427 1428 return forNode; 1429 } 1430 1431 @Override 1432 public Node leaveTernaryNode(final TernaryNode ternaryNode) { 1433 final Expression trueExpr = ternaryNode.getTrueExpression(); 1434 final Expression falseExpr = ternaryNode.getFalseExpression(); 1435 1436 ensureTypeNotUnknown(trueExpr); 1437 ensureTypeNotUnknown(falseExpr); 1438 1439 final Type type = widestReturnType(trueExpr.getType(), falseExpr.getType()); 1440 return end(ensureSymbol(type, ternaryNode)); 1441 } 1442 1443 /** 1444 * When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to 1445 * anything other than Object. Also, widening a numeric type to an object type must widen to Object proper and not 1446 * any more specific subclass (e.g. widest of int/long/double and String is Object). 1447 * @param t1 type 1 1448 * @param t2 type 2 1449 * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, or if one is 1450 * numeric and the other is neither numeric nor unknown in which case {@code Type.OBJECT} is returned. 1451 */ 1452 private static Type widestReturnType(final Type t1, final Type t2) { 1453 if (t1.isUnknown()) { 1454 return t2; 1455 } else if (t2.isUnknown()) { 1456 return t1; 1457 } else if (t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) { 1458 return Type.OBJECT; 1459 } 1460 return Type.widest(t1, t2); 1461 } 1462 1463 private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) { 1464 final Class<?> type = cc.type(); 1465 // Must not call this method for constants with no explicit types; use the one with (..., Type) signature instead. 1466 assert type != null; 1467 initCompileConstant(cc, block, flags, Type.typeFor(type)); 1468 } 1469 1470 private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) { 1471 final Symbol symbol = defineSymbol(block, cc.symbolName(), flags); 1472 symbol.setTypeOverride(type); 1473 symbol.setNeedsSlot(true); 1474 } 1475 1476 /** 1477 * Initialize parameters for function node. This may require specializing 1478 * types if a specialization profile is known 1479 * 1480 * @param functionNode the function node 1481 */ 1482 private void initParameters(final FunctionNode functionNode, final Block body) { 1483 int pos = 0; 1484 for (final IdentNode param : functionNode.getParameters()) { 1485 addLocalDef(param.getName()); 1486 1487 final Type callSiteParamType = functionNode.getHints().getParameterType(pos); 1488 int flags = IS_PARAM; 1489 if (callSiteParamType != null) { 1490 LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that."); 1491 flags |= Symbol.IS_SPECIALIZED_PARAM; 1492 } 1493 1494 final Symbol paramSymbol = defineSymbol(body, param.getName(), flags); 1495 assert paramSymbol != null; 1496 1497 newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType); 1498 1499 LOG.info("Initialized param ", pos, "=", paramSymbol); 1500 pos++; 1501 } 1502 1503 } 1504 1505 /** 1506 * This has to run before fix assignment types, store any type specializations for 1507 * paramters, then turn then to objects for the generic version of this method 1508 * 1509 * @param functionNode functionNode 1510 */ 1511 private FunctionNode finalizeParameters(final FunctionNode functionNode) { 1512 final List<IdentNode> newParams = new ArrayList<>(); 1513 final boolean isVarArg = functionNode.isVarArg(); 1514 final int nparams = functionNode.getParameters().size(); 1515 1516 int specialize = 0; 1517 int pos = 0; 1518 for (final IdentNode param : functionNode.getParameters()) { 1519 final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName()); 1520 assert paramSymbol != null; 1521 assert paramSymbol.isParam(); 1522 newParams.add((IdentNode)param.setSymbol(lc, paramSymbol)); 1523 1524 assert paramSymbol != null; 1525 Type type = functionNode.getHints().getParameterType(pos); 1526 if (type == null) { 1527 type = Type.OBJECT; 1528 } 1529 1530 // if we know that a parameter is only used as a certain type throughout 1531 // this function, we can tell the runtime system that no matter what the 1532 // call site is, use this information: 1533 // we also need more than half of the parameters to be specializable 1534 // for the heuristic to be worth it, and we need more than one use of 1535 // the parameter to consider it, i.e. function(x) { call(x); } doens't count 1536 if (paramSymbol.getUseCount() > 1 && !paramSymbol.getSymbolType().isObject()) { 1537 LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType()); 1538 specialize++; 1539 } 1540 1541 newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType())); 1542 1543 // parameters should not be slots for a function that uses variable arity signature 1544 if (isVarArg) { 1545 paramSymbol.setNeedsSlot(false); 1546 } 1547 1548 pos++; 1549 } 1550 1551 FunctionNode newFunctionNode = functionNode; 1552 1553 if (nparams == 0 || (specialize * 2) < nparams) { 1554 newFunctionNode = newFunctionNode.clearSnapshot(lc); 1555 } 1556 1557 return newFunctionNode.setParameters(lc, newParams); 1558 } 1559 1560 /** 1561 * Move any properties from a global map into the scope of this method 1562 * @param block the function node body for which to init scope vars 1563 */ 1564 private void initFromPropertyMap(final Block block) { 1565 // For a script, add scope symbols as defined in the property map 1566 1567 final PropertyMap map = Context.getGlobalMap(); 1568 1569 for (final Property property : map.getProperties()) { 1570 final String key = property.getKey(); 1571 final Symbol symbol = defineSymbol(block, key, IS_GLOBAL); 1572 newType(symbol, Type.OBJECT); 1573 LOG.info("Added global symbol from property map ", symbol); 1574 } 1575 } 1576 1577 private static void ensureTypeNotUnknown(final Expression node) { 1578 1579 final Symbol symbol = node.getSymbol(); 1580 1581 LOG.info("Ensure type not unknown for: ", symbol); 1582 1583 /* 1584 * Note that not just unknowns, but params need to be blown 1585 * up to objects, because we can have something like 1586 * 1587 * function f(a) { 1588 * var b = ~a; //b and a are inferred to be int 1589 * return b; 1590 * } 1591 * 1592 * In this case, it would be correct to say that "if you have 1593 * an int at the callsite, just pass it". 1594 * 1595 * However 1596 * 1597 * function f(a) { 1598 * var b = ~a; //b and a are inferred to be int 1599 * return b == 17; //b is still inferred to be int. 1600 * } 1601 * 1602 * can be called with f("17") and if we assume that b is an 1603 * int and don't blow it up to an object in the comparison, we 1604 * are screwed. I hate JavaScript. 1605 * 1606 * This check has to be done for any operation that might take 1607 * objects as parameters, for example +, but not *, which is known 1608 * to coerce types into doubles 1609 */ 1610 if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) { 1611 newType(symbol, Type.OBJECT); 1612 symbol.setCanBeUndefined(); 1613 } 1614 } 1615 1616 private static Symbol pseudoSymbol(final String name) { 1617 return new Symbol(name, 0, Type.OBJECT); 1618 } 1619 1620 private Symbol exceptionSymbol() { 1621 return newInternal(lc.getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(EXCEPTION_PREFIX.type())); 1622 } 1623 1624 /** 1625 * Return the type that arithmetic ops should use. Until we have implemented better type 1626 * analysis (range based) or overflow checks that are fast enough for int arithmetic, 1627 * this is the number type 1628 * @return the arithetic type 1629 */ 1630 private static Type arithType() { 1631 return Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER; 1632 } 1633 1634 /** 1635 * If types have changed, we can have failed to update vars. For example 1636 * 1637 * var x = 17; //x is int 1638 * x = "apa"; //x is object. This will be converted fine 1639 * 1640 * @param functionNode 1641 */ 1642 private FunctionNode finalizeTypes(final FunctionNode functionNode) { 1643 final Set<Node> changed = new HashSet<>(); 1644 FunctionNode currentFunctionNode = functionNode; 1645 do { 1646 changed.clear(); 1647 final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 1648 1649 private Expression widen(final Expression node, final Type to) { 1650 if (node instanceof LiteralNode) { 1651 return node; 1652 } 1653 Type from = node.getType(); 1654 if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) { 1655 LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to); 1656 Symbol symbol = node.getSymbol(); 1657 if (symbol.isShared() && symbol.wouldChangeType(to)) { 1658 symbol = temporarySymbols.getTypedTemporarySymbol(to); 1659 } 1660 newType(symbol, to); 1661 final Expression newNode = node.setSymbol(lc, symbol); 1662 changed.add(newNode); 1663 return newNode; 1664 } 1665 return node; 1666 } 1667 1668 @Override 1669 public boolean enterFunctionNode(final FunctionNode node) { 1670 return !node.isLazy(); 1671 } 1672 1673 // 1674 // Eg. 1675 // 1676 // var d = 17; 1677 // var e; 1678 // e = d; //initially typed as int for node type, should retype as double 1679 // e = object; 1680 // 1681 // var d = 17; 1682 // var e; 1683 // e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric 1684 // e = object; 1685 // 1686 @SuppressWarnings("fallthrough") 1687 @Override 1688 public Node leaveBinaryNode(final BinaryNode binaryNode) { 1689 final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); 1690 BinaryNode newBinaryNode = binaryNode; 1691 1692 if (isAdd(binaryNode)) { 1693 newBinaryNode = (BinaryNode)widen(newBinaryNode, widest); 1694 if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) { 1695 return new RuntimeNode(newBinaryNode, Request.ADD); 1696 } 1697 } else if (binaryNode.isComparison()) { 1698 final Expression lhs = newBinaryNode.lhs(); 1699 final Expression rhs = newBinaryNode.rhs(); 1700 1701 Type cmpWidest = Type.widest(lhs.getType(), rhs.getType()); 1702 1703 boolean newRuntimeNode = false, finalized = false; 1704 switch (newBinaryNode.tokenType()) { 1705 case EQ_STRICT: 1706 case NE_STRICT: 1707 if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { 1708 newRuntimeNode = true; 1709 cmpWidest = Type.OBJECT; 1710 finalized = true; 1711 } 1712 //fallthru 1713 default: 1714 if (newRuntimeNode || cmpWidest.isObject()) { 1715 return new RuntimeNode(newBinaryNode, Request.requestFor(binaryNode)).setIsFinal(finalized); 1716 } 1717 break; 1718 } 1719 1720 return newBinaryNode; 1721 } else { 1722 if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) { 1723 return newBinaryNode; 1724 } 1725 checkThisAssignment(binaryNode); 1726 newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest)); 1727 newBinaryNode = (BinaryNode)widen(newBinaryNode, widest); 1728 } 1729 1730 return newBinaryNode; 1731 1732 } 1733 1734 private boolean isAdd(final Node node) { 1735 return node.isTokenType(TokenType.ADD); 1736 } 1737 1738 /** 1739 * Determine if the outcome of + operator is a string. 1740 * 1741 * @param node Node to test. 1742 * @return true if a string result. 1743 */ 1744 private boolean isAddString(final Node node) { 1745 if (node instanceof BinaryNode && isAdd(node)) { 1746 final BinaryNode binaryNode = (BinaryNode)node; 1747 final Node lhs = binaryNode.lhs(); 1748 final Node rhs = binaryNode.rhs(); 1749 1750 return isAddString(lhs) || isAddString(rhs); 1751 } 1752 1753 return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString(); 1754 } 1755 1756 private void checkThisAssignment(final BinaryNode binaryNode) { 1757 if (binaryNode.isAssignment()) { 1758 if (binaryNode.lhs() instanceof AccessNode) { 1759 final AccessNode accessNode = (AccessNode) binaryNode.lhs(); 1760 1761 if (accessNode.getBase().getSymbol().isThis()) { 1762 lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName()); 1763 } 1764 } 1765 } 1766 } 1767 }); 1768 lc.replace(currentFunctionNode, newFunctionNode); 1769 currentFunctionNode = newFunctionNode; 1770 } while (!changed.isEmpty()); 1771 1772 return currentFunctionNode; 1773 } 1774 1775 private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) { 1776 return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType()); 1777 } 1778 1779 private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) { 1780 //e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType()) is the coerce type 1781 final Expression lhs = binaryNode.lhs(); 1782 1783 newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType 1784 1785 return end(ensureSymbol(destType, binaryNode)); 1786 } 1787 1788 private Expression ensureSymbol(final Type type, final Expression expr) { 1789 LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type); 1790 return temporarySymbols.ensureSymbol(lc, type, expr); 1791 } 1792 1793 private Symbol newInternal(final String name, final Type type) { 1794 final Symbol iter = defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL); 1795 iter.setType(type); // NASHORN-73 1796 return iter; 1797 } 1798 1799 private static void newType(final Symbol symbol, final Type type) { 1800 final Type oldType = symbol.getSymbolType(); 1801 symbol.setType(type); 1802 1803 if (symbol.getSymbolType() != oldType) { 1804 LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")"); 1805 } 1806 1807 if (symbol.isParam()) { 1808 symbol.setType(type); 1809 LOG.info("Param type change ", symbol); 1810 } 1811 } 1812 1813 private void pushLocalsFunction() { 1814 localDefs.push(new HashSet<String>()); 1815 localUses.push(new HashSet<String>()); 1816 } 1817 1818 private void pushLocalsBlock() { 1819 localDefs.push(new HashSet<>(localDefs.peek())); 1820 localUses.push(new HashSet<>(localUses.peek())); 1821 } 1822 1823 private void popLocals() { 1824 localDefs.pop(); 1825 localUses.pop(); 1826 } 1827 1828 private boolean isLocalDef(final String name) { 1829 return localDefs.peek().contains(name); 1830 } 1831 1832 private void addLocalDef(final String name) { 1833 LOG.info("Adding local def of symbol: '", name, "'"); 1834 localDefs.peek().add(name); 1835 } 1836 1837 private void removeLocalDef(final String name) { 1838 LOG.info("Removing local def of symbol: '", name, "'"); 1839 localDefs.peek().remove(name); 1840 } 1841 1842 private boolean isLocalUse(final String name) { 1843 return localUses.peek().contains(name); 1844 } 1845 1846 private void addLocalUse(final String name) { 1847 LOG.info("Adding local use of symbol: '", name, "'"); 1848 localUses.peek().add(name); 1849 } 1850 1851 /** 1852 * Pessimistically promote all symbols in current function node to Object types 1853 * This is done when the function contains unevaluated black boxes such as 1854 * lazy sub-function nodes that have not been compiled. 1855 * 1856 * @param body body for the function node we are leaving 1857 */ 1858 private static void objectifySymbols(final Block body) { 1859 body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 1860 private void toObject(final Block block) { 1861 for (final Symbol symbol : block.getSymbols()) { 1862 if (!symbol.isTemp()) { 1863 newType(symbol, Type.OBJECT); 1864 } 1865 } 1866 } 1867 1868 @Override 1869 public boolean enterBlock(final Block block) { 1870 toObject(block); 1871 return true; 1872 } 1873 1874 @Override 1875 public boolean enterFunctionNode(final FunctionNode node) { 1876 return false; 1877 } 1878 }); 1879 } 1880 1881 private static String name(final Node node) { 1882 final String cn = node.getClass().getName(); 1883 int lastDot = cn.lastIndexOf('.'); 1884 if (lastDot == -1) { 1885 return cn; 1886 } 1887 return cn.substring(lastDot + 1); 1888 } 1889 1890 private boolean start(final Node node) { 1891 return start(node, true); 1892 } 1893 1894 private boolean start(final Node node, final boolean printNode) { 1895 if (DEBUG) { 1896 final StringBuilder sb = new StringBuilder(); 1897 1898 sb.append("[ENTER "). 1899 append(name(node)). 1900 append("] "). 1901 append(printNode ? node.toString() : ""). 1902 append(" in '"). 1903 append(lc.getCurrentFunction().getName()). 1904 append("'"); 1905 LOG.info(sb); 1906 LOG.indent(); 1907 } 1908 1909 return true; 1910 } 1911 1912 private <T extends Node> T end(final T node) { 1913 return end(node, true); 1914 } 1915 1916 private <T extends Node> T end(final T node, final boolean printNode) { 1917 if(node instanceof Statement) { 1918 // If we're done with a statement, all temporaries can be reused. 1919 temporarySymbols.reuse(); 1920 } 1921 if (DEBUG) { 1922 final StringBuilder sb = new StringBuilder(); 1923 1924 sb.append("[LEAVE "). 1925 append(name(node)). 1926 append("] "). 1927 append(printNode ? node.toString() : ""). 1928 append(" in '"). 1929 append(lc.getCurrentFunction().getName()). 1930 append('\''); 1931 1932 if (node instanceof Expression) { 1933 final Symbol symbol = ((Expression)node).getSymbol(); 1934 if (symbol == null) { 1935 sb.append(" <NO SYMBOL>"); 1936 } else { 1937 sb.append(" <symbol=").append(symbol).append('>'); 1938 } 1939 } 1940 1941 LOG.unindent(); 1942 LOG.info(sb); 1943 } 1944 1945 return node; 1946 } 1947 } --- EOF ---