Compiler.java revision 1522:d2eb81e4ddc8
1219820Sjeff/*
2219820Sjeff * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3219820Sjeff * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4219820Sjeff *
5219820Sjeff * This code is free software; you can redistribute it and/or modify it
6219820Sjeff * under the terms of the GNU General Public License version 2 only, as
7219820Sjeff * published by the Free Software Foundation.  Oracle designates this
8219820Sjeff * particular file as subject to the "Classpath" exception as provided
9219820Sjeff * by Oracle in the LICENSE file that accompanied this code.
10219820Sjeff *
11219820Sjeff * This code is distributed in the hope that it will be useful, but WITHOUT
12219820Sjeff * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13219820Sjeff * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14219820Sjeff * version 2 for more details (a copy is included in the LICENSE file that
15219820Sjeff * accompanied this code).
16219820Sjeff *
17219820Sjeff * You should have received a copy of the GNU General Public License version
18219820Sjeff * 2 along with this work; if not, write to the Free Software Foundation,
19219820Sjeff * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20219820Sjeff *
21219820Sjeff * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22219820Sjeff * or visit www.oracle.com if you need additional information or have any
23219820Sjeff * questions.
24219820Sjeff */
25219820Sjeff
26219820Sjeffpackage jdk.nashorn.internal.codegen;
27219820Sjeff
28219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
29219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
30219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
31219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
32219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
33219820Sjeffimport static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
34219820Sjeffimport static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
35219820Sjeff
36219820Sjeffimport java.io.File;
37219820Sjeffimport java.lang.invoke.MethodType;
38219820Sjeffimport java.util.ArrayList;
39219820Sjeffimport java.util.Arrays;
40219820Sjeffimport java.util.Collections;
41219820Sjeffimport java.util.Comparator;
42219820Sjeffimport java.util.HashMap;
43219820Sjeffimport java.util.Iterator;
44219820Sjeffimport java.util.LinkedHashMap;
45219820Sjeffimport java.util.List;
46219820Sjeffimport java.util.Map;
47219820Sjeffimport java.util.Set;
48219820Sjeffimport java.util.TreeMap;
49219820Sjeffimport java.util.concurrent.TimeUnit;
50219820Sjeffimport java.util.concurrent.atomic.AtomicInteger;
51219820Sjeffimport java.util.function.Consumer;
52219820Sjeffimport java.util.logging.Level;
53219820Sjeffimport jdk.nashorn.internal.codegen.types.Type;
54219820Sjeffimport jdk.nashorn.internal.ir.Expression;
55219820Sjeffimport jdk.nashorn.internal.ir.FunctionNode;
56219820Sjeffimport jdk.nashorn.internal.ir.Optimistic;
57219820Sjeffimport jdk.nashorn.internal.ir.debug.ClassHistogramElement;
58219820Sjeffimport jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
59219820Sjeffimport jdk.nashorn.internal.runtime.CodeInstaller;
60219820Sjeffimport jdk.nashorn.internal.runtime.Context;
61219820Sjeffimport jdk.nashorn.internal.runtime.ErrorManager;
62219820Sjeffimport jdk.nashorn.internal.runtime.FunctionInitializer;
63219820Sjeffimport jdk.nashorn.internal.runtime.ParserException;
64219820Sjeffimport jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
65219820Sjeffimport jdk.nashorn.internal.runtime.ScriptEnvironment;
66219820Sjeffimport jdk.nashorn.internal.runtime.ScriptObject;
67219820Sjeffimport jdk.nashorn.internal.runtime.ScriptRuntime;
68219820Sjeffimport jdk.nashorn.internal.runtime.Source;
69219820Sjeffimport jdk.nashorn.internal.runtime.linker.NameCodec;
70219820Sjeffimport jdk.nashorn.internal.runtime.logging.DebugLogger;
71219820Sjeffimport jdk.nashorn.internal.runtime.logging.Loggable;
72219820Sjeffimport jdk.nashorn.internal.runtime.logging.Logger;
73219820Sjeff
74219820Sjeff/**
75219820Sjeff * Responsible for converting JavaScripts to java byte code. Main entry
76219820Sjeff * point for code generator. The compiler may also install classes given some
77219820Sjeff * predefined Code installation policy, given to it at construction time.
78219820Sjeff * @see CodeInstaller
79219820Sjeff */
80219820Sjeff@Logger(name="compiler")
81219820Sjeffpublic final class Compiler implements Loggable {
82219820Sjeff
83219820Sjeff    /** Name of the scripts package */
84219820Sjeff    public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts";
85219820Sjeff
86219820Sjeff    /** Name of the objects package */
87219820Sjeff    public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
88219820Sjeff
89219820Sjeff    private final ScriptEnvironment env;
90219820Sjeff
91219820Sjeff    private final Source source;
92219820Sjeff
93219820Sjeff    private final String sourceName;
94219820Sjeff
95219820Sjeff    private final ErrorManager errors;
96219820Sjeff
97219820Sjeff    private final boolean optimistic;
98219820Sjeff
99219820Sjeff    private final Map<String, byte[]> bytecode;
100219820Sjeff
101219820Sjeff    private final Set<CompileUnit> compileUnits;
102219820Sjeff
103219820Sjeff    private final ConstantData constantData;
104219820Sjeff
105219820Sjeff    private final CodeInstaller installer;
106219820Sjeff
107219820Sjeff    /** logger for compiler, trampolines and related code generation events
108219820Sjeff     *  that affect classes */
109219820Sjeff    private final DebugLogger log;
110219820Sjeff
111219820Sjeff    private final Context context;
112219820Sjeff
113219820Sjeff    private final TypeMap types;
114219820Sjeff
115219820Sjeff    // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
116219820Sjeff    // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
117219820Sjeff    private final TypeEvaluator typeEvaluator;
118219820Sjeff
119219820Sjeff    private final boolean strict;
120219820Sjeff
121219820Sjeff    private final boolean onDemand;
122219820Sjeff
123219820Sjeff    /**
124219820Sjeff     * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
125219820Sjeff     * that using whatever was at program point 17 as an int failed.
126219820Sjeff     */
127219820Sjeff    private final Map<Integer, Type> invalidatedProgramPoints;
128219820Sjeff
129219820Sjeff    /**
130219820Sjeff     * Descriptor of the location where we write the type information after compilation.
131219820Sjeff     */
132219820Sjeff    private final Object typeInformationFile;
133219820Sjeff
134219820Sjeff    /**
135219820Sjeff     * Compile unit name of first compile unit - this prefix will be used for all
136219820Sjeff     * classes that a compilation generates.
137219820Sjeff     */
138219820Sjeff    private final String firstCompileUnitName;
139219820Sjeff
140219820Sjeff    /**
141219820Sjeff     * Contains the program point that should be used as the continuation entry point, as well as all previous
142219820Sjeff     * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
143219820Sjeff     * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
144219820Sjeff     * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
145219820Sjeff     * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
146219820Sjeff     * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
147219820Sjeff     * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
148219820Sjeff     */
149219820Sjeff    private final int[] continuationEntryPoints;
150219820Sjeff
151219820Sjeff    /**
152219820Sjeff     * ScriptFunction data for what is being compile, where applicable.
153219820Sjeff     * TODO: make this immutable, propagate it through the CompilationPhases
154219820Sjeff     */
155219820Sjeff    private RecompilableScriptFunctionData compiledFunction;
156219820Sjeff
157219820Sjeff    /**
158219820Sjeff     * Most compile unit names are longer than the default StringBuilder buffer,
159219820Sjeff     * worth startup performance when massive class generation is going on to increase
160219820Sjeff     * this
161219820Sjeff     */
162219820Sjeff    private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32;
163219820Sjeff
164219820Sjeff    /**
165219820Sjeff     * Compilation phases that a compilation goes through
166219820Sjeff     */
167219820Sjeff    public static class CompilationPhases implements Iterable<CompilationPhase> {
168219820Sjeff
169219820Sjeff        /**
170219820Sjeff         * Singleton that describes compilation up to the phase where a function can be cached.
171219820Sjeff         */
172219820Sjeff        private final static CompilationPhases COMPILE_UPTO_CACHED = new CompilationPhases(
173219820Sjeff                "Common initial phases",
174219820Sjeff                CompilationPhase.CONSTANT_FOLDING_PHASE,
175219820Sjeff                CompilationPhase.LOWERING_PHASE,
176219820Sjeff                CompilationPhase.APPLY_SPECIALIZATION_PHASE,
177219820Sjeff                CompilationPhase.SPLITTING_PHASE,
178219820Sjeff                CompilationPhase.PROGRAM_POINT_PHASE,
179219820Sjeff                CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
180219820Sjeff                CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
181219820Sjeff                CompilationPhase.CACHE_AST_PHASE
182219820Sjeff                );
183219820Sjeff
184219820Sjeff        private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases(
185219820Sjeff                "After common phases, before bytecode generator",
186219820Sjeff                CompilationPhase.DECLARE_LOCAL_SYMBOLS_PHASE,
187219820Sjeff                CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
188219820Sjeff                CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE
189219820Sjeff                );
190219820Sjeff
191219820Sjeff        /**
192219820Sjeff         * Singleton that describes additional steps to be taken after retrieving a cached function, all the
193219820Sjeff         * way up to (but not including) generating and installing code.
194219820Sjeff         */
195219820Sjeff        public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases(
196219820Sjeff                "Recompile cached function up to bytecode",
197219820Sjeff                CompilationPhase.REINITIALIZE_CACHED,
198219820Sjeff                COMPILE_CACHED_UPTO_BYTECODE
199219820Sjeff                );
200219820Sjeff
201219820Sjeff        /**
202219820Sjeff         * Singleton that describes back end of method generation, given that we have generated the normal
203219820Sjeff         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
204219820Sjeff         */
205219820Sjeff        public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL = new CompilationPhases(
206219820Sjeff                "Generate bytecode and install",
207219820Sjeff                CompilationPhase.BYTECODE_GENERATION_PHASE,
208219820Sjeff                CompilationPhase.INSTALL_PHASE
209219820Sjeff                );
210219820Sjeff
211219820Sjeff        /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
212219820Sjeff        public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases(
213219820Sjeff                "Compile upto bytecode",
214219820Sjeff                COMPILE_UPTO_CACHED,
215219820Sjeff                COMPILE_CACHED_UPTO_BYTECODE);
216219820Sjeff
217219820Sjeff        /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
218219820Sjeff        public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases(
219219820Sjeff                "Compile without install",
220219820Sjeff                COMPILE_UPTO_BYTECODE,
221219820Sjeff                CompilationPhase.BYTECODE_GENERATION_PHASE);
222219820Sjeff
223219820Sjeff        /** Singleton that describes a standard eager compilation - this includes code installation */
224219820Sjeff        public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
225219820Sjeff                "Full eager compilation",
226219820Sjeff                COMPILE_UPTO_BYTECODE,
227219820Sjeff                GENERATE_BYTECODE_AND_INSTALL);
228219820Sjeff
229219820Sjeff        /** Singleton that describes a full compilation - this includes code installation - from serialized state*/
230219820Sjeff        public final static CompilationPhases COMPILE_ALL_CACHED = new CompilationPhases(
231219820Sjeff                "Eager compilation from serializaed state",
232219820Sjeff                RECOMPILE_CACHED_UPTO_BYTECODE,
233219820Sjeff                GENERATE_BYTECODE_AND_INSTALL);
234219820Sjeff
235219820Sjeff        /**
236219820Sjeff         * Singleton that describes restOf method generation, given that we have generated the normal
237219820Sjeff         * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
238219820Sjeff         */
239219820Sjeff        public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL_RESTOF = new CompilationPhases(
240219820Sjeff                "Generate bytecode and install - RestOf method",
241219820Sjeff                CompilationPhase.REUSE_COMPILE_UNITS_PHASE,
242219820Sjeff                GENERATE_BYTECODE_AND_INSTALL);
243219820Sjeff
244219820Sjeff        /** Compile all for a rest of method */
245219820Sjeff        public final static CompilationPhases COMPILE_ALL_RESTOF = new CompilationPhases(
246219820Sjeff                "Compile all, rest of",
247219820Sjeff                COMPILE_UPTO_BYTECODE,
248219820Sjeff                GENERATE_BYTECODE_AND_INSTALL_RESTOF);
249219820Sjeff
250219820Sjeff        /** Compile from serialized for a rest of method */
251219820Sjeff        public final static CompilationPhases COMPILE_CACHED_RESTOF = new CompilationPhases(
252219820Sjeff                "Compile serialized, rest of",
253219820Sjeff                RECOMPILE_CACHED_UPTO_BYTECODE,
254219820Sjeff                GENERATE_BYTECODE_AND_INSTALL_RESTOF);
255219820Sjeff
256219820Sjeff        private final List<CompilationPhase> phases;
257219820Sjeff
258219820Sjeff        private final String desc;
259219820Sjeff
260219820Sjeff        private CompilationPhases(final String desc, final CompilationPhase... phases) {
261219820Sjeff            this(desc, Arrays.asList(phases));
262219820Sjeff        }
263219820Sjeff
264219820Sjeff        private CompilationPhases(final String desc, final CompilationPhases base, final CompilationPhase... phases) {
265219820Sjeff            this(desc, concat(base.phases, Arrays.asList(phases)));
266219820Sjeff        }
267219820Sjeff
268219820Sjeff        private CompilationPhases(final String desc, final CompilationPhase first, final CompilationPhases rest) {
269219820Sjeff            this(desc, concat(Collections.singletonList(first), rest.phases));
270219820Sjeff        }
271219820Sjeff
272219820Sjeff        private CompilationPhases(final String desc, final CompilationPhases base) {
273219820Sjeff            this(desc, base.phases);
274219820Sjeff        }
275219820Sjeff
276219820Sjeff        private CompilationPhases(final String desc, final CompilationPhases... bases) {
277219820Sjeff            this(desc, concatPhases(bases));
278219820Sjeff        }
279219820Sjeff
280219820Sjeff        private CompilationPhases(final String desc, final List<CompilationPhase> phases) {
281219820Sjeff            this.desc = desc;
282219820Sjeff            this.phases = phases;
283219820Sjeff        }
284219820Sjeff
285219820Sjeff        private static List<CompilationPhase> concatPhases(final CompilationPhases[] bases) {
286219820Sjeff            final ArrayList<CompilationPhase> l = new ArrayList<>();
287219820Sjeff            for(final CompilationPhases base: bases) {
288219820Sjeff                l.addAll(base.phases);
289219820Sjeff            }
290219820Sjeff            l.trimToSize();
291219820Sjeff            return l;
292219820Sjeff        }
293219820Sjeff
294219820Sjeff        private static <T> List<T> concat(final List<T> l1, final List<T> l2) {
295219820Sjeff            final ArrayList<T> l = new ArrayList<>(l1);
296219820Sjeff            l.addAll(l2);
297219820Sjeff            l.trimToSize();
298219820Sjeff            return l;
299219820Sjeff        }
300219820Sjeff
301219820Sjeff        @Override
302219820Sjeff        public String toString() {
303219820Sjeff            return "'" + desc + "' " + phases.toString();
304219820Sjeff        }
305219820Sjeff
306219820Sjeff        boolean contains(final CompilationPhase phase) {
307219820Sjeff            return phases.contains(phase);
308219820Sjeff        }
309219820Sjeff
310219820Sjeff        @Override
311219820Sjeff        public Iterator<CompilationPhase> iterator() {
312219820Sjeff            return phases.iterator();
313219820Sjeff        }
314219820Sjeff
315219820Sjeff        boolean isRestOfCompilation() {
316219820Sjeff            return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_CACHED_RESTOF;
317219820Sjeff        }
318219820Sjeff
319219820Sjeff        String getDesc() {
320219820Sjeff            return desc;
321219820Sjeff        }
322219820Sjeff
323219820Sjeff        String toString(final String prefix) {
324219820Sjeff            final StringBuilder sb = new StringBuilder();
325219820Sjeff            for (final CompilationPhase phase : phases) {
326219820Sjeff                sb.append(prefix).append(phase).append('\n');
327219820Sjeff            }
328219820Sjeff            return sb.toString();
329219820Sjeff        }
330219820Sjeff    }
331219820Sjeff
332219820Sjeff    /**
333219820Sjeff     * This array contains names that need to be reserved at the start
334219820Sjeff     * of a compile, to avoid conflict with variable names later introduced.
335219820Sjeff     * See {@link CompilerConstants} for special names used for structures
336219820Sjeff     * during a compile.
337219820Sjeff     */
338219820Sjeff    private static String[] RESERVED_NAMES = {
339219820Sjeff        SCOPE.symbolName(),
340219820Sjeff        THIS.symbolName(),
341219820Sjeff        RETURN.symbolName(),
342219820Sjeff        CALLEE.symbolName(),
343219820Sjeff        VARARGS.symbolName(),
344219820Sjeff        ARGUMENTS.symbolName()
345219820Sjeff    };
346219820Sjeff
347219820Sjeff    // per instance
348219820Sjeff    private final int compilationId = COMPILATION_ID.getAndIncrement();
349219820Sjeff
350219820Sjeff    // per instance
351219820Sjeff    private final AtomicInteger nextCompileUnitId = new AtomicInteger(0);
352219820Sjeff
353219820Sjeff    private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0);
354219820Sjeff
355219820Sjeff    /**
356219820Sjeff     * Creates a new compiler instance for initial compilation of a script.
357219820Sjeff     *
358219820Sjeff     * @param installer code installer
359219820Sjeff     * @param source    source to compile
360219820Sjeff     * @param errors    error manager
361219820Sjeff     * @param isStrict  is this a strict compilation
362219820Sjeff     * @return a new compiler
363219820Sjeff     */
364219820Sjeff    public static Compiler forInitialCompilation(
365219820Sjeff            final CodeInstaller installer,
366219820Sjeff            final Source source,
367219820Sjeff            final ErrorManager errors,
368219820Sjeff            final boolean isStrict) {
369219820Sjeff        return new Compiler(installer.getContext(), installer, source, errors, isStrict);
370219820Sjeff    }
371219820Sjeff
372219820Sjeff    /**
373219820Sjeff     * Creates a compiler without a code installer. It can only be used to compile code, not install the
374219820Sjeff     * generated classes and as such it is useful only for implementation of {@code --compile-only} command
375219820Sjeff     * line option.
376219820Sjeff     * @param context  the current context
377219820Sjeff     * @param source   source to compile
378219820Sjeff     * @param isStrict is this a strict compilation
379219820Sjeff     * @return a new compiler
380219820Sjeff     */
381219820Sjeff    public static Compiler forNoInstallerCompilation(
382219820Sjeff            final Context context,
383219820Sjeff            final Source source,
384219820Sjeff            final boolean isStrict) {
385219820Sjeff        return new Compiler(context, null, source, context.getErrorManager(), isStrict);
386219820Sjeff    }
387219820Sjeff
388219820Sjeff    /**
389219820Sjeff     * Creates a compiler for an on-demand compilation job.
390219820Sjeff     *
391219820Sjeff     * @param installer                code installer
392219820Sjeff     * @param source                   source to compile
393219820Sjeff     * @param isStrict                 is this a strict compilation
394219820Sjeff     * @param compiledFunction         compiled function, if any
395219820Sjeff     * @param types                    parameter and return value type information, if any is known
396219820Sjeff     * @param invalidatedProgramPoints invalidated program points for recompilation
397219820Sjeff     * @param typeInformationFile      descriptor of the location where type information is persisted
398219820Sjeff     * @param continuationEntryPoints  continuation entry points for restof method
399219820Sjeff     * @param runtimeScope             runtime scope for recompilation type lookup in {@code TypeEvaluator}
400219820Sjeff     * @return a new compiler
401219820Sjeff     */
402219820Sjeff    public static Compiler forOnDemandCompilation(
403219820Sjeff            final CodeInstaller installer,
404219820Sjeff            final Source source,
405219820Sjeff            final boolean isStrict,
406219820Sjeff            final RecompilableScriptFunctionData compiledFunction,
407219820Sjeff            final TypeMap types,
408219820Sjeff            final Map<Integer, Type> invalidatedProgramPoints,
409219820Sjeff            final Object typeInformationFile,
410219820Sjeff            final int[] continuationEntryPoints,
411219820Sjeff            final ScriptObject runtimeScope) {
412219820Sjeff        final Context context = installer.getContext();
413219820Sjeff        return new Compiler(context, installer, source, context.getErrorManager(), isStrict, true,
414219820Sjeff                compiledFunction, types, invalidatedProgramPoints, typeInformationFile,
415219820Sjeff                continuationEntryPoints, runtimeScope);
416219820Sjeff    }
417219820Sjeff
418219820Sjeff    /**
419219820Sjeff     * Convenience constructor for non on-demand compiler instances.
420219820Sjeff     */
421219820Sjeff    private Compiler(
422219820Sjeff            final Context context,
423219820Sjeff            final CodeInstaller installer,
424219820Sjeff            final Source source,
425219820Sjeff            final ErrorManager errors,
426219820Sjeff            final boolean isStrict) {
427219820Sjeff        this(context, installer, source, errors, isStrict, false, null, null, null, null, null, null);
428219820Sjeff    }
429219820Sjeff
430219820Sjeff    private Compiler(
431219820Sjeff            final Context context,
432219820Sjeff            final CodeInstaller installer,
433219820Sjeff            final Source source,
434219820Sjeff            final ErrorManager errors,
435219820Sjeff            final boolean isStrict,
436219820Sjeff            final boolean isOnDemand,
437219820Sjeff            final RecompilableScriptFunctionData compiledFunction,
438219820Sjeff            final TypeMap types,
439219820Sjeff            final Map<Integer, Type> invalidatedProgramPoints,
440219820Sjeff            final Object typeInformationFile,
441219820Sjeff            final int[] continuationEntryPoints,
442219820Sjeff            final ScriptObject runtimeScope) {
443219820Sjeff        this.context                  = context;
444219820Sjeff        this.env                      = context.getEnv();
445219820Sjeff        this.installer                = installer;
446219820Sjeff        this.constantData             = new ConstantData();
447219820Sjeff        this.compileUnits             = CompileUnit.createCompileUnitSet();
448219820Sjeff        this.bytecode                 = new LinkedHashMap<>();
449219820Sjeff        this.log                      = initLogger(context);
450219820Sjeff        this.source                   = source;
451219820Sjeff        this.errors                   = errors;
452219820Sjeff        this.sourceName               = FunctionNode.getSourceName(source);
453219820Sjeff        this.onDemand                 = isOnDemand;
454219820Sjeff        this.compiledFunction         = compiledFunction;
455219820Sjeff        this.types                    = types;
456219820Sjeff        this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<>() : invalidatedProgramPoints;
457219820Sjeff        this.typeInformationFile      = typeInformationFile;
458219820Sjeff        this.continuationEntryPoints  = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
459219820Sjeff        this.typeEvaluator            = new TypeEvaluator(this, runtimeScope);
460219820Sjeff        this.firstCompileUnitName     = firstCompileUnitName();
461219820Sjeff        this.strict                   = isStrict;
462219820Sjeff
463219820Sjeff        this.optimistic = env._optimistic_types;
464219820Sjeff    }
465219820Sjeff
466219820Sjeff    private String safeSourceName() {
467219820Sjeff        String baseName = new File(source.getName()).getName();
468219820Sjeff
469219820Sjeff        final int index = baseName.lastIndexOf(".js");
470219820Sjeff        if (index != -1) {
471219820Sjeff            baseName = baseName.substring(0, index);
472219820Sjeff        }
473219820Sjeff
474219820Sjeff        baseName = baseName.replace('.', '_').replace('-', '_');
475219820Sjeff        if (!env._loader_per_compile) {
476219820Sjeff            baseName += installer.getUniqueScriptId();
477219820Sjeff        }
478219820Sjeff
479219820Sjeff        // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char.
480219820Sjeff        // While ASM accepts such escapes for method names, field names, it enforces Java identifier
481219820Sjeff        // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_'
482219820Sjeff        // rather than safe encoding using '\'.
483219820Sjeff        final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName);
484219820Sjeff        return mangled != null ? mangled : baseName;
485219820Sjeff    }
486219820Sjeff
487219820Sjeff    private static final String DANGEROUS_CHARS   = "\\/.;:$[]<>";
488219820Sjeff    private static String replaceDangerChars(final String name) {
489219820Sjeff        final int len = name.length();
490219820Sjeff        final StringBuilder buf = new StringBuilder();
491219820Sjeff        for (int i = 0; i < len; i++) {
492219820Sjeff            final char ch = name.charAt(i);
493219820Sjeff            if (DANGEROUS_CHARS.indexOf(ch) != -1) {
494219820Sjeff                buf.append('_');
495219820Sjeff            } else {
496219820Sjeff                buf.append(ch);
497219820Sjeff            }
498219820Sjeff        }
499219820Sjeff        return buf.toString();
500219820Sjeff    }
501219820Sjeff
502219820Sjeff    private String firstCompileUnitName() {
503219820Sjeff        final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
504219820Sjeff                append('/').
505219820Sjeff                append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()).
506219820Sjeff                append('$');
507219820Sjeff
508219820Sjeff        if (isOnDemandCompilation()) {
509219820Sjeff            sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX);
510219820Sjeff        }
511219820Sjeff
512219820Sjeff        if (compilationId > 0) {
513219820Sjeff            sb.append(compilationId).append('$');
514219820Sjeff        }
515219820Sjeff
516219820Sjeff        if (types != null && compiledFunction.getFunctionNodeId() > 0) {
517219820Sjeff            sb.append(compiledFunction.getFunctionNodeId());
518219820Sjeff            final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId());
519219820Sjeff            for (final Type t : paramTypes) {
520219820Sjeff                sb.append(Type.getShortSignatureDescriptor(t));
521219820Sjeff            }
522219820Sjeff            sb.append('$');
523219820Sjeff        }
524219820Sjeff
525219820Sjeff        sb.append(safeSourceName());
526219820Sjeff
527219820Sjeff        return sb.toString();
528219820Sjeff    }
529219820Sjeff
530219820Sjeff    void declareLocalSymbol(final String symbolName) {
531219820Sjeff        typeEvaluator.declareLocalSymbol(symbolName);
532219820Sjeff    }
533219820Sjeff
534219820Sjeff    void setData(final RecompilableScriptFunctionData data) {
535219820Sjeff        assert this.compiledFunction == null : data;
536219820Sjeff        this.compiledFunction = data;
537219820Sjeff    }
538219820Sjeff
539219820Sjeff    @Override
540219820Sjeff    public DebugLogger getLogger() {
541219820Sjeff        return log;
542219820Sjeff    }
543219820Sjeff
544219820Sjeff    @Override
545219820Sjeff    public DebugLogger initLogger(final Context ctxt) {
546219820Sjeff        final boolean optimisticTypes = env._optimistic_types;
547219820Sjeff        final boolean lazyCompilation = env._lazy_compilation;
548219820Sjeff
549219820Sjeff        return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() {
550219820Sjeff            @Override
551219820Sjeff            public void accept(final DebugLogger newLogger) {
552219820Sjeff                if (!lazyCompilation) {
553219820Sjeff                    newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting.");
554219820Sjeff                }
555219820Sjeff                newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED.");
556219820Sjeff            }
557219820Sjeff        });
558219820Sjeff    }
559219820Sjeff
560219820Sjeff    ScriptEnvironment getScriptEnvironment() {
561219820Sjeff        return env;
562219820Sjeff    }
563219820Sjeff
564219820Sjeff    boolean isOnDemandCompilation() {
565219820Sjeff        return onDemand;
566219820Sjeff    }
567219820Sjeff
568219820Sjeff    boolean useOptimisticTypes() {
569219820Sjeff        return optimistic;
570219820Sjeff    }
571219820Sjeff
572219820Sjeff    Context getContext() {
573219820Sjeff        return context;
574219820Sjeff    }
575219820Sjeff
576219820Sjeff    Type getOptimisticType(final Optimistic node) {
577219820Sjeff        return typeEvaluator.getOptimisticType(node);
578219820Sjeff    }
579219820Sjeff
580219820Sjeff    /**
581219820Sjeff     * Returns true if the expression can be safely evaluated, and its value is an object known to always use
582219820Sjeff     * String as the type of its property names retrieved through
583219820Sjeff     * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its
584219820Sjeff     * property name types.
585219820Sjeff     * @param expr the expression to test
586219820Sjeff     * @return true if the expression can be safely evaluated, and its value is an object known to always use
587219820Sjeff     * String as the type of its property iterators.
588219820Sjeff     */
589219820Sjeff    boolean hasStringPropertyIterator(final Expression expr) {
590219820Sjeff        return typeEvaluator.hasStringPropertyIterator(expr);
591219820Sjeff    }
592219820Sjeff
593219820Sjeff    void addInvalidatedProgramPoint(final int programPoint, final Type type) {
594219820Sjeff        invalidatedProgramPoints.put(programPoint, type);
595219820Sjeff    }
596219820Sjeff
597219820Sjeff
598219820Sjeff    /**
599219820Sjeff     * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The
600219820Sjeff     * copy is not live with regard to changes in state in this compiler instance, and is mutable.
601219820Sjeff     * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types.
602219820Sjeff     */
603219820Sjeff    public Map<Integer, Type> getInvalidatedProgramPoints() {
604219820Sjeff        return invalidatedProgramPoints.isEmpty() ? null : new TreeMap<>(invalidatedProgramPoints);
605219820Sjeff    }
606219820Sjeff
607219820Sjeff    TypeMap getTypeMap() {
608219820Sjeff        return types;
609219820Sjeff    }
610219820Sjeff
611219820Sjeff    MethodType getCallSiteType(final FunctionNode fn) {
612219820Sjeff        if (types == null || !isOnDemandCompilation()) {
613219820Sjeff            return null;
614219820Sjeff        }
615219820Sjeff        return types.getCallSiteType(fn);
616219820Sjeff    }
617219820Sjeff
618219820Sjeff    Type getParamType(final FunctionNode fn, final int pos) {
619219820Sjeff        return types == null ? null : types.get(fn, pos);
620219820Sjeff    }
621219820Sjeff
622219820Sjeff    /**
623219820Sjeff     * Do a compilation job
624219820Sjeff     *
625219820Sjeff     * @param functionNode function node to compile
626219820Sjeff     * @param phases phases of compilation transforms to apply to function
627219820Sjeff
628219820Sjeff     * @return transformed function
629219820Sjeff     *
630219820Sjeff     * @throws CompilationException if error occurs during compilation
631219820Sjeff     */
632219820Sjeff    public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
633219820Sjeff        if (log.isEnabled()) {
634219820Sjeff            log.info(">> Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
635219820Sjeff            log.indent();
636219820Sjeff        }
637219820Sjeff
638219820Sjeff        final String name = DebugLogger.quote(functionNode.getName());
639219820Sjeff
640219820Sjeff        FunctionNode newFunctionNode = functionNode;
641219820Sjeff
642219820Sjeff        for (final String reservedName : RESERVED_NAMES) {
643219820Sjeff            newFunctionNode.uniqueName(reservedName);
644219820Sjeff        }
645219820Sjeff
646219820Sjeff        final boolean info = log.isLoggable(Level.INFO);
647219820Sjeff
648219820Sjeff        final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null;
649219820Sjeff
650219820Sjeff        long time = 0L;
651219820Sjeff
652219820Sjeff        for (final CompilationPhase phase : phases) {
653219820Sjeff            log.fine(phase, " starting for ", name);
654219820Sjeff
655219820Sjeff            try {
656219820Sjeff                newFunctionNode = phase.apply(this, phases, newFunctionNode);
657219820Sjeff            } catch (final ParserException error) {
658219820Sjeff                errors.error(error);
659219820Sjeff                if (env._dump_on_error) {
660219820Sjeff                    error.printStackTrace(env.getErr());
661219820Sjeff                }
662219820Sjeff                return null;
663219820Sjeff            }
664219820Sjeff
665219820Sjeff            log.fine(phase, " done for function ", quote(name));
666219820Sjeff
667219820Sjeff            if (env._print_mem_usage) {
668219820Sjeff                printMemoryUsage(functionNode, phase.toString());
669219820Sjeff            }
670219820Sjeff
671219820Sjeff            time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
672219820Sjeff        }
673219820Sjeff
674219820Sjeff        if (typeInformationFile != null && !phases.isRestOfCompilation()) {
675219820Sjeff            OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
676219820Sjeff        }
677219820Sjeff
678219820Sjeff        log.unindent();
679219820Sjeff
680219820Sjeff        if (info) {
681219820Sjeff            final StringBuilder sb = new StringBuilder("<< Finished compile job for ");
682219820Sjeff            sb.append(newFunctionNode.getSource()).
683219820Sjeff            append(':').
684219820Sjeff            append(quote(newFunctionNode.getName()));
685219820Sjeff
686219820Sjeff            if (time > 0L && timeLogger != null) {
687219820Sjeff                assert env.isTimingEnabled();
688219820Sjeff                sb.append(" in ").append(TimeUnit.NANOSECONDS.toMillis(time)).append(" ms");
689219820Sjeff            }
690219820Sjeff            log.info(sb);
691219820Sjeff        }
692219820Sjeff
693219820Sjeff        return newFunctionNode;
694219820Sjeff    }
695219820Sjeff
696219820Sjeff    Source getSource() {
697219820Sjeff        return source;
698219820Sjeff    }
699219820Sjeff
700219820Sjeff    Map<String, byte[]> getBytecode() {
701219820Sjeff        return Collections.unmodifiableMap(bytecode);
702219820Sjeff    }
703219820Sjeff
704219820Sjeff    /**
705219820Sjeff     * Reset bytecode cache for compiler reuse.
706219820Sjeff     */
707219820Sjeff    void clearBytecode() {
708219820Sjeff        bytecode.clear();
709219820Sjeff    }
710219820Sjeff
711219820Sjeff    CompileUnit getFirstCompileUnit() {
712219820Sjeff        assert !compileUnits.isEmpty();
713219820Sjeff        return compileUnits.iterator().next();
714219820Sjeff    }
715219820Sjeff
716219820Sjeff    Set<CompileUnit> getCompileUnits() {
717219820Sjeff        return compileUnits;
718219820Sjeff    }
719219820Sjeff
720219820Sjeff    ConstantData getConstantData() {
721219820Sjeff        return constantData;
722219820Sjeff    }
723219820Sjeff
724219820Sjeff    CodeInstaller getCodeInstaller() {
725219820Sjeff        return installer;
726219820Sjeff    }
727219820Sjeff
728219820Sjeff    void addClass(final String name, final byte[] code) {
729219820Sjeff        bytecode.put(name, code);
730219820Sjeff    }
731219820Sjeff
732219820Sjeff    String nextCompileUnitName() {
733219820Sjeff        final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE);
734219820Sjeff        sb.append(firstCompileUnitName);
735219820Sjeff        final int cuid = nextCompileUnitId.getAndIncrement();
736219820Sjeff        if (cuid > 0) {
737219820Sjeff            sb.append("$cu").append(cuid);
738219820Sjeff        }
739219820Sjeff
740219820Sjeff        return sb.toString();
741219820Sjeff    }
742219820Sjeff
743219820Sjeff    /**
744219820Sjeff     * Persist current compilation with the given {@code cacheKey}.
745219820Sjeff     * @param cacheKey cache key
746219820Sjeff     * @param functionNode function node
747219820Sjeff     */
748219820Sjeff    public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) {
749219820Sjeff        if (cacheKey != null && env._persistent_cache) {
750219820Sjeff            // If this is an on-demand compilation create a function initializer for the function being compiled.
751219820Sjeff            // Otherwise use function initializer map generated by codegen.
752219820Sjeff            final Map<Integer, FunctionInitializer> initializers = new HashMap<>();
753219820Sjeff            if (isOnDemandCompilation()) {
754219820Sjeff                initializers.put(functionNode.getId(), new FunctionInitializer(functionNode, getInvalidatedProgramPoints()));
755219820Sjeff            } else {
756219820Sjeff                for (final CompileUnit compileUnit : getCompileUnits()) {
757219820Sjeff                    for (final FunctionNode fn : compileUnit.getFunctionNodes()) {
758219820Sjeff                        initializers.put(fn.getId(), new FunctionInitializer(fn));
759219820Sjeff                    }
760219820Sjeff                }
761219820Sjeff            }
762219820Sjeff            final String mainClassName = getFirstCompileUnit().getUnitClassName();
763219820Sjeff            installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId);
764219820Sjeff        }
765219820Sjeff    }
766219820Sjeff
767219820Sjeff    /**
768219820Sjeff     * Make sure the next compilation id is greater than {@code value}.
769219820Sjeff     * @param value compilation id value
770219820Sjeff     */
771219820Sjeff    public static void updateCompilationId(final int value) {
772219820Sjeff        if (value >= COMPILATION_ID.get()) {
773219820Sjeff            COMPILATION_ID.set(value + 1);
774219820Sjeff        }
775219820Sjeff    }
776219820Sjeff
777219820Sjeff    CompileUnit addCompileUnit(final long initialWeight) {
778219820Sjeff        final CompileUnit compileUnit = createCompileUnit(initialWeight);
779219820Sjeff        compileUnits.add(compileUnit);
780219820Sjeff        log.fine("Added compile unit ", compileUnit);
781219820Sjeff        return compileUnit;
782219820Sjeff    }
783219820Sjeff
784219820Sjeff    CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
785219820Sjeff        final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
786219820Sjeff        final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
787219820Sjeff        classEmitter.begin();
788219820Sjeff
789219820Sjeff        return compileUnit;
790219820Sjeff    }
791219820Sjeff
792219820Sjeff    private CompileUnit createCompileUnit(final long initialWeight) {
793219820Sjeff        return createCompileUnit(nextCompileUnitName(), initialWeight);
794219820Sjeff    }
795219820Sjeff
796219820Sjeff    boolean isStrict() {
797219820Sjeff        return strict;
798219820Sjeff    }
799219820Sjeff
800219820Sjeff    void replaceCompileUnits(final Set<CompileUnit> newUnits) {
801219820Sjeff        compileUnits.clear();
802219820Sjeff        compileUnits.addAll(newUnits);
803219820Sjeff    }
804219820Sjeff
805219820Sjeff    CompileUnit findUnit(final long weight) {
806219820Sjeff        for (final CompileUnit unit : compileUnits) {
807219820Sjeff            if (unit.canHold(weight)) {
808219820Sjeff                unit.addWeight(weight);
809219820Sjeff                return unit;
810219820Sjeff            }
811219820Sjeff        }
812219820Sjeff
813219820Sjeff        return addCompileUnit(weight);
814219820Sjeff    }
815219820Sjeff
816219820Sjeff    /**
817219820Sjeff     * Convert a package/class name to a binary name.
818219820Sjeff     *
819219820Sjeff     * @param name Package/class name.
820219820Sjeff     * @return Binary name.
821219820Sjeff     */
822219820Sjeff    public static String binaryName(final String name) {
823219820Sjeff        return name.replace('/', '.');
824219820Sjeff    }
825219820Sjeff
826219820Sjeff    RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
827219820Sjeff        assert compiledFunction != null;
828219820Sjeff        final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId);
829219820Sjeff        assert fn != null : functionId;
830219820Sjeff        return fn;
831219820Sjeff    }
832219820Sjeff
833219820Sjeff    boolean isGlobalSymbol(final FunctionNode fn, final String name) {
834219820Sjeff        return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name);
835219820Sjeff    }
836219820Sjeff
837219820Sjeff    int[] getContinuationEntryPoints() {
838219820Sjeff        return continuationEntryPoints;
839219820Sjeff    }
840219820Sjeff
841219820Sjeff    Type getInvalidatedProgramPointType(final int programPoint) {
842219820Sjeff        return invalidatedProgramPoints.get(programPoint);
843219820Sjeff    }
844219820Sjeff
845219820Sjeff    private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) {
846219820Sjeff        if (!log.isEnabled()) {
847219820Sjeff            return;
848219820Sjeff        }
849219820Sjeff
850219820Sjeff        log.info(phaseName, "finished. Doing IR size calculation...");
851219820Sjeff
852219820Sjeff        final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification());
853219820Sjeff        osc.calculateObjectSize(functionNode);
854219820Sjeff
855219820Sjeff        final List<ClassHistogramElement> list      = osc.getClassHistogram();
856219820Sjeff        final StringBuilder               sb        = new StringBuilder();
857219820Sjeff        final long                        totalSize = osc.calculateObjectSize(functionNode);
858219820Sjeff
859219820Sjeff        sb.append(phaseName).
860219820Sjeff        append(" Total size = ").
861219820Sjeff        append(totalSize / 1024 / 1024).
862219820Sjeff        append("MB");
863219820Sjeff        log.info(sb);
864219820Sjeff
865219820Sjeff        Collections.sort(list, new Comparator<ClassHistogramElement>() {
866219820Sjeff            @Override
867219820Sjeff            public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
868219820Sjeff                final long diff = o1.getBytes() - o2.getBytes();
869219820Sjeff                if (diff < 0) {
870219820Sjeff                    return 1;
871219820Sjeff                } else if (diff > 0) {
872219820Sjeff                    return -1;
873219820Sjeff                } else {
874219820Sjeff                    return 0;
875219820Sjeff                }
876219820Sjeff            }
877219820Sjeff        });
878219820Sjeff        for (final ClassHistogramElement e : list) {
879219820Sjeff            final String line = String.format("    %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances());
880219820Sjeff            log.info(line);
881219820Sjeff            if (e.getBytes() < totalSize / 200) {
882219820Sjeff                log.info("    ...");
883219820Sjeff                break; // never mind, so little memory anyway
884219820Sjeff            }
885219820Sjeff        }
886219820Sjeff    }
887219820Sjeff}
888219820Sjeff