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