Skip to content

Commit

Permalink
feat: Implement execution of function expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
PrestonLTaylor committed Apr 18, 2024
1 parent 38ce2d0 commit 99a26d5
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 2 deletions.
14 changes: 14 additions & 0 deletions JSS.Lib.UnitTests/ASTTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,20 @@ public void ConstDeclarations_InsideFunctionDeclaration_DontEscapeToOuterScope()
completion.Value.Should().Be(new String($"{identifier} is not defined"));
}

[Test]
public void AssignmentExpreesion_ToFunctionExpression_SetsIdentifier_ToFunction()
{
// Arrange
var script = ParseScript("a = function() { return 1 }; a()");

// Act
var completion = script.ScriptEvaluation();

// Assert
completion.IsNormalCompletion().Should().BeTrue();
completion.Value.Should().Be(new Number(1));
}

static private string EscapeString(string toEscape, char quote = '"')
{
return $"{quote}{toEscape}{quote}";
Expand Down
80 changes: 78 additions & 2 deletions JSS.Lib/AST/FunctionExpression.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace JSS.Lib.AST;
using JSS.Lib.AST.Values;
using JSS.Lib.Execution;

namespace JSS.Lib.AST;

// 15.2 Function Definitions, https://tc39.es/ecma262/#sec-function-definitions
internal sealed class FunctionExpression : IExpression
Expand All @@ -10,7 +13,80 @@ public FunctionExpression(string? identifier, List<Identifier> parameters, State
Body = body;
}

// FIXME: 15.2.6 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluation
// 15.2.6 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluation
public override Completion Evaluate(VM vm)
{
// 1. Return InstantiateOrdinaryFunctionExpression of FunctionExpression.
if (Identifier is null)
{
return InstantiateOrdinaryFunctionExpressionWithNoName(vm);
}
else
{
return InstantiateOrdinaryFunctionExpression(vm);
}
}

// 15.2.5 Runtime Semantics: InstantiateOrdinaryFunctionExpression, https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression
private Completion InstantiateOrdinaryFunctionExpressionWithNoName(VM vm, string? name = null)
{
// 1. If name is not present, set name to "".
name ??= "";

// 2. Let env be the LexicalEnvironment of the running execution context.
var env = (vm.CurrentExecutionContext as ScriptExecutionContext)!.LexicalEnvironment;

// FIXME: 3. Let privateEnv be the running execution context's PrivateEnvironment.

// FIXME: 4. Let sourceText be the source text matched by FunctionExpression.

// 5. Let closure be OrdinaryFunctionCreate(%Function.prototype%, sourceText, FormalParameters, FunctionBody, NON-LEXICAL-THIS, env, privateEnv).
var closure = FunctionObject.OrdinaryFunctionCreate(Parameters, Body, LexicalThisMode.NON_LEXICAL_THIS, env!);

// 6. Perform SetFunctionName(closure, name).
closure.SetFunctionName(name);

// 7. Perform MakeConstructor(closure).
closure.MakeConstructor();

// 8. Return closure.
return closure;
}

private Completion InstantiateOrdinaryFunctionExpression(VM vm)
{
// 1. Assert: name is not present.
// 2. Set name to StringValue of BindingIdentifier.
var name = Identifier!;

// 3. Let outerEnv be the running execution context's LexicalEnvironment.
var outerEnv = (vm.CurrentExecutionContext as ScriptExecutionContext)!.LexicalEnvironment;

// 4. Let funcEnv be NewDeclarativeEnvironment(outerEnv).
var funcEnv = new DeclarativeEnvironment(outerEnv);

// 5. Perform ! funcEnv.CreateImmutableBinding(name, false).
MUST(funcEnv.CreateImmutableBinding(name, false));

// FIXME: 6. Let privateEnv be the running execution context's PrivateEnvironment.

// FIXME: 7. Let sourceText be the source text matched by FunctionExpression.

// 8. Let closure be OrdinaryFunctionCreate(%Function.prototype%, sourceText, FormalParameters, FunctionBody, NON-LEXICAL-THIS, funcEnv, privateEnv).
var closure = FunctionObject.OrdinaryFunctionCreate(Parameters, Body, LexicalThisMode.NON_LEXICAL_THIS, funcEnv);

// 9. Perform SetFunctionName(closure, name).
closure.SetFunctionName(name);

// 10. Perform MakeConstructor(closure).
closure.MakeConstructor();

// 11. Perform ! funcEnv.InitializeBinding(name, closure).
MUST(funcEnv.InitializeBinding(name, closure));

// 12. Return closure.
return closure;
}

public string? Identifier { get; }
public IReadOnlyList<Identifier> Parameters { get; }
Expand Down

0 comments on commit 99a26d5

Please sign in to comment.