work in progress: unignoring tests
This commit is contained in:
1 parent
24c4ed85b4
commit
308a9c0bcb
22 files changed
+637
-335
No files matched your search
@@ -26,11 +26,7 @@ import net.sergeych.lyng.miniast.*
|
||||
import net.sergeych.lyng.obj.*
|
||||
import net.sergeych.lyng.pacman.ImportManager
|
||||
import net.sergeych.lyng.pacman.ImportProvider
|
||||
import net.sergeych.lyng.resolution.CompileTimeResolver
|
||||
import net.sergeych.lyng.resolution.ResolutionReport
|
||||
import net.sergeych.lyng.resolution.ResolutionSink
|
||||
import net.sergeych.lyng.resolution.ScopeKind
|
||||
import net.sergeych.lyng.resolution.SymbolKind
|
||||
import net.sergeych.lyng.resolution.*
|
||||
|
||||
/**
|
||||
* The LYNG compiler.
|
||||
@@ -147,6 +143,7 @@ class Compiler(
|
||||
while (current != null) {
|
||||
for ((name, record) in current.objects) {
|
||||
if (!record.visibility.isPublic) continue
|
||||
if (plan.slots.containsKey(name)) continue
|
||||
declareSlotNameIn(plan, name, record.isMutable, record.type == ObjRecord.Type.Delegated)
|
||||
}
|
||||
for ((cls, map) in current.extensions) {
|
||||
@@ -154,34 +151,46 @@ class Compiler(
|
||||
if (!record.visibility.isPublic) continue
|
||||
when (record.type) {
|
||||
ObjRecord.Type.Property -> {
|
||||
declareSlotNameIn(
|
||||
plan,
|
||||
extensionPropertyGetterName(cls.className, name),
|
||||
isMutable = false,
|
||||
isDelegated = false
|
||||
)
|
||||
val prop = record.value as? ObjProperty
|
||||
if (prop?.setter != null) {
|
||||
val getterName = extensionPropertyGetterName(cls.className, name)
|
||||
if (!plan.slots.containsKey(getterName)) {
|
||||
declareSlotNameIn(
|
||||
plan,
|
||||
extensionPropertySetterName(cls.className, name),
|
||||
getterName,
|
||||
isMutable = false,
|
||||
isDelegated = false
|
||||
)
|
||||
}
|
||||
val prop = record.value as? ObjProperty
|
||||
if (prop?.setter != null) {
|
||||
val setterName = extensionPropertySetterName(cls.className, name)
|
||||
if (!plan.slots.containsKey(setterName)) {
|
||||
declareSlotNameIn(
|
||||
plan,
|
||||
setterName,
|
||||
isMutable = false,
|
||||
isDelegated = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val callableName = extensionCallableName(cls.className, name)
|
||||
if (!plan.slots.containsKey(callableName)) {
|
||||
declareSlotNameIn(
|
||||
plan,
|
||||
callableName,
|
||||
isMutable = false,
|
||||
isDelegated = false
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> declareSlotNameIn(
|
||||
plan,
|
||||
extensionCallableName(cls.className, name),
|
||||
isMutable = false,
|
||||
isDelegated = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
for ((name, slotIndex) in current.slotNameToIndexSnapshot()) {
|
||||
val record = current.getSlotRecord(slotIndex)
|
||||
if (!record.visibility.isPublic) continue
|
||||
if (plan.slots.containsKey(name)) continue
|
||||
declareSlotNameIn(plan, name, record.isMutable, record.type == ObjRecord.Type.Delegated)
|
||||
}
|
||||
if (!includeParents) return
|
||||
@@ -336,12 +345,19 @@ class Compiler(
|
||||
private fun resolveCompileClassInfo(name: String): CompileClassInfo? {
|
||||
compileClassInfos[name]?.let { return it }
|
||||
val scopeRec = seedScope?.get(name) ?: importManager.rootScope.get(name)
|
||||
val cls = scopeRec?.value as? ObjClass ?: return null
|
||||
val clsFromScope = scopeRec?.value as? ObjClass
|
||||
val clsFromImports = if (clsFromScope == null) {
|
||||
importedScopes.asReversed().firstNotNullOfOrNull { it.get(name)?.value as? ObjClass }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val cls = clsFromScope ?: clsFromImports ?: return null
|
||||
val fieldIds = cls.instanceFieldIdMap()
|
||||
val methodIds = cls.instanceMethodIdMap(includeAbstract = true)
|
||||
val baseNames = cls.directParents.map { it.className }
|
||||
val nextFieldId = (fieldIds.values.maxOrNull() ?: -1) + 1
|
||||
val nextMethodId = (methodIds.values.maxOrNull() ?: -1) + 1
|
||||
return CompileClassInfo(name, fieldIds, methodIds, nextFieldId, nextMethodId)
|
||||
return CompileClassInfo(name, fieldIds, methodIds, nextFieldId, nextMethodId, baseNames)
|
||||
}
|
||||
|
||||
private data class BaseMemberIds(
|
||||
@@ -594,6 +610,20 @@ class Compiler(
|
||||
resolutionSink?.reference(name, pos)
|
||||
return ref
|
||||
}
|
||||
val moduleLoc = if (slotPlanStack.size == 1) lookupSlotLocation(name, includeModule = true) else null
|
||||
if (moduleLoc != null) {
|
||||
val ref = LocalSlotRef(
|
||||
name,
|
||||
moduleLoc.slot,
|
||||
moduleLoc.scopeId,
|
||||
moduleLoc.isMutable,
|
||||
moduleLoc.isDelegated,
|
||||
pos,
|
||||
strictSlotRefs
|
||||
)
|
||||
resolutionSink?.reference(name, pos)
|
||||
return ref
|
||||
}
|
||||
val classCtx = codeContexts.lastOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||
if (classCtx != null && classCtx.declaredMembers.contains(name)) {
|
||||
resolutionSink?.referenceMember(name, pos)
|
||||
@@ -900,7 +930,8 @@ class Compiler(
|
||||
val fieldIds: Map<String, Int>,
|
||||
val methodIds: Map<String, Int>,
|
||||
val nextFieldId: Int,
|
||||
val nextMethodId: Int
|
||||
val nextMethodId: Int,
|
||||
val baseNames: List<String>
|
||||
)
|
||||
|
||||
private val compileClassInfos = mutableMapOf<String, CompileClassInfo>()
|
||||
@@ -959,6 +990,7 @@ class Compiler(
|
||||
val stdlib = importManager.prepareImport(start, "lyng.stdlib", null)
|
||||
seedResolutionFromScope(stdlib, start)
|
||||
seedSlotPlanFromScope(stdlib)
|
||||
importedScopes.add(stdlib)
|
||||
}
|
||||
predeclareTopLevelSymbols()
|
||||
}
|
||||
@@ -1351,12 +1383,24 @@ class Compiler(
|
||||
)
|
||||
}
|
||||
|
||||
private fun wrapFunctionBytecode(stmt: Statement, name: String): Statement {
|
||||
private fun wrapFunctionBytecode(
|
||||
stmt: Statement,
|
||||
name: String,
|
||||
extraKnownNameObjClass: Map<String, ObjClass> = emptyMap()
|
||||
): Statement {
|
||||
if (!useBytecodeStatements) return stmt
|
||||
if (containsDelegatedRefs(stmt)) return stmt
|
||||
if (containsUnsupportedForBytecode(stmt)) return stmt
|
||||
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
||||
val allowedScopeNames = moduleSlotPlan()?.slots?.keys
|
||||
val knownNames = if (extraKnownNameObjClass.isEmpty()) {
|
||||
knownClassMapForBytecode()
|
||||
} else {
|
||||
val merged = LinkedHashMap<String, ObjClass>()
|
||||
merged.putAll(knownClassMapForBytecode())
|
||||
merged.putAll(extraKnownNameObjClass)
|
||||
merged
|
||||
}
|
||||
return BytecodeStatement.wrap(
|
||||
stmt,
|
||||
"fn@$name",
|
||||
@@ -1365,7 +1409,7 @@ class Compiler(
|
||||
rangeLocalNames = currentRangeParamNames,
|
||||
allowedScopeNames = allowedScopeNames,
|
||||
slotTypeByScopeId = slotTypeByScopeId,
|
||||
knownNameObjClass = knownClassMapForBytecode()
|
||||
knownNameObjClass = knownNames
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2362,8 +2406,12 @@ class Compiler(
|
||||
val t = cc.next()
|
||||
if (t.type != Token.Type.ID) throw ScriptError(t.pos, "Expecting ID after ::")
|
||||
return when (t.value) {
|
||||
"class" -> ValueFnRef { scope ->
|
||||
operand.get(scope).value.objClass.asReadonly
|
||||
"class" -> {
|
||||
val ref = ValueFnRef { scope ->
|
||||
operand.get(scope).value.objClass.asReadonly
|
||||
}
|
||||
lambdaReturnTypeByRef[ref] = ObjClassType
|
||||
ref
|
||||
}
|
||||
|
||||
else -> throw ScriptError(t.pos, "Unknown scope operation: ${t.value}")
|
||||
@@ -2987,11 +3035,13 @@ class Compiler(
|
||||
|
||||
private fun inferObjClassFromRef(ref: ObjRef): ObjClass? = when (ref) {
|
||||
is ConstRef -> ref.constValue as? ObjClass ?: (ref.constValue as? Obj)?.objClass
|
||||
is LocalVarRef -> nameObjClass[ref.name]
|
||||
is LocalVarRef -> nameObjClass[ref.name] ?: resolveClassByName(ref.name)
|
||||
is LocalSlotRef -> {
|
||||
val ownerScopeId = ref.captureOwnerScopeId ?: ref.scopeId
|
||||
val ownerSlot = ref.captureOwnerSlot ?: ref.slot
|
||||
slotTypeByScopeId[ownerScopeId]?.get(ownerSlot) ?: nameObjClass[ref.name]
|
||||
slotTypeByScopeId[ownerScopeId]?.get(ownerSlot)
|
||||
?: nameObjClass[ref.name]
|
||||
?: resolveClassByName(ref.name)
|
||||
}
|
||||
is ListLiteralRef -> ObjList.type
|
||||
is MapLiteralRef -> ObjMap.type
|
||||
@@ -3027,6 +3077,20 @@ class Compiler(
|
||||
is LocalVarRef -> nameObjClass[ref.name]
|
||||
?: nameTypeDecl[ref.name]?.let { resolveTypeDeclObjClass(it) }
|
||||
?: resolveClassByName(ref.name)
|
||||
is ImplicitThisMemberRef -> {
|
||||
val typeName = ref.preferredThisTypeName() ?: currentImplicitThisTypeName()
|
||||
val targetClass = typeName?.let { resolveClassByName(it) }
|
||||
inferFieldReturnClass(targetClass, ref.name)
|
||||
}
|
||||
is ThisFieldSlotRef -> {
|
||||
val typeName = currentImplicitThisTypeName()
|
||||
val targetClass = typeName?.let { resolveClassByName(it) }
|
||||
inferFieldReturnClass(targetClass, ref.name)
|
||||
}
|
||||
is QualifiedThisFieldSlotRef -> {
|
||||
val targetClass = resolveClassByName(ref.receiverTypeName())
|
||||
inferFieldReturnClass(targetClass, ref.name)
|
||||
}
|
||||
is ConstRef -> ref.constValue as? ObjClass ?: (ref.constValue as? Obj)?.objClass
|
||||
is ListLiteralRef -> ObjList.type
|
||||
is MapLiteralRef -> ObjMap.type
|
||||
@@ -3555,15 +3619,19 @@ class Compiler(
|
||||
val implicitThisTypeName = currentImplicitThisTypeName()
|
||||
return when (left) {
|
||||
is ImplicitThisMemberRef ->
|
||||
ImplicitThisMethodCallRef(
|
||||
left.name,
|
||||
left.methodId,
|
||||
args,
|
||||
detectedBlockArgument,
|
||||
isOptional,
|
||||
left.atPos,
|
||||
implicitThisTypeName
|
||||
)
|
||||
if (left.methodId == null && left.fieldId != null) {
|
||||
CallRef(left, args, detectedBlockArgument, isOptional)
|
||||
} else {
|
||||
ImplicitThisMethodCallRef(
|
||||
left.name,
|
||||
left.methodId,
|
||||
args,
|
||||
detectedBlockArgument,
|
||||
isOptional,
|
||||
left.atPos,
|
||||
implicitThisTypeName
|
||||
)
|
||||
}
|
||||
is LocalVarRef -> {
|
||||
val classContext = codeContexts.any { ctx -> ctx is CodeContext.ClassBody }
|
||||
val implicitThis = codeContexts.any { ctx ->
|
||||
@@ -4388,7 +4456,8 @@ class Compiler(
|
||||
fieldIds = fieldIds,
|
||||
methodIds = methodIds,
|
||||
nextFieldId = fieldIds.size,
|
||||
nextMethodId = methodIds.size
|
||||
nextMethodId = methodIds.size,
|
||||
baseNames = listOf("Object")
|
||||
)
|
||||
enumEntriesByName[nameToken.value] = names.toList()
|
||||
|
||||
@@ -4412,6 +4481,7 @@ class Compiler(
|
||||
val className = nameToken?.value ?: generateAnonName(startPos)
|
||||
if (nameToken != null) {
|
||||
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
||||
declareLocalName(nameToken.value, isMutable = false)
|
||||
}
|
||||
|
||||
val doc = pendingDeclDoc ?: consumePendingDoc()
|
||||
@@ -4463,10 +4533,31 @@ class Compiler(
|
||||
slotPlanStack.add(classSlotPlan)
|
||||
resolutionSink?.declareClass(className, baseSpecs.map { it.name }, startPos)
|
||||
resolutionSink?.enterScope(ScopeKind.CLASS, startPos, className, baseSpecs.map { it.name })
|
||||
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
||||
classCtx?.let { ctx ->
|
||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
||||
ctx.memberMethodIds.putAll(baseIds.methodIds)
|
||||
ctx.nextFieldId = maxOf(ctx.nextFieldId, baseIds.nextFieldId)
|
||||
ctx.nextMethodId = maxOf(ctx.nextMethodId, baseIds.nextMethodId)
|
||||
}
|
||||
val st = try {
|
||||
withLocalNames(emptySet()) {
|
||||
val parsed = withLocalNames(emptySet()) {
|
||||
parseScript()
|
||||
}
|
||||
if (!isExtern) {
|
||||
classCtx?.let { ctx ->
|
||||
compileClassInfos[className] = CompileClassInfo(
|
||||
name = className,
|
||||
fieldIds = ctx.memberFieldIds.toMap(),
|
||||
methodIds = ctx.memberMethodIds.toMap(),
|
||||
nextFieldId = ctx.nextFieldId,
|
||||
nextMethodId = ctx.nextMethodId,
|
||||
baseNames = baseSpecs.map { it.name }
|
||||
)
|
||||
}
|
||||
}
|
||||
parsed
|
||||
} finally {
|
||||
slotPlanStack.removeLast()
|
||||
resolutionSink?.exitScope(cc.currentPos())
|
||||
@@ -4492,6 +4583,17 @@ class Compiler(
|
||||
miniSink?.onClassDecl(node)
|
||||
}
|
||||
resolutionSink?.declareClass(className, baseSpecs.map { it.name }, startPos)
|
||||
if (!isExtern) {
|
||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||
compileClassInfos[className] = CompileClassInfo(
|
||||
name = className,
|
||||
fieldIds = baseIds.fieldIds,
|
||||
methodIds = baseIds.methodIds,
|
||||
nextFieldId = baseIds.nextFieldId,
|
||||
nextMethodId = baseIds.nextMethodId,
|
||||
baseNames = baseSpecs.map { it.name }
|
||||
)
|
||||
}
|
||||
cc.restorePos(saved)
|
||||
null
|
||||
}
|
||||
@@ -4563,6 +4665,15 @@ class Compiler(
|
||||
"Bad class declaration: expected ')' at the end of the primary constructor"
|
||||
)
|
||||
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
if (param.accessType != null) {
|
||||
val declClass = resolveTypeDeclObjClass(param.type)
|
||||
if (declClass != null) {
|
||||
classFieldTypesByName.getOrPut(nameToken.value) { mutableMapOf() }[param.name] = declClass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val classSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
||||
classCtx?.slotPlanId = classSlotPlan.id
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
@@ -4667,6 +4778,14 @@ class Compiler(
|
||||
throw ScriptError(nameToken.pos, "extern member $member is not found in runtime class ${nameToken.value}")
|
||||
}
|
||||
}
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
if (param.accessType == null) return@forEach
|
||||
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
||||
val fieldId = existingExternInfo.fieldIds[param.name]
|
||||
?: throw ScriptError(nameToken.pos, "extern field ${param.name} is not found in runtime class ${nameToken.value}")
|
||||
ctx.memberFieldIds[param.name] = fieldId
|
||||
}
|
||||
}
|
||||
compileClassInfos[nameToken.value] = existingExternInfo
|
||||
} else {
|
||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||
@@ -4687,11 +4806,11 @@ class Compiler(
|
||||
throw ScriptError(nameToken.pos, "member $member overrides parent member but 'override' keyword is missing")
|
||||
}
|
||||
}
|
||||
if (!ctx.memberFieldIds.containsKey(member)) {
|
||||
ctx.memberFieldIds[member] = ctx.nextFieldId++
|
||||
}
|
||||
if (!ctx.memberMethodIds.containsKey(member)) {
|
||||
ctx.memberMethodIds[member] = ctx.nextMethodId++
|
||||
}
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
if (param.accessType == null) return@forEach
|
||||
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
||||
ctx.memberFieldIds[param.name] = ctx.nextFieldId++
|
||||
}
|
||||
}
|
||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
||||
@@ -4699,13 +4818,27 @@ class Compiler(
|
||||
fieldIds = ctx.memberFieldIds.toMap(),
|
||||
methodIds = ctx.memberMethodIds.toMap(),
|
||||
nextFieldId = ctx.nextFieldId,
|
||||
nextMethodId = ctx.nextMethodId
|
||||
nextMethodId = ctx.nextMethodId,
|
||||
baseNames = baseSpecs.map { it.name }
|
||||
)
|
||||
}
|
||||
}
|
||||
withLocalNames(constructorArgsDeclaration?.params?.map { it.name }?.toSet() ?: emptySet()) {
|
||||
val parsed = withLocalNames(constructorArgsDeclaration?.params?.map { it.name }?.toSet() ?: emptySet()) {
|
||||
parseScript()
|
||||
}
|
||||
if (!isExtern) {
|
||||
classCtx?.let { ctx ->
|
||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
||||
name = nameToken.value,
|
||||
fieldIds = ctx.memberFieldIds.toMap(),
|
||||
methodIds = ctx.memberMethodIds.toMap(),
|
||||
nextFieldId = ctx.nextFieldId,
|
||||
nextMethodId = ctx.nextMethodId,
|
||||
baseNames = baseSpecs.map { it.name }
|
||||
)
|
||||
}
|
||||
}
|
||||
parsed
|
||||
} finally {
|
||||
slotPlanStack.removeLast()
|
||||
resolutionSink?.exitScope(cc.currentPos())
|
||||
@@ -4738,20 +4871,28 @@ class Compiler(
|
||||
ctx.memberMethodIds.putAll(existingExternInfo.methodIds)
|
||||
ctx.nextFieldId = maxOf(ctx.nextFieldId, existingExternInfo.nextFieldId)
|
||||
ctx.nextMethodId = maxOf(ctx.nextMethodId, existingExternInfo.nextMethodId)
|
||||
for (member in ctx.declaredMembers) {
|
||||
val hasField = member in existingExternInfo.fieldIds
|
||||
val hasMethod = member in existingExternInfo.methodIds
|
||||
if (!hasField && !hasMethod) {
|
||||
throw ScriptError(nameToken.pos, "extern member $member is not found in runtime class ${nameToken.value}")
|
||||
for (member in ctx.declaredMembers) {
|
||||
val hasField = member in existingExternInfo.fieldIds
|
||||
val hasMethod = member in existingExternInfo.methodIds
|
||||
if (!hasField && !hasMethod) {
|
||||
throw ScriptError(nameToken.pos, "extern member $member is not found in runtime class ${nameToken.value}")
|
||||
}
|
||||
}
|
||||
}
|
||||
compileClassInfos[nameToken.value] = existingExternInfo
|
||||
} else {
|
||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
||||
ctx.memberMethodIds.putAll(baseIds.methodIds)
|
||||
ctx.nextFieldId = maxOf(ctx.nextFieldId, baseIds.nextFieldId)
|
||||
ctx.nextMethodId = maxOf(ctx.nextMethodId, baseIds.nextMethodId)
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
if (param.accessType == null) return@forEach
|
||||
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
||||
val fieldId = existingExternInfo.fieldIds[param.name]
|
||||
?: throw ScriptError(nameToken.pos, "extern field ${param.name} is not found in runtime class ${nameToken.value}")
|
||||
ctx.memberFieldIds[param.name] = fieldId
|
||||
}
|
||||
}
|
||||
compileClassInfos[nameToken.value] = existingExternInfo
|
||||
} else {
|
||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
||||
ctx.memberMethodIds.putAll(baseIds.methodIds)
|
||||
ctx.nextFieldId = maxOf(ctx.nextFieldId, baseIds.nextFieldId)
|
||||
ctx.nextMethodId = maxOf(ctx.nextMethodId, baseIds.nextMethodId)
|
||||
for (member in ctx.declaredMembers) {
|
||||
val isOverride = ctx.memberOverrides[member] == true
|
||||
val hasBaseField = member in baseIds.fieldIds
|
||||
@@ -4761,24 +4902,25 @@ class Compiler(
|
||||
throw ScriptError(nameToken.pos, "member $member is marked 'override' but does not override anything")
|
||||
}
|
||||
} else {
|
||||
if (hasBaseField || hasBaseMethod) {
|
||||
throw ScriptError(nameToken.pos, "member $member overrides parent member but 'override' keyword is missing")
|
||||
if (hasBaseField || hasBaseMethod) {
|
||||
throw ScriptError(nameToken.pos, "member $member overrides parent member but 'override' keyword is missing")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ctx.memberFieldIds.containsKey(member)) {
|
||||
ctx.memberFieldIds[member] = ctx.nextFieldId++
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
if (param.accessType == null) return@forEach
|
||||
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
||||
ctx.memberFieldIds[param.name] = ctx.nextFieldId++
|
||||
}
|
||||
}
|
||||
if (!ctx.memberMethodIds.containsKey(member)) {
|
||||
ctx.memberMethodIds[member] = ctx.nextMethodId++
|
||||
}
|
||||
}
|
||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
||||
name = nameToken.value,
|
||||
fieldIds = ctx.memberFieldIds.toMap(),
|
||||
methodIds = ctx.memberMethodIds.toMap(),
|
||||
nextFieldId = ctx.nextFieldId,
|
||||
nextMethodId = ctx.nextMethodId
|
||||
)
|
||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
||||
name = nameToken.value,
|
||||
fieldIds = ctx.memberFieldIds.toMap(),
|
||||
methodIds = ctx.memberMethodIds.toMap(),
|
||||
nextFieldId = ctx.nextFieldId,
|
||||
nextMethodId = ctx.nextMethodId,
|
||||
baseNames = baseSpecs.map { it.name }
|
||||
)
|
||||
}
|
||||
}
|
||||
// restore if no body starts here
|
||||
@@ -5248,13 +5390,19 @@ class Compiler(
|
||||
}
|
||||
val extensionWrapperName = extTypeName?.let { extensionCallableName(it, name) }
|
||||
val classCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||
val memberMethodId = if (extTypeName == null) classCtx?.memberMethodIds?.get(name) else null
|
||||
var memberMethodId = if (extTypeName == null) classCtx?.memberMethodIds?.get(name) else null
|
||||
val externCallSignature = if (actualExtern) importManager.rootScope.getLocalRecordDirect(name)?.callSignature else null
|
||||
|
||||
val declKind = if (parentContext is CodeContext.ClassBody) SymbolKind.MEMBER else SymbolKind.FUNCTION
|
||||
resolutionSink?.declareSymbol(name, declKind, isMutable = false, pos = nameStartPos, isOverride = isOverride)
|
||||
if (parentContext is CodeContext.ClassBody && extTypeName == null) {
|
||||
parentContext.declaredMembers.add(name)
|
||||
if (!isStatic) {
|
||||
if (!parentContext.memberMethodIds.containsKey(name)) {
|
||||
parentContext.memberMethodIds[name] = parentContext.nextMethodId++
|
||||
}
|
||||
memberMethodId = parentContext.memberMethodIds[name]
|
||||
}
|
||||
}
|
||||
if (declKind != SymbolKind.MEMBER) {
|
||||
declareLocalName(name, isMutable = false)
|
||||
@@ -5444,7 +5592,12 @@ class Compiler(
|
||||
}
|
||||
val fnStatements = rawFnStatements?.let { stmt ->
|
||||
if (useBytecodeStatements && !containsUnsupportedForBytecode(stmt)) {
|
||||
wrapFunctionBytecode(stmt, name)
|
||||
val paramKnownClasses = mutableMapOf<String, ObjClass>()
|
||||
for (param in argsDeclaration.params) {
|
||||
val cls = resolveTypeDeclObjClass(param.type) ?: continue
|
||||
paramKnownClasses[param.name] = cls
|
||||
}
|
||||
wrapFunctionBytecode(stmt, name, paramKnownClasses)
|
||||
} else {
|
||||
stmt
|
||||
}
|
||||
@@ -5503,7 +5656,7 @@ class Compiler(
|
||||
override val pos: Pos = start
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
if (isDelegated) {
|
||||
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.Callable")
|
||||
val accessType = ObjString("Callable")
|
||||
val initValue = delegateExpression!!.execute(context)
|
||||
val finalDelegate = try {
|
||||
initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, context.thisObj))
|
||||
@@ -5549,7 +5702,7 @@ class Compiler(
|
||||
cls.instanceInitializers += object : Statement() {
|
||||
override val pos: Pos = start
|
||||
override suspend fun execute(scp: Scope): Obj {
|
||||
val accessType2 = scp.resolveQualifiedIdentifier("DelegateAccess.Callable")
|
||||
val accessType2 = ObjString("Callable")
|
||||
val initValue2 = delegateExpression.execute(scp)
|
||||
val finalDelegate2 = try {
|
||||
initValue2.invokeInstanceMethod(scp, "bind", Arguments(ObjString(name), accessType2, scp.thisObj))
|
||||
@@ -5785,14 +5938,16 @@ class Compiler(
|
||||
}
|
||||
|
||||
private fun resolveInitializerObjClass(initializer: Statement?): ObjClass? {
|
||||
if (initializer is BytecodeStatement) {
|
||||
val fn = initializer.bytecodeFunction()
|
||||
var unwrapped = initializer
|
||||
while (unwrapped is BytecodeStatement) {
|
||||
val fn = unwrapped.bytecodeFunction()
|
||||
if (fn.cmds.any { it is CmdListLiteral }) return ObjList.type
|
||||
if (fn.cmds.any { it is CmdMakeRange || it is CmdRangeIntBounds }) return ObjRange.type
|
||||
unwrapped = unwrapped.original
|
||||
}
|
||||
if (initializer is DoWhileStatement) {
|
||||
val bodyType = inferReturnClassFromStatement(initializer.body)
|
||||
val elseType = initializer.elseStatement?.let { inferReturnClassFromStatement(it) }
|
||||
if (unwrapped is DoWhileStatement) {
|
||||
val bodyType = inferReturnClassFromStatement(unwrapped.body)
|
||||
val elseType = unwrapped.elseStatement?.let { inferReturnClassFromStatement(it) }
|
||||
return when {
|
||||
bodyType == null && elseType == null -> null
|
||||
bodyType != null && elseType != null && bodyType == elseType -> bodyType
|
||||
@@ -5801,15 +5956,20 @@ class Compiler(
|
||||
else -> Obj.rootObjectType
|
||||
}
|
||||
}
|
||||
val directRef = unwrapDirectRef(initializer)
|
||||
val directRef = unwrapDirectRef(unwrapped)
|
||||
return when (directRef) {
|
||||
is ListLiteralRef -> ObjList.type
|
||||
is MapLiteralRef -> ObjMap.type
|
||||
is RangeRef -> ObjRange.type
|
||||
is ValueFnRef -> lambdaReturnTypeByRef[directRef]
|
||||
is CastRef -> resolveTypeRefClass(directRef.castTypeRef())
|
||||
is BinaryOpRef -> inferBinaryOpReturnClass(directRef)
|
||||
is ImplicitThisMethodCallRef -> {
|
||||
if (directRef.methodName() == "iterator") ObjIterator else null
|
||||
when (directRef.methodName()) {
|
||||
"iterator" -> ObjIterator
|
||||
"lazy" -> ObjLazyDelegate.type
|
||||
else -> inferMethodCallReturnClass(directRef.methodName())
|
||||
}
|
||||
}
|
||||
is ThisMethodSlotCallRef -> {
|
||||
if (directRef.methodName() == "iterator") ObjIterator else null
|
||||
@@ -5817,9 +5977,6 @@ class Compiler(
|
||||
is MethodCallRef -> {
|
||||
inferMethodCallReturnClass(directRef)
|
||||
}
|
||||
is ImplicitThisMethodCallRef -> {
|
||||
inferMethodCallReturnClass(directRef.methodName())
|
||||
}
|
||||
is FieldRef -> {
|
||||
val targetClass = resolveReceiverClassForMember(directRef.target)
|
||||
inferFieldReturnClass(targetClass, directRef.name)
|
||||
@@ -5828,12 +5985,20 @@ class Compiler(
|
||||
val target = directRef.target
|
||||
when {
|
||||
target is LocalSlotRef -> {
|
||||
callableReturnTypeByScopeId[target.scopeId]?.get(target.slot)
|
||||
?: if (target.name == "iterator") ObjIterator else resolveClassByName(target.name)
|
||||
when (target.name) {
|
||||
"lazy" -> ObjLazyDelegate.type
|
||||
"iterator" -> ObjIterator
|
||||
else -> callableReturnTypeByScopeId[target.scopeId]?.get(target.slot)
|
||||
?: resolveClassByName(target.name)
|
||||
}
|
||||
}
|
||||
target is LocalVarRef -> {
|
||||
callableReturnTypeByName[target.name]
|
||||
?: if (target.name == "iterator") ObjIterator else resolveClassByName(target.name)
|
||||
when (target.name) {
|
||||
"lazy" -> ObjLazyDelegate.type
|
||||
"iterator" -> ObjIterator
|
||||
else -> callableReturnTypeByName[target.name]
|
||||
?: resolveClassByName(target.name)
|
||||
}
|
||||
}
|
||||
target is LocalVarRef && target.name == "List" -> ObjList.type
|
||||
target is LocalVarRef && target.name == "Map" -> ObjMap.type
|
||||
@@ -5866,6 +6031,23 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureDelegateType(initializer: Statement) {
|
||||
val delegateClass = resolveClassByName("Delegate")
|
||||
?: throw ScriptError(initializer.pos, "Delegate type is not available")
|
||||
val initClass = resolveInitializerObjClass(initializer)
|
||||
?: unwrapDirectRef(initializer)?.let { inferObjClassFromRef(it) }
|
||||
?: throw ScriptError(initializer.pos, "Delegate type must be known at compile time")
|
||||
if (initClass !== delegateClass &&
|
||||
!initClass.allParentsSet.contains(delegateClass) &&
|
||||
!initClass.allImplementingNames.contains(delegateClass.className)
|
||||
) {
|
||||
throw ScriptError(
|
||||
initializer.pos,
|
||||
"Delegate initializer must return Delegate, got ${initClass.className}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveTypeDeclObjClass(type: TypeDecl): ObjClass? {
|
||||
val rawName = when (type) {
|
||||
is TypeDecl.Simple -> type.name
|
||||
@@ -5920,35 +6102,42 @@ class Compiler(
|
||||
(imported?.value as? ObjClass)?.let { return it }
|
||||
}
|
||||
val info = compileClassInfos[name] ?: return null
|
||||
return compileClassStubs.getOrPut(info.name) {
|
||||
val stub = ObjInstanceClass(info.name)
|
||||
val stub = compileClassStubs.getOrPut(info.name) {
|
||||
val parents = info.baseNames.mapNotNull { resolveClassByName(it) }
|
||||
ObjInstanceClass(info.name, *parents.toTypedArray())
|
||||
}
|
||||
if (stub is ObjInstanceClass) {
|
||||
for ((fieldName, fieldId) in info.fieldIds) {
|
||||
stub.createField(
|
||||
fieldName,
|
||||
ObjNull,
|
||||
isMutable = true,
|
||||
visibility = Visibility.Public,
|
||||
pos = Pos.builtIn,
|
||||
declaringClass = stub,
|
||||
type = ObjRecord.Type.Field,
|
||||
fieldId = fieldId
|
||||
)
|
||||
if (stub.members[fieldName] == null) {
|
||||
stub.createField(
|
||||
fieldName,
|
||||
ObjNull,
|
||||
isMutable = true,
|
||||
visibility = Visibility.Public,
|
||||
pos = Pos.builtIn,
|
||||
declaringClass = stub,
|
||||
type = ObjRecord.Type.Field,
|
||||
fieldId = fieldId
|
||||
)
|
||||
}
|
||||
}
|
||||
for ((methodName, methodId) in info.methodIds) {
|
||||
stub.createField(
|
||||
methodName,
|
||||
ObjNull,
|
||||
isMutable = false,
|
||||
visibility = Visibility.Public,
|
||||
pos = Pos.builtIn,
|
||||
declaringClass = stub,
|
||||
isAbstract = true,
|
||||
type = ObjRecord.Type.Fun,
|
||||
methodId = methodId
|
||||
)
|
||||
if (stub.members[methodName] == null) {
|
||||
stub.createField(
|
||||
methodName,
|
||||
ObjNull,
|
||||
isMutable = false,
|
||||
visibility = Visibility.Public,
|
||||
pos = Pos.builtIn,
|
||||
declaringClass = stub,
|
||||
isAbstract = true,
|
||||
type = ObjRecord.Type.Fun,
|
||||
methodId = methodId
|
||||
)
|
||||
}
|
||||
}
|
||||
stub
|
||||
}
|
||||
return stub
|
||||
}
|
||||
|
||||
private suspend fun parseBlockWithPredeclared(
|
||||
@@ -6169,8 +6358,8 @@ class Compiler(
|
||||
try {
|
||||
|
||||
val classCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||
val memberFieldId = if (extTypeName == null) classCtx?.memberFieldIds?.get(name) else null
|
||||
val memberMethodId = if (extTypeName == null) classCtx?.memberMethodIds?.get(name) else null
|
||||
var memberFieldId = if (extTypeName == null) classCtx?.memberFieldIds?.get(name) else null
|
||||
var memberMethodId = if (extTypeName == null) classCtx?.memberMethodIds?.get(name) else null
|
||||
|
||||
// Optional explicit type annotation
|
||||
cc.skipWsTokens()
|
||||
@@ -6300,14 +6489,54 @@ class Compiler(
|
||||
false
|
||||
}
|
||||
|
||||
if (declaringClassNameCaptured != null && extTypeName == null && !isStatic) {
|
||||
if (isDelegate || isProperty) {
|
||||
if (classCtx != null) {
|
||||
if (!classCtx.memberMethodIds.containsKey(name)) {
|
||||
classCtx.memberMethodIds[name] = classCtx.nextMethodId++
|
||||
}
|
||||
memberMethodId = classCtx.memberMethodIds[name]
|
||||
}
|
||||
} else {
|
||||
if (classCtx != null) {
|
||||
if (!classCtx.memberFieldIds.containsKey(name)) {
|
||||
classCtx.memberFieldIds[name] = classCtx.nextFieldId++
|
||||
}
|
||||
memberFieldId = classCtx.memberFieldIds[name]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val initialExpression = if (setNull || isProperty) null
|
||||
else parseStatement(true)
|
||||
?: throw ScriptError(effectiveEqToken!!.pos, "Expected initializer expression")
|
||||
|
||||
if (isDelegate && initialExpression != null) {
|
||||
ensureDelegateType(initialExpression)
|
||||
if (isMutable && resolveInitializerObjClass(initialExpression) == ObjLazyDelegate.type) {
|
||||
throw ScriptError(initialExpression.pos, "lazy delegate is read-only")
|
||||
}
|
||||
}
|
||||
|
||||
if (!isStatic && isDelegate) {
|
||||
markDelegatedSlot(name)
|
||||
}
|
||||
|
||||
if (declaringClassNameCaptured != null && extTypeName == null && !isStatic) {
|
||||
val directRef = unwrapDirectRef(initialExpression)
|
||||
val declClass = resolveTypeDeclObjClass(varTypeDecl)
|
||||
val initFromExpr = resolveInitializerObjClass(initialExpression)
|
||||
val isNullLiteral = (directRef as? ConstRef)?.constValue == ObjNull
|
||||
val initClass = when {
|
||||
isDelegate && declClass != null -> declClass
|
||||
declClass != null && isNullLiteral -> declClass
|
||||
else -> initFromExpr ?: declClass
|
||||
}
|
||||
if (initClass != null) {
|
||||
classFieldTypesByName.getOrPut(declaringClassNameCaptured) { mutableMapOf() }[name] = initClass
|
||||
}
|
||||
}
|
||||
|
||||
// Emit MiniValDecl for this declaration (before execution wiring), attach doc if any
|
||||
run {
|
||||
val declRange = MiniRange(pendingDeclStart ?: start, cc.currentPos())
|
||||
@@ -6418,7 +6647,7 @@ class Compiler(
|
||||
val initValue = initialExpression?.execute(scope)?.byValueCopy() ?: ObjNull
|
||||
if (isDelegate) {
|
||||
val accessTypeStr = if (isMutable) "Var" else "Val"
|
||||
val accessType = scope.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
|
||||
val accessType = ObjString(accessTypeStr)
|
||||
val finalDelegate = try {
|
||||
initValue.invokeInstanceMethod(
|
||||
scope,
|
||||
@@ -6736,7 +6965,7 @@ class Compiler(
|
||||
override suspend fun execute(scp: Scope): Obj {
|
||||
val initValue = initialExpression!!.execute(scp)
|
||||
val accessTypeStr = if (isMutable) "Var" else "Val"
|
||||
val accessType = scp.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
|
||||
val accessType = ObjString(accessTypeStr)
|
||||
val finalDelegate = try {
|
||||
initValue.invokeInstanceMethod(
|
||||
scp,
|
||||
@@ -6763,7 +6992,7 @@ class Compiler(
|
||||
} else {
|
||||
val initValue = initialExpression!!.execute(context)
|
||||
val accessTypeStr = if (isMutable) "Var" else "Val"
|
||||
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
|
||||
val accessType = ObjString(accessTypeStr)
|
||||
val finalDelegate = try {
|
||||
initValue.invokeInstanceMethod(
|
||||
context,
|
||||
@@ -6787,7 +7016,7 @@ class Compiler(
|
||||
} else {
|
||||
val initValue = initialExpression!!.execute(context)
|
||||
val accessTypeStr = if (isMutable) "Var" else "Val"
|
||||
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
|
||||
val accessType = ObjString(accessTypeStr)
|
||||
val finalDelegate = try {
|
||||
initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, ObjNull))
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
* 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.
|
||||
@@ -12,6 +12,7 @@
|
||||
* 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
|
||||
@@ -34,7 +35,7 @@ class DelegatedVarDeclStatement(
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
val initValue = initializer.execute(context)
|
||||
val accessTypeStr = if (isMutable) "Var" else "Val"
|
||||
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
|
||||
val accessType = ObjString(accessTypeStr)
|
||||
val finalDelegate = try {
|
||||
initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, ObjNull))
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -118,6 +118,16 @@ class Script(
|
||||
println()
|
||||
ObjVoid
|
||||
}
|
||||
addFn("call") {
|
||||
val callee = args.list.firstOrNull()
|
||||
?: raiseError("call requires a callable as the first argument")
|
||||
val rest = if (args.list.size > 1) {
|
||||
Arguments(args.list.drop(1))
|
||||
} else {
|
||||
Arguments.EMPTY
|
||||
}
|
||||
callee.callOn(createChildScope(pos, args = rest))
|
||||
}
|
||||
addFn("floor") {
|
||||
val x = args.firstAndOnly()
|
||||
(if (x is ObjInt) x
|
||||
@@ -393,7 +403,6 @@ class Script(
|
||||
val builder = requireOnlyArg<Statement>()
|
||||
ObjLazyDelegate(builder, this)
|
||||
}
|
||||
|
||||
addVoidFn("delay") {
|
||||
val a = args.firstAndOnly()
|
||||
when (a) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
* 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.
|
||||
@@ -12,29 +12,12 @@
|
||||
* 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.bytecode
|
||||
|
||||
import net.sergeych.lyng.BlockStatement
|
||||
import net.sergeych.lyng.DelegatedVarDeclStatement
|
||||
import net.sergeych.lyng.DestructuringVarDeclStatement
|
||||
import net.sergeych.lyng.ExpressionStatement
|
||||
import net.sergeych.lyng.IfStatement
|
||||
import net.sergeych.lyng.ParsedArgument
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.ToBoolStatement
|
||||
import net.sergeych.lyng.VarDeclStatement
|
||||
import net.sergeych.lyng.Visibility
|
||||
import net.sergeych.lyng.WhenCondition
|
||||
import net.sergeych.lyng.WhenEqualsCondition
|
||||
import net.sergeych.lyng.WhenInCondition
|
||||
import net.sergeych.lyng.WhenIsCondition
|
||||
import net.sergeych.lyng.WhenStatement
|
||||
import net.sergeych.lyng.extensionCallableName
|
||||
import net.sergeych.lyng.extensionPropertyGetterName
|
||||
import net.sergeych.lyng.extensionPropertySetterName
|
||||
import net.sergeych.lyng.*
|
||||
import net.sergeych.lyng.obj.*
|
||||
|
||||
class BytecodeCompiler(
|
||||
@@ -4490,6 +4473,7 @@ class BytecodeCompiler(
|
||||
val slot = resolveSlot(ref)
|
||||
val fromSlot = slot?.let { slotObjClass[it] }
|
||||
fromSlot
|
||||
?: slotTypeByScopeId[refScopeId(ref)]?.get(refSlot(ref))
|
||||
?: nameObjClass[ref.name]
|
||||
?: resolveTypeNameClass(ref.name)
|
||||
?: slotInitClassByKey[ScopeSlotKey(refScopeId(ref), refSlot(ref))]
|
||||
@@ -4501,9 +4485,16 @@ class BytecodeCompiler(
|
||||
match?.value
|
||||
}
|
||||
}
|
||||
is LocalVarRef -> resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
||||
?: nameObjClass[ref.name]
|
||||
?: resolveTypeNameClass(ref.name)
|
||||
is LocalVarRef -> {
|
||||
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
||||
if (fromSlot != null) return fromSlot
|
||||
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
|
||||
key?.let {
|
||||
slotTypeByScopeId[it.scopeId]?.get(it.slot)
|
||||
?: slotInitClassByKey[it]
|
||||
} ?: nameObjClass[ref.name]
|
||||
?: resolveTypeNameClass(ref.name)
|
||||
}
|
||||
is ListLiteralRef -> ObjList.type
|
||||
is MapLiteralRef -> ObjMap.type
|
||||
is RangeRef -> ObjRange.type
|
||||
|
||||
@@ -482,7 +482,8 @@ open class Obj {
|
||||
if (obj.type == ObjRecord.Type.Delegated) {
|
||||
val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
||||
val th = if (this === ObjVoid) ObjNull else this
|
||||
if (del.objClass.getInstanceMemberOrNull("getValue") == null) {
|
||||
val getValueRec = del.objClass.getInstanceMemberOrNull("getValue")
|
||||
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
|
||||
val wrapper = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
override suspend fun execute(s: Scope): Obj {
|
||||
|
||||
@@ -19,10 +19,7 @@ package net.sergeych.lyng.obj
|
||||
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import net.sergeych.lyng.Arguments
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Visibility
|
||||
import net.sergeych.lyng.canAccessMember
|
||||
import net.sergeych.lyng.*
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
import net.sergeych.lynon.LynonType
|
||||
@@ -173,6 +170,18 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
||||
}
|
||||
}
|
||||
del = del ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
||||
val getValueRec = del.objClass.getInstanceMemberOrNull("getValue")
|
||||
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
|
||||
val wrapper = object : Statement() {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
override suspend fun execute(s: Scope): Obj {
|
||||
val th2 = if (s.thisObj === ObjVoid) ObjNull else s.thisObj
|
||||
val allArgs = (listOf(th2, ObjString(name)) + s.args.list).toTypedArray()
|
||||
return del.invokeInstanceMethod(s, "invoke", Arguments(*allArgs))
|
||||
}
|
||||
}
|
||||
return obj.copy(value = wrapper, type = ObjRecord.Type.Other)
|
||||
}
|
||||
val res = del.invokeInstanceMethod(scope, "getValue", Arguments(this, ObjString(name)))
|
||||
return obj.copy(value = res, type = ObjRecord.Type.Other)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
* 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.
|
||||
@@ -12,13 +12,12 @@
|
||||
* 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.obj
|
||||
|
||||
import net.sergeych.lyng.Arguments
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.*
|
||||
|
||||
/**
|
||||
* Lazy delegate used by `val x by lazy { ... }`.
|
||||
@@ -41,7 +40,8 @@ class ObjLazyDelegate(
|
||||
return when (name) {
|
||||
"getValue" -> {
|
||||
if (!calculated) {
|
||||
cachedValue = builder.execute(capturedScope)
|
||||
val callScope = capturedScope.createChildScope(capturedScope.pos, args = Arguments.EMPTY)
|
||||
cachedValue = builder.callOn(callScope)
|
||||
calculated = true
|
||||
}
|
||||
cachedValue
|
||||
@@ -52,6 +52,26 @@ class ObjLazyDelegate(
|
||||
}
|
||||
|
||||
companion object {
|
||||
val type = ObjClass("LazyDelegate")
|
||||
val type = ObjClass("LazyDelegate").apply {
|
||||
implementingNames.add("Delegate")
|
||||
createField(
|
||||
"getValue",
|
||||
ObjNull,
|
||||
isMutable = false,
|
||||
visibility = Visibility.Public,
|
||||
pos = Pos.builtIn,
|
||||
declaringClass = this,
|
||||
type = ObjRecord.Type.Fun
|
||||
)
|
||||
createField(
|
||||
"setValue",
|
||||
ObjNull,
|
||||
isMutable = false,
|
||||
visibility = Visibility.Public,
|
||||
pos = Pos.builtIn,
|
||||
declaringClass = this,
|
||||
type = ObjRecord.Type.Fun
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user