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