From 99a26d50f510c83f10c266db2edc000a06d5acf5 Mon Sep 17 00:00:00 2001 From: Preston Taylor <95388976+PrestonLTaylor@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:45:49 +0100 Subject: [PATCH] feat: Implement execution of function expressions --- JSS.Lib.UnitTests/ASTTests.cs | 14 ++++++ JSS.Lib/AST/FunctionExpression.cs | 80 ++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/JSS.Lib.UnitTests/ASTTests.cs b/JSS.Lib.UnitTests/ASTTests.cs index 9e60a3b..a5658b5 100644 --- a/JSS.Lib.UnitTests/ASTTests.cs +++ b/JSS.Lib.UnitTests/ASTTests.cs @@ -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}"; diff --git a/JSS.Lib/AST/FunctionExpression.cs b/JSS.Lib/AST/FunctionExpression.cs index b455d6c..777ba7b 100644 --- a/JSS.Lib/AST/FunctionExpression.cs +++ b/JSS.Lib/AST/FunctionExpression.cs @@ -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 @@ -10,7 +13,80 @@ public FunctionExpression(string? identifier, List 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 Parameters { get; }