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