Compiler.java revision 1350:3cb11f4d617e
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.atomic.AtomicInteger; 50import java.util.function.Consumer; 51import java.util.logging.Level; 52import jdk.internal.dynalink.support.NameCodec; 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.logging.DebugLogger; 70import jdk.nashorn.internal.runtime.logging.Loggable; 71import 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") 80public 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<ScriptEnvironment> 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 private final Map<Integer, byte[]> serializedAsts = new HashMap<>(); 164 165 /** 166 * Compilation phases that a compilation goes through 167 */ 168 public static class CompilationPhases implements Iterable<CompilationPhase> { 169 170 /** 171 * Singleton that describes compilation up to the phase where a function can be serialized. 172 */ 173 private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases( 174 "Common initial phases", 175 CompilationPhase.CONSTANT_FOLDING_PHASE, 176 CompilationPhase.LOWERING_PHASE, 177 CompilationPhase.TRANSFORM_BUILTINS_PHASE, 178 CompilationPhase.SPLITTING_PHASE, 179 CompilationPhase.PROGRAM_POINT_PHASE, 180 CompilationPhase.SERIALIZE_SPLIT_PHASE 181 ); 182 183 private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases( 184 "After common phases, before bytecode generator", 185 CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, 186 CompilationPhase.SCOPE_DEPTH_COMPUTATION_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 deserializing, all the way up to (but not 193 * including) generating and installing code. 194 */ 195 public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases( 196 "Recompile serialized function up to bytecode", 197 CompilationPhase.REINITIALIZE_SERIALIZED, 198 COMPILE_SERIALIZABLE_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_SERIALIZABLE, 215 COMPILE_SERIALIZABLE_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_SERIALIZED = new CompilationPhases( 231 "Eager compilation from serializaed state", 232 RECOMPILE_SERIALIZED_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_SERIALIZED_RESTOF = new CompilationPhases( 252 "Compile serialized, rest of", 253 RECOMPILE_SERIALIZED_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_SERIALIZED_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 * Constructor 357 * 358 * @param context context 359 * @param env script environment 360 * @param installer code installer 361 * @param source source to compile 362 * @param errors error manager 363 * @param isStrict is this a strict compilation 364 */ 365 public Compiler( 366 final Context context, 367 final ScriptEnvironment env, 368 final CodeInstaller<ScriptEnvironment> installer, 369 final Source source, 370 final ErrorManager errors, 371 final boolean isStrict) { 372 this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null); 373 } 374 375 /** 376 * Constructor 377 * 378 * @param context context 379 * @param env script environment 380 * @param installer code installer 381 * @param source source to compile 382 * @param errors error manager 383 * @param isStrict is this a strict compilation 384 * @param isOnDemand is this an on demand compilation 385 * @param compiledFunction compiled function, if any 386 * @param types parameter and return value type information, if any is known 387 * @param invalidatedProgramPoints invalidated program points for recompilation 388 * @param typeInformationFile descriptor of the location where type information is persisted 389 * @param continuationEntryPoints continuation entry points for restof method 390 * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator} 391 */ 392 @SuppressWarnings("unused") 393 public Compiler( 394 final Context context, 395 final ScriptEnvironment env, 396 final CodeInstaller<ScriptEnvironment> installer, 397 final Source source, 398 final ErrorManager errors, 399 final boolean isStrict, 400 final boolean isOnDemand, 401 final RecompilableScriptFunctionData compiledFunction, 402 final TypeMap types, 403 final Map<Integer, Type> invalidatedProgramPoints, 404 final Object typeInformationFile, 405 final int[] continuationEntryPoints, 406 final ScriptObject runtimeScope) { 407 this.context = context; 408 this.env = env; 409 this.installer = installer; 410 this.constantData = new ConstantData(); 411 this.compileUnits = CompileUnit.createCompileUnitSet(); 412 this.bytecode = new LinkedHashMap<>(); 413 this.log = initLogger(context); 414 this.source = source; 415 this.errors = errors; 416 this.sourceName = FunctionNode.getSourceName(source); 417 this.onDemand = isOnDemand; 418 this.compiledFunction = compiledFunction; 419 this.types = types; 420 this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints; 421 this.typeInformationFile = typeInformationFile; 422 this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone(); 423 this.typeEvaluator = new TypeEvaluator(this, runtimeScope); 424 this.firstCompileUnitName = firstCompileUnitName(); 425 this.strict = isStrict; 426 427 this.optimistic = env._optimistic_types; 428 } 429 430 private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) { 431 String baseName = new File(source.getName()).getName(); 432 433 final int index = baseName.lastIndexOf(".js"); 434 if (index != -1) { 435 baseName = baseName.substring(0, index); 436 } 437 438 baseName = baseName.replace('.', '_').replace('-', '_'); 439 if (!env._loader_per_compile) { 440 baseName += installer.getUniqueScriptId(); 441 } 442 443 // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char. 444 // While ASM accepts such escapes for method names, field names, it enforces Java identifier 445 // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_' 446 // rather than safe encoding using '\'. 447 final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName); 448 return mangled != null ? mangled : baseName; 449 } 450 451 private static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; 452 private static String replaceDangerChars(final String name) { 453 final int len = name.length(); 454 final StringBuilder buf = new StringBuilder(); 455 for (int i = 0; i < len; i++) { 456 final char ch = name.charAt(i); 457 if (DANGEROUS_CHARS.indexOf(ch) != -1) { 458 buf.append('_'); 459 } else { 460 buf.append(ch); 461 } 462 } 463 return buf.toString(); 464 } 465 466 private String firstCompileUnitName() { 467 final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE). 468 append('/'). 469 append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()). 470 append('$'); 471 472 if (isOnDemandCompilation()) { 473 sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX); 474 } 475 476 if (compilationId > 0) { 477 sb.append(compilationId).append('$'); 478 } 479 480 if (types != null && compiledFunction.getFunctionNodeId() > 0) { 481 sb.append(compiledFunction.getFunctionNodeId()); 482 final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId()); 483 for (final Type t : paramTypes) { 484 sb.append(Type.getShortSignatureDescriptor(t)); 485 } 486 sb.append('$'); 487 } 488 489 sb.append(Compiler.safeSourceName(env, installer, source)); 490 491 return sb.toString(); 492 } 493 494 void declareLocalSymbol(final String symbolName) { 495 typeEvaluator.declareLocalSymbol(symbolName); 496 } 497 498 void setData(final RecompilableScriptFunctionData data) { 499 assert this.compiledFunction == null : data; 500 this.compiledFunction = data; 501 } 502 503 @Override 504 public DebugLogger getLogger() { 505 return log; 506 } 507 508 @Override 509 public DebugLogger initLogger(final Context ctxt) { 510 final boolean optimisticTypes = env._optimistic_types; 511 final boolean lazyCompilation = env._lazy_compilation; 512 513 return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() { 514 @Override 515 public void accept(final DebugLogger newLogger) { 516 if (!lazyCompilation) { 517 newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting."); 518 } 519 newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED."); 520 } 521 }); 522 } 523 524 ScriptEnvironment getScriptEnvironment() { 525 return env; 526 } 527 528 boolean isOnDemandCompilation() { 529 return onDemand; 530 } 531 532 boolean useOptimisticTypes() { 533 return optimistic; 534 } 535 536 Context getContext() { 537 return context; 538 } 539 540 Type getOptimisticType(final Optimistic node) { 541 return typeEvaluator.getOptimisticType(node); 542 } 543 544 /** 545 * Returns true if the expression can be safely evaluated, and its value is an object known to always use 546 * String as the type of its property names retrieved through 547 * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its 548 * property name types. 549 * @param expr the expression to test 550 * @return true if the expression can be safely evaluated, and its value is an object known to always use 551 * String as the type of its property iterators. 552 */ 553 boolean hasStringPropertyIterator(final Expression expr) { 554 return typeEvaluator.hasStringPropertyIterator(expr); 555 } 556 557 void addInvalidatedProgramPoint(final int programPoint, final Type type) { 558 invalidatedProgramPoints.put(programPoint, type); 559 } 560 561 562 /** 563 * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The 564 * copy is not live with regard to changes in state in this compiler instance, and is mutable. 565 * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types. 566 */ 567 public Map<Integer, Type> getInvalidatedProgramPoints() { 568 return invalidatedProgramPoints.isEmpty() ? null : new TreeMap<>(invalidatedProgramPoints); 569 } 570 571 TypeMap getTypeMap() { 572 return types; 573 } 574 575 MethodType getCallSiteType(final FunctionNode fn) { 576 if (types == null || !isOnDemandCompilation()) { 577 return null; 578 } 579 return types.getCallSiteType(fn); 580 } 581 582 Type getParamType(final FunctionNode fn, final int pos) { 583 return types == null ? null : types.get(fn, pos); 584 } 585 586 /** 587 * Do a compilation job 588 * 589 * @param functionNode function node to compile 590 * @param phases phases of compilation transforms to apply to function 591 592 * @return transformed function 593 * 594 * @throws CompilationException if error occurs during compilation 595 */ 596 public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException { 597 if (log.isEnabled()) { 598 log.info(">> Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc())); 599 log.indent(); 600 } 601 602 final String name = DebugLogger.quote(functionNode.getName()); 603 604 FunctionNode newFunctionNode = functionNode; 605 606 for (final String reservedName : RESERVED_NAMES) { 607 newFunctionNode.uniqueName(reservedName); 608 } 609 610 final boolean info = log.isLoggable(Level.INFO); 611 612 final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null; 613 614 long time = 0L; 615 616 for (final CompilationPhase phase : phases) { 617 log.fine(phase, " starting for ", name); 618 619 try { 620 newFunctionNode = phase.apply(this, phases, newFunctionNode); 621 } catch (final ParserException error) { 622 errors.error(error); 623 if (env._dump_on_error) { 624 error.printStackTrace(env.getErr()); 625 } 626 return null; 627 } 628 629 log.fine(phase, " done for function ", quote(name)); 630 631 if (env._print_mem_usage) { 632 printMemoryUsage(functionNode, phase.toString()); 633 } 634 635 time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L); 636 } 637 638 if (typeInformationFile != null && !phases.isRestOfCompilation()) { 639 OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints); 640 } 641 642 log.unindent(); 643 644 if (info) { 645 final StringBuilder sb = new StringBuilder("<< Finished compile job for "); 646 sb.append(newFunctionNode.getSource()). 647 append(':'). 648 append(quote(newFunctionNode.getName())); 649 650 if (time > 0L && timeLogger != null) { 651 assert env.isTimingEnabled(); 652 sb.append(" in ").append(time).append(" ms"); 653 } 654 log.info(sb); 655 } 656 657 return newFunctionNode; 658 } 659 660 Source getSource() { 661 return source; 662 } 663 664 Map<String, byte[]> getBytecode() { 665 return Collections.unmodifiableMap(bytecode); 666 } 667 668 /** 669 * Reset bytecode cache for compiler reuse. 670 */ 671 void clearBytecode() { 672 bytecode.clear(); 673 } 674 675 CompileUnit getFirstCompileUnit() { 676 assert !compileUnits.isEmpty(); 677 return compileUnits.iterator().next(); 678 } 679 680 Set<CompileUnit> getCompileUnits() { 681 return compileUnits; 682 } 683 684 ConstantData getConstantData() { 685 return constantData; 686 } 687 688 CodeInstaller<ScriptEnvironment> getCodeInstaller() { 689 return installer; 690 } 691 692 void addClass(final String name, final byte[] code) { 693 bytecode.put(name, code); 694 } 695 696 String nextCompileUnitName() { 697 final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE); 698 sb.append(firstCompileUnitName); 699 final int cuid = nextCompileUnitId.getAndIncrement(); 700 if (cuid > 0) { 701 sb.append("$cu").append(cuid); 702 } 703 704 return sb.toString(); 705 } 706 707 /** 708 * Persist current compilation with the given {@code cacheKey}. 709 * @param cacheKey cache key 710 * @param functionNode function node 711 */ 712 public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) { 713 if (cacheKey != null && env._persistent_cache) { 714 // If this is an on-demand compilation create a function initializer for the function being compiled. 715 // Otherwise use function initializer map generated by codegen. 716 final Map<Integer, FunctionInitializer> initializers = new HashMap<>(); 717 if (isOnDemandCompilation()) { 718 initializers.put(functionNode.getId(), new FunctionInitializer(functionNode, getInvalidatedProgramPoints())); 719 } else { 720 for (final CompileUnit compileUnit : getCompileUnits()) { 721 for (final FunctionNode fn : compileUnit.getFunctionNodes()) { 722 initializers.put(fn.getId(), new FunctionInitializer(fn)); 723 } 724 } 725 } 726 final String mainClassName = getFirstCompileUnit().getUnitClassName(); 727 installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId); 728 } 729 } 730 731 /** 732 * Make sure the next compilation id is greater than {@code value}. 733 * @param value compilation id value 734 */ 735 public static void updateCompilationId(final int value) { 736 if (value >= COMPILATION_ID.get()) { 737 COMPILATION_ID.set(value + 1); 738 } 739 } 740 741 CompileUnit addCompileUnit(final long initialWeight) { 742 final CompileUnit compileUnit = createCompileUnit(initialWeight); 743 compileUnits.add(compileUnit); 744 log.fine("Added compile unit ", compileUnit); 745 return compileUnit; 746 } 747 748 CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) { 749 final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict()); 750 final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); 751 classEmitter.begin(); 752 753 return compileUnit; 754 } 755 756 private CompileUnit createCompileUnit(final long initialWeight) { 757 return createCompileUnit(nextCompileUnitName(), initialWeight); 758 } 759 760 boolean isStrict() { 761 return strict; 762 } 763 764 void replaceCompileUnits(final Set<CompileUnit> newUnits) { 765 compileUnits.clear(); 766 compileUnits.addAll(newUnits); 767 } 768 769 void serializeAst(final FunctionNode fn) { 770 serializedAsts.put(fn.getId(), AstSerializer.serialize(fn)); 771 } 772 773 byte[] removeSerializedAst(final int fnId) { 774 return serializedAsts.remove(fnId); 775 } 776 777 CompileUnit findUnit(final long weight) { 778 for (final CompileUnit unit : compileUnits) { 779 if (unit.canHold(weight)) { 780 unit.addWeight(weight); 781 return unit; 782 } 783 } 784 785 return addCompileUnit(weight); 786 } 787 788 /** 789 * Convert a package/class name to a binary name. 790 * 791 * @param name Package/class name. 792 * @return Binary name. 793 */ 794 public static String binaryName(final String name) { 795 return name.replace('/', '.'); 796 } 797 798 RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { 799 assert compiledFunction != null; 800 final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId); 801 assert fn != null : functionId; 802 return fn; 803 } 804 805 boolean isGlobalSymbol(final FunctionNode fn, final String name) { 806 return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name); 807 } 808 809 int[] getContinuationEntryPoints() { 810 return continuationEntryPoints; 811 } 812 813 Type getInvalidatedProgramPointType(final int programPoint) { 814 return invalidatedProgramPoints.get(programPoint); 815 } 816 817 private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) { 818 if (!log.isEnabled()) { 819 return; 820 } 821 822 log.info(phaseName, "finished. Doing IR size calculation..."); 823 824 final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification()); 825 osc.calculateObjectSize(functionNode); 826 827 final List<ClassHistogramElement> list = osc.getClassHistogram(); 828 final StringBuilder sb = new StringBuilder(); 829 final long totalSize = osc.calculateObjectSize(functionNode); 830 831 sb.append(phaseName). 832 append(" Total size = "). 833 append(totalSize / 1024 / 1024). 834 append("MB"); 835 log.info(sb); 836 837 Collections.sort(list, new Comparator<ClassHistogramElement>() { 838 @Override 839 public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) { 840 final long diff = o1.getBytes() - o2.getBytes(); 841 if (diff < 0) { 842 return 1; 843 } else if (diff > 0) { 844 return -1; 845 } else { 846 return 0; 847 } 848 } 849 }); 850 for (final ClassHistogramElement e : list) { 851 final String line = String.format(" %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances()); 852 log.info(line); 853 if (e.getBytes() < totalSize / 200) { 854 log.info(" ..."); 855 break; // never mind, so little memory anyway 856 } 857 } 858 } 859} 860