Compare commits
No commits in common. "ab39110834fd3a41919827a1cf73433095fc7369" and "a1ea09440d5837f2ea0b27cc10dcffda4d8eb2a6" have entirely different histories.
ab39110834
...
a1ea09440d
@ -1,137 +0,0 @@
|
|||||||
package net.sergeych
|
|
||||||
|
|
||||||
import net.sergeych.lyng.EvalSession
|
|
||||||
import net.sergeych.lyng.Source
|
|
||||||
import net.sergeych.lyng.obj.ObjString
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.nio.file.Files
|
|
||||||
import kotlin.io.path.writeText
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class CliLocalModuleImportRegressionJvmTest {
|
|
||||||
|
|
||||||
private fun writeTransitiveImportTree(root: java.nio.file.Path) {
|
|
||||||
val packageDir = Files.createDirectories(root.resolve("package1"))
|
|
||||||
val nestedDir = Files.createDirectories(packageDir.resolve("nested"))
|
|
||||||
|
|
||||||
packageDir.resolve("alpha.lyng").writeText(
|
|
||||||
"""
|
|
||||||
package package1.alpha
|
|
||||||
|
|
||||||
import lyng.stdlib
|
|
||||||
import lyng.io.net
|
|
||||||
|
|
||||||
class Alpha {
|
|
||||||
val headers = Map<String, String>()
|
|
||||||
|
|
||||||
fun makeTask(port: Int, host: String): Deferred = launch {
|
|
||||||
host + ":" + port
|
|
||||||
}
|
|
||||||
|
|
||||||
fun netModule() = Net
|
|
||||||
}
|
|
||||||
|
|
||||||
fun alphaValue() = "alpha"
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
packageDir.resolve("beta.lyng").writeText(
|
|
||||||
"""
|
|
||||||
package package1.beta
|
|
||||||
|
|
||||||
import lyng.stdlib
|
|
||||||
import package1.alpha
|
|
||||||
|
|
||||||
fun betaValue() = alphaValue() + "|beta"
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
nestedDir.resolve("gamma.lyng").writeText(
|
|
||||||
"""
|
|
||||||
package package1.nested.gamma
|
|
||||||
|
|
||||||
import lyng.io.net
|
|
||||||
import package1.alpha
|
|
||||||
import package1.beta
|
|
||||||
|
|
||||||
val String.gammaTag get() = this + "|gamma"
|
|
||||||
|
|
||||||
fun gammaValue() = betaValue().gammaTag
|
|
||||||
fun netModule() = Net
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
packageDir.resolve("entry.lyng").writeText(
|
|
||||||
"""
|
|
||||||
package package1.entry
|
|
||||||
|
|
||||||
import lyng.stdlib
|
|
||||||
import lyng.io.net
|
|
||||||
import package1.alpha
|
|
||||||
import package1.beta
|
|
||||||
import package1.nested.gamma
|
|
||||||
|
|
||||||
fun report() = gammaValue() + "|entry"
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun localModuleUsingLaunchAndNetImportsWithoutStdlibRedefinition() = runBlocking {
|
|
||||||
val root = Files.createTempDirectory("lyng-cli-import-regression")
|
|
||||||
try {
|
|
||||||
val mainFile = root.resolve("main.lyng")
|
|
||||||
writeTransitiveImportTree(root)
|
|
||||||
mainFile.writeText(
|
|
||||||
"""
|
|
||||||
import package1.entry
|
|
||||||
import package1.beta
|
|
||||||
import package1.nested.gamma
|
|
||||||
|
|
||||||
println(report())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
|
|
||||||
executeFile(mainFile.toString(), emptyList())
|
|
||||||
} finally {
|
|
||||||
root.toFile().deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun localModuleImportsAreNoOpsWhenEvaldRepeatedlyOnSameCliContext() = runBlocking {
|
|
||||||
val root = Files.createTempDirectory("lyng-cli-import-regression-repeat")
|
|
||||||
try {
|
|
||||||
val mainFile = root.resolve("main.lyng")
|
|
||||||
writeTransitiveImportTree(root)
|
|
||||||
mainFile.writeText("println(\"bootstrap\")")
|
|
||||||
|
|
||||||
val session = EvalSession(newCliScope(emptyList(), mainFile.toString()))
|
|
||||||
try {
|
|
||||||
repeat(5) { index ->
|
|
||||||
val result = evalOnCliDispatcher(
|
|
||||||
session,
|
|
||||||
Source(
|
|
||||||
"<repeat-local-import-$index>",
|
|
||||||
"""
|
|
||||||
import package1.entry
|
|
||||||
import package1.nested.gamma
|
|
||||||
import package1.beta
|
|
||||||
import package1.alpha
|
|
||||||
|
|
||||||
report()
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
) as ObjString
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
"alpha|beta|gamma|entry",
|
|
||||||
result.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
session.cancelAndJoin()
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
root.toFile().deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -64,73 +64,6 @@ class CliLocalImportsJvmTest {
|
|||||||
return CliResult(outBuf.toString("UTF-8"), errBuf.toString("UTF-8"), exitCode)
|
return CliResult(outBuf.toString("UTF-8"), errBuf.toString("UTF-8"), exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeTransitiveImportTree(root: java.nio.file.Path) {
|
|
||||||
val packageDir = Files.createDirectories(root.resolve("package1"))
|
|
||||||
val nestedDir = Files.createDirectories(packageDir.resolve("nested"))
|
|
||||||
|
|
||||||
Files.writeString(
|
|
||||||
packageDir.resolve("alpha.lyng"),
|
|
||||||
"""
|
|
||||||
package package1.alpha
|
|
||||||
|
|
||||||
import lyng.stdlib
|
|
||||||
import lyng.io.net
|
|
||||||
|
|
||||||
class Alpha {
|
|
||||||
val headers = Map<String, String>()
|
|
||||||
|
|
||||||
fun makeTask(port: Int, host: String): Deferred = launch {
|
|
||||||
host + ":" + port
|
|
||||||
}
|
|
||||||
|
|
||||||
fun netModule() = Net
|
|
||||||
}
|
|
||||||
|
|
||||||
fun alphaValue() = "alpha"
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
Files.writeString(
|
|
||||||
packageDir.resolve("beta.lyng"),
|
|
||||||
"""
|
|
||||||
package package1.beta
|
|
||||||
|
|
||||||
import lyng.stdlib
|
|
||||||
import package1.alpha
|
|
||||||
|
|
||||||
fun betaValue() = alphaValue() + "|beta"
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
Files.writeString(
|
|
||||||
nestedDir.resolve("gamma.lyng"),
|
|
||||||
"""
|
|
||||||
package package1.nested.gamma
|
|
||||||
|
|
||||||
import lyng.io.net
|
|
||||||
import package1.alpha
|
|
||||||
import package1.beta
|
|
||||||
|
|
||||||
val String.gammaTag get() = this + "|gamma"
|
|
||||||
|
|
||||||
fun gammaValue() = betaValue().gammaTag
|
|
||||||
fun netModule() = Net
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
Files.writeString(
|
|
||||||
packageDir.resolve("entry.lyng"),
|
|
||||||
"""
|
|
||||||
package package1.entry
|
|
||||||
|
|
||||||
import lyng.stdlib
|
|
||||||
import lyng.io.net
|
|
||||||
import package1.alpha
|
|
||||||
import package1.beta
|
|
||||||
import package1.nested.gamma
|
|
||||||
|
|
||||||
fun report() = gammaValue() + "|entry"
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun cliDiscoversSiblingAndNestedLocalImportsFromEntryRoot() {
|
fun cliDiscoversSiblingAndNestedLocalImportsFromEntryRoot() {
|
||||||
val dir = Files.createTempDirectory("lyng_cli_local_imports_")
|
val dir = Files.createTempDirectory("lyng_cli_local_imports_")
|
||||||
@ -201,37 +134,4 @@ class CliLocalImportsJvmTest {
|
|||||||
dir.toFile().deleteRecursively()
|
dir.toFile().deleteRecursively()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun cliHandlesOverlappingDirectoryImportsWithTransitiveStdlibAndNetSymbols() {
|
|
||||||
val dir = Files.createTempDirectory("lyng_cli_local_imports_transitive_")
|
|
||||||
try {
|
|
||||||
val mainFile = dir.resolve("main.lyng")
|
|
||||||
writeTransitiveImportTree(dir)
|
|
||||||
Files.writeString(
|
|
||||||
mainFile,
|
|
||||||
"""
|
|
||||||
import package1.entry
|
|
||||||
import package1.beta
|
|
||||||
import package1.nested.gamma
|
|
||||||
|
|
||||||
println(report())
|
|
||||||
println(gammaValue())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
|
|
||||||
val result = runCli(mainFile.toString())
|
|
||||||
assertTrue(result.err, result.err.isBlank())
|
|
||||||
assertTrue(
|
|
||||||
result.out,
|
|
||||||
result.out.contains("alpha|beta|gamma|entry")
|
|
||||||
)
|
|
||||||
assertTrue(
|
|
||||||
result.out,
|
|
||||||
result.out.contains("alpha|beta|gamma")
|
|
||||||
)
|
|
||||||
} finally {
|
|
||||||
dir.toFile().deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3436,12 +3436,12 @@ class Compiler(
|
|||||||
val resolvedRecords = ArrayList<ObjRecord>(captureSlots.size)
|
val resolvedRecords = ArrayList<ObjRecord>(captureSlots.size)
|
||||||
val resolvedNames = ArrayList<String>(captureSlots.size)
|
val resolvedNames = ArrayList<String>(captureSlots.size)
|
||||||
for (capture in captureSlots) {
|
for (capture in captureSlots) {
|
||||||
val rec = resolveStableCaptureRecord(
|
val rec = closureScope.chainLookupIgnoreClosure(
|
||||||
closureScope,
|
|
||||||
capture.name,
|
capture.name,
|
||||||
context.currentClassCtx
|
followClosure = true,
|
||||||
|
caller = context.currentClassCtx
|
||||||
) ?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
) ?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
resolvedRecords.add(freezeImmutableCaptureRecord(rec))
|
resolvedRecords.add(rec)
|
||||||
resolvedNames.add(capture.name)
|
resolvedNames.add(capture.name)
|
||||||
}
|
}
|
||||||
context.captureRecords = resolvedRecords
|
context.captureRecords = resolvedRecords
|
||||||
@ -8766,46 +8766,6 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun freezeImmutableCaptureRecord(record: ObjRecord): ObjRecord {
|
|
||||||
val value = record.value as Obj?
|
|
||||||
if (record.isMutable || record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || value is ObjProperty) {
|
|
||||||
return record
|
|
||||||
}
|
|
||||||
return when (value) {
|
|
||||||
is FrameSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
|
||||||
is RecordSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
|
||||||
is ScopeSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
|
||||||
null -> record
|
|
||||||
else -> record.copy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isTransientCapturePlaceholder(value: Obj?): Boolean {
|
|
||||||
return when (value) {
|
|
||||||
null, ObjVoid -> true
|
|
||||||
is FrameSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid }
|
|
||||||
is RecordSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid }
|
|
||||||
is ScopeSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid }
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveStableCaptureRecord(scope: Scope, name: String, caller: ObjClass?): ObjRecord? {
|
|
||||||
val direct = scope.chainLookupIgnoreClosure(name, followClosure = true, caller = caller) ?: scope.get(name)
|
|
||||||
if (direct != null && !isTransientCapturePlaceholder(direct.value as Obj?)) {
|
|
||||||
return direct
|
|
||||||
}
|
|
||||||
var parent = scope.parent
|
|
||||||
while (parent != null) {
|
|
||||||
val candidate = parent.chainLookupIgnoreClosure(name, followClosure = true, caller = caller) ?: parent.get(name)
|
|
||||||
if (candidate != null && !isTransientCapturePlaceholder(candidate.value as Obj?)) {
|
|
||||||
return candidate
|
|
||||||
}
|
|
||||||
parent = parent.parent
|
|
||||||
}
|
|
||||||
return direct
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun parseFunctionDeclaration(
|
private suspend fun parseFunctionDeclaration(
|
||||||
visibility: Visibility = Visibility.Public,
|
visibility: Visibility = Visibility.Public,
|
||||||
isAbstract: Boolean = false,
|
isAbstract: Boolean = false,
|
||||||
@ -9187,12 +9147,12 @@ class Compiler(
|
|||||||
} else if (captureBase != null && captureNames.isNotEmpty()) {
|
} else if (captureBase != null && captureNames.isNotEmpty()) {
|
||||||
val resolvedRecords = ArrayList<ObjRecord>(captureNames.size)
|
val resolvedRecords = ArrayList<ObjRecord>(captureNames.size)
|
||||||
for (name in captureNames) {
|
for (name in captureNames) {
|
||||||
val rec = resolveStableCaptureRecord(
|
val rec = captureBase.chainLookupIgnoreClosure(
|
||||||
captureBase,
|
|
||||||
name,
|
name,
|
||||||
context.currentClassCtx
|
followClosure = true,
|
||||||
|
caller = context.currentClassCtx
|
||||||
) ?: captureBase.raiseSymbolNotFound("symbol $name not found")
|
) ?: captureBase.raiseSymbolNotFound("symbol $name not found")
|
||||||
resolvedRecords.add(freezeImmutableCaptureRecord(rec))
|
resolvedRecords.add(rec)
|
||||||
}
|
}
|
||||||
context.captureRecords = resolvedRecords
|
context.captureRecords = resolvedRecords
|
||||||
context.captureNames = captureNames
|
context.captureNames = captureNames
|
||||||
@ -9233,12 +9193,7 @@ class Compiler(
|
|||||||
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
||||||
context.resolve(record, localName)
|
context.resolve(record, localName)
|
||||||
} else {
|
} else {
|
||||||
when (val direct = record.value) {
|
record.value
|
||||||
is FrameSlotRef -> direct.read()
|
|
||||||
is RecordSlotRef -> direct.read(context, localName)
|
|
||||||
is ScopeSlotRef -> direct.read()
|
|
||||||
else -> direct
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
frame.frame.setObj(i, value)
|
frame.frame.setObj(i, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,13 +79,6 @@ class FrameSlotRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun resolvedCaptureValueOrNull(): Obj? {
|
|
||||||
return when (frame.getSlotTypeCode(slot)) {
|
|
||||||
SlotType.INT.code, SlotType.REAL.code, SlotType.BOOL.code -> read()
|
|
||||||
else -> peekValue()?.let { read() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(value: Obj) {
|
fun write(value: Obj) {
|
||||||
when (value) {
|
when (value) {
|
||||||
is ObjInt -> frame.setInt(slot, value.value)
|
is ObjInt -> frame.setInt(slot, value.value)
|
||||||
@ -139,16 +132,6 @@ class ScopeSlotRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun resolvedCaptureValueOrNull(): Obj? {
|
|
||||||
val record = scope.getSlotRecord(slot)
|
|
||||||
return when (val direct = record.value as Obj?) {
|
|
||||||
is FrameSlotRef -> direct.resolvedCaptureValueOrNull()
|
|
||||||
is RecordSlotRef -> direct.resolvedCaptureValueOrNull()
|
|
||||||
is ScopeSlotRef -> direct.resolvedCaptureValueOrNull()
|
|
||||||
else -> direct
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(value: Obj) {
|
fun write(value: Obj) {
|
||||||
scope.setSlotValue(slot, value)
|
scope.setSlotValue(slot, value)
|
||||||
}
|
}
|
||||||
@ -215,15 +198,6 @@ class RecordSlotRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun resolvedCaptureValueOrNull(): Obj? {
|
|
||||||
return when (val direct = record.value as Obj?) {
|
|
||||||
is FrameSlotRef -> direct.resolvedCaptureValueOrNull()
|
|
||||||
is RecordSlotRef -> direct.resolvedCaptureValueOrNull()
|
|
||||||
is ScopeSlotRef -> direct.resolvedCaptureValueOrNull()
|
|
||||||
else -> direct
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(value: Obj) {
|
fun write(value: Obj) {
|
||||||
val direct = record.value
|
val direct = record.value
|
||||||
if (direct is ScopeSlotRef) {
|
if (direct is ScopeSlotRef) {
|
||||||
|
|||||||
@ -32,7 +32,6 @@ class ModuleScope(
|
|||||||
pos: Pos = Pos.builtIn,
|
pos: Pos = Pos.builtIn,
|
||||||
override val packageName: String
|
override val packageName: String
|
||||||
) : Scope(importProvider.rootScope, Arguments.EMPTY, pos) {
|
) : Scope(importProvider.rootScope, Arguments.EMPTY, pos) {
|
||||||
private fun ObjRecord.importedCopy(source: Scope): ObjRecord = copy(importedFrom = source)
|
|
||||||
|
|
||||||
constructor(importProvider: ImportProvider, source: Source) : this(importProvider, source.startPos, source.fileName)
|
constructor(importProvider: ImportProvider, source: Source) : this(importProvider, source.startPos, source.fileName)
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ class ModuleScope(
|
|||||||
override suspend fun importInto(scope: Scope, symbols: Map<String, String>?) {
|
override suspend fun importInto(scope: Scope, symbols: Map<String, String>?) {
|
||||||
val symbolsToImport = symbols?.keys?.toMutableSet()
|
val symbolsToImport = symbols?.keys?.toMutableSet()
|
||||||
for ((symbol, record) in this.objects) {
|
for ((symbol, record) in this.objects) {
|
||||||
if (record.visibility.isPublic && record.importedFrom == null) {
|
if (record.visibility.isPublic) {
|
||||||
val newName = symbols?.let { ss: Map<String, String> ->
|
val newName = symbols?.let { ss: Map<String, String> ->
|
||||||
ss[symbol]
|
ss[symbol]
|
||||||
?.also { symbolsToImport!!.remove(symbol) }
|
?.also { symbolsToImport!!.remove(symbol) }
|
||||||
@ -95,21 +94,21 @@ class ModuleScope(
|
|||||||
if (newName != null) {
|
if (newName != null) {
|
||||||
val existing = scope.objects[newName]
|
val existing = scope.objects[newName]
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
val sameBinding = existing.importedFrom == this
|
if (existing.importedFrom != record.importedFrom)
|
||||||
if (!sameBinding)
|
|
||||||
scope.raiseError("symbol ${existing.importedFrom?.packageName}.$newName already exists, redefinition on import is not allowed")
|
scope.raiseError("symbol ${existing.importedFrom?.packageName}.$newName already exists, redefinition on import is not allowed")
|
||||||
// already imported
|
// already imported
|
||||||
} else {
|
} else {
|
||||||
val imported = record.importedCopy(this)
|
// when importing records, we keep track of its package (not otherwise needed)
|
||||||
scope.objects[newName] = imported
|
if (record.importedFrom == null) record.importedFrom = this
|
||||||
scope.updateSlotFor(newName, imported)
|
scope.objects[newName] = record
|
||||||
|
scope.updateSlotFor(newName, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for ((cls, map) in this.extensions) {
|
for ((cls, map) in this.extensions) {
|
||||||
for ((symbol, record) in map) {
|
for ((symbol, record) in map) {
|
||||||
if (record.visibility.isPublic && record.importedFrom == null) {
|
if (record.visibility.isPublic) {
|
||||||
val newName = symbols?.let { ss: Map<String, String> ->
|
val newName = symbols?.let { ss: Map<String, String> ->
|
||||||
ss[symbol]
|
ss[symbol]
|
||||||
?.also { symbolsToImport!!.remove(symbol) }
|
?.also { symbolsToImport!!.remove(symbol) }
|
||||||
@ -117,7 +116,7 @@ class ModuleScope(
|
|||||||
} ?: if (symbols == null) symbol else null
|
} ?: if (symbols == null) symbol else null
|
||||||
|
|
||||||
if (newName != null) {
|
if (newName != null) {
|
||||||
scope.addExtension(cls, newName, record.importedCopy(this))
|
scope.addExtension(cls, newName, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,42 +181,37 @@ class Script(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importedBindingRecord(record: ObjRecord, source: Scope): ObjRecord =
|
|
||||||
record.copy(importedFrom = source)
|
|
||||||
|
|
||||||
private suspend fun seedImportBindings(scope: Scope, seedScope: Scope) {
|
private suspend fun seedImportBindings(scope: Scope, seedScope: Scope) {
|
||||||
val provider = scope.currentImportProvider
|
val provider = scope.currentImportProvider
|
||||||
val importedModules = LinkedHashSet<ModuleScope>()
|
val importedModules = LinkedHashSet<ModuleScope>()
|
||||||
for (moduleRef in this.importedModules) {
|
for (moduleRef in this.importedModules) {
|
||||||
importedModules.add(provider.prepareImport(moduleRef.pos, moduleRef.name, null))
|
importedModules.add(provider.prepareImport(moduleRef.pos, moduleRef.name, null))
|
||||||
}
|
}
|
||||||
|
if (scope is ModuleScope) {
|
||||||
|
scope.importedModules = importedModules.toList()
|
||||||
|
}
|
||||||
for (module in importedModules) {
|
for (module in importedModules) {
|
||||||
module.importInto(scope, null)
|
module.importInto(scope, null)
|
||||||
}
|
}
|
||||||
for ((name, binding) in importBindings) {
|
for ((name, binding) in importBindings) {
|
||||||
val sourceScope: Scope
|
val record = when (val source = binding.source) {
|
||||||
val baseRecord = when (val source = binding.source) {
|
|
||||||
is ImportBindingSource.Module -> {
|
is ImportBindingSource.Module -> {
|
||||||
val module = provider.prepareImport(source.pos, source.name, null)
|
val module = provider.prepareImport(source.pos, source.name, null)
|
||||||
importedModules.add(module)
|
importedModules.add(module)
|
||||||
sourceScope = module
|
|
||||||
module.objects[binding.symbol]?.takeIf { it.visibility.isPublic }
|
module.objects[binding.symbol]?.takeIf { it.visibility.isPublic }
|
||||||
?: scope.raiseSymbolNotFound("symbol ${source.name}.${binding.symbol} not found")
|
?: scope.raiseSymbolNotFound("symbol ${source.name}.${binding.symbol} not found")
|
||||||
}
|
}
|
||||||
ImportBindingSource.Root -> {
|
ImportBindingSource.Root -> {
|
||||||
sourceScope = provider.rootScope
|
|
||||||
provider.rootScope.objects[binding.symbol]?.takeIf { it.visibility.isPublic }
|
provider.rootScope.objects[binding.symbol]?.takeIf { it.visibility.isPublic }
|
||||||
?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found")
|
?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found")
|
||||||
}
|
}
|
||||||
ImportBindingSource.Seed -> {
|
ImportBindingSource.Seed -> {
|
||||||
sourceScope = seedScope
|
|
||||||
findSeedRecord(seedScope, binding.symbol)
|
findSeedRecord(seedScope, binding.symbol)
|
||||||
?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found")
|
?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val record = importedBindingRecord(baseRecord, sourceScope)
|
|
||||||
if (name == "Exception" && record.value !is ObjClass) {
|
if (name == "Exception" && record.value !is ObjClass) {
|
||||||
scope.updateSlotFor(name, ObjRecord(ObjException.Root, isMutable = false, importedFrom = sourceScope))
|
scope.updateSlotFor(name, ObjRecord(ObjException.Root, isMutable = false))
|
||||||
} else {
|
} else {
|
||||||
scope.updateSlotFor(name, record)
|
scope.updateSlotFor(name, record)
|
||||||
}
|
}
|
||||||
@ -224,15 +219,12 @@ class Script(
|
|||||||
for (module in importedModules) {
|
for (module in importedModules) {
|
||||||
for ((cls, map) in module.extensions) {
|
for ((cls, map) in module.extensions) {
|
||||||
for ((symbol, record) in map) {
|
for ((symbol, record) in map) {
|
||||||
if (record.visibility.isPublic && record.importedFrom == null) {
|
if (record.visibility.isPublic) {
|
||||||
scope.addExtension(cls, symbol, importedBindingRecord(record, module))
|
scope.addExtension(cls, symbol, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (scope is ModuleScope) {
|
|
||||||
scope.importedModules = importedModules.toList()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findSeedRecord(scope: Scope?, name: String): ObjRecord? {
|
private fun findSeedRecord(scope: Scope?, name: String): ObjRecord? {
|
||||||
|
|||||||
@ -53,12 +53,7 @@ class BytecodeStatement private constructor(
|
|||||||
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is net.sergeych.lyng.obj.ObjProperty) {
|
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is net.sergeych.lyng.obj.ObjProperty) {
|
||||||
scope.resolve(record, name)
|
scope.resolve(record, name)
|
||||||
} else {
|
} else {
|
||||||
when (val direct = record.value) {
|
record.value
|
||||||
is FrameSlotRef -> direct.read()
|
|
||||||
is RecordSlotRef -> direct.read(scope, name)
|
|
||||||
is ScopeSlotRef -> direct.read()
|
|
||||||
else -> direct
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
frame.frame.setObj(i, value)
|
frame.frame.setObj(i, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2560,46 +2560,6 @@ private fun captureNamesForStatement(stmt: Statement?): List<String> {
|
|||||||
return ordered.toList()
|
return ordered.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun freezeImmutableCaptureRecord(record: ObjRecord): ObjRecord {
|
|
||||||
val value = record.value as Obj?
|
|
||||||
if (record.isMutable || record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || value is ObjProperty) {
|
|
||||||
return record
|
|
||||||
}
|
|
||||||
return when (value) {
|
|
||||||
is FrameSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
|
||||||
is RecordSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
|
||||||
is ScopeSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
|
||||||
null -> record
|
|
||||||
else -> record.copy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isTransientCapturePlaceholder(value: Obj?): Boolean {
|
|
||||||
return when (value) {
|
|
||||||
null, ObjVoid -> true
|
|
||||||
is FrameSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid }
|
|
||||||
is RecordSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid }
|
|
||||||
is ScopeSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid }
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveStableCaptureRecord(scope: Scope, name: String): ObjRecord? {
|
|
||||||
val direct = scope.chainLookupIgnoreClosure(name, followClosure = true) ?: scope.get(name)
|
|
||||||
if (direct != null && !isTransientCapturePlaceholder(direct.value as Obj?)) {
|
|
||||||
return direct
|
|
||||||
}
|
|
||||||
var parent = scope.parent
|
|
||||||
while (parent != null) {
|
|
||||||
val candidate = parent.chainLookupIgnoreClosure(name, followClosure = true) ?: parent.get(name)
|
|
||||||
if (candidate != null && !isTransientCapturePlaceholder(candidate.value as Obj?)) {
|
|
||||||
return candidate
|
|
||||||
}
|
|
||||||
parent = parent.parent
|
|
||||||
}
|
|
||||||
return direct
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List<String>): List<ObjRecord>? {
|
private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List<String>): List<ObjRecord>? {
|
||||||
if (captureNames.isEmpty()) return null
|
if (captureNames.isEmpty()) return null
|
||||||
val records = ArrayList<ObjRecord>(captureNames.size)
|
val records = ArrayList<ObjRecord>(captureNames.size)
|
||||||
@ -2615,16 +2575,12 @@ private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List<Stri
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val raw = frame.frame.getRawObj(localIndex)
|
val raw = frame.frame.getRawObj(localIndex)
|
||||||
val captureRecord = if (isTransientCapturePlaceholder(raw)) {
|
val scoped = frame.scope.chainLookupIgnoreClosure(name, followClosure = true) ?: frame.scope.get(name)
|
||||||
resolveStableCaptureRecord(frame.scope.parent ?: frame.scope, name)
|
if (scoped != null && scoped.value !== ObjUnset) {
|
||||||
} else {
|
records += scoped
|
||||||
resolveStableCaptureRecord(frame.scope, name)
|
|
||||||
}
|
|
||||||
if (captureRecord != null) {
|
|
||||||
records += freezeImmutableCaptureRecord(captureRecord)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
records += freezeImmutableCaptureRecord(ObjRecord(FrameSlotRef(frame.frame, localIndex), isMutable))
|
records += ObjRecord(FrameSlotRef(frame.frame, localIndex), isMutable)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -2632,7 +2588,7 @@ private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List<Stri
|
|||||||
if (scopeSlot >= 0) {
|
if (scopeSlot >= 0) {
|
||||||
val target = frame.scopeTarget(scopeSlot)
|
val target = frame.scopeTarget(scopeSlot)
|
||||||
val index = frame.fn.scopeSlotIndices[scopeSlot]
|
val index = frame.fn.scopeSlotIndices[scopeSlot]
|
||||||
records += freezeImmutableCaptureRecord(target.getSlotRecord(index))
|
records += target.getSlotRecord(index)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val scopeCaptures = frame.scope.captureRecords
|
val scopeCaptures = frame.scope.captureRecords
|
||||||
@ -2649,7 +2605,7 @@ private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List<Stri
|
|||||||
}
|
}
|
||||||
val scoped = frame.scope.chainLookupIgnoreClosure(name, followClosure = true) ?: frame.scope.get(name)
|
val scoped = frame.scope.chainLookupIgnoreClosure(name, followClosure = true) ?: frame.scope.get(name)
|
||||||
if (scoped != null) {
|
if (scoped != null) {
|
||||||
records += freezeImmutableCaptureRecord(scoped)
|
records += scoped
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
frame.ensureScope().raiseSymbolNotFound("capture $name not found")
|
frame.ensureScope().raiseSymbolNotFound("capture $name not found")
|
||||||
@ -3421,12 +3377,7 @@ class CmdGetMemberSlot(
|
|||||||
resolved.declaringClass
|
resolved.declaringClass
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
when (val value = resolved.value) {
|
resolved.value
|
||||||
is FrameSlotRef -> value.read()
|
|
||||||
is RecordSlotRef -> value.read(frame.ensureScope(), name)
|
|
||||||
is ScopeSlotRef -> value.read()
|
|
||||||
else -> value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (receiver is ObjQualifiedView) {
|
if (receiver is ObjQualifiedView) {
|
||||||
@ -3833,15 +3784,13 @@ class BytecodeLambdaCallable(
|
|||||||
override val pos: Pos,
|
override val pos: Pos,
|
||||||
) : Statement(), BytecodeCallable {
|
) : Statement(), BytecodeCallable {
|
||||||
private fun freezeRecord(record: ObjRecord): ObjRecord {
|
private fun freezeRecord(record: ObjRecord): ObjRecord {
|
||||||
if (record.isMutable) return record
|
val frozenValue = when (val raw = record.value) {
|
||||||
val raw = record.value as Obj?
|
is net.sergeych.lyng.FrameSlotRef -> raw.read()
|
||||||
return when (raw) {
|
is net.sergeych.lyng.RecordSlotRef -> raw.read()
|
||||||
is net.sergeych.lyng.FrameSlotRef -> raw.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
is net.sergeych.lyng.ScopeSlotRef -> raw.read()
|
||||||
is net.sergeych.lyng.RecordSlotRef -> raw.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
else -> raw
|
||||||
is net.sergeych.lyng.ScopeSlotRef -> raw.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record
|
|
||||||
null -> record
|
|
||||||
else -> record.copy()
|
|
||||||
}
|
}
|
||||||
|
return record.copy(value = frozenValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveCaptureRecords(base: Scope): List<ObjRecord>? {
|
private fun resolveCaptureRecords(base: Scope): List<ObjRecord>? {
|
||||||
@ -3859,7 +3808,7 @@ class BytecodeLambdaCallable(
|
|||||||
return BytecodeLambdaCallable(
|
return BytecodeLambdaCallable(
|
||||||
fn = fn,
|
fn = fn,
|
||||||
closureScope = newClosureScope,
|
closureScope = newClosureScope,
|
||||||
captureRecords = captureRecords ?: resolveCaptureRecords(newClosureScope),
|
captureRecords = resolveCaptureRecords(newClosureScope) ?: captureRecords,
|
||||||
captureNames = captureNames,
|
captureNames = captureNames,
|
||||||
paramSlotPlan = paramSlotPlan,
|
paramSlotPlan = paramSlotPlan,
|
||||||
argsDeclaration = argsDeclaration,
|
argsDeclaration = argsDeclaration,
|
||||||
@ -3974,12 +3923,7 @@ class BytecodeLambdaCallable(
|
|||||||
if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
||||||
context.resolve(record, name)
|
context.resolve(record, name)
|
||||||
} else {
|
} else {
|
||||||
when (val direct = record.value) {
|
record.value
|
||||||
is FrameSlotRef -> direct.read()
|
|
||||||
is RecordSlotRef -> direct.read(context, name)
|
|
||||||
is ScopeSlotRef -> direct.read()
|
|
||||||
else -> direct
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
frame.frame.setObj(i, value)
|
frame.frame.setObj(i, value)
|
||||||
}
|
}
|
||||||
@ -4146,31 +4090,7 @@ class CmdFrame(
|
|||||||
frame.setObj(localIndex, record.delegate ?: ObjNull)
|
frame.setObj(localIndex, record.delegate ?: ObjNull)
|
||||||
} else {
|
} else {
|
||||||
val value = record.value
|
val value = record.value
|
||||||
if (!record.isMutable && value is FrameSlotRef) {
|
if (value is FrameSlotRef) {
|
||||||
val resolved = value.peekValue()
|
|
||||||
if (resolved != null) {
|
|
||||||
if (value.refersTo(frame, localIndex)) continue
|
|
||||||
frame.setObj(localIndex, value.read())
|
|
||||||
} else {
|
|
||||||
frame.setObj(localIndex, value)
|
|
||||||
}
|
|
||||||
} else if (!record.isMutable && value is RecordSlotRef) {
|
|
||||||
val resolved = value.peekValue()
|
|
||||||
if (resolved != null) {
|
|
||||||
frame.setObj(localIndex, value.read())
|
|
||||||
} else {
|
|
||||||
frame.setObj(localIndex, value)
|
|
||||||
}
|
|
||||||
} else if (!record.isMutable && value is ScopeSlotRef) {
|
|
||||||
val resolved = value.peekValue()
|
|
||||||
if (resolved != null) {
|
|
||||||
frame.setObj(localIndex, value.read())
|
|
||||||
} else {
|
|
||||||
frame.setObj(localIndex, value)
|
|
||||||
}
|
|
||||||
} else if (!record.isMutable) {
|
|
||||||
frame.setObj(localIndex, value)
|
|
||||||
} else if (value is FrameSlotRef) {
|
|
||||||
if (value.refersTo(frame, localIndex)) continue
|
if (value.refersTo(frame, localIndex)) continue
|
||||||
frame.setObj(localIndex, value)
|
frame.setObj(localIndex, value)
|
||||||
} else {
|
} else {
|
||||||
@ -4185,30 +4105,7 @@ class CmdFrame(
|
|||||||
frame.setObj(idx, record.delegate ?: ObjNull)
|
frame.setObj(idx, record.delegate ?: ObjNull)
|
||||||
} else {
|
} else {
|
||||||
val value = record.value
|
val value = record.value
|
||||||
if (!record.isMutable && value is FrameSlotRef) {
|
if (value is FrameSlotRef) {
|
||||||
val resolved = value.peekValue()
|
|
||||||
if (resolved != null) {
|
|
||||||
frame.setObj(idx, value.read())
|
|
||||||
} else {
|
|
||||||
frame.setObj(idx, value)
|
|
||||||
}
|
|
||||||
} else if (!record.isMutable && value is RecordSlotRef) {
|
|
||||||
val resolved = value.peekValue()
|
|
||||||
if (resolved != null) {
|
|
||||||
frame.setObj(idx, value.read())
|
|
||||||
} else {
|
|
||||||
frame.setObj(idx, value)
|
|
||||||
}
|
|
||||||
} else if (!record.isMutable && value is ScopeSlotRef) {
|
|
||||||
val resolved = value.peekValue()
|
|
||||||
if (resolved != null) {
|
|
||||||
frame.setObj(idx, value.read())
|
|
||||||
} else {
|
|
||||||
frame.setObj(idx, value)
|
|
||||||
}
|
|
||||||
} else if (!record.isMutable) {
|
|
||||||
frame.setObj(idx, value)
|
|
||||||
} else if (value is FrameSlotRef) {
|
|
||||||
frame.setObj(idx, value)
|
frame.setObj(idx, value)
|
||||||
} else {
|
} else {
|
||||||
frame.setObj(idx, RecordSlotRef(record))
|
frame.setObj(idx, RecordSlotRef(record))
|
||||||
@ -5187,7 +5084,6 @@ class CmdFrame(
|
|||||||
when (obj) {
|
when (obj) {
|
||||||
is FrameSlotRef -> obj.read()
|
is FrameSlotRef -> obj.read()
|
||||||
is RecordSlotRef -> obj.read(scope, localName)
|
is RecordSlotRef -> obj.read(scope, localName)
|
||||||
is ScopeSlotRef -> obj.read()
|
|
||||||
is ObjProperty -> resolvePropertyLikeLocal(localName, obj)
|
is ObjProperty -> resolvePropertyLikeLocal(localName, obj)
|
||||||
ObjUnset -> resolveUnsetLocal(localName)
|
ObjUnset -> resolveUnsetLocal(localName)
|
||||||
else -> obj
|
else -> obj
|
||||||
@ -5199,7 +5095,6 @@ class CmdFrame(
|
|||||||
when (obj) {
|
when (obj) {
|
||||||
is FrameSlotRef -> obj.read()
|
is FrameSlotRef -> obj.read()
|
||||||
is RecordSlotRef -> obj.read(scope, localName)
|
is RecordSlotRef -> obj.read(scope, localName)
|
||||||
is ScopeSlotRef -> obj.read()
|
|
||||||
is ObjProperty -> resolvePropertyLikeLocal(localName, obj)
|
is ObjProperty -> resolvePropertyLikeLocal(localName, obj)
|
||||||
ObjUnset -> resolveUnsetLocal(localName)
|
ObjUnset -> resolveUnsetLocal(localName)
|
||||||
else -> obj
|
else -> obj
|
||||||
@ -5224,24 +5119,7 @@ class CmdFrame(
|
|||||||
if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
||||||
return scope.resolve(record, localName)
|
return scope.resolve(record, localName)
|
||||||
}
|
}
|
||||||
return when (val value = record.value) {
|
return record.value
|
||||||
is FrameSlotRef -> value.read()
|
|
||||||
is RecordSlotRef -> value.read(scope, localName)
|
|
||||||
is ScopeSlotRef -> value.read()
|
|
||||||
else -> value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun readResolvedScopeRecord(target: Scope, name: String, record: ObjRecord): Obj {
|
|
||||||
val value = record.value
|
|
||||||
return when {
|
|
||||||
record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || value is ObjProperty ->
|
|
||||||
target.resolve(record, name)
|
|
||||||
value is FrameSlotRef -> value.read()
|
|
||||||
value is RecordSlotRef -> value.read(target, name)
|
|
||||||
value is ScopeSlotRef -> value.read()
|
|
||||||
else -> value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getScopeSlotValue(slot: Int): Obj {
|
private suspend fun getScopeSlotValue(slot: Int): Obj {
|
||||||
@ -5256,7 +5134,10 @@ class CmdFrame(
|
|||||||
if (name != null && record.memberName != null && record.memberName != name) {
|
if (name != null && record.memberName != null && record.memberName != name) {
|
||||||
val resolved = target.get(name)
|
val resolved = target.get(name)
|
||||||
if (resolved != null) {
|
if (resolved != null) {
|
||||||
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
val resolvedValue = resolved.value
|
||||||
|
if (resolved.type == ObjRecord.Type.Delegated || resolved.type == ObjRecord.Type.Property || resolvedValue is ObjProperty) {
|
||||||
|
return target.resolve(resolved, name)
|
||||||
|
}
|
||||||
if (resolvedValue !== ObjUnset) {
|
if (resolvedValue !== ObjUnset) {
|
||||||
target.updateSlotFor(name, resolved)
|
target.updateSlotFor(name, resolved)
|
||||||
}
|
}
|
||||||
@ -5275,13 +5156,12 @@ class CmdFrame(
|
|||||||
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, record)
|
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, record)
|
||||||
return record.value
|
return record.value
|
||||||
}
|
}
|
||||||
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
if (resolved.value !== ObjUnset) {
|
||||||
if (resolvedValue !== ObjUnset) {
|
|
||||||
target.updateSlotFor(name, resolved)
|
target.updateSlotFor(name, resolved)
|
||||||
} else {
|
} else {
|
||||||
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, resolved)
|
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, resolved)
|
||||||
}
|
}
|
||||||
return resolvedValue
|
return resolved.value
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getScopeSlotValueAtAddr(addrSlot: Int): Obj {
|
private suspend fun getScopeSlotValueAtAddr(addrSlot: Int): Obj {
|
||||||
@ -5297,7 +5177,10 @@ class CmdFrame(
|
|||||||
if (name != null && record.memberName != null && record.memberName != name) {
|
if (name != null && record.memberName != null && record.memberName != name) {
|
||||||
val resolved = target.get(name)
|
val resolved = target.get(name)
|
||||||
if (resolved != null) {
|
if (resolved != null) {
|
||||||
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
val resolvedValue = resolved.value
|
||||||
|
if (resolved.type == ObjRecord.Type.Delegated || resolved.type == ObjRecord.Type.Property || resolvedValue is ObjProperty) {
|
||||||
|
return target.resolve(resolved, name)
|
||||||
|
}
|
||||||
if (resolvedValue !== ObjUnset) {
|
if (resolvedValue !== ObjUnset) {
|
||||||
target.updateSlotFor(name, resolved)
|
target.updateSlotFor(name, resolved)
|
||||||
}
|
}
|
||||||
@ -5316,13 +5199,12 @@ class CmdFrame(
|
|||||||
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, record)
|
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, record)
|
||||||
return record.value
|
return record.value
|
||||||
}
|
}
|
||||||
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
if (resolved.value !== ObjUnset) {
|
||||||
if (resolvedValue !== ObjUnset) {
|
|
||||||
target.updateSlotFor(name, resolved)
|
target.updateSlotFor(name, resolved)
|
||||||
} else {
|
} else {
|
||||||
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, resolved)
|
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, resolved)
|
||||||
}
|
}
|
||||||
return resolvedValue
|
return resolved.value
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setScopeSlotValueAtAddr(addrSlot: Int, value: Obj) {
|
private suspend fun setScopeSlotValueAtAddr(addrSlot: Int, value: Obj) {
|
||||||
|
|||||||
@ -34,12 +34,7 @@ internal suspend fun seedFrameLocalsFromScope(frame: CmdFrame, scope: Scope) {
|
|||||||
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is net.sergeych.lyng.obj.ObjProperty) {
|
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is net.sergeych.lyng.obj.ObjProperty) {
|
||||||
scope.resolve(record, name)
|
scope.resolve(record, name)
|
||||||
} else {
|
} else {
|
||||||
when (val direct = record.value) {
|
record.value
|
||||||
is net.sergeych.lyng.FrameSlotRef -> direct.resolvedCaptureValueOrNull() ?: direct
|
|
||||||
is net.sergeych.lyng.RecordSlotRef -> direct.resolvedCaptureValueOrNull() ?: direct
|
|
||||||
is net.sergeych.lyng.ScopeSlotRef -> direct.resolvedCaptureValueOrNull() ?: direct
|
|
||||||
else -> direct
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, i)) {
|
if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, i)) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -16,60 +16,17 @@
|
|||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.Compiler
|
import net.sergeych.lyng.Compiler
|
||||||
import net.sergeych.lyng.EvalSession
|
|
||||||
import net.sergeych.lyng.Script
|
import net.sergeych.lyng.Script
|
||||||
import net.sergeych.lyng.Source
|
import net.sergeych.lyng.Source
|
||||||
import net.sergeych.lyng.obj.ObjString
|
|
||||||
import net.sergeych.lyng.obj.toInt
|
import net.sergeych.lyng.obj.toInt
|
||||||
import net.sergeych.lyng.pacman.ImportManager
|
import net.sergeych.lyng.pacman.ImportManager
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
import kotlin.test.assertSame
|
|
||||||
|
|
||||||
class ScriptImportPreparationTest {
|
class ScriptImportPreparationTest {
|
||||||
|
|
||||||
private fun nestedImportSources(prefix: String): Array<String> =
|
|
||||||
arrayOf(
|
|
||||||
"""
|
|
||||||
package $prefix.alpha
|
|
||||||
|
|
||||||
class Alpha {
|
|
||||||
val headers = Map<String, String>()
|
|
||||||
|
|
||||||
fun tagged(port: Int, host: String): String {
|
|
||||||
val task: Deferred = launch {
|
|
||||||
host + ":" + port + ":" + headers.size
|
|
||||||
}
|
|
||||||
return task.await()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun alphaValue() = Alpha().tagged(7, "alpha")
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
package $prefix.beta
|
|
||||||
import $prefix.alpha
|
|
||||||
|
|
||||||
fun betaValue() = alphaValue() + "|" + Alpha().tagged(8, "beta")
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
package $prefix.gamma
|
|
||||||
import $prefix.alpha
|
|
||||||
import $prefix.beta
|
|
||||||
|
|
||||||
val String.gammaTag get() = this + ":gamma"
|
|
||||||
|
|
||||||
fun gammaValue() = betaValue() + "|" + "done".gammaTag
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun nestedImportManager(prefix: String = "tree"): ImportManager =
|
|
||||||
Script.defaultImportManager.copy().apply {
|
|
||||||
addTextPackages(*nestedImportSources(prefix))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun scriptImportIntoExplicitlyPreparesExistingScope() = runTest {
|
fun scriptImportIntoExplicitlyPreparesExistingScope() = runTest {
|
||||||
val manager = ImportManager()
|
val manager = ImportManager()
|
||||||
@ -127,67 +84,4 @@ class ScriptImportPreparationTest {
|
|||||||
val record = assertNotNull(module["answer"])
|
val record = assertNotNull(module["answer"])
|
||||||
assertEquals(42, module.resolve(record, "answer").toInt())
|
assertEquals(42, module.resolve(record, "answer").toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun repeatedImportIntoOnSameScopeIsIdempotentForNestedPackageGraph() = runTest {
|
|
||||||
val manager = nestedImportManager()
|
|
||||||
val script = Compiler.compile(
|
|
||||||
Source(
|
|
||||||
"<repeat-import-into>",
|
|
||||||
"""
|
|
||||||
import tree.gamma
|
|
||||||
import tree.beta
|
|
||||||
import tree.alpha
|
|
||||||
|
|
||||||
gammaValue()
|
|
||||||
""".trimIndent()
|
|
||||||
),
|
|
||||||
manager
|
|
||||||
)
|
|
||||||
val scope = manager.newStdScope()
|
|
||||||
|
|
||||||
script.importInto(scope)
|
|
||||||
val importedGammaValue = assertNotNull(scope["gammaValue"])
|
|
||||||
val importedAlpha = assertNotNull(scope["Alpha"])
|
|
||||||
|
|
||||||
repeat(5) {
|
|
||||||
script.importInto(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertSame(importedGammaValue, scope["gammaValue"])
|
|
||||||
assertSame(importedAlpha, scope["Alpha"])
|
|
||||||
assertEquals(
|
|
||||||
"alpha:7:0|beta:8:0|done:gamma",
|
|
||||||
(script.execute(scope) as ObjString).value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun repeatedEvalOnSameSessionCanReimportNestedPackageGraph() = runTest {
|
|
||||||
val prefix = "repeattree"
|
|
||||||
val manager = nestedImportManager(prefix)
|
|
||||||
val scope = manager.newModule()
|
|
||||||
val session = EvalSession(scope)
|
|
||||||
|
|
||||||
try {
|
|
||||||
repeat(5) { index ->
|
|
||||||
val result = session.eval(
|
|
||||||
Source(
|
|
||||||
"<repeat-eval-$index>",
|
|
||||||
"""
|
|
||||||
import $prefix.gamma
|
|
||||||
import $prefix.beta
|
|
||||||
import $prefix.alpha
|
|
||||||
|
|
||||||
gammaValue()
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
) as ObjString
|
|
||||||
|
|
||||||
assertEquals("alpha:7:0|beta:8:0|done:gamma", result.value)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
session.cancelAndJoin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user