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