1 /*
2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.nashorn.internal.codegen;
27 
28 import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
29 
30 import java.io.PrintWriter;
31 import java.util.HashMap;
32 import java.util.LinkedHashMap;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Set;
36 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
37 import jdk.nashorn.internal.ir.Block;
38 import jdk.nashorn.internal.ir.FunctionNode;
39 import jdk.nashorn.internal.ir.LiteralNode;
40 import jdk.nashorn.internal.ir.Node;
41 import jdk.nashorn.internal.ir.Symbol;
42 import jdk.nashorn.internal.ir.debug.ASTWriter;
43 import jdk.nashorn.internal.ir.debug.PrintVisitor;
44 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
45 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
46 import jdk.nashorn.internal.runtime.CodeInstaller;
47 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
48 import jdk.nashorn.internal.runtime.ScriptEnvironment;
49 import jdk.nashorn.internal.runtime.logging.DebugLogger;
50 
51 /**
52  * A compilation phase is a step in the processes of turning a JavaScript
53  * FunctionNode into bytecode. It has an optional return value.
54  */
55 abstract class CompilationPhase {
56 
57     private static final class ConstantFoldingPhase extends CompilationPhase {
58         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)59         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
60             return transformFunction(fn, new FoldConstants(compiler));
61         }
62 
63         @Override
toString()64         public String toString() {
65             return "'Constant Folding'";
66         }
67     }
68 
69     /**
70      * Constant folding pass Simple constant folding that will make elementary
71      * constructs go away
72      */
73     static final CompilationPhase CONSTANT_FOLDING_PHASE = new ConstantFoldingPhase();
74 
75     private static final class LoweringPhase extends CompilationPhase {
76         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)77         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
78             return transformFunction(fn, new Lower(compiler));
79         }
80 
81         @Override
toString()82         public String toString() {
83             return "'Control Flow Lowering'";
84         }
85     }
86 
87     /**
88      * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
89      * finally constructs and similar things. Establishes termination criteria
90      * for nodes Guarantee return instructions to method making sure control
91      * flow cannot fall off the end. Replacing high level nodes with lower such
92      * as runtime nodes where applicable.
93      */
94     static final CompilationPhase LOWERING_PHASE = new LoweringPhase();
95 
96     private static final class ApplySpecializationPhase extends CompilationPhase {
97         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)98         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
99             return transformFunction(fn, new ApplySpecialization(compiler));
100         }
101 
102         @Override
toString()103         public String toString() {
104             return "'Builtin Replacement'";
105         }
106     };
107 
108     /**
109      * Phase used to transform Function.prototype.apply.
110      */
111     static final CompilationPhase APPLY_SPECIALIZATION_PHASE = new ApplySpecializationPhase();
112 
113     private static final class SplittingPhase extends CompilationPhase {
114         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)115         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
116             final CompileUnit  outermostCompileUnit = compiler.addCompileUnit(0L);
117 
118             FunctionNode newFunctionNode;
119 
120             //ensure elementTypes, postsets and presets exist for splitter and arraynodes
121             newFunctionNode = transformFunction(fn, new SimpleNodeVisitor() {
122                 @Override
123                 public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) {
124                     return literalNode.initialize(lc);
125                 }
126             });
127 
128             newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true);
129             newFunctionNode = transformFunction(newFunctionNode, new SplitIntoFunctions(compiler));
130             assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
131             assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName());
132 
133             return newFunctionNode;
134         }
135 
136         @Override
toString()137         public String toString() {
138             return "'Code Splitting'";
139         }
140     };
141 
142     /**
143      * Splitter Split the AST into several compile units based on a heuristic size calculation.
144      * Split IR can lead to scope information being changed.
145      */
146     static final CompilationPhase SPLITTING_PHASE = new SplittingPhase();
147 
148     private static final class ProgramPointPhase extends CompilationPhase {
149         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)150         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
151             return transformFunction(fn, new ProgramPoints());
152         }
153 
154         @Override
toString()155         public String toString() {
156             return "'Program Point Calculation'";
157         }
158     };
159 
160     /**
161      * Phase used only when doing optimistic code generation. It assigns all potentially
162      * optimistic ops a program point so that an UnwarrantedException knows from where
163      * a guess went wrong when creating the continuation to roll back this execution
164      */
165     static final CompilationPhase PROGRAM_POINT_PHASE = new ProgramPointPhase();
166 
167     private static final class CacheAstPhase extends CompilationPhase {
168         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)169         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
170             if (!compiler.isOnDemandCompilation()) {
171                 // Only do this on initial preprocessing of the source code. For on-demand compilations from
172                 // source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function
173                 // being compiled.
174                 transformFunction(fn, new CacheAst(compiler));
175             }
176             // NOTE: we're returning the original fn as we have destructively modified the cached functions by
177             // removing their bodies. This step is associating FunctionNode objects with
178             // RecompilableScriptFunctionData; it's not really modifying the AST.
179             return fn;
180         }
181 
182         @Override
toString()183         public String toString() {
184             return "'Cache ASTs'";
185         }
186     };
187 
188     static final CompilationPhase CACHE_AST_PHASE = new CacheAstPhase();
189 
190     private static final class SymbolAssignmentPhase extends CompilationPhase {
191         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)192         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
193             return transformFunction(fn, new AssignSymbols(compiler));
194         }
195 
196         @Override
toString()197         public String toString() {
198             return "'Symbol Assignment'";
199         }
200     };
201 
202     static final CompilationPhase SYMBOL_ASSIGNMENT_PHASE = new SymbolAssignmentPhase();
203 
204     private static final class ScopeDepthComputationPhase extends CompilationPhase {
205         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)206         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
207             return transformFunction(fn, new FindScopeDepths(compiler));
208         }
209 
210         @Override
toString()211         public String toString() {
212             return "'Scope Depth Computation'";
213         }
214     }
215 
216     static final CompilationPhase SCOPE_DEPTH_COMPUTATION_PHASE = new ScopeDepthComputationPhase();
217 
218     private static final class DeclareLocalSymbolsPhase extends CompilationPhase {
219         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)220         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
221             // It's not necessary to guard the marking of symbols as locals with this "if" condition for
222             // correctness, it's just an optimization -- runtime type calculation is not used when the compilation
223             // is not an on-demand optimistic compilation, so we can skip locals marking then.
224             if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
225                 fn.getBody().accept(new SimpleNodeVisitor() {
226                     @Override
227                     public boolean enterFunctionNode(final FunctionNode functionNode) {
228                         // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand
229                         // compilation, and we're skipping parsing the function bodies for nested functions, this
230                         // basically only means their parameters. It'd be enough to mistakenly declare to be a local a
231                         // symbol in the outer function named the same as one of the parameters, though.
232                         return false;
233                     };
234                     @Override
235                     public boolean enterBlock(final Block block) {
236                         for (final Symbol symbol: block.getSymbols()) {
237                             if (!symbol.isScope()) {
238                                 compiler.declareLocalSymbol(symbol.getName());
239                             }
240                         }
241                         return true;
242                     };
243                 });
244             }
245             return fn;
246         }
247 
248         @Override
toString()249         public String toString() {
250             return "'Local Symbols Declaration'";
251         }
252     };
253 
254     static final CompilationPhase DECLARE_LOCAL_SYMBOLS_PHASE = new DeclareLocalSymbolsPhase();
255 
256     private static final class OptimisticTypeAssignmentPhase extends CompilationPhase {
257         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)258         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
259             if (compiler.useOptimisticTypes()) {
260                 return transformFunction(fn, new OptimisticTypesCalculator(compiler));
261             }
262             return fn;
263         }
264 
265         @Override
toString()266         public String toString() {
267             return "'Optimistic Type Assignment'";
268         }
269     }
270 
271     static final CompilationPhase OPTIMISTIC_TYPE_ASSIGNMENT_PHASE = new OptimisticTypeAssignmentPhase();
272 
273     private static final class LocalVariableTypeCalculationPhase extends CompilationPhase {
274         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)275         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
276             final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler,
277                     compiler.getReturnType()));
278             final ScriptEnvironment senv = compiler.getScriptEnvironment();
279             final PrintWriter       err  = senv.getErr();
280 
281             //TODO separate phase for the debug printouts for abstraction and clarity
282             if (senv._print_lower_ast || fn.getDebugFlag(FunctionNode.DEBUG_PRINT_LOWER_AST)) {
283                 err.println("Lower AST for: " + quote(newFunctionNode.getName()));
284                 err.println(new ASTWriter(newFunctionNode));
285             }
286 
287             if (senv._print_lower_parse || fn.getDebugFlag(FunctionNode.DEBUG_PRINT_LOWER_PARSE)) {
288                 err.println("Lower AST for: " + quote(newFunctionNode.getName()));
289                 err.println(new PrintVisitor(newFunctionNode));
290             }
291 
292             return newFunctionNode;
293         }
294 
295         @Override
toString()296         public String toString() {
297             return "'Local Variable Type Calculation'";
298         }
299     };
300 
301     static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase();
302 
303     private static final class ReuseCompileUnitsPhase extends CompilationPhase {
304         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)305         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
306             assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods";
307 
308             final Map<CompileUnit, CompileUnit> map = new HashMap<>();
309             final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet();
310 
311             final DebugLogger log = compiler.getLogger();
312 
313             log.fine("Clearing bytecode cache");
314             compiler.clearBytecode();
315 
316             for (final CompileUnit oldUnit : compiler.getCompileUnits()) {
317                 assert map.get(oldUnit) == null;
318                 final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
319                 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit);
320                 map.put(oldUnit, newUnit);
321                 assert newUnit != null;
322                 newUnits.add(newUnit);
323             }
324 
325             log.fine("Replacing compile units in Compiler...");
326             compiler.replaceCompileUnits(newUnits);
327             log.fine("Done");
328 
329             //replace old compile units in function nodes, if any are assigned,
330             //for example by running the splitter on this function node in a previous
331             //partial code generation
332             final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() {
333                 @Override
334                 CompileUnit getReplacement(final CompileUnit original) {
335                     return map.get(original);
336                 }
337 
338                 @Override
339                 public Node leaveDefault(final Node node) {
340                     return node.ensureUniqueLabels(lc);
341                 }
342             });
343 
344             return newFunctionNode;
345         }
346 
347         @Override
toString()348         public String toString() {
349             return "'Reuse Compile Units'";
350         }
351     }
352 
353     /**
354      * Reuse compile units, if they are already present. We are using the same compiler
355      * to recompile stuff
356      */
357     static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase();
358 
359     private static final class ReinitializeCachedPhase extends CompilationPhase {
360         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)361         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
362             final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet();
363             final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>();
364 
365             // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase
366             // will use that as the root class.
367             createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases);
368 
369             final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() {
370                 @Override
371                 CompileUnit getReplacement(final CompileUnit oldUnit) {
372                     final CompileUnit existing = unitMap.get(oldUnit);
373                     if (existing != null) {
374                         return existing;
375                     }
376                     return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases);
377                 }
378 
379                 @Override
380                 public Node leaveFunctionNode(final FunctionNode fn2) {
381                     return super.leaveFunctionNode(
382                             // restore flags for deserialized nested function nodes
383                             compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2));
384                 };
385             });
386             compiler.replaceCompileUnits(unitSet);
387             return newFn;
388         }
389 
createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet, final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases)390         private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet,
391                 final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) {
392             final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
393             unitMap.put(oldUnit, newUnit);
394             unitSet.add(newUnit);
395             return newUnit;
396         }
397 
398         @Override
toString()399         public String toString() {
400             return "'Reinitialize cached'";
401         }
402     }
403 
404     static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase();
405 
406     private static final class BytecodeGenerationPhase extends CompilationPhase {
407         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)408         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
409             final ScriptEnvironment senv = compiler.getScriptEnvironment();
410 
411             FunctionNode newFunctionNode = fn;
412 
413             //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped
414             //in CodeGeneration - the rest can be used as a working "is compile unit used" metric
415             fn.getCompileUnit().setUsed();
416 
417             compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
418 
419             final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
420 
421             try {
422                 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
423                 // in the lazy + optimistic world. See CodeGenerator.skipFunction().
424                 newFunctionNode = transformFunction(newFunctionNode, codegen);
425                 codegen.generateScopeCalls();
426             } catch (final VerifyError e) {
427                 if (senv._verify_code || senv._print_code) {
428                     senv.getErr().println(e.getClass().getSimpleName() + ": "  + e.getMessage());
429                     if (senv._dump_on_error) {
430                         e.printStackTrace(senv.getErr());
431                     }
432                 } else {
433                     throw e;
434                 }
435             } catch (final Throwable e) {
436                 // Provide source file and line number being compiled when the assertion occurred
437                 throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e);
438             }
439 
440             for (final CompileUnit compileUnit : compiler.getCompileUnits()) {
441                 final ClassEmitter classEmitter = compileUnit.getClassEmitter();
442                 classEmitter.end();
443 
444                 if (!compileUnit.isUsed()) {
445                     compiler.getLogger().fine("Skipping unused compile unit ", compileUnit);
446                     continue;
447                 }
448 
449                 final byte[] bytecode = classEmitter.toByteArray();
450                 assert bytecode != null;
451 
452                 final String className = compileUnit.getUnitClassName();
453                 compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used
454 
455                 CompileUnit.increaseEmitCount();
456 
457                 // should we verify the generated code?
458                 if (senv._verify_code) {
459                     compiler.getCodeInstaller().verify(bytecode);
460                 }
461 
462                 DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className);
463             }
464 
465             return newFunctionNode;
466         }
467 
468         @Override
toString()469         public String toString() {
470             return "'Bytecode Generation'";
471         }
472     }
473 
474     /**
475      * Bytecode generation:
476      *
477      * Generate the byte code class(es) resulting from the compiled FunctionNode
478      */
479     static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase();
480 
481     private static final class InstallPhase extends CompilationPhase {
482         @Override
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn)483         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
484             final DebugLogger log = compiler.getLogger();
485 
486             final Map<String, Class<?>> installedClasses = new LinkedHashMap<>();
487 
488             boolean first = true;
489             Class<?> rootClass = null;
490             long length = 0L;
491 
492             final CodeInstaller origCodeInstaller = compiler.getCodeInstaller();
493             final Map<String, byte[]> bytecode = compiler.getBytecode();
494             final CodeInstaller codeInstaller = bytecode.size() > 1 ? origCodeInstaller.getMultiClassCodeInstaller() : origCodeInstaller;
495 
496             for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
497                 final String className = entry.getKey();
498                 //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName();
499                 final byte[] code = entry.getValue();
500                 length += code.length;
501 
502                 final Class<?> clazz = codeInstaller.install(className, code);
503                 if (first) {
504                     rootClass = clazz;
505                     first = false;
506                 }
507                 installedClasses.put(className, clazz);
508             }
509 
510             if (rootClass == null) {
511                 throw new CompilationException("Internal compiler error: root class not found!");
512             }
513 
514             final Object[] constants = compiler.getConstantData().toArray();
515             codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants);
516 
517             // initialize transient fields on recompilable script function data
518             for (final Object constant: constants) {
519                 if (constant instanceof RecompilableScriptFunctionData) {
520                     ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller);
521                 }
522             }
523 
524             // initialize function in the compile units
525             for (final CompileUnit unit : compiler.getCompileUnits()) {
526                 if (!unit.isUsed()) {
527                     continue;
528                 }
529                 unit.setCode(installedClasses.get(unit.getUnitClassName()));
530                 unit.initializeFunctionsCode();
531             }
532 
533             if (log.isEnabled()) {
534                 final StringBuilder sb = new StringBuilder();
535 
536                 sb.append("Installed class '").
537                     append(rootClass.getSimpleName()).
538                     append('\'').
539                     append(" [").
540                     append(rootClass.getName()).
541                     append(", size=").
542                     append(length).
543                     append(" bytes, ").
544                     append(compiler.getCompileUnits().size()).
545                     append(" compile unit(s)]");
546 
547                 log.fine(sb.toString());
548             }
549 
550             return fn.setRootClass(null, rootClass);
551         }
552 
553         @Override
toString()554         public String toString() {
555             return "'Class Installation'";
556         }
557     }
558 
559     static final CompilationPhase INSTALL_PHASE = new InstallPhase();
560 
561     /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
562     private long startTime;
563 
564     /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
565     private long endTime;
566 
567     /** boolean that is true upon transform completion */
568     private boolean isFinished;
569 
CompilationPhase()570     private CompilationPhase() {}
571 
572     /**
573      * Start a compilation phase
574      * @param compiler the compiler to use
575      * @param functionNode function to compile
576      * @return function node
577      */
begin(final Compiler compiler, final FunctionNode functionNode)578     protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) {
579         compiler.getLogger().indent();
580         startTime = System.nanoTime();
581 
582          return functionNode;
583      }
584 
585     /**
586      * End a compilation phase
587      * @param compiler the compiler
588      * @param functionNode function node to compile
589      * @return function node
590      */
end(final Compiler compiler, final FunctionNode functionNode)591     protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) {
592         compiler.getLogger().unindent();
593         endTime = System.nanoTime();
594         compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime);
595 
596         isFinished = true;
597         return functionNode;
598     }
599 
isFinished()600     boolean isFinished() {
601         return isFinished;
602     }
603 
getStartTime()604     long getStartTime() {
605         return startTime;
606     }
607 
getEndTime()608     long getEndTime() {
609         return endTime;
610     }
611 
transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode)612     abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException;
613 
614     /**
615      * Apply a transform to a function node, returning the transformed function node. If the transform is not
616      * applicable, an exception is thrown. Every transform requires the function to have a certain number of
617      * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor
618      * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states.
619      *
620      * @param compiler     compiler
621      * @param phases       current complete pipeline of which this phase is one
622      * @param functionNode function node to transform
623      *
624      * @return transformed function node
625      *
626      * @throws CompilationException if function node lacks the state required to run the transform on it
627      */
apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode)628     final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException {
629         assert phases.contains(this);
630 
631         return end(compiler, transform(compiler, phases, begin(compiler, functionNode)));
632     }
633 
transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor)634     private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) {
635         return (FunctionNode) fn.accept(visitor);
636     }
637 
createNewCompileUnit(final Compiler compiler, final CompilationPhases phases)638     private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) {
639         final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
640         if (phases.isRestOfCompilation()) {
641             sb.append("$restOf");
642         }
643         //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what
644         //fills those out anyway. Thus no need for a copy constructor
645         return compiler.createCompileUnit(sb.toString(), 0);
646     }
647 }
648