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