Compiler.java revision 971:c93b6091b11e
1218887Sdim/*
2218887Sdim * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3218887Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4218887Sdim *
5218887Sdim * This code is free software; you can redistribute it and/or modify it
6218887Sdim * under the terms of the GNU General Public License version 2 only, as
7218887Sdim * published by the Free Software Foundation.  Oracle designates this
8218887Sdim * particular file as subject to the "Classpath" exception as provided
9218887Sdim * by Oracle in the LICENSE file that accompanied this code.
10218887Sdim *
11218887Sdim * This code is distributed in the hope that it will be useful, but WITHOUT
12218887Sdim * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13218887Sdim * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14218887Sdim * version 2 for more details (a copy is included in the LICENSE file that
15218887Sdim * accompanied this code).
16243830Sdim *
17249423Sdim * You should have received a copy of the GNU General Public License version
18243830Sdim * 2 along with this work; if not, write to the Free Software Foundation,
19243830Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20218887Sdim *
21243830Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22218887Sdim * or visit www.oracle.com if you need additional information or have any
23243830Sdim * questions.
24243830Sdim */
25243830Sdim
26234353Sdimpackage jdk.nashorn.internal.codegen;
27243830Sdim
28218887Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
29218887Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
30218887Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
31218887Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
32243830Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
33243830Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
34243830Sdimimport static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
35243830Sdim
36243830Sdimimport java.io.File;
37243830Sdimimport java.lang.invoke.MethodType;
38243830Sdimimport java.util.Arrays;
39243830Sdimimport java.util.Collections;
40243830Sdimimport java.util.Comparator;
41243830Sdimimport java.util.EnumSet;
42243830Sdimimport java.util.HashMap;
43243830Sdimimport java.util.Iterator;
44243830Sdimimport java.util.LinkedHashMap;
45243830Sdimimport java.util.LinkedList;
46243830Sdimimport java.util.List;
47243830Sdimimport java.util.Map;
48243830Sdimimport java.util.Set;
49243830Sdimimport java.util.TreeMap;
50243830Sdimimport java.util.concurrent.atomic.AtomicInteger;
51243830Sdimimport java.util.function.Consumer;
52243830Sdimimport java.util.logging.Level;
53243830Sdimimport jdk.internal.dynalink.support.NameCodec;
54243830Sdimimport jdk.nashorn.internal.codegen.ClassEmitter.Flag;
55243830Sdimimport jdk.nashorn.internal.codegen.types.Type;
56243830Sdimimport jdk.nashorn.internal.ir.FunctionNode;
57243830Sdimimport jdk.nashorn.internal.ir.Optimistic;
58243830Sdimimport jdk.nashorn.internal.ir.debug.ClassHistogramElement;
59243830Sdimimport jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
60243830Sdimimport jdk.nashorn.internal.runtime.CodeInstaller;
61243830Sdimimport jdk.nashorn.internal.runtime.Context;
62243830Sdimimport jdk.nashorn.internal.runtime.FunctionInitializer;
63243830Sdimimport jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
64243830Sdimimport jdk.nashorn.internal.runtime.ScriptEnvironment;
65218887Sdimimport jdk.nashorn.internal.runtime.ScriptObject;
66218887Sdimimport jdk.nashorn.internal.runtime.Source;
67218887Sdimimport jdk.nashorn.internal.runtime.logging.DebugLogger;
68218887Sdimimport jdk.nashorn.internal.runtime.logging.Loggable;
69218887Sdimimport jdk.nashorn.internal.runtime.logging.Logger;
70218887Sdim
71218887Sdim/**
72218887Sdim * Responsible for converting JavaScripts to java byte code. Main entry
73218887Sdim * point for code generator. The compiler may also install classes given some
74218887Sdim * predefined Code installation policy, given to it at construction time.
75218887Sdim * @see CodeInstaller
76218887Sdim */
77218887Sdim@Logger(name="compiler")
78218887Sdimpublic final class Compiler implements Loggable {
79218887Sdim
80218887Sdim    /** Name of the scripts package */
81218887Sdim    public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts";
82218887Sdim
83218887Sdim    /** Name of the objects package */
84218887Sdim    public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
85218887Sdim
86218887Sdim    private final ScriptEnvironment env;
87226633Sdim
88218887Sdim    private final Source source;
89218887Sdim
90218887Sdim    private final String sourceName;
91218887Sdim
92218887Sdim    private final boolean optimistic;
93218887Sdim
94218887Sdim    private final Map<String, byte[]> bytecode;
95218887Sdim
96218887Sdim    private final Set<CompileUnit> compileUnits;
97218887Sdim
98218887Sdim    private final ConstantData constantData;
99218887Sdim
100218887Sdim    private final CodeInstaller<ScriptEnvironment> installer;
101218887Sdim
102218887Sdim    /** logger for compiler, trampolines, splits and related code generation events
103218887Sdim     *  that affect classes */
104251662Sdim    private final DebugLogger log;
105251662Sdim
106234353Sdim    private final Context context;
107234353Sdim
108234353Sdim    private final TypeMap types;
109234353Sdim
110234353Sdim    // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
111234353Sdim    // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
112234353Sdim    private final TypeEvaluator typeEvaluator;
113234353Sdim
114234353Sdim    private final boolean strict;
115251662Sdim
116251662Sdim    private final boolean onDemand;
117251662Sdim
118251662Sdim    /**
119234353Sdim     * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
120234353Sdim     * that using whatever was at program point 17 as an int failed.
121234353Sdim     */
122234353Sdim    private final Map<Integer, Type> invalidatedProgramPoints;
123234353Sdim
124218887Sdim    /**
125226633Sdim     * Descriptor of the location where we write the type information after compilation.
126218887Sdim     */
127218887Sdim    private final Object typeInformationFile;
128218887Sdim
129234353Sdim    /**
130218887Sdim     * Compile unit name of first compile unit - this prefix will be used for all
131226633Sdim     * classes that a compilation generates.
132234353Sdim     */
133218887Sdim    private final String firstCompileUnitName;
134249423Sdim
135218887Sdim    /**
136218887Sdim     * Contains the program point that should be used as the continuation entry point, as well as all previous
137218887Sdim     * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
138218887Sdim     * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
139218887Sdim     * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
140234353Sdim     * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
141226633Sdim     * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
142226633Sdim     * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
143218887Sdim     */
144218887Sdim    private final int[] continuationEntryPoints;
145218887Sdim
146218887Sdim    /**
147243830Sdim     * ScriptFunction data for what is being compile, where applicable.
148243830Sdim     * TODO: make this immutable, propagate it through the CompilationPhases
149243830Sdim     */
150243830Sdim    private RecompilableScriptFunctionData compiledFunction;
151243830Sdim
152243830Sdim    /**
153243830Sdim     * Compilation phases that a compilation goes through
154243830Sdim     */
155243830Sdim    public static class CompilationPhases implements Iterable<CompilationPhase> {
156243830Sdim
157243830Sdim        /** Singleton that describes a standard eager compilation - this includes code installation */
158243830Sdim        public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
159243830Sdim                "Compile all",
160243830Sdim                new CompilationPhase[] {
161243830Sdim                        CompilationPhase.CONSTANT_FOLDING_PHASE,
162243830Sdim                        CompilationPhase.LOWERING_PHASE,
163243830Sdim                        CompilationPhase.PROGRAM_POINT_PHASE,
164226633Sdim                        CompilationPhase.TRANSFORM_BUILTINS_PHASE,
165226633Sdim                        CompilationPhase.SPLITTING_PHASE,
166218887Sdim                        CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
167218887Sdim                        CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
168218887Sdim                        CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
169218887Sdim                        CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
170218887Sdim                        CompilationPhase.BYTECODE_GENERATION_PHASE,
171218887Sdim                        CompilationPhase.INSTALL_PHASE
172218887Sdim                });
173218887Sdim
174218887Sdim        /** Compile all for a rest of method */
175218887Sdim        public final static CompilationPhases COMPILE_ALL_RESTOF =
176218887Sdim                COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
177218887Sdim
178218887Sdim        /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
179234353Sdim        public final static CompilationPhases COMPILE_ALL_NO_INSTALL =
180226633Sdim                COMPILE_ALL.
181226633Sdim                removeLast().
182218887Sdim                setDescription("Compile without install");
183218887Sdim
184218887Sdim        /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
185218887Sdim        public final static CompilationPhases COMPILE_UPTO_BYTECODE =
186226633Sdim                COMPILE_ALL.
187226633Sdim                removeLast().
188218887Sdim                removeLast().
189218887Sdim                setDescription("Compile upto bytecode");
190218887Sdim
191218887Sdim        /**
192218887Sdim         * Singleton that describes back end of method generation, given that we have generated the normal
193218887Sdim         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
194226633Sdim         */
195218887Sdim        public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases(
196218887Sdim                "Generate bytecode and install",
197218887Sdim                new CompilationPhase[] {
198218887Sdim                        CompilationPhase.BYTECODE_GENERATION_PHASE,
199218887Sdim                        CompilationPhase.INSTALL_PHASE
200218887Sdim                });
201218887Sdim
202218887Sdim        /**
203218887Sdim         * Singleton that describes restOf method generation, given that we have generated the normal
204234353Sdim         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
205218887Sdim         */
206218887Sdim        public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF =
207226633Sdim                COMPILE_FROM_BYTECODE.
208218887Sdim                addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE).
209226633Sdim                setDescription("Generate bytecode and install - RestOf method");
210218887Sdim
211218887Sdim        private final List<CompilationPhase> phases;
212218887Sdim
213218887Sdim        private final String desc;
214218887Sdim
215218887Sdim        private CompilationPhases(final String desc, final CompilationPhase... phases) {
216218887Sdim            this.desc = desc;
217218887Sdim
218243830Sdim            final List<CompilationPhase> newPhases = new LinkedList<>();
219226633Sdim            newPhases.addAll(Arrays.asList(phases));
220226633Sdim            this.phases = Collections.unmodifiableList(newPhases);
221226633Sdim        }
222226633Sdim
223226633Sdim        @Override
224226633Sdim        public String toString() {
225218887Sdim            return "'" + desc + "' " + phases.toString();
226218887Sdim        }
227226633Sdim
228226633Sdim        private CompilationPhases setDescription(final String desc) {
229226633Sdim            return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()]));
230226633Sdim        }
231218887Sdim
232218887Sdim        private CompilationPhases removeLast() {
233226633Sdim            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
234218887Sdim            list.removeLast();
235218887Sdim            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
236218887Sdim        }
237226633Sdim
238226633Sdim        private CompilationPhases addFirst(final CompilationPhase phase) {
239218887Sdim            if (phases.contains(phase)) {
240218887Sdim                return this;
241218887Sdim            }
242218887Sdim            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
243226633Sdim            list.addFirst(phase);
244218887Sdim            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
245218887Sdim        }
246218887Sdim
247218887Sdim        private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
248218887Sdim            final LinkedList<CompilationPhase> list = new LinkedList<>();
249218887Sdim            for (final CompilationPhase p : phases) {
250218887Sdim                list.add(p);
251218887Sdim                if (p == phase) {
252218887Sdim                    list.add(newPhase);
253218887Sdim                }
254218887Sdim            }
255218887Sdim            return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
256226633Sdim        }
257226633Sdim
258218887Sdim        boolean contains(final CompilationPhase phase) {
259218887Sdim            return phases.contains(phase);
260218887Sdim        }
261218887Sdim
262218887Sdim        @Override
263218887Sdim        public Iterator<CompilationPhase> iterator() {
264218887Sdim            return phases.iterator();
265218887Sdim        }
266218887Sdim
267226633Sdim        boolean isRestOfCompilation() {
268218887Sdim            return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF;
269218887Sdim        }
270226633Sdim
271218887Sdim        String getDesc() {
272218887Sdim            return desc;
273218887Sdim        }
274251662Sdim
275251662Sdim        String toString(final String prefix) {
276251662Sdim            final StringBuilder sb = new StringBuilder();
277234353Sdim            for (final CompilationPhase phase : phases) {
278218887Sdim                sb.append(prefix).append(phase).append('\n');
279218887Sdim            }
280234353Sdim            return sb.toString();
281218887Sdim        }
282218887Sdim    }
283218887Sdim
284218887Sdim    /**
285218887Sdim     * This array contains names that need to be reserved at the start
286234353Sdim     * of a compile, to avoid conflict with variable names later introduced.
287218887Sdim     * See {@link CompilerConstants} for special names used for structures
288218887Sdim     * during a compile.
289218887Sdim     */
290218887Sdim    private static String[] RESERVED_NAMES = {
291218887Sdim        SCOPE.symbolName(),
292218887Sdim        THIS.symbolName(),
293218887Sdim        RETURN.symbolName(),
294218887Sdim        CALLEE.symbolName(),
295226633Sdim        VARARGS.symbolName(),
296218887Sdim        ARGUMENTS.symbolName()
297218887Sdim    };
298226633Sdim
299218887Sdim    // per instance
300218887Sdim    private final int compilationId = COMPILATION_ID.getAndIncrement();
301218887Sdim
302226633Sdim    // per instance
303218887Sdim    private final AtomicInteger nextCompileUnitId = new AtomicInteger(0);
304218887Sdim
305218887Sdim    private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0);
306226633Sdim
307218887Sdim    /**
308226633Sdim     * Constructor
309226633Sdim     *
310218887Sdim     * @param context   context
311226633Sdim     * @param env       script environment
312218887Sdim     * @param installer code installer
313218887Sdim     * @param source    source to compile
314226633Sdim     * @param isStrict  is this a strict compilation
315218887Sdim     */
316218887Sdim    public Compiler(
317226633Sdim            final Context context,
318218887Sdim            final ScriptEnvironment env,
319218887Sdim            final CodeInstaller<ScriptEnvironment> installer,
320218887Sdim            final Source source,
321218887Sdim            final boolean isStrict) {
322218887Sdim        this(context, env, installer, source, isStrict, false, null, null, null, null, null, null);
323218887Sdim    }
324218887Sdim
325218887Sdim    /**
326218887Sdim     * Constructor
327218887Sdim     *
328234353Sdim     * @param context                  context
329234353Sdim     * @param env                      script environment
330234353Sdim     * @param installer                code installer
331224145Sdim     * @param source                   source to compile
332224145Sdim     * @param isStrict                 is this a strict compilation
333234353Sdim     * @param isOnDemand               is this an on demand compilation
334234353Sdim     * @param compiledFunction         compiled function, if any
335251662Sdim     * @param types                    parameter and return value type information, if any is known
336234353Sdim     * @param invalidatedProgramPoints invalidated program points for recompilation
337218887Sdim     * @param typeInformationFile      descriptor of the location where type information is persisted
338218887Sdim     * @param continuationEntryPoints  continuation entry points for restof method
339218887Sdim     * @param runtimeScope             runtime scope for recompilation type lookup in {@code TypeEvaluator}
340218887Sdim     */
341218887Sdim    public Compiler(
342218887Sdim            final Context context,
343218887Sdim            final ScriptEnvironment env,
344218887Sdim            final CodeInstaller<ScriptEnvironment> installer,
345243830Sdim            final Source source,
346218887Sdim            final boolean isStrict,
347218887Sdim            final boolean isOnDemand,
348218887Sdim            final RecompilableScriptFunctionData compiledFunction,
349218887Sdim            final TypeMap types,
350218887Sdim            final Map<Integer, Type> invalidatedProgramPoints,
351218887Sdim            final Object typeInformationFile,
352218887Sdim            final int[] continuationEntryPoints,
353234353Sdim            final ScriptObject runtimeScope) {
354218887Sdim        this.context                  = context;
355218887Sdim        this.env                      = env;
356234353Sdim        this.installer                = installer;
357234353Sdim        this.constantData             = new ConstantData();
358234353Sdim        this.compileUnits             = CompileUnit.createCompileUnitSet();
359218887Sdim        this.bytecode                 = new LinkedHashMap<>();
360218887Sdim        this.log                      = initLogger(context);
361218887Sdim        this.source                   = source;
362218887Sdim        this.sourceName               = FunctionNode.getSourceName(source);
363218887Sdim        this.onDemand                 = isOnDemand;
364218887Sdim        this.compiledFunction         = compiledFunction;
365218887Sdim        this.types                    = types;
366218887Sdim        this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
367218887Sdim        this.typeInformationFile      = typeInformationFile;
368218887Sdim        this.continuationEntryPoints  = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
369218887Sdim        this.typeEvaluator            = new TypeEvaluator(this, runtimeScope);
370218887Sdim        this.firstCompileUnitName     = firstCompileUnitName();
371218887Sdim        this.strict                   = isStrict;
372218887Sdim
373218887Sdim        this.optimistic = env._optimistic_types;
374218887Sdim    }
375218887Sdim
376218887Sdim    private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) {
377226633Sdim        String baseName = new File(source.getName()).getName();
378226633Sdim
379226633Sdim        final int index = baseName.lastIndexOf(".js");
380218887Sdim        if (index != -1) {
381218887Sdim            baseName = baseName.substring(0, index);
382218887Sdim        }
383218887Sdim
384218887Sdim        baseName = baseName.replace('.', '_').replace('-', '_');
385218887Sdim        if (!env._loader_per_compile) {
386218887Sdim            baseName = baseName + installer.getUniqueScriptId();
387218887Sdim        }
388218887Sdim
389218887Sdim        final String mangled = NameCodec.encode(baseName);
390218887Sdim        return mangled != null ? mangled : baseName;
391218887Sdim    }
392218887Sdim
393218887Sdim    private String firstCompileUnitName() {
394218887Sdim        final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
395218887Sdim                append('/').
396218887Sdim                append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()).
397218887Sdim                append('$');
398218887Sdim
399218887Sdim        if (isOnDemandCompilation()) {
400218887Sdim            sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX);
401226633Sdim        }
402218887Sdim
403218887Sdim        if (compilationId > 0) {
404218887Sdim            sb.append(compilationId).append('$');
405218887Sdim        }
406226633Sdim
407218887Sdim        if (types != null && compiledFunction.getFunctionNodeId() > 0) {
408226633Sdim            sb.append(compiledFunction.getFunctionNodeId());
409226633Sdim            final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId());
410218887Sdim            for (final Type t : paramTypes) {
411218887Sdim                sb.append(Type.getShortSignatureDescriptor(t));
412218887Sdim            }
413218887Sdim            sb.append('$');
414218887Sdim        }
415218887Sdim
416218887Sdim        sb.append(Compiler.safeSourceName(env, installer, source));
417218887Sdim
418218887Sdim        return sb.toString();
419218887Sdim    }
420218887Sdim
421218887Sdim    void declareLocalSymbol(final String symbolName) {
422218887Sdim        typeEvaluator.declareLocalSymbol(symbolName);
423218887Sdim    }
424221345Sdim
425218887Sdim    void setData(final RecompilableScriptFunctionData data) {
426218887Sdim        assert this.compiledFunction == null : data;
427218887Sdim        this.compiledFunction = data;
428249423Sdim    }
429249423Sdim
430249423Sdim    @Override
431249423Sdim    public DebugLogger getLogger() {
432249423Sdim        return log;
433249423Sdim    }
434249423Sdim
435249423Sdim    @Override
436249423Sdim    public DebugLogger initLogger(final Context ctxt) {
437226633Sdim        return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() {
438218887Sdim            @Override
439234353Sdim            public void accept(final DebugLogger newLogger) {
440218887Sdim                if (!Compiler.this.getScriptEnvironment()._lazy_compilation) {
441218887Sdim                    newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting.");
442218887Sdim                }
443226633Sdim            }
444226633Sdim        });
445218887Sdim    }
446218887Sdim
447218887Sdim    ScriptEnvironment getScriptEnvironment() {
448218887Sdim        return env;
449218887Sdim    }
450218887Sdim
451218887Sdim    boolean isOnDemandCompilation() {
452218887Sdim        return onDemand;
453    }
454
455    boolean useOptimisticTypes() {
456        return optimistic;
457    }
458
459    Context getContext() {
460        return context;
461    }
462
463    Type getOptimisticType(final Optimistic node) {
464        return typeEvaluator.getOptimisticType(node);
465    }
466
467    void addInvalidatedProgramPoint(final int programPoint, final Type type) {
468        invalidatedProgramPoints.put(programPoint, type);
469    }
470
471
472    /**
473     * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The
474     * copy is not live with regard to changes in state in this compiler instance, and is mutable.
475     * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types.
476     */
477    public Map<Integer, Type> getInvalidatedProgramPoints() {
478        return invalidatedProgramPoints == null ? null : new TreeMap<>(invalidatedProgramPoints);
479    }
480
481    TypeMap getTypeMap() {
482        return types;
483    }
484
485    MethodType getCallSiteType(final FunctionNode fn) {
486        if (types == null || !isOnDemandCompilation()) {
487            return null;
488        }
489        return types.getCallSiteType(fn);
490    }
491
492    Type getParamType(final FunctionNode fn, final int pos) {
493        return types == null ? null : types.get(fn, pos);
494    }
495
496    /**
497     * Do a compilation job
498     *
499     * @param functionNode function node to compile
500     * @param phases phases of compilation transforms to apply to function
501
502     * @return transformed function
503     *
504     * @throws CompilationException if error occurs during compilation
505     */
506    public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
507
508        log.finest("Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
509        log.indent();
510
511        final String name = DebugLogger.quote(functionNode.getName());
512
513        FunctionNode newFunctionNode = functionNode;
514
515        for (final String reservedName : RESERVED_NAMES) {
516            newFunctionNode.uniqueName(reservedName);
517        }
518
519        final boolean info = log.levelFinerThanOrEqual(Level.INFO);
520
521        final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null;
522
523        long time = 0L;
524
525        for (final CompilationPhase phase : phases) {
526            log.fine(phase, " starting for ", quote(name));
527            newFunctionNode = phase.apply(this, phases, newFunctionNode);
528            log.fine(phase, " done for function ", quote(name));
529
530            if (env._print_mem_usage) {
531                printMemoryUsage(functionNode, phase.toString());
532            }
533
534            time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
535        }
536
537        if (typeInformationFile != null && !phases.isRestOfCompilation()) {
538            OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
539        }
540
541        log.unindent();
542
543        if (info) {
544            final StringBuilder sb = new StringBuilder();
545            sb.append("Compile job for ").append(newFunctionNode.getSource()).append(':').append(quote(newFunctionNode.getName())).append(" finished");
546            if (time > 0L && timeLogger != null) {
547                assert env.isTimingEnabled();
548                sb.append(" in ").append(time).append(" ms");
549            }
550            log.info(sb);
551        }
552
553        return newFunctionNode;
554    }
555
556    Source getSource() {
557        return source;
558    }
559
560    Map<String, byte[]> getBytecode() {
561        return Collections.unmodifiableMap(bytecode);
562    }
563
564    /**
565     * Reset bytecode cache for compiler reuse.
566     */
567    void clearBytecode() {
568        bytecode.clear();
569    }
570
571    CompileUnit getFirstCompileUnit() {
572        assert !compileUnits.isEmpty();
573        return compileUnits.iterator().next();
574    }
575
576    Set<CompileUnit> getCompileUnits() {
577        return compileUnits;
578    }
579
580    ConstantData getConstantData() {
581        return constantData;
582    }
583
584    CodeInstaller<ScriptEnvironment> getCodeInstaller() {
585        return installer;
586    }
587
588    void addClass(final String name, final byte[] code) {
589        bytecode.put(name, code);
590    }
591
592    String nextCompileUnitName() {
593        final StringBuilder sb = new StringBuilder(firstCompileUnitName);
594        final int cuid = nextCompileUnitId.getAndIncrement();
595        if (cuid > 0) {
596            sb.append("$cu").append(cuid);
597        }
598
599        return sb.toString();
600    }
601
602    Map<Integer, FunctionInitializer> functionInitializers;
603
604    void addFunctionInitializer(final RecompilableScriptFunctionData functionData, final FunctionNode functionNode) {
605        if (functionInitializers == null) {
606            functionInitializers = new HashMap<>();
607        }
608        if (!functionInitializers.containsKey(functionData)) {
609            functionInitializers.put(functionData.getFunctionNodeId(), new FunctionInitializer(functionNode));
610        }
611    }
612
613    Map<Integer, FunctionInitializer> getFunctionInitializers() {
614        return functionInitializers;
615    }
616
617    /**
618     * Persist current compilation with the given {@code cacheKey}.
619     * @param cacheKey cache key
620     * @param functionNode function node
621     */
622    public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) {
623        if (cacheKey != null && env._persistent_cache) {
624            Map<Integer, FunctionInitializer> initializers;
625            // If this is an on-demand compilation create a function initializer for the function being compiled.
626            // Otherwise use function initializer map generated by codegen.
627            if (functionInitializers == null) {
628                initializers = new HashMap<>();
629                final FunctionInitializer initializer = new FunctionInitializer(functionNode, getInvalidatedProgramPoints());
630                initializers.put(functionNode.getId(), initializer);
631            } else {
632                initializers = functionInitializers;
633            }
634            final String mainClassName = getFirstCompileUnit().getUnitClassName();
635            installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId);
636        }
637    }
638
639    /**
640     * Make sure the next compilation id is greater than {@code value}.
641     * @param value compilation id value
642     */
643    public static void updateCompilationId(final int value) {
644        if (value >= COMPILATION_ID.get()) {
645            COMPILATION_ID.set(value + 1);
646        }
647    }
648
649    CompileUnit addCompileUnit(final long initialWeight) {
650        final CompileUnit compileUnit = createCompileUnit(initialWeight);
651        compileUnits.add(compileUnit);
652        log.fine("Added compile unit ", compileUnit);
653        return compileUnit;
654    }
655
656    CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
657        final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
658        final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
659
660        classEmitter.begin();
661
662        final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
663        initMethod.begin();
664        initMethod.load(Type.OBJECT, 0);
665        initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
666        initMethod.returnVoid();
667        initMethod.end();
668
669        return compileUnit;
670    }
671
672    private CompileUnit createCompileUnit(final long initialWeight) {
673        return createCompileUnit(nextCompileUnitName(), initialWeight);
674    }
675
676    boolean isStrict() {
677        return strict;
678    }
679
680    void replaceCompileUnits(final Set<CompileUnit> newUnits) {
681        compileUnits.clear();
682        compileUnits.addAll(newUnits);
683    }
684
685    CompileUnit findUnit(final long weight) {
686        for (final CompileUnit unit : compileUnits) {
687            if (unit.canHold(weight)) {
688                unit.addWeight(weight);
689                return unit;
690            }
691        }
692
693        return addCompileUnit(weight);
694    }
695
696    /**
697     * Convert a package/class name to a binary name.
698     *
699     * @param name Package/class name.
700     * @return Binary name.
701     */
702    public static String binaryName(final String name) {
703        return name.replace('/', '.');
704    }
705
706    RecompilableScriptFunctionData getProgram() {
707        if (compiledFunction == null) {
708            return null;
709        }
710        return compiledFunction.getProgram();
711    }
712
713    RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
714        return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
715    }
716
717    boolean isGlobalSymbol(final FunctionNode fn, final String name) {
718        return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name);
719    }
720
721    int[] getContinuationEntryPoints() {
722        return continuationEntryPoints;
723    }
724
725    Type getInvalidatedProgramPointType(final int programPoint) {
726        return invalidatedProgramPoints.get(programPoint);
727    }
728
729    private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) {
730        if (!log.isEnabled()) {
731            return;
732        }
733
734        log.info(phaseName, "finished. Doing IR size calculation...");
735
736        final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification());
737        osc.calculateObjectSize(functionNode);
738
739        final List<ClassHistogramElement> list      = osc.getClassHistogram();
740        final StringBuilder               sb        = new StringBuilder();
741        final long                        totalSize = osc.calculateObjectSize(functionNode);
742
743        sb.append(phaseName).
744            append(" Total size = ").
745            append(totalSize / 1024 / 1024).
746            append("MB");
747        log.info(sb);
748
749        Collections.sort(list, new Comparator<ClassHistogramElement>() {
750            @Override
751            public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
752                final long diff = o1.getBytes() - o2.getBytes();
753                if (diff < 0) {
754                    return 1;
755                } else if (diff > 0) {
756                    return -1;
757                } else {
758                    return 0;
759                }
760            }
761        });
762        for (final ClassHistogramElement e : list) {
763            final String line = String.format("    %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances());
764            log.info(line);
765            if (e.getBytes() < totalSize / 200) {
766                log.info("    ...");
767                break; // never mind, so little memory anyway
768            }
769        }
770    }
771}
772