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