private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) { String baseName = new File(source.getName()).getName(); final int index = baseName.lastIndexOf(".js"); if (index != -1) { baseName = baseName.substring(0, index); } baseName = baseName.replace('.', '_').replace('-', '_'); if (!env._loader_per_compile) { baseName = baseName + installer.getUniqueScriptId(); } // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char. // While ASM accepts such escapes for method names, field names, it enforces Java identifier // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_' // rather than safe encoding using '\'. final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName); return mangled != null ? mangled : baseName; }
/** * Creates a compiler for an on-demand compilation job. * * @param installer code installer * @param source source to compile * @param isStrict is this a strict compilation * @param compiledFunction compiled function, if any * @param types parameter and return value type information, if any is known * @param invalidatedProgramPoints invalidated program points for recompilation * @param typeInformationFile descriptor of the location where type information is persisted * @param continuationEntryPoints continuation entry points for restof method * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator} * @return a new compiler */ public static Compiler forOnDemandCompilation( final CodeInstaller installer, final Source source, final boolean isStrict, final RecompilableScriptFunctionData compiledFunction, final TypeMap types, final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile, final int[] continuationEntryPoints, final ScriptObject runtimeScope) { final Context context = installer.getContext(); return new Compiler(context, installer, source, context.getErrorManager(), isStrict, true, compiledFunction, types, invalidatedProgramPoints, typeInformationFile, continuationEntryPoints, runtimeScope); }
/** * Constructor * * @param context context * @param env script environment * @param installer code installer * @param source source to compile * @param errors error manager * @param isStrict is this a strict compilation * @param isOnDemand is this an on demand compilation * @param compiledFunction compiled function, if any * @param types parameter and return value type information, if any is known * @param invalidatedProgramPoints invalidated program points for recompilation * @param typeInformationFile descriptor of the location where type information is persisted * @param continuationEntryPoints continuation entry points for restof method * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator} */ @SuppressWarnings("unused") public Compiler( final Context context, final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source, final ErrorManager errors, final boolean isStrict, final boolean isOnDemand, final RecompilableScriptFunctionData compiledFunction, final TypeMap types, final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile, final int[] continuationEntryPoints, final ScriptObject runtimeScope) { this.context = context; this.env = env; this.installer = installer; this.constantData = new ConstantData(); this.compileUnits = CompileUnit.createCompileUnitSet(); this.bytecode = new LinkedHashMap<>(); this.log = initLogger(context); this.source = source; this.errors = errors; this.sourceName = FunctionNode.getSourceName(source); this.onDemand = isOnDemand; this.compiledFunction = compiledFunction; this.types = types; this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints; this.typeInformationFile = typeInformationFile; this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone(); this.typeEvaluator = new TypeEvaluator(this, runtimeScope); this.firstCompileUnitName = firstCompileUnitName(); this.strict = isStrict; this.optimistic = env._optimistic_types; }
/** * Convenience constructor for non on-demand compiler instances. */ private Compiler( final Context context, final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict) { this(context, installer, source, errors, isStrict, false, null, null, null, null, null, null); }
private Compiler( final Context context, final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict, final boolean isOnDemand, final RecompilableScriptFunctionData compiledFunction, final TypeMap types, final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile, final int[] continuationEntryPoints, final ScriptObject runtimeScope) { this.context = context; this.env = context.getEnv(); this.installer = installer; this.constantData = new ConstantData(); this.compileUnits = CompileUnit.createCompileUnitSet(); this.bytecode = new LinkedHashMap<>(); this.log = initLogger(context); this.source = source; this.errors = errors; this.sourceName = FunctionNode.getSourceName(source); this.onDemand = isOnDemand; this.compiledFunction = compiledFunction; this.types = types; this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<>() : invalidatedProgramPoints; this.typeInformationFile = typeInformationFile; this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone(); this.typeEvaluator = new TypeEvaluator(this, runtimeScope); this.firstCompileUnitName = firstCompileUnitName(); this.strict = isStrict; this.optimistic = env._optimistic_types; }
private Compiler( final Context context, final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict, final boolean isOnDemand, final RecompilableScriptFunctionData compiledFunction, final TypeMap types, final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile, final int[] continuationEntryPoints, final ScriptObject runtimeScope) { this.context = context; this.env = context.getEnv(); this.installer = installer; this.constantData = new ConstantData(); this.compileUnits = CompileUnit.createCompileUnitSet(); this.bytecode = new LinkedHashMap<>(); this.log = initLogger(context); this.source = source; this.errors = errors; this.sourceName = FunctionNode.getSourceName(source); this.onDemand = isOnDemand; this.compiledFunction = compiledFunction; this.types = types; this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints; this.typeInformationFile = typeInformationFile; this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone(); this.typeEvaluator = new TypeEvaluator(this, runtimeScope); this.firstCompileUnitName = firstCompileUnitName(); this.strict = isStrict; this.optimistic = env._optimistic_types; }
/** * Constructor * * @param env script environment * @param installer code installer * @param sequence {@link Compiler.CompilationSequence} of {@link CompilationPhase}s to apply as this compilation * @param strict should this compilation use strict mode semantics */ //TODO support an array of FunctionNodes for batch lazy compilation Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final CompilationSequence sequence, final boolean strict) { this.env = env; this.sequence = sequence; this.installer = installer; this.constantData = new ConstantData(); this.compileUnits = new TreeSet<>(); this.bytecode = new LinkedHashMap<>(); }
/** * Constructor * * @param env script environment * @param installer code installer * @param sequence {@link Compiler.CompilationSequence} of {@link CompilationPhase}s to apply as this compilation * @param strict should this compilation use strict mode semantics */ //TODO support an array of FunctionNodes for batch lazy compilation Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final CompilationSequence sequence, final boolean strict) { this.env = env; this.sequence = sequence; this.installer = installer; this.constantData = new ConstantData(); this.compileUnits = new HashSet<>(); this.bytecode = new HashMap<>(); }
CodeInstaller<ScriptEnvironment> getCodeInstaller() { return installer; }
@Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final DebugLogger log = compiler.getLogger(); final Map<String, Class<?>> installedClasses = new LinkedHashMap<>(); boolean first = true; Class<?> rootClass = null; long length = 0L; final CodeInstaller origCodeInstaller = compiler.getCodeInstaller(); final Map<String, byte[]> bytecode = compiler.getBytecode(); final CodeInstaller codeInstaller = bytecode.size() > 1 ? origCodeInstaller.getMultiClassCodeInstaller() : origCodeInstaller; for (final Entry<String, byte[]> entry : bytecode.entrySet()) { final String className = entry.getKey(); //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName(); final byte[] code = entry.getValue(); length += code.length; final Class<?> clazz = codeInstaller.install(className, code); if (first) { rootClass = clazz; first = false; } installedClasses.put(className, clazz); } if (rootClass == null) { throw new CompilationException("Internal compiler error: root class not found!"); } final Object[] constants = compiler.getConstantData().toArray(); codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants); // initialize transient fields on recompilable script function data for (final Object constant: constants) { if (constant instanceof RecompilableScriptFunctionData) { ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller); } } // initialize function in the compile units for (final CompileUnit unit : compiler.getCompileUnits()) { if (!unit.isUsed()) { continue; } unit.setCode(installedClasses.get(unit.getUnitClassName())); unit.initializeFunctionsCode(); } if (log.isEnabled()) { final StringBuilder sb = new StringBuilder(); sb.append("Installed class '"). append(rootClass.getSimpleName()). append('\''). append(" ["). append(rootClass.getName()). append(", size="). append(length). append(" bytes, "). append(compiler.getCompileUnits().size()). append(" compile unit(s)]"); log.fine(sb.toString()); } return fn.setRootClass(null, rootClass); }
CodeInstaller getCodeInstaller() { return installer; }
@Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final DebugLogger log = compiler.getLogger(); final Map<String, Class<?>> installedClasses = new LinkedHashMap<>(); boolean first = true; Class<?> rootClass = null; long length = 0L; final CodeInstaller codeInstaller = compiler.getCodeInstaller(); final Map<String, byte[]> bytecode = compiler.getBytecode(); for (final Entry<String, byte[]> entry : bytecode.entrySet()) { final String className = entry.getKey(); //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName(); final byte[] code = entry.getValue(); length += code.length; final Class<?> clazz = codeInstaller.install(className, code); if (first) { rootClass = clazz; first = false; } installedClasses.put(className, clazz); } if (rootClass == null) { throw new CompilationException("Internal compiler error: root class not found!"); } final Object[] constants = compiler.getConstantData().toArray(); codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants); // initialize transient fields on recompilable script function data for (final Object constant: constants) { if (constant instanceof RecompilableScriptFunctionData) { ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller); } } // initialize function in the compile units for (final CompileUnit unit : compiler.getCompileUnits()) { if (!unit.isUsed()) { continue; } unit.setCode(installedClasses.get(unit.getUnitClassName())); unit.initializeFunctionsCode(); } if (log.isEnabled()) { final StringBuilder sb = new StringBuilder(); sb.append("Installed class '"). append(rootClass.getSimpleName()). append('\''). append(" ["). append(rootClass.getName()). append(", size="). append(length). append(" bytes, "). append(compiler.getCompileUnits().size()). append(" compile unit(s)]"); log.fine(sb.toString()); } return fn.setRootClass(null, rootClass); }
/** * Constructor. */ Lower(final CodeInstaller<?> installer) { super(new BlockLexicalContext() { @Override public List<Statement> popStatements() { final List<Statement> newStatements = new ArrayList<>(); boolean terminated = false; final List<Statement> statements = super.popStatements(); for (final Statement statement : statements) { if (!terminated) { newStatements.add(statement); if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why? terminated = true; } } else { statement.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public boolean enterVarNode(final VarNode varNode) { newStatements.add(varNode.setInit(null)); return false; } }); } } return newStatements; } @Override protected Block afterSetStatements(final Block block) { final List<Statement> stmts = block.getStatements(); for(final ListIterator<Statement> li = stmts.listIterator(stmts.size()); li.hasPrevious();) { final Statement stmt = li.previous(); // popStatements() guarantees that the only thing after a terminal statement are uninitialized // VarNodes. We skip past those, and set the terminal state of the block to the value of the // terminal state of the first statement that is not an uninitialized VarNode. if(!(stmt instanceof VarNode && ((VarNode)stmt).getInit() == null)) { return block.setIsTerminal(this, stmt.isTerminal()); } } return block.setIsTerminal(this, false); } }); this.installer = installer; }
/** * Constructor * * @param context context * @param env script environment * @param installer code installer * @param source source to compile * @param errors error manager * @param isStrict is this a strict compilation */ public Compiler( final Context context, final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source, final ErrorManager errors, final boolean isStrict) { this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null); }
/** * Creates a new compiler instance for initial compilation of a script. * * @param installer code installer * @param source source to compile * @param errors error manager * @param isStrict is this a strict compilation * @return a new compiler */ public static Compiler forInitialCompilation( final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict) { return new Compiler(installer.getContext(), installer, source, errors, isStrict); }