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