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.atomic.AtomicInteger;
  50 import java.util.function.Consumer;
  51 import java.util.logging.Level;
  52 import jdk.internal.dynalink.support.NameCodec;
  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.logging.DebugLogger;
  70 import jdk.nashorn.internal.runtime.logging.Loggable;
  71 import 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")
  80 public 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 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     private final Map<Integer, byte[]> serializedAsts = new HashMap<>();
 164 
 165     /**
 166      * Compilation phases that a compilation goes through
 167      */
 168     public static class CompilationPhases implements Iterable<CompilationPhase> {
 169 
 170         /**
 171          * Singleton that describes compilation up to the phase where a function can be serialized.
 172          */
 173         private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases(
 174                 "Common initial phases",
 175                 CompilationPhase.CONSTANT_FOLDING_PHASE,
 176                 CompilationPhase.LOWERING_PHASE,
 177                 CompilationPhase.TRANSFORM_BUILTINS_PHASE,
 178                 CompilationPhase.SPLITTING_PHASE,
 179                 CompilationPhase.PROGRAM_POINT_PHASE,
 180                 CompilationPhase.SERIALIZE_SPLIT_PHASE
 181                 );
 182 
 183         private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases(
 184                 "After common phases, before bytecode generator",
 185                 CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
 186                 CompilationPhase.SCOPE_DEPTH_COMPUTATION_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 deserializing, all the way up to (but not
 193          * including) generating and installing code.
 194          */
 195         public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases(
 196                 "Recompile serialized function up to bytecode",
 197                 CompilationPhase.REINITIALIZE_SERIALIZED,
 198                 COMPILE_SERIALIZABLE_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_SERIALIZABLE,
 215                 COMPILE_SERIALIZABLE_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_SERIALIZED = new CompilationPhases(
 231                 "Eager compilation from serializaed state",
 232                 RECOMPILE_SERIALIZED_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_SERIALIZED_RESTOF = new CompilationPhases(
 252                 "Compile serialized, rest of",
 253                 RECOMPILE_SERIALIZED_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_SERIALIZED_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      * Constructor
 357      *
 358      * @param context   context
 359      * @param env       script environment
 360      * @param installer code installer
 361      * @param source    source to compile
 362      * @param errors    error manager
 363      * @param isStrict  is this a strict compilation
 364      */
 365     public Compiler(
 366             final Context context,
 367             final ScriptEnvironment env,
 368             final CodeInstaller<ScriptEnvironment> installer,
 369             final Source source,
 370             final ErrorManager errors,
 371             final boolean isStrict) {
 372         this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null);
 373     }
 374 
 375     /**
 376      * Constructor
 377      *
 378      * @param context                  context
 379      * @param env                      script environment
 380      * @param installer                code installer
 381      * @param source                   source to compile
 382      * @param errors                   error manager
 383      * @param isStrict                 is this a strict compilation
 384      * @param isOnDemand               is this an on demand compilation
 385      * @param compiledFunction         compiled function, if any
 386      * @param types                    parameter and return value type information, if any is known
 387      * @param invalidatedProgramPoints invalidated program points for recompilation
 388      * @param typeInformationFile      descriptor of the location where type information is persisted
 389      * @param continuationEntryPoints  continuation entry points for restof method
 390      * @param runtimeScope             runtime scope for recompilation type lookup in {@code TypeEvaluator}
 391      */
 392     @SuppressWarnings("unused")
 393     public Compiler(
 394             final Context context,
 395             final ScriptEnvironment env,
 396             final CodeInstaller<ScriptEnvironment> installer,
 397             final Source source,
 398             final ErrorManager errors,
 399             final boolean isStrict,
 400             final boolean isOnDemand,
 401             final RecompilableScriptFunctionData compiledFunction,
 402             final TypeMap types,
 403             final Map<Integer, Type> invalidatedProgramPoints,
 404             final Object typeInformationFile,
 405             final int[] continuationEntryPoints,
 406             final ScriptObject runtimeScope) {
 407         this.context                  = context;
 408         this.env                      = env;
 409         this.installer                = installer;
 410         this.constantData             = new ConstantData();
 411         this.compileUnits             = CompileUnit.createCompileUnitSet();
 412         this.bytecode                 = new LinkedHashMap<>();
 413         this.log                      = initLogger(context);
 414         this.source                   = source;
 415         this.errors                   = errors;
 416         this.sourceName               = FunctionNode.getSourceName(source);
 417         this.onDemand                 = isOnDemand;
 418         this.compiledFunction         = compiledFunction;
 419         this.types                    = types;
 420         this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
 421         this.typeInformationFile      = typeInformationFile;
 422         this.continuationEntryPoints  = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
 423         this.typeEvaluator            = new TypeEvaluator(this, runtimeScope);
 424         this.firstCompileUnitName     = firstCompileUnitName();
 425         this.strict                   = isStrict;
 426 
 427         this.optimistic = env._optimistic_types;
 428     }
 429 
 430     private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) {
 431         String baseName = new File(source.getName()).getName();
 432 
 433         final int index = baseName.lastIndexOf(".js");
 434         if (index != -1) {
 435             baseName = baseName.substring(0, index);
 436         }
 437 
 438         baseName = baseName.replace('.', '_').replace('-', '_');
 439         if (!env._loader_per_compile) {
 440             baseName = baseName + installer.getUniqueScriptId();
 441         }
 442 
 443         // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char.
 444         // While ASM accepts such escapes for method names, field names, it enforces Java identifier
 445         // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_'
 446         // rather than safe encoding using '\'.
 447         final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName);
 448         return mangled != null ? mangled : baseName;
 449     }
 450 
 451     private static final String DANGEROUS_CHARS   = "\\/.;:$[]<>";
 452     private static String replaceDangerChars(final String name) {
 453         final int len = name.length();
 454         final StringBuilder buf = new StringBuilder();
 455         for (int i = 0; i < len; i++) {
 456             final char ch = name.charAt(i);
 457             if (DANGEROUS_CHARS.indexOf(ch) != -1) {
 458                 buf.append('_');
 459             } else {
 460                 buf.append(ch);
 461             }
 462         }
 463         return buf.toString();
 464     }
 465 
 466     private String firstCompileUnitName() {
 467         final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
 468                 append('/').
 469                 append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()).
 470                 append('$');
 471 
 472         if (isOnDemandCompilation()) {
 473             sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX);
 474         }
 475 
 476         if (compilationId > 0) {
 477             sb.append(compilationId).append('$');
 478         }
 479 
 480         if (types != null && compiledFunction.getFunctionNodeId() > 0) {
 481             sb.append(compiledFunction.getFunctionNodeId());
 482             final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId());
 483             for (final Type t : paramTypes) {
 484                 sb.append(Type.getShortSignatureDescriptor(t));
 485             }
 486             sb.append('$');
 487         }
 488 
 489         sb.append(Compiler.safeSourceName(env, installer, source));
 490 
 491         return sb.toString();
 492     }
 493 
 494     void declareLocalSymbol(final String symbolName) {
 495         typeEvaluator.declareLocalSymbol(symbolName);
 496     }
 497 
 498     void setData(final RecompilableScriptFunctionData data) {
 499         assert this.compiledFunction == null : data;
 500         this.compiledFunction = data;
 501     }
 502 
 503     @Override
 504     public DebugLogger getLogger() {
 505         return log;
 506     }
 507 
 508     @Override
 509     public DebugLogger initLogger(final Context ctxt) {
 510         final boolean optimisticTypes = env._optimistic_types;
 511         final boolean lazyCompilation = env._lazy_compilation;
 512 
 513         return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() {
 514             @Override
 515             public void accept(final DebugLogger newLogger) {
 516                 if (!lazyCompilation) {
 517                     newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting.");
 518                 }
 519                 newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED.");
 520             }
 521         });
 522     }
 523 
 524     ScriptEnvironment getScriptEnvironment() {
 525         return env;
 526     }
 527 
 528     boolean isOnDemandCompilation() {
 529         return onDemand;
 530     }
 531 
 532     boolean useOptimisticTypes() {
 533         return optimistic;
 534     }
 535 
 536     Context getContext() {
 537         return context;
 538     }
 539 
 540     Type getOptimisticType(final Optimistic node) {
 541         return typeEvaluator.getOptimisticType(node);
 542     }
 543 
 544     /**
 545      * Returns true if the expression can be safely evaluated, and its value is an object known to always use
 546      * String as the type of its property names retrieved through
 547      * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its
 548      * property name types.
 549      * @param expr the expression to test
 550      * @return true if the expression can be safely evaluated, and its value is an object known to always use
 551      * String as the type of its property iterators.
 552      */
 553     boolean hasStringPropertyIterator(final Expression expr) {
 554         return typeEvaluator.hasStringPropertyIterator(expr);
 555     }
 556 
 557     void addInvalidatedProgramPoint(final int programPoint, final Type type) {
 558         invalidatedProgramPoints.put(programPoint, type);
 559     }
 560 
 561 
 562     /**
 563      * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The
 564      * copy is not live with regard to changes in state in this compiler instance, and is mutable.
 565      * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types.
 566      */
 567     public Map<Integer, Type> getInvalidatedProgramPoints() {
 568         return invalidatedProgramPoints.isEmpty() ? null : new TreeMap<>(invalidatedProgramPoints);
 569     }
 570 
 571     TypeMap getTypeMap() {
 572         return types;
 573     }
 574 
 575     MethodType getCallSiteType(final FunctionNode fn) {
 576         if (types == null || !isOnDemandCompilation()) {
 577             return null;
 578         }
 579         return types.getCallSiteType(fn);
 580     }
 581 
 582     Type getParamType(final FunctionNode fn, final int pos) {
 583         return types == null ? null : types.get(fn, pos);
 584     }
 585 
 586     /**
 587      * Do a compilation job
 588      *
 589      * @param functionNode function node to compile
 590      * @param phases phases of compilation transforms to apply to function
 591 
 592      * @return transformed function
 593      *
 594      * @throws CompilationException if error occurs during compilation
 595      */
 596     public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
 597         if (log.isEnabled()) {
 598             log.info(">> Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
 599             log.indent();
 600         }
 601 
 602         final String name = DebugLogger.quote(functionNode.getName());
 603 
 604         FunctionNode newFunctionNode = functionNode;
 605 
 606         for (final String reservedName : RESERVED_NAMES) {
 607             newFunctionNode.uniqueName(reservedName);
 608         }
 609 
 610         final boolean info = log.levelFinerThanOrEqual(Level.INFO);
 611 
 612         final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null;
 613 
 614         long time = 0L;
 615 
 616         for (final CompilationPhase phase : phases) {
 617             log.fine(phase, " starting for ", name);
 618 
 619             try {
 620                 newFunctionNode = phase.apply(this, phases, newFunctionNode);
 621             } catch (final ParserException error) {
 622                 errors.error(error);
 623                 if (env._dump_on_error) {
 624                     error.printStackTrace(env.getErr());
 625                 }
 626                 return null;
 627             }
 628 
 629             log.fine(phase, " done for function ", quote(name));
 630 
 631             if (env._print_mem_usage) {
 632                 printMemoryUsage(functionNode, phase.toString());
 633             }
 634 
 635             time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
 636         }
 637 
 638         if (typeInformationFile != null && !phases.isRestOfCompilation()) {
 639             OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
 640         }
 641 
 642         log.unindent();
 643 
 644         if (info) {
 645             final StringBuilder sb = new StringBuilder("<< Finished compile job for ");
 646             sb.append(newFunctionNode.getSource()).
 647                 append(':').
 648                 append(quote(newFunctionNode.getName()));
 649 
 650             if (time > 0L && timeLogger != null) {
 651                 assert env.isTimingEnabled();
 652                 sb.append(" in ").append(time).append(" ms");
 653             }
 654             log.info(sb);
 655         }
 656 
 657         return newFunctionNode;
 658     }
 659 
 660     Source getSource() {
 661         return source;
 662     }
 663 
 664     Map<String, byte[]> getBytecode() {
 665         return Collections.unmodifiableMap(bytecode);
 666     }
 667 
 668     /**
 669      * Reset bytecode cache for compiler reuse.
 670      */
 671     void clearBytecode() {
 672         bytecode.clear();
 673     }
 674 
 675     CompileUnit getFirstCompileUnit() {
 676         assert !compileUnits.isEmpty();
 677         return compileUnits.iterator().next();
 678     }
 679 
 680     Set<CompileUnit> getCompileUnits() {
 681         return compileUnits;
 682     }
 683 
 684     ConstantData getConstantData() {
 685         return constantData;
 686     }
 687 
 688     CodeInstaller<ScriptEnvironment> getCodeInstaller() {
 689         return installer;
 690     }
 691 
 692     void addClass(final String name, final byte[] code) {
 693         bytecode.put(name, code);
 694     }
 695 
 696     String nextCompileUnitName() {
 697         final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE);
 698         sb.append(firstCompileUnitName);
 699         final int cuid = nextCompileUnitId.getAndIncrement();
 700         if (cuid > 0) {
 701             sb.append("$cu").append(cuid);
 702         }
 703 
 704         return sb.toString();
 705     }
 706 
 707     /**
 708      * Persist current compilation with the given {@code cacheKey}.
 709      * @param cacheKey cache key
 710      * @param functionNode function node
 711      */
 712     public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) {
 713         if (cacheKey != null && env._persistent_cache) {
 714             // If this is an on-demand compilation create a function initializer for the function being compiled.
 715             // Otherwise use function initializer map generated by codegen.
 716             Map<Integer, FunctionInitializer> initializers = new HashMap<>();
 717             if (isOnDemandCompilation()) {
 718                 initializers.put(functionNode.getId(), new FunctionInitializer(functionNode, getInvalidatedProgramPoints()));
 719             } else {
 720                 for (final CompileUnit compileUnit : getCompileUnits()) {
 721                     for (final FunctionNode fn : compileUnit.getFunctionNodes()) {
 722                         initializers.put(fn.getId(), new FunctionInitializer(fn));
 723                     }
 724                 }
 725             }
 726             final String mainClassName = getFirstCompileUnit().getUnitClassName();
 727             installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId);
 728         }
 729     }
 730 
 731     /**
 732      * Make sure the next compilation id is greater than {@code value}.
 733      * @param value compilation id value
 734      */
 735     public static void updateCompilationId(final int value) {
 736         if (value >= COMPILATION_ID.get()) {
 737             COMPILATION_ID.set(value + 1);
 738         }
 739     }
 740 
 741     CompileUnit addCompileUnit(final long initialWeight) {
 742         final CompileUnit compileUnit = createCompileUnit(initialWeight);
 743         compileUnits.add(compileUnit);
 744         log.fine("Added compile unit ", compileUnit);
 745         return compileUnit;
 746     }
 747 
 748     CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
 749         final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
 750         final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
 751         classEmitter.begin();
 752 
 753         return compileUnit;
 754     }
 755 
 756     private CompileUnit createCompileUnit(final long initialWeight) {
 757         return createCompileUnit(nextCompileUnitName(), initialWeight);
 758     }
 759 
 760     boolean isStrict() {
 761         return strict;
 762     }
 763 
 764     void replaceCompileUnits(final Set<CompileUnit> newUnits) {
 765         compileUnits.clear();
 766         compileUnits.addAll(newUnits);
 767     }
 768 
 769     void serializeAst(final FunctionNode fn) {
 770         serializedAsts.put(fn.getId(), AstSerializer.serialize(fn));
 771     }
 772 
 773     byte[] removeSerializedAst(final int fnId) {
 774         return serializedAsts.remove(fnId);
 775     }
 776 
 777     CompileUnit findUnit(final long weight) {
 778         for (final CompileUnit unit : compileUnits) {
 779             if (unit.canHold(weight)) {
 780                 unit.addWeight(weight);
 781                 return unit;
 782             }
 783         }
 784 
 785         return addCompileUnit(weight);
 786     }
 787 
 788     /**
 789      * Convert a package/class name to a binary name.
 790      *
 791      * @param name Package/class name.
 792      * @return Binary name.
 793      */
 794     public static String binaryName(final String name) {
 795         return name.replace('/', '.');
 796     }
 797 
 798     RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
 799         assert compiledFunction != null;
 800         final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId);
 801         assert fn != null : functionId;
 802         return fn;
 803     }
 804 
 805     boolean isGlobalSymbol(final FunctionNode fn, final String name) {
 806         return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name);
 807     }
 808 
 809     int[] getContinuationEntryPoints() {
 810         return continuationEntryPoints;
 811     }
 812 
 813     Type getInvalidatedProgramPointType(final int programPoint) {
 814         return invalidatedProgramPoints.get(programPoint);
 815     }
 816 
 817     private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) {
 818         if (!log.isEnabled()) {
 819             return;
 820         }
 821 
 822         log.info(phaseName, "finished. Doing IR size calculation...");
 823 
 824         final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification());
 825         osc.calculateObjectSize(functionNode);
 826 
 827         final List<ClassHistogramElement> list      = osc.getClassHistogram();
 828         final StringBuilder               sb        = new StringBuilder();
 829         final long                        totalSize = osc.calculateObjectSize(functionNode);
 830 
 831         sb.append(phaseName).
 832             append(" Total size = ").
 833             append(totalSize / 1024 / 1024).
 834             append("MB");
 835         log.info(sb);
 836 
 837         Collections.sort(list, new Comparator<ClassHistogramElement>() {
 838             @Override
 839             public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
 840                 final long diff = o1.getBytes() - o2.getBytes();
 841                 if (diff < 0) {
 842                     return 1;
 843                 } else if (diff > 0) {
 844                     return -1;
 845                 } else {
 846                     return 0;
 847                 }
 848             }
 849         });
 850         for (final ClassHistogramElement e : list) {
 851             final String line = String.format("    %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances());
 852             log.info(line);
 853             if (e.getBytes() < totalSize / 200) {
 854                 log.info("    ...");
 855                 break; // never mind, so little memory anyway
 856             }
 857         }
 858     }
 859 }