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