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             final ScriptEnvironment senv = compiler.getScriptEnvironment();
 278             final PrintWriter       err  = senv.getErr();
 279 
 280             //TODO separate phase for the debug printouts for abstraction and clarity
 281             if (senv._print_lower_ast || fn.getDebugFlag(FunctionNode.DEBUG_PRINT_LOWER_AST)) {
 282                 err.println("Lower AST for: " + quote(newFunctionNode.getName()));
 283                 err.println(new ASTWriter(newFunctionNode));
 284             }
 285 
 286             if (senv._print_lower_parse || fn.getDebugFlag(FunctionNode.DEBUG_PRINT_LOWER_PARSE)) {
 287                 err.println("Lower AST for: " + quote(newFunctionNode.getName()));
 288                 err.println(new PrintVisitor(newFunctionNode));
 289             }
 290 
 291             return newFunctionNode;
 292         }
 293 
 294         @Override
 295         public String toString() {
 296             return "'Local Variable Type Calculation'";
 297         }
 298     };
 299 
 300     static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase();
 301 
 302     private static final class ReuseCompileUnitsPhase extends CompilationPhase {
 303         @Override
 304         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 305             assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods";
 306 
 307             final Map<CompileUnit, CompileUnit> map = new HashMap<>();
 308             final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet();
 309 
 310             final DebugLogger log = compiler.getLogger();
 311 
 312             log.fine("Clearing bytecode cache");
 313             compiler.clearBytecode();
 314 
 315             for (final CompileUnit oldUnit : compiler.getCompileUnits()) {
 316                 assert map.get(oldUnit) == null;
 317                 final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
 318                 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit);
 319                 map.put(oldUnit, newUnit);
 320                 assert newUnit != null;
 321                 newUnits.add(newUnit);
 322             }
 323 
 324             log.fine("Replacing compile units in Compiler...");
 325             compiler.replaceCompileUnits(newUnits);
 326             log.fine("Done");
 327 
 328             //replace old compile units in function nodes, if any are assigned,
 329             //for example by running the splitter on this function node in a previous
 330             //partial code generation
 331             final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() {
 332                 @Override
 333                 CompileUnit getReplacement(final CompileUnit original) {
 334                     return map.get(original);
 335                 }
 336 
 337                 @Override
 338                 public Node leaveDefault(final Node node) {
 339                     return node.ensureUniqueLabels(lc);
 340                 }
 341             });
 342 
 343             return newFunctionNode;
 344         }
 345 
 346         @Override
 347         public String toString() {
 348             return "'Reuse Compile Units'";
 349         }
 350     }
 351 
 352     /**
 353      * Reuse compile units, if they are already present. We are using the same compiler
 354      * to recompile stuff
 355      */
 356     static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase();
 357 
 358     private static final class ReinitializeCachedPhase extends CompilationPhase {
 359         @Override
 360         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 361             final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet();
 362             final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>();
 363 
 364             // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase
 365             // will use that as the root class.
 366             createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases);
 367 
 368             final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() {
 369                 @Override
 370                 CompileUnit getReplacement(final CompileUnit oldUnit) {
 371                     final CompileUnit existing = unitMap.get(oldUnit);
 372                     if (existing != null) {
 373                         return existing;
 374                     }
 375                     return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases);
 376                 }
 377 
 378                 @Override
 379                 public Node leaveFunctionNode(final FunctionNode fn2) {
 380                     return super.leaveFunctionNode(
 381                             // restore flags for deserialized nested function nodes
 382                             compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2));
 383                 };
 384             });
 385             compiler.replaceCompileUnits(unitSet);
 386             return newFn;
 387         }
 388 
 389         private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet,
 390                 final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) {
 391             final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
 392             unitMap.put(oldUnit, newUnit);
 393             unitSet.add(newUnit);
 394             return newUnit;
 395         }
 396 
 397         @Override
 398         public String toString() {
 399             return "'Reinitialize cached'";
 400         }
 401     }
 402 
 403     static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase();
 404 
 405     private static final class BytecodeGenerationPhase extends CompilationPhase {
 406         @Override
 407         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 408             final ScriptEnvironment senv = compiler.getScriptEnvironment();
 409 
 410             FunctionNode newFunctionNode = fn;
 411 
 412             //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped
 413             //in CodeGeneration - the rest can be used as a working "is compile unit used" metric
 414             fn.getCompileUnit().setUsed();
 415 
 416             compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
 417 
 418             final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
 419 
 420             try {
 421                 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
 422                 // in the lazy + optimistic world. See CodeGenerator.skipFunction().
 423                 newFunctionNode = transformFunction(newFunctionNode, codegen);
 424                 codegen.generateScopeCalls();
 425             } catch (final VerifyError e) {
 426                 if (senv._verify_code || senv._print_code) {
 427                     senv.getErr().println(e.getClass().getSimpleName() + ": "  + e.getMessage());
 428                     if (senv._dump_on_error) {
 429                         e.printStackTrace(senv.getErr());
 430                     }
 431                 } else {
 432                     throw e;
 433                 }
 434             } catch (final Throwable e) {
 435                 // Provide source file and line number being compiled when the assertion occurred
 436                 throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e);
 437             }
 438 
 439             for (final CompileUnit compileUnit : compiler.getCompileUnits()) {
 440                 final ClassEmitter classEmitter = compileUnit.getClassEmitter();
 441                 classEmitter.end();
 442 
 443                 if (!compileUnit.isUsed()) {
 444                     compiler.getLogger().fine("Skipping unused compile unit ", compileUnit);
 445                     continue;
 446                 }
 447 
 448                 final byte[] bytecode = classEmitter.toByteArray();
 449                 assert bytecode != null;
 450 
 451                 final String className = compileUnit.getUnitClassName();
 452                 compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used
 453 
 454                 CompileUnit.increaseEmitCount();
 455 
 456                 // should we verify the generated code?
 457                 if (senv._verify_code) {
 458                     compiler.getCodeInstaller().verify(bytecode);
 459                 }
 460 
 461                 DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className);
 462             }
 463 
 464             return newFunctionNode;
 465         }
 466 
 467         @Override
 468         public String toString() {
 469             return "'Bytecode Generation'";
 470         }
 471     }
 472 
 473     /**
 474      * Bytecode generation:
 475      *
 476      * Generate the byte code class(es) resulting from the compiled FunctionNode
 477      */
 478     static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase();
 479 
 480     private static final class InstallPhase extends CompilationPhase {
 481         @Override
 482         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 483             final DebugLogger log = compiler.getLogger();
 484 
 485             final Map<String, Class<?>> installedClasses = new LinkedHashMap<>();
 486 
 487             boolean first = true;
 488             Class<?> rootClass = null;
 489             long length = 0L;
 490 
 491             final CodeInstaller origCodeInstaller = compiler.getCodeInstaller();
 492             final Map<String, byte[]> bytecode = compiler.getBytecode();
 493             final CodeInstaller codeInstaller = bytecode.size() > 1 ? origCodeInstaller.getMultiClassCodeInstaller() : origCodeInstaller;
 494 
 495             for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
 496                 final String className = entry.getKey();
 497                 //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName();
 498                 final byte[] code = entry.getValue();
 499                 length += code.length;
 500 
 501                 final Class<?> clazz = codeInstaller.install(className, code);
 502                 if (first) {
 503                     rootClass = clazz;
 504                     first = false;
 505                 }
 506                 installedClasses.put(className, clazz);
 507             }
 508 
 509             if (rootClass == null) {
 510                 throw new CompilationException("Internal compiler error: root class not found!");
 511             }
 512 
 513             final Object[] constants = compiler.getConstantData().toArray();
 514             codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants);
 515 
 516             // initialize transient fields on recompilable script function data
 517             for (final Object constant: constants) {
 518                 if (constant instanceof RecompilableScriptFunctionData) {
 519                     ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller);
 520                 }
 521             }
 522 
 523             // initialize function in the compile units
 524             for (final CompileUnit unit : compiler.getCompileUnits()) {
 525                 if (!unit.isUsed()) {
 526                     continue;
 527                 }
 528                 unit.setCode(installedClasses.get(unit.getUnitClassName()));
 529                 unit.initializeFunctionsCode();
 530             }
 531 
 532             if (log.isEnabled()) {
 533                 final StringBuilder sb = new StringBuilder();
 534 
 535                 sb.append("Installed class '").
 536                     append(rootClass.getSimpleName()).
 537                     append('\'').
 538                     append(" [").
 539                     append(rootClass.getName()).
 540                     append(", size=").
 541                     append(length).
 542                     append(" bytes, ").
 543                     append(compiler.getCompileUnits().size()).
 544                     append(" compile unit(s)]");
 545 
 546                 log.fine(sb.toString());
 547             }
 548 
 549             return fn.setRootClass(null, rootClass);
 550         }
 551 
 552         @Override
 553         public String toString() {
 554             return "'Class Installation'";
 555         }
 556     }
 557 
 558     static final CompilationPhase INSTALL_PHASE = new InstallPhase();
 559 
 560     /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
 561     private long startTime;
 562 
 563     /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
 564     private long endTime;
 565 
 566     /** boolean that is true upon transform completion */
 567     private boolean isFinished;
 568 
 569     private CompilationPhase() {}
 570 
 571     /**
 572      * Start a compilation phase
 573      * @param compiler the compiler to use
 574      * @param functionNode function to compile
 575      * @return function node
 576      */
 577     protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) {
 578         compiler.getLogger().indent();
 579         startTime = System.nanoTime();
 580 
 581          return functionNode;
 582      }
 583 
 584     /**
 585      * End a compilation phase
 586      * @param compiler the compiler
 587      * @param functionNode function node to compile
 588      * @return function node
 589      */
 590     protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) {
 591         compiler.getLogger().unindent();
 592         endTime = System.nanoTime();
 593         compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime);
 594 
 595         isFinished = true;
 596         return functionNode;
 597     }
 598 
 599     boolean isFinished() {
 600         return isFinished;
 601     }
 602 
 603     long getStartTime() {
 604         return startTime;
 605     }
 606 
 607     long getEndTime() {
 608         return endTime;
 609     }
 610 
 611     abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException;
 612 
 613     /**
 614      * Apply a transform to a function node, returning the transformed function node. If the transform is not
 615      * applicable, an exception is thrown. Every transform requires the function to have a certain number of
 616      * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor
 617      * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states.
 618      *
 619      * @param compiler     compiler
 620      * @param phases       current complete pipeline of which this phase is one
 621      * @param functionNode function node to transform
 622      *
 623      * @return transformed function node
 624      *
 625      * @throws CompilationException if function node lacks the state required to run the transform on it
 626      */
 627     final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException {
 628         assert phases.contains(this);
 629 
 630         return end(compiler, transform(compiler, phases, begin(compiler, functionNode)));
 631     }
 632 
 633     private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) {
 634         return (FunctionNode) fn.accept(visitor);
 635     }
 636 
 637     private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) {
 638         final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
 639         if (phases.isRestOfCompilation()) {
 640             sb.append("$restOf");
 641         }
 642         //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what
 643         //fills those out anyway. Thus no need for a copy constructor
 644         return compiler.createCompileUnit(sb.toString(), 0);
 645     }
 646 }