diff options
Diffstat (limited to 'src/jdk/nashorn/internal/codegen/Compiler.java')
-rw-r--r-- | src/jdk/nashorn/internal/codegen/Compiler.java | 787 |
1 files changed, 247 insertions, 540 deletions
diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java index 3b21ab58..23f38749 100644 --- a/src/jdk/nashorn/internal/codegen/Compiler.java +++ b/src/jdk/nashorn/internal/codegen/Compiler.java @@ -27,458 +27,246 @@ package jdk.nashorn.internal.codegen; import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME; -import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT; +import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; - import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.util.Arrays; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.TreeMap; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.debug.ASTWriter; -import jdk.nashorn.internal.ir.debug.PrintVisitor; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; -import jdk.nashorn.internal.parser.Parser; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.DebugLogger; -import jdk.nashorn.internal.runtime.ECMAErrors; -import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.options.Options; import org.dynalang.dynalink.support.NameCodec; /** * Responsible for converting JavaScripts to java byte code. Main entry - * point for code generator + * point for code generator. The compiler may also install classes given some + * predefined Code installation policy, given to it at construction time. + * @see CodeInstaller */ public final class Compiler { - /** Compiler states available */ - public enum State { - /** compiler is ready */ - INITIALIZED, - /** method has been parsed */ - PARSED, - /** constant folding pass */ - CONSTANT_FOLDED, - /** method has been lowered */ - LOWERED, - /** method hass been attributed */ - ATTR, - /** method has been split */ - SPLIT, - /** method has had its types finalized */ - FINALIZED, - /** method has been emitted to bytecode */ - EMITTED - } - - /** Current context */ - private final Context context; - - /** Currently compiled source */ - private final Source source; - - /** Current error manager */ - private final ErrorManager errors; - - /** Names uniqueName for this compile. */ - private final Namespace namespace; - - /** Current function node, or null if compiling from source until parsed */ - private FunctionNode functionNode; - - /** Current compiler state */ - private final EnumSet<State> state; - /** Name of the scripts package */ public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts"; /** Name of the objects package */ public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects"; - /** Name of the runtime package */ - public static final String RUNTIME_PACKAGE = "jdk/nashorn/internal/runtime"; - - /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */ - public static final String GLOBAL_OBJECT = OBJECTS_PACKAGE + '/' + "Global"; + static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy"); - /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */ - public static final String SCRIPTFUNCTION_IMPL_OBJECT = OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl"; + static final boolean TIME_COMPILATION = Options.getBooleanProperty("nashorn.compiler.time"); - /** Name of the Trampoline, cannot be referred to as .class @see FunctionObjectCreator */ - public static final String TRAMPOLINE_OBJECT = OBJECTS_PACKAGE + '/' + "Trampoline"; + private final Map<String, byte[]> bytecode; - /** Compile unit (class) table. */ private final Set<CompileUnit> compileUnits; - /** All the "complex" constants used in the code. */ private final ConstantData constantData; - static final DebugLogger LOG = new DebugLogger("compiler"); + private final FunctionNode functionNode; - /** Script name */ - private String scriptName; + private final CompilationSequence sequence; - /** Should we dump classes to disk and compile only? */ - private final boolean dumpClass; + private final Context context; - /** Code map class name -> byte code for all classes generated from this Source or FunctionNode */ - private Map<String, byte[]> code; + private final String scriptName; - /** Are we compiling in strict mode? */ private boolean strict; - /** Is this a lazy compilation - i.e. not from source, but jitting a previously parsed FunctionNode? */ - private boolean isLazy; + private CodeInstaller<Context> installer; - /** Lazy jitting is disabled by default */ - private static final boolean LAZY_JIT = false; + static final DebugLogger LOG = new DebugLogger("compiler"); /** - * Should we use integers for arithmetic operations as well? - * TODO: We currently generate no overflow checks so this is - * disabled - * - * @see #shouldUseIntegers() - * - * @return true if arithmetic operations should not widen integer - * operands by default. + * This array contains names that need to be reserved at the start + * of a compile, to avoid conflict with variable names later introduced. + * See {@link CompilerConstants} for special names used for structures + * during a compile. */ - static boolean shouldUseIntegerArithmetic() { - return USE_INT_ARITH; - } + private static String[] RESERVED_NAMES = { + SCOPE.tag(), + THIS.tag() + }; - private static final boolean USE_INT_ARITH; + /** + * This class makes it possible to do your own compilation sequence + * from the code generation package. There are predefined compilation + * sequences already + */ + @SuppressWarnings("serial") + static class CompilationSequence extends LinkedList<CompilationPhase> { - static { - USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic"); + CompilationSequence(final CompilationPhase... phases) { + super(Arrays.asList(phases)); + } - assert !USE_INT_ARITH : "Integer arithmetic is not enabled"; - } + CompilationSequence(final CompilationSequence sequence) { + this(sequence.toArray(new CompilationPhase[sequence.size()])); + } - /** - * Factory method for compiler that should compile from source to bytecode - * - * @param source the source - * @param context context - * - * @return compiler instance - */ - public static Compiler compiler(final Source source, final Context context) { - return Compiler.compiler(source, context, context.getErrorManager(), context._strict); - } + CompilationSequence insertAfter(final CompilationPhase phase, final CompilationPhase newPhase) { + final CompilationSequence newSeq = new CompilationSequence(); + for (final CompilationPhase elem : this) { + newSeq.add(phase); + if (elem.equals(phase)) { + newSeq.add(newPhase); + } + } + assert newSeq.contains(newPhase); + return newSeq; + } - /** - * Factory method to get a compiler that goes from from source to bytecode - * - * @param source source code - * @param context context - * @param errors error manager - * @param strict compilation in strict mode? - * - * @return compiler instance - */ - public static Compiler compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) { - return new Compiler(source, context, errors, strict); + CompilationSequence insertBefore(final CompilationPhase phase, final CompilationPhase newPhase) { + final CompilationSequence newSeq = new CompilationSequence(); + for (final CompilationPhase elem : this) { + if (elem.equals(phase)) { + newSeq.add(newPhase); + } + newSeq.add(phase); + } + assert newSeq.contains(newPhase); + return newSeq; + } + + CompilationSequence insertFirst(final CompilationPhase phase) { + final CompilationSequence newSeq = new CompilationSequence(this); + newSeq.addFirst(phase); + return newSeq; + } + + CompilationSequence insertLast(final CompilationPhase phase) { + final CompilationSequence newSeq = new CompilationSequence(this); + newSeq.addLast(phase); + return newSeq; + } } /** - * Factory method to get a compiler that goes from FunctionNode (parsed) to bytecode - * Requires previous compiler for state - * - * @param compiler primordial compiler - * @param functionNode functionNode to compile - * - * @return compiler + * Standard (non-lazy) compilation, that basically will take an entire script + * and JIT it at once. This can lead to long startup time and fewer type + * specializations */ - public static Compiler compiler(final Compiler compiler, final FunctionNode functionNode) { - assert false : "lazy jit - not implemented"; - final Compiler newCompiler = new Compiler(compiler); - newCompiler.state.add(State.PARSED); - newCompiler.functionNode = functionNode; - newCompiler.isLazy = true; - return compiler; - } - - private Compiler(final Compiler compiler) { - this(compiler.source, compiler.context, compiler.errors, compiler.strict); - } + final static CompilationSequence SEQUENCE_NORMAL = new CompilationSequence( + CompilationPhase.CONSTANT_FOLDING_PHASE, + CompilationPhase.LOWERING_PHASE, + CompilationPhase.ATTRIBUTION_PHASE, + CompilationPhase.SPLITTING_PHASE, + CompilationPhase.TYPE_FINALIZATION_PHASE, + CompilationPhase.BYTECODE_GENERATION_PHASE); + + final static CompilationSequence SEQUENCE_LAZY = + SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE); + + final static CompilationSequence SEQUENCE_DEFAULT = + LAZY_JIT ? + SEQUENCE_LAZY : + SEQUENCE_NORMAL; /** * Constructor * - * @param source the source to compile - * @param context context - * @param errors error manager - * @param strict compile in strict mode + * @param installer code installer from + * @param functionNode function node (in any available {@link CompilationState}) to compile + * @param sequence {@link Compiler#CompilationSequence} of {@link CompilationPhase}s to apply as this compilation + * @param strict should this compilation use strict mode semantics */ - private Compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) { - this.source = source; - this.context = context; - this.errors = errors; - this.strict = strict; - this.namespace = new Namespace(context.getNamespace()); - this.compileUnits = new HashSet<>(); - this.constantData = new ConstantData(); - this.state = EnumSet.of(State.INITIALIZED); - this.dumpClass = context._compile_only && context._dest_dir != null; - } - - private String scriptsPackageName() { - return dumpClass ? "" : (SCRIPTS_PACKAGE + '/'); - } + Compiler(final Context context, final CodeInstaller<Context> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) { + this.context = context; + this.functionNode = functionNode; + this.sequence = sequence; + this.installer = installer; + this.strict = strict || functionNode.isStrictMode(); + this.constantData = new ConstantData(); + this.compileUnits = new HashSet<>(); + this.bytecode = new HashMap<>(); - private int nextCompileUnitIndex() { - return compileUnits.size() + 1; - } + final StringBuilder sb = new StringBuilder(); + sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag())). + append('$'). + append(safeSourceName(functionNode.getSource())). + append(functionNode.isLazy() ? LAZY.tag() : ""); - private String firstCompileUnitName() { - return scriptsPackageName() + scriptName; - } + this.scriptName = sb.toString(); - private String nextCompileUnitName() { - return firstCompileUnitName() + '$' + nextCompileUnitIndex(); + LOG.info("Initializing compiler for scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'"); } - private CompileUnit addCompileUnit(final long initialWeight) { - return addCompileUnit(nextCompileUnitName(), initialWeight); + /** + * Constructor + * + * @param installer code installer from context + * @param functionNode function node (in any available {@link CompilationState}) to compile + * @param strict should this compilation use strict mode semantics + */ + public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode, final boolean strict) { + this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict); } - private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) { - final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight); - compileUnits.add(compileUnit); - LOG.info("Added compile unit " + compileUnit); - return compileUnit; + /** + * Constructor - compilation will use the same strict semantics as context + * + * @param installer code installer from context + * @param functionNode function node (in any available {@link CompilationState}) to compile + */ + public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode) { + this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict); } - private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) { - final ClassEmitter classEmitter = new ClassEmitter(this, unitClassName, strict); - final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); - - classEmitter.begin(); - - final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE)); - initMethod.begin(); - initMethod.load(Type.OBJECT, 0); - initMethod.newInstance(jdk.nashorn.internal.scripts.JS$.class); - initMethod.returnVoid(); - initMethod.end(); - - return compileUnit; + /** + * Constructor - compilation needs no installer, but uses a context + * Used in "compile only" scenarios + * @param context a context + * @param functionNode functionNode to compile + */ + public Compiler(final Context context, final FunctionNode functionNode) { + this(context, null, functionNode, SEQUENCE_DEFAULT, context._strict); } /** - * Perform compilation - * - * @return true if successful, false otherwise - if false check the error manager + * Execute the compilation this Compiler was created with + * @return true if compilation succeeds. */ public boolean compile() { - assert state.contains(State.INITIALIZED); - - /** do we need to parse source? */ - if (!state.contains(State.PARSED)) { - LOG.info("Parsing '" + source + "'"); - - assert this.functionNode == null; - this.functionNode = new Parser(this, strict).parse(RUN_SCRIPT.tag()); - - state.add(State.PARSED); - debugPrintParse(); - - if (errors.hasErrors() || context._parse_only) { - return false; - } - - assert !isLazy; - //tag lazy nodes for later code generation and trampolines - functionNode.accept(new NodeVisitor() { - @Override - public Node enter(final FunctionNode node) { - if (LAZY_JIT) { - node.setIsLazy(!node.isScript()); - } - return node; - } - }); - } else { - assert isLazy; - functionNode.accept(new NodeVisitor() { - @Override - public Node enter(final FunctionNode node) { - node.setIsLazy(false); - return null; //TODO do we want to do this recursively? then return "node" instead - } - }); + for (final String reservedName : RESERVED_NAMES) { + functionNode.uniqueName(reservedName); } - assert functionNode != null; - final boolean oldStrict = strict; - - try { - strict |= functionNode.isStrictMode(); - - /* - * These are the compile phases: - * - * Constant folding pass - * Simple constant folding that will make elementary constructs go away - * - * Lower (Control flow pass) - * Finalizes the control flow. Clones blocks for finally constructs and - * similar things. Establishes termination criteria for nodes - * Guarantee return instructions to method making sure control flow - * cannot fall off the end. Replacing high level nodes with lower such - * as runtime nodes where applicable. - * - * Attr - * Assign symbols and types to all nodes. - * - * Splitter - * Split the AST into several compile units based on a size heuristic - * Splitter needs attributed AST for weight calculations (e.g. is - * a + b a ScriptRuntime.ADD with call overhead or a dadd with much - * less). Split IR can lead to scope information being changed. - * - * Contract: all variables must have slot assignments and scope assignments - * before lowering. - * - * FinalizeTypes - * This pass finalizes the types for nodes. If Attr created wider types than - * known during the first pass, convert nodes are inserted or access nodes - * are specialized where scope accesses. - * - * Runtime nodes may be removed and primitivized or reintroduced depending - * on information that was established in Attr. - * - * CodeGeneration - * Emit bytecode - * - */ - - debugPrintAST(); - - if (!state.contains(State.FINALIZED)) { - LOG.info("Folding constants in '" + functionNode.getName() + "'"); - functionNode.accept(new FoldConstants()); - state.add(State.CONSTANT_FOLDED); - - LOG.info("Lowering '" + functionNode.getName() + "'"); - functionNode.accept(new Lower(this)); - state.add(State.LOWERED); - debugPrintAST(); - - LOG.info("Attributing types '" + functionNode.getName() + "'"); - functionNode.accept(new Attr(this)); - state.add(State.ATTR); - - this.scriptName = computeNames(); - - // Main script code always goes to this compile unit. Note that since we start this with zero weight - // and add script code last this class may end up slightly larger than others, but reserving one class - // just for the main script seems wasteful. - final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0L); - LOG.info("Splitting '" + functionNode.getName() + "'"); - new Splitter(this, functionNode, scriptCompileUnit).split(); - state.add(State.SPLIT); - assert functionNode.getCompileUnit() == scriptCompileUnit; - - assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode(); - if (functionNode.isStrictMode()) { - strict = true; - } - - LOG.info("Finalizing types for '" + functionNode.getName() + "'"); - functionNode.accept(new FinalizeTypes(this)); - state.add(State.FINALIZED); - - // print ast and parse if --print-lower-ast and/or --print-lower-parse are selected - debugPrintAST(); - debugPrintParse(); - - if (errors.hasErrors()) { + for (final CompilationPhase phase : sequence) { + LOG.info("Entering compile phase " + phase + " for function '" + functionNode.getName() + "'"); + if (phase.isApplicable(functionNode)) { + if (!phase.apply(this, functionNode)) { //TODO exceptions, error logging return false; } } - - try { - LOG.info("Emitting bytecode for '" + functionNode.getName() + "'"); - final CodeGenerator codegen = new CodeGenerator(this); - functionNode.accept(codegen); - codegen.generateScopeCalls(); - } catch (final VerifyError e) { - if (context._verify_code || context._print_code) { - context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage()); - if (context._dump_on_error) { - e.printStackTrace(context.getErr()); - } - } else { - throw e; - } - } - - state.add(State.EMITTED); - - code = new TreeMap<>(); - for (final CompileUnit compileUnit : compileUnits) { - final ClassEmitter classEmitter = compileUnit.getClassEmitter(); - classEmitter.end(); - - if (!errors.hasErrors()) { - final byte[] bytecode = classEmitter.toByteArray(); - if (bytecode != null) { - code.put(compileUnit.getUnitClassName(), bytecode); - debugDisassemble(); - debugVerify(); - } - } - } - - if (code.isEmpty()) { - return false; - } - - try { - dumpClassFiles(); - } catch (final IOException e) { - throw new RuntimeException(e); - } - - return true; - } finally { - strict = oldStrict; - LOG.info("Done with '" + functionNode.getName() + "'"); } + return true; } /** * Install compiled classes into a given loader - * @param installer that takes the generated classes and puts them in the system * @return root script class - if there are several compile units they will also be installed */ - public Class<?> install(final CodeInstaller installer) { - assert state.contains(State.EMITTED); - assert scriptName != null; - + public Class<?> install() { Class<?> rootClass = null; - for (final Entry<String, byte[]> entry : code.entrySet()) { + for (final Entry<String, byte[]> entry : bytecode.entrySet()) { final String className = entry.getKey(); LOG.info("Installing class " + className); - final byte[] bytecode = entry.getValue(); - final Class<?> clazz = installer.install(Compiler.binaryName(className), bytecode); + final byte[] code = entry.getValue(); + final Class<?> clazz = installer.install(Compiler.binaryName(className), code); if (rootClass == null && firstCompileUnitName().equals(className)) { rootClass = clazz; @@ -486,76 +274,56 @@ public final class Compiler { try { //use reflection to write source and constants table to installed classes - clazz.getField(SOURCE.tag()).set(null, source); - clazz.getField(CONSTANTS.tag()).set(null, constantData.toArray()); + clazz.getField(SOURCE.tag()).set(null, getSource()); + clazz.getField(CONSTANTS.tag()).set(null, getConstantData().toArray()); } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException(e); } } LOG.info("Root class: " + rootClass); - return rootClass; } - /** - * Find a unit that will hold a node of the specified weight. - * - * @param weight Weight of a node - * @return Unit to hold node. - */ - CompileUnit findUnit(final long weight) { - for (final CompileUnit unit : compileUnits) { - if (unit.canHold(weight)) { - unit.addWeight(weight); - return unit; - } - } + Set<CompileUnit> getCompileUnits() { + return compileUnits; + } - return addCompileUnit(weight); + boolean getStrictMode() { + return strict; } - /** - * Generate a uniqueName name. Public as {@link Parser} is using this to - * create symbols in a different package - * - * @param name to base unique name on - * @return unique name - */ - public String uniqueName(final String name) { - return namespace.uniqueName(name); + void setStrictMode(final boolean strict) { + this.strict = strict; } - /** - * Internal function to compute reserved names and base names for class to - * be generated - * - * @return scriptName - */ - private String computeNames() { - // Reserve internally used names. - addReservedNames(); - - if (dumpClass) { - // get source file name and remove ".js" - final String baseName = getSource().getName(); - final int index = baseName.lastIndexOf(".js"); - if (index != -1) { - return baseName.substring(0, index); - } - return baseName; - } + FunctionNode getFunctionNode() { + return functionNode; + } + + ConstantData getConstantData() { + return constantData; + } + + CodeInstaller<Context> getCodeInstaller() { + return installer; + } + + Source getSource() { + return functionNode.getSource(); + } - return namespace.getParent().uniqueName( - DEFAULT_SCRIPT_NAME.tag() + - '$' + - safeSourceName(source) + - (isLazy ? CompilerConstants.LAZY.tag() : "") - ); + void addClass(final String name, final byte[] code) { + bytecode.put(name, code); + } + + Context getContext() { + return this.context; } private static String safeSourceName(final Source source) { String baseName = new File(source.getName()).getName(); + final int index = baseName.lastIndexOf(".js"); if (index != -1) { baseName = baseName.substring(0, index); @@ -564,152 +332,61 @@ public final class Compiler { baseName = baseName.replace('.', '_').replace('-', '_'); final String mangled = NameCodec.encode(baseName); - baseName = mangled != null ? mangled : baseName; - return baseName; - } - - static void verify(final Context context, final byte[] code) { - context.verify(code); - } - - /** - * Fill in the namespace with internally reserved names. - */ - private void addReservedNames() { - namespace.uniqueName(SCOPE.tag()); - namespace.uniqueName(THIS.tag()); + return mangled != null ? mangled : baseName; } - /** - * Get the constant data for this Compiler - * - * @return the constant data - */ - public ConstantData getConstantData() { - return constantData; + private int nextCompileUnitIndex() { + return compileUnits.size() + 1; } - /** - * Get the Context used for Compilation - * @see Context - * @return the context - */ - public Context getContext() { - return context; + String firstCompileUnitName() { + return SCRIPTS_PACKAGE + '/' + scriptName; } - /** - * Get the Source being compiled - * @see Source - * @return the source - */ - public Source getSource() { - return source; + private String nextCompileUnitName() { + return firstCompileUnitName() + '$' + nextCompileUnitIndex(); } - /** - * Get the error manager used for this compiler - * @return the error manager - */ - public ErrorManager getErrors() { - return errors; + CompileUnit addCompileUnit(final long initialWeight) { + return addCompileUnit(nextCompileUnitName(), initialWeight); } - /** - * Get the namespace used for this Compiler - * @see Namespace - * @return the namespace - */ - public Namespace getNamespace() { - return namespace; + CompileUnit addCompileUnit(final String unitClassName) { + return addCompileUnit(unitClassName, 0L); } - /* - * Debugging - */ - - /** - * Print the AST before or after lowering, see --print-ast, --print-lower-ast - */ - private void debugPrintAST() { - assert functionNode != null; - if (context._print_lower_ast && state.contains(State.LOWERED) || - context._print_ast && !state.contains(State.LOWERED)) { - context.getErr().println(new ASTWriter(functionNode)); - } + private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) { + final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight); + compileUnits.add(compileUnit); + LOG.info("Added compile unit " + compileUnit); + return compileUnit; } - /** - * Print the parsed code before or after lowering, see --print-parse, --print-lower-parse - */ - private boolean debugPrintParse() { - if (errors.hasErrors()) { - return false; - } - - assert functionNode != null; + private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) { + final ClassEmitter classEmitter = new ClassEmitter(context, functionNode.getSource().getName(), unitClassName, strict); + final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); - if (context._print_lower_parse && state.contains(State.LOWERED) || - context._print_parse && !state.contains(State.LOWERED)) { - final PrintVisitor pv = new PrintVisitor(); - functionNode.accept(pv); - context.getErr().print(pv); - context.getErr().flush(); - } + classEmitter.begin(); - return true; - } + final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE)); + initMethod.begin(); + initMethod.load(Type.OBJECT, 0); + initMethod.newInstance(jdk.nashorn.internal.scripts.JS$.class); + initMethod.returnVoid(); + initMethod.end(); - private void debugDisassemble() { - assert code != null; - if (context._print_code) { - for (final Map.Entry<String, byte[]> entry : code.entrySet()) { - context.getErr().println("CLASS: " + entry.getKey()); - context.getErr().println(); - ClassEmitter.disassemble(context, entry.getValue()); - context.getErr().println("======"); - } - } + return compileUnit; } - private void debugVerify() { - if (context._verify_code) { - for (final Map.Entry<String, byte[]> entry : code.entrySet()) { - Compiler.verify(context, entry.getValue()); + CompileUnit findUnit(final long weight) { + for (final CompileUnit unit : compileUnits) { + if (unit.canHold(weight)) { + unit.addWeight(weight); + return unit; } } - } - - /** - * Implements the "-d" option - dump class files from script to specified output directory - * - * @throws IOException if classes cannot be written - */ - private void dumpClassFiles() throws IOException { - if (context._dest_dir == null) { - return; - } - - assert code != null; - - for (final Entry<String, byte[]> entry : code.entrySet()) { - final String className = entry.getKey(); - final String fileName = className.replace('.', File.separatorChar) + ".class"; - final int index = fileName.lastIndexOf(File.separatorChar); - if (index != -1) { - final File dir = new File(fileName.substring(0, index)); - if (!dir.exists() && !dir.mkdirs()) { - throw new IOException(ECMAErrors.getMessage("io.error.cant.write", dir.toString())); - } - } - - final byte[] bytecode = entry.getValue(); - final File outFile = new File(context._dest_dir, fileName); - try (final FileOutputStream fos = new FileOutputStream(outFile)) { - fos.write(bytecode); - } - } + return addCompileUnit(weight); } /** @@ -723,14 +400,44 @@ public final class Compiler { } /** - * Convert a binary name to a package/class name. + * Should we use integers for arithmetic operations as well? + * TODO: We currently generate no overflow checks so this is + * disabled + * + * @see #shouldUseIntegers() * - * @param name Binary name. - * @return Package/class name. + * @return true if arithmetic operations should not widen integer + * operands by default. */ - public static String pathName(final String name) { - return name.replace('.', '/'); + static boolean shouldUseIntegerArithmetic() { + return USE_INT_ARITH; } + private static final boolean USE_INT_ARITH; + + static { + USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic"); + assert !USE_INT_ARITH : "Integer arithmetic is not enabled"; + } + static { + if (TIME_COMPILATION) { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + for (final CompilationPhase phase : CompilationPhase.values()) { + final StringBuilder sb = new StringBuilder(); + sb.append(phase); + while (sb.length() < 32) { + sb.append(' '); + } + sb.append(CompilationPhase.getAccumulatedTime(phase)); + sb.append(' '); + sb.append(" ms"); + System.err.println(sb.toString()); //Context err is gone by shutdown TODO + } + } + }); + } + } } |