CompilationPhase.java revision 1040:cc3000241e57
1/* 2 * Copyright (c) 2010, 2014, 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 26package jdk.nashorn.internal.codegen; 27 28import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED; 29import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED; 30import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED; 31import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED; 32import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED; 33import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED; 34import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED; 35import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.OPTIMISTIC_TYPES_ASSIGNED; 36import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED; 37import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED; 38import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; 39import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED; 40import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; 41import java.io.PrintWriter; 42import java.util.ArrayList; 43import java.util.EnumSet; 44import java.util.HashMap; 45import java.util.LinkedHashMap; 46import java.util.List; 47import java.util.Map; 48import java.util.Map.Entry; 49import java.util.Set; 50import jdk.nashorn.internal.AssertsEnabled; 51import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 52import jdk.nashorn.internal.ir.FunctionNode; 53import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 54import jdk.nashorn.internal.ir.LexicalContext; 55import jdk.nashorn.internal.ir.LiteralNode; 56import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 57import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; 58import jdk.nashorn.internal.ir.Node; 59import jdk.nashorn.internal.ir.SplitNode; 60import jdk.nashorn.internal.ir.debug.ASTWriter; 61import jdk.nashorn.internal.ir.debug.PrintVisitor; 62import jdk.nashorn.internal.ir.visitor.NodeVisitor; 63import jdk.nashorn.internal.runtime.CodeInstaller; 64import jdk.nashorn.internal.runtime.FunctionInitializer; 65import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; 66import jdk.nashorn.internal.runtime.ScriptEnvironment; 67import jdk.nashorn.internal.runtime.logging.DebugLogger; 68 69/** 70 * A compilation phase is a step in the processes of turning a JavaScript 71 * FunctionNode into bytecode. It has an optional return value. 72 */ 73enum CompilationPhase { 74 /** 75 * Constant folding pass Simple constant folding that will make elementary 76 * constructs go away 77 */ 78 CONSTANT_FOLDING_PHASE( 79 EnumSet.of( 80 INITIALIZED, 81 PARSED)) { 82 @Override 83 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 84 return (FunctionNode)fn.accept(new FoldConstants(compiler)); 85 } 86 87 @Override 88 public String toString() { 89 return "'Constant Folding'"; 90 } 91 }, 92 93 /** 94 * Lower (Control flow pass) Finalizes the control flow. Clones blocks for 95 * finally constructs and similar things. Establishes termination criteria 96 * for nodes Guarantee return instructions to method making sure control 97 * flow cannot fall off the end. Replacing high level nodes with lower such 98 * as runtime nodes where applicable. 99 */ 100 LOWERING_PHASE( 101 EnumSet.of( 102 INITIALIZED, 103 PARSED, 104 CONSTANT_FOLDED)) { 105 @Override 106 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 107 return (FunctionNode)fn.accept(new Lower(compiler)); 108 } 109 110 @Override 111 public String toString() { 112 return "'Control Flow Lowering'"; 113 } 114 }, 115 116 /** 117 * Phase used only when doing optimistic code generation. It assigns all potentially 118 * optimistic ops a program point so that an UnwarrantedException knows from where 119 * a guess went wrong when creating the continuation to roll back this execution 120 */ 121 PROGRAM_POINT_PHASE( 122 EnumSet.of( 123 INITIALIZED, 124 PARSED, 125 CONSTANT_FOLDED, 126 LOWERED)) { 127 @Override 128 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 129 return (FunctionNode)fn.accept(new ProgramPoints()); 130 } 131 132 @Override 133 public String toString() { 134 return "'Program Point Calculation'"; 135 } 136 }, 137 138 TRANSFORM_BUILTINS_PHASE( 139 EnumSet.of( 140 INITIALIZED, 141 PARSED, 142 CONSTANT_FOLDED, 143 LOWERED)) { 144 //we only do this if we have a param type map, otherwise this is not a specialized recompile 145 @Override 146 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 147 final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new ApplySpecialization(compiler)); 148 return (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 149 @Override 150 public Node leaveFunctionNode(final FunctionNode node) { 151 return node.setState(lc, BUILTINS_TRANSFORMED); 152 } 153 }); 154 } 155 156 @Override 157 public String toString() { 158 return "'Builtin Replacement'"; 159 } 160 }, 161 162 /** 163 * Splitter Split the AST into several compile units based on a heuristic size calculation. 164 * Split IR can lead to scope information being changed. 165 */ 166 SPLITTING_PHASE( 167 EnumSet.of( 168 INITIALIZED, 169 PARSED, 170 CONSTANT_FOLDED, 171 LOWERED, 172 BUILTINS_TRANSFORMED)) { 173 @Override 174 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 175 final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L); 176 177 FunctionNode newFunctionNode; 178 179 //ensure elementTypes, postsets and presets exist for splitter and arraynodes 180 newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 181 @Override 182 public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) { 183 return literalNode.initialize(lc); 184 } 185 }); 186 187 newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true); 188 189 assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit; 190 assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName()); 191 192 return newFunctionNode; 193 } 194 195 @Override 196 public String toString() { 197 return "'Code Splitting'"; 198 } 199 }, 200 201 SYMBOL_ASSIGNMENT_PHASE( 202 EnumSet.of( 203 INITIALIZED, 204 PARSED, 205 CONSTANT_FOLDED, 206 LOWERED, 207 BUILTINS_TRANSFORMED, 208 SPLIT)) { 209 @Override 210 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 211 return (FunctionNode)fn.accept(new AssignSymbols(compiler)); 212 } 213 214 @Override 215 public String toString() { 216 return "'Symbol Assignment'"; 217 } 218 }, 219 220 SCOPE_DEPTH_COMPUTATION_PHASE( 221 EnumSet.of( 222 INITIALIZED, 223 PARSED, 224 CONSTANT_FOLDED, 225 LOWERED, 226 BUILTINS_TRANSFORMED, 227 SPLIT, 228 SYMBOLS_ASSIGNED)) { 229 @Override 230 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 231 return (FunctionNode)fn.accept(new FindScopeDepths(compiler)); 232 } 233 234 @Override 235 public String toString() { 236 return "'Scope Depth Computation'"; 237 } 238 }, 239 240 OPTIMISTIC_TYPE_ASSIGNMENT_PHASE( 241 EnumSet.of( 242 INITIALIZED, 243 PARSED, 244 CONSTANT_FOLDED, 245 LOWERED, 246 BUILTINS_TRANSFORMED, 247 SPLIT, 248 SYMBOLS_ASSIGNED, 249 SCOPE_DEPTHS_COMPUTED)) { 250 @Override 251 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 252 if (compiler.useOptimisticTypes()) { 253 return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler)); 254 } 255 return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED); 256 } 257 258 @Override 259 public String toString() { 260 return "'Optimistic Type Assignment'"; 261 } 262 }, 263 264 LOCAL_VARIABLE_TYPE_CALCULATION_PHASE( 265 EnumSet.of( 266 INITIALIZED, 267 PARSED, 268 CONSTANT_FOLDED, 269 LOWERED, 270 BUILTINS_TRANSFORMED, 271 SPLIT, 272 SYMBOLS_ASSIGNED, 273 SCOPE_DEPTHS_COMPUTED, 274 OPTIMISTIC_TYPES_ASSIGNED)) { 275 @Override 276 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 277 final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler)); 278 279 final ScriptEnvironment senv = compiler.getScriptEnvironment(); 280 final PrintWriter err = senv.getErr(); 281 282 //TODO separate phase for the debug printouts for abstraction and clarity 283 if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) { 284 err.println("Lower AST for: " + quote(newFunctionNode.getName())); 285 err.println(new ASTWriter(newFunctionNode)); 286 } 287 288 if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) { 289 err.println("Lower AST for: " + quote(newFunctionNode.getName())); 290 err.println(new PrintVisitor(newFunctionNode)); 291 } 292 293 return newFunctionNode; 294 } 295 296 @Override 297 public String toString() { 298 return "'Local Variable Type Calculation'"; 299 } 300 }, 301 302 303 /** 304 * Reuse compile units, if they are already present. We are using the same compiler 305 * to recompile stuff 306 */ 307 REUSE_COMPILE_UNITS_PHASE( 308 EnumSet.of( 309 INITIALIZED, 310 PARSED, 311 CONSTANT_FOLDED, 312 LOWERED, 313 BUILTINS_TRANSFORMED, 314 SPLIT, 315 SYMBOLS_ASSIGNED, 316 SCOPE_DEPTHS_COMPUTED, 317 OPTIMISTIC_TYPES_ASSIGNED, 318 LOCAL_VARIABLE_TYPES_CALCULATED)) { 319 @Override 320 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 321 assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods"; 322 323 final Map<CompileUnit, CompileUnit> map = new HashMap<>(); 324 final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet(); 325 326 final DebugLogger log = compiler.getLogger(); 327 328 log.fine("Clearing bytecode cache"); 329 compiler.clearBytecode(); 330 331 for (final CompileUnit oldUnit : compiler.getCompileUnits()) { 332 assert map.get(oldUnit) == null; 333 final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); 334 if (phases.isRestOfCompilation()) { 335 sb.append("$restOf"); 336 } 337 //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what 338 //fills those out anyway. Thus no need for a copy constructor 339 final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); 340 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); 341 map.put(oldUnit, newUnit); 342 assert newUnit != null; 343 newUnits.add(newUnit); 344 } 345 346 log.fine("Replacing compile units in Compiler..."); 347 compiler.replaceCompileUnits(newUnits); 348 log.fine("Done"); 349 350 //replace old compile units in function nodes, if any are assigned, 351 //for example by running the splitter on this function node in a previous 352 //partial code generation 353 final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 354 @Override 355 public Node leaveFunctionNode(final FunctionNode node) { 356 final CompileUnit oldUnit = node.getCompileUnit(); 357 assert oldUnit != null : "no compile unit in function node"; 358 359 final CompileUnit newUnit = map.get(oldUnit); 360 assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; 361 362 log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); 363 return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED); 364 } 365 366 @Override 367 public Node leaveSplitNode(final SplitNode node) { 368 final CompileUnit oldUnit = node.getCompileUnit(); 369 assert oldUnit != null : "no compile unit in function node"; 370 371 final CompileUnit newUnit = map.get(oldUnit); 372 assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; 373 374 log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); 375 return node.setCompileUnit(lc, newUnit); 376 } 377 378 @Override 379 public Node leaveLiteralNode(final LiteralNode<?> node) { 380 if (node instanceof ArrayLiteralNode) { 381 final ArrayLiteralNode aln = (ArrayLiteralNode)node; 382 if (aln.getUnits() == null) { 383 return node; 384 } 385 final List<ArrayUnit> newArrayUnits = new ArrayList<>(); 386 for (final ArrayUnit au : aln.getUnits()) { 387 final CompileUnit newUnit = map.get(au.getCompileUnit()); 388 assert newUnit != null; 389 newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi())); 390 } 391 return aln.setUnits(lc, newArrayUnits); 392 } 393 return node; 394 } 395 396 @Override 397 public Node leaveDefault(final Node node) { 398 return node.ensureUniqueLabels(lc); 399 } 400 }); 401 402 return newFunctionNode; 403 } 404 405 @Override 406 public String toString() { 407 return "'Reuse Compile Units'"; 408 } 409 }, 410 411 /** 412 * Bytecode generation: 413 * 414 * Generate the byte code class(es) resulting from the compiled FunctionNode 415 */ 416 BYTECODE_GENERATION_PHASE( 417 EnumSet.of( 418 INITIALIZED, 419 PARSED, 420 CONSTANT_FOLDED, 421 LOWERED, 422 BUILTINS_TRANSFORMED, 423 SPLIT, 424 SYMBOLS_ASSIGNED, 425 SCOPE_DEPTHS_COMPUTED, 426 OPTIMISTIC_TYPES_ASSIGNED, 427 LOCAL_VARIABLE_TYPES_CALCULATED)) { 428 429 @Override 430 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 431 final ScriptEnvironment senv = compiler.getScriptEnvironment(); 432 433 FunctionNode newFunctionNode = fn; 434 435 //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped 436 //in CodeGeneration - the rest can be used as a working "is compile unit used" metric 437 fn.getCompileUnit().setUsed(); 438 439 compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); 440 441 final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); 442 443 try { 444 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program 445 // in the lazy + optimistic world. See CodeGenerator.skipFunction(). 446 newFunctionNode = ((FunctionNode)newFunctionNode.accept(codegen)).setState(null, BYTECODE_GENERATED); 447 codegen.generateScopeCalls(); 448 } catch (final VerifyError e) { 449 if (senv._verify_code || senv._print_code) { 450 senv.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage()); 451 if (senv._dump_on_error) { 452 e.printStackTrace(senv.getErr()); 453 } 454 } else { 455 throw e; 456 } 457 } catch (final Throwable e) { 458 // Provide source file and line number being compiled when the assertion occurred 459 throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e); 460 } 461 462 for (final CompileUnit compileUnit : compiler.getCompileUnits()) { 463 final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 464 classEmitter.end(); 465 466 if (!compileUnit.isUsed()) { 467 compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); 468 continue; 469 } 470 471 final byte[] bytecode = classEmitter.toByteArray(); 472 assert bytecode != null; 473 474 final String className = compileUnit.getUnitClassName(); 475 compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used 476 477 CompileUnit.increaseEmitCount(); 478 479 // should we verify the generated code? 480 if (senv._verify_code) { 481 compiler.getCodeInstaller().verify(bytecode); 482 } 483 484 DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className); 485 } 486 487 return newFunctionNode; 488 } 489 490 @Override 491 public String toString() { 492 return "'Bytecode Generation'"; 493 } 494 }, 495 496 INSTALL_PHASE( 497 EnumSet.of( 498 INITIALIZED, 499 PARSED, 500 CONSTANT_FOLDED, 501 LOWERED, 502 BUILTINS_TRANSFORMED, 503 SPLIT, 504 SYMBOLS_ASSIGNED, 505 SCOPE_DEPTHS_COMPUTED, 506 OPTIMISTIC_TYPES_ASSIGNED, 507 LOCAL_VARIABLE_TYPES_CALCULATED, 508 BYTECODE_GENERATED)) { 509 510 @Override 511 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 512 final DebugLogger log = compiler.getLogger(); 513 514 final Map<String, Class<?>> installedClasses = new LinkedHashMap<>(); 515 516 boolean first = true; 517 Class<?> rootClass = null; 518 long length = 0L; 519 520 final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller(); 521 final Map<String, byte[]> bytecode = compiler.getBytecode(); 522 523 for (final Entry<String, byte[]> entry : bytecode.entrySet()) { 524 final String className = entry.getKey(); 525 //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName(); 526 final byte[] code = entry.getValue(); 527 length += code.length; 528 529 final Class<?> clazz = codeInstaller.install(className, code); 530 if (first) { 531 rootClass = clazz; 532 first = false; 533 } 534 installedClasses.put(className, clazz); 535 } 536 537 if (rootClass == null) { 538 throw new CompilationException("Internal compiler error: root class not found!"); 539 } 540 541 final Object[] constants = compiler.getConstantData().toArray(); 542 codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants); 543 544 // initialize transient fields on recompilable script function data 545 for (final Object constant: constants) { 546 if (constant instanceof RecompilableScriptFunctionData) { 547 ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller); 548 } 549 } 550 551 // initialize function in the compile units 552 for (final CompileUnit unit : compiler.getCompileUnits()) { 553 if (!unit.isUsed()) { 554 continue; 555 } 556 unit.setCode(installedClasses.get(unit.getUnitClassName())); 557 } 558 559 if (!compiler.isOnDemandCompilation()) { 560 // Initialize functions 561 final Map<Integer, FunctionInitializer> initializers = compiler.getFunctionInitializers(); 562 if (initializers != null) { 563 for (final Entry<Integer, FunctionInitializer> entry : initializers.entrySet()) { 564 final FunctionInitializer initializer = entry.getValue(); 565 initializer.setCode(installedClasses.get(initializer.getClassName())); 566 compiler.getScriptFunctionData(entry.getKey()).initializeCode(initializer); 567 } 568 } 569 } 570 571 if (log.isEnabled()) { 572 final StringBuilder sb = new StringBuilder(); 573 574 sb.append("Installed class '"). 575 append(rootClass.getSimpleName()). 576 append('\''). 577 append(" ["). 578 append(rootClass.getName()). 579 append(", size="). 580 append(length). 581 append(" bytes, "). 582 append(compiler.getCompileUnits().size()). 583 append(" compile unit(s)]"); 584 585 log.fine(sb.toString()); 586 } 587 588 return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED); 589 } 590 591 @Override 592 public String toString() { 593 return "'Class Installation'"; 594 } 595 596 }; 597 598 /** pre conditions required for function node to which this transform is to be applied */ 599 private final EnumSet<CompilationState> pre; 600 601 /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ 602 private long startTime; 603 604 /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ 605 private long endTime; 606 607 /** boolean that is true upon transform completion */ 608 private boolean isFinished; 609 610 private CompilationPhase(final EnumSet<CompilationState> pre) { 611 this.pre = pre; 612 } 613 614 private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) { 615 if (!AssertsEnabled.assertsEnabled()) { 616 return functionNode; 617 } 618 return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 619 @Override 620 public Node leaveFunctionNode(final FunctionNode fn) { 621 return fn.setState(lc, state); 622 } 623 }); 624 } 625 626 /** 627 * Start a compilation phase 628 * @param compiler the compiler to use 629 * @param functionNode function to compile 630 * @return function node 631 */ 632 protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) { 633 compiler.getLogger().indent(); 634 635 assert pre != null; 636 637 if (!functionNode.hasState(pre)) { 638 final StringBuilder sb = new StringBuilder("Compilation phase "); 639 sb.append(this). 640 append(" is not applicable to "). 641 append(quote(functionNode.getName())). 642 append("\n\tFunctionNode state = "). 643 append(functionNode.getState()). 644 append("\n\tRequired state = "). 645 append(this.pre); 646 647 throw new CompilationException(sb.toString()); 648 } 649 650 startTime = System.nanoTime(); 651 652 return functionNode; 653 } 654 655 /** 656 * End a compilation phase 657 * @param compiler the compiler 658 * @param functionNode function node to compile 659 * @return function node 660 */ 661 protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) { 662 compiler.getLogger().unindent(); 663 endTime = System.nanoTime(); 664 compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime); 665 666 isFinished = true; 667 return functionNode; 668 } 669 670 boolean isFinished() { 671 return isFinished; 672 } 673 674 long getStartTime() { 675 return startTime; 676 } 677 678 long getEndTime() { 679 return endTime; 680 } 681 682 abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException; 683 684 /** 685 * Apply a transform to a function node, returning the transfored function node. If the transform is not 686 * applicable, an exception is thrown. Every transform requires the function to have a certain number of 687 * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor 688 * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states. 689 * 690 * @param compiler compiler 691 * @param phases current complete pipeline of which this phase is one 692 * @param functionNode function node to transform 693 * 694 * @return transformed function node 695 * 696 * @throws CompilationException if function node lacks the state required to run the transform on it 697 */ 698 final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException { 699 assert phases.contains(this); 700 701 return end(compiler, transform(compiler, phases, begin(compiler, functionNode))); 702 } 703 704} 705