src/jdk/nashorn/internal/codegen/Compiler.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1645
b7bbed8b05dd
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

Merge

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

mercurial