/** * 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.*; import java.util.List; import com.github.anba.es6draft.ast.BlockStatement; import com.github.anba.es6draft.ast.Declaration; import com.github.anba.es6draft.ast.HoistableDeclaration; import com.github.anba.es6draft.ast.SwitchStatement; import com.github.anba.es6draft.ast.scope.Name; import com.github.anba.es6draft.compiler.assembler.MethodName; import com.github.anba.es6draft.compiler.assembler.Variable; import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.types.builtins.FunctionObject; /** * <h1>13 ECMAScript Language: Statements and Declarations</h1><br> * <h2>13.2 Block</h2> * <ul> * <li>13.2.14 Runtime Semantics: BlockDeclarationInstantiation( code, env ) * </ul> */ final class BlockDeclarationInstantiationGenerator extends DeclarationBindingInstantiationGenerator { private static final int INLINE_LIMIT = 1 << 5; private static final int METHOD_LIMIT = 1 << 12; BlockDeclarationInstantiationGenerator(CodeGenerator codegen) { super(codegen); } /** * stack: [env] {@literal ->} [env] * * @param node * the block statement * @param mv * the code visitor */ void generate(BlockStatement node, CodeVisitor mv) { int declarations = LexicallyDeclaredNames(node).size(); if (declarations > INLINE_LIMIT) { MethodName method = codegen.compile(node, this); // stack: [env] -> [env] mv.dup(); mv.loadExecutionContext(); mv.invoke(method); } else { generateInline(LexicallyScopedDeclarations(node), mv); } } /** * stack: [env] {@literal ->} [env] * * @param node * the switch statement * @param mv * the code visitor */ void generate(SwitchStatement node, CodeVisitor mv) { int declarations = LexicallyDeclaredNames(node).size(); if (declarations > INLINE_LIMIT) { MethodName method = codegen.compile(node, this); // stack: [env] -> [env] mv.dup(); mv.loadExecutionContext(); mv.invoke(method); } else { generateInline(LexicallyScopedDeclarations(node), mv); } } /** * stack: [] {@literal ->} [] * * @param node * the block statement * @param cx * the execution context * @param env * the lexical environment * @param mv * the instruction visitor */ void generateMethod(BlockStatement node, Variable<ExecutionContext> cx, Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env, InstructionVisitor mv) { List<Declaration> declarations = LexicallyScopedDeclarations(node); if (declarations.size() <= METHOD_LIMIT) { generate(declarations, cx, env, mv); } else { for (int i = 0, size = declarations.size(); i < size; i += METHOD_LIMIT) { List<Declaration> sublist = declarations.subList(i, Math.min(i + METHOD_LIMIT, size)); MethodName method = codegen.compile(node, sublist, this); mv.load(env); mv.load(cx); mv.invoke(method); } } } /** * stack: [] {@literal ->} [] * * @param node * the switch statement * @param cx * the execution context * @param env * the lexical environment * @param mv * the instruction visitor */ void generateMethod(SwitchStatement node, Variable<ExecutionContext> cx, Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env, InstructionVisitor mv) { List<Declaration> declarations = LexicallyScopedDeclarations(node); if (declarations.size() <= METHOD_LIMIT) { generate(declarations, cx, env, mv); } else { for (int i = 0, size = declarations.size(); i < size; i += METHOD_LIMIT) { List<Declaration> sublist = declarations.subList(i, Math.min(i + METHOD_LIMIT, size)); MethodName method = codegen.compile(node, sublist, this); mv.load(env); mv.load(cx); mv.invoke(method); } } } /** * stack: [] {@literal ->} [] * * @param declarations * the block declarations * @param cx * the execution context * @param env * the lexical environment * @param mv * the instruction visitor */ void generateMethod(List<Declaration> declarations, Variable<ExecutionContext> cx, Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env, InstructionVisitor mv) { assert declarations.size() <= METHOD_LIMIT : declarations.size(); generate(declarations, cx, env, mv); } private void generateInline(List<Declaration> declarations, CodeVisitor mv) { mv.enterVariableScope(); Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env = mv.newVariable("env", LexicalEnvironment.class) .uncheckedCast(); // stack: [env] -> [] mv.store(env); generate(declarations, mv.executionContext(), env, mv); // stack: [] -> [env] mv.load(env); mv.exitVariableScope(); } private void generate(List<Declaration> declarations, Variable<ExecutionContext> cx, Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env, InstructionVisitor mv) { Variable<DeclarativeEnvironmentRecord> envRec = mv.newVariable("envRec", DeclarativeEnvironmentRecord.class); Variable<FunctionObject> fo = null; getEnvironmentRecord(env, envRec, mv); /* steps 1-2 */ for (Declaration d : declarations) { if (!(d instanceof HoistableDeclaration)) { for (Name dn : BoundNames(d)) { BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(envRec, dn); if (IsConstantDeclaration(d)) { op.createImmutableBinding(envRec, dn, true, mv); } else { op.createMutableBinding(envRec, dn, false, mv); } } } else { Name fn = BoundName((HoistableDeclaration) d); BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(envRec, fn); op.createMutableBinding(envRec, fn, false, mv); InstantiateFunctionObject(cx, env, d, mv); if (fo == null) { fo = mv.newVariable("fo", FunctionObject.class); } mv.store(fo); op.initializeBinding(envRec, fn, fo, mv); } } } }