Temporary lazy builtin baseline
This commit is contained in:
parent
9ddc7dbee6
commit
217787e17a
@ -184,6 +184,7 @@ class Compiler(
|
|||||||
private val encodedPayloadTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
private val encodedPayloadTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
||||||
private val objectDeclNames: MutableSet<String> = mutableSetOf()
|
private val objectDeclNames: MutableSet<String> = mutableSetOf()
|
||||||
private val externCallableNames: MutableSet<String> = mutableSetOf()
|
private val externCallableNames: MutableSet<String> = mutableSetOf()
|
||||||
|
private val externBindingNames: MutableSet<String> = mutableSetOf()
|
||||||
private val moduleDeclaredNames: MutableSet<String> = mutableSetOf()
|
private val moduleDeclaredNames: MutableSet<String> = mutableSetOf()
|
||||||
private var seedingSlotPlan: Boolean = false
|
private var seedingSlotPlan: Boolean = false
|
||||||
|
|
||||||
@ -192,6 +193,7 @@ class Compiler(
|
|||||||
if (plan.slots.isEmpty()) return emptyMap()
|
if (plan.slots.isEmpty()) return emptyMap()
|
||||||
val result = LinkedHashMap<String, ForcedLocalSlotInfo>(plan.slots.size)
|
val result = LinkedHashMap<String, ForcedLocalSlotInfo>(plan.slots.size)
|
||||||
for ((name, entry) in plan.slots) {
|
for ((name, entry) in plan.slots) {
|
||||||
|
if (externBindingNames.contains(name)) continue
|
||||||
result[name] = ForcedLocalSlotInfo(
|
result[name] = ForcedLocalSlotInfo(
|
||||||
index = entry.index,
|
index = entry.index,
|
||||||
isMutable = entry.isMutable,
|
isMutable = entry.isMutable,
|
||||||
@ -885,6 +887,7 @@ class Compiler(
|
|||||||
name = declaredName,
|
name = declaredName,
|
||||||
isMutable = false,
|
isMutable = false,
|
||||||
visibility = Visibility.Public,
|
visibility = Visibility.Public,
|
||||||
|
actualExtern = false,
|
||||||
initializer = initStmt,
|
initializer = initStmt,
|
||||||
isTransient = false,
|
isTransient = false,
|
||||||
typeDecl = null,
|
typeDecl = null,
|
||||||
@ -1751,6 +1754,7 @@ class Compiler(
|
|||||||
enumEntriesByName = enumEntriesByName,
|
enumEntriesByName = enumEntriesByName,
|
||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
callableReturnTypeByName = callableReturnTypeByName,
|
callableReturnTypeByName = callableReturnTypeByName,
|
||||||
|
externBindingNames = externBindingNames,
|
||||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||||
) as BytecodeStatement
|
) as BytecodeStatement
|
||||||
unwrapped to bytecodeStmt.bytecodeFunction()
|
unwrapped to bytecodeStmt.bytecodeFunction()
|
||||||
@ -1957,6 +1961,9 @@ class Compiler(
|
|||||||
val scopeIndex = slotPlanStack.indexOfLast { it.id == slotLoc.scopeId }
|
val scopeIndex = slotPlanStack.indexOfLast { it.id == slotLoc.scopeId }
|
||||||
if (functionIndex >= 0 && scopeIndex >= functionIndex) return null
|
if (functionIndex >= 0 && scopeIndex >= functionIndex) return null
|
||||||
val modulePlan = moduleSlotPlan()
|
val modulePlan = moduleSlotPlan()
|
||||||
|
if (modulePlan != null && slotLoc.scopeId == modulePlan.id && externBindingNames.contains(name)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
if (useScopeSlots && modulePlan != null && slotLoc.scopeId == modulePlan.id) {
|
if (useScopeSlots && modulePlan != null && slotLoc.scopeId == modulePlan.id) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -2075,6 +2082,7 @@ class Compiler(
|
|||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
callableReturnTypeByName = callableReturnTypeByName,
|
callableReturnTypeByName = callableReturnTypeByName,
|
||||||
externCallableNames = externCallableNames,
|
externCallableNames = externCallableNames,
|
||||||
|
externBindingNames = externBindingNames,
|
||||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2105,6 +2113,7 @@ class Compiler(
|
|||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
callableReturnTypeByName = callableReturnTypeByName,
|
callableReturnTypeByName = callableReturnTypeByName,
|
||||||
externCallableNames = externCallableNames,
|
externCallableNames = externCallableNames,
|
||||||
|
externBindingNames = externBindingNames,
|
||||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2160,6 +2169,7 @@ class Compiler(
|
|||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
callableReturnTypeByName = callableReturnTypeByName,
|
callableReturnTypeByName = callableReturnTypeByName,
|
||||||
externCallableNames = externCallableNames,
|
externCallableNames = externCallableNames,
|
||||||
|
externBindingNames = externBindingNames,
|
||||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2343,6 +2353,7 @@ class Compiler(
|
|||||||
stmt.name,
|
stmt.name,
|
||||||
stmt.isMutable,
|
stmt.isMutable,
|
||||||
stmt.visibility,
|
stmt.visibility,
|
||||||
|
stmt.actualExtern,
|
||||||
init,
|
init,
|
||||||
stmt.isTransient,
|
stmt.isTransient,
|
||||||
stmt.typeDecl,
|
stmt.typeDecl,
|
||||||
@ -8893,7 +8904,7 @@ class Compiler(
|
|||||||
val effectiveEqToken = if (isProperty) null else eqToken
|
val effectiveEqToken = if (isProperty) null else eqToken
|
||||||
|
|
||||||
// Register the local name at compile time so that subsequent identifiers can be emitted as fast locals
|
// Register the local name at compile time so that subsequent identifiers can be emitted as fast locals
|
||||||
if (!isStatic && declaringClassNameCaptured == null) declareLocalName(name, isMutable)
|
if (!isStatic && declaringClassNameCaptured == null && !actualExtern) declareLocalName(name, isMutable)
|
||||||
val declKind = if (codeContexts.lastOrNull() is CodeContext.ClassBody) {
|
val declKind = if (codeContexts.lastOrNull() is CodeContext.ClassBody) {
|
||||||
SymbolKind.MEMBER
|
SymbolKind.MEMBER
|
||||||
} else {
|
} else {
|
||||||
@ -8902,6 +8913,8 @@ class Compiler(
|
|||||||
resolutionSink?.declareSymbol(name, declKind, isMutable, nameStartPos, isOverride = isOverride)
|
resolutionSink?.declareSymbol(name, declKind, isMutable, nameStartPos, isOverride = isOverride)
|
||||||
if (declKind == SymbolKind.MEMBER && extTypeName == null) {
|
if (declKind == SymbolKind.MEMBER && extTypeName == null) {
|
||||||
(codeContexts.lastOrNull() as? CodeContext.ClassBody)?.declaredMembers?.add(name)
|
(codeContexts.lastOrNull() as? CodeContext.ClassBody)?.declaredMembers?.add(name)
|
||||||
|
} else if (actualExtern) {
|
||||||
|
externBindingNames.add(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isDelegate = if (isAbstract || actualExtern) {
|
val isDelegate = if (isAbstract || actualExtern) {
|
||||||
@ -9059,6 +9072,7 @@ class Compiler(
|
|||||||
name,
|
name,
|
||||||
isMutable,
|
isMutable,
|
||||||
visibility,
|
visibility,
|
||||||
|
actualExtern,
|
||||||
initialExpression,
|
initialExpression,
|
||||||
isTransient,
|
isTransient,
|
||||||
declaredType,
|
declaredType,
|
||||||
|
|||||||
@ -542,7 +542,11 @@ class Script(
|
|||||||
}
|
}
|
||||||
addFn("lazy") {
|
addFn("lazy") {
|
||||||
val builder = requireOnlyArg<Obj>()
|
val builder = requireOnlyArg<Obj>()
|
||||||
ObjLazyDelegate(builder, requireScope())
|
ObjLazyDelegate(builder, requireScope().snapshotForClosure())
|
||||||
|
}
|
||||||
|
addFn("__builtinLazy") {
|
||||||
|
val builder = requireOnlyArg<Obj>()
|
||||||
|
ObjLazyDelegate(builder, requireScope().snapshotForClosure())
|
||||||
}
|
}
|
||||||
addVoidFn("delay") {
|
addVoidFn("delay") {
|
||||||
val a = args.firstAndOnly()
|
val a = args.firstAndOnly()
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class VarDeclStatement(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val isMutable: Boolean,
|
val isMutable: Boolean,
|
||||||
val visibility: Visibility,
|
val visibility: Visibility,
|
||||||
|
val actualExtern: Boolean,
|
||||||
val initializer: Statement?,
|
val initializer: Statement?,
|
||||||
val isTransient: Boolean,
|
val isTransient: Boolean,
|
||||||
val typeDecl: TypeDecl?,
|
val typeDecl: TypeDecl?,
|
||||||
|
|||||||
@ -42,6 +42,7 @@ class BytecodeCompiler(
|
|||||||
private val callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
private val callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
private val callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
private val callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
||||||
private val externCallableNames: Set<String> = emptySet(),
|
private val externCallableNames: Set<String> = emptySet(),
|
||||||
|
private val externBindingNames: Set<String> = emptySet(),
|
||||||
private val lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
|
private val lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
|
||||||
) {
|
) {
|
||||||
private val useScopeSlots: Boolean = allowedScopeNames != null || scopeSlotNameSet != null
|
private val useScopeSlots: Boolean = allowedScopeNames != null || scopeSlotNameSet != null
|
||||||
@ -4508,7 +4509,7 @@ class BytecodeCompiler(
|
|||||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||||
return CompiledValue(slot, resolved)
|
return CompiledValue(slot, resolved)
|
||||||
}
|
}
|
||||||
if (useScopeSlots && allowedScopeNames?.contains(name) == true) {
|
if (useScopeSlots && isPreparedScopeName(name)) {
|
||||||
scopeSlotIndexByName[name]?.let { slot ->
|
scopeSlotIndexByName[name]?.let { slot ->
|
||||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||||
return CompiledValue(slot, resolved)
|
return CompiledValue(slot, resolved)
|
||||||
@ -8073,7 +8074,7 @@ class BytecodeCompiler(
|
|||||||
slotInitClassByKey[ScopeSlotKey(scopeId, slotIndex)] = cls
|
slotInitClassByKey[ScopeSlotKey(scopeId, slotIndex)] = cls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allowLocalSlots && slotIndex != null && !shouldUseScopeSlotFor(scopeId)) {
|
if (allowLocalSlots && slotIndex != null && (stmt.actualExtern || !shouldUseScopeSlotFor(scopeId, stmt.name, isDelegated = false))) {
|
||||||
val key = ScopeSlotKey(scopeId, slotIndex)
|
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||||
declaredLocalKeys.add(key)
|
declaredLocalKeys.add(key)
|
||||||
if (!localSlotInfoMap.containsKey(key)) {
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
@ -8100,7 +8101,7 @@ class BytecodeCompiler(
|
|||||||
val scopeId = stmt.spec.scopeId ?: 0
|
val scopeId = stmt.spec.scopeId ?: 0
|
||||||
if (slotIndex != null) {
|
if (slotIndex != null) {
|
||||||
val key = ScopeSlotKey(scopeId, slotIndex)
|
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||||
if (allowLocalSlots && !shouldUseScopeSlotFor(scopeId)) {
|
if (allowLocalSlots && !shouldUseScopeSlotFor(scopeId, stmt.spec.name, isDelegated = false)) {
|
||||||
if (!localSlotInfoMap.containsKey(key)) {
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
localSlotInfoMap[key] = LocalSlotInfo(stmt.spec.name, isMutable = false, isDelegated = false)
|
localSlotInfoMap[key] = LocalSlotInfo(stmt.spec.name, isMutable = false, isDelegated = false)
|
||||||
}
|
}
|
||||||
@ -8117,7 +8118,7 @@ class BytecodeCompiler(
|
|||||||
is DelegatedVarDeclStatement -> {
|
is DelegatedVarDeclStatement -> {
|
||||||
val slotIndex = stmt.slotIndex
|
val slotIndex = stmt.slotIndex
|
||||||
val scopeId = stmt.scopeId ?: 0
|
val scopeId = stmt.scopeId ?: 0
|
||||||
if (allowLocalSlots && slotIndex != null && !shouldUseScopeSlotFor(scopeId)) {
|
if (allowLocalSlots && slotIndex != null && !shouldUseScopeSlotFor(scopeId, stmt.name, isDelegated = true)) {
|
||||||
val key = ScopeSlotKey(scopeId, slotIndex)
|
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||||
declaredLocalKeys.add(key)
|
declaredLocalKeys.add(key)
|
||||||
if (!localSlotInfoMap.containsKey(key)) {
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
@ -8283,15 +8284,26 @@ class BytecodeCompiler(
|
|||||||
|
|
||||||
private fun isModuleSlot(scopeId: Int, name: String?): Boolean {
|
private fun isModuleSlot(scopeId: Int, name: String?): Boolean {
|
||||||
if (moduleScopeId != null && scopeId != moduleScopeId) return false
|
if (moduleScopeId != null && scopeId != moduleScopeId) return false
|
||||||
val scopeNames = allowedScopeNames ?: scopeSlotNameSet
|
return isPreparedScopeName(name)
|
||||||
if (scopeNames == null || name == null) return false
|
|
||||||
return scopeNames.contains(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldUseScopeSlotFor(scopeId: Int): Boolean {
|
private fun shouldUseScopeSlotFor(scopeId: Int): Boolean {
|
||||||
return useScopeSlots && moduleScopeId != null && scopeId == moduleScopeId
|
return useScopeSlots && moduleScopeId != null && scopeId == moduleScopeId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldUseScopeSlotFor(scopeId: Int, name: String, isDelegated: Boolean): Boolean {
|
||||||
|
if (moduleScopeId == null || scopeId != moduleScopeId) return false
|
||||||
|
if (isDelegated) return false
|
||||||
|
if (externBindingNames.contains(name)) return true
|
||||||
|
return useScopeSlots && isPreparedScopeName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isPreparedScopeName(name: String?): Boolean {
|
||||||
|
if (name == null) return false
|
||||||
|
if (scopeSlotNameSet?.contains(name) == true) return true
|
||||||
|
return allowedScopeNames?.contains(name) == true
|
||||||
|
}
|
||||||
|
|
||||||
private fun collectLoopVarNames(stmt: Statement) {
|
private fun collectLoopVarNames(stmt: Statement) {
|
||||||
if (stmt is BytecodeStatement) {
|
if (stmt is BytecodeStatement) {
|
||||||
collectLoopVarNames(stmt.original)
|
collectLoopVarNames(stmt.original)
|
||||||
@ -8400,8 +8412,9 @@ class BytecodeCompiler(
|
|||||||
captureSlotKeys.add(key)
|
captureSlotKeys.add(key)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
val forceScopeSlot = shouldUseScopeSlotFor(scopeId, ref.name, ref.isDelegated)
|
||||||
val isModuleSlot = if (ref.isDelegated) false else isModuleSlot(scopeId, ref.name)
|
val isModuleSlot = if (ref.isDelegated) false else isModuleSlot(scopeId, ref.name)
|
||||||
if (allowLocalSlots && !isModuleSlot) {
|
if (allowLocalSlots && !isModuleSlot && !forceScopeSlot) {
|
||||||
if (!localSlotInfoMap.containsKey(key)) {
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable, ref.isDelegated)
|
localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable, ref.isDelegated)
|
||||||
}
|
}
|
||||||
@ -8448,8 +8461,9 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
captureSlotKeys.add(key)
|
captureSlotKeys.add(key)
|
||||||
} else {
|
} else {
|
||||||
|
val forceScopeSlot = shouldUseScopeSlotFor(scopeId, target.name, target.isDelegated)
|
||||||
val isModuleSlot = if (target.isDelegated) false else isModuleSlot(scopeId, target.name)
|
val isModuleSlot = if (target.isDelegated) false else isModuleSlot(scopeId, target.name)
|
||||||
if (allowLocalSlots && !isModuleSlot) {
|
if (allowLocalSlots && !isModuleSlot && !forceScopeSlot) {
|
||||||
if (!localSlotInfoMap.containsKey(key)) {
|
if (!localSlotInfoMap.containsKey(key)) {
|
||||||
localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable, target.isDelegated)
|
localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable, target.isDelegated)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,6 +87,7 @@ class BytecodeStatement private constructor(
|
|||||||
callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
||||||
externCallableNames: Set<String> = emptySet(),
|
externCallableNames: Set<String> = emptySet(),
|
||||||
|
externBindingNames: Set<String> = emptySet(),
|
||||||
lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
|
lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
|
||||||
slotTypeDeclByScopeId: Map<Int, Map<Int, TypeDecl>> = emptyMap(),
|
slotTypeDeclByScopeId: Map<Int, Map<Int, TypeDecl>> = emptyMap(),
|
||||||
): Statement {
|
): Statement {
|
||||||
@ -122,6 +123,7 @@ class BytecodeStatement private constructor(
|
|||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
callableReturnTypeByName = callableReturnTypeByName,
|
callableReturnTypeByName = callableReturnTypeByName,
|
||||||
externCallableNames = externCallableNames,
|
externCallableNames = externCallableNames,
|
||||||
|
externBindingNames = externBindingNames,
|
||||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||||
)
|
)
|
||||||
val compiled = compiler.compileStatement(nameHint, statement)
|
val compiled = compiler.compileStatement(nameHint, statement)
|
||||||
@ -236,6 +238,7 @@ class BytecodeStatement private constructor(
|
|||||||
stmt.name,
|
stmt.name,
|
||||||
stmt.isMutable,
|
stmt.isMutable,
|
||||||
stmt.visibility,
|
stmt.visibility,
|
||||||
|
stmt.actualExtern,
|
||||||
stmt.initializer?.let { unwrapDeep(it) },
|
stmt.initializer?.let { unwrapDeep(it) },
|
||||||
stmt.isTransient,
|
stmt.isTransient,
|
||||||
stmt.typeDecl,
|
stmt.typeDecl,
|
||||||
|
|||||||
@ -2392,8 +2392,13 @@ class CmdDeclLocal(internal val constId: Int, internal val slot: Int) : Cmd() {
|
|||||||
?: error("DECL_LOCAL expects LocalDecl at $constId")
|
?: error("DECL_LOCAL expects LocalDecl at $constId")
|
||||||
if (slot < frame.fn.scopeSlotCount) {
|
if (slot < frame.fn.scopeSlotCount) {
|
||||||
val target = frame.scopeTarget(slot)
|
val target = frame.scopeTarget(slot)
|
||||||
frame.ensureScopeSlot(target, slot)
|
val index = frame.ensureScopeSlot(target, slot)
|
||||||
val value = frame.slotToObj(slot).byValueCopy()
|
val raw = target.getSlotRecord(index).value
|
||||||
|
val value = when (raw) {
|
||||||
|
is FrameSlotRef -> raw.read()
|
||||||
|
is RecordSlotRef -> raw.read()
|
||||||
|
else -> raw
|
||||||
|
}.byValueCopy()
|
||||||
target.updateSlotFor(
|
target.updateSlotFor(
|
||||||
decl.name,
|
decl.name,
|
||||||
ObjRecord(
|
ObjRecord(
|
||||||
@ -4557,7 +4562,7 @@ class CmdFrame(
|
|||||||
return getScopeSlotValueAtAddr(addrSlot)
|
return getScopeSlotValueAtAddr(addrSlot)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setAddrObj(addrSlot: Int, value: Obj) {
|
suspend fun setAddrObj(addrSlot: Int, value: Obj) {
|
||||||
setScopeSlotValueAtAddr(addrSlot, value)
|
setScopeSlotValueAtAddr(addrSlot, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4565,7 +4570,7 @@ class CmdFrame(
|
|||||||
return getScopeSlotValueAtAddr(addrSlot).toLong()
|
return getScopeSlotValueAtAddr(addrSlot).toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setAddrInt(addrSlot: Int, value: Long) {
|
suspend fun setAddrInt(addrSlot: Int, value: Long) {
|
||||||
setScopeSlotValueAtAddr(addrSlot, ObjInt.of(value))
|
setScopeSlotValueAtAddr(addrSlot, ObjInt.of(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4573,7 +4578,7 @@ class CmdFrame(
|
|||||||
return getScopeSlotValueAtAddr(addrSlot).toDouble()
|
return getScopeSlotValueAtAddr(addrSlot).toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setAddrReal(addrSlot: Int, value: Double) {
|
suspend fun setAddrReal(addrSlot: Int, value: Double) {
|
||||||
setScopeSlotValueAtAddr(addrSlot, ObjReal.of(value))
|
setScopeSlotValueAtAddr(addrSlot, ObjReal.of(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4581,7 +4586,7 @@ class CmdFrame(
|
|||||||
return getScopeSlotValueAtAddr(addrSlot).toBool()
|
return getScopeSlotValueAtAddr(addrSlot).toBool()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setAddrBool(addrSlot: Int, value: Boolean) {
|
suspend fun setAddrBool(addrSlot: Int, value: Boolean) {
|
||||||
setScopeSlotValueAtAddr(addrSlot, if (value) ObjTrue else ObjFalse)
|
setScopeSlotValueAtAddr(addrSlot, if (value) ObjTrue else ObjFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4870,9 +4875,16 @@ class CmdFrame(
|
|||||||
return resolved.value
|
return resolved.value
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setScopeSlotValueAtAddr(addrSlot: Int, value: Obj) {
|
private suspend fun setScopeSlotValueAtAddr(addrSlot: Int, value: Obj) {
|
||||||
val target = addrScopes[addrSlot] ?: error("Address slot $addrSlot is not resolved")
|
val target = addrScopes[addrSlot] ?: error("Address slot $addrSlot is not resolved")
|
||||||
val index = addrIndices[addrSlot]
|
val index = addrIndices[addrSlot]
|
||||||
|
val record = target.getSlotRecord(index)
|
||||||
|
val slotId = addrScopeSlots[addrSlot]
|
||||||
|
val name = fn.scopeSlotNames.getOrNull(slotId)
|
||||||
|
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty)) {
|
||||||
|
target.assign(record, name, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
target.setSlotValue(index, value)
|
target.setSlotValue(index, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,9 +18,11 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
|
import net.sergeych.lyng.BytecodeBodyProvider
|
||||||
import net.sergeych.lyng.Pos
|
import net.sergeych.lyng.Pos
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
|
import net.sergeych.lyng.bytecode.BytecodeStatement
|
||||||
import net.sergeych.lyng.Visibility
|
import net.sergeych.lyng.Visibility
|
||||||
import net.sergeych.lyng.executeBytecodeWithSeed
|
import net.sergeych.lyng.executeBytecodeWithSeed
|
||||||
|
|
||||||
@ -43,13 +45,25 @@ class ObjLazyDelegate(
|
|||||||
onNotFoundResult: (suspend () -> Obj?)?,
|
onNotFoundResult: (suspend () -> Obj?)?,
|
||||||
): Obj {
|
): Obj {
|
||||||
return when (name) {
|
return when (name) {
|
||||||
|
"bind" -> {
|
||||||
|
val access = args.getOrNull(1)?.toString() ?: ""
|
||||||
|
if (!access.endsWith("Val")) {
|
||||||
|
scope.raiseIllegalArgument("lazy delegate can only be used with 'val'")
|
||||||
|
}
|
||||||
|
this
|
||||||
|
}
|
||||||
"getValue" -> {
|
"getValue" -> {
|
||||||
if (!calculated) {
|
if (!calculated) {
|
||||||
val callScope = capturedScope.createChildScope(capturedScope.pos, args = Arguments.EMPTY)
|
val receiver = args.getOrNull(0) ?: ObjNull
|
||||||
cachedValue = if (builder is Statement) {
|
val callScope = scope.createChildScope(
|
||||||
executeBytecodeWithSeed(callScope, builder, "lazy delegate")
|
scope.pos,
|
||||||
|
args = Arguments.EMPTY,
|
||||||
|
newThisObj = receiver
|
||||||
|
)
|
||||||
|
cachedValue = if (builder is BytecodeStatement || builder is BytecodeBodyProvider) {
|
||||||
|
executeBytecodeWithSeed(callScope, builder as Statement, "lazy delegate")
|
||||||
} else {
|
} else {
|
||||||
builder.callOn(callScope)
|
builder.invoke(callScope, receiver, Arguments.EMPTY)
|
||||||
}
|
}
|
||||||
calculated = true
|
calculated = true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import net.sergeych.lyng.bridge.bindGlobalVar
|
||||||
|
import net.sergeych.lyng.bridge.globalBinder
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class GlobalPropertyCaptureRegressionTest {
|
||||||
|
@Test
|
||||||
|
fun externGlobalVarAssignmentInsideFunctionShouldCallBoundSetter() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
var x = 1.0
|
||||||
|
|
||||||
|
scope.eval(
|
||||||
|
"""
|
||||||
|
extern var X: Real
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
X = X + 1.0
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
scope.globalBinder().bindGlobalVar(
|
||||||
|
name = "X",
|
||||||
|
get = { x },
|
||||||
|
set = { x = it }
|
||||||
|
)
|
||||||
|
|
||||||
|
scope.eval("main()")
|
||||||
|
|
||||||
|
assertEquals(2.0, x, "bound extern var should stay live inside function bodies")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -415,24 +415,26 @@ fun with<T,R>(self: T, block: T.()->R): R {
|
|||||||
block(self)
|
block(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern fun __builtinLazy(creator: Object): Object
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Standard implementation of a lazy-initialized property delegate.
|
Standard implementation of a lazy-initialized property delegate.
|
||||||
The provided creator lambda is called once on the first access to compute the value.
|
The provided creator lambda is called once on the first access to compute the value.
|
||||||
Can only be used with 'val' properties.
|
Can only be used with 'val' properties.
|
||||||
*/
|
*/
|
||||||
class lazy<T,ThisRefType=Object>(creatorParam: ThisRefType.()->T) : Delegate<T,ThisRefType> {
|
class lazy<T,ThisRefType=Object>(creatorParam: ThisRefType.()->T) : Delegate<T,ThisRefType> {
|
||||||
private val creator: ThisRefType.()->T = creatorParam
|
private val delegate: Delegate<T,ThisRefType> = __builtinLazy(creatorParam) as Delegate<T,ThisRefType>
|
||||||
private var value = Unset
|
|
||||||
|
|
||||||
override fun bind(name: String, access: DelegateAccess, thisRef: ThisRefType): Object {
|
override fun bind(name: String, access: DelegateAccess, thisRef: ThisRefType): Object {
|
||||||
if (access != DelegateAccess.Val) throw "lazy delegate can only be used with 'val'"
|
delegate.bind(name, access, thisRef)
|
||||||
this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getValue(thisRef: ThisRefType, name: String): T {
|
override fun getValue(thisRef: ThisRefType, name: String): T {
|
||||||
if (value == Unset)
|
delegate.getValue(thisRef, name) as T
|
||||||
value = with(thisRef,creator)
|
}
|
||||||
value as T
|
|
||||||
|
override fun setValue(thisRef: ThisRefType, name: String, newValue: T): void {
|
||||||
|
delegate.setValue(thisRef, name, newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user