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