Drive higher-order lambda inlining from call metadata

This commit is contained in:
sergeych committed 2026-04-21 20:51:16 +03:00
1 parent f4ab2ebab4
commit fbb5688696
11 files changed
+401 -121

No files matched your search

@@ -20,5 +20,30 @@ package net.sergeych.lyng
* Compile-time call metadata for known functions. Used to select lambda receiver semantics.
*/
data class CallSignature(
val tailBlockReceiverType: String? = null
)
val tailBlockReceiverType: String? = null,
val inlineHigherOrder: HigherOrderInline? = null
) {
data class HigherOrderInline(
val kind: Kind,
val result: ResultMode,
val argCount: Int = 1,
val lambdaArgIndex: Int = 0
)
enum class Kind {
UNARY_ARGUMENT,
RECEIVER,
ITERABLE,
MAP_GET_OR_PUT
}
enum class ResultMode {
BLOCK_RESULT,
RETURN_RECEIVER,
FOR_EACH,
MAP,
FILTER,
MAP_NOT_NULL,
ASSOCIATE_BY
}
}
@@ -187,6 +187,7 @@ class Compiler(
private val callableReturnTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
private val callableReturnTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
private val callableReturnTypeDeclByName: MutableMap<String, TypeDecl> = mutableMapOf()
private val callSignatureByName: MutableMap<String, CallSignature> = mutableMapOf()
private val lambdaReturnTypeByRef: MutableMap<ObjRef, ObjClass> = mutableMapOf()
private val exactLambdaRefByScopeId: MutableMap<Int, MutableMap<Int, LambdaFnRef>> = mutableMapOf()
private val lambdaCaptureEntriesByRef: MutableMap<ValueFnRef, List<net.sergeych.lyng.bytecode.LambdaCaptureEntry>> =
@@ -265,6 +266,11 @@ class Compiler(
if (plan.slots.containsKey(name)) continue
declareSlotNameIn(plan, name, record.isMutable, record.type == ObjRecord.Type.Delegated)
scopeSeedNames.add(name)
record.callSignature?.let { signature ->
if (!callSignatureByName.containsKey(name)) {
callSignatureByName[name] = signature
}
}
if (record.typeDecl != null && nameTypeDecl[name] == null) {
nameTypeDecl[name] = record.typeDecl
if (nameObjClass[name] == null) {
@@ -291,6 +297,11 @@ class Compiler(
)
scopeSeedNames.add(getterName)
}
record.callSignature?.let { signature ->
if (!callSignatureByName.containsKey(getterName)) {
callSignatureByName[getterName] = signature
}
}
val prop = record.value as? ObjProperty
if (prop?.setter != null) {
val setterName = extensionPropertySetterName(cls.className, name)
@@ -316,6 +327,11 @@ class Compiler(
)
scopeSeedNames.add(callableName)
}
record.callSignature?.let { signature ->
if (!callSignatureByName.containsKey(callableName)) {
callSignatureByName[callableName] = signature
}
}
}
}
}
@@ -347,6 +363,11 @@ class Compiler(
record.type == ObjRecord.Type.Delegated
)
scopeSeedNames.add(name)
record.callSignature?.let { signature ->
if (!callSignatureByName.containsKey(name)) {
callSignatureByName[name] = signature
}
}
if (record.typeDecl != null && nameTypeDecl[name] == null) {
nameTypeDecl[name] = record.typeDecl
if (nameObjClass[name] == null) {
@@ -762,6 +783,29 @@ class Compiler(
?: importManager.rootScope.getLocalRecordDirect(name)?.callSignature
}
private fun declaredCallSignature(name: String, extTypeName: String?, actualExtern: Boolean): CallSignature? {
val imported = if (actualExtern) {
importManager.rootScope.getLocalRecordDirect(name)?.callSignature
} else {
null
}
val inferred = when {
extTypeName == "Iterable" && name == "filter" -> CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.ITERABLE,
result = CallSignature.ResultMode.FILTER
)
)
else -> null
}
return when {
imported == null -> inferred
inferred == null -> imported
imported.inlineHigherOrder != null -> imported
else -> imported.copy(inlineHigherOrder = inferred.inlineHigherOrder)
}
}
internal data class MemberIds(val fieldId: Int?, val methodId: Int?)
private fun resolveMemberIds(name: String, pos: Pos, qualifier: String? = null): MemberIds {
@@ -1429,6 +1473,11 @@ class Compiler(
}
private fun seedImportTypeMetadata(name: String, record: ObjRecord) {
record.callSignature?.let { signature ->
if (!callSignatureByName.containsKey(name)) {
callSignatureByName[name] = signature
}
}
if (record.typeDecl != null && nameTypeDecl[name] == null) {
nameTypeDecl[name] = record.typeDecl
}
@@ -1904,6 +1953,7 @@ class Compiler(
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName,
callSignatureByName = callSignatureByName,
externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
scopeRefPosByName = moduleReferencePosByName,
@@ -2260,6 +2310,7 @@ class Compiler(
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName,
callSignatureByName = callSignatureByName,
externCallableNames = externCallableNames,
externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
@@ -2294,6 +2345,7 @@ class Compiler(
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName,
callSignatureByName = callSignatureByName,
externCallableNames = externCallableNames,
externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
@@ -2353,6 +2405,7 @@ class Compiler(
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName,
callSignatureByName = callSignatureByName,
externCallableNames = externCallableNames,
externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
@@ -9208,7 +9261,7 @@ class Compiler(
val extensionWrapperName = extTypeName?.let { extensionCallableName(it, name) }
val classCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
var memberMethodId = if (extTypeName == null) classCtx?.memberMethodIds?.get(name) else null
val externCallSignature = if (actualExtern) importManager.rootScope.getLocalRecordDirect(name)?.callSignature else null
val externCallSignature = declaredCallSignature(name, extTypeName, actualExtern)
val declKind = if (parentContext is CodeContext.ClassBody) SymbolKind.MEMBER else SymbolKind.FUNCTION
resolutionSink?.declareSymbol(name, declKind, isMutable = false, pos = nameStartPos, isOverride = isOverride)
@@ -9230,7 +9283,9 @@ class Compiler(
}
if (extensionWrapperName != null) {
declareLocalName(extensionWrapperName, isMutable = false)
externCallSignature?.let { callSignatureByName[extensionWrapperName] = it }
}
externCallSignature?.let { callSignatureByName[name] = it }
if (actualExtern && declKind != SymbolKind.MEMBER) {
externCallableNames.add(name)
}
@@ -118,7 +118,14 @@ internal suspend fun executeFunctionDecl(
scope.addExtension(
type,
spec.name,
ObjRecord(ObjUnset, isMutable = false, visibility = spec.visibility, declaringClass = null, type = ObjRecord.Type.Delegated).apply {
ObjRecord(
ObjUnset,
isMutable = false,
visibility = spec.visibility,
declaringClass = null,
type = ObjRecord.Type.Delegated,
callSignature = spec.externCallSignature
).apply {
delegate = finalDelegate
}
)
@@ -135,7 +142,8 @@ internal suspend fun executeFunctionDecl(
null,
spec.startPos,
isTransient = spec.isTransient,
type = ObjRecord.Type.Delegated
type = ObjRecord.Type.Delegated,
callSignature = spec.externCallSignature
).apply {
delegate = finalDelegate
}
@@ -204,6 +212,7 @@ internal suspend fun executeFunctionDecl(
visibility = spec.visibility,
pos = spec.startPos,
type = ObjRecord.Type.Fun,
callSignature = spec.externCallSignature,
)
} else {
scope.addExtension(
@@ -215,6 +224,7 @@ internal suspend fun executeFunctionDecl(
visibility = spec.visibility,
declaringClass = null,
type = ObjRecord.Type.Fun,
callSignature = spec.externCallSignature,
typeDecl = spec.typeDecl
)
)
@@ -227,6 +237,7 @@ internal suspend fun executeFunctionDecl(
wrapper,
spec.visibility,
recordType = ObjRecord.Type.Fun,
callSignature = spec.externCallSignature,
typeDecl = spec.typeDecl
)
} ?: run {
@@ -245,7 +256,8 @@ internal suspend fun executeFunctionDecl(
isOverride = spec.isOverride,
type = ObjRecord.Type.Fun,
methodId = spec.memberMethodId,
typeDecl = spec.typeDecl
typeDecl = spec.typeDecl,
callSignature = spec.externCallSignature
)
val memberValue = cls.members[spec.name]?.value ?: compiledFnBody
scope.addItem(
@@ -42,6 +42,7 @@ class BytecodeCompiler(
private val enumEntriesByName: Map<String, List<String>> = emptyMap(),
private val callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
private val callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
private val callSignatureByName: Map<String, CallSignature> = emptyMap(),
private val externCallableNames: Set<String> = emptySet(),
private val externBindingNames: Set<String> = emptySet(),
private val preparedModuleBindingNames: Set<String> = emptySet(),
@@ -4980,22 +4981,22 @@ class BytecodeCompiler(
}
private fun compileInlineHigherOrderMethodCall(ref: MethodCallRef): CompiledValue? {
val spec = inlineHigherOrderMethodSpec(ref.name) ?: return null
val spec = inlineHigherOrderMethodSpec(ref) ?: return null
if (ref.args.size != spec.argCount || ref.args.any { it.isSplat || it.name != null }) return null
if (!ref.explicitTypeArgs.isNullOrEmpty()) return null
val lambdaRef = extractExactLambdaRef(ref.args[spec.lambdaArgIndex].value) ?: return null
val inlineRef = lambdaRef.inlineBodyRef ?: return null
return when (spec.kind) {
InlineHigherOrderMethodKind.UNARY_ARGUMENT -> {
CallSignature.Kind.UNARY_ARGUMENT -> {
if (!isMethodInlineSafe(lambdaRef, inlineRef, allowReceiverRefs = false, allowCaptures = true)) return null
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
val receiverObj = ensureObjSlot(receiver)
compileOptionalInlineMethod(ref.isOptional, receiverObj) {
val bindings = prepareInlineLambdaBindingsFromValues(lambdaRef, listOf(receiver)) ?: return@compileOptionalInlineMethod null
when (spec.result) {
InlineHigherOrderResultMode.BLOCK_RESULT ->
CallSignature.ResultMode.BLOCK_RESULT ->
compileInlineLambdaBody(lambdaRef, inlineRef, bindings)
InlineHigherOrderResultMode.RETURN_RECEIVER -> {
CallSignature.ResultMode.RETURN_RECEIVER -> {
compileInlineLambdaBody(lambdaRef, inlineRef, bindings) ?: return@compileOptionalInlineMethod null
CompiledValue(receiverObj.slot, SlotType.OBJ)
}
@@ -5003,7 +5004,7 @@ class BytecodeCompiler(
}
}
}
InlineHigherOrderMethodKind.RECEIVER -> {
CallSignature.Kind.RECEIVER -> {
val receiverInfo = receiverInlineInfo(lambdaRef) ?: return null
if (!isMethodInlineSafe(lambdaRef, inlineRef, allowReceiverRefs = true, allowCaptures = true)) return null
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
@@ -5012,7 +5013,7 @@ class BytecodeCompiler(
compileInlineReceiverLambdaInvocation(receiverObj, lambdaRef, spec.result, receiverInfo)
}
}
InlineHigherOrderMethodKind.ITERABLE -> {
CallSignature.Kind.ITERABLE -> {
if (!isMethodInlineSafe(lambdaRef, inlineRef, allowReceiverRefs = false, allowCaptures = true)) return null
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
val receiverObj = ensureObjSlot(receiver)
@@ -5020,7 +5021,7 @@ class BytecodeCompiler(
compileInlineIterableLambdaLoop(receiverObj, ref, lambdaRef, inlineRef, spec.result)
}
}
InlineHigherOrderMethodKind.MAP_GET_OR_PUT -> {
CallSignature.Kind.MAP_GET_OR_PUT -> {
val receiverClass = resolveReceiverClass(ref.receiver) ?: return null
if (receiverClass != ObjMap.type) return null
if (!isMethodInlineSafe(lambdaRef, inlineRef, allowReceiverRefs = false, allowCaptures = true)) return null
@@ -5131,81 +5132,20 @@ class BytecodeCompiler(
return slot
}
private enum class InlineHigherOrderMethodKind {
UNARY_ARGUMENT,
RECEIVER,
ITERABLE,
MAP_GET_OR_PUT
}
private enum class InlineHigherOrderResultMode {
BLOCK_RESULT,
RETURN_RECEIVER,
FOR_EACH,
MAP,
FILTER,
MAP_NOT_NULL,
ASSOCIATE_BY
}
private data class InlineHigherOrderMethodSpec(
val kind: InlineHigherOrderMethodKind,
val result: InlineHigherOrderResultMode,
val argCount: Int = 1,
val lambdaArgIndex: Int = 0
)
private data class InlineReceiverInfo(
val explicitBindings: List<Pair<String, Int>>,
val thisTypeName: String?
)
private fun inlineHigherOrderMethodSpec(name: String): InlineHigherOrderMethodSpec? {
return when (name) {
"let" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.UNARY_ARGUMENT,
InlineHigherOrderResultMode.BLOCK_RESULT
)
"also" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.UNARY_ARGUMENT,
InlineHigherOrderResultMode.RETURN_RECEIVER
)
"apply" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.RECEIVER,
InlineHigherOrderResultMode.RETURN_RECEIVER
)
"run" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.RECEIVER,
InlineHigherOrderResultMode.BLOCK_RESULT
)
"forEach" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.ITERABLE,
InlineHigherOrderResultMode.FOR_EACH
)
"map" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.ITERABLE,
InlineHigherOrderResultMode.MAP
)
"filter" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.ITERABLE,
InlineHigherOrderResultMode.FILTER
)
"mapNotNull" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.ITERABLE,
InlineHigherOrderResultMode.MAP_NOT_NULL
)
"associateBy" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.ITERABLE,
InlineHigherOrderResultMode.ASSOCIATE_BY
)
"getOrPut" -> InlineHigherOrderMethodSpec(
InlineHigherOrderMethodKind.MAP_GET_OR_PUT,
InlineHigherOrderResultMode.BLOCK_RESULT,
argCount = 2,
lambdaArgIndex = 1
)
else -> null
}
private fun inlineHigherOrderMethodSpec(ref: MethodCallRef): CallSignature.HigherOrderInline? {
resolveReceiverClass(ref.receiver)
?.resolveInstanceMember(ref.name)
?.record
?.callSignature
?.inlineHigherOrder
?.let { return it }
val receiverClass = resolveReceiverClass(ref.receiver) ?: return null
return extensionCallableSignature(receiverClass, ref.name)?.inlineHigherOrder
}
private fun compileOptionalInlineMethod(
@@ -5329,7 +5269,7 @@ class BytecodeCompiler(
private fun compileInlineReceiverLambdaInvocation(
receiverObj: CompiledValue,
lambdaRef: LambdaFnRef,
behavior: InlineHigherOrderResultMode,
behavior: CallSignature.ResultMode,
receiverInfo: InlineReceiverInfo
): CompiledValue? {
val inlineRef = lambdaRef.inlineBodyRef ?: return null
@@ -5338,9 +5278,9 @@ class BytecodeCompiler(
inlineThisBindings.addLast(previousBinding)
return try {
when (behavior) {
InlineHigherOrderResultMode.BLOCK_RESULT ->
CallSignature.ResultMode.BLOCK_RESULT ->
compileInlineLambdaBody(lambdaRef, inlineRef, receiverInfo.explicitBindings)
InlineHigherOrderResultMode.RETURN_RECEIVER -> {
CallSignature.ResultMode.RETURN_RECEIVER -> {
compileInlineLambdaBody(lambdaRef, inlineRef, receiverInfo.explicitBindings) ?: return null
CompiledValue(receiverSlot, SlotType.OBJ)
}
@@ -5372,7 +5312,7 @@ class BytecodeCompiler(
ref: MethodCallRef,
lambdaRef: LambdaFnRef,
inlineRef: ObjRef,
behavior: InlineHigherOrderResultMode
behavior: CallSignature.ResultMode
): CompiledValue? {
val iterableMethods = ObjIterable.instanceMethodIdMap(includeAbstract = true)
val iteratorMethodId = iterableMethods["iterator"]
@@ -5388,17 +5328,17 @@ class BytecodeCompiler(
builder.emit(Opcode.ITER_PUSH, iterSlot)
val result = when (behavior) {
InlineHigherOrderResultMode.FOR_EACH -> CompiledValue(ensureVoidSlot(), SlotType.OBJ)
InlineHigherOrderResultMode.MAP,
InlineHigherOrderResultMode.FILTER,
InlineHigherOrderResultMode.MAP_NOT_NULL -> createEmptyMutableList() ?: return null
InlineHigherOrderResultMode.ASSOCIATE_BY -> createEmptyMutableMap() ?: return null
CallSignature.ResultMode.FOR_EACH -> CompiledValue(ensureVoidSlot(), SlotType.OBJ)
CallSignature.ResultMode.MAP,
CallSignature.ResultMode.FILTER,
CallSignature.ResultMode.MAP_NOT_NULL -> createEmptyMutableList() ?: return null
CallSignature.ResultMode.ASSOCIATE_BY -> createEmptyMutableMap() ?: return null
else -> return null
}
if (behavior == InlineHigherOrderResultMode.FILTER) {
if (behavior == CallSignature.ResultMode.FILTER) {
listElementClassFromReceiverRef(ref.receiver)?.let { listElementClassBySlot[result.slot] = it }
}
if (behavior == InlineHigherOrderResultMode.MAP) {
if (behavior == CallSignature.ResultMode.MAP) {
lambdaRef.inferredReturnClass?.let { listElementClassBySlot[result.slot] = it }
}
@@ -5418,14 +5358,14 @@ class BytecodeCompiler(
val nextObj = ensureObjSlot(CompiledValue(nextSlot, SlotType.UNKNOWN))
val bindings = prepareInlineLambdaBindingsFromValues(lambdaRef, listOf(nextObj)) ?: return null
when (behavior) {
InlineHigherOrderResultMode.FOR_EACH -> {
CallSignature.ResultMode.FOR_EACH -> {
compileInlineLambdaBody(lambdaRef, inlineRef, bindings) ?: return null
}
InlineHigherOrderResultMode.MAP -> {
CallSignature.ResultMode.MAP -> {
val mapped = compileInlineLambdaBody(lambdaRef, inlineRef, bindings) ?: return null
appendToList(result, mapped) ?: return null
}
InlineHigherOrderResultMode.FILTER -> {
CallSignature.ResultMode.FILTER -> {
val predicate = compileInlineLambdaBody(lambdaRef, inlineRef, bindings) ?: return null
val predicateBool = compileValueAsBool(predicate)
val skipLabel = builder.label()
@@ -5436,7 +5376,7 @@ class BytecodeCompiler(
appendToList(result, nextObj) ?: return null
builder.mark(skipLabel)
}
InlineHigherOrderResultMode.MAP_NOT_NULL -> {
CallSignature.ResultMode.MAP_NOT_NULL -> {
val mapped = compileInlineLambdaBody(lambdaRef, inlineRef, bindings) ?: return null
val mappedObj = ensureObjSlot(mapped)
val nullSlot = allocSlot()
@@ -5451,7 +5391,7 @@ class BytecodeCompiler(
appendToList(result, mappedObj) ?: return null
builder.mark(skipLabel)
}
InlineHigherOrderResultMode.ASSOCIATE_BY -> {
CallSignature.ResultMode.ASSOCIATE_BY -> {
val key = compileInlineLambdaBody(lambdaRef, inlineRef, bindings) ?: return null
appendToMap(result, key, nextObj)
}
@@ -5986,11 +5926,11 @@ class BytecodeCompiler(
return names
}
private fun resolveExtensionSlotByReceiverNames(
private fun resolveExtensionWrapperNameByReceiverNames(
receiverClass: ObjClass,
memberName: String,
wrapperName: (String, String) -> String
): CompiledValue? {
): String? {
for (receiverName in extensionReceiverTypeNames(receiverClass)) {
val candidate = wrapperName(receiverName, memberName)
if (allowedScopeNames != null &&
@@ -5999,15 +5939,15 @@ class BytecodeCompiler(
) {
continue
}
resolveDirectNameSlot(candidate)?.let { return it }
if (resolveDirectNameSlot(candidate) != null) return candidate
}
return null
}
private fun resolveUniqueExtensionWrapperSlot(
private fun resolveUniqueExtensionWrapperName(
memberName: String,
wrapperPrefix: String
): CompiledValue? {
): String? {
val suffix = "__$memberName"
val candidates = LinkedHashSet<String>()
for (name in localSlotIndexByName.keys) {
@@ -6020,8 +5960,31 @@ class BytecodeCompiler(
candidates.add(name)
}
}
if (candidates.size != 1) return null
return resolveDirectNameSlot(candidates.first())
return candidates.singleOrNull()
}
private fun resolveExtensionSlotByReceiverNames(
receiverClass: ObjClass,
memberName: String,
wrapperName: (String, String) -> String
): CompiledValue? {
val resolvedName = resolveExtensionWrapperNameByReceiverNames(receiverClass, memberName, wrapperName) ?: return null
return resolveDirectNameSlot(resolvedName)
}
private fun resolveUniqueExtensionWrapperSlot(
memberName: String,
wrapperPrefix: String
): CompiledValue? {
val resolvedName = resolveUniqueExtensionWrapperName(memberName, wrapperPrefix) ?: return null
return resolveDirectNameSlot(resolvedName)
}
private fun extensionCallableSignature(receiverClass: ObjClass, memberName: String): CallSignature? {
val wrapperName = resolveExtensionWrapperNameByReceiverNames(receiverClass, memberName, ::extensionCallableName)
?: resolveUniqueExtensionWrapperName(memberName, "__ext__")
?: return null
return callSignatureByName[wrapperName]
}
private fun resolveExtensionCallableSlot(receiverClass: ObjClass, memberName: String): CompiledValue? {
@@ -106,6 +106,7 @@ class BytecodeStatement private constructor(
enumEntriesByName: Map<String, List<String>> = emptyMap(),
callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
callSignatureByName: Map<String, CallSignature> = emptyMap(),
externCallableNames: Set<String> = emptySet(),
externBindingNames: Set<String> = emptySet(),
preparedModuleBindingNames: Set<String> = emptySet(),
@@ -146,6 +147,7 @@ class BytecodeStatement private constructor(
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName,
callSignatureByName = callSignatureByName,
externCallableNames = externCallableNames,
externBindingNames = externBindingNames,
preparedModuleBindingNames = preparedModuleBindingNames,
@@ -99,10 +99,11 @@ fun ObjClass.addFnDoc(
visibility: Visibility = Visibility.Public,
tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null,
callSignature: net.sergeych.lyng.CallSignature? = null,
code: suspend ScopeFacade.() -> Obj
) {
// Register runtime method
addFn(name, isOpen, visibility, code = code)
addFn(name, isOpen, visibility, callSignature = callSignature, code = code)
// Register docs for the member under this class
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
classDoc(this@addFnDoc.className, doc = "") {
@@ -137,9 +138,10 @@ fun ObjClass.addClassFnDoc(
isOpen: Boolean = false,
tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null,
callSignature: net.sergeych.lyng.CallSignature? = null,
code: suspend ScopeFacade.() -> Obj
) {
addClassFn(name, isOpen, code)
addClassFn(name, isOpen, callSignature, code)
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
classDoc(this@addClassFnDoc.className, doc = "") {
method(name = name, doc = doc, params = params, returns = returns, isStatic = true, tags = tags)
@@ -828,7 +828,13 @@ open class Obj {
name = "let",
doc = "Calls the specified function block with `this` value as its argument and returns its result.",
params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.UNARY_ARGUMENT,
result = CallSignature.ResultMode.BLOCK_RESULT
)
)
) {
call(args.firstAndOnly(), Arguments(thisObj))
}
@@ -836,7 +842,13 @@ open class Obj {
name = "apply",
doc = "Calls the specified function block with `this` value as its receiver and returns `this` value.",
params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.RECEIVER,
result = CallSignature.ResultMode.RETURN_RECEIVER
)
)
) {
val body = args.firstAndOnly()
val scope = requireScope()
@@ -852,7 +864,13 @@ open class Obj {
name = "also",
doc = "Calls the specified function block with `this` value as its argument and returns `this` value.",
params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.UNARY_ARGUMENT,
result = CallSignature.ResultMode.RETURN_RECEIVER
)
)
) {
call(args.firstAndOnly(), Arguments(thisObj))
thisObj
@@ -861,7 +879,13 @@ open class Obj {
name = "run",
doc = "Calls the specified function block with `this` value as its receiver and returns its result.",
params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.RECEIVER,
result = CallSignature.ResultMode.BLOCK_RESULT
)
)
) {
call(args.firstAndOnly())
}
@@ -849,6 +849,7 @@ open class ObjClass(
fieldId: Int? = null,
methodId: Int? = null,
typeDecl: net.sergeych.lyng.TypeDecl? = null,
callSignature: net.sergeych.lyng.CallSignature? = null,
): ObjRecord {
// Validation of override rules: only for non-system declarations
var existing: ObjRecord? = null
@@ -949,6 +950,7 @@ open class ObjClass(
isOverride = isOverride,
isTransient = isTransient,
type = type,
callSignature = callSignature,
typeDecl = typeDecl,
memberName = name,
fieldId = effectiveFieldId,
@@ -975,7 +977,8 @@ open class ObjClass(
isTransient: Boolean = false,
type: ObjRecord.Type = ObjRecord.Type.Field,
fieldId: Int? = null,
methodId: Int? = null
methodId: Int? = null,
callSignature: net.sergeych.lyng.CallSignature? = null
): ObjRecord {
initClassScope()
val existing = classScope!!.objects[name]
@@ -1016,6 +1019,7 @@ open class ObjClass(
writeVisibility,
recordType = type,
isTransient = isTransient,
callSignature = callSignature,
fieldId = effectiveFieldId,
methodId = effectiveMethodId
)
@@ -1035,6 +1039,7 @@ open class ObjClass(
isOverride: Boolean = false,
pos: Pos = Pos.builtIn,
methodId: Int? = null,
callSignature: net.sergeych.lyng.CallSignature? = null,
code: (suspend net.sergeych.lyng.ScopeFacade.() -> Obj)? = null
) {
val stmt = code?.let { ObjExternCallable.fromBridge { it() } } ?: ObjNull
@@ -1042,7 +1047,8 @@ open class ObjClass(
name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass,
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
type = ObjRecord.Type.Fun,
methodId = methodId
methodId = methodId,
callSignature = callSignature
)
}
@@ -1074,8 +1080,19 @@ open class ObjClass(
}
fun addClassConst(name: String, value: Obj) = createClassField(name, value)
fun addClassFn(name: String, isOpen: Boolean = false, code: suspend net.sergeych.lyng.ScopeFacade.() -> Obj) {
createClassField(name, ObjExternCallable.fromBridge { code() }, isOpen, type = ObjRecord.Type.Fun)
fun addClassFn(
name: String,
isOpen: Boolean = false,
callSignature: net.sergeych.lyng.CallSignature? = null,
code: suspend net.sergeych.lyng.ScopeFacade.() -> Obj
) {
createClassField(
name,
ObjExternCallable.fromBridge { code() },
isOpen,
type = ObjRecord.Type.Fun,
callSignature = callSignature
)
}
@@ -18,6 +18,7 @@
package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments
import net.sergeych.lyng.CallSignature
import net.sergeych.lyng.miniast.ParamDoc
import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.addPropertyDoc
@@ -189,7 +190,13 @@ val ObjIterable by lazy {
doc = "Build a map from elements using the lambda result as key.",
params = listOf(ParamDoc("keySelector")),
returns = type("lyng.Map"),
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.ITERABLE,
result = CallSignature.ResultMode.ASSOCIATE_BY
)
)
) {
val association = requireOnlyArg<Obj>()
val result = ObjMap()
@@ -204,7 +211,13 @@ val ObjIterable by lazy {
doc = "Apply the lambda to each element in iteration order.",
params = listOf(ParamDoc("action")),
isOpen = true,
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.ITERABLE,
result = CallSignature.ResultMode.FOR_EACH
)
)
) {
val scope = requireScope()
val it = thisObj.invokeInstanceMethod(scope, "iterator")
@@ -222,7 +235,13 @@ val ObjIterable by lazy {
params = listOf(ParamDoc("transform")),
returns = type("lyng.List"),
isOpen = true,
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.ITERABLE,
result = CallSignature.ResultMode.MAP
)
)
) {
val fn = requiredArg<Obj>(0)
val result = mutableListOf<Obj>()
@@ -238,7 +257,13 @@ val ObjIterable by lazy {
params = listOf(ParamDoc("transform")),
returns = type("lyng.List"),
isOpen = true,
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.ITERABLE,
result = CallSignature.ResultMode.MAP_NOT_NULL
)
)
) {
val fn = requiredArg<Obj>(0)
val result = mutableListOf<Obj>()
@@ -19,6 +19,7 @@ package net.sergeych.lyng.obj
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import net.sergeych.lyng.CallSignature
import net.sergeych.lyng.Scope
import net.sergeych.lyng.miniast.*
import net.sergeych.lynon.LynonDecoder
@@ -262,7 +263,15 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
doc = "Get value by key or compute, store, and return the default from a lambda.",
params = listOf(ParamDoc("key"), ParamDoc("default")),
returns = type("lyng.Any"),
moduleName = "lyng.stdlib"
moduleName = "lyng.stdlib",
callSignature = CallSignature(
inlineHigherOrder = CallSignature.HigherOrderInline(
kind = CallSignature.Kind.MAP_GET_OR_PUT,
result = CallSignature.ResultMode.BLOCK_RESULT,
argCount = 2,
lambdaArgIndex = 1
)
)
) {
val key = requiredArg<Obj>(0)
thisAs<ObjMap>().map.getOrPut(key) {