/** * 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 com.github.anba.es6draft.ast.Script; import com.github.anba.es6draft.compiler.CodeGenerator.ScriptName; import com.github.anba.es6draft.compiler.assembler.Code.MethodCode; import com.github.anba.es6draft.compiler.assembler.MethodName; import com.github.anba.es6draft.compiler.assembler.TryCatchLabel; import com.github.anba.es6draft.compiler.assembler.Type; import com.github.anba.es6draft.compiler.assembler.Value; 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.Realm; /** * Generates bytecode for the script entry method. */ final class ScriptCodeGenerator { private static final class Methods { // ExecutionContext static final MethodName ExecutionContext_newEvalExecutionContext = MethodName.findStatic( Types.ExecutionContext, "newEvalExecutionContext", Type.methodType(Types.ExecutionContext, Types.ExecutionContext, Types.Script, Types.LexicalEnvironment, Types.LexicalEnvironment)); static final MethodName ExecutionContext_newScriptExecutionContext = MethodName.findStatic( Types.ExecutionContext, "newScriptExecutionContext", Type.methodType(Types.ExecutionContext, Types.Realm, Types.Script)); static final MethodName ExecutionContext_getLexicalEnvironment = MethodName.findVirtual( Types.ExecutionContext, "getLexicalEnvironment", Type.methodType(Types.LexicalEnvironment)); static final MethodName ExecutionContext_setLexicalEnvironment = MethodName.findVirtual( Types.ExecutionContext, "setLexicalEnvironment", Type.methodType(Type.VOID_TYPE, Types.LexicalEnvironment)); static final MethodName ExecutionContext_getVariableEnvironment = MethodName.findVirtual( Types.ExecutionContext, "getVariableEnvironment", Type.methodType(Types.LexicalEnvironment)); static final MethodName ExecutionContext_getRealm = MethodName .findVirtual(Types.ExecutionContext, "getRealm", Type.methodType(Types.Realm)); // class: LexicalEnvironment static final MethodName LexicalEnvironment_newDeclarativeEnvironment = MethodName .findStatic(Types.LexicalEnvironment, "newDeclarativeEnvironment", Type.methodType( Types.LexicalEnvironment, Types.LexicalEnvironment)); // Realm static final MethodName Realm_getGlobalEnv = MethodName.findVirtual(Types.Realm, "getGlobalEnv", Type.methodType(Types.LexicalEnvironment)); static final MethodName Realm_getScriptContext = MethodName.findVirtual(Types.Realm, "getScriptContext", Type.methodType(Types.ExecutionContext)); static final MethodName Realm_setScriptContext = MethodName.findVirtual(Types.Realm, "setScriptContext", Type.methodType(Type.VOID_TYPE, Types.ExecutionContext)); } private static final int EXECUTION_CONTEXT = 0; private static final int SCRIPT = 1; private static final class ScriptEvalMethodGenerator extends InstructionVisitor { ScriptEvalMethodGenerator(MethodCode method) { super(method); } @Override public void begin() { super.begin(); setParameterName("callerContext", EXECUTION_CONTEXT, Types.ExecutionContext); setParameterName("script", SCRIPT, Types.Script); } } private final CodeGenerator codegen; ScriptCodeGenerator(CodeGenerator codegen) { this.codegen = codegen; } void generate(Script node) { InstructionVisitor mv = new ScriptEvalMethodGenerator( codegen.newMethod(node, ScriptName.Eval)); mv.lineInfo(node); mv.begin(); if (node.isScripting()) { generateScriptingEvaluation(node, mv); } else if (node.isEvalScript()) { generateEvalScriptEvaluation(node, mv); } else { generateGlobalScriptEvaluation(node, mv); } mv.end(); } /** * 15.1.7 Runtime Semantics: ScriptEvaluation * * @param node * the script node * @param mv * the instruction visitor */ private void generateGlobalScriptEvaluation(Script node, InstructionVisitor mv) { Variable<ExecutionContext> callerContext = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); Variable<com.github.anba.es6draft.Script> script = mv.getParameter(SCRIPT, com.github.anba.es6draft.Script.class); Variable<Realm> realm = mv.newVariable("realm", Realm.class); Variable<ExecutionContext> scriptCxt = mv.newVariable("scriptCxt", ExecutionContext.class); Variable<ExecutionContext> oldScriptContext = mv.newVariable("oldScriptContext", ExecutionContext.class); Variable<Object> result = mv.newVariable("result", Object.class); Variable<Throwable> throwable = mv.newVariable("throwable", Throwable.class); getRealm(callerContext, realm, mv); /* steps 1-2 (not applicable) */ /* steps 3-7 */ newScriptExecutionContext(realm, script, scriptCxt, mv); /* step 8 */ getScriptContext(realm, oldScriptContext, mv); /* step 9 */ setScriptContext(realm, scriptCxt, mv); TryCatchLabel startFinally = new TryCatchLabel(), endFinally = new TryCatchLabel(); TryCatchLabel handlerFinally = new TryCatchLabel(); mv.mark(startFinally); { /* step 10 */ mv.load(scriptCxt); mv.invoke(codegen.methodDesc(node, ScriptName.Init)); /* steps 11-12 */ mv.load(scriptCxt); mv.invoke(codegen.methodDesc(node, ScriptName.Code)); mv.store(result); /* steps 13-15 */ setScriptContext(realm, oldScriptContext, mv); /* step 16 */ mv.load(result); mv._return(); } mv.mark(endFinally); // Exception: Restore script context and then rethrow exception mv.finallyHandler(handlerFinally); mv.store(throwable); /* steps 13-15 */ setScriptContext(realm, oldScriptContext, mv); mv.load(throwable); mv.athrow(); mv.tryFinally(startFinally, endFinally, handlerFinally); } /** * 18.2.1.1 Runtime Semantics: PerformEval( x, evalRealm, strictCaller, direct) * * @param node * the script node * @param mv * the instruction visitor */ private void generateEvalScriptEvaluation(Script node, InstructionVisitor mv) { Variable<ExecutionContext> callerContext = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); Variable<com.github.anba.es6draft.Script> script = mv.getParameter(SCRIPT, com.github.anba.es6draft.Script.class); Variable<ExecutionContext> evalCxt = mv.newVariable("evalCxt", ExecutionContext.class); Variable<? extends LexicalEnvironment<?>> varEnv = mv .newVariable("varEnv", LexicalEnvironment.class).uncheckedCast(); Variable<? extends LexicalEnvironment<?>> lexEnv = mv .newVariable("lexEnv", LexicalEnvironment.class).uncheckedCast(); // Optimization: Skip creating lexical environment if no declarations are present. boolean noDeclarations = node.getScope().lexicallyDeclaredNames().isEmpty(); if (node.isStrict()) { noDeclarations &= node.getScope().varDeclaredNames().isEmpty(); } /* steps 1-5 (not applicable) */ /* steps 6-7 */ boolean strictEval = node.isStrict(); /* step 8 (omitted) */ /* steps 9-10 */ if (node.isDirectEval()) { /* step 9 */ getVariableEnvironment(callerContext, varEnv, mv); if (noDeclarations) { getLexicalEnvironment(callerContext, lexEnv, mv); } else { newDeclarativeEnvironment(callerContext, lexEnv, mv); } } else { /* step 10 */ getGlobalEnv(callerContext, varEnv, mv); if (noDeclarations) { lexEnv = varEnv; } else { newDeclarativeEnvironment(varEnv, lexEnv, mv); } } /* step 11 */ if (strictEval) { varEnv = lexEnv; } /* steps 12-17 */ newEvalExecutionContext(callerContext, script, varEnv, lexEnv, evalCxt, mv); /* step 18 */ mv.load(evalCxt); mv.invoke(codegen.methodDesc(node, ScriptName.Init)); /* steps 19-23 */ mv.load(evalCxt); mv.invoke(codegen.methodDesc(node, ScriptName.Code)); mv._return(); } private void generateScriptingEvaluation(Script node, InstructionVisitor mv) { Variable<ExecutionContext> context = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> lexEnv = mv .newVariable("lexEnv", LexicalEnvironment.class).uncheckedCast(); // Create an empty declarative environment for the lexical bindings. newDeclarativeEnvironment(context, lexEnv, mv); // Replace the lexical environment component. setLexicalEnvironment(context, lexEnv, mv); // Create local bindings. mv.load(context); mv.invoke(codegen.methodDesc(node, ScriptName.Init)); // Evaluate the actual script code. mv.load(context); mv.invoke(codegen.methodDesc(node, ScriptName.Code)); mv._return(); } /** * Emit: {@code realm = context.getRealm()} */ private void getRealm(Variable<ExecutionContext> context, Variable<Realm> realm, InstructionVisitor mv) { mv.load(context); mv.invoke(Methods.ExecutionContext_getRealm); mv.store(realm); } /** * Emit: {@code context = ExecutionContext.newScriptExecutionContext(realm, script)} */ private void newScriptExecutionContext(Variable<Realm> realm, Variable<com.github.anba.es6draft.Script> script, Variable<ExecutionContext> context, InstructionVisitor mv) { mv.load(realm); mv.load(script); mv.invoke(Methods.ExecutionContext_newScriptExecutionContext); mv.store(context); } /** * Emit: * {@code context = ExecutionContext.newEvalExecutionContext(callerContext, script, varEnv, lexEnv)} */ private void newEvalExecutionContext(Variable<ExecutionContext> callerContext, Variable<com.github.anba.es6draft.Script> script, Variable<? extends LexicalEnvironment<?>> varEnv, Variable<? extends LexicalEnvironment<?>> lexEnv, Variable<ExecutionContext> context, InstructionVisitor mv) { mv.load(callerContext); mv.load(script); mv.load(varEnv); mv.load(lexEnv); mv.invoke(Methods.ExecutionContext_newEvalExecutionContext); mv.store(context); } /** * Emit: {@code env = cx.getRealm().getGlobalEnv()} */ private void getGlobalEnv(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, InstructionVisitor mv) { mv.load(context); mv.invoke(Methods.ExecutionContext_getRealm); mv.invoke(Methods.Realm_getGlobalEnv); mv.store(env); } /** * Emit: {@code realm = realm.getScriptContext()} */ private void getScriptContext(Variable<Realm> realm, Variable<ExecutionContext> context, InstructionVisitor mv) { mv.load(realm); mv.invoke(Methods.Realm_getScriptContext); mv.store(context); } /** * Emit: {@code realm.setScriptContext(context)} */ private void setScriptContext(Variable<Realm> realm, Variable<ExecutionContext> context, InstructionVisitor mv) { mv.load(realm); mv.load(context); mv.invoke(Methods.Realm_setScriptContext); } /** * Emit: {@code env = LexicalEnvironment.newDeclarativeEnvironment(outer)} */ private void newDeclarativeEnvironment(Value<? extends LexicalEnvironment<?>> outer, Variable<? extends LexicalEnvironment<?>> env, InstructionVisitor mv) { mv.load(outer); mv.invoke(Methods.LexicalEnvironment_newDeclarativeEnvironment); mv.store(env); } /** * Emit: * {@code env = LexicalEnvironment.newDeclarativeEnvironment(context.getLexicalEnvironment())} */ private void newDeclarativeEnvironment(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, InstructionVisitor mv) { mv.load(context); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); mv.invoke(Methods.LexicalEnvironment_newDeclarativeEnvironment); mv.store(env); } /** * Emit: {@code env = context.getVariableEnvironment()} */ private void getVariableEnvironment(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, InstructionVisitor mv) { mv.load(context); mv.invoke(Methods.ExecutionContext_getVariableEnvironment); mv.store(env); } /** * Emit: {@code env = context.getLexicalEnvironment()} */ private void getLexicalEnvironment(Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<?>> env, InstructionVisitor mv) { mv.load(context); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); mv.store(env); } /** * Emit: {@code context.setLexicalEnvironment(env)} */ private void setLexicalEnvironment(Variable<ExecutionContext> context, Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env, InstructionVisitor mv) { mv.load(context); mv.load(env); mv.invoke(Methods.ExecutionContext_setLexicalEnvironment); } }