/** * 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.compiler; import static com.github.anba.es6draft.semantics.StaticSemantics.IsStrict; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import com.github.anba.es6draft.ast.AsyncFunctionDeclaration; import com.github.anba.es6draft.ast.AsyncGeneratorDeclaration; import com.github.anba.es6draft.ast.Declaration; import com.github.anba.es6draft.ast.FunctionDeclaration; import com.github.anba.es6draft.ast.GeneratorDeclaration; import com.github.anba.es6draft.ast.HoistableDeclaration; import com.github.anba.es6draft.ast.LegacyGeneratorDeclaration; import com.github.anba.es6draft.ast.Node; import com.github.anba.es6draft.ast.VariableDeclaration; import com.github.anba.es6draft.ast.scope.Name; import com.github.anba.es6draft.compiler.assembler.Jump; import com.github.anba.es6draft.compiler.assembler.MethodName; import com.github.anba.es6draft.compiler.assembler.Type; import com.github.anba.es6draft.compiler.assembler.Variable; import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord; import com.github.anba.es6draft.runtime.EnvironmentRecord; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.GlobalEnvironmentRecord; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.internal.CompatibilityOption; import com.github.anba.es6draft.runtime.internal.RuntimeInfo; import com.github.anba.es6draft.runtime.internal.ScriptRuntime; import com.github.anba.es6draft.runtime.types.builtins.FunctionObject; /** * Base class for Binding Instantiation generators */ class DeclarationBindingInstantiationGenerator { private static final class Methods { // class: ExecutionContext static final MethodName ExecutionContext_getLexicalEnvironment = MethodName.findVirtual( Types.ExecutionContext, "getLexicalEnvironment", Type.methodType(Types.LexicalEnvironment)); static final MethodName ExecutionContext_getVariableEnvironment = MethodName.findVirtual( Types.ExecutionContext, "getVariableEnvironment", Type.methodType(Types.LexicalEnvironment)); // class: GlobalEnvironmentRecord static final MethodName GlobalEnvironmentRecord_createGlobalVarBinding = MethodName .findVirtual(Types.GlobalEnvironmentRecord, "createGlobalVarBinding", Type.methodType(Type.VOID_TYPE, Types.String, Type.BOOLEAN_TYPE)); static final MethodName GlobalEnvironmentRecord_createGlobalFunctionBinding = MethodName .findVirtual(Types.GlobalEnvironmentRecord, "createGlobalFunctionBinding", Type .methodType(Type.VOID_TYPE, Types.String, Types.Object, Type.BOOLEAN_TYPE)); static final MethodName GlobalEnvironmentRecord_canDeclareGlobalFunction = MethodName.findVirtual( Types.GlobalEnvironmentRecord, "canDeclareGlobalFunction", Type.methodType(Type.BOOLEAN_TYPE, Types.String)); static final MethodName GlobalEnvironmentRecord_hasLexicalDeclaration = MethodName.findVirtual( Types.GlobalEnvironmentRecord, "hasLexicalDeclaration", Type.methodType(Type.BOOLEAN_TYPE, Types.String)); // class: LexicalEnvironment static final MethodName LexicalEnvironment_getEnvRec = MethodName.findVirtual( Types.LexicalEnvironment, "getEnvRec", Type.methodType(Types.EnvironmentRecord)); // class: ScriptRuntime static final MethodName ScriptRuntime_canDeclareGlobalFunctionOrThrow = MethodName .findStatic(Types.ScriptRuntime, "canDeclareGlobalFunctionOrThrow", Type .methodType(Type.VOID_TYPE, Types.ExecutionContext, Types.GlobalEnvironmentRecord, Types.String)); static final MethodName ScriptRuntime_canDeclareGlobalVarOrThrow = MethodName.findStatic( Types.ScriptRuntime, "canDeclareGlobalVarOrThrow", Type.methodType(Type.VOID_TYPE, Types.ExecutionContext, Types.GlobalEnvironmentRecord, Types.String)); static final MethodName ScriptRuntime_canDeclareLexicalScopedOrThrow = MethodName .findStatic(Types.ScriptRuntime, "canDeclareLexicalScopedOrThrow", Type.methodType( Type.VOID_TYPE, Types.ExecutionContext, Types.GlobalEnvironmentRecord, Types.String)); static final MethodName ScriptRuntime_canDeclareVarScopedOrThrow = MethodName.findStatic( Types.ScriptRuntime, "canDeclareVarScopedOrThrow", Type.methodType(Type.VOID_TYPE, Types.ExecutionContext, Types.GlobalEnvironmentRecord, Types.String)); static final MethodName ScriptRuntime_canDeclareVarBinding = MethodName.findStatic(Types.ScriptRuntime, "canDeclareVarBinding", Type.methodType(Type.BOOLEAN_TYPE, Types.LexicalEnvironment, Types.LexicalEnvironment, Types.String, Type.BOOLEAN_TYPE)); static final MethodName ScriptRuntime_setLegacyBlockFunction = MethodName.findStatic(Types.ScriptRuntime, "setLegacyBlockFunction", Type.methodType(Type.VOID_TYPE, Types.ExecutionContext, Type.INT_TYPE)); static final MethodName ScriptRuntime_InstantiateAsyncFunctionObject = MethodName .findStatic(Types.ScriptRuntime, "InstantiateAsyncFunctionObject", Type.methodType( Types.OrdinaryAsyncFunction, Types.LexicalEnvironment, Types.ExecutionContext, Types.RuntimeInfo$Function)); static final MethodName ScriptRuntime_InstantiateAsyncGeneratorObject = MethodName .findStatic(Types.ScriptRuntime, "InstantiateAsyncGeneratorObject", Type.methodType( Types.OrdinaryAsyncGenerator, Types.LexicalEnvironment, Types.ExecutionContext, Types.RuntimeInfo$Function)); static final MethodName ScriptRuntime_InstantiateFunctionObject = MethodName.findStatic( Types.ScriptRuntime, "InstantiateFunctionObject", Type.methodType( Types.OrdinaryConstructorFunction, Types.LexicalEnvironment, Types.ExecutionContext, Types.RuntimeInfo$Function)); static final MethodName ScriptRuntime_InstantiateLegacyFunctionObject = MethodName.findStatic( Types.ScriptRuntime, "InstantiateLegacyFunctionObject", Type.methodType(Types.LegacyConstructorFunction, Types.LexicalEnvironment, Types.ExecutionContext, Types.RuntimeInfo$Function)); static final MethodName ScriptRuntime_InstantiateConstructorGeneratorObject = MethodName.findStatic( Types.ScriptRuntime, "InstantiateConstructorGeneratorObject", Type.methodType( Types.OrdinaryConstructorGenerator, Types.LexicalEnvironment, Types.ExecutionContext, Types.RuntimeInfo$Function)); static final MethodName ScriptRuntime_InstantiateGeneratorObject = MethodName.findStatic(Types.ScriptRuntime, "InstantiateGeneratorObject", Type.methodType(Types.OrdinaryGenerator, Types.LexicalEnvironment, Types.ExecutionContext, Types.RuntimeInfo$Function)); static final MethodName ScriptRuntime_InstantiateLegacyGeneratorObject = MethodName .findStatic(Types.ScriptRuntime, "InstantiateLegacyGeneratorObject", Type .methodType(Types.OrdinaryConstructorGenerator, Types.LexicalEnvironment, Types.ExecutionContext, Types.RuntimeInfo$Function)); } protected final CodeGenerator codegen; protected DeclarationBindingInstantiationGenerator(CodeGenerator codegen) { this.codegen = codegen; } /** * Emit function call for: {@link ExecutionContext#getLexicalEnvironment()} * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param mv * the instruction visitor */ protected final void getLexicalEnvironment(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, InstructionVisitor mv) { mv.load(context); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); mv.store(env); } /** * Emit function call for: {@link ExecutionContext#getVariableEnvironment()} * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param mv * the instruction visitor */ protected final void getVariableEnvironment(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, InstructionVisitor mv) { mv.load(context); mv.invoke(Methods.ExecutionContext_getVariableEnvironment); mv.store(env); } /** * Emit function call for: {@link LexicalEnvironment#getEnvRec()} * * @param env * the variable which holds the lexical environment * @param envRec * the variable which holds the environment record * @param mv * the instruction visitor */ protected final <R extends EnvironmentRecord, R2 extends R, R3 extends R2> void getEnvironmentRecord( Variable<? extends LexicalEnvironment<? extends R2>> env, Variable<R3> envRec, InstructionVisitor mv) { mv.load(env); mv.invoke(Methods.LexicalEnvironment_getEnvRec); if (envRec.getType() != Types.EnvironmentRecord) { mv.checkcast(envRec.getType()); } mv.store(envRec); } /** * Emit: {@code ScriptRuntime.canDeclareLexicalScopedOrThrow(cx, envRec, name)} * * @param context * the variable which holds the execution context * @param envRec * the variable which holds the environment record * @param node * the declaration node * @param name * the binding name * @param mv * the instruction visitor */ protected final void canDeclareLexicalScopedOrThrow(Variable<ExecutionContext> context, Variable<GlobalEnvironmentRecord> envRec, Node node, Name name, InstructionVisitor mv) { mv.load(context); mv.load(envRec); mv.aconst(name.getIdentifier()); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_canDeclareLexicalScopedOrThrow); } /** * Emit: {@code ScriptRuntime.canDeclareVarScopedOrThrow(cx, envRec, name)} * * @param context * the variable which holds the execution context * @param envRec * the variable which holds the environment record * @param node * the declaration node * @param name * the binding name * @param mv * the instruction visitor */ protected final void canDeclareVarScopedOrThrow(Variable<ExecutionContext> context, Variable<GlobalEnvironmentRecord> envRec, Node node, Name name, InstructionVisitor mv) { mv.load(context); mv.load(envRec); mv.aconst(name.getIdentifier()); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_canDeclareVarScopedOrThrow); } /** * Emit: {@code ScriptRuntime.canDeclareGlobalFunctionOrThrow(cx, envRec, name)} * * @param context * the variable which holds the execution context * @param envRec * the variable which holds the environment record * @param node * the function node * @param name * the binding name * @param mv * the instruction visitor */ protected final void canDeclareGlobalFunctionOrThrow(Variable<ExecutionContext> context, Variable<GlobalEnvironmentRecord> envRec, HoistableDeclaration node, Name name, InstructionVisitor mv) { mv.load(context); mv.load(envRec); mv.aconst(name.getIdentifier()); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_canDeclareGlobalFunctionOrThrow); } /** * Emit: {@code ScriptRuntime.canDeclareGlobalVarOrThrow(cx, envRec, name)} * * @param context * the variable which holds the execution context * @param envRec * the variable which holds the environment record * @param node * the variable declaration node * @param name * the binding name * @param mv * the instruction visitor */ protected final void canDeclareGlobalVarOrThrow(Variable<ExecutionContext> context, Variable<GlobalEnvironmentRecord> envRec, VariableDeclaration node, Name name, InstructionVisitor mv) { mv.load(context); mv.load(envRec); mv.aconst(name.getIdentifier()); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_canDeclareGlobalVarOrThrow); } /** * Emit: {@code envRec.createGlobalVarBinding(name, deletableBindings)} * * @param envRec * the variable which holds the environment record * @param node * the variable declaration node * @param name * the binding name * @param deletableBindings * the variable which holds the deletable flag * @param mv * the instruction visitor */ protected final void createGlobalVarBinding(Variable<GlobalEnvironmentRecord> envRec, VariableDeclaration node, Name name, boolean deletableBindings, InstructionVisitor mv) { mv.load(envRec); mv.aconst(name.getIdentifier()); mv.iconst(deletableBindings); mv.lineInfo(node); mv.invoke(Methods.GlobalEnvironmentRecord_createGlobalVarBinding); } /** * Emit: {@code envRec.createGlobalFunctionBinding(name, functionObject, deletableBindings)} * * @param envRec * the variable which holds the environment record * @param node * the function node * @param name * the binding name * @param fo * the function object * @param deletableBindings * the variable which holds the deletable flag * @param mv * the instruction visitor */ protected final void createGlobalFunctionBinding(Variable<GlobalEnvironmentRecord> envRec, HoistableDeclaration node, Name name, Variable<FunctionObject> fo, boolean deletableBindings, InstructionVisitor mv) { mv.load(envRec); mv.aconst(name.getIdentifier()); mv.load(fo); mv.iconst(deletableBindings); mv.lineInfo(node); mv.invoke(Methods.GlobalEnvironmentRecord_createGlobalFunctionBinding); } /** * Emit: {@code envRec.createGlobalFunctionBinding(name, undefined, deletableBindings)} * * @param envRec * the variable which holds the environment record * @param node * the function node * @param name * the binding name * @param deletableBindings * the variable which holds the deletable flag * @param mv * the instruction visitor */ protected final void createGlobalFunctionBinding(Variable<GlobalEnvironmentRecord> envRec, HoistableDeclaration node, Name name, boolean deletableBindings, InstructionVisitor mv) { mv.load(envRec); mv.aconst(name.getIdentifier()); mv.loadUndefined(); mv.iconst(deletableBindings); mv.lineInfo(node); mv.invoke(Methods.GlobalEnvironmentRecord_createGlobalFunctionBinding); } /** * Emit: {@code !envRec.hasLexicalDeclaration(name) && envRec.canDeclareGlobalFunction} * * @param envRec * the variable which holds the environment record * @param node * the function node * @param name * the binding name * @param next * the next instruction * @param mv * the instruction visitor */ protected final void canDeclareGlobalFunction(Variable<GlobalEnvironmentRecord> envRec, HoistableDeclaration node, Name name, Jump next, InstructionVisitor mv) { mv.load(envRec); mv.aconst(name.getIdentifier()); mv.invoke(Methods.GlobalEnvironmentRecord_hasLexicalDeclaration); mv.ifne(next); mv.load(envRec); mv.aconst(name.getIdentifier()); mv.lineInfo(node); mv.invoke(Methods.GlobalEnvironmentRecord_canDeclareGlobalFunction); mv.ifeq(next); } /** * Emit: {@code ScriptRuntime.canDeclareVarBinding(varEnv, lexEnv, name)} * * @param <R> * the environment record type * @param varEnv * the variable environment * @param lexEnv * the lexical environment * @param name * the binding name * @param catchVar * the {@code catchVar} flag * @param next * the next instruction * @param mv * the instruction visitor */ protected final <R extends EnvironmentRecord> void canDeclareVarBinding(Variable<LexicalEnvironment<R>> varEnv, Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> lexEnv, Name name, boolean catchVar, Jump next, InstructionVisitor mv) { mv.load(varEnv); mv.load(lexEnv); mv.aconst(name.getIdentifier()); mv.iconst(catchVar); mv.invoke(Methods.ScriptRuntime_canDeclareVarBinding); mv.ifeq(next); } /** * Emit: {@code ScriptRuntime.setScriptBlockFunction(cx, functionId)} * * @param context * the variable which holds the execution context * @param function * the function declaration * @param mv * the instruction visitor */ protected final void setLegacyBlockFunction(Variable<ExecutionContext> context, FunctionDeclaration function, InstructionVisitor mv) { mv.load(context); mv.iconst(function.getLegacyBlockScopeId()); mv.invoke(Methods.ScriptRuntime_setLegacyBlockFunction); } /** * Emit runtime call to initialize the function object. * <p> * stack: [] {@literal ->} [fo] * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param f * the function declaration to instantiate * @param mv * the instruction visitor */ protected final void InstantiateFunctionObject(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, Declaration f, InstructionVisitor mv) { if (f instanceof FunctionDeclaration) { if (isLegacy((FunctionDeclaration) f)) { InstantiateLegacyFunctionObject(context, env, (FunctionDeclaration) f, mv); } else { InstantiateFunctionObject(context, env, (FunctionDeclaration) f, mv); } } else if (f instanceof GeneratorDeclaration) { InstantiateGeneratorObject(context, env, (GeneratorDeclaration) f, mv); } else if (f instanceof AsyncFunctionDeclaration) { InstantiateAsyncFunctionObject(context, env, (AsyncFunctionDeclaration) f, mv); } else { InstantiateAsyncGeneratorObject(context, env, (AsyncGeneratorDeclaration) f, mv); } } /** * Emit function call for: * {@link ScriptRuntime#InstantiateAsyncFunctionObject(LexicalEnvironment, ExecutionContext, RuntimeInfo.Function)} * <p> * stack: [] {@literal ->} [fo] * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param f * the function declaration to instantiate * @param mv * the instruction visitor */ private void InstantiateAsyncFunctionObject(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, AsyncFunctionDeclaration f, InstructionVisitor mv) { MethodName method = codegen.compile(f); mv.load(env); mv.load(context); mv.invoke(method); mv.invoke(Methods.ScriptRuntime_InstantiateAsyncFunctionObject); } /** * Emit function call for: * {@link ScriptRuntime#InstantiateAsyncGeneratorObject(LexicalEnvironment, ExecutionContext, RuntimeInfo.Function)} * <p> * stack: [] {@literal ->} [fo] * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param f * the function declaration to instantiate * @param mv * the instruction visitor */ private void InstantiateAsyncGeneratorObject(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, AsyncGeneratorDeclaration f, InstructionVisitor mv) { MethodName method = codegen.compile(f); mv.load(env); mv.load(context); mv.invoke(method); mv.invoke(Methods.ScriptRuntime_InstantiateAsyncGeneratorObject); } /** * Emit function call for: * {@link ScriptRuntime#InstantiateFunctionObject(LexicalEnvironment, ExecutionContext, RuntimeInfo.Function)} * <p> * stack: [] {@literal ->} [fo] * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param f * the function declaration to instantiate * @param mv * the instruction visitor */ private void InstantiateFunctionObject(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, FunctionDeclaration f, InstructionVisitor mv) { MethodName method = codegen.compile(f); mv.load(env); mv.load(context); mv.invoke(method); mv.invoke(Methods.ScriptRuntime_InstantiateFunctionObject); } /** * Emit function call for: * {@link ScriptRuntime#InstantiateLegacyFunctionObject(LexicalEnvironment, ExecutionContext, RuntimeInfo.Function)} * <p> * stack: [] {@literal ->} [fo] * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param f * the function declaration to instantiate * @param mv * the instruction visitor */ private void InstantiateLegacyFunctionObject(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, FunctionDeclaration f, InstructionVisitor mv) { MethodName method = codegen.compile(f); mv.load(env); mv.load(context); mv.invoke(method); mv.invoke(Methods.ScriptRuntime_InstantiateLegacyFunctionObject); } /** * Emit function call for: * {@link ScriptRuntime#InstantiateGeneratorObject(LexicalEnvironment, ExecutionContext, RuntimeInfo.Function)} * <p> * stack: [] {@literal ->} [fo] * * @param context * the variable which holds the execution context * @param env * the variable which holds the lexical environment * @param f * the generator declaration to instantiate * @param mv * the instruction visitor */ private void InstantiateGeneratorObject(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, GeneratorDeclaration f, InstructionVisitor mv) { MethodName method = codegen.compile(f); mv.load(env); mv.load(context); mv.invoke(method); if (f instanceof LegacyGeneratorDeclaration) { mv.invoke(Methods.ScriptRuntime_InstantiateLegacyGeneratorObject); } else if (f.isConstructor()) { mv.invoke(Methods.ScriptRuntime_InstantiateConstructorGeneratorObject); } else { mv.invoke(Methods.ScriptRuntime_InstantiateGeneratorObject); } } private boolean isLegacy(FunctionDeclaration node) { if (IsStrict(node)) { return false; } return codegen.isEnabled(CompatibilityOption.FunctionArguments) || codegen.isEnabled(CompatibilityOption.FunctionCaller); } protected static final <T> Iterable<T> reverse(List<T> list) { return () -> new Iterator<T>() { final ListIterator<T> iter = list.listIterator(list.size()); @Override public boolean hasNext() { return iter.hasPrevious(); } @Override public T next() { return iter.previous(); } }; } }