1 /* 2 * Copyright (c) 2010, 2013, 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.codegen.CompilerConstants.ARGUMENTS; 29 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 30 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; 31 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 32 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 33 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 34 import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; 35 36 import java.io.File; 37 import java.lang.invoke.MethodType; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.HashMap; 42 import java.util.Iterator; 43 import java.util.LinkedHashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Set; 47 import java.util.TreeMap; 48 import java.util.concurrent.TimeUnit; 49 import java.util.concurrent.atomic.AtomicInteger; 50 import java.util.function.Consumer; 51 import java.util.logging.Level; 52 import jdk.nashorn.internal.codegen.types.Type; 53 import jdk.nashorn.internal.ir.Expression; 54 import jdk.nashorn.internal.ir.FunctionNode; 55 import jdk.nashorn.internal.ir.Optimistic; 56 import jdk.nashorn.internal.runtime.CodeInstaller; 57 import jdk.nashorn.internal.runtime.Context; 58 import jdk.nashorn.internal.runtime.ErrorManager; 59 import jdk.nashorn.internal.runtime.FunctionInitializer; 60 import jdk.nashorn.internal.runtime.ParserException; 61 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; 62 import jdk.nashorn.internal.runtime.ScriptEnvironment; 63 import jdk.nashorn.internal.runtime.ScriptObject; 64 import jdk.nashorn.internal.runtime.ScriptRuntime; 65 import jdk.nashorn.internal.runtime.Source; 66 import jdk.nashorn.internal.runtime.linker.NameCodec; 67 import jdk.nashorn.internal.runtime.logging.DebugLogger; 68 import jdk.nashorn.internal.runtime.logging.Loggable; 69 import jdk.nashorn.internal.runtime.logging.Logger; 70 71 /** 72 * Responsible for converting JavaScripts to java byte code. Main entry 73 * point for code generator. The compiler may also install classes given some 74 * predefined Code installation policy, given to it at construction time. 75 * @see CodeInstaller 76 */ 77 @Logger(name="compiler") 78 public final class Compiler implements Loggable { 79 80 /** Name of the scripts package */ 81 public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts"; 82 83 /** Name of the objects package */ 84 public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects"; 85 86 private final ScriptEnvironment env; 87 88 private final Source source; 89 90 private final String sourceName; 91 92 private final ErrorManager errors; 93 94 private final boolean optimistic; 95 96 private final Map<String, byte[]> bytecode; 97 98 private final Set<CompileUnit> compileUnits; 99 100 private final ConstantData constantData; 101 102 private final CodeInstaller installer; 103 104 /** logger for compiler, trampolines and related code generation events 105 * that affect classes */ 106 private final DebugLogger log; 107 108 private final Context context; 109 110 private final TypeMap types; 111 112 // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly 113 // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations). 114 private final TypeEvaluator typeEvaluator; 115 116 private final boolean strict; 117 118 private final boolean onDemand; 119 120 /** 121 * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means 122 * that using whatever was at program point 17 as an int failed. 123 */ 124 private final Map<Integer, Type> invalidatedProgramPoints; 125 126 /** 127 * Descriptor of the location where we write the type information after compilation. 128 */ 129 private final Object typeInformationFile; 130 131 /** 132 * Compile unit name of first compile unit - this prefix will be used for all 133 * classes that a compilation generates. 134 */ 135 private final String firstCompileUnitName; 136 137 /** 138 * Contains the program point that should be used as the continuation entry point, as well as all previous 139 * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if 140 * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program 141 * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program 142 * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only 143 * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have 144 * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on. 145 */ 146 private final int[] continuationEntryPoints; 147 148 /** 149 * ScriptFunction data for what is being compile, where applicable. 150 * TODO: make this immutable, propagate it through the CompilationPhases 151 */ 152 private RecompilableScriptFunctionData compiledFunction; 153 154 /** 155 * Most compile unit names are longer than the default StringBuilder buffer, 156 * worth startup performance when massive class generation is going on to increase 157 * this 158 */ 159 private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; 160 161 /** 162 * Compilation phases that a compilation goes through 163 */ 164 public static class CompilationPhases implements Iterable<CompilationPhase> { 165 166 /** 167 * Singleton that describes compilation up to the phase where a function can be cached. 168 */ 169 private final static CompilationPhases COMPILE_UPTO_CACHED = new CompilationPhases( 170 "Common initial phases", 171 CompilationPhase.CONSTANT_FOLDING_PHASE, 172 CompilationPhase.LOWERING_PHASE, 173 CompilationPhase.APPLY_SPECIALIZATION_PHASE, 174 CompilationPhase.SPLITTING_PHASE, 175 CompilationPhase.PROGRAM_POINT_PHASE, 176 CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, 177 CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, 178 CompilationPhase.CACHE_AST_PHASE 179 ); 180 181 private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( 182 "After common phases, before bytecode generator", 183 CompilationPhase.DECLARE_LOCAL_SYMBOLS_PHASE, 184 CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, 185 CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE 186 ); 187 188 /** 189 * Singleton that describes additional steps to be taken after retrieving a cached function, all the 190 * way up to (but not including) generating and installing code. 191 */ 192 public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( 193 "Recompile cached function up to bytecode", 194 CompilationPhase.REINITIALIZE_CACHED, 195 COMPILE_CACHED_UPTO_BYTECODE 196 ); 197 198 /** 199 * Singleton that describes back end of method generation, given that we have generated the normal 200 * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} 201 */ 202 public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL = new CompilationPhases( 203 "Generate bytecode and install", 204 CompilationPhase.BYTECODE_GENERATION_PHASE, 205 CompilationPhase.INSTALL_PHASE 206 ); 207 208 /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ 209 public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( 210 "Compile upto bytecode", 211 COMPILE_UPTO_CACHED, 212 COMPILE_CACHED_UPTO_BYTECODE); 213 214 /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ 215 public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( 216 "Compile without install", 217 COMPILE_UPTO_BYTECODE, 218 CompilationPhase.BYTECODE_GENERATION_PHASE); 219 220 /** Singleton that describes a standard eager compilation - this includes code installation */ 221 public final static CompilationPhases COMPILE_ALL = new CompilationPhases( 222 "Full eager compilation", 223 COMPILE_UPTO_BYTECODE, 224 GENERATE_BYTECODE_AND_INSTALL); 225 226 /** Singleton that describes a full compilation - this includes code installation - from serialized state*/ 227 public final static CompilationPhases COMPILE_ALL_CACHED = new CompilationPhases( 228 "Eager compilation from serializaed state", 229 RECOMPILE_CACHED_UPTO_BYTECODE, 230 GENERATE_BYTECODE_AND_INSTALL); 231 232 /** 233 * Singleton that describes restOf method generation, given that we have generated the normal 234 * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} 235 */ 236 public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL_RESTOF = new CompilationPhases( 237 "Generate bytecode and install - RestOf method", 238 CompilationPhase.REUSE_COMPILE_UNITS_PHASE, 239 GENERATE_BYTECODE_AND_INSTALL); 240 241 /** Compile all for a rest of method */ 242 public final static CompilationPhases COMPILE_ALL_RESTOF = new CompilationPhases( 243 "Compile all, rest of", 244 COMPILE_UPTO_BYTECODE, 245 GENERATE_BYTECODE_AND_INSTALL_RESTOF); 246 247 /** Compile from serialized for a rest of method */ 248 public final static CompilationPhases COMPILE_CACHED_RESTOF = new CompilationPhases( 249 "Compile serialized, rest of", 250 RECOMPILE_CACHED_UPTO_BYTECODE, 251 GENERATE_BYTECODE_AND_INSTALL_RESTOF); 252 253 private final List<CompilationPhase> phases; 254 255 private final String desc; 256 257 private CompilationPhases(final String desc, final CompilationPhase... phases) { 258 this(desc, Arrays.asList(phases)); 259 } 260 261 private CompilationPhases(final String desc, final CompilationPhases base, final CompilationPhase... phases) { 262 this(desc, concat(base.phases, Arrays.asList(phases))); 263 } 264 265 private CompilationPhases(final String desc, final CompilationPhase first, final CompilationPhases rest) { 266 this(desc, concat(Collections.singletonList(first), rest.phases)); 267 } 268 269 private CompilationPhases(final String desc, final CompilationPhases base) { 270 this(desc, base.phases); 271 } 272 273 private CompilationPhases(final String desc, final CompilationPhases... bases) { 274 this(desc, concatPhases(bases)); 275 } 276 277 private CompilationPhases(final String desc, final List<CompilationPhase> phases) { 278 this.desc = desc; 279 this.phases = phases; 280 } 281 282 private static List<CompilationPhase> concatPhases(final CompilationPhases[] bases) { 283 final ArrayList<CompilationPhase> l = new ArrayList<>(); 284 for(final CompilationPhases base: bases) { 285 l.addAll(base.phases); 286 } 287 l.trimToSize(); 288 return l; 289 } 290 291 private static <T> List<T> concat(final List<T> l1, final List<T> l2) { 292 final ArrayList<T> l = new ArrayList<>(l1); 293 l.addAll(l2); 294 l.trimToSize(); 295 return l; 296 } 297 298 @Override 299 public String toString() { 300 return "'" + desc + "' " + phases.toString(); 301 } 302 303 boolean contains(final CompilationPhase phase) { 304 return phases.contains(phase); 305 } 306 307 @Override 308 public Iterator<CompilationPhase> iterator() { 309 return phases.iterator(); 310 } 311 312 boolean isRestOfCompilation() { 313 return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_CACHED_RESTOF; 314 } 315 316 String getDesc() { 317 return desc; 318 } 319 320 String toString(final String prefix) { 321 final StringBuilder sb = new StringBuilder(); 322 for (final CompilationPhase phase : phases) { 323 sb.append(prefix).append(phase).append('\n'); 324 } 325 return sb.toString(); 326 } 327 } 328 329 /** 330 * This array contains names that need to be reserved at the start 331 * of a compile, to avoid conflict with variable names later introduced. 332 * See {@link CompilerConstants} for special names used for structures 333 * during a compile. 334 */ 335 private static String[] RESERVED_NAMES = { 336 SCOPE.symbolName(), 337 THIS.symbolName(), 338 RETURN.symbolName(), 339 CALLEE.symbolName(), 340 VARARGS.symbolName(), 341 ARGUMENTS.symbolName() 342 }; 343 344 // per instance 345 private final int compilationId = COMPILATION_ID.getAndIncrement(); 346 347 // per instance 348 private final AtomicInteger nextCompileUnitId = new AtomicInteger(0); 349 350 private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0); 351 352 /** 353 * Creates a new compiler instance for initial compilation of a script. 354 * 355 * @param installer code installer 356 * @param source source to compile 357 * @param errors error manager 358 * @param isStrict is this a strict compilation 359 * @return a new compiler 360 */ 361 public static Compiler forInitialCompilation( 362 final CodeInstaller installer, 363 final Source source, 364 final ErrorManager errors, 365 final boolean isStrict) { 366 return new Compiler(installer.getContext(), installer, source, errors, isStrict); 367 } 368 369 /** 370 * Creates a compiler without a code installer. It can only be used to compile code, not install the 371 * generated classes and as such it is useful only for implementation of {@code --compile-only} command 372 * line option. 373 * @param context the current context 374 * @param source source to compile 375 * @param isStrict is this a strict compilation 376 * @return a new compiler 377 */ 378 public static Compiler forNoInstallerCompilation( 379 final Context context, 380 final Source source, 381 final boolean isStrict) { 382 return new Compiler(context, null, source, context.getErrorManager(), isStrict); 383 } 384 385 /** 386 * Creates a compiler for an on-demand compilation job. 387 * 388 * @param installer code installer 389 * @param source source to compile 390 * @param isStrict is this a strict compilation 391 * @param compiledFunction compiled function, if any 392 * @param types parameter and return value type information, if any is known 393 * @param invalidatedProgramPoints invalidated program points for recompilation 394 * @param typeInformationFile descriptor of the location where type information is persisted 395 * @param continuationEntryPoints continuation entry points for restof method 396 * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator} 397 * @return a new compiler 398 */ 399 public static Compiler forOnDemandCompilation( 400 final CodeInstaller installer, 401 final Source source, 402 final boolean isStrict, 403 final RecompilableScriptFunctionData compiledFunction, 404 final TypeMap types, 405 final Map<Integer, Type> invalidatedProgramPoints, 406 final Object typeInformationFile, 407 final int[] continuationEntryPoints, 408 final ScriptObject runtimeScope) { 409 final Context context = installer.getContext(); 410 return new Compiler(context, installer, source, context.getErrorManager(), isStrict, true, 411 compiledFunction, types, invalidatedProgramPoints, typeInformationFile, 412 continuationEntryPoints, runtimeScope); 413 } 414 415 /** 416 * Convenience constructor for non on-demand compiler instances. 417 */ 418 private Compiler( 419 final Context context, 420 final CodeInstaller installer, 421 final Source source, 422 final ErrorManager errors, 423 final boolean isStrict) { 424 this(context, installer, source, errors, isStrict, false, null, null, null, null, null, null); 425 } 426 427 private Compiler( 428 final Context context, 429 final CodeInstaller installer, 430 final Source source, 431 final ErrorManager errors, 432 final boolean isStrict, 433 final boolean isOnDemand, 434 final RecompilableScriptFunctionData compiledFunction, 435 final TypeMap types, 436 final Map<Integer, Type> invalidatedProgramPoints, 437 final Object typeInformationFile, 438 final int[] continuationEntryPoints, 439 final ScriptObject runtimeScope) { 440 this.context = context; 441 this.env = context.getEnv(); 442 this.installer = installer; 443 this.constantData = new ConstantData(); 444 this.compileUnits = CompileUnit.createCompileUnitSet(); 445 this.bytecode = new LinkedHashMap<>(); 446 this.log = initLogger(context); 447 this.source = source; 448 this.errors = errors; 449 this.sourceName = FunctionNode.getSourceName(source); 450 this.onDemand = isOnDemand; 451 this.compiledFunction = compiledFunction; 452 this.types = types; 453 this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<>() : invalidatedProgramPoints; 454 this.typeInformationFile = typeInformationFile; 455 this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone(); 456 this.typeEvaluator = new TypeEvaluator(this, runtimeScope); 457 this.firstCompileUnitName = firstCompileUnitName(); 458 this.strict = isStrict; 459 460 this.optimistic = env._optimistic_types; 461 } 462 463 private String safeSourceName() { 464 String baseName = new File(source.getName()).getName(); 465 466 final int index = baseName.lastIndexOf(".js"); 467 if (index != -1) { 468 baseName = baseName.substring(0, index); 469 } 470 471 baseName = baseName.replace('.', '_').replace('-', '_'); 472 if (!env._loader_per_compile) { 473 baseName += installer.getUniqueScriptId(); 474 } 475 476 // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char. 477 // While ASM accepts such escapes for method names, field names, it enforces Java identifier 478 // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_' 479 // rather than safe encoding using '\'. 480 final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName); 481 return mangled != null ? mangled : baseName; 482 } 483 484 private static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; 485 private static String replaceDangerChars(final String name) { 486 final int len = name.length(); 487 final StringBuilder buf = new StringBuilder(); 488 for (int i = 0; i < len; i++) { 489 final char ch = name.charAt(i); 490 if (DANGEROUS_CHARS.indexOf(ch) != -1) { 491 buf.append('_'); 492 } else { 493 buf.append(ch); 494 } 495 } 496 return buf.toString(); 497 } 498 499 private String firstCompileUnitName() { 500 final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE). 501 append('/'). 502 append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()). 503 append('$'); 504 505 if (isOnDemandCompilation()) { 506 sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX); 507 } 508 509 if (compilationId > 0) { 510 sb.append(compilationId).append('$'); 511 } 512 513 if (types != null && compiledFunction.getFunctionNodeId() > 0) { 514 sb.append(compiledFunction.getFunctionNodeId()); 515 final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId()); 516 for (final Type t : paramTypes) { 517 sb.append(Type.getShortSignatureDescriptor(t)); 518 } 519 sb.append('$'); 520 } 521 522 sb.append(safeSourceName()); 523 524 return sb.toString(); 525 } 526 527 void declareLocalSymbol(final String symbolName) { 528 typeEvaluator.declareLocalSymbol(symbolName); 529 } 530 531 void setData(final RecompilableScriptFunctionData data) { 532 assert this.compiledFunction == null : data; 533 this.compiledFunction = data; 534 } 535 536 @Override 537 public DebugLogger getLogger() { 538 return log; 539 } 540 541 @Override 542 public DebugLogger initLogger(final Context ctxt) { 543 final boolean optimisticTypes = env._optimistic_types; 544 final boolean lazyCompilation = env._lazy_compilation; 545 546 return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() { 547 @Override 548 public void accept(final DebugLogger newLogger) { 549 if (!lazyCompilation) { 550 newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting."); 551 } 552 newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED."); 553 } 554 }); 555 } 556 557 ScriptEnvironment getScriptEnvironment() { 558 return env; 559 } 560 561 boolean isOnDemandCompilation() { 562 return onDemand; 563 } 564 565 boolean useOptimisticTypes() { 566 return optimistic; 567 } 568 569 Context getContext() { 570 return context; 571 } 572 573 Type getOptimisticType(final Optimistic node) { 574 return typeEvaluator.getOptimisticType(node); 575 } 576 577 /** 578 * Returns true if the expression can be safely evaluated, and its value is an object known to always use 579 * String as the type of its property names retrieved through 580 * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its 581 * property name types. 582 * @param expr the expression to test 583 * @return true if the expression can be safely evaluated, and its value is an object known to always use 584 * String as the type of its property iterators. 585 */ 586 boolean hasStringPropertyIterator(final Expression expr) { 587 return typeEvaluator.hasStringPropertyIterator(expr); 588 } 589 590 void addInvalidatedProgramPoint(final int programPoint, final Type type) { 591 invalidatedProgramPoints.put(programPoint, type); 592 } 593 594 595 /** 596 * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The 597 * copy is not live with regard to changes in state in this compiler instance, and is mutable. 598 * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types. 599 */ 600 public Map<Integer, Type> getInvalidatedProgramPoints() { 601 return invalidatedProgramPoints.isEmpty() ? null : new TreeMap<>(invalidatedProgramPoints); 602 } 603 604 TypeMap getTypeMap() { 605 return types; 606 } 607 608 MethodType getCallSiteType(final FunctionNode fn) { 609 if (types == null || !isOnDemandCompilation()) { 610 return null; 611 } 612 return types.getCallSiteType(fn); 613 } 614 615 Type getParamType(final FunctionNode fn, final int pos) { 616 return types == null ? null : types.get(fn, pos); 617 } 618 619 Type getReturnType() { 620 return types == null || !isOnDemandCompilation() ? Type.UNKNOWN : types.getReturnType(); 621 } 622 623 /** 624 * Do a compilation job 625 * 626 * @param functionNode function node to compile 627 * @param phases phases of compilation transforms to apply to function 628 629 * @return transformed function 630 * 631 * @throws CompilationException if error occurs during compilation 632 */ 633 public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException { 634 if (log.isEnabled()) { 635 log.info(">> Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc())); 636 log.indent(); 637 } 638 639 final String name = DebugLogger.quote(functionNode.getName()); 640 641 FunctionNode newFunctionNode = functionNode; 642 643 for (final String reservedName : RESERVED_NAMES) { 644 newFunctionNode.uniqueName(reservedName); 645 } 646 647 final boolean info = log.isLoggable(Level.INFO); 648 649 final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null; 650 651 long time = 0L; 652 653 for (final CompilationPhase phase : phases) { 654 log.fine(phase, " starting for ", name); 655 656 try { 657 newFunctionNode = phase.apply(this, phases, newFunctionNode); 658 } catch (final ParserException error) { 659 errors.error(error); 660 if (env._dump_on_error) { 661 error.printStackTrace(env.getErr()); 662 } 663 return null; 664 } 665 666 log.fine(phase, " done for function ", quote(name)); 667 668 time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L); 669 } 670 671 if (typeInformationFile != null && !phases.isRestOfCompilation()) { 672 OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints); 673 } 674 675 log.unindent(); 676 677 if (info) { 678 final StringBuilder sb = new StringBuilder("<< Finished compile job for "); 679 sb.append(newFunctionNode.getSource()). 680 append(':'). 681 append(quote(newFunctionNode.getName())); 682 683 if (time > 0L && timeLogger != null) { 684 assert env.isTimingEnabled(); 685 sb.append(" in ").append(TimeUnit.NANOSECONDS.toMillis(time)).append(" ms"); 686 } 687 log.info(sb); 688 } 689 690 return newFunctionNode; 691 } 692 693 Source getSource() { 694 return source; 695 } 696 697 Map<String, byte[]> getBytecode() { 698 return Collections.unmodifiableMap(bytecode); 699 } 700 701 /** 702 * Reset bytecode cache for compiler reuse. 703 */ 704 void clearBytecode() { 705 bytecode.clear(); 706 } 707 708 CompileUnit getFirstCompileUnit() { 709 assert !compileUnits.isEmpty(); 710 return compileUnits.iterator().next(); 711 } 712 713 Set<CompileUnit> getCompileUnits() { 714 return compileUnits; 715 } 716 717 ConstantData getConstantData() { 718 return constantData; 719 } 720 721 CodeInstaller getCodeInstaller() { 722 return installer; 723 } 724 725 void addClass(final String name, final byte[] code) { 726 bytecode.put(name, code); 727 } 728 729 String nextCompileUnitName() { 730 final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE); 731 sb.append(firstCompileUnitName); 732 final int cuid = nextCompileUnitId.getAndIncrement(); 733 if (cuid > 0) { 734 sb.append("$cu").append(cuid); 735 } 736 737 return sb.toString(); 738 } 739 740 /** 741 * Persist current compilation with the given {@code cacheKey}. 742 * @param cacheKey cache key 743 * @param functionNode function node 744 */ 745 public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) { 746 if (cacheKey != null && env._persistent_cache) { 747 // If this is an on-demand compilation create a function initializer for the function being compiled. 748 // Otherwise use function initializer map generated by codegen. 749 final Map<Integer, FunctionInitializer> initializers = new HashMap<>(); 750 if (isOnDemandCompilation()) { 751 initializers.put(functionNode.getId(), new FunctionInitializer(functionNode, getInvalidatedProgramPoints())); 752 } else { 753 for (final CompileUnit compileUnit : getCompileUnits()) { 754 for (final FunctionNode fn : compileUnit.getFunctionNodes()) { 755 initializers.put(fn.getId(), new FunctionInitializer(fn)); 756 } 757 } 758 } 759 final String mainClassName = getFirstCompileUnit().getUnitClassName(); 760 installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId); 761 } 762 } 763 764 /** 765 * Make sure the next compilation id is greater than {@code value}. 766 * @param value compilation id value 767 */ 768 public static void updateCompilationId(final int value) { 769 if (value >= COMPILATION_ID.get()) { 770 COMPILATION_ID.set(value + 1); 771 } 772 } 773 774 CompileUnit addCompileUnit(final long initialWeight) { 775 final CompileUnit compileUnit = createCompileUnit(initialWeight); 776 compileUnits.add(compileUnit); 777 log.fine("Added compile unit ", compileUnit); 778 return compileUnit; 779 } 780 781 CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) { 782 final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict()); 783 final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); 784 classEmitter.begin(); 785 786 return compileUnit; 787 } 788 789 private CompileUnit createCompileUnit(final long initialWeight) { 790 return createCompileUnit(nextCompileUnitName(), initialWeight); 791 } 792 793 boolean isStrict() { 794 return strict; 795 } 796 797 void replaceCompileUnits(final Set<CompileUnit> newUnits) { 798 compileUnits.clear(); 799 compileUnits.addAll(newUnits); 800 } 801 802 CompileUnit findUnit(final long weight) { 803 for (final CompileUnit unit : compileUnits) { 804 if (unit.canHold(weight)) { 805 unit.addWeight(weight); 806 return unit; 807 } 808 } 809 810 return addCompileUnit(weight); 811 } 812 813 /** 814 * Convert a package/class name to a binary name. 815 * 816 * @param name Package/class name. 817 * @return Binary name. 818 */ 819 public static String binaryName(final String name) { 820 return name.replace('/', '.'); 821 } 822 823 RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { 824 assert compiledFunction != null; 825 final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId); 826 assert fn != null : functionId; 827 return fn; 828 } 829 830 boolean isGlobalSymbol(final FunctionNode fn, final String name) { 831 return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name); 832 } 833 834 int[] getContinuationEntryPoints() { 835 return continuationEntryPoints; 836 } 837 838 Type getInvalidatedProgramPointType(final int programPoint) { 839 return invalidatedProgramPoints.get(programPoint); 840 } 841 842 }