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