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