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