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