Thu, 24 May 2018 16:39:31 +0800
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 | } |