rev 1901 : 8135251: Use Unsafe.defineAnonymousClass for loading Nashorn script code
Reviewed-by: hannesw, lagergren, sundar

   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     static final CompilationPhase PROGRAM_POINT_PHASE = new ProgramPointPhase();
 161 
 162     private static final class CacheAstPhase extends CompilationPhase {
 163         @Override
 164         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 165             if (!compiler.isOnDemandCompilation()) {
 166                 // Only do this on initial preprocessing of the source code. For on-demand compilations from
 167                 // source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function
 168                 // being compiled.
 169                 transformFunction(fn, new CacheAst(compiler));
 170             }
 171             // NOTE: we're returning the original fn as we have destructively modified the cached functions by
 172             // removing their bodies. This step is associating FunctionNode objects with
 173             // RecompilableScriptFunctionData; it's not really modifying the AST.
 174             return fn;
 175         }
 176 
 177         @Override
 178         public String toString() {
 179             return "'Cache ASTs'";
 180         }
 181     };
 182 
 183     static final CompilationPhase CACHE_AST_PHASE = new CacheAstPhase();
 184 
 185     private static final class SymbolAssignmentPhase extends CompilationPhase {
 186         @Override
 187         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 188             return transformFunction(fn, new AssignSymbols(compiler));
 189         }
 190 
 191         @Override
 192         public String toString() {
 193             return "'Symbol Assignment'";
 194         }
 195     };
 196 
 197     static final CompilationPhase SYMBOL_ASSIGNMENT_PHASE = new SymbolAssignmentPhase();
 198 
 199     private static final class ScopeDepthComputationPhase extends CompilationPhase {
 200         @Override
 201         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 202             return transformFunction(fn, new FindScopeDepths(compiler));
 203         }
 204 
 205         @Override
 206         public String toString() {
 207             return "'Scope Depth Computation'";
 208         }
 209     };
 210 
 211     static final CompilationPhase SCOPE_DEPTH_COMPUTATION_PHASE = new ScopeDepthComputationPhase();
 212 
 213     private static final class DeclareLocalSymbolsPhase extends CompilationPhase {
 214         @Override
 215         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 216             // It's not necessary to guard the marking of symbols as locals with this "if" condition for
 217             // correctness, it's just an optimization -- runtime type calculation is not used when the compilation
 218             // is not an on-demand optimistic compilation, so we can skip locals marking then.
 219             if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
 220                 fn.getBody().accept(new SimpleNodeVisitor() {
 221                     @Override
 222                     public boolean enterFunctionNode(final FunctionNode functionNode) {
 223                         // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand
 224                         // compilation, and we're skipping parsing the function bodies for nested functions, this
 225                         // basically only means their parameters. It'd be enough to mistakenly declare to be a local a
 226                         // symbol in the outer function named the same as one of the parameters, though.
 227                         return false;
 228                     };
 229                     @Override
 230                     public boolean enterBlock(final Block block) {
 231                         for (final Symbol symbol: block.getSymbols()) {
 232                             if (!symbol.isScope()) {
 233                                 compiler.declareLocalSymbol(symbol.getName());
 234                             }
 235                         }
 236                         return true;
 237                     };
 238                 });
 239             }
 240             return fn;
 241         }
 242 
 243         @Override
 244         public String toString() {
 245             return "'Local Symbols Declaration'";
 246         }
 247     };
 248 
 249     static final CompilationPhase DECLARE_LOCAL_SYMBOLS_PHASE = new DeclareLocalSymbolsPhase();
 250 
 251     private static final class OptimisticTypeAssignmentPhase extends CompilationPhase {
 252         @Override
 253         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 254             if (compiler.useOptimisticTypes()) {
 255                 return transformFunction(fn, new OptimisticTypesCalculator(compiler));
 256             }
 257             return fn;
 258         }
 259 
 260         @Override
 261         public String toString() {
 262             return "'Optimistic Type Assignment'";
 263         }
 264     }
 265 
 266     static final CompilationPhase OPTIMISTIC_TYPE_ASSIGNMENT_PHASE = new OptimisticTypeAssignmentPhase();
 267 
 268     private static final class LocalVariableTypeCalculationPhase extends CompilationPhase {
 269         @Override
 270         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 271             final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler));
 272             final ScriptEnvironment senv = compiler.getScriptEnvironment();
 273             final PrintWriter       err  = senv.getErr();
 274 
 275             //TODO separate phase for the debug printouts for abstraction and clarity
 276             if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) {
 277                 err.println("Lower AST for: " + quote(newFunctionNode.getName()));
 278                 err.println(new ASTWriter(newFunctionNode));
 279             }
 280 
 281             if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) {
 282                 err.println("Lower AST for: " + quote(newFunctionNode.getName()));
 283                 err.println(new PrintVisitor(newFunctionNode));
 284             }
 285 
 286             return newFunctionNode;
 287         }
 288 
 289         @Override
 290         public String toString() {
 291             return "'Local Variable Type Calculation'";
 292         }
 293     };
 294 
 295     static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase();
 296 
 297     private static final class ReuseCompileUnitsPhase extends CompilationPhase {
 298         @Override
 299         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 300             assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods";
 301 
 302             final Map<CompileUnit, CompileUnit> map = new HashMap<>();
 303             final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet();
 304 
 305             final DebugLogger log = compiler.getLogger();
 306 
 307             log.fine("Clearing bytecode cache");
 308             compiler.clearBytecode();
 309 
 310             for (final CompileUnit oldUnit : compiler.getCompileUnits()) {
 311                 assert map.get(oldUnit) == null;
 312                 final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
 313                 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit);
 314                 map.put(oldUnit, newUnit);
 315                 assert newUnit != null;
 316                 newUnits.add(newUnit);
 317             }
 318 
 319             log.fine("Replacing compile units in Compiler...");
 320             compiler.replaceCompileUnits(newUnits);
 321             log.fine("Done");
 322 
 323             //replace old compile units in function nodes, if any are assigned,
 324             //for example by running the splitter on this function node in a previous
 325             //partial code generation
 326             final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() {
 327                 @Override
 328                 CompileUnit getReplacement(final CompileUnit original) {
 329                     return map.get(original);
 330                 }
 331 
 332                 @Override
 333                 public Node leaveDefault(final Node node) {
 334                     return node.ensureUniqueLabels(lc);
 335                 }
 336             });
 337 
 338             return newFunctionNode;
 339         }
 340 
 341         @Override
 342         public String toString() {
 343             return "'Reuse Compile Units'";
 344         }
 345     }
 346 
 347     /**
 348      * Reuse compile units, if they are already present. We are using the same compiler
 349      * to recompile stuff
 350      */
 351     static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase();
 352 
 353     private static final class ReinitializeCachedPhase extends CompilationPhase {
 354         @Override
 355         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 356             final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet();
 357             final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>();
 358 
 359             // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase
 360             // will use that as the root class.
 361             createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases);
 362 
 363             final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() {
 364                 @Override
 365                 CompileUnit getReplacement(final CompileUnit oldUnit) {
 366                     final CompileUnit existing = unitMap.get(oldUnit);
 367                     if (existing != null) {
 368                         return existing;
 369                     }
 370                     return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases);
 371                 }
 372 
 373                 @Override
 374                 public Node leaveFunctionNode(final FunctionNode fn2) {
 375                     return super.leaveFunctionNode(
 376                             // restore flags for deserialized nested function nodes
 377                             compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2));
 378                 };
 379             });
 380             compiler.replaceCompileUnits(unitSet);
 381             return newFn;
 382         }
 383 
 384         private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet,
 385                 final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) {
 386             final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
 387             unitMap.put(oldUnit, newUnit);
 388             unitSet.add(newUnit);
 389             return newUnit;
 390         }
 391 
 392         @Override
 393         public String toString() {
 394             return "'Reinitialize cached'";
 395         }
 396     }
 397 
 398     static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase();
 399 
 400     private static final class BytecodeGenerationPhase extends CompilationPhase {
 401         @Override
 402         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 403             final ScriptEnvironment senv = compiler.getScriptEnvironment();
 404 
 405             FunctionNode newFunctionNode = fn;
 406 
 407             //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped
 408             //in CodeGeneration - the rest can be used as a working "is compile unit used" metric
 409             fn.getCompileUnit().setUsed();
 410 
 411             compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
 412 
 413             final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
 414 
 415             try {
 416                 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
 417                 // in the lazy + optimistic world. See CodeGenerator.skipFunction().
 418                 newFunctionNode = transformFunction(newFunctionNode, codegen);
 419                 codegen.generateScopeCalls();
 420             } catch (final VerifyError e) {
 421                 if (senv._verify_code || senv._print_code) {
 422                     senv.getErr().println(e.getClass().getSimpleName() + ": "  + e.getMessage());
 423                     if (senv._dump_on_error) {
 424                         e.printStackTrace(senv.getErr());
 425                     }
 426                 } else {
 427                     throw e;
 428                 }
 429             } catch (final Throwable e) {
 430                 // Provide source file and line number being compiled when the assertion occurred
 431                 throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e);
 432             }
 433 
 434             for (final CompileUnit compileUnit : compiler.getCompileUnits()) {
 435                 final ClassEmitter classEmitter = compileUnit.getClassEmitter();
 436                 classEmitter.end();
 437 
 438                 if (!compileUnit.isUsed()) {
 439                     compiler.getLogger().fine("Skipping unused compile unit ", compileUnit);
 440                     continue;
 441                 }
 442 
 443                 final byte[] bytecode = classEmitter.toByteArray();
 444                 assert bytecode != null;
 445 
 446                 final String className = compileUnit.getUnitClassName();
 447                 compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used
 448 
 449                 CompileUnit.increaseEmitCount();
 450 
 451                 // should we verify the generated code?
 452                 if (senv._verify_code) {
 453                     compiler.getCodeInstaller().verify(bytecode);
 454                 }
 455 
 456                 DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className);
 457             }
 458 
 459             return newFunctionNode;
 460         }
 461 
 462         @Override
 463         public String toString() {
 464             return "'Bytecode Generation'";
 465         }
 466     }
 467 
 468     /**
 469      * Bytecode generation:
 470      *
 471      * Generate the byte code class(es) resulting from the compiled FunctionNode
 472      */
 473     static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase();
 474 
 475     private static final class InstallPhase extends CompilationPhase {
 476         @Override
 477         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
 478             final DebugLogger log = compiler.getLogger();
 479 
 480             final Map<String, Class<?>> installedClasses = new LinkedHashMap<>();
 481 
 482             boolean first = true;
 483             Class<?> rootClass = null;
 484             long length = 0L;
 485 
 486             final CodeInstaller origCodeInstaller = compiler.getCodeInstaller();
 487             final Map<String, byte[]> bytecode = compiler.getBytecode();
 488             final CodeInstaller codeInstaller = bytecode.size() > 1 ? origCodeInstaller.getMultiClassCodeInstaller() : origCodeInstaller;
 489 
 490             for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
 491                 final String className = entry.getKey();
 492                 //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName();
 493                 final byte[] code = entry.getValue();
 494                 length += code.length;
 495 
 496                 final Class<?> clazz = codeInstaller.install(className, code);
 497                 if (first) {
 498                     rootClass = clazz;
 499                     first = false;
 500                 }
 501                 installedClasses.put(className, clazz);
 502             }
 503 
 504             if (rootClass == null) {
 505                 throw new CompilationException("Internal compiler error: root class not found!");
 506             }
 507 
 508             final Object[] constants = compiler.getConstantData().toArray();
 509             codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants);
 510 
 511             // initialize transient fields on recompilable script function data
 512             for (final Object constant: constants) {
 513                 if (constant instanceof RecompilableScriptFunctionData) {
 514                     ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller);
 515                 }
 516             }
 517 
 518             // initialize function in the compile units
 519             for (final CompileUnit unit : compiler.getCompileUnits()) {
 520                 if (!unit.isUsed()) {
 521                     continue;
 522                 }
 523                 unit.setCode(installedClasses.get(unit.getUnitClassName()));
 524                 unit.initializeFunctionsCode();
 525             }
 526 
 527             if (log.isEnabled()) {
 528                 final StringBuilder sb = new StringBuilder();
 529 
 530                 sb.append("Installed class '").
 531                     append(rootClass.getSimpleName()).
 532                     append('\'').
 533                     append(" [").
 534                     append(rootClass.getName()).
 535                     append(", size=").
 536                     append(length).
 537                     append(" bytes, ").
 538                     append(compiler.getCompileUnits().size()).
 539                     append(" compile unit(s)]");
 540 
 541                 log.fine(sb.toString());
 542             }
 543 
 544             return fn.setRootClass(null, rootClass);
 545         }
 546 
 547         @Override
 548         public String toString() {
 549             return "'Class Installation'";
 550         }
 551     }
 552 
 553     static final CompilationPhase INSTALL_PHASE = new InstallPhase();
 554 
 555     /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
 556     private long startTime;
 557 
 558     /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
 559     private long endTime;
 560 
 561     /** boolean that is true upon transform completion */
 562     private boolean isFinished;
 563 
 564     private CompilationPhase() {}
 565 
 566     /**
 567      * Start a compilation phase
 568      * @param compiler the compiler to use
 569      * @param functionNode function to compile
 570      * @return function node
 571      */
 572     protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) {
 573         compiler.getLogger().indent();
 574         startTime = System.nanoTime();
 575 
 576          return functionNode;
 577      }
 578 
 579     /**
 580      * End a compilation phase
 581      * @param compiler the compiler
 582      * @param functionNode function node to compile
 583      * @return function node
 584      */
 585     protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) {
 586         compiler.getLogger().unindent();
 587         endTime = System.nanoTime();
 588         compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime);
 589 
 590         isFinished = true;
 591         return functionNode;
 592     }
 593 
 594     boolean isFinished() {
 595         return isFinished;
 596     }
 597 
 598     long getStartTime() {
 599         return startTime;
 600     }
 601 
 602     long getEndTime() {
 603         return endTime;
 604     }
 605 
 606     abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException;
 607 
 608     /**
 609      * Apply a transform to a function node, returning the transformed function node. If the transform is not
 610      * applicable, an exception is thrown. Every transform requires the function to have a certain number of
 611      * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor
 612      * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states.
 613      *
 614      * @param compiler     compiler
 615      * @param phases       current complete pipeline of which this phase is one
 616      * @param functionNode function node to transform
 617      *
 618      * @return transformed function node
 619      *
 620      * @throws CompilationException if function node lacks the state required to run the transform on it
 621      */
 622     final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException {
 623         assert phases.contains(this);
 624 
 625         return end(compiler, transform(compiler, phases, begin(compiler, functionNode)));
 626     }
 627 
 628     private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) {
 629         return (FunctionNode) fn.accept(visitor);
 630     }
 631 
 632     private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) {
 633         final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
 634         if (phases.isRestOfCompilation()) {
 635             sb.append("$restOf");
 636         }
 637         //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what
 638         //fills those out anyway. Thus no need for a copy constructor
 639         return compiler.createCompileUnit(sb.toString(), 0);
 640     }
 641 }
--- EOF ---