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