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