Fix transitive import re-exports and stabilize captures
This commit is contained in:
parent
eefa4c8f1a
commit
47eb2d7d61
@ -27,6 +27,8 @@ class CliLocalModuleImportRegressionJvmTest {
|
|||||||
import lyng.io.net
|
import lyng.io.net
|
||||||
|
|
||||||
class Alpha {
|
class Alpha {
|
||||||
|
val headers = Map<String, String>()
|
||||||
|
|
||||||
fn startListen(port, host) {
|
fn startListen(port, host) {
|
||||||
launch {
|
launch {
|
||||||
println(port, host)
|
println(port, host)
|
||||||
|
|||||||
@ -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 = closureScope.chainLookupIgnoreClosure(
|
val rec = resolveStableCaptureRecord(
|
||||||
|
closureScope,
|
||||||
capture.name,
|
capture.name,
|
||||||
followClosure = true,
|
context.currentClassCtx
|
||||||
caller = context.currentClassCtx
|
|
||||||
) ?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
) ?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
resolvedRecords.add(rec)
|
resolvedRecords.add(freezeImmutableCaptureRecord(rec))
|
||||||
resolvedNames.add(capture.name)
|
resolvedNames.add(capture.name)
|
||||||
}
|
}
|
||||||
context.captureRecords = resolvedRecords
|
context.captureRecords = resolvedRecords
|
||||||
@ -8766,6 +8766,46 @@ 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,
|
||||||
@ -9147,12 +9187,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 = captureBase.chainLookupIgnoreClosure(
|
val rec = resolveStableCaptureRecord(
|
||||||
|
captureBase,
|
||||||
name,
|
name,
|
||||||
followClosure = true,
|
context.currentClassCtx
|
||||||
caller = context.currentClassCtx
|
|
||||||
) ?: captureBase.raiseSymbolNotFound("symbol $name not found")
|
) ?: captureBase.raiseSymbolNotFound("symbol $name not found")
|
||||||
resolvedRecords.add(rec)
|
resolvedRecords.add(freezeImmutableCaptureRecord(rec))
|
||||||
}
|
}
|
||||||
context.captureRecords = resolvedRecords
|
context.captureRecords = resolvedRecords
|
||||||
context.captureNames = captureNames
|
context.captureNames = captureNames
|
||||||
@ -9193,7 +9233,12 @@ 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 {
|
||||||
record.value
|
when (val direct = 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,6 +79,13 @@ 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)
|
||||||
@ -132,6 +139,16 @@ 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)
|
||||||
}
|
}
|
||||||
@ -198,6 +215,15 @@ 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) {
|
||||||
|
|||||||
@ -19,8 +19,6 @@ package net.sergeych.lyng
|
|||||||
|
|
||||||
import net.sergeych.lyng.bytecode.BytecodeFrame
|
import net.sergeych.lyng.bytecode.BytecodeFrame
|
||||||
import net.sergeych.lyng.bytecode.CmdFunction
|
import net.sergeych.lyng.bytecode.CmdFunction
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
|
||||||
import net.sergeych.lyng.obj.ObjExternCallable
|
|
||||||
import net.sergeych.lyng.obj.ObjRecord
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
import net.sergeych.lyng.obj.ObjString
|
import net.sergeych.lyng.obj.ObjString
|
||||||
import net.sergeych.lyng.pacman.ImportProvider
|
import net.sergeych.lyng.pacman.ImportProvider
|
||||||
@ -34,20 +32,7 @@ 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)
|
||||||
private fun ObjRecord.isEquivalentStdlibAlias(other: ObjRecord): Boolean {
|
|
||||||
val leftOrigin = importedFrom?.packageName
|
|
||||||
val rightOrigin = other.importedFrom?.packageName
|
|
||||||
val origins = setOf(leftOrigin, rightOrigin)
|
|
||||||
val rootStdlibAlias =
|
|
||||||
"lyng.stdlib" in origins &&
|
|
||||||
origins.all { it == null || it == "<anonymous package>" || it == "lyng.stdlib" }
|
|
||||||
if (!rootStdlibAlias) return false
|
|
||||||
if (value !== other.value) return false
|
|
||||||
if (receiver !== other.receiver || delegate !== other.delegate) return false
|
|
||||||
if (memberName != other.memberName || fieldId != other.fieldId || methodId != other.methodId) return false
|
|
||||||
return value is ObjExternCallable || value is ObjClass
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(importProvider: ImportProvider, source: Source) : this(importProvider, source.startPos, source.fileName)
|
constructor(importProvider: ImportProvider, source: Source) : this(importProvider, source.startPos, source.fileName)
|
||||||
|
|
||||||
@ -100,7 +85,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) {
|
if (record.visibility.isPublic && record.importedFrom == null) {
|
||||||
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) }
|
||||||
@ -110,25 +95,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 =
|
val sameBinding = existing.importedFrom == this
|
||||||
existing === record ||
|
|
||||||
existing.importedFrom == record.importedFrom ||
|
|
||||||
existing.isEquivalentStdlibAlias(record)
|
|
||||||
if (!sameBinding)
|
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 {
|
||||||
// when importing records, we keep track of its package (not otherwise needed)
|
val imported = record.importedCopy(this)
|
||||||
if (record.importedFrom == null) record.importedFrom = this
|
scope.objects[newName] = imported
|
||||||
scope.objects[newName] = record
|
scope.updateSlotFor(newName, imported)
|
||||||
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) {
|
if (record.visibility.isPublic && record.importedFrom == null) {
|
||||||
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) }
|
||||||
@ -136,7 +117,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)
|
scope.addExtension(cls, newName, record.importedCopy(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,37 +181,42 @@ 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 record = when (val source = binding.source) {
|
val sourceScope: Scope
|
||||||
|
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))
|
scope.updateSlotFor(name, ObjRecord(ObjException.Root, isMutable = false, importedFrom = sourceScope))
|
||||||
} else {
|
} else {
|
||||||
scope.updateSlotFor(name, record)
|
scope.updateSlotFor(name, record)
|
||||||
}
|
}
|
||||||
@ -219,12 +224,15 @@ 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) {
|
if (record.visibility.isPublic && record.importedFrom == null) {
|
||||||
scope.addExtension(cls, symbol, record)
|
scope.addExtension(cls, symbol, importedBindingRecord(record, module))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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,7 +53,12 @@ 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 {
|
||||||
record.value
|
when (val direct = 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,6 +2560,46 @@ 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)
|
||||||
@ -2575,12 +2615,16 @@ private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List<Stri
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val raw = frame.frame.getRawObj(localIndex)
|
val raw = frame.frame.getRawObj(localIndex)
|
||||||
val scoped = frame.scope.chainLookupIgnoreClosure(name, followClosure = true) ?: frame.scope.get(name)
|
val captureRecord = if (isTransientCapturePlaceholder(raw)) {
|
||||||
if (scoped != null && scoped.value !== ObjUnset) {
|
resolveStableCaptureRecord(frame.scope.parent ?: frame.scope, name)
|
||||||
records += scoped
|
} else {
|
||||||
|
resolveStableCaptureRecord(frame.scope, name)
|
||||||
|
}
|
||||||
|
if (captureRecord != null) {
|
||||||
|
records += freezeImmutableCaptureRecord(captureRecord)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
records += ObjRecord(FrameSlotRef(frame.frame, localIndex), isMutable)
|
records += freezeImmutableCaptureRecord(ObjRecord(FrameSlotRef(frame.frame, localIndex), isMutable))
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -2588,7 +2632,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 += target.getSlotRecord(index)
|
records += freezeImmutableCaptureRecord(target.getSlotRecord(index))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val scopeCaptures = frame.scope.captureRecords
|
val scopeCaptures = frame.scope.captureRecords
|
||||||
@ -2605,7 +2649,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 += scoped
|
records += freezeImmutableCaptureRecord(scoped)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
frame.ensureScope().raiseSymbolNotFound("capture $name not found")
|
frame.ensureScope().raiseSymbolNotFound("capture $name not found")
|
||||||
@ -3377,7 +3421,12 @@ class CmdGetMemberSlot(
|
|||||||
resolved.declaringClass
|
resolved.declaringClass
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
resolved.value
|
when (val 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) {
|
||||||
@ -3785,13 +3834,14 @@ class BytecodeLambdaCallable(
|
|||||||
) : Statement(), BytecodeCallable {
|
) : Statement(), BytecodeCallable {
|
||||||
private fun freezeRecord(record: ObjRecord): ObjRecord {
|
private fun freezeRecord(record: ObjRecord): ObjRecord {
|
||||||
if (record.isMutable) return record
|
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>? {
|
||||||
@ -3924,7 +3974,12 @@ 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 {
|
||||||
record.value
|
when (val direct = 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)
|
||||||
}
|
}
|
||||||
@ -4091,7 +4146,31 @@ 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 (value is FrameSlotRef) {
|
if (!record.isMutable && 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 {
|
||||||
@ -4106,7 +4185,30 @@ 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 (value is FrameSlotRef) {
|
if (!record.isMutable && 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))
|
||||||
@ -5085,6 +5187,7 @@ 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
|
||||||
@ -5096,6 +5199,7 @@ 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
|
||||||
@ -5120,7 +5224,24 @@ 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 record.value
|
return when (val value = 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 {
|
||||||
@ -5135,10 +5256,7 @@ 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 = resolved.value
|
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -5157,12 +5275,13 @@ class CmdFrame(
|
|||||||
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, record)
|
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, record)
|
||||||
return record.value
|
return record.value
|
||||||
}
|
}
|
||||||
if (resolved.value !== ObjUnset) {
|
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
||||||
|
if (resolvedValue !== ObjUnset) {
|
||||||
target.updateSlotFor(name, resolved)
|
target.updateSlotFor(name, resolved)
|
||||||
} else {
|
} else {
|
||||||
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, resolved)
|
failMissingPreparedModuleBinding(slot, name, hadNamedBinding, resolved)
|
||||||
}
|
}
|
||||||
return resolved.value
|
return resolvedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getScopeSlotValueAtAddr(addrSlot: Int): Obj {
|
private suspend fun getScopeSlotValueAtAddr(addrSlot: Int): Obj {
|
||||||
@ -5178,10 +5297,7 @@ 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 = resolved.value
|
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -5200,12 +5316,13 @@ class CmdFrame(
|
|||||||
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, record)
|
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, record)
|
||||||
return record.value
|
return record.value
|
||||||
}
|
}
|
||||||
if (resolved.value !== ObjUnset) {
|
val resolvedValue = readResolvedScopeRecord(target, name, resolved)
|
||||||
|
if (resolvedValue !== ObjUnset) {
|
||||||
target.updateSlotFor(name, resolved)
|
target.updateSlotFor(name, resolved)
|
||||||
} else {
|
} else {
|
||||||
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, resolved)
|
failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, resolved)
|
||||||
}
|
}
|
||||||
return resolved.value
|
return resolvedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setScopeSlotValueAtAddr(addrSlot: Int, value: Obj) {
|
private suspend fun setScopeSlotValueAtAddr(addrSlot: Int, value: Obj) {
|
||||||
|
|||||||
@ -34,7 +34,12 @@ 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 {
|
||||||
record.value
|
when (val direct = 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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user