From 14d14431cf9698d2d04c1c41b77b4301f015fd22 Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Sat, 1 Jul 2023 00:20:29 -0700 Subject: [PATCH] tmp: Be better about binding things to envrecords --- .../kotlin/com/reevajs/reeva/ast/structure.kt | 3 + .../core/lifecycle/SourceTextModuleRecord.kt | 42 +------- .../reevajs/reeva/interpreter/Interpreter.kt | 101 ++++++++++++++++-- .../kotlin/com/reevajs/reeva/parsing/Scope.kt | 1 - .../reevajs/reeva/parsing/ScopeResolver.kt | 2 +- .../reevajs/reeva/transformer/IRPrinter.kt | 2 + .../reevajs/reeva/transformer/Transformer.kt | 27 +++-- .../transformer/opcodes/OpcodeVisitor.kt | 6 ++ .../reeva/transformer/opcodes/opcodes.kt | 7 ++ 9 files changed, 131 insertions(+), 60 deletions(-) diff --git a/src/main/kotlin/com/reevajs/reeva/ast/structure.kt b/src/main/kotlin/com/reevajs/reeva/ast/structure.kt index 267cd3ad..0fb4282e 100644 --- a/src/main/kotlin/com/reevajs/reeva/ast/structure.kt +++ b/src/main/kotlin/com/reevajs/reeva/ast/structure.kt @@ -132,6 +132,9 @@ abstract class VariableSourceNode(sourceLocation: SourceLocation) : NodeWithScop lateinit var type: VariableType lateinit var mode: VariableMode + // Used by the Transformer + var isInitialized = false + abstract fun name(): String } diff --git a/src/main/kotlin/com/reevajs/reeva/core/lifecycle/SourceTextModuleRecord.kt b/src/main/kotlin/com/reevajs/reeva/core/lifecycle/SourceTextModuleRecord.kt index 7178866c..1ad27f75 100644 --- a/src/main/kotlin/com/reevajs/reeva/core/lifecycle/SourceTextModuleRecord.kt +++ b/src/main/kotlin/com/reevajs/reeva/core/lifecycle/SourceTextModuleRecord.kt @@ -114,46 +114,10 @@ class SourceTextModuleRecord(realm: Realm, val parsedSource: ParsedSource) : Cyc } } - // TODO: Why does this push an execution context only to remove it right after? - // 8. Let moduleContext be a new ECMAScript code execution context. - // 9. Set the Function of moduleContext to null. - // 10. Assert: module.[[Realm]] is not undefined. - // 11. Set the Realm of moduleContext to module.[[Realm]]. - // 12. Set the ScriptOrModule of moduleContext to module. - // 13. Set the VariableEnvironment of moduleContext to module.[[Environment]]. - // 14. Set the LexicalEnvironment of moduleContext to module.[[Environment]]. - // 15. Set the PrivateEnvironment of moduleContext to null. - // 16. Set module.[[Context]] to moduleContext. - // 17. Push moduleContext onto the execution context stack; moduleContext is now the running execution context. - // 18. Let code be module.[[ECMAScriptCode]]. - // 19. Let varDeclarations be the VarScopedDeclarations of code. - // 20. Let declaredVarNames be a new empty List. - // 21. For each element d of varDeclarations, do - // a. For each element dn of the BoundNames of d, do - // i. If dn is not an element of declaredVarNames, then - // 1. Perform ! env.CreateMutableBinding(dn, false). - // 2. Perform ! env.InitializeBinding(dn, undefined). - // 3. Append dn to declaredVarNames. - // 22. Let lexDeclarations be the LexicallyScopedDeclarations of code. - // 23. Let privateEnv be null. - // 24. For each element d of lexDeclarations, do - // a. For each element dn of the BoundNames of d, do - // i. If IsConstantDeclaration of d is true, then - // 1. Perform ! env.CreateImmutableBinding(dn, true). - // ii. Else, - // 1. Perform ! env.CreateMutableBinding(dn, false). - // iii. If d is a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then - // 1. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv. - // 2. Perform ! env.InitializeBinding(dn, fo). - // 25. Remove moduleContext from the execution context stack. - // 26. Return unused. - - // NOTE: Most of the above is not necessary, as GlobalDeclarationInstantiation is performed at the - // IR level. However, we do need to initialize exported variable names in the environment - - for (entry in moduleNode.localExportEntries) { + for (entry in moduleNode.localExportEntries) env.createImmutableBinding(entry.exportName!!, true) - } + + // NOTE: The rest of the algorithms are handled via the ModuleEnvironmentInitialization opcode } override fun resolveExport( diff --git a/src/main/kotlin/com/reevajs/reeva/interpreter/Interpreter.kt b/src/main/kotlin/com/reevajs/reeva/interpreter/Interpreter.kt index 2c787cc9..51f84503 100644 --- a/src/main/kotlin/com/reevajs/reeva/interpreter/Interpreter.kt +++ b/src/main/kotlin/com/reevajs/reeva/interpreter/Interpreter.kt @@ -4,6 +4,7 @@ import com.reevajs.reeva.ast.literals.MethodDefinitionNode import com.reevajs.reeva.core.Agent import com.reevajs.reeva.core.Realm import com.reevajs.reeva.core.environment.DeclarativeEnvRecord +import com.reevajs.reeva.core.environment.GlobalEnvRecord import com.reevajs.reeva.core.environment.ModuleEnvRecord import com.reevajs.reeva.core.errors.ThrowException import com.reevajs.reeva.parsing.HoistingScope @@ -611,7 +612,8 @@ class Interpreter( // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation override fun visitGlobalDeclarationInstantiation(opcode: GlobalDeclarationInstantiation) { - val env = realm.globalEnv + val env = agent.activeEnvRecord + require(env is GlobalEnvRecord) // 1. Let lexNames be the LexicallyDeclaredNames of script. val lexNames = opcode.scope.lexNames @@ -735,9 +737,81 @@ class Interpreter( // 18. Return unused. } + override fun visitModuleEnvironmentInitialization(opcode: ModuleEnvironmentInitialization) { + val env = agent.activeEnvRecord + require(env is ModuleEnvRecord) + + // NOTE: All previous steps are handled in SourceTextModuleRecord::initializeEnvironment() + + // TODO: Why does it require a new execution context? + // 8. Let moduleContext be a new ECMAScript code execution context. + // 9. Set the Function of moduleContext to null. + // 10. Assert: module.[[Realm]] is not undefined. + // 11. Set the Realm of moduleContext to module.[[Realm]]. + // 12. Set the ScriptOrModule of moduleContext to module. + // 13. Set the VariableEnvironment of moduleContext to module.[[Environment]]. + // 14. Set the LexicalEnvironment of moduleContext to module.[[Environment]]. + // 15. Set the PrivateEnvironment of moduleContext to null. + // 16. Set module.[[Context]] to moduleContext. + // 17. Push moduleContext onto the execution context stack; moduleContext is now the running execution context. + + // 18. Let code be module.[[ECMAScriptCode]]. + + // 19. Let varDeclarations be the VarScopedDeclarations of code. + val varDeclarations = opcode.scope.varNames + + // 20. Let declaredVarNames be a new empty List. + val declaredVarNames = mutableSetOf() + + // 21. For each element d of varDeclarations, do + // a. For each element dn of the BoundNames of d, do + for ((name, _) in varDeclarations) { + // i. If declaredVarNames does not contain dn, then + if (name !in declaredVarNames) { + // 1. Perform ! env.CreateMutableBinding(dn, false). + env.createMutableBinding(name, deletable = false) + + // 2. Perform ! env.InitializeBinding(dn, undefined). + env.initializeBinding(name, JSUndefined) + + // 3. Append dn to declaredVarNames. + declaredVarNames.add(name) + } + } + + // 22. Let lexDeclarations be the LexicallyScopedDeclarations of code. + val lexDeclarations = opcode.scope.lexNames + + // 23. Let privateEnv be null. + + // 24. For each element d of lexDeclarations, do + // a. For each element dn of the BoundNames of d, do + for ((name, isConst) in lexDeclarations) { + // i. If IsConstantDeclaration of d is true, then + if (isConst) { + // 1. Perform ! env.CreateImmutableBinding(dn, true). + env.createImmutableBinding(name, isStrict = true) + } + // ii. Else, + else { + // 1. Perform ! env.CreateMutableBinding(dn, false). + env.createMutableBinding(name, deletable = false) + } + + // iii. If d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an + // AsyncGeneratorDeclaration, then + // 1. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv. + // 2. Perform ! env.InitializeBinding(dn, fo). + // NOTE: This is handled by DeclareGlobalFunc + } + // 25. Remove moduleContext from the execution context stack. + // 26. Return unused. + } + // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation override fun visitDeclareGlobalFunc(opcode: DeclareGlobalFunc) { - val env = realm.globalEnv + val env = agent.activeEnvRecord + require(env is GlobalEnvRecord) // Snippet from GlobalDeclarationInstantiation: // 16. For each Parse Node f of functionsToInitialize, do @@ -753,8 +827,15 @@ class Interpreter( env.createGlobalFunctionBinding(fn, fo, deletable = false) } + // https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment + override fun visitDeclareModuleFunc(opcode: DeclareModuleFunc) { + val env = agent.activeEnvRecord + require(env is ModuleEnvRecord) + env.initializeBinding(opcode.name, popValue()) + } + override fun visitInitializeFunctionParameters(opcode: InitializeFunctionParameters) { - val env = agent.runningExecutionContext.envRecord!! + val env = agent.activeEnvRecord val hasDuplicates = opcode.parameterNames.duplicates().size != opcode.parameterNames.size for (paramName in opcode.parameterNames) { @@ -781,7 +862,7 @@ class Interpreter( } override fun visitInitializeFunctionVarBindings(opcode: InitializeFunctionVarBindings) { - val env = agent.runningExecutionContext.envRecord!! + val env = agent.activeEnvRecord for ((name, initializeWithValue) in opcode.varBindings) { env.createMutableBinding(name, deletable = false) @@ -795,7 +876,7 @@ class Interpreter( } override fun visitInitializeLexBindings(opcode: InitializeLexBindings) { - val env = agent.runningExecutionContext.envRecord!! + val env = agent.activeEnvRecord for ((name, isConst) in opcode.lexBindings) { if (isConst) { @@ -832,27 +913,27 @@ class Interpreter( } override fun visitLoadCurrentEnvName(opcode: LoadCurrentEnvName) { - push(agent.runningExecutionContext.envRecord!!.getBindingValue(opcode.name, opcode.isStrict)) + push(agent.activeEnvRecord.getBindingValue(opcode.name, opcode.isStrict)) } override fun visitStoreCurrentEnvName(opcode: StoreCurrentEnvName) { - agent.runningExecutionContext.envRecord!!.setMutableBinding(opcode.name, popValue(), opcode.isStrict) + agent.activeEnvRecord.setMutableBinding(opcode.name, popValue(), opcode.isStrict) } override fun visitLoadEnvName(opcode: LoadEnvName) { - var env = agent.runningExecutionContext.envRecord!! + var env = agent.activeEnvRecord repeat(opcode.distance) { env = env.outer!! } push(env.getBindingValue(opcode.name, opcode.isStrict)) } override fun visitStoreEnvName(opcode: StoreEnvName) { - var env = agent.runningExecutionContext.envRecord!! + var env = agent.activeEnvRecord repeat(opcode.distance) { env = env.outer!! } env.setMutableBinding(opcode.name, popValue(), opcode.isStrict) } override fun visitInitializeEnvName(opcode: InitializeEnvName) { - var env = agent.runningExecutionContext.envRecord!! + var env = agent.activeEnvRecord repeat(opcode.distance) { env = env.outer!! } env.initializeBinding(opcode.name, popValue()) } diff --git a/src/main/kotlin/com/reevajs/reeva/parsing/Scope.kt b/src/main/kotlin/com/reevajs/reeva/parsing/Scope.kt index e3b9fa95..ccdf755e 100644 --- a/src/main/kotlin/com/reevajs/reeva/parsing/Scope.kt +++ b/src/main/kotlin/com/reevajs/reeva/parsing/Scope.kt @@ -7,7 +7,6 @@ import com.reevajs.reeva.utils.unreachable open class Scope(val outer: Scope? = null) { val outerHoistingScope = outerScopeOfType() - val outerGlobalScope = outerScopeOfType() val childScopes = mutableListOf() diff --git a/src/main/kotlin/com/reevajs/reeva/parsing/ScopeResolver.kt b/src/main/kotlin/com/reevajs/reeva/parsing/ScopeResolver.kt index 916906f9..614086b1 100644 --- a/src/main/kotlin/com/reevajs/reeva/parsing/ScopeResolver.kt +++ b/src/main/kotlin/com/reevajs/reeva/parsing/ScopeResolver.kt @@ -29,8 +29,8 @@ class ScopeResolver : DefaultAstVisitor() { } is ModuleNode -> { globalScope.isIntrinsicallyStrict = true - node.body.forEach { it.accept(this) } scope = ModuleScope(globalScope) + node.body.forEach { it.accept(this) } } } diff --git a/src/main/kotlin/com/reevajs/reeva/transformer/IRPrinter.kt b/src/main/kotlin/com/reevajs/reeva/transformer/IRPrinter.kt index aa4531d1..fa292f32 100644 --- a/src/main/kotlin/com/reevajs/reeva/transformer/IRPrinter.kt +++ b/src/main/kotlin/com/reevajs/reeva/transformer/IRPrinter.kt @@ -56,7 +56,9 @@ object IRPrinter { is CreateClosure -> println(" ") is CreateGeneratorClosure -> println(" ") is DeclareGlobalFunc -> println(" ${opcode.name}") + is DeclareModuleFunc -> println(" ${opcode.name}") is GlobalDeclarationInstantiation -> printScope(opcode.scope) + is ModuleEnvironmentInitialization -> printScope(opcode.scope) is InitializeFunctionVarBindings -> println(" ${opcode.varBindings.joinToString { it.name }}") is InitializeLexBindings -> println(" ${opcode.lexBindings.joinToString { it.name }}") is LoadNamedProperty -> println(" \"${opcode.name}\"") diff --git a/src/main/kotlin/com/reevajs/reeva/transformer/Transformer.kt b/src/main/kotlin/com/reevajs/reeva/transformer/Transformer.kt index efc65ade..3118f3f2 100644 --- a/src/main/kotlin/com/reevajs/reeva/transformer/Transformer.kt +++ b/src/main/kotlin/com/reevajs/reeva/transformer/Transformer.kt @@ -5,9 +5,7 @@ import com.reevajs.reeva.ast.expressions.* import com.reevajs.reeva.ast.literals.* import com.reevajs.reeva.ast.statements.* import com.reevajs.reeva.core.Realm -import com.reevajs.reeva.parsing.HoistingScope -import com.reevajs.reeva.parsing.ParsedSource -import com.reevajs.reeva.parsing.Scope +import com.reevajs.reeva.parsing.* import com.reevajs.reeva.parsing.lexer.SourceLocation import com.reevajs.reeva.runtime.functions.JSFunction import com.reevajs.reeva.transformer.opcodes.* @@ -28,7 +26,7 @@ class Transformer(val parsedSource: ParsedSource) : AstVisitor { builder = IRBuilder(RESERVED_LOCALS_COUNT, 0) - globalDeclarationInstantiation(rootNode.scope.outerGlobalScope as HoistingScope, isEval) { + globalDeclarationInstantiation(rootNode.scope as HoistingScope, isEval) { rootNode.children.forEach { it.accept(this) } if (!builder.activeBlockReturns()) { @@ -81,7 +79,12 @@ class Transformer(val parsedSource: ParsedSource) : AstVisitor { currentScope = scope val irScope = buildIRScope(scope) - +GlobalDeclarationInstantiation(irScope) + if (scope is GlobalScope) { + +GlobalDeclarationInstantiation(irScope) + } else { + require(scope is ModuleScope) + +ModuleEnvironmentInitialization(irScope) + } val functions = scope.variableSources .filterIsInstance() @@ -91,7 +94,11 @@ class Transformer(val parsedSource: ParsedSource) : AstVisitor { for (function in functions) { functionDeclarationInstantiation(function) - +DeclareGlobalFunc(function.identifier!!.processedName) + if (scope is GlobalScope) { + +DeclareGlobalFunc(function.identifier!!.processedName) + } else { + +DeclareModuleFunc(function.identifier!!.processedName) + } } block() @@ -139,7 +146,7 @@ class Transformer(val parsedSource: ParsedSource) : AstVisitor { builder = IRBuilder(parameterNames.size + RESERVED_LOCALS_COUNT, functionScope.inlineableLocalCount) enterScope(functionScope) - if (parameterNames.isNotEmpty()) + if (parameterNames.isNotEmpty() || argumentsObjectNeeded) +InitializeFunctionParameters(parameterNames, argumentsMode, strict) // Assign parameter values @@ -318,7 +325,8 @@ class Transformer(val parsedSource: ParsedSource) : AstVisitor { for (decl in lexDecls) lexBindings.add(LexBinding(decl.name(), decl.type == VariableType.Const)) - +InitializeLexBindings(lexBindings) + if (lexBindings.isNotEmpty()) + +InitializeLexBindings(lexBindings) for (decl in funcDecls) { functionDeclarationInstantiation(decl) @@ -384,8 +392,9 @@ class Transformer(val parsedSource: ParsedSource) : AstVisitor { val distance = currentScope!!.envDistanceFrom(source.scope) val name = source.name() - if (source.type != VariableType.Var) { + if (source.type != VariableType.Var && !source.isInitialized) { +InitializeEnvName(name, distance) + source.isInitialized = true } else { if (distance == 0) { +StoreCurrentEnvName(name, source.scope.isStrict) diff --git a/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/OpcodeVisitor.kt b/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/OpcodeVisitor.kt index 9d2a5c1b..59c2ee51 100644 --- a/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/OpcodeVisitor.kt +++ b/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/OpcodeVisitor.kt @@ -68,10 +68,12 @@ interface OpcodeVisitor { is Construct -> visitConstruct(opcode) ConstructArray -> visitConstructArray() is GlobalDeclarationInstantiation -> visitGlobalDeclarationInstantiation(opcode) + is ModuleEnvironmentInitialization -> visitModuleEnvironmentInitialization(opcode) is InitializeFunctionParameters -> visitInitializeFunctionParameters(opcode) is InitializeFunctionVarBindings -> visitInitializeFunctionVarBindings(opcode) is InitializeLexBindings -> visitInitializeLexBindings(opcode) is DeclareGlobalFunc -> visitDeclareGlobalFunc(opcode) + is DeclareModuleFunc -> visitDeclareModuleFunc(opcode) is PushDeclarativeEnvRecord -> visitPushDeclarativeEnvRecord(opcode) is PushModuleEnvRecord -> visitPushModuleEnvRecord() PopEnvRecord -> visitPopEnvRecord() @@ -257,6 +259,8 @@ interface OpcodeVisitor { fun visitGlobalDeclarationInstantiation(opcode: GlobalDeclarationInstantiation) + fun visitModuleEnvironmentInitialization(opcode: ModuleEnvironmentInitialization) + fun visitInitializeFunctionParameters(opcode: InitializeFunctionParameters) fun visitInitializeFunctionVarBindings(opcode: InitializeFunctionVarBindings) @@ -265,6 +269,8 @@ interface OpcodeVisitor { fun visitDeclareGlobalFunc(opcode: DeclareGlobalFunc) + fun visitDeclareModuleFunc(opcode: DeclareModuleFunc) + fun visitPushDeclarativeEnvRecord(opcode: PushDeclarativeEnvRecord) fun visitPushModuleEnvRecord() diff --git a/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/opcodes.kt b/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/opcodes.kt index 8c58969a..f3dbb191 100644 --- a/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/opcodes.kt +++ b/src/main/kotlin/com/reevajs/reeva/transformer/opcodes/opcodes.kt @@ -462,6 +462,8 @@ object ConstructArray : Opcode(-2) class GlobalDeclarationInstantiation(val scope: IRScope) : Opcode(0) +class ModuleEnvironmentInitialization(val scope: IRScope) : Opcode(0) + class InitializeFunctionParameters( val parameterNames: List, val argumentsMode: HoistingScope.ArgumentsMode, @@ -484,6 +486,11 @@ class InitializeLexBindings( */ class DeclareGlobalFunc(val name: String) : Opcode(-1) +/** + * Declare a module function. Takes a function off the stack + */ +class DeclareModuleFunc(val name: String) : Opcode(-1) + /** * Creates a new DeclarativeEnvRecord with the current EnvRecord as its parent, * and sets the new record as the active EnvRecord. If slotCount is null, this