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