Skip to content

Commit

Permalink
tmp: Be better about binding things to envrecords
Browse files Browse the repository at this point in the history
  • Loading branch information
mattco98 committed Jul 1, 2023
1 parent 1edbda2 commit 14d1443
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 60 deletions.
3 changes: 3 additions & 0 deletions src/main/kotlin/com/reevajs/reeva/ast/structure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
101 changes: 91 additions & 10 deletions src/main/kotlin/com/reevajs/reeva/interpreter/Interpreter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<String>()

// 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
Expand All @@ -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) {
Expand All @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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())
}
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/com/reevajs/reeva/parsing/Scope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import com.reevajs.reeva.utils.unreachable

open class Scope(val outer: Scope? = null) {
val outerHoistingScope = outerScopeOfType<HoistingScope>()
val outerGlobalScope = outerScopeOfType<GlobalScope>()

val childScopes = mutableListOf<Scope>()

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/reevajs/reeva/parsing/ScopeResolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/reevajs/reeva/transformer/IRPrinter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ object IRPrinter {
is CreateClosure -> println(" <FunctionInfo ${opcode.ir.name}>")
is CreateGeneratorClosure -> println(" <FunctionInfo ${opcode.ir.name}>")
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}\"")
Expand Down
27 changes: 18 additions & 9 deletions src/main/kotlin/com/reevajs/reeva/transformer/Transformer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand All @@ -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()) {
Expand Down Expand Up @@ -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<FunctionDeclarationNode>()
Expand All @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -257,6 +259,8 @@ interface OpcodeVisitor {

fun visitGlobalDeclarationInstantiation(opcode: GlobalDeclarationInstantiation)

fun visitModuleEnvironmentInitialization(opcode: ModuleEnvironmentInitialization)

fun visitInitializeFunctionParameters(opcode: InitializeFunctionParameters)

fun visitInitializeFunctionVarBindings(opcode: InitializeFunctionVarBindings)
Expand All @@ -265,6 +269,8 @@ interface OpcodeVisitor {

fun visitDeclareGlobalFunc(opcode: DeclareGlobalFunc)

fun visitDeclareModuleFunc(opcode: DeclareModuleFunc)

fun visitPushDeclarativeEnvRecord(opcode: PushDeclarativeEnvRecord)

fun visitPushModuleEnvRecord()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
val argumentsMode: HoistingScope.ArgumentsMode,
Expand All @@ -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
Expand Down

0 comments on commit 14d1443

Please sign in to comment.