Compiler.java revision 1017:1f2fa7bd6d95
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.codegen;
27
28import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
29import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
30import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
31import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
32import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
33import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
34import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
35
36import java.io.File;
37import java.lang.invoke.MethodType;
38import java.util.Arrays;
39import java.util.Collections;
40import java.util.Comparator;
41import java.util.HashMap;
42import java.util.Iterator;
43import java.util.LinkedHashMap;
44import java.util.LinkedList;
45import java.util.List;
46import java.util.Map;
47import java.util.Set;
48import java.util.TreeMap;
49import java.util.concurrent.atomic.AtomicInteger;
50import java.util.function.Consumer;
51import java.util.logging.Level;
52import jdk.internal.dynalink.support.NameCodec;
53import jdk.nashorn.internal.codegen.types.Type;
54import jdk.nashorn.internal.ir.Expression;
55import jdk.nashorn.internal.ir.FunctionNode;
56import jdk.nashorn.internal.ir.Optimistic;
57import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
58import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
59import jdk.nashorn.internal.runtime.CodeInstaller;
60import jdk.nashorn.internal.runtime.Context;
61import jdk.nashorn.internal.runtime.ErrorManager;
62import jdk.nashorn.internal.runtime.FunctionInitializer;
63import jdk.nashorn.internal.runtime.ParserException;
64import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
65import jdk.nashorn.internal.runtime.ScriptEnvironment;
66import jdk.nashorn.internal.runtime.ScriptObject;
67import jdk.nashorn.internal.runtime.ScriptRuntime;
68import jdk.nashorn.internal.runtime.Source;
69import jdk.nashorn.internal.runtime.logging.DebugLogger;
70import jdk.nashorn.internal.runtime.logging.Loggable;
71import 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")
80public 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     * Compilation phases that a compilation goes through
158     */
159    public static class CompilationPhases implements Iterable<CompilationPhase> {
160
161        /** Singleton that describes a standard eager compilation - this includes code installation */
162        public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
163                "Compile all",
164                new CompilationPhase[] {
165                        CompilationPhase.CONSTANT_FOLDING_PHASE,
166                        CompilationPhase.LOWERING_PHASE,
167                        CompilationPhase.PROGRAM_POINT_PHASE,
168                        CompilationPhase.TRANSFORM_BUILTINS_PHASE,
169                        CompilationPhase.SPLITTING_PHASE,
170                        CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
171                        CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
172                        CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
173                        CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
174                        CompilationPhase.BYTECODE_GENERATION_PHASE,
175                        CompilationPhase.INSTALL_PHASE
176                });
177
178        /** Compile all for a rest of method */
179        public final static CompilationPhases COMPILE_ALL_RESTOF =
180                COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
181
182        /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
183        public final static CompilationPhases COMPILE_ALL_NO_INSTALL =
184                COMPILE_ALL.
185                removeLast().
186                setDescription("Compile without install");
187
188        /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
189        public final static CompilationPhases COMPILE_UPTO_BYTECODE =
190                COMPILE_ALL.
191                removeLast().
192                removeLast().
193                setDescription("Compile upto bytecode");
194
195        /**
196         * Singleton that describes back end of method generation, given that we have generated the normal
197         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
198         */
199        public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases(
200                "Generate bytecode and install",
201                new CompilationPhase[] {
202                        CompilationPhase.BYTECODE_GENERATION_PHASE,
203                        CompilationPhase.INSTALL_PHASE
204                });
205
206        /**
207         * Singleton that describes restOf method generation, given that we have generated the normal
208         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
209         */
210        public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF =
211                COMPILE_FROM_BYTECODE.
212                addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE).
213                setDescription("Generate bytecode and install - RestOf method");
214
215        private final List<CompilationPhase> phases;
216
217        private final String desc;
218
219        private CompilationPhases(final String desc, final CompilationPhase... phases) {
220            this.desc = desc;
221
222            final List<CompilationPhase> newPhases = new LinkedList<>();
223            newPhases.addAll(Arrays.asList(phases));
224            this.phases = Collections.unmodifiableList(newPhases);
225        }
226
227        @Override
228        public String toString() {
229            return "'" + desc + "' " + phases.toString();
230        }
231
232        private CompilationPhases setDescription(final String desc) {
233            return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()]));
234        }
235
236        private CompilationPhases removeLast() {
237            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
238            list.removeLast();
239            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
240        }
241
242        private CompilationPhases addFirst(final CompilationPhase phase) {
243            if (phases.contains(phase)) {
244                return this;
245            }
246            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
247            list.addFirst(phase);
248            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
249        }
250
251        @SuppressWarnings("unused") //TODO I'll use this soon
252        private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) {
253            final LinkedList<CompilationPhase> list = new LinkedList<>();
254            for (final CompilationPhase p : phases) {
255                list.add(p == phase ? newPhase : p);
256            }
257            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
258        }
259
260        private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
261            final LinkedList<CompilationPhase> list = new LinkedList<>();
262            for (final CompilationPhase p : phases) {
263                list.add(p);
264                if (p == phase) {
265                    list.add(newPhase);
266                }
267            }
268            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
269        }
270
271        boolean contains(final CompilationPhase phase) {
272            return phases.contains(phase);
273        }
274
275        @Override
276        public Iterator<CompilationPhase> iterator() {
277            return phases.iterator();
278        }
279
280        boolean isRestOfCompilation() {
281            return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF;
282        }
283
284        String getDesc() {
285            return desc;
286        }
287
288        String toString(final String prefix) {
289            final StringBuilder sb = new StringBuilder();
290            for (final CompilationPhase phase : phases) {
291                sb.append(prefix).append(phase).append('\n');
292            }
293            return sb.toString();
294        }
295    }
296
297    /**
298     * This array contains names that need to be reserved at the start
299     * of a compile, to avoid conflict with variable names later introduced.
300     * See {@link CompilerConstants} for special names used for structures
301     * during a compile.
302     */
303    private static String[] RESERVED_NAMES = {
304        SCOPE.symbolName(),
305        THIS.symbolName(),
306        RETURN.symbolName(),
307        CALLEE.symbolName(),
308        VARARGS.symbolName(),
309        ARGUMENTS.symbolName()
310    };
311
312    // per instance
313    private final int compilationId = COMPILATION_ID.getAndIncrement();
314
315    // per instance
316    private final AtomicInteger nextCompileUnitId = new AtomicInteger(0);
317
318    private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0);
319
320    /**
321     * Constructor
322     *
323     * @param context   context
324     * @param env       script environment
325     * @param installer code installer
326     * @param source    source to compile
327     * @param errors    error manager
328     * @param isStrict  is this a strict compilation
329     */
330    public Compiler(
331            final Context context,
332            final ScriptEnvironment env,
333            final CodeInstaller<ScriptEnvironment> installer,
334            final Source source,
335            final ErrorManager errors,
336            final boolean isStrict) {
337        this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null);
338    }
339
340    /**
341     * Constructor
342     *
343     * @param context                  context
344     * @param env                      script environment
345     * @param installer                code installer
346     * @param source                   source to compile
347     * @param errors                   error manager
348     * @param isStrict                 is this a strict compilation
349     * @param isOnDemand               is this an on demand compilation
350     * @param compiledFunction         compiled function, if any
351     * @param types                    parameter and return value type information, if any is known
352     * @param invalidatedProgramPoints invalidated program points for recompilation
353     * @param typeInformationFile      descriptor of the location where type information is persisted
354     * @param continuationEntryPoints  continuation entry points for restof method
355     * @param runtimeScope             runtime scope for recompilation type lookup in {@code TypeEvaluator}
356     */
357    public Compiler(
358            final Context context,
359            final ScriptEnvironment env,
360            final CodeInstaller<ScriptEnvironment> installer,
361            final Source source,
362            final ErrorManager errors,
363            final boolean isStrict,
364            final boolean isOnDemand,
365            final RecompilableScriptFunctionData compiledFunction,
366            final TypeMap types,
367            final Map<Integer, Type> invalidatedProgramPoints,
368            final Object typeInformationFile,
369            final int[] continuationEntryPoints,
370            final ScriptObject runtimeScope) {
371        this.context                  = context;
372        this.env                      = env;
373        this.installer                = installer;
374        this.constantData             = new ConstantData();
375        this.compileUnits             = CompileUnit.createCompileUnitSet();
376        this.bytecode                 = new LinkedHashMap<>();
377        this.log                      = initLogger(context);
378        this.source                   = source;
379        this.errors                   = errors;
380        this.sourceName               = FunctionNode.getSourceName(source);
381        this.onDemand                 = isOnDemand;
382        this.compiledFunction         = compiledFunction;
383        this.types                    = types;
384        this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
385        this.typeInformationFile      = typeInformationFile;
386        this.continuationEntryPoints  = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
387        this.typeEvaluator            = new TypeEvaluator(this, runtimeScope);
388        this.firstCompileUnitName     = firstCompileUnitName();
389        this.strict                   = isStrict;
390
391        this.optimistic = env._optimistic_types;
392    }
393
394    private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) {
395        String baseName = new File(source.getName()).getName();
396
397        final int index = baseName.lastIndexOf(".js");
398        if (index != -1) {
399            baseName = baseName.substring(0, index);
400        }
401
402        baseName = baseName.replace('.', '_').replace('-', '_');
403        if (!env._loader_per_compile) {
404            baseName = baseName + installer.getUniqueScriptId();
405        }
406
407        final String mangled = NameCodec.encode(baseName);
408        return mangled != null ? mangled : baseName;
409    }
410
411    private String firstCompileUnitName() {
412        final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
413                append('/').
414                append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()).
415                append('$');
416
417        if (isOnDemandCompilation()) {
418            sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX);
419        }
420
421        if (compilationId > 0) {
422            sb.append(compilationId).append('$');
423        }
424
425        if (types != null && compiledFunction.getFunctionNodeId() > 0) {
426            sb.append(compiledFunction.getFunctionNodeId());
427            final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId());
428            for (final Type t : paramTypes) {
429                sb.append(Type.getShortSignatureDescriptor(t));
430            }
431            sb.append('$');
432        }
433
434        sb.append(Compiler.safeSourceName(env, installer, source));
435
436        return sb.toString();
437    }
438
439    void declareLocalSymbol(final String symbolName) {
440        typeEvaluator.declareLocalSymbol(symbolName);
441    }
442
443    void setData(final RecompilableScriptFunctionData data) {
444        assert this.compiledFunction == null : data;
445        this.compiledFunction = data;
446    }
447
448    @Override
449    public DebugLogger getLogger() {
450        return log;
451    }
452
453    @Override
454    public DebugLogger initLogger(final Context ctxt) {
455        return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() {
456            @Override
457            public void accept(final DebugLogger newLogger) {
458                if (!Compiler.this.getScriptEnvironment()._lazy_compilation) {
459                    newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting.");
460                }
461            }
462        });
463    }
464
465    ScriptEnvironment getScriptEnvironment() {
466        return env;
467    }
468
469    boolean isOnDemandCompilation() {
470        return onDemand;
471    }
472
473    boolean useOptimisticTypes() {
474        return optimistic;
475    }
476
477    Context getContext() {
478        return context;
479    }
480
481    Type getOptimisticType(final Optimistic node) {
482        return typeEvaluator.getOptimisticType(node);
483    }
484
485    /**
486     * Returns true if the expression can be safely evaluated, and its value is an object known to always use
487     * String as the type of its property names retrieved through
488     * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its
489     * property name types.
490     * @param expr the expression to test
491     * @return true if the expression can be safely evaluated, and its value is an object known to always use
492     * String as the type of its property iterators.
493     */
494    boolean hasStringPropertyIterator(final Expression expr) {
495        return typeEvaluator.hasStringPropertyIterator(expr);
496    }
497
498    void addInvalidatedProgramPoint(final int programPoint, final Type type) {
499        invalidatedProgramPoints.put(programPoint, type);
500    }
501
502
503    /**
504     * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The
505     * copy is not live with regard to changes in state in this compiler instance, and is mutable.
506     * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types.
507     */
508    public Map<Integer, Type> getInvalidatedProgramPoints() {
509        return invalidatedProgramPoints == null ? null : new TreeMap<>(invalidatedProgramPoints);
510    }
511
512    TypeMap getTypeMap() {
513        return types;
514    }
515
516    MethodType getCallSiteType(final FunctionNode fn) {
517        if (types == null || !isOnDemandCompilation()) {
518            return null;
519        }
520        return types.getCallSiteType(fn);
521    }
522
523    Type getParamType(final FunctionNode fn, final int pos) {
524        return types == null ? null : types.get(fn, pos);
525    }
526
527    /**
528     * Do a compilation job
529     *
530     * @param functionNode function node to compile
531     * @param phases phases of compilation transforms to apply to function
532
533     * @return transformed function
534     *
535     * @throws CompilationException if error occurs during compilation
536     */
537    public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
538
539        log.finest("Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
540        log.indent();
541
542        final String name = DebugLogger.quote(functionNode.getName());
543
544        FunctionNode newFunctionNode = functionNode;
545
546        for (final String reservedName : RESERVED_NAMES) {
547            newFunctionNode.uniqueName(reservedName);
548        }
549
550        final boolean info = log.levelFinerThanOrEqual(Level.INFO);
551
552        final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null;
553
554        long time = 0L;
555
556        for (final CompilationPhase phase : phases) {
557            log.fine(phase, " starting for ", quote(name));
558
559            try {
560                newFunctionNode = phase.apply(this, phases, newFunctionNode);
561            } catch (final ParserException error) {
562                errors.error(error);
563                if (env._dump_on_error) {
564                    error.printStackTrace(env.getErr());
565                }
566                return null;
567            }
568
569            log.fine(phase, " done for function ", quote(name));
570
571            if (env._print_mem_usage) {
572                printMemoryUsage(functionNode, phase.toString());
573            }
574
575            time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
576        }
577
578        if (typeInformationFile != null && !phases.isRestOfCompilation()) {
579            OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
580        }
581
582        log.unindent();
583
584        if (info) {
585            final StringBuilder sb = new StringBuilder();
586            sb.append("Compile job for ").append(newFunctionNode.getSource()).append(':').append(quote(newFunctionNode.getName())).append(" finished");
587            if (time > 0L && timeLogger != null) {
588                assert env.isTimingEnabled();
589                sb.append(" in ").append(time).append(" ms");
590            }
591            log.info(sb);
592        }
593
594        return newFunctionNode;
595    }
596
597    Source getSource() {
598        return source;
599    }
600
601    Map<String, byte[]> getBytecode() {
602        return Collections.unmodifiableMap(bytecode);
603    }
604
605    /**
606     * Reset bytecode cache for compiler reuse.
607     */
608    void clearBytecode() {
609        bytecode.clear();
610    }
611
612    CompileUnit getFirstCompileUnit() {
613        assert !compileUnits.isEmpty();
614        return compileUnits.iterator().next();
615    }
616
617    Set<CompileUnit> getCompileUnits() {
618        return compileUnits;
619    }
620
621    ConstantData getConstantData() {
622        return constantData;
623    }
624
625    CodeInstaller<ScriptEnvironment> getCodeInstaller() {
626        return installer;
627    }
628
629    void addClass(final String name, final byte[] code) {
630        bytecode.put(name, code);
631    }
632
633    String nextCompileUnitName() {
634        final StringBuilder sb = new StringBuilder(firstCompileUnitName);
635        final int cuid = nextCompileUnitId.getAndIncrement();
636        if (cuid > 0) {
637            sb.append("$cu").append(cuid);
638        }
639
640        return sb.toString();
641    }
642
643    Map<Integer, FunctionInitializer> functionInitializers;
644
645    void addFunctionInitializer(final RecompilableScriptFunctionData functionData, final FunctionNode functionNode) {
646        if (functionInitializers == null) {
647            functionInitializers = new HashMap<>();
648        }
649        if (!functionInitializers.containsKey(functionData)) {
650            functionInitializers.put(functionData.getFunctionNodeId(), new FunctionInitializer(functionNode));
651        }
652    }
653
654    Map<Integer, FunctionInitializer> getFunctionInitializers() {
655        return functionInitializers;
656    }
657
658    /**
659     * Persist current compilation with the given {@code cacheKey}.
660     * @param cacheKey cache key
661     * @param functionNode function node
662     */
663    public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) {
664        if (cacheKey != null && env._persistent_cache) {
665            Map<Integer, FunctionInitializer> initializers;
666            // If this is an on-demand compilation create a function initializer for the function being compiled.
667            // Otherwise use function initializer map generated by codegen.
668            if (functionInitializers == null) {
669                initializers = new HashMap<>();
670                final FunctionInitializer initializer = new FunctionInitializer(functionNode, getInvalidatedProgramPoints());
671                initializers.put(functionNode.getId(), initializer);
672            } else {
673                initializers = functionInitializers;
674            }
675            final String mainClassName = getFirstCompileUnit().getUnitClassName();
676            installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId);
677        }
678    }
679
680    /**
681     * Make sure the next compilation id is greater than {@code value}.
682     * @param value compilation id value
683     */
684    public static void updateCompilationId(final int value) {
685        if (value >= COMPILATION_ID.get()) {
686            COMPILATION_ID.set(value + 1);
687        }
688    }
689
690    CompileUnit addCompileUnit(final long initialWeight) {
691        final CompileUnit compileUnit = createCompileUnit(initialWeight);
692        compileUnits.add(compileUnit);
693        log.fine("Added compile unit ", compileUnit);
694        return compileUnit;
695    }
696
697    CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
698        final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
699        final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
700        classEmitter.begin();
701
702        return compileUnit;
703    }
704
705    private CompileUnit createCompileUnit(final long initialWeight) {
706        return createCompileUnit(nextCompileUnitName(), initialWeight);
707    }
708
709    boolean isStrict() {
710        return strict;
711    }
712
713    void replaceCompileUnits(final Set<CompileUnit> newUnits) {
714        compileUnits.clear();
715        compileUnits.addAll(newUnits);
716    }
717
718    CompileUnit findUnit(final long weight) {
719        for (final CompileUnit unit : compileUnits) {
720            if (unit.canHold(weight)) {
721                unit.addWeight(weight);
722                return unit;
723            }
724        }
725
726        return addCompileUnit(weight);
727    }
728
729    /**
730     * Convert a package/class name to a binary name.
731     *
732     * @param name Package/class name.
733     * @return Binary name.
734     */
735    public static String binaryName(final String name) {
736        return name.replace('/', '.');
737    }
738
739    RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
740        return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
741    }
742
743    boolean isGlobalSymbol(final FunctionNode fn, final String name) {
744        return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name);
745    }
746
747    int[] getContinuationEntryPoints() {
748        return continuationEntryPoints;
749    }
750
751    Type getInvalidatedProgramPointType(final int programPoint) {
752        return invalidatedProgramPoints.get(programPoint);
753    }
754
755    private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) {
756        if (!log.isEnabled()) {
757            return;
758        }
759
760        log.info(phaseName, "finished. Doing IR size calculation...");
761
762        final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification());
763        osc.calculateObjectSize(functionNode);
764
765        final List<ClassHistogramElement> list      = osc.getClassHistogram();
766        final StringBuilder               sb        = new StringBuilder();
767        final long                        totalSize = osc.calculateObjectSize(functionNode);
768
769        sb.append(phaseName).
770            append(" Total size = ").
771            append(totalSize / 1024 / 1024).
772            append("MB");
773        log.info(sb);
774
775        Collections.sort(list, new Comparator<ClassHistogramElement>() {
776            @Override
777            public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
778                final long diff = o1.getBytes() - o2.getBytes();
779                if (diff < 0) {
780                    return 1;
781                } else if (diff > 0) {
782                    return -1;
783                } else {
784                    return 0;
785                }
786            }
787        });
788        for (final ClassHistogramElement e : list) {
789            final String line = String.format("    %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances());
790            log.info(line);
791            if (e.getBytes() < totalSize / 200) {
792                log.info("    ...");
793                break; // never mind, so little memory anyway
794            }
795        }
796    }
797}
798