/**
* 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;
import com.github.anba.es6draft.Executable;
import com.github.anba.es6draft.Module;
import com.github.anba.es6draft.Script;
import com.github.anba.es6draft.runtime.internal.RuntimeContext;
import com.github.anba.es6draft.runtime.internal.RuntimeInfo.SourceObject;
import com.github.anba.es6draft.runtime.modules.SourceTextModuleRecord;
import com.github.anba.es6draft.runtime.objects.async.Async;
import com.github.anba.es6draft.runtime.objects.iteration.GeneratorObject;
import com.github.anba.es6draft.runtime.types.Constructor;
import com.github.anba.es6draft.runtime.types.Intrinsics;
import com.github.anba.es6draft.runtime.types.Reference;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.builtins.FunctionObject;
import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject;
/**
* <h1>8 Executable Code and Execution Contexts</h1>
* <ul>
* <li>8.3 Execution Contexts
* </ul>
*/
public final class ExecutionContext {
private final Realm realm;
private LexicalEnvironment<?> varEnv;
private LexicalEnvironment<?> lexEnv;
private final LexicalEnvironment<FunctionEnvironmentRecord> funVarEnv;
private final Executable executable;
private final FunctionObject function;
private GeneratorObject generator;
private Async async;
private ExecutionContext(Realm realm, LexicalEnvironment<?> varEnv,
LexicalEnvironment<?> lexEnv, LexicalEnvironment<FunctionEnvironmentRecord> funVarEnv,
Executable executable, FunctionObject function) {
this.realm = realm;
this.varEnv = varEnv;
this.lexEnv = lexEnv;
this.funVarEnv = funVarEnv;
this.executable = executable;
this.function = function;
}
/**
* Returns the {@code Realm} component of this execution context.
*
* @return the {@code Realm} component
*/
public Realm getRealm() {
return realm;
}
/**
* Returns the runtime context of this execution context.
*
* @return the runtime context
*/
public RuntimeContext getRuntimeContext() {
return realm.getWorld().getContext();
}
/**
* Returns the requested intrinsic from this execution context's {@code Realm}.
*
* @param id
* the intrinsic identifier
* @return the intrinsic object
* @see Realm#getIntrinsic(Intrinsics)
*/
public OrdinaryObject getIntrinsic(Intrinsics id) {
return realm.getIntrinsic(id);
}
/**
* Returns the requested intrinsic from this execution context's {@code Realm}.
*
* @param <T>
* the intrisic's type
* @param id
* the intrinsic identifier
* @param klass
* the intrinsic's class
* @return the intrinsic object
* @see Realm#getIntrinsic(Intrinsics)
*/
@SuppressWarnings("unchecked")
public <T extends OrdinaryObject> OrdinaryObject getIntrinsic(Intrinsics id, Class<T> klass) {
OrdinaryObject intrinsic = realm.getIntrinsic(id);
assert klass.isInstance(intrinsic) : "Unexpected type: " + intrinsic.getClass();
return (T) intrinsic;
}
/**
* Returns the current executable object.
*
* @return the executable object
*/
public Executable getCurrentExecutable() {
return executable;
}
/**
* Returns the {@code Function} component of this execution context.
*
* @return the {@code Function} component or {@code null} if not evaluating a function
*/
public FunctionObject getCurrentFunction() {
return function;
}
/**
* Returns the {@code Generator} component of this execution context.
*
* @return the {@code Generator} component or {@code null} if not evaluating a generator
*/
public GeneratorObject getCurrentGenerator() {
return generator;
}
/**
* Sets the {@code Generator} component of this execution context.
*
* @param generator
* the {@code Generator} component
*/
public void setCurrentGenerator(GeneratorObject generator) {
assert this.generator == null && generator != null;
this.generator = generator;
}
/**
* Returns the {@code Async} component of this execution context.
*
* @return the {@code Async} component or {@code null} if not evaluating an async function
*/
public Async getCurrentAsync() {
return async;
}
/**
* Sets the {@code Async} component of this execution context.
*
* @param async
* the {@code Async} component
*/
public void setCurrentAsync(Async async) {
assert this.async == null && async != null;
this.async = async;
}
/**
* Returns the {@code LexicalEnvironment} component of this execution context.
*
* @return the {@code LexicalEnvironment} component or {@code null} if not evaluating ECMAScript
* code
*/
public LexicalEnvironment<?> getLexicalEnvironment() {
return lexEnv;
}
/**
* Returns the {@code VariableEnvironment} component of this execution context.
*
* @return the {@code VariableEnvironment} component or {@code null} if not evaluating
* ECMAScript code
*/
public LexicalEnvironment<?> getVariableEnvironment() {
return varEnv;
}
/**
* Returns the {@code FunctionVariableEnvironment} component of this execution context.
*
* @return the {@code FunctionVariableEnvironment} component or {@code null} if not evaluating
* ECMAScript function code
*/
public LexicalEnvironment<FunctionEnvironmentRecord> getFunctionVariableEnvironment() {
return funVarEnv;
}
/**
* Returns the environment record of the {@code LexicalEnvironment} component of this execution
* context. Must not be called if not evaluating ECMAScript code.
*
* @return the environment record
*/
public EnvironmentRecord getLexicalEnvironmentRecord() {
return lexEnv.getEnvRec();
}
/**
* Returns the environment record of the {@code VariableEnvironment} component of this execution
* context. Must not be called if not evaluating ECMAScript code.
*
* @return the environment record
*/
public EnvironmentRecord getVariableEnvironmentRecord() {
return varEnv.getEnvRec();
}
/**
* Returns the environment record of the {@code FunctionVariableEnvironment} component of this
* execution context. Must not be called if not evaluating ECMAScript function code.
*
* @return the environment record
*/
public FunctionEnvironmentRecord getFunctionVariableEnvironmentRecord() {
return funVarEnv.getEnvRec();
}
/**
* [Called from generated code]
*
* @param lexEnv
* the new lexical environment
*/
public void pushLexicalEnvironment(LexicalEnvironment<?> lexEnv) {
assert lexEnv.getOuter() == this.lexEnv;
this.lexEnv = lexEnv;
}
/**
* [Called from generated code]
*/
public void popLexicalEnvironment() {
this.lexEnv = lexEnv.getOuter();
}
/**
* [Called from generated code]
*
* @param lexEnv
* the new lexical environment
*/
public void restoreLexicalEnvironment(LexicalEnvironment<?> lexEnv) {
this.lexEnv = lexEnv;
}
/**
* [Called from generated code]
*
* @param lexEnv
* the new lexical environment
*/
public void replaceLexicalEnvironment(LexicalEnvironment<?> lexEnv) {
assert lexEnv.getOuter() == this.lexEnv.getOuter();
this.lexEnv = lexEnv;
}
/**
* [Called from generated code]
*
* @param env
* the new lexical environment
*/
public void setVariableEnvironment(LexicalEnvironment<?> env) {
this.varEnv = env;
}
/**
* [Called from generated code]
*
* @param env
* the new lexical environment
*/
public void setLexicalEnvironment(LexicalEnvironment<?> env) {
this.lexEnv = env;
}
/**
* [Called from generated code]
*
* @param env
* the new lexical environment
*/
public void setVariableAndLexicalEnvironment(LexicalEnvironment<?> env) {
assert this.varEnv == this.lexEnv;
this.varEnv = env;
this.lexEnv = env;
}
/**
* Creates a new default execution context.
*
* @param realm
* the realm instance
* @return the new default execution context
*/
static ExecutionContext newDefaultExecutionContext(Realm realm) {
return new ExecutionContext(realm, null, null, null, DefaultExecutable.INSTANCE, null);
}
private static final class DefaultExecutable implements Executable {
private static final DefaultExecutable INSTANCE = new DefaultExecutable();
@Override
public SourceObject getSourceObject() {
return null;
}
}
/**
* <ul>
* <li>15 ECMAScript Language: Scripts and Modules
* <ul>
* <li>15.1 Script
* </ul>
* </ul>
* <p>
* 15.1.7 Runtime Semantics: ScriptEvaluation
*
* @param realm
* the realm instance
* @param script
* the script object
* @return the new script execution context
*/
public static ExecutionContext newScriptExecutionContext(Realm realm, Script script) {
/* steps 3-6 */
return new ExecutionContext(realm, realm.getGlobalEnv(), realm.getGlobalEnv(), null,
script, null);
}
/**
* <ul>
* <li>15 ECMAScript Language: Scripts and Modules
* <ul>
* <li>15.2 Modules
* <ul>
* <li>15.2.1 Module Semantics
* </ul>
* </ul>
* </ul>
* <p>
* 15.2.1.21 Runtime Semantics: ModuleDeclarationInstantiation( module, realm, moduleSet )
*
* @param realm
* the realm instance
* @param module
* the module object
* @return the new module execution context
*/
public static ExecutionContext newModuleDeclarationExecutionContext(Realm realm, Module module) {
return new ExecutionContext(realm, realm.getGlobalEnv(), realm.getGlobalEnv(), null,
module, null);
}
/**
* <ul>
* <li>15 ECMAScript Language: Scripts and Modules
* <ul>
* <li>15.2 Modules
* <ul>
* <li>15.2.1 Module Semantics
* </ul>
* </ul>
* </ul>
* <p>
* 15.2.1.22 Runtime Semantics: ModuleEvaluation(module, realm)
*
* @param realm
* the realm instance
* @param module
* the module object
* @return the new module execution context
*/
public static ExecutionContext newModuleExecutionContext(Realm realm,
SourceTextModuleRecord module) {
/* steps 4-9 */
return new ExecutionContext(realm, module.getEnvironment(), module.getEnvironment(), null,
module.getScriptCode(), null);
}
/**
* <ul>
* <li>18 The Global Object
* <ul>
* <li>18.2 Function Properties of the Global Object
* </ul>
* </ul>
* <p>
* 18.2.1.1 Runtime Semantics: PerformEval( x, evalRealm, strictCaller, direct)
*
* @param callerContext
* the caller execution context
* @param evalScript
* the eval script object
* @param varEnv
* the current variable environment
* @param lexEnv
* the new lexical environment
* @return the new eval execution context
*/
public static ExecutionContext newEvalExecutionContext(ExecutionContext callerContext,
Script evalScript, LexicalEnvironment<?> varEnv, LexicalEnvironment<?> lexEnv) {
/* steps 12-17 */
return new ExecutionContext(callerContext.realm, varEnv, lexEnv, callerContext.funVarEnv,
evalScript, callerContext.function);
}
/**
* <ul>
* <li>9 Ordinary and Exotic Objects Behaviours
* <ul>
* <li>9.2 ECMAScript Function Objects
* </ul>
* </ul>
* <p>
* 9.2.2.1 PrepareForOrdinaryCall( F, newTarget )
*
* @param f
* the callee function object
* @param localEnv
* the function environment
* @return the new function execution context
*/
public static ExecutionContext newFunctionExecutionContext(FunctionObject f,
LexicalEnvironment<FunctionEnvironmentRecord> localEnv) {
/* steps 1-2, 7 (not applicable) */
/* steps 3-6, 8-13 */
return new ExecutionContext(f.getRealm(), localEnv, localEnv, localEnv, f.getExecutable(),
f);
}
/**
* Returns a new execution context for JSR-223 scripting.
*
* @param realm
* the realm instance
* @param script
* the script object
* @param varEnv
* the variable environment
* @return the new scripting execution context
*/
public static ExecutionContext newScriptingExecutionContext(Realm realm, Script script,
LexicalEnvironment<?> varEnv) {
return new ExecutionContext(realm, varEnv, varEnv, null, script, null);
}
/**
* Combined {@link #resolveBinding(String, boolean)} with
* {@link Reference#GetValue(Object, ExecutionContext)} internal method.
*
* @param name
* the binding name
* @param strict
* the strict mode flag
* @return the resolved reference value
*/
public Object resolveBindingValue(String name, boolean strict) {
return LexicalEnvironment.getIdentifierValueOrThrow(lexEnv, name, strict);
}
/**
* 8.3.1 ResolveBinding(name)
*
* @param name
* the binding name
* @param strict
* the strict mode flag
* @return the resolved reference
*/
public Reference<?, String> resolveBinding(String name, boolean strict) {
/* steps 1-3 */
return LexicalEnvironment.getIdentifierReference(lexEnv, name, strict);
}
/**
* 8.3.2 GetThisEnvironment()
*
* @return the first environment record with a this-binding
*/
public EnvironmentRecord getThisEnvironment() {
/* step 1 */
LexicalEnvironment<?> lex = lexEnv;
/* step 2 */
for (;;) {
EnvironmentRecord envRec = lex.getEnvRec();
boolean exists = envRec.hasThisBinding();
if (exists) {
return envRec;
}
lex = lex.getOuter();
}
}
/**
* 8.3.3 ResolveThisBinding()
*
* @return the this-binding object
*/
public Object resolveThisBinding() {
/* step 1 */
EnvironmentRecord envRec = getThisEnvironment();
/* step 2 */
return envRec.getThisBinding(this);
}
/**
* 8.3.4 GetNewTarget ( )
*
* @return the NewTarget constructor object
*/
public Constructor getNewTarget() {
/* step 1 */
EnvironmentRecord envRec = getThisEnvironment();
/* step 2 */
assert envRec instanceof FunctionEnvironmentRecord : String.format(
"Wrong environment kind = %s", envRec.getClass().getSimpleName());
/* step 3 */
return ((FunctionEnvironmentRecord) envRec).getNewTarget();
}
/**
* 8.3.5 GetGlobalObject()
*
* @return the global object instance
*/
public ScriptObject getGlobalObject() {
/* steps 1-2 */
Realm currentRealm = realm;
/* step 3 */
return currentRealm.getGlobalObject();
}
}