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 static final CompilationPhase PROGRAM_POINT_PHASE = new ProgramPointPhase(); 161 162 private static final class CacheAstPhase extends CompilationPhase { 163 @Override 164 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 165 if (!compiler.isOnDemandCompilation()) { 166 // Only do this on initial preprocessing of the source code. For on-demand compilations from 167 // source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function 168 // being compiled. 169 transformFunction(fn, new CacheAst(compiler)); 170 } 171 // NOTE: we're returning the original fn as we have destructively modified the cached functions by 172 // removing their bodies. This step is associating FunctionNode objects with 173 // RecompilableScriptFunctionData; it's not really modifying the AST. 174 return fn; 175 } 176 177 @Override 178 public String toString() { 179 return "'Cache ASTs'"; 180 } 181 }; 182 183 static final CompilationPhase CACHE_AST_PHASE = new CacheAstPhase(); 184 185 private static final class SymbolAssignmentPhase extends CompilationPhase { 186 @Override 187 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 188 return transformFunction(fn, new AssignSymbols(compiler)); 189 } 190 191 @Override 192 public String toString() { 193 return "'Symbol Assignment'"; 194 } 195 }; 196 197 static final CompilationPhase SYMBOL_ASSIGNMENT_PHASE = new SymbolAssignmentPhase(); 198 199 private static final class ScopeDepthComputationPhase extends CompilationPhase { 200 @Override 201 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 202 return transformFunction(fn, new FindScopeDepths(compiler)); 203 } 204 205 @Override 206 public String toString() { 207 return "'Scope Depth Computation'"; 208 } 209 }; 210 211 static final CompilationPhase SCOPE_DEPTH_COMPUTATION_PHASE = new ScopeDepthComputationPhase(); 212 213 private static final class DeclareLocalSymbolsPhase extends CompilationPhase { 214 @Override 215 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 216 // It's not necessary to guard the marking of symbols as locals with this "if" condition for 217 // correctness, it's just an optimization -- runtime type calculation is not used when the compilation 218 // is not an on-demand optimistic compilation, so we can skip locals marking then. 219 if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { 220 fn.getBody().accept(new SimpleNodeVisitor() { 221 @Override 222 public boolean enterFunctionNode(final FunctionNode functionNode) { 223 // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand 224 // compilation, and we're skipping parsing the function bodies for nested functions, this 225 // basically only means their parameters. It'd be enough to mistakenly declare to be a local a 226 // symbol in the outer function named the same as one of the parameters, though. 227 return false; 228 }; 229 @Override 230 public boolean enterBlock(final Block block) { 231 for (final Symbol symbol: block.getSymbols()) { 232 if (!symbol.isScope()) { 233 compiler.declareLocalSymbol(symbol.getName()); 234 } 235 } 236 return true; 237 }; 238 }); 239 } 240 return fn; 241 } 242 243 @Override 244 public String toString() { 245 return "'Local Symbols Declaration'"; 246 } 247 }; 248 249 static final CompilationPhase DECLARE_LOCAL_SYMBOLS_PHASE = new DeclareLocalSymbolsPhase(); 250 251 private static final class OptimisticTypeAssignmentPhase extends CompilationPhase { 252 @Override 253 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 254 if (compiler.useOptimisticTypes()) { 255 return transformFunction(fn, new OptimisticTypesCalculator(compiler)); 256 } 257 return fn; 258 } 259 260 @Override 261 public String toString() { 262 return "'Optimistic Type Assignment'"; 263 } 264 } 265 266 static final CompilationPhase OPTIMISTIC_TYPE_ASSIGNMENT_PHASE = new OptimisticTypeAssignmentPhase(); 267 268 private static final class LocalVariableTypeCalculationPhase extends CompilationPhase { 269 @Override 270 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 271 final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler)); 272 final ScriptEnvironment senv = compiler.getScriptEnvironment(); 273 final PrintWriter err = senv.getErr(); 274 275 //TODO separate phase for the debug printouts for abstraction and clarity 276 if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) { 277 err.println("Lower AST for: " + quote(newFunctionNode.getName())); 278 err.println(new ASTWriter(newFunctionNode)); 279 } 280 281 if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) { 282 err.println("Lower AST for: " + quote(newFunctionNode.getName())); 283 err.println(new PrintVisitor(newFunctionNode)); 284 } 285 286 return newFunctionNode; 287 } 288 289 @Override 290 public String toString() { 291 return "'Local Variable Type Calculation'"; 292 } 293 }; 294 295 static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase(); 296 297 private static final class ReuseCompileUnitsPhase extends CompilationPhase { 298 @Override 299 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 300 assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods"; 301 302 final Map<CompileUnit, CompileUnit> map = new HashMap<>(); 303 final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet(); 304 305 final DebugLogger log = compiler.getLogger(); 306 307 log.fine("Clearing bytecode cache"); 308 compiler.clearBytecode(); 309 310 for (final CompileUnit oldUnit : compiler.getCompileUnits()) { 311 assert map.get(oldUnit) == null; 312 final CompileUnit newUnit = createNewCompileUnit(compiler, phases); 313 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); 314 map.put(oldUnit, newUnit); 315 assert newUnit != null; 316 newUnits.add(newUnit); 317 } 318 319 log.fine("Replacing compile units in Compiler..."); 320 compiler.replaceCompileUnits(newUnits); 321 log.fine("Done"); 322 323 //replace old compile units in function nodes, if any are assigned, 324 //for example by running the splitter on this function node in a previous 325 //partial code generation 326 final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() { 327 @Override 328 CompileUnit getReplacement(final CompileUnit original) { 329 return map.get(original); 330 } 331 332 @Override 333 public Node leaveDefault(final Node node) { 334 return node.ensureUniqueLabels(lc); 335 } 336 }); 337 338 return newFunctionNode; 339 } 340 341 @Override 342 public String toString() { 343 return "'Reuse Compile Units'"; 344 } 345 } 346 347 /** 348 * Reuse compile units, if they are already present. We are using the same compiler 349 * to recompile stuff 350 */ 351 static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase(); 352 353 private static final class ReinitializeCachedPhase extends CompilationPhase { 354 @Override 355 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 356 final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet(); 357 final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>(); 358 359 // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase 360 // will use that as the root class. 361 createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases); 362 363 final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() { 364 @Override 365 CompileUnit getReplacement(final CompileUnit oldUnit) { 366 final CompileUnit existing = unitMap.get(oldUnit); 367 if (existing != null) { 368 return existing; 369 } 370 return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases); 371 } 372 373 @Override 374 public Node leaveFunctionNode(final FunctionNode fn2) { 375 return super.leaveFunctionNode( 376 // restore flags for deserialized nested function nodes 377 compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2)); 378 }; 379 }); 380 compiler.replaceCompileUnits(unitSet); 381 return newFn; 382 } 383 384 private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet, 385 final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) { 386 final CompileUnit newUnit = createNewCompileUnit(compiler, phases); 387 unitMap.put(oldUnit, newUnit); 388 unitSet.add(newUnit); 389 return newUnit; 390 } 391 392 @Override 393 public String toString() { 394 return "'Reinitialize cached'"; 395 } 396 } 397 398 static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase(); 399 400 private static final class BytecodeGenerationPhase extends CompilationPhase { 401 @Override 402 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 403 final ScriptEnvironment senv = compiler.getScriptEnvironment(); 404 405 FunctionNode newFunctionNode = fn; 406 407 //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped 408 //in CodeGeneration - the rest can be used as a working "is compile unit used" metric 409 fn.getCompileUnit().setUsed(); 410 411 compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); 412 413 final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); 414 415 try { 416 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program 417 // in the lazy + optimistic world. See CodeGenerator.skipFunction(). 418 newFunctionNode = transformFunction(newFunctionNode, codegen); 419 codegen.generateScopeCalls(); 420 } catch (final VerifyError e) { 421 if (senv._verify_code || senv._print_code) { 422 senv.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage()); 423 if (senv._dump_on_error) { 424 e.printStackTrace(senv.getErr()); 425 } 426 } else { 427 throw e; 428 } 429 } catch (final Throwable e) { 430 // Provide source file and line number being compiled when the assertion occurred 431 throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e); 432 } 433 434 for (final CompileUnit compileUnit : compiler.getCompileUnits()) { 435 final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 436 classEmitter.end(); 437 438 if (!compileUnit.isUsed()) { 439 compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); 440 continue; 441 } 442 443 final byte[] bytecode = classEmitter.toByteArray(); 444 assert bytecode != null; 445 446 final String className = compileUnit.getUnitClassName(); 447 compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used 448 449 CompileUnit.increaseEmitCount(); 450 451 // should we verify the generated code? 452 if (senv._verify_code) { 453 compiler.getCodeInstaller().verify(bytecode); 454 } 455 456 DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className); 457 } 458 459 return newFunctionNode; 460 } 461 462 @Override 463 public String toString() { 464 return "'Bytecode Generation'"; 465 } 466 } 467 468 /** 469 * Bytecode generation: 470 * 471 * Generate the byte code class(es) resulting from the compiled FunctionNode 472 */ 473 static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase(); 474 475 private static final class InstallPhase extends CompilationPhase { 476 @Override 477 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 478 final DebugLogger log = compiler.getLogger(); 479 480 final Map<String, Class<?>> installedClasses = new LinkedHashMap<>(); 481 482 boolean first = true; 483 Class<?> rootClass = null; 484 long length = 0L; 485 486 final CodeInstaller codeInstaller = compiler.getCodeInstaller(); 487 final Map<String, byte[]> bytecode = compiler.getBytecode(); 488 489 for (final Entry<String, byte[]> entry : bytecode.entrySet()) { 490 final String className = entry.getKey(); 491 //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName(); 492 final byte[] code = entry.getValue(); 493 length += code.length; 494 495 final Class<?> clazz = codeInstaller.install(className, code); 496 if (first) { 497 rootClass = clazz; 498 first = false; 499 } 500 installedClasses.put(className, clazz); 501 } 502 503 if (rootClass == null) { 504 throw new CompilationException("Internal compiler error: root class not found!"); 505 } 506 507 final Object[] constants = compiler.getConstantData().toArray(); 508 codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants); 509 510 // initialize transient fields on recompilable script function data 511 for (final Object constant: constants) { 512 if (constant instanceof RecompilableScriptFunctionData) { 513 ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller); 514 } 515 } 516 517 // initialize function in the compile units 518 for (final CompileUnit unit : compiler.getCompileUnits()) { 519 if (!unit.isUsed()) { 520 continue; 521 } 522 unit.setCode(installedClasses.get(unit.getUnitClassName())); 523 unit.initializeFunctionsCode(); 524 } 525 526 if (log.isEnabled()) { 527 final StringBuilder sb = new StringBuilder(); 528 529 sb.append("Installed class '"). 530 append(rootClass.getSimpleName()). 531 append('\''). 532 append(" ["). 533 append(rootClass.getName()). 534 append(", size="). 535 append(length). 536 append(" bytes, "). 537 append(compiler.getCompileUnits().size()). 538 append(" compile unit(s)]"); 539 540 log.fine(sb.toString()); 541 } 542 543 return fn.setRootClass(null, rootClass); 544 } 545 546 @Override 547 public String toString() { 548 return "'Class Installation'"; 549 } 550 } 551 552 static final CompilationPhase INSTALL_PHASE = new InstallPhase(); 553 554 /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ 555 private long startTime; 556 557 /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ 558 private long endTime; 559 560 /** boolean that is true upon transform completion */ 561 private boolean isFinished; 562 563 private CompilationPhase() {} 564 565 /** 566 * Start a compilation phase 567 * @param compiler the compiler to use 568 * @param functionNode function to compile 569 * @return function node 570 */ 571 protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) { 572 compiler.getLogger().indent(); 573 startTime = System.nanoTime(); 574 575 return functionNode; 576 } 577 578 /** 579 * End a compilation phase 580 * @param compiler the compiler 581 * @param functionNode function node to compile 582 * @return function node 583 */ 584 protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) { 585 compiler.getLogger().unindent(); 586 endTime = System.nanoTime(); 587 compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime); 588 589 isFinished = true; 590 return functionNode; 591 } 592 593 boolean isFinished() { 594 return isFinished; 595 } 596 597 long getStartTime() { 598 return startTime; 599 } 600 601 long getEndTime() { 602 return endTime; 603 } 604 605 abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException; 606 607 /** 608 * Apply a transform to a function node, returning the transformed function node. If the transform is not 609 * applicable, an exception is thrown. Every transform requires the function to have a certain number of 610 * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor 611 * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states. 612 * 613 * @param compiler compiler 614 * @param phases current complete pipeline of which this phase is one 615 * @param functionNode function node to transform 616 * 617 * @return transformed function node 618 * 619 * @throws CompilationException if function node lacks the state required to run the transform on it 620 */ 621 final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException { 622 assert phases.contains(this); 623 624 return end(compiler, transform(compiler, phases, begin(compiler, functionNode))); 625 } 626 627 private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) { 628 return (FunctionNode) fn.accept(visitor); 629 } 630 631 private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) { 632 final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); 633 if (phases.isRestOfCompilation()) { 634 sb.append("$restOf"); 635 } 636 //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what 637 //fills those out anyway. Thus no need for a copy constructor 638 return compiler.createCompileUnit(sb.toString(), 0); 639 } 640 }