Skip to content

Commit

Permalink
JSS.Lib: Implement the OrdinaryCallBindThis abstract operation
Browse files Browse the repository at this point in the history
  • Loading branch information
PrestonLTaylor committed Jan 11, 2024
1 parent ffc6ff1 commit 8c4c520
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 58 deletions.
184 changes: 127 additions & 57 deletions JSS.Lib/AST/Values/FunctionObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace JSS.Lib.AST.Values;

enum ThisMode
{
STRICT,
LEXICAL,
GLOBAL,
}
Expand Down Expand Up @@ -35,7 +36,7 @@ public Completion Call(VM vm, Value thisArgument, List argumentsList)
// 1. Let callerContext be the running execution context.

// 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined).
var calleeContext = PrepareForOrdinaryCall(vm, Undefined.The);
var calleeContext = (PrepareForOrdinaryCall(vm, Undefined.The) as ScriptExecutionContext)!;

// 3. Assert: calleeContext is now the running execution context.
Debug.Assert(vm.CurrentExecutionContext == calleeContext);
Expand All @@ -45,7 +46,9 @@ public Completion Call(VM vm, Value thisArgument, List argumentsList)
// FIXME: b. NOTE: error is created in calleeContext with F's associated Realm Record.
// FIXME: c. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
// FIXME: d. Return ThrowCompletion(error).
// FIXME: 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).

// 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
OrdinaryCallBindThis(vm, calleeContext, thisArgument);

// 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)).
var result = OrdinaryCallEvaluateBody(vm, argumentsList);
Expand All @@ -63,6 +66,117 @@ public Completion Call(VM vm, Value thisArgument, List argumentsList)
return Completion.NormalCompletion(Undefined.The);
}

// 10.2.1.1 PrepareForOrdinaryCall ( F, newTarget ), https://tc39.es/ecma262/#sec-prepareforordinarycall
private ExecutionContext PrepareForOrdinaryCall(VM vm, Object newTarget)
{
// 1. Let callerContext be the running execution context.
var callerContext = vm.CurrentExecutionContext;

// 2. Let calleeContext be a new ECMAScript code execution context.
// FIXME: 3. Set the Function of calleeContext to F.
// 4. Let calleeRealm be F.[[Realm]].
// 5. Set the Realm of calleeContext to calleeRealm.
// 6. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
var calleeContext = new ScriptExecutionContext(vm.Realm);

// 7. Let localEnv be NewFunctionEnvironment(F, newTarget).
var localEnv = new FunctionEnvironment(this, newTarget);

// 8. Set the LexicalEnvironment of calleeContext to localEnv.
calleeContext.LexicalEnvironment = localEnv;

// 9. Set the VariableEnvironment of calleeContext to localEnv.
calleeContext.VariableEnvironment = localEnv;

// FIXME: 10. Set the PrivateEnvironment of calleeContext to F.[[PrivateEnvironment]].

// FIXME: 11. If callerContext is not already suspended, suspend callerContext.

// 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
vm.PushExecutionContext(calleeContext);

// 13. NOTE: Any exception objects produced after this point are associated with calleeRealm.
// 14. Return calleeContext.
return calleeContext;
}

// 10.2.1.2 OrdinaryCallBindThis ( F, calleeContext, thisArgument ), https://tc39.es/ecma262/#sec-ordinarycallbindthis
private void OrdinaryCallBindThis(VM vm, ScriptExecutionContext calleeContext, Value thisArgument)
{
// 1. Let thisMode be F.[[ThisMode]].

// 2. If thisMode is LEXICAL, return UNUSED.
if (ThisMode == ThisMode.LEXICAL) return;

// 3. Let calleeRealm be FIXME: F.[[Realm]].
var calleeRealm = vm.Realm;

// 4. Let localEnv be the LexicalEnvironment of calleeContext.
var localEnv = calleeContext.LexicalEnvironment;

Value thisValue;

// 5. If thisMode is STRICT, then
if (ThisMode == ThisMode.STRICT)
{
// a. Let thisValue be thisArgument.
thisValue = thisArgument;
}
// 6. Else,
else
{
// a. If thisArgument is either undefined or null, then
if (thisArgument.IsUndefined() || thisArgument.IsNull())
{
// i. Let globalEnv be calleeRealm.[[GlobalEnv]].
var globalEnv = calleeRealm.GlobalEnv;

// ii. Assert: globalEnv is a Global Environment Record.
Debug.Assert(globalEnv is GlobalEnvironment);

// iii. Let thisValue be globalEnv.[[GlobalThisValue]].
thisValue = globalEnv.GlobalThisValue;
}
// b. Else,
else
{
// i. Let thisValue be ! ToObject(thisArgument).
var toObject = thisArgument.ToObject();
Debug.Assert(toObject.IsNormalCompletion());
thisValue = toObject.Value;

// ii. NOTE: ToObject produces wrapper objects using calleeRealm.
}
}

// 7. Assert: localEnv is a Function Environment Record.
Debug.Assert(localEnv is FunctionEnvironment);

// 8. Assert: The next step never returns an abrupt completion because localEnv.[[ThisBindingStatus]] is not INITIALIZED.

// 9. Perform ! localEnv.BindThisValue(thisValue).
var localFunctionEnv = localEnv as FunctionEnvironment;
var bindResult = localFunctionEnv!.BindThisValue(thisValue);
Debug.Assert(bindResult.IsNormalCompletion());

// 10. Return UNUSED.
}

// 10.2.1.3 Runtime Semantics: EvaluateBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatebody
private Completion EvaluateBody(VM vm, List argumentsList)
{
// FIXME: Evaluate other types of functions when we implement them
// 1. Return ? EvaluateFunctionBody of FunctionBody with arguments functionObject and argumentsList.
return EvaluateFunctionBody(vm, argumentsList);
}

// 10.2.1.4 OrdinaryCallEvaluateBody ( F, argumentsList ), https://tc39.es/ecma262/#sec-ordinarycallevaluatebody
private Completion OrdinaryCallEvaluateBody(VM vm, List argumentsList)
{
// 1. Return ? EvaluateBody of F.[[ECMAScriptCode]] with arguments F and argumentsList.
return EvaluateBody(vm, argumentsList);
}

// 10.2.2 [[Construct]] ( argumentsList, FIXME: newTarget ), https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
public Completion Construct(VM vm, List argumentsList)
{
Expand All @@ -86,12 +200,17 @@ public Completion Construct(VM vm, List argumentsList)
// 5. Assert: calleeContext is now the running execution context.
Debug.Assert(vm.CurrentExecutionContext == calleeContext);

// FIXME: 6. If kind is BASE, then
// FIXME: a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
// FIXME: b. Let initializeResult be Completion(InitializeInstanceElements(thisArgument, F)).
// FIXME: c. If initializeResult is an abrupt completion, then
// FIXME: i. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
// FIXME: ii. Return ? initializeResult.
// 6. If kind is BASE, then
if (ConstructorKind == ConstructorKind.BASE)
{
// a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
OrdinaryCallBindThis(vm, calleeContext, thisArgument);

// FIXME: b. Let initializeResult be Completion(InitializeInstanceElements(thisArgument, F)).
// FIXME: c. If initializeResult is an abrupt completion, then
// FIXME: i. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
// FIXME: ii. Return ? initializeResult.
}

// 7. Let constructorEnv be the LexicalEnvironment of calleeContext.
var constructorEnv = calleeContext.LexicalEnvironment;
Expand Down Expand Up @@ -127,48 +246,6 @@ public Completion Construct(VM vm, List argumentsList)
return Completion.NormalCompletion(Undefined.The);
}

// 10.2.1.1 PrepareForOrdinaryCall ( F, newTarget ), https://tc39.es/ecma262/#sec-prepareforordinarycall
private ExecutionContext PrepareForOrdinaryCall(VM vm, Object newTarget)
{
// 1. Let callerContext be the running execution context.
var callerContext = vm.CurrentExecutionContext;

// 2. Let calleeContext be a new ECMAScript code execution context.
// FIXME: 3. Set the Function of calleeContext to F.
// 4. Let calleeRealm be F.[[Realm]].
// 5. Set the Realm of calleeContext to calleeRealm.
// 6. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
var calleeContext = new ScriptExecutionContext(vm.Realm);

// 7. Let localEnv be NewFunctionEnvironment(F, newTarget).
var localEnv = new FunctionEnvironment(this, newTarget);

// 8. Set the LexicalEnvironment of calleeContext to localEnv.
calleeContext.LexicalEnvironment = localEnv;

// 9. Set the VariableEnvironment of calleeContext to localEnv.
calleeContext.VariableEnvironment = localEnv;

// FIXME: 10. Set the PrivateEnvironment of calleeContext to F.[[PrivateEnvironment]].

// FIXME: 11. If callerContext is not already suspended, suspend callerContext.

// 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
vm.PushExecutionContext(calleeContext);

// 13. NOTE: Any exception objects produced after this point are associated with calleeRealm.
// 14. Return calleeContext.
return calleeContext;
}

// 10.2.1.3 Runtime Semantics: EvaluateBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatebody
private Completion EvaluateBody(VM vm, List argumentsList)
{
// FIXME: Evaluate other types of functions when we implement them
// 1. Return ? EvaluateFunctionBody of FunctionBody with arguments functionObject and argumentsList.
return EvaluateFunctionBody(vm, argumentsList);
}

// 10.2.3 OrdinaryFunctionCreate ( FIXME: functionPrototype, FIXME: sourceText, ParameterList, Body, thisMode, env, FIXME: privateEnv ), https://tc39.es/ecma262/#sec-ordinaryfunctioncreate
static public FunctionObject OrdinaryFunctionCreate(IReadOnlyList<Identifier> parameterList, StatementList body, LexicalThisMode thisMode, Environment env)
{
Expand Down Expand Up @@ -234,13 +311,6 @@ private Completion EvaluateFunctionBody(VM vm, List argumentsList)
return ECMAScriptCode.Evaluate(vm);
}

// 10.2.1.4 OrdinaryCallEvaluateBody ( F, argumentsList ), https://tc39.es/ecma262/#sec-ordinarycallevaluatebody
private Completion OrdinaryCallEvaluateBody(VM vm, List argumentsList)
{
// 1. Return ? EvaluateBody of F.[[ECMAScriptCode]] with arguments F and argumentsList.
return EvaluateBody(vm, argumentsList);
}

// 10.2.5 MakeConstructor ( F [ , writablePrototype [ , prototype ] ] ), https://tc39.es/ecma262/#sec-makeconstructor
public void MakeConstructor(bool? writiablePrototype = null, Object? prototype = null)
{
Expand Down
2 changes: 1 addition & 1 deletion JSS.Lib/Execution/FunctionEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public FunctionEnvironment(FunctionObject F, Object newTarget) : base(F.Environm
public Completion BindThisValue(Value V)
{
// 1. Assert: envRec.[[ThisBindingStatus]] is not LEXICAL.
Debug.Assert(ThisBindingStatus == ThisBindingStatus.LEXICAL);
Debug.Assert(ThisBindingStatus != ThisBindingStatus.LEXICAL);

// 2. If envRec.[[ThisBindingStatus]] is INITIALIZED, throw a FIXKME: ReferenceError exception.
if (ThisBindingStatus == ThisBindingStatus.INITIALIZED)
Expand Down

0 comments on commit 8c4c520

Please sign in to comment.