/** * 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.IOException; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import com.github.anba.es6draft.Script; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.ModuleEnvironmentRecord; import com.github.anba.es6draft.runtime.modules.MalformedNameException; import com.github.anba.es6draft.runtime.modules.ResolutionException; import com.github.anba.es6draft.runtime.modules.SourceTextModuleRecord; /** * Classes for function and script code bootstrapping. */ public final class RuntimeInfo { private RuntimeInfo() { } public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) { assert "methodInfo".equals(name); // Empty object as a placeholder. return new ConstantCallSite(MethodHandles.constant(Object.class, new Object())); } /** * Returns a new {@link Function} object. * * @param methodInfo * the method info object * @param functionName * the function name * @param functionFlags * the function flags * @param expectedArgumentCount * the number of expected arguments * @param parameters * the parameter names or {@code null} * @param source * the encoded source string * @param bodySourceStart * the body source start index * @param handle * the method handle * @param callMethod * the call method handle * @param constructMethod * the construct method handle * @return the new function object */ public static Function newFunction(Object methodInfo, String functionName, int functionFlags, int expectedArgumentCount, String[] parameters, String source, int bodySourceStart, MethodHandle handle, MethodHandle callMethod, MethodHandle constructMethod) { return new CompiledFunction(methodInfo, functionName, functionFlags, expectedArgumentCount, parameters, source, bodySourceStart, handle, callMethod, constructMethod, null); } /** * Returns a new {@link Function} object. * * @param methodInfo * the method info object * @param functionName * the function name * @param functionFlags * the function flags * @param expectedArgumentCount * the number of expected arguments * @param parameters * the parameter names or {@code null} * @param source * the encoded source string * @param bodySourceStart * the body source start index * @param handle * the method handle * @param callMethod * the call method handle * @param constructMethod * the construct method handle * @param debugInfo * the debug info method handle * @return the new function object */ public static Function newFunction(Object methodInfo, String functionName, int functionFlags, int expectedArgumentCount, String[] parameters, String source, int bodySourceStart, MethodHandle handle, MethodHandle callMethod, MethodHandle constructMethod, MethodHandle debugInfo) { return new CompiledFunction(methodInfo, functionName, functionFlags, expectedArgumentCount, parameters, source, bodySourceStart, handle, callMethod, constructMethod, debugInfo); } /** * Returns a new {@link ModuleBody} object. * * @param sourceName * the source name * @param sourcePath * the source path * @param initialization * the initialization method handle * @param handle * the code method handle * @return the new module object */ public static ModuleBody newModuleBody(String sourceName, String sourcePath, MethodHandle initialization, MethodHandle handle) { return new CompiledModuleBody(sourceName, sourcePath, initialization, handle, null); } /** * Returns a new {@link ModuleBody} object. * * @param sourceName * the source name * @param sourcePath * the source path * @param initialization * the initialization method handle * @param handle * the code method handle * @param debugInfo * the debug info method handle * @return the new module object */ public static ModuleBody newModuleBody(String sourceName, String sourcePath, MethodHandle initialization, MethodHandle handle, MethodHandle debugInfo) { return new CompiledModuleBody(sourceName, sourcePath, initialization, handle, debugInfo); } /** * Returns a new {@link ScriptBody} object. * * @param sourceName * the source name * @param sourcePath * the source path * @param evaluation * the script evaluation method handle * @return the new script object */ public static ScriptBody newScriptBody(String sourceName, String sourcePath, MethodHandle evaluation) { return new CompiledScriptBody(sourceName, sourcePath, evaluation, null); } /** * Returns a new {@link ScriptBody} object. * * @param sourceName * the source name * @param sourcePath * the source path * @param evaluation * the script evaluation method handle * @param debugInfo * the debug info method handle * @return the new script object */ public static ScriptBody newScriptBody(String sourceName, String sourcePath, MethodHandle evaluation, MethodHandle debugInfo) { return new CompiledScriptBody(sourceName, sourcePath, evaluation, debugInfo); } /** * Compiled source object. */ public interface SourceObject { /** * Returns the source information for this object. * * @return the source object */ Source toSource(); /** * Returns the debug information or {@code null} if not available. * * @return the debug information */ DebugInfo debugInfo(); } /** * Compiled script body information. */ public interface ScriptBody extends SourceObject { /** * Evaluates the script. * * @param cx * the execution context * @param script * the script * @return the evaluation result */ Object evaluate(ExecutionContext cx, Script script); } @SuppressWarnings("unchecked") private static <E extends Throwable> E rethrow(Throwable e) throws E { throw (E) e; } private static final class CompiledScriptBody implements ScriptBody { private final String sourceName; private final String sourceFile; private final MethodHandle evaluation; private final MethodHandle debugInfo; CompiledScriptBody(String sourceName, String sourceFile, MethodHandle evaluation, MethodHandle debugInfo) { this.sourceName = sourceName; this.sourceFile = sourceFile; this.evaluation = evaluation; this.debugInfo = debugInfo; } @Override public Source toSource() { return new Source(sourceFile, sourceName, 1); } @Override public Object evaluate(ExecutionContext cx, Script script) { try { return evaluation.invokeExact(cx, script); } catch (Throwable e) { throw RuntimeInfo.<RuntimeException> rethrow(e); } } @Override public DebugInfo debugInfo() { if (debugInfo != null) { try { return (DebugInfo) debugInfo.invokeExact(); } catch (Throwable e) { throw RuntimeInfo.<RuntimeException> rethrow(e); } } return null; } } /** * Compiled module information. */ public interface ModuleBody extends SourceObject { /** * Performs 15.2.1.16.4 ModuleDeclarationInstantiation( ) Concrete Method. * * @param cx * the execution context * @param module * the module record * @param env * the lexical environment * @throws IOException * if there was any I/O error * @throws ResolutionException * if any export or import binding cannot be resolved * @throws MalformedNameException * if any module specifier cannot be normalized */ void moduleDeclarationInstantiation(ExecutionContext cx, SourceTextModuleRecord module, LexicalEnvironment<ModuleEnvironmentRecord> env) throws IOException, ResolutionException, MalformedNameException; /** * Performs 15.2.1.16.5 ModuleEvaluation() Concrete Method. * * @param cx * the execution context * @return the evaluation result */ Object evaluate(ExecutionContext cx); } private static final class CompiledModuleBody implements ModuleBody { private final String sourceName; private final String sourceFile; private final MethodHandle initialization; private final MethodHandle handle; private final MethodHandle debugInfo; CompiledModuleBody(String sourceName, String sourceFile, MethodHandle initialization, MethodHandle handle, MethodHandle debugInfo) { this.sourceName = sourceName; this.sourceFile = sourceFile; this.initialization = initialization; this.handle = handle; this.debugInfo = debugInfo; } @Override public Source toSource() { return new Source(sourceFile, sourceName, 1); } @Override public void moduleDeclarationInstantiation(ExecutionContext cx, SourceTextModuleRecord module, LexicalEnvironment<ModuleEnvironmentRecord> env) throws IOException, ResolutionException, MalformedNameException { try { initialization.invokeExact(cx, module, env); } catch (Throwable e) { throw RuntimeInfo.<RuntimeException> rethrow(e); } } @Override public Object evaluate(ExecutionContext cx) { try { return handle.invokeExact(cx); } catch (Throwable e) { throw RuntimeInfo.<RuntimeException> rethrow(e); } } @Override public DebugInfo debugInfo() { if (debugInfo != null) { try { return (DebugInfo) debugInfo.invokeExact(); } catch (Throwable e) { throw RuntimeInfo.<RuntimeException> rethrow(e); } } return null; } } /** * Function flags enumeration. */ public enum FunctionFlags { /** * Flag for strict-mode functions. */ Strict(0x0001), /** * Flag for implicit strict functions. */ ImplicitStrict(0x0002), /** * Flag for generator functions. */ Generator(0x0004), /** * Flag for async functions. */ Async(0x0008), /** * Flag for arrow functions. */ Arrow(0x0010), /** * Flag for declarative functions. */ Declaration(0x0020), /** * Flag for expression functions. */ Expression(0x0040), /** * Flag for functions with concise, braceless body. */ ConciseBody(0x0080), /** * Flag for method definitions. */ Method(0x0100), /** * Flag for static method definitions. */ Static(0x0200), /** * Flag for legacy generator functions. */ LegacyGenerator(0x0400), /** * Flag for legacy functions. */ Legacy(0x0800), /** * Flag for functions which create a named scope binding. */ ScopedName(0x1000), /** * Flag for functions with super-binding. */ Super(0x2000), /** * Unused. */ Unused(0x4000), /** * Flag for tail-call functions. */ TailCall(0x8000), /** * Flag for native functions. */ Native(0x10000), /** * Flag for functions with direct eval calls. */ Eval(0x20000), /** * Flag for tail-call functions. */ TailConstruct(0x40000), /** * Flag for class functions. */ Class(0x80000), /** * Flag for mapped arguments. */ MappedArguments(0x100000), ; private final int value; private FunctionFlags(int value) { this.value = value; } /** * Returns the function flag bitmask. * * @return the function flag bitmask */ public int getValue() { return value; } /** * Returns {@code true} if this function flag is set in <var>bitmask</var>. * * @param bitmask * the bitmask * @return {@code true} if the function flag is set */ public boolean isSet(int bitmask) { return (value & bitmask) != 0; } } /** * Compiled function source information */ public static final class FunctionSource { private String source; private final int bodyStart; private boolean compressed = true; FunctionSource(String source, int bodyStart) { this.source = source; this.bodyStart = bodyStart; } /** * Returns the function source string. * * @return the function source string */ public synchronized String sourceString() { if (compressed) { try { source = SourceCompressor.decompress(source); } catch (IOException e) { throw new RuntimeException(e); } compressed = false; } return source; } /** * Returns the function parameters source string. * * @return the function parameters source string */ public String parameters() { return sourceString().substring(0, bodyStart); } /** * Returns the function body source string. * * @return the function body source string */ public String body() { return sourceString().substring(bodyStart); } } /** * Compiled function information */ public interface Function { /** * Returns the method info object. * * @return the method info object */ Object methodInfo(); /** * Returns the function's name. * * @return the function name */ String functionName(); /** * Returns {@code true} for strict mode functions. * <p> * Convenience method for {@code is(FunctionFlags.Strict)}. * * @return {@code true} if a strict mode function */ boolean isStrict(); /** * Returns {@code true} for generator function. * <p> * Convenience method for {@code is(FunctionFlags.Generator)}. * * @return {@code true} if the function is a generator function */ boolean isGenerator(); /** * Returns {@code true} for async function. * <p> * Convenience method for {@code is(FunctionFlags.Async)}. * * @return {@code true} if the function is an async function */ boolean isAsync(); /** * Returns {@code true} if the function flag is set for this function. * * @param flag * the function flag * @return {@code true} if the function flag is set */ boolean is(FunctionFlags flag); /** * Returns the function flags bitmask. * * @return the function flags bitmask * @see FunctionFlags */ int functionFlags(); /** * Returns the number of expected arguments of this function. * * @return the number of expected arguments */ int expectedArgumentCount(); /** * Returns the parameter names of this function. * * @return the formal parameter names or {@code null} */ String[] parameters(); /** * Returns the compressed source string. * * @return the compressed source string */ FunctionSource source(); /** * (? extends FunctionObject, ExecutionContext, Object, Object[]) {@literal ->} Object. * * @return the method handle for normal calls */ MethodHandle callMethod(); /** * (? extends FunctionObject, ExecutionContext, Constructor, Object[]) {@literal ->} Object. * * @return the method handle for construct calls */ MethodHandle constructMethod(); /** * (ExecutionContext, ...?) {@literal ->} Object * * @return the method handle for the function body */ MethodHandle handle(); /** * Returns the debug information or {@code null} if not available. * * @return the debug information */ DebugInfo debugInfo(); } private static final class CompiledFunction implements Function { private final Object methodInfo; private final String functionName; private final int functionFlags; private final int expectedArgumentCount; private final String[] parameters; private final FunctionSource source; private final MethodHandle handle; private final MethodHandle callMethod; private final MethodHandle constructMethod; private final MethodHandle debugInfo; CompiledFunction(Object methodInfo, String functionName, int functionFlags, int expectedArgumentCount, String[] parameters, String source, int bodySourceStart, MethodHandle handle, MethodHandle callMethod, MethodHandle constructMethod, MethodHandle debugInfo) { this.methodInfo = methodInfo; this.functionName = functionName; this.functionFlags = functionFlags; this.expectedArgumentCount = expectedArgumentCount; this.parameters = parameters; this.source = source != null ? new FunctionSource(source, bodySourceStart) : null; this.handle = handle; this.callMethod = callMethod; this.constructMethod = constructMethod; this.debugInfo = debugInfo; } @Override public Object methodInfo() { return methodInfo; } @Override public String functionName() { return functionName; } @Override public boolean isStrict() { return FunctionFlags.Strict.isSet(functionFlags); } @Override public boolean isGenerator() { return FunctionFlags.Generator.isSet(functionFlags); } @Override public boolean isAsync() { return FunctionFlags.Async.isSet(functionFlags); } @Override public boolean is(FunctionFlags flag) { return flag.isSet(functionFlags); } @Override public int functionFlags() { return functionFlags; } @Override public int expectedArgumentCount() { return expectedArgumentCount; } @Override public String[] parameters() { return parameters; } @Override public FunctionSource source() { return source; } @Override public MethodHandle handle() { return handle; } @Override public MethodHandle callMethod() { return callMethod; } @Override public MethodHandle constructMethod() { return constructMethod; } @Override public DebugInfo debugInfo() { if (debugInfo != null) { try { return (DebugInfo) debugInfo.invokeExact(); } catch (Throwable e) { throw RuntimeInfo.<RuntimeException> rethrow(e); } } return null; } } }