/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.internal; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.EnumSet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import com.github.anba.es6draft.Module; import com.github.anba.es6draft.Script; import com.github.anba.es6draft.ast.AsyncFunctionDefinition; import com.github.anba.es6draft.ast.AsyncGeneratorDefinition; import com.github.anba.es6draft.ast.FunctionDefinition; import com.github.anba.es6draft.ast.GeneratorDefinition; import com.github.anba.es6draft.compiler.CompilationException; import com.github.anba.es6draft.compiler.CompiledFunction; import com.github.anba.es6draft.compiler.CompiledModule; import com.github.anba.es6draft.compiler.CompiledScript; import com.github.anba.es6draft.compiler.Compiler; import com.github.anba.es6draft.interpreter.Interpreter; import com.github.anba.es6draft.parser.Parser; import com.github.anba.es6draft.parser.ParserException; import com.github.anba.es6draft.runtime.modules.SourceTextModuleRecord; import com.github.anba.es6draft.runtime.objects.Eval; import com.github.anba.es6draft.runtime.objects.FunctionConstructor; import com.github.anba.es6draft.runtime.objects.iteration.GeneratorFunctionConstructor; /** * */ public final class ScriptLoader { private final RuntimeContext context; private final AtomicInteger scriptCounter = new AtomicInteger(0); private final AtomicInteger moduleCounter = new AtomicInteger(0); private final AtomicInteger evalCounter = new AtomicInteger(0); private final AtomicInteger functionCounter = new AtomicInteger(0); /** * Next class name for eval scripts. * * @return the next class name for eval scripts * @see Eval */ private String nextEvalName() { return "#Eval_" + evalCounter.incrementAndGet(); } /** * Next class name for functions. * * @return the next class name for functions * @see FunctionConstructor * @see GeneratorFunctionConstructor */ private String nextFunctionName() { return "#Function_" + functionCounter.incrementAndGet(); } /** * Next class name for modules. * * @return the next class name for modules */ private String nextModuleName() { return "#Module_" + moduleCounter.incrementAndGet(); } /** * Next class name for scripts. * * @return the next class name for scripts */ private String nextScriptName() { return "#Script_" + scriptCounter.incrementAndGet(); } /** * Constructs a new script loader. * * @param context * the runtime context */ public ScriptLoader(RuntimeContext context) { this.context = context; } /** * Parses the javascript script source. * * @param source * the script source descriptor * @param sourceCode * the source code * @return the parsed script node * @throws ParserException * if the source contains any syntax errors */ public com.github.anba.es6draft.ast.Script parseScript(Source source, String sourceCode) throws ParserException { Parser parser = new Parser(context, source); return parser.parseScript(sourceCode); } /** * Parses the javascript module source. * * @param source * the script source descriptor * @param sourceCode * the source code * @return the parsed script node * @throws ParserException * if the source contains any syntax errors */ public com.github.anba.es6draft.ast.Module parseModule(Source source, String sourceCode) throws ParserException { Parser parser = new Parser(context, source); return parser.parseModule(sourceCode); } /** * Parses and compiles the javascript eval-script. * * @param source * the script source descriptor * @param sourceCode * the source code * @param evalOptions * the eval parser options * @return the compiled script * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public Script evalScript(Source source, String sourceCode, EnumSet<Parser.Option> evalOptions) throws ParserException, CompilationException { Parser parser = new Parser(source, context.getOptions(), evalOptions); com.github.anba.es6draft.ast.Script parsedScript = parser.parseScript(sourceCode); if (parsedScript.getStatements().isEmpty()) { return null; } return load(parsedScript, nextEvalName()); } /** * Parses and compiles the javascript function. * * @param source * the script source descriptor * @param formals * the formal parameters * @param bodyText * the function body * @return the compiled function * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public CompiledFunction function(Source source, String formals, String bodyText) throws ParserException, CompilationException { Parser parser = new Parser(context, source); FunctionDefinition functionDef = parser.parseFunction(formals, bodyText); return compile(functionDef, nextFunctionName()); } /** * Parses and compiles the javascript generator function. * * @param source * the script source descriptor * @param formals * the formal parameters * @param bodyText * the generator function body * @return the compiled generator function * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public CompiledFunction generator(Source source, String formals, String bodyText) throws ParserException, CompilationException { Parser parser = new Parser(context, source); GeneratorDefinition generatorDef = parser.parseGenerator(formals, bodyText); return compile(generatorDef, nextFunctionName()); } /** * Parses and compiles the javascript async function. * * @param source * the script source descriptor * @param formals * the formal parameters * @param bodyText * the async function body * @return the compiled async function * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public CompiledFunction asyncFunction(Source source, String formals, String bodyText) throws ParserException, CompilationException { Parser parser = new Parser(context, source); AsyncFunctionDefinition asyncDef = parser.parseAsyncFunction(formals, bodyText); return compile(asyncDef, nextFunctionName()); } /** * Parses and compiles the javascript async generator. * * @param source * the script source descriptor * @param formals * the formal parameters * @param bodyText * the async generator body * @return the compiled async generator * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public CompiledFunction asyncGenerator(Source source, String formals, String bodyText) throws ParserException, CompilationException { Parser parser = new Parser(context, source); AsyncGeneratorDefinition asyncDef = parser.parseAsyncGenerator(formals, bodyText); return compile(asyncDef, nextFunctionName()); } /** * Parses and compiles the javascript file. * * @param source * the script source descriptor * @param file * the script file URL * @return the compiled script * @throws IOException * if there was any I/O error * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public Script script(Source source, URL file) throws IOException, ParserException, CompilationException { return script(source, newReader(file.openStream())); } /** * Parses and compiles the javascript file. * * @param source * the script source descriptor * @param stream * the source * @return the compiled script * @throws IOException * if there was any I/O error * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public Script script(Source source, InputStream stream) throws IOException, ParserException, CompilationException { return script(source, newReader(stream)); } /** * Parses and compiles the javascript file. * * @param source * the script source descriptor * @param reader * the source * @return the compiled script * @throws IOException * if there was any I/O error * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public Script script(Source source, Reader reader) throws IOException, ParserException, CompilationException { return script(source, readFully(reader)); } /** * Parses and compiles the javascript file. * * @param source * the script source descriptor * @param file * the script file path * @return the compiled script * @throws IOException * if there was any I/O error * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public Script script(Source source, Path file) throws IOException, ParserException, CompilationException { if (!file.isAbsolute()) { throw new IllegalArgumentException(String.format("'%s' is not an absolute path", file)); } // Don't interpret script files to get better stack trace information. com.github.anba.es6draft.ast.Script parsedScript = parseScript(source, readFully(file)); return compile(parsedScript, nextScriptName()); } /** * Parses and compiles the javascript file. * * @param source * the script source descriptor * @param sourceCode * the source code * @return the compiled script * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public Script script(Source source, String sourceCode) throws ParserException, CompilationException { com.github.anba.es6draft.ast.Script parsedScript = parseScript(source, sourceCode); return load(parsedScript, nextScriptName()); } /** * Returns an executable {@link Script} object for the {@link com.github.anba.es6draft.ast.Script Script} AST-node. * * @param parsedScript * the script node * @param className * the class name * @return the script object */ public Script load(com.github.anba.es6draft.ast.Script parsedScript, String className) throws CompilationException { Script script = Interpreter.script(parsedScript); if (script == null) { script = compile(parsedScript, className); } return script; } /** * Returns an executable {@link Module} object for the {@link com.github.anba.es6draft.ast.Module Module} AST-node. * * @param parsedModule * the module node * @param moduleRecord * the module record * @return the module object */ public Module load(com.github.anba.es6draft.ast.Module parsedModule, SourceTextModuleRecord moduleRecord) throws CompilationException { return compile(parsedModule, moduleRecord, nextModuleName()); } /** * Returns an executable {@link Module} object for the {@link com.github.anba.es6draft.ast.Module Module} AST-node. * * @param parsedModule * the module node * @param moduleRecord * the module record * @param className * the class name * @return the module object */ public Module load(com.github.anba.es6draft.ast.Module parsedModule, SourceTextModuleRecord moduleRecord, String className) throws CompilationException { return compile(parsedModule, moduleRecord, className); } /** * Compiles the {@link com.github.anba.es6draft.ast.Script Script} AST-node to an executable {@link CompiledScript} * object. * * @param parsedScript * the script node * @param className * the class name * @return the script object */ public CompiledScript compile(com.github.anba.es6draft.ast.Script parsedScript, String className) throws CompilationException { try (CloseableExecutor t = executor()) { Compiler compiler = new Compiler(context, t.executor()); return compiler.compile(parsedScript, className); } } /** * Compiles the {@link com.github.anba.es6draft.ast.Module Module} AST-node to an executable {@link CompiledModule} * object. * * @param parsedModule * the module node * @param moduleRecord * the module record * @param className * the class name * @return the module object */ public CompiledModule compile(com.github.anba.es6draft.ast.Module parsedModule, SourceTextModuleRecord moduleRecord, String className) throws CompilationException { try (CloseableExecutor t = executor()) { Compiler compiler = new Compiler(context, t.executor()); return compiler.compile(parsedModule, moduleRecord, className); } } /** * Compiles the {@link FunctionDefinition} AST-node to a {@link CompiledFunction} object. * * @param function * the function node * @param className * the class name * @return the compiled function */ public CompiledFunction compile(FunctionDefinition function, String className) throws CompilationException { try (CloseableExecutor t = executor()) { Compiler compiler = new Compiler(context, t.executor()); return compiler.compile(function, className); } } /** * Compiles the {@link GeneratorDefinition} AST-node to a {@link CompiledFunction} object. * * @param generator * the generator node * @param className * the class name * @return the compiled generator function */ public CompiledFunction compile(GeneratorDefinition generator, String className) throws CompilationException { try (CloseableExecutor t = executor()) { Compiler compiler = new Compiler(context, t.executor()); return compiler.compile(generator, className); } } /** * Compiles the {@link AsyncFunctionDefinition} AST-node to a {@link CompiledFunction} object. * * @param asyncFunction * the async function node * @param className * the class name * @return the compiled async function */ public CompiledFunction compile(AsyncFunctionDefinition asyncFunction, String className) throws CompilationException { try (CloseableExecutor t = executor()) { Compiler compiler = new Compiler(context, t.executor()); return compiler.compile(asyncFunction, className); } } /** * Compiles the {@link AsyncGeneratorDefinition} AST-node to a {@link CompiledFunction} object. * * @param asyncGenerator * the async generator node * @param className * the class name * @return the compiled async generator */ public CompiledFunction compile(AsyncGeneratorDefinition asyncGenerator, String className) throws CompilationException { try (CloseableExecutor t = executor()) { Compiler compiler = new Compiler(context, t.executor()); return compiler.compile(asyncGenerator, className); } } private CloseableExecutor executor() { if (context.getExecutor().isShutdown()) { return new TempExecutor(); } return new ContextExecutor(); } interface CloseableExecutor extends AutoCloseable { ExecutorService executor(); @Override void close(); } static final class TempExecutor implements CloseableExecutor { final ExecutorService executor = Executors.newFixedThreadPool(2); @Override public ExecutorService executor() { return executor; } @Override public void close() { executor.shutdown(); } } final class ContextExecutor implements CloseableExecutor { @Override public ExecutorService executor() { return context.getExecutor(); } @Override public void close() { // empty } } private static Reader newReader(InputStream stream) { return new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); } private static String readFully(Reader reader) throws IOException { try { StringBuilder sb = new StringBuilder(4096); char cbuf[] = new char[4096]; for (int len; (len = reader.read(cbuf)) != -1;) { sb.append(cbuf, 0, len); } return sb.toString(); } finally { reader.close(); } } private static String readFully(Path p) throws IOException { return new String(Files.readAllBytes(p), StandardCharsets.UTF_8); } }