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