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