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 59 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 60 return transformFunction(fn, new FoldConstants(compiler)); 61 } 62 63 @Override 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 77 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 78 return transformFunction(fn, new Lower(compiler)); 79 } 80 81 @Override 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 98 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 99 return transformFunction(fn, new ApplySpecialization(compiler)); 100 } 101 102 @Override 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 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 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 150 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 151 return transformFunction(fn, new ProgramPoints()); 152 } 153 154 @Override 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 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 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 192 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 193 return transformFunction(fn, new AssignSymbols(compiler)); 194 } 195 196 @Override 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 206 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 207 return transformFunction(fn, new FindScopeDepths(compiler)); 208 } 209 210 @Override 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 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 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 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 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 275 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 276 final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler)); 277 final ScriptEnvironment senv = compiler.getScriptEnvironment(); 278 final PrintWriter err = senv.getErr(); 279 280 //TODO separate phase for the debug printouts for abstraction and clarity 281 if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) { 282 err.println("Lower AST for: " + quote(newFunctionNode.getName())); 283 err.println(new ASTWriter(newFunctionNode)); 284 } 285 286 if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) { 287 err.println("Lower AST for: " + quote(newFunctionNode.getName())); 288 err.println(new PrintVisitor(newFunctionNode)); 289 } 290 291 return newFunctionNode; 292 } 293 294 @Override 295 public String toString() { 296 return "'Local Variable Type Calculation'"; 297 } 298 }; 299 300 static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase(); 301 302 private static final class ReuseCompileUnitsPhase extends CompilationPhase { 303 @Override 304 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 305 assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods"; 306 307 final Map<CompileUnit, CompileUnit> map = new HashMap<>(); 308 final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet(); 309 310 final DebugLogger log = compiler.getLogger(); 311 312 log.fine("Clearing bytecode cache"); 313 compiler.clearBytecode(); 314 315 for (final CompileUnit oldUnit : compiler.getCompileUnits()) { 316 assert map.get(oldUnit) == null; 317 final CompileUnit newUnit = createNewCompileUnit(compiler, phases); 318 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); 319 map.put(oldUnit, newUnit); 320 assert newUnit != null; 321 newUnits.add(newUnit); 322 } 323 324 log.fine("Replacing compile units in Compiler..."); 325 compiler.replaceCompileUnits(newUnits); 326 log.fine("Done"); 327 328 //replace old compile units in function nodes, if any are assigned, 329 //for example by running the splitter on this function node in a previous 330 //partial code generation 331 final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() { 332 @Override 333 CompileUnit getReplacement(final CompileUnit original) { 334 return map.get(original); 335 } 336 337 @Override 338 public Node leaveDefault(final Node node) { 339 return node.ensureUniqueLabels(lc); 340 } 341 }); 342 343 return newFunctionNode; 344 } 345 346 @Override 347 public String toString() { 348 return "'Reuse Compile Units'"; 349 } 350 } 351 352 /** 353 * Reuse compile units, if they are already present. We are using the same compiler 354 * to recompile stuff 355 */ 356 static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase(); 357 358 private static final class ReinitializeCachedPhase extends CompilationPhase { 359 @Override 360 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 361 final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet(); 362 final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>(); 363 364 // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase 365 // will use that as the root class. 366 createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases); 367 368 final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() { 369 @Override 370 CompileUnit getReplacement(final CompileUnit oldUnit) { 371 final CompileUnit existing = unitMap.get(oldUnit); 372 if (existing != null) { 373 return existing; 374 } 375 return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases); 376 } 377 378 @Override 379 public Node leaveFunctionNode(final FunctionNode fn2) { 380 return super.leaveFunctionNode( 381 // restore flags for deserialized nested function nodes 382 compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2)); 383 }; 384 }); 385 compiler.replaceCompileUnits(unitSet); 386 return newFn; 387 } 388 389 private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet, 390 final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) { 391 final CompileUnit newUnit = createNewCompileUnit(compiler, phases); 392 unitMap.put(oldUnit, newUnit); 393 unitSet.add(newUnit); 394 return newUnit; 395 } 396 397 @Override 398 public String toString() { 399 return "'Reinitialize cached'"; 400 } 401 } 402 403 static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase(); 404 405 private static final class BytecodeGenerationPhase extends CompilationPhase { 406 @Override 407 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 408 final ScriptEnvironment senv = compiler.getScriptEnvironment(); 409 410 FunctionNode newFunctionNode = fn; 411 412 //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped 413 //in CodeGeneration - the rest can be used as a working "is compile unit used" metric 414 fn.getCompileUnit().setUsed(); 415 416 compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); 417 418 final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); 419 420 try { 421 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program 422 // in the lazy + optimistic world. See CodeGenerator.skipFunction(). 423 newFunctionNode = transformFunction(newFunctionNode, codegen); 424 codegen.generateScopeCalls(); 425 } catch (final VerifyError e) { 426 if (senv._verify_code || senv._print_code) { 427 senv.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage()); 428 if (senv._dump_on_error) { 429 e.printStackTrace(senv.getErr()); 430 } 431 } else { 432 throw e; 433 } 434 } catch (final Throwable e) { 435 // Provide source file and line number being compiled when the assertion occurred 436 throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e); 437 } 438 439 for (final CompileUnit compileUnit : compiler.getCompileUnits()) { 440 final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 441 classEmitter.end(); 442 443 if (!compileUnit.isUsed()) { 444 compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); 445 continue; 446 } 447 448 final byte[] bytecode = classEmitter.toByteArray(); 449 assert bytecode != null; 450 451 final String className = compileUnit.getUnitClassName(); 452 compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used 453 454 CompileUnit.increaseEmitCount(); 455 456 // should we verify the generated code? 457 if (senv._verify_code) { 458 compiler.getCodeInstaller().verify(bytecode); 459 } 460 461 DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className); 462 } 463 464 return newFunctionNode; 465 } 466 467 @Override 468 public String toString() { 469 return "'Bytecode Generation'"; 470 } 471 } 472 473 /** 474 * Bytecode generation: 475 * 476 * Generate the byte code class(es) resulting from the compiled FunctionNode 477 */ 478 static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase(); 479 480 private static final class InstallPhase extends CompilationPhase { 481 @Override 482 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 483 final DebugLogger log = compiler.getLogger(); 484 485 final Map<String, Class<?>> installedClasses = new LinkedHashMap<>(); 486 487 boolean first = true; 488 Class<?> rootClass = null; 489 long length = 0L; 490 491 final CodeInstaller origCodeInstaller = compiler.getCodeInstaller(); 492 final Map<String, byte[]> bytecode = compiler.getBytecode(); 493 final CodeInstaller codeInstaller = bytecode.size() > 1 ? origCodeInstaller.getMultiClassCodeInstaller() : origCodeInstaller; 494 495 for (final Entry<String, byte[]> entry : bytecode.entrySet()) { 496 final String className = entry.getKey(); 497 //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName(); 498 final byte[] code = entry.getValue(); 499 length += code.length; 500 501 final Class<?> clazz = codeInstaller.install(className, code); 502 if (first) { 503 rootClass = clazz; 504 first = false; 505 } 506 installedClasses.put(className, clazz); 507 } 508 509 if (rootClass == null) { 510 throw new CompilationException("Internal compiler error: root class not found!"); 511 } 512 513 final Object[] constants = compiler.getConstantData().toArray(); 514 codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants); 515 516 // initialize transient fields on recompilable script function data 517 for (final Object constant: constants) { 518 if (constant instanceof RecompilableScriptFunctionData) { 519 ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller); 520 } 521 } 522 523 // initialize function in the compile units 524 for (final CompileUnit unit : compiler.getCompileUnits()) { 525 if (!unit.isUsed()) { 526 continue; 527 } 528 unit.setCode(installedClasses.get(unit.getUnitClassName())); 529 unit.initializeFunctionsCode(); 530 } 531 532 if (log.isEnabled()) { 533 final StringBuilder sb = new StringBuilder(); 534 535 sb.append("Installed class '"). 536 append(rootClass.getSimpleName()). 537 append('\''). 538 append(" ["). 539 append(rootClass.getName()). 540 append(", size="). 541 append(length). 542 append(" bytes, "). 543 append(compiler.getCompileUnits().size()). 544 append(" compile unit(s)]"); 545 546 log.fine(sb.toString()); 547 } 548 549 return fn.setRootClass(null, rootClass); 550 } 551 552 @Override 553 public String toString() { 554 return "'Class Installation'"; 555 } 556 } 557 558 static final CompilationPhase INSTALL_PHASE = new InstallPhase(); 559 560 /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ 561 private long startTime; 562 563 /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ 564 private long endTime; 565 566 /** boolean that is true upon transform completion */ 567 private boolean isFinished; 568 569 private CompilationPhase() {} 570 571 /** 572 * Start a compilation phase 573 * @param compiler the compiler to use 574 * @param functionNode function to compile 575 * @return function node 576 */ 577 protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) { 578 compiler.getLogger().indent(); 579 startTime = System.nanoTime(); 580 581 return functionNode; 582 } 583 584 /** 585 * End a compilation phase 586 * @param compiler the compiler 587 * @param functionNode function node to compile 588 * @return function node 589 */ 590 protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) { 591 compiler.getLogger().unindent(); 592 endTime = System.nanoTime(); 593 compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime); 594 595 isFinished = true; 596 return functionNode; 597 } 598 599 boolean isFinished() { 600 return isFinished; 601 } 602 603 long getStartTime() { 604 return startTime; 605 } 606 607 long getEndTime() { 608 return endTime; 609 } 610 611 abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException; 612 613 /** 614 * Apply a transform to a function node, returning the transformed function node. If the transform is not 615 * applicable, an exception is thrown. Every transform requires the function to have a certain number of 616 * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor 617 * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states. 618 * 619 * @param compiler compiler 620 * @param phases current complete pipeline of which this phase is one 621 * @param functionNode function node to transform 622 * 623 * @return transformed function node 624 * 625 * @throws CompilationException if function node lacks the state required to run the transform on it 626 */ 627 final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException { 628 assert phases.contains(this); 629 630 return end(compiler, transform(compiler, phases, begin(compiler, functionNode))); 631 } 632 633 private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) { 634 return (FunctionNode) fn.accept(visitor); 635 } 636 637 private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) { 638 final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); 639 if (phases.isRestOfCompilation()) { 640 sb.append("$restOf"); 641 } 642 //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what 643 //fills those out anyway. Thus no need for a copy constructor 644 return compiler.createCompileUnit(sb.toString(), 0); 645 } 646 }