Compiler.java revision 1522:d2eb81e4ddc8
1219820Sjeff/* 2219820Sjeff * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3219820Sjeff * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4219820Sjeff * 5219820Sjeff * This code is free software; you can redistribute it and/or modify it 6219820Sjeff * under the terms of the GNU General Public License version 2 only, as 7219820Sjeff * published by the Free Software Foundation. Oracle designates this 8219820Sjeff * particular file as subject to the "Classpath" exception as provided 9219820Sjeff * by Oracle in the LICENSE file that accompanied this code. 10219820Sjeff * 11219820Sjeff * This code is distributed in the hope that it will be useful, but WITHOUT 12219820Sjeff * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13219820Sjeff * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14219820Sjeff * version 2 for more details (a copy is included in the LICENSE file that 15219820Sjeff * accompanied this code). 16219820Sjeff * 17219820Sjeff * You should have received a copy of the GNU General Public License version 18219820Sjeff * 2 along with this work; if not, write to the Free Software Foundation, 19219820Sjeff * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20219820Sjeff * 21219820Sjeff * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22219820Sjeff * or visit www.oracle.com if you need additional information or have any 23219820Sjeff * questions. 24219820Sjeff */ 25219820Sjeff 26219820Sjeffpackage jdk.nashorn.internal.codegen; 27219820Sjeff 28219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; 29219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 30219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; 31219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 32219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 33219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 34219820Sjeffimport static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; 35219820Sjeff 36219820Sjeffimport java.io.File; 37219820Sjeffimport java.lang.invoke.MethodType; 38219820Sjeffimport java.util.ArrayList; 39219820Sjeffimport java.util.Arrays; 40219820Sjeffimport java.util.Collections; 41219820Sjeffimport java.util.Comparator; 42219820Sjeffimport java.util.HashMap; 43219820Sjeffimport java.util.Iterator; 44219820Sjeffimport java.util.LinkedHashMap; 45219820Sjeffimport java.util.List; 46219820Sjeffimport java.util.Map; 47219820Sjeffimport java.util.Set; 48219820Sjeffimport java.util.TreeMap; 49219820Sjeffimport java.util.concurrent.TimeUnit; 50219820Sjeffimport java.util.concurrent.atomic.AtomicInteger; 51219820Sjeffimport java.util.function.Consumer; 52219820Sjeffimport java.util.logging.Level; 53219820Sjeffimport jdk.nashorn.internal.codegen.types.Type; 54219820Sjeffimport jdk.nashorn.internal.ir.Expression; 55219820Sjeffimport jdk.nashorn.internal.ir.FunctionNode; 56219820Sjeffimport jdk.nashorn.internal.ir.Optimistic; 57219820Sjeffimport jdk.nashorn.internal.ir.debug.ClassHistogramElement; 58219820Sjeffimport jdk.nashorn.internal.ir.debug.ObjectSizeCalculator; 59219820Sjeffimport jdk.nashorn.internal.runtime.CodeInstaller; 60219820Sjeffimport jdk.nashorn.internal.runtime.Context; 61219820Sjeffimport jdk.nashorn.internal.runtime.ErrorManager; 62219820Sjeffimport jdk.nashorn.internal.runtime.FunctionInitializer; 63219820Sjeffimport jdk.nashorn.internal.runtime.ParserException; 64219820Sjeffimport jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; 65219820Sjeffimport jdk.nashorn.internal.runtime.ScriptEnvironment; 66219820Sjeffimport jdk.nashorn.internal.runtime.ScriptObject; 67219820Sjeffimport jdk.nashorn.internal.runtime.ScriptRuntime; 68219820Sjeffimport jdk.nashorn.internal.runtime.Source; 69219820Sjeffimport jdk.nashorn.internal.runtime.linker.NameCodec; 70219820Sjeffimport jdk.nashorn.internal.runtime.logging.DebugLogger; 71219820Sjeffimport jdk.nashorn.internal.runtime.logging.Loggable; 72219820Sjeffimport jdk.nashorn.internal.runtime.logging.Logger; 73219820Sjeff 74219820Sjeff/** 75219820Sjeff * Responsible for converting JavaScripts to java byte code. Main entry 76219820Sjeff * point for code generator. The compiler may also install classes given some 77219820Sjeff * predefined Code installation policy, given to it at construction time. 78219820Sjeff * @see CodeInstaller 79219820Sjeff */ 80219820Sjeff@Logger(name="compiler") 81219820Sjeffpublic final class Compiler implements Loggable { 82219820Sjeff 83219820Sjeff /** Name of the scripts package */ 84219820Sjeff public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts"; 85219820Sjeff 86219820Sjeff /** Name of the objects package */ 87219820Sjeff public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects"; 88219820Sjeff 89219820Sjeff private final ScriptEnvironment env; 90219820Sjeff 91219820Sjeff private final Source source; 92219820Sjeff 93219820Sjeff private final String sourceName; 94219820Sjeff 95219820Sjeff private final ErrorManager errors; 96219820Sjeff 97219820Sjeff private final boolean optimistic; 98219820Sjeff 99219820Sjeff private final Map<String, byte[]> bytecode; 100219820Sjeff 101219820Sjeff private final Set<CompileUnit> compileUnits; 102219820Sjeff 103219820Sjeff private final ConstantData constantData; 104219820Sjeff 105219820Sjeff private final CodeInstaller installer; 106219820Sjeff 107219820Sjeff /** logger for compiler, trampolines and related code generation events 108219820Sjeff * that affect classes */ 109219820Sjeff private final DebugLogger log; 110219820Sjeff 111219820Sjeff private final Context context; 112219820Sjeff 113219820Sjeff private final TypeMap types; 114219820Sjeff 115219820Sjeff // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly 116219820Sjeff // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations). 117219820Sjeff private final TypeEvaluator typeEvaluator; 118219820Sjeff 119219820Sjeff private final boolean strict; 120219820Sjeff 121219820Sjeff private final boolean onDemand; 122219820Sjeff 123219820Sjeff /** 124219820Sjeff * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means 125219820Sjeff * that using whatever was at program point 17 as an int failed. 126219820Sjeff */ 127219820Sjeff private final Map<Integer, Type> invalidatedProgramPoints; 128219820Sjeff 129219820Sjeff /** 130219820Sjeff * Descriptor of the location where we write the type information after compilation. 131219820Sjeff */ 132219820Sjeff private final Object typeInformationFile; 133219820Sjeff 134219820Sjeff /** 135219820Sjeff * Compile unit name of first compile unit - this prefix will be used for all 136219820Sjeff * classes that a compilation generates. 137219820Sjeff */ 138219820Sjeff private final String firstCompileUnitName; 139219820Sjeff 140219820Sjeff /** 141219820Sjeff * Contains the program point that should be used as the continuation entry point, as well as all previous 142219820Sjeff * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if 143219820Sjeff * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program 144219820Sjeff * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program 145219820Sjeff * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only 146219820Sjeff * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have 147219820Sjeff * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on. 148219820Sjeff */ 149219820Sjeff private final int[] continuationEntryPoints; 150219820Sjeff 151219820Sjeff /** 152219820Sjeff * ScriptFunction data for what is being compile, where applicable. 153219820Sjeff * TODO: make this immutable, propagate it through the CompilationPhases 154219820Sjeff */ 155219820Sjeff private RecompilableScriptFunctionData compiledFunction; 156219820Sjeff 157219820Sjeff /** 158219820Sjeff * Most compile unit names are longer than the default StringBuilder buffer, 159219820Sjeff * worth startup performance when massive class generation is going on to increase 160219820Sjeff * this 161219820Sjeff */ 162219820Sjeff private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; 163219820Sjeff 164219820Sjeff /** 165219820Sjeff * Compilation phases that a compilation goes through 166219820Sjeff */ 167219820Sjeff public static class CompilationPhases implements Iterable<CompilationPhase> { 168219820Sjeff 169219820Sjeff /** 170219820Sjeff * Singleton that describes compilation up to the phase where a function can be cached. 171219820Sjeff */ 172219820Sjeff private final static CompilationPhases COMPILE_UPTO_CACHED = new CompilationPhases( 173219820Sjeff "Common initial phases", 174219820Sjeff CompilationPhase.CONSTANT_FOLDING_PHASE, 175219820Sjeff CompilationPhase.LOWERING_PHASE, 176219820Sjeff CompilationPhase.APPLY_SPECIALIZATION_PHASE, 177219820Sjeff CompilationPhase.SPLITTING_PHASE, 178219820Sjeff CompilationPhase.PROGRAM_POINT_PHASE, 179219820Sjeff CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, 180219820Sjeff CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, 181219820Sjeff CompilationPhase.CACHE_AST_PHASE 182219820Sjeff ); 183219820Sjeff 184219820Sjeff private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( 185219820Sjeff "After common phases, before bytecode generator", 186219820Sjeff CompilationPhase.DECLARE_LOCAL_SYMBOLS_PHASE, 187219820Sjeff CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, 188219820Sjeff CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE 189219820Sjeff ); 190219820Sjeff 191219820Sjeff /** 192219820Sjeff * Singleton that describes additional steps to be taken after retrieving a cached function, all the 193219820Sjeff * way up to (but not including) generating and installing code. 194219820Sjeff */ 195219820Sjeff public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( 196219820Sjeff "Recompile cached function up to bytecode", 197219820Sjeff CompilationPhase.REINITIALIZE_CACHED, 198219820Sjeff COMPILE_CACHED_UPTO_BYTECODE 199219820Sjeff ); 200219820Sjeff 201219820Sjeff /** 202219820Sjeff * Singleton that describes back end of method generation, given that we have generated the normal 203219820Sjeff * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} 204219820Sjeff */ 205219820Sjeff public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL = new CompilationPhases( 206219820Sjeff "Generate bytecode and install", 207219820Sjeff CompilationPhase.BYTECODE_GENERATION_PHASE, 208219820Sjeff CompilationPhase.INSTALL_PHASE 209219820Sjeff ); 210219820Sjeff 211219820Sjeff /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ 212219820Sjeff public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( 213219820Sjeff "Compile upto bytecode", 214219820Sjeff COMPILE_UPTO_CACHED, 215219820Sjeff COMPILE_CACHED_UPTO_BYTECODE); 216219820Sjeff 217219820Sjeff /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ 218219820Sjeff public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( 219219820Sjeff "Compile without install", 220219820Sjeff COMPILE_UPTO_BYTECODE, 221219820Sjeff CompilationPhase.BYTECODE_GENERATION_PHASE); 222219820Sjeff 223219820Sjeff /** Singleton that describes a standard eager compilation - this includes code installation */ 224219820Sjeff public final static CompilationPhases COMPILE_ALL = new CompilationPhases( 225219820Sjeff "Full eager compilation", 226219820Sjeff COMPILE_UPTO_BYTECODE, 227219820Sjeff GENERATE_BYTECODE_AND_INSTALL); 228219820Sjeff 229219820Sjeff /** Singleton that describes a full compilation - this includes code installation - from serialized state*/ 230219820Sjeff public final static CompilationPhases COMPILE_ALL_CACHED = new CompilationPhases( 231219820Sjeff "Eager compilation from serializaed state", 232219820Sjeff RECOMPILE_CACHED_UPTO_BYTECODE, 233219820Sjeff GENERATE_BYTECODE_AND_INSTALL); 234219820Sjeff 235219820Sjeff /** 236219820Sjeff * Singleton that describes restOf method generation, given that we have generated the normal 237219820Sjeff * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} 238219820Sjeff */ 239219820Sjeff public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL_RESTOF = new CompilationPhases( 240219820Sjeff "Generate bytecode and install - RestOf method", 241219820Sjeff CompilationPhase.REUSE_COMPILE_UNITS_PHASE, 242219820Sjeff GENERATE_BYTECODE_AND_INSTALL); 243219820Sjeff 244219820Sjeff /** Compile all for a rest of method */ 245219820Sjeff public final static CompilationPhases COMPILE_ALL_RESTOF = new CompilationPhases( 246219820Sjeff "Compile all, rest of", 247219820Sjeff COMPILE_UPTO_BYTECODE, 248219820Sjeff GENERATE_BYTECODE_AND_INSTALL_RESTOF); 249219820Sjeff 250219820Sjeff /** Compile from serialized for a rest of method */ 251219820Sjeff public final static CompilationPhases COMPILE_CACHED_RESTOF = new CompilationPhases( 252219820Sjeff "Compile serialized, rest of", 253219820Sjeff RECOMPILE_CACHED_UPTO_BYTECODE, 254219820Sjeff GENERATE_BYTECODE_AND_INSTALL_RESTOF); 255219820Sjeff 256219820Sjeff private final List<CompilationPhase> phases; 257219820Sjeff 258219820Sjeff private final String desc; 259219820Sjeff 260219820Sjeff private CompilationPhases(final String desc, final CompilationPhase... phases) { 261219820Sjeff this(desc, Arrays.asList(phases)); 262219820Sjeff } 263219820Sjeff 264219820Sjeff private CompilationPhases(final String desc, final CompilationPhases base, final CompilationPhase... phases) { 265219820Sjeff this(desc, concat(base.phases, Arrays.asList(phases))); 266219820Sjeff } 267219820Sjeff 268219820Sjeff private CompilationPhases(final String desc, final CompilationPhase first, final CompilationPhases rest) { 269219820Sjeff this(desc, concat(Collections.singletonList(first), rest.phases)); 270219820Sjeff } 271219820Sjeff 272219820Sjeff private CompilationPhases(final String desc, final CompilationPhases base) { 273219820Sjeff this(desc, base.phases); 274219820Sjeff } 275219820Sjeff 276219820Sjeff private CompilationPhases(final String desc, final CompilationPhases... bases) { 277219820Sjeff this(desc, concatPhases(bases)); 278219820Sjeff } 279219820Sjeff 280219820Sjeff private CompilationPhases(final String desc, final List<CompilationPhase> phases) { 281219820Sjeff this.desc = desc; 282219820Sjeff this.phases = phases; 283219820Sjeff } 284219820Sjeff 285219820Sjeff private static List<CompilationPhase> concatPhases(final CompilationPhases[] bases) { 286219820Sjeff final ArrayList<CompilationPhase> l = new ArrayList<>(); 287219820Sjeff for(final CompilationPhases base: bases) { 288219820Sjeff l.addAll(base.phases); 289219820Sjeff } 290219820Sjeff l.trimToSize(); 291219820Sjeff return l; 292219820Sjeff } 293219820Sjeff 294219820Sjeff private static <T> List<T> concat(final List<T> l1, final List<T> l2) { 295219820Sjeff final ArrayList<T> l = new ArrayList<>(l1); 296219820Sjeff l.addAll(l2); 297219820Sjeff l.trimToSize(); 298219820Sjeff return l; 299219820Sjeff } 300219820Sjeff 301219820Sjeff @Override 302219820Sjeff public String toString() { 303219820Sjeff return "'" + desc + "' " + phases.toString(); 304219820Sjeff } 305219820Sjeff 306219820Sjeff boolean contains(final CompilationPhase phase) { 307219820Sjeff return phases.contains(phase); 308219820Sjeff } 309219820Sjeff 310219820Sjeff @Override 311219820Sjeff public Iterator<CompilationPhase> iterator() { 312219820Sjeff return phases.iterator(); 313219820Sjeff } 314219820Sjeff 315219820Sjeff boolean isRestOfCompilation() { 316219820Sjeff return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_CACHED_RESTOF; 317219820Sjeff } 318219820Sjeff 319219820Sjeff String getDesc() { 320219820Sjeff return desc; 321219820Sjeff } 322219820Sjeff 323219820Sjeff String toString(final String prefix) { 324219820Sjeff final StringBuilder sb = new StringBuilder(); 325219820Sjeff for (final CompilationPhase phase : phases) { 326219820Sjeff sb.append(prefix).append(phase).append('\n'); 327219820Sjeff } 328219820Sjeff return sb.toString(); 329219820Sjeff } 330219820Sjeff } 331219820Sjeff 332219820Sjeff /** 333219820Sjeff * This array contains names that need to be reserved at the start 334219820Sjeff * of a compile, to avoid conflict with variable names later introduced. 335219820Sjeff * See {@link CompilerConstants} for special names used for structures 336219820Sjeff * during a compile. 337219820Sjeff */ 338219820Sjeff private static String[] RESERVED_NAMES = { 339219820Sjeff SCOPE.symbolName(), 340219820Sjeff THIS.symbolName(), 341219820Sjeff RETURN.symbolName(), 342219820Sjeff CALLEE.symbolName(), 343219820Sjeff VARARGS.symbolName(), 344219820Sjeff ARGUMENTS.symbolName() 345219820Sjeff }; 346219820Sjeff 347219820Sjeff // per instance 348219820Sjeff private final int compilationId = COMPILATION_ID.getAndIncrement(); 349219820Sjeff 350219820Sjeff // per instance 351219820Sjeff private final AtomicInteger nextCompileUnitId = new AtomicInteger(0); 352219820Sjeff 353219820Sjeff private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0); 354219820Sjeff 355219820Sjeff /** 356219820Sjeff * Creates a new compiler instance for initial compilation of a script. 357219820Sjeff * 358219820Sjeff * @param installer code installer 359219820Sjeff * @param source source to compile 360219820Sjeff * @param errors error manager 361219820Sjeff * @param isStrict is this a strict compilation 362219820Sjeff * @return a new compiler 363219820Sjeff */ 364219820Sjeff public static Compiler forInitialCompilation( 365219820Sjeff final CodeInstaller installer, 366219820Sjeff final Source source, 367219820Sjeff final ErrorManager errors, 368219820Sjeff final boolean isStrict) { 369219820Sjeff return new Compiler(installer.getContext(), installer, source, errors, isStrict); 370219820Sjeff } 371219820Sjeff 372219820Sjeff /** 373219820Sjeff * Creates a compiler without a code installer. It can only be used to compile code, not install the 374219820Sjeff * generated classes and as such it is useful only for implementation of {@code --compile-only} command 375219820Sjeff * line option. 376219820Sjeff * @param context the current context 377219820Sjeff * @param source source to compile 378219820Sjeff * @param isStrict is this a strict compilation 379219820Sjeff * @return a new compiler 380219820Sjeff */ 381219820Sjeff public static Compiler forNoInstallerCompilation( 382219820Sjeff final Context context, 383219820Sjeff final Source source, 384219820Sjeff final boolean isStrict) { 385219820Sjeff return new Compiler(context, null, source, context.getErrorManager(), isStrict); 386219820Sjeff } 387219820Sjeff 388219820Sjeff /** 389219820Sjeff * Creates a compiler for an on-demand compilation job. 390219820Sjeff * 391219820Sjeff * @param installer code installer 392219820Sjeff * @param source source to compile 393219820Sjeff * @param isStrict is this a strict compilation 394219820Sjeff * @param compiledFunction compiled function, if any 395219820Sjeff * @param types parameter and return value type information, if any is known 396219820Sjeff * @param invalidatedProgramPoints invalidated program points for recompilation 397219820Sjeff * @param typeInformationFile descriptor of the location where type information is persisted 398219820Sjeff * @param continuationEntryPoints continuation entry points for restof method 399219820Sjeff * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator} 400219820Sjeff * @return a new compiler 401219820Sjeff */ 402219820Sjeff public static Compiler forOnDemandCompilation( 403219820Sjeff final CodeInstaller installer, 404219820Sjeff final Source source, 405219820Sjeff final boolean isStrict, 406219820Sjeff final RecompilableScriptFunctionData compiledFunction, 407219820Sjeff final TypeMap types, 408219820Sjeff final Map<Integer, Type> invalidatedProgramPoints, 409219820Sjeff final Object typeInformationFile, 410219820Sjeff final int[] continuationEntryPoints, 411219820Sjeff final ScriptObject runtimeScope) { 412219820Sjeff final Context context = installer.getContext(); 413219820Sjeff return new Compiler(context, installer, source, context.getErrorManager(), isStrict, true, 414219820Sjeff compiledFunction, types, invalidatedProgramPoints, typeInformationFile, 415219820Sjeff continuationEntryPoints, runtimeScope); 416219820Sjeff } 417219820Sjeff 418219820Sjeff /** 419219820Sjeff * Convenience constructor for non on-demand compiler instances. 420219820Sjeff */ 421219820Sjeff private Compiler( 422219820Sjeff final Context context, 423219820Sjeff final CodeInstaller installer, 424219820Sjeff final Source source, 425219820Sjeff final ErrorManager errors, 426219820Sjeff final boolean isStrict) { 427219820Sjeff this(context, installer, source, errors, isStrict, false, null, null, null, null, null, null); 428219820Sjeff } 429219820Sjeff 430219820Sjeff private Compiler( 431219820Sjeff final Context context, 432219820Sjeff final CodeInstaller installer, 433219820Sjeff final Source source, 434219820Sjeff final ErrorManager errors, 435219820Sjeff final boolean isStrict, 436219820Sjeff final boolean isOnDemand, 437219820Sjeff final RecompilableScriptFunctionData compiledFunction, 438219820Sjeff final TypeMap types, 439219820Sjeff final Map<Integer, Type> invalidatedProgramPoints, 440219820Sjeff final Object typeInformationFile, 441219820Sjeff final int[] continuationEntryPoints, 442219820Sjeff final ScriptObject runtimeScope) { 443219820Sjeff this.context = context; 444219820Sjeff this.env = context.getEnv(); 445219820Sjeff this.installer = installer; 446219820Sjeff this.constantData = new ConstantData(); 447219820Sjeff this.compileUnits = CompileUnit.createCompileUnitSet(); 448219820Sjeff this.bytecode = new LinkedHashMap<>(); 449219820Sjeff this.log = initLogger(context); 450219820Sjeff this.source = source; 451219820Sjeff this.errors = errors; 452219820Sjeff this.sourceName = FunctionNode.getSourceName(source); 453219820Sjeff this.onDemand = isOnDemand; 454219820Sjeff this.compiledFunction = compiledFunction; 455219820Sjeff this.types = types; 456219820Sjeff this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<>() : invalidatedProgramPoints; 457219820Sjeff this.typeInformationFile = typeInformationFile; 458219820Sjeff this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone(); 459219820Sjeff this.typeEvaluator = new TypeEvaluator(this, runtimeScope); 460219820Sjeff this.firstCompileUnitName = firstCompileUnitName(); 461219820Sjeff this.strict = isStrict; 462219820Sjeff 463219820Sjeff this.optimistic = env._optimistic_types; 464219820Sjeff } 465219820Sjeff 466219820Sjeff private String safeSourceName() { 467219820Sjeff String baseName = new File(source.getName()).getName(); 468219820Sjeff 469219820Sjeff final int index = baseName.lastIndexOf(".js"); 470219820Sjeff if (index != -1) { 471219820Sjeff baseName = baseName.substring(0, index); 472219820Sjeff } 473219820Sjeff 474219820Sjeff baseName = baseName.replace('.', '_').replace('-', '_'); 475219820Sjeff if (!env._loader_per_compile) { 476219820Sjeff baseName += installer.getUniqueScriptId(); 477219820Sjeff } 478219820Sjeff 479219820Sjeff // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char. 480219820Sjeff // While ASM accepts such escapes for method names, field names, it enforces Java identifier 481219820Sjeff // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_' 482219820Sjeff // rather than safe encoding using '\'. 483219820Sjeff final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName); 484219820Sjeff return mangled != null ? mangled : baseName; 485219820Sjeff } 486219820Sjeff 487219820Sjeff private static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; 488219820Sjeff private static String replaceDangerChars(final String name) { 489219820Sjeff final int len = name.length(); 490219820Sjeff final StringBuilder buf = new StringBuilder(); 491219820Sjeff for (int i = 0; i < len; i++) { 492219820Sjeff final char ch = name.charAt(i); 493219820Sjeff if (DANGEROUS_CHARS.indexOf(ch) != -1) { 494219820Sjeff buf.append('_'); 495219820Sjeff } else { 496219820Sjeff buf.append(ch); 497219820Sjeff } 498219820Sjeff } 499219820Sjeff return buf.toString(); 500219820Sjeff } 501219820Sjeff 502219820Sjeff private String firstCompileUnitName() { 503219820Sjeff final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE). 504219820Sjeff append('/'). 505219820Sjeff append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()). 506219820Sjeff append('$'); 507219820Sjeff 508219820Sjeff if (isOnDemandCompilation()) { 509219820Sjeff sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX); 510219820Sjeff } 511219820Sjeff 512219820Sjeff if (compilationId > 0) { 513219820Sjeff sb.append(compilationId).append('$'); 514219820Sjeff } 515219820Sjeff 516219820Sjeff if (types != null && compiledFunction.getFunctionNodeId() > 0) { 517219820Sjeff sb.append(compiledFunction.getFunctionNodeId()); 518219820Sjeff final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId()); 519219820Sjeff for (final Type t : paramTypes) { 520219820Sjeff sb.append(Type.getShortSignatureDescriptor(t)); 521219820Sjeff } 522219820Sjeff sb.append('$'); 523219820Sjeff } 524219820Sjeff 525219820Sjeff sb.append(safeSourceName()); 526219820Sjeff 527219820Sjeff return sb.toString(); 528219820Sjeff } 529219820Sjeff 530219820Sjeff void declareLocalSymbol(final String symbolName) { 531219820Sjeff typeEvaluator.declareLocalSymbol(symbolName); 532219820Sjeff } 533219820Sjeff 534219820Sjeff void setData(final RecompilableScriptFunctionData data) { 535219820Sjeff assert this.compiledFunction == null : data; 536219820Sjeff this.compiledFunction = data; 537219820Sjeff } 538219820Sjeff 539219820Sjeff @Override 540219820Sjeff public DebugLogger getLogger() { 541219820Sjeff return log; 542219820Sjeff } 543219820Sjeff 544219820Sjeff @Override 545219820Sjeff public DebugLogger initLogger(final Context ctxt) { 546219820Sjeff final boolean optimisticTypes = env._optimistic_types; 547219820Sjeff final boolean lazyCompilation = env._lazy_compilation; 548219820Sjeff 549219820Sjeff return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() { 550219820Sjeff @Override 551219820Sjeff public void accept(final DebugLogger newLogger) { 552219820Sjeff if (!lazyCompilation) { 553219820Sjeff newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting."); 554219820Sjeff } 555219820Sjeff newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED."); 556219820Sjeff } 557219820Sjeff }); 558219820Sjeff } 559219820Sjeff 560219820Sjeff ScriptEnvironment getScriptEnvironment() { 561219820Sjeff return env; 562219820Sjeff } 563219820Sjeff 564219820Sjeff boolean isOnDemandCompilation() { 565219820Sjeff return onDemand; 566219820Sjeff } 567219820Sjeff 568219820Sjeff boolean useOptimisticTypes() { 569219820Sjeff return optimistic; 570219820Sjeff } 571219820Sjeff 572219820Sjeff Context getContext() { 573219820Sjeff return context; 574219820Sjeff } 575219820Sjeff 576219820Sjeff Type getOptimisticType(final Optimistic node) { 577219820Sjeff return typeEvaluator.getOptimisticType(node); 578219820Sjeff } 579219820Sjeff 580219820Sjeff /** 581219820Sjeff * Returns true if the expression can be safely evaluated, and its value is an object known to always use 582219820Sjeff * String as the type of its property names retrieved through 583219820Sjeff * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its 584219820Sjeff * property name types. 585219820Sjeff * @param expr the expression to test 586219820Sjeff * @return true if the expression can be safely evaluated, and its value is an object known to always use 587219820Sjeff * String as the type of its property iterators. 588219820Sjeff */ 589219820Sjeff boolean hasStringPropertyIterator(final Expression expr) { 590219820Sjeff return typeEvaluator.hasStringPropertyIterator(expr); 591219820Sjeff } 592219820Sjeff 593219820Sjeff void addInvalidatedProgramPoint(final int programPoint, final Type type) { 594219820Sjeff invalidatedProgramPoints.put(programPoint, type); 595219820Sjeff } 596219820Sjeff 597219820Sjeff 598219820Sjeff /** 599219820Sjeff * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The 600219820Sjeff * copy is not live with regard to changes in state in this compiler instance, and is mutable. 601219820Sjeff * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types. 602219820Sjeff */ 603219820Sjeff public Map<Integer, Type> getInvalidatedProgramPoints() { 604219820Sjeff return invalidatedProgramPoints.isEmpty() ? null : new TreeMap<>(invalidatedProgramPoints); 605219820Sjeff } 606219820Sjeff 607219820Sjeff TypeMap getTypeMap() { 608219820Sjeff return types; 609219820Sjeff } 610219820Sjeff 611219820Sjeff MethodType getCallSiteType(final FunctionNode fn) { 612219820Sjeff if (types == null || !isOnDemandCompilation()) { 613219820Sjeff return null; 614219820Sjeff } 615219820Sjeff return types.getCallSiteType(fn); 616219820Sjeff } 617219820Sjeff 618219820Sjeff Type getParamType(final FunctionNode fn, final int pos) { 619219820Sjeff return types == null ? null : types.get(fn, pos); 620219820Sjeff } 621219820Sjeff 622219820Sjeff /** 623219820Sjeff * Do a compilation job 624219820Sjeff * 625219820Sjeff * @param functionNode function node to compile 626219820Sjeff * @param phases phases of compilation transforms to apply to function 627219820Sjeff 628219820Sjeff * @return transformed function 629219820Sjeff * 630219820Sjeff * @throws CompilationException if error occurs during compilation 631219820Sjeff */ 632219820Sjeff public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException { 633219820Sjeff if (log.isEnabled()) { 634219820Sjeff log.info(">> Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc())); 635219820Sjeff log.indent(); 636219820Sjeff } 637219820Sjeff 638219820Sjeff final String name = DebugLogger.quote(functionNode.getName()); 639219820Sjeff 640219820Sjeff FunctionNode newFunctionNode = functionNode; 641219820Sjeff 642219820Sjeff for (final String reservedName : RESERVED_NAMES) { 643219820Sjeff newFunctionNode.uniqueName(reservedName); 644219820Sjeff } 645219820Sjeff 646219820Sjeff final boolean info = log.isLoggable(Level.INFO); 647219820Sjeff 648219820Sjeff final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null; 649219820Sjeff 650219820Sjeff long time = 0L; 651219820Sjeff 652219820Sjeff for (final CompilationPhase phase : phases) { 653219820Sjeff log.fine(phase, " starting for ", name); 654219820Sjeff 655219820Sjeff try { 656219820Sjeff newFunctionNode = phase.apply(this, phases, newFunctionNode); 657219820Sjeff } catch (final ParserException error) { 658219820Sjeff errors.error(error); 659219820Sjeff if (env._dump_on_error) { 660219820Sjeff error.printStackTrace(env.getErr()); 661219820Sjeff } 662219820Sjeff return null; 663219820Sjeff } 664219820Sjeff 665219820Sjeff log.fine(phase, " done for function ", quote(name)); 666219820Sjeff 667219820Sjeff if (env._print_mem_usage) { 668219820Sjeff printMemoryUsage(functionNode, phase.toString()); 669219820Sjeff } 670219820Sjeff 671219820Sjeff time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L); 672219820Sjeff } 673219820Sjeff 674219820Sjeff if (typeInformationFile != null && !phases.isRestOfCompilation()) { 675219820Sjeff OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints); 676219820Sjeff } 677219820Sjeff 678219820Sjeff log.unindent(); 679219820Sjeff 680219820Sjeff if (info) { 681219820Sjeff final StringBuilder sb = new StringBuilder("<< Finished compile job for "); 682219820Sjeff sb.append(newFunctionNode.getSource()). 683219820Sjeff append(':'). 684219820Sjeff append(quote(newFunctionNode.getName())); 685219820Sjeff 686219820Sjeff if (time > 0L && timeLogger != null) { 687219820Sjeff assert env.isTimingEnabled(); 688219820Sjeff sb.append(" in ").append(TimeUnit.NANOSECONDS.toMillis(time)).append(" ms"); 689219820Sjeff } 690219820Sjeff log.info(sb); 691219820Sjeff } 692219820Sjeff 693219820Sjeff return newFunctionNode; 694219820Sjeff } 695219820Sjeff 696219820Sjeff Source getSource() { 697219820Sjeff return source; 698219820Sjeff } 699219820Sjeff 700219820Sjeff Map<String, byte[]> getBytecode() { 701219820Sjeff return Collections.unmodifiableMap(bytecode); 702219820Sjeff } 703219820Sjeff 704219820Sjeff /** 705219820Sjeff * Reset bytecode cache for compiler reuse. 706219820Sjeff */ 707219820Sjeff void clearBytecode() { 708219820Sjeff bytecode.clear(); 709219820Sjeff } 710219820Sjeff 711219820Sjeff CompileUnit getFirstCompileUnit() { 712219820Sjeff assert !compileUnits.isEmpty(); 713219820Sjeff return compileUnits.iterator().next(); 714219820Sjeff } 715219820Sjeff 716219820Sjeff Set<CompileUnit> getCompileUnits() { 717219820Sjeff return compileUnits; 718219820Sjeff } 719219820Sjeff 720219820Sjeff ConstantData getConstantData() { 721219820Sjeff return constantData; 722219820Sjeff } 723219820Sjeff 724219820Sjeff CodeInstaller getCodeInstaller() { 725219820Sjeff return installer; 726219820Sjeff } 727219820Sjeff 728219820Sjeff void addClass(final String name, final byte[] code) { 729219820Sjeff bytecode.put(name, code); 730219820Sjeff } 731219820Sjeff 732219820Sjeff String nextCompileUnitName() { 733219820Sjeff final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE); 734219820Sjeff sb.append(firstCompileUnitName); 735219820Sjeff final int cuid = nextCompileUnitId.getAndIncrement(); 736219820Sjeff if (cuid > 0) { 737219820Sjeff sb.append("$cu").append(cuid); 738219820Sjeff } 739219820Sjeff 740219820Sjeff return sb.toString(); 741219820Sjeff } 742219820Sjeff 743219820Sjeff /** 744219820Sjeff * Persist current compilation with the given {@code cacheKey}. 745219820Sjeff * @param cacheKey cache key 746219820Sjeff * @param functionNode function node 747219820Sjeff */ 748219820Sjeff public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) { 749219820Sjeff if (cacheKey != null && env._persistent_cache) { 750219820Sjeff // If this is an on-demand compilation create a function initializer for the function being compiled. 751219820Sjeff // Otherwise use function initializer map generated by codegen. 752219820Sjeff final Map<Integer, FunctionInitializer> initializers = new HashMap<>(); 753219820Sjeff if (isOnDemandCompilation()) { 754219820Sjeff initializers.put(functionNode.getId(), new FunctionInitializer(functionNode, getInvalidatedProgramPoints())); 755219820Sjeff } else { 756219820Sjeff for (final CompileUnit compileUnit : getCompileUnits()) { 757219820Sjeff for (final FunctionNode fn : compileUnit.getFunctionNodes()) { 758219820Sjeff initializers.put(fn.getId(), new FunctionInitializer(fn)); 759219820Sjeff } 760219820Sjeff } 761219820Sjeff } 762219820Sjeff final String mainClassName = getFirstCompileUnit().getUnitClassName(); 763219820Sjeff installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId); 764219820Sjeff } 765219820Sjeff } 766219820Sjeff 767219820Sjeff /** 768219820Sjeff * Make sure the next compilation id is greater than {@code value}. 769219820Sjeff * @param value compilation id value 770219820Sjeff */ 771219820Sjeff public static void updateCompilationId(final int value) { 772219820Sjeff if (value >= COMPILATION_ID.get()) { 773219820Sjeff COMPILATION_ID.set(value + 1); 774219820Sjeff } 775219820Sjeff } 776219820Sjeff 777219820Sjeff CompileUnit addCompileUnit(final long initialWeight) { 778219820Sjeff final CompileUnit compileUnit = createCompileUnit(initialWeight); 779219820Sjeff compileUnits.add(compileUnit); 780219820Sjeff log.fine("Added compile unit ", compileUnit); 781219820Sjeff return compileUnit; 782219820Sjeff } 783219820Sjeff 784219820Sjeff CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) { 785219820Sjeff final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict()); 786219820Sjeff final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); 787219820Sjeff classEmitter.begin(); 788219820Sjeff 789219820Sjeff return compileUnit; 790219820Sjeff } 791219820Sjeff 792219820Sjeff private CompileUnit createCompileUnit(final long initialWeight) { 793219820Sjeff return createCompileUnit(nextCompileUnitName(), initialWeight); 794219820Sjeff } 795219820Sjeff 796219820Sjeff boolean isStrict() { 797219820Sjeff return strict; 798219820Sjeff } 799219820Sjeff 800219820Sjeff void replaceCompileUnits(final Set<CompileUnit> newUnits) { 801219820Sjeff compileUnits.clear(); 802219820Sjeff compileUnits.addAll(newUnits); 803219820Sjeff } 804219820Sjeff 805219820Sjeff CompileUnit findUnit(final long weight) { 806219820Sjeff for (final CompileUnit unit : compileUnits) { 807219820Sjeff if (unit.canHold(weight)) { 808219820Sjeff unit.addWeight(weight); 809219820Sjeff return unit; 810219820Sjeff } 811219820Sjeff } 812219820Sjeff 813219820Sjeff return addCompileUnit(weight); 814219820Sjeff } 815219820Sjeff 816219820Sjeff /** 817219820Sjeff * Convert a package/class name to a binary name. 818219820Sjeff * 819219820Sjeff * @param name Package/class name. 820219820Sjeff * @return Binary name. 821219820Sjeff */ 822219820Sjeff public static String binaryName(final String name) { 823219820Sjeff return name.replace('/', '.'); 824219820Sjeff } 825219820Sjeff 826219820Sjeff RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { 827219820Sjeff assert compiledFunction != null; 828219820Sjeff final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId); 829219820Sjeff assert fn != null : functionId; 830219820Sjeff return fn; 831219820Sjeff } 832219820Sjeff 833219820Sjeff boolean isGlobalSymbol(final FunctionNode fn, final String name) { 834219820Sjeff return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name); 835219820Sjeff } 836219820Sjeff 837219820Sjeff int[] getContinuationEntryPoints() { 838219820Sjeff return continuationEntryPoints; 839219820Sjeff } 840219820Sjeff 841219820Sjeff Type getInvalidatedProgramPointType(final int programPoint) { 842219820Sjeff return invalidatedProgramPoints.get(programPoint); 843219820Sjeff } 844219820Sjeff 845219820Sjeff private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) { 846219820Sjeff if (!log.isEnabled()) { 847219820Sjeff return; 848219820Sjeff } 849219820Sjeff 850219820Sjeff log.info(phaseName, "finished. Doing IR size calculation..."); 851219820Sjeff 852219820Sjeff final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification()); 853219820Sjeff osc.calculateObjectSize(functionNode); 854219820Sjeff 855219820Sjeff final List<ClassHistogramElement> list = osc.getClassHistogram(); 856219820Sjeff final StringBuilder sb = new StringBuilder(); 857219820Sjeff final long totalSize = osc.calculateObjectSize(functionNode); 858219820Sjeff 859219820Sjeff sb.append(phaseName). 860219820Sjeff append(" Total size = "). 861219820Sjeff append(totalSize / 1024 / 1024). 862219820Sjeff append("MB"); 863219820Sjeff log.info(sb); 864219820Sjeff 865219820Sjeff Collections.sort(list, new Comparator<ClassHistogramElement>() { 866219820Sjeff @Override 867219820Sjeff public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) { 868219820Sjeff final long diff = o1.getBytes() - o2.getBytes(); 869219820Sjeff if (diff < 0) { 870219820Sjeff return 1; 871219820Sjeff } else if (diff > 0) { 872219820Sjeff return -1; 873219820Sjeff } else { 874219820Sjeff return 0; 875219820Sjeff } 876219820Sjeff } 877219820Sjeff }); 878219820Sjeff for (final ClassHistogramElement e : list) { 879219820Sjeff final String line = String.format(" %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances()); 880219820Sjeff log.info(line); 881219820Sjeff if (e.getBytes() < totalSize / 200) { 882219820Sjeff log.info(" ..."); 883219820Sjeff break; // never mind, so little memory anyway 884219820Sjeff } 885219820Sjeff } 886219820Sjeff } 887219820Sjeff} 888219820Sjeff