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