Compare commits
3 Commits
18394ce286
...
c54d947f1c
| Author | SHA1 | Date | |
|---|---|---|---|
| c54d947f1c | |||
| 8a32ffe655 | |||
| 65edf9fe67 |
@ -261,14 +261,14 @@ moduleScope.eval("class Counter { extern var value: Int; extern fun inc(by: Int)
|
||||
moduleScope.bind("Counter") {
|
||||
addVar(
|
||||
name = "value",
|
||||
get = { _, self -> self.readField(this, "value").value },
|
||||
set = { _, self, v -> self.writeField(this, "value", v) }
|
||||
get = { thisObj.readField(this, "value").value },
|
||||
set = { v -> thisObj.writeField(this, "value", v) }
|
||||
)
|
||||
addFun("inc") { _, self, args ->
|
||||
addFun("inc") {
|
||||
val by = args.requiredArg<ObjInt>(0).value
|
||||
val current = self.readField(this, "value").value as ObjInt
|
||||
val current = thisObj.readField(this, "value").value as ObjInt
|
||||
val next = ObjInt(current.value + by)
|
||||
self.writeField(this, "value", next)
|
||||
thisObj.writeField(this, "value", next)
|
||||
next
|
||||
}
|
||||
}
|
||||
@ -303,16 +303,16 @@ moduleScope.eval("""
|
||||
moduleScope.bindObject("HostObject") {
|
||||
classData = "OK"
|
||||
init { _ -> data = 0L }
|
||||
addFun("add") { _, _, args ->
|
||||
addFun("add") {
|
||||
val a = args.requiredArg<ObjInt>(0).value
|
||||
val b = args.requiredArg<ObjInt>(1).value
|
||||
ObjInt.of(a + b)
|
||||
}
|
||||
addVal("status") { _, _ -> ObjString(classData as String) }
|
||||
addVal("status") { ObjString(classData as String) }
|
||||
addVar(
|
||||
"count",
|
||||
get = { _, inst -> ObjInt.of((inst as ObjInstance).data as Long) },
|
||||
set = { _, inst, value -> (inst as ObjInstance).data = (value as ObjInt).value }
|
||||
get = { ObjInt.of((thisObj as ObjInstance).data as Long) },
|
||||
set = { value -> (thisObj as ObjInstance).data = (value as ObjInt).value }
|
||||
)
|
||||
}
|
||||
```
|
||||
@ -336,7 +336,7 @@ moduleScope.eval("""
|
||||
""".trimIndent())
|
||||
|
||||
moduleScope.bindObject("HostObject") {
|
||||
addFun("ping") { _, _, _ -> ObjInt.of(7) }
|
||||
addFun("ping") { ObjInt.of(7) }
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -220,6 +220,7 @@ data class ParsedArgument(
|
||||
val list: List<Obj>,
|
||||
val tailBlockMode: Boolean = false,
|
||||
val named: Map<String, Obj> = emptyMap(),
|
||||
val explicitTypeArgs: List<TypeDecl> = emptyList(),
|
||||
) : List<Obj> by list {
|
||||
|
||||
constructor(vararg values: Obj) : this(values.toList())
|
||||
|
||||
@ -4487,6 +4487,7 @@ class Compiler(
|
||||
is ListLiteralRef -> ObjList.type
|
||||
is MapLiteralRef -> ObjMap.type
|
||||
is RangeRef -> ObjRange.type
|
||||
is ClassOperatorRef -> ObjClassType
|
||||
is CastRef -> resolveTypeRefClass(ref.castTypeRef())
|
||||
else -> null
|
||||
}
|
||||
@ -4580,6 +4581,7 @@ class Compiler(
|
||||
is ListLiteralRef -> ObjList.type
|
||||
is MapLiteralRef -> ObjMap.type
|
||||
is RangeRef -> ObjRange.type
|
||||
is ClassOperatorRef -> ObjClassType
|
||||
is CastRef -> resolveTypeRefClass(ref.castTypeRef())
|
||||
is QualifiedThisRef -> resolveClassByName(ref.typeName)
|
||||
is StatementRef -> (ref.statement as? ExpressionStatement)?.let { resolveReceiverClassForMember(it.ref) }
|
||||
@ -5346,6 +5348,10 @@ class Compiler(
|
||||
typeParams: List<TypeDecl.TypeParam>
|
||||
): Map<String, Obj> {
|
||||
if (typeParams.isEmpty()) return emptyMap()
|
||||
val explicitTypeArgs = context.args.explicitTypeArgs
|
||||
if (explicitTypeArgs.size > typeParams.size) {
|
||||
context.raiseError("too many type arguments: expected ${typeParams.size}, got ${explicitTypeArgs.size}")
|
||||
}
|
||||
val inferred = mutableMapOf<String, TypeDecl>()
|
||||
val argValues = context.args.list
|
||||
for ((index, param) in argsDeclaration.params.withIndex()) {
|
||||
@ -5358,8 +5364,11 @@ class Compiler(
|
||||
collectRuntimeTypeVarBindings(param.type, value, inferred)
|
||||
}
|
||||
val boundValues = LinkedHashMap<String, Obj>(typeParams.size)
|
||||
for (tp in typeParams) {
|
||||
val inferredType = inferred[tp.name] ?: tp.defaultType ?: TypeDecl.TypeAny
|
||||
for ((index, tp) in typeParams.withIndex()) {
|
||||
val inferredType = explicitTypeArgs.getOrNull(index)
|
||||
?: inferred[tp.name]
|
||||
?: tp.defaultType
|
||||
?: TypeDecl.TypeAny
|
||||
val normalized = normalizeRuntimeTypeDecl(inferredType)
|
||||
val cls = resolveTypeDeclObjClass(normalized)
|
||||
val boundValue = if (cls != null &&
|
||||
@ -5681,7 +5690,7 @@ class Compiler(
|
||||
val result = when (left) {
|
||||
is ImplicitThisMemberRef ->
|
||||
if (left.methodId == null && left.fieldId != null) {
|
||||
CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
||||
CallRef(left, args, detectedBlockArgument, isOptional, explicitTypeArgs).also { callRef ->
|
||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
||||
}
|
||||
} else {
|
||||
@ -5716,7 +5725,7 @@ class Compiler(
|
||||
checkFunctionTypeCallArity(left, args, left.pos())
|
||||
checkFunctionTypeCallTypes(left, args, left.pos())
|
||||
checkGenericBoundsAtCall(left.name, args, left.pos())
|
||||
CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
||||
CallRef(left, args, detectedBlockArgument, isOptional, explicitTypeArgs).also { callRef ->
|
||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
||||
}
|
||||
}
|
||||
@ -5742,12 +5751,12 @@ class Compiler(
|
||||
checkFunctionTypeCallArity(left, args, left.pos())
|
||||
checkFunctionTypeCallTypes(left, args, left.pos())
|
||||
checkGenericBoundsAtCall(left.name, args, left.pos())
|
||||
CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
||||
CallRef(left, args, detectedBlockArgument, isOptional, explicitTypeArgs).also { callRef ->
|
||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
||||
else -> CallRef(left, args, detectedBlockArgument, isOptional, explicitTypeArgs).also { callRef ->
|
||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,12 +60,54 @@ interface ClassBridgeBinder {
|
||||
/** Register an initialization hook that runs for each instance. */
|
||||
fun init(block: suspend BridgeInstanceContext.(ScopeFacade) -> Unit)
|
||||
/** Register an initialization hook with direct access to the instance. */
|
||||
fun initWithInstance(block: suspend ScopeFacade.() -> Unit)
|
||||
/**
|
||||
* Legacy initWithInstance form.
|
||||
* Prefer [initWithInstance] with [ScopeFacade] receiver and use [ScopeFacade.thisObj].
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "Use initWithInstance { ... } with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith("initWithInstance { block(this, thisObj) }")
|
||||
)
|
||||
fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit)
|
||||
/** Bind a Lyng function/member to a Kotlin implementation (requires `extern` in Lyng). */
|
||||
fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj)
|
||||
/**
|
||||
* Legacy addFun form.
|
||||
* Prefer [addFun] with [ScopeFacade] receiver and use [ScopeFacade.thisObj]/[ScopeFacade.args].
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "Use addFun(name) { ... } with ScopeFacade receiver; use thisObj/args from scope",
|
||||
replaceWith = ReplaceWith("addFun(name) { impl(this, thisObj, args) }")
|
||||
)
|
||||
fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj)
|
||||
/** Bind a read-only member (val/property getter) declared as `extern`. */
|
||||
fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj)
|
||||
/**
|
||||
* Legacy addVal form.
|
||||
* Prefer [addVal] with [ScopeFacade] receiver and use [ScopeFacade.thisObj].
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "Use addVal(name) { ... } with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith("addVal(name) { impl(this, thisObj) }")
|
||||
)
|
||||
fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj)
|
||||
/** Bind a mutable member (var/property getter/setter) declared as `extern`. */
|
||||
fun addVar(
|
||||
name: String,
|
||||
get: suspend ScopeFacade.() -> Obj,
|
||||
set: suspend ScopeFacade.(Obj) -> Unit
|
||||
)
|
||||
/**
|
||||
* Legacy addVar form.
|
||||
* Prefer [addVar] with [ScopeFacade] receiver and use [ScopeFacade.thisObj].
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "Use addVar(name, get, set) with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith(
|
||||
"addVar(name, get = { get(this, thisObj) }, set = { value -> set(this, thisObj, value) })"
|
||||
)
|
||||
)
|
||||
fun addVar(
|
||||
name: String,
|
||||
get: suspend (ScopeFacade, Obj) -> Obj,
|
||||
@ -234,17 +276,27 @@ private class ClassBridgeBinderImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun initWithInstance(block: suspend ScopeFacade.() -> Unit) {
|
||||
initHooks.add { scope, _ ->
|
||||
scope.block()
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Use initWithInstance { ... } with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith("initWithInstance { block(this, thisObj) }")
|
||||
)
|
||||
override fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit) {
|
||||
initHooks.add { scope, inst ->
|
||||
block(scope, inst)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) {
|
||||
override fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj) {
|
||||
ensureTemplateNotBuilt()
|
||||
val target = findMember(name)
|
||||
val callable = ObjExternCallable.fromBridge {
|
||||
impl(this, thisObj, args)
|
||||
impl()
|
||||
}
|
||||
val methodId = cls.ensureMethodIdForBridge(name, target.record)
|
||||
val newRecord = target.record.copy(
|
||||
@ -255,14 +307,24 @@ private class ClassBridgeBinderImpl(
|
||||
replaceMember(target, newRecord)
|
||||
}
|
||||
|
||||
override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) {
|
||||
@Deprecated(
|
||||
message = "Use addFun(name) { ... } with ScopeFacade receiver; use thisObj/args from scope",
|
||||
replaceWith = ReplaceWith("addFun(name) { impl(this, thisObj, args) }")
|
||||
)
|
||||
override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) {
|
||||
addFun(name) {
|
||||
impl(this, thisObj, args)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj) {
|
||||
ensureTemplateNotBuilt()
|
||||
val target = findMember(name)
|
||||
if (target.record.isMutable) {
|
||||
throw ScriptError(Pos.builtIn, "extern val $name is mutable in class ${cls.className}")
|
||||
}
|
||||
val getter = ObjExternCallable.fromBridge {
|
||||
impl(this, thisObj)
|
||||
impl()
|
||||
}
|
||||
val prop = ObjProperty(name, getter, null)
|
||||
val isFieldLike = target.record.type == ObjRecord.Type.Field ||
|
||||
@ -287,10 +349,20 @@ private class ClassBridgeBinderImpl(
|
||||
replaceMember(target, newRecord)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Use addVal(name) { ... } with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith("addVal(name) { impl(this, thisObj) }")
|
||||
)
|
||||
override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) {
|
||||
addVal(name) {
|
||||
impl(this, thisObj)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addVar(
|
||||
name: String,
|
||||
get: suspend (ScopeFacade, Obj) -> Obj,
|
||||
set: suspend (ScopeFacade, Obj, Obj) -> Unit
|
||||
get: suspend ScopeFacade.() -> Obj,
|
||||
set: suspend ScopeFacade.(Obj) -> Unit
|
||||
) {
|
||||
ensureTemplateNotBuilt()
|
||||
val target = findMember(name)
|
||||
@ -298,11 +370,11 @@ private class ClassBridgeBinderImpl(
|
||||
throw ScriptError(Pos.builtIn, "extern var $name is readonly in class ${cls.className}")
|
||||
}
|
||||
val getter = ObjExternCallable.fromBridge {
|
||||
get(this, thisObj)
|
||||
get()
|
||||
}
|
||||
val setter = ObjExternCallable.fromBridge {
|
||||
val value = requiredArg<Obj>(0)
|
||||
set(this, thisObj, value)
|
||||
set(value)
|
||||
ObjVoid
|
||||
}
|
||||
val prop = ObjProperty(name, getter, setter)
|
||||
@ -328,6 +400,24 @@ private class ClassBridgeBinderImpl(
|
||||
replaceMember(target, newRecord)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Use addVar(name, get, set) with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith(
|
||||
"addVar(name, get = { get(this, thisObj) }, set = { value -> set(this, thisObj, value) })"
|
||||
)
|
||||
)
|
||||
override fun addVar(
|
||||
name: String,
|
||||
get: suspend (ScopeFacade, Obj) -> Obj,
|
||||
set: suspend (ScopeFacade, Obj, Obj) -> Unit
|
||||
) {
|
||||
addVar(
|
||||
name,
|
||||
get = { get(this, thisObj) },
|
||||
set = { value -> set(this, thisObj, value) }
|
||||
)
|
||||
}
|
||||
|
||||
fun commit() {
|
||||
if (initHooks.isNotEmpty()) {
|
||||
val target = cls.bridgeInitHooks ?: mutableListOf<suspend (ScopeFacade, ObjInstance) -> Unit>().also {
|
||||
@ -399,16 +489,26 @@ private class ObjectBridgeBinderImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun initWithInstance(block: suspend ScopeFacade.() -> Unit) {
|
||||
initHooks.add { scope, _ ->
|
||||
scope.block()
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Use initWithInstance { ... } with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith("initWithInstance { block(this, thisObj) }")
|
||||
)
|
||||
override fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit) {
|
||||
initHooks.add { scope, inst ->
|
||||
block(scope, inst)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) {
|
||||
override fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj) {
|
||||
val target = findMember(name)
|
||||
val callable = ObjExternCallable.fromBridge {
|
||||
impl(this, thisObj, args)
|
||||
impl()
|
||||
}
|
||||
val methodId = cls.ensureMethodIdForBridge(name, target.record)
|
||||
val newRecord = target.record.copy(
|
||||
@ -420,13 +520,23 @@ private class ObjectBridgeBinderImpl(
|
||||
updateInstanceMember(target, newRecord)
|
||||
}
|
||||
|
||||
override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) {
|
||||
@Deprecated(
|
||||
message = "Use addFun(name) { ... } with ScopeFacade receiver; use thisObj/args from scope",
|
||||
replaceWith = ReplaceWith("addFun(name) { impl(this, thisObj, args) }")
|
||||
)
|
||||
override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) {
|
||||
addFun(name) {
|
||||
impl(this, thisObj, args)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj) {
|
||||
val target = findMember(name)
|
||||
if (target.record.isMutable) {
|
||||
throw ScriptError(Pos.builtIn, "extern val $name is mutable in class ${cls.className}")
|
||||
}
|
||||
val getter = ObjExternCallable.fromBridge {
|
||||
impl(this, thisObj)
|
||||
impl()
|
||||
}
|
||||
val prop = ObjProperty(name, getter, null)
|
||||
val isFieldLike = target.record.type == ObjRecord.Type.Field ||
|
||||
@ -452,21 +562,31 @@ private class ObjectBridgeBinderImpl(
|
||||
updateInstanceMember(target, newRecord)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Use addVal(name) { ... } with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith("addVal(name) { impl(this, thisObj) }")
|
||||
)
|
||||
override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) {
|
||||
addVal(name) {
|
||||
impl(this, thisObj)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addVar(
|
||||
name: String,
|
||||
get: suspend (ScopeFacade, Obj) -> Obj,
|
||||
set: suspend (ScopeFacade, Obj, Obj) -> Unit
|
||||
get: suspend ScopeFacade.() -> Obj,
|
||||
set: suspend ScopeFacade.(Obj) -> Unit
|
||||
) {
|
||||
val target = findMember(name)
|
||||
if (!target.record.isMutable) {
|
||||
throw ScriptError(Pos.builtIn, "extern var $name is readonly in class ${cls.className}")
|
||||
}
|
||||
val getter = ObjExternCallable.fromBridge {
|
||||
get(this, thisObj)
|
||||
get()
|
||||
}
|
||||
val setter = ObjExternCallable.fromBridge {
|
||||
val value = requiredArg<Obj>(0)
|
||||
set(this, thisObj, value)
|
||||
set(value)
|
||||
ObjVoid
|
||||
}
|
||||
val prop = ObjProperty(name, getter, setter)
|
||||
@ -493,6 +613,24 @@ private class ObjectBridgeBinderImpl(
|
||||
updateInstanceMember(target, newRecord)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Use addVar(name, get, set) with ScopeFacade receiver; use thisObj from scope",
|
||||
replaceWith = ReplaceWith(
|
||||
"addVar(name, get = { get(this, thisObj) }, set = { value -> set(this, thisObj, value) })"
|
||||
)
|
||||
)
|
||||
override fun addVar(
|
||||
name: String,
|
||||
get: suspend (ScopeFacade, Obj) -> Obj,
|
||||
set: suspend (ScopeFacade, Obj, Obj) -> Unit
|
||||
) {
|
||||
addVar(
|
||||
name,
|
||||
get = { get(this, thisObj) },
|
||||
set = { value -> set(this, thisObj, value) }
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun commit() {
|
||||
if (initHooks.isNotEmpty()) {
|
||||
val target = cls.bridgeInitHooks ?: mutableListOf<suspend (ScopeFacade, ObjInstance) -> Unit>().also {
|
||||
|
||||
@ -4395,7 +4395,7 @@ class BytecodeCompiler(
|
||||
val dst = allocSlot()
|
||||
val encodedMethodId = encodeMemberId(receiverClass, methodId) ?: methodId
|
||||
if (!ref.isOptionalInvoke) {
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
||||
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||
setPos(callPos)
|
||||
builder.emit(Opcode.CALL_MEMBER_SLOT, receiver.slot, encodedMethodId, args.base, encodedCount, dst)
|
||||
@ -4410,7 +4410,7 @@ class BytecodeCompiler(
|
||||
Opcode.JMP_IF_TRUE,
|
||||
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(nullLabel))
|
||||
)
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
||||
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||
setPos(callPos)
|
||||
builder.emit(Opcode.CALL_MEMBER_SLOT, receiver.slot, encodedMethodId, args.base, encodedCount, dst)
|
||||
@ -4450,7 +4450,7 @@ class BytecodeCompiler(
|
||||
val callee = compileRefWithFallback(ref.target, null, refPosOrCurrent(ref.target)) ?: return null
|
||||
val dst = allocSlot()
|
||||
if (!ref.isOptionalInvoke) {
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
||||
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||
setPos(callPos)
|
||||
builder.emit(
|
||||
@ -4475,7 +4475,7 @@ class BytecodeCompiler(
|
||||
Opcode.JMP_IF_TRUE,
|
||||
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(nullLabel))
|
||||
)
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
|
||||
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
||||
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||
setPos(callPos)
|
||||
builder.emit(
|
||||
@ -4876,10 +4876,14 @@ class BytecodeCompiler(
|
||||
return CallArgs(base = argSlots[0], count = argSlots.size, planId = planId)
|
||||
}
|
||||
|
||||
private fun compileCallArgs(args: List<ParsedArgument>, tailBlock: Boolean): CallArgs? {
|
||||
if (args.isEmpty()) return CallArgs(base = 0, count = 0, planId = null)
|
||||
private fun compileCallArgs(
|
||||
args: List<ParsedArgument>,
|
||||
tailBlock: Boolean,
|
||||
explicitTypeArgs: List<TypeDecl>? = null
|
||||
): CallArgs? {
|
||||
if (args.isEmpty() && explicitTypeArgs.isNullOrEmpty()) return CallArgs(base = 0, count = 0, planId = null)
|
||||
val argSlots = IntArray(args.size) { allocSlot() }
|
||||
val needPlan = tailBlock || args.any { it.isSplat || it.name != null }
|
||||
val needPlan = tailBlock || args.any { it.isSplat || it.name != null } || !explicitTypeArgs.isNullOrEmpty()
|
||||
val specs = if (needPlan) ArrayList<BytecodeConst.CallArgSpec>(args.size) else null
|
||||
for ((index, arg) in args.withIndex()) {
|
||||
val compiled = compileArgValue(arg.value) ?: return null
|
||||
@ -4891,11 +4895,17 @@ class BytecodeCompiler(
|
||||
specs?.add(BytecodeConst.CallArgSpec(arg.name, arg.isSplat))
|
||||
}
|
||||
val planId = if (needPlan) {
|
||||
builder.addConst(BytecodeConst.CallArgsPlan(tailBlock, specs ?: emptyList()))
|
||||
builder.addConst(
|
||||
BytecodeConst.CallArgsPlan(
|
||||
tailBlock = tailBlock,
|
||||
specs = specs ?: emptyList(),
|
||||
explicitTypeArgs = explicitTypeArgs ?: emptyList()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return CallArgs(base = argSlots[0], count = argSlots.size, planId = planId)
|
||||
return CallArgs(base = if (argSlots.isEmpty()) 0 else argSlots[0], count = argSlots.size, planId = planId)
|
||||
}
|
||||
|
||||
private fun compileArgValue(value: Obj): CompiledValue? {
|
||||
@ -8464,6 +8474,9 @@ class BytecodeCompiler(
|
||||
collectScopeSlotsRef(ref.targetRef)
|
||||
collectScopeSlotsRef(ref.indexRef)
|
||||
}
|
||||
is ClassOperatorRef -> {
|
||||
collectScopeSlotsRef(ref.target)
|
||||
}
|
||||
is ListLiteralRef -> {
|
||||
for (entry in ref.entries()) {
|
||||
when (entry) {
|
||||
|
||||
@ -180,6 +180,10 @@ sealed class BytecodeConst {
|
||||
val pattern: ListLiteralRef,
|
||||
val pos: Pos,
|
||||
) : BytecodeConst()
|
||||
data class CallArgsPlan(val tailBlock: Boolean, val specs: List<CallArgSpec>) : BytecodeConst()
|
||||
data class CallArgsPlan(
|
||||
val tailBlock: Boolean,
|
||||
val specs: List<CallArgSpec>,
|
||||
val explicitTypeArgs: List<TypeDecl> = emptyList()
|
||||
) : BytecodeConst()
|
||||
data class CallArgSpec(val name: String?, val isSplat: Boolean)
|
||||
}
|
||||
|
||||
@ -4699,7 +4699,12 @@ class CmdFrame(
|
||||
positional.add(value)
|
||||
}
|
||||
}
|
||||
return Arguments(positional, plan.tailBlock, named ?: emptyMap())
|
||||
return Arguments(
|
||||
list = positional,
|
||||
tailBlockMode = plan.tailBlock,
|
||||
named = named ?: emptyMap(),
|
||||
explicitTypeArgs = plan.explicitTypeArgs
|
||||
)
|
||||
}
|
||||
|
||||
private fun resolveLocalScope(localIndex: Int): Scope? {
|
||||
|
||||
@ -646,6 +646,9 @@ open class Obj {
|
||||
}
|
||||
|
||||
open suspend fun getAt(scope: Scope, index: Obj): Obj {
|
||||
if (hasNonRootIndexerMember("getAt")) {
|
||||
return invokeInstanceMethod(scope, "getAt", Arguments(index))
|
||||
}
|
||||
if (index is ObjString) {
|
||||
return readField(scope, index.value).value
|
||||
}
|
||||
@ -655,6 +658,16 @@ open class Obj {
|
||||
suspend fun getAt(scope: Scope, index: Int): Obj = getAt(scope, ObjInt(index.toLong()))
|
||||
|
||||
open suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
||||
when {
|
||||
hasNonRootIndexerMember("putAt") -> {
|
||||
invokeInstanceMethod(scope, "putAt", Arguments(index, newValue))
|
||||
return
|
||||
}
|
||||
hasNonRootIndexerMember("setAt") -> {
|
||||
invokeInstanceMethod(scope, "setAt", Arguments(index, newValue))
|
||||
return
|
||||
}
|
||||
}
|
||||
if (index is ObjString) {
|
||||
writeField(scope, index.value, newValue)
|
||||
return
|
||||
@ -662,6 +675,17 @@ open class Obj {
|
||||
scope.raiseNotImplemented("indexing")
|
||||
}
|
||||
|
||||
private fun hasNonRootIndexerMember(name: String): Boolean {
|
||||
for (cls in objClass.mro) {
|
||||
if (cls.className == "Obj") break
|
||||
val rec = cls.members[name] ?: cls.classScope?.objects?.get(name)
|
||||
if (rec != null && !rec.isAbstract && rec.type != ObjRecord.Type.Delegated) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open suspend fun callOn(scope: Scope): Obj {
|
||||
scope.raiseNotImplemented()
|
||||
}
|
||||
|
||||
@ -45,18 +45,18 @@ class ObjKotlinIterator(val iterator: Iterator<Any?>) : Obj() {
|
||||
}
|
||||
boundType = cls
|
||||
LyngClassBridge.bind(cls) {
|
||||
addFun("next") { scope, self, _ ->
|
||||
when (self) {
|
||||
addFun("next") {
|
||||
when (val self = thisObj) {
|
||||
is ObjKotlinIterator -> self.iterator.next().toObj()
|
||||
is ObjKotlinObjIterator -> self.iterator.next()
|
||||
else -> scope.raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}")
|
||||
else -> raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}")
|
||||
}
|
||||
}
|
||||
addFun("hasNext") { scope, self, _ ->
|
||||
when (self) {
|
||||
addFun("hasNext") {
|
||||
when (val self = thisObj) {
|
||||
is ObjKotlinIterator -> self.iterator.hasNext().toObj()
|
||||
is ObjKotlinObjIterator -> self.iterator.hasNext().toObj()
|
||||
else -> scope.raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}")
|
||||
else -> raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,6 +469,7 @@ class CallRef(
|
||||
internal val args: List<ParsedArgument>,
|
||||
internal val tailBlock: Boolean,
|
||||
internal val isOptionalInvoke: Boolean,
|
||||
internal val explicitTypeArgs: List<TypeDecl>? = null,
|
||||
) : ObjRef {
|
||||
override suspend fun get(scope: Scope): ObjRecord = scope.raiseObjRefEvalDisabled()
|
||||
}
|
||||
|
||||
@ -99,7 +99,9 @@ internal fun typeDeclIsSubtype(scope: Scope, left: TypeDecl, right: TypeDecl): B
|
||||
is TypeDecl.Simple, is TypeDecl.Generic, is TypeDecl.Function, is TypeDecl.Ellipsis -> {
|
||||
val leftClass = resolveTypeDeclClass(scope, l) ?: return false
|
||||
val rightClass = resolveTypeDeclClass(scope, r) ?: return false
|
||||
leftClass == rightClass || leftClass.allParentsSet.contains(rightClass)
|
||||
leftClass == rightClass ||
|
||||
rightClass == Obj.rootObjectType ||
|
||||
leftClass.allParentsSet.contains(rightClass)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
@ -61,39 +61,39 @@ class BridgeBindingTest {
|
||||
init { _ ->
|
||||
data = CounterState(0)
|
||||
}
|
||||
addFun("add") { _, _, args ->
|
||||
addFun("add") {
|
||||
val a = (args.list[0] as ObjInt).value
|
||||
val b = (args.list[1] as ObjInt).value
|
||||
ObjInt.of(a + b)
|
||||
}
|
||||
addVal("status") { _, _ -> ObjString(classData as String) }
|
||||
addVal("status") { ObjString(classData as String) }
|
||||
addVar(
|
||||
"count",
|
||||
get = { _, instance ->
|
||||
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
get = {
|
||||
val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
ObjInt.of(st.count)
|
||||
},
|
||||
set = { _, instance, value ->
|
||||
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
set = { value ->
|
||||
val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
st.count = (value as ObjInt).value
|
||||
}
|
||||
)
|
||||
addFun("secret") { _, _, _ -> ObjInt.of(42) }
|
||||
addFun("ping") { _, _, _ -> ObjInt.of(7) }
|
||||
addFun("secret") { ObjInt.of(42) }
|
||||
addFun("ping") { ObjInt.of(7) }
|
||||
}
|
||||
|
||||
LyngClassBridge.bind(className = "Bar", module = "bridge.mod", importManager = im) {
|
||||
initWithInstance { _, instance ->
|
||||
(instance as net.sergeych.lyng.obj.ObjInstance).data = CounterState(10)
|
||||
initWithInstance {
|
||||
(thisObj as net.sergeych.lyng.obj.ObjInstance).data = CounterState(10)
|
||||
}
|
||||
addVar(
|
||||
"count",
|
||||
get = { _, instance ->
|
||||
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
get = {
|
||||
val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
ObjInt.of(st.count)
|
||||
},
|
||||
set = { _, instance, value ->
|
||||
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
set = { value ->
|
||||
val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
st.count = (value as ObjInt).value
|
||||
}
|
||||
)
|
||||
@ -156,7 +156,7 @@ class BridgeBindingTest {
|
||||
|
||||
val bindFailed = try {
|
||||
LyngClassBridge.bind(className = "Late", module = "bridge.late", importManager = im) {
|
||||
addVal("status") { _, _ -> ObjString("late") }
|
||||
addVal("status") { ObjString("late") }
|
||||
}
|
||||
false
|
||||
} catch (_: ScriptError) {
|
||||
@ -183,20 +183,20 @@ class BridgeBindingTest {
|
||||
init { _ ->
|
||||
data = CounterState(5)
|
||||
}
|
||||
addFun("add") { _, _, args ->
|
||||
addFun("add") {
|
||||
val a = (args.list[0] as ObjInt).value
|
||||
val b = (args.list[1] as ObjInt).value
|
||||
ObjInt.of(a + b)
|
||||
}
|
||||
addVal("status") { _, _ -> ObjString(classData as String) }
|
||||
addVal("status") { ObjString(classData as String) }
|
||||
addVar(
|
||||
"count",
|
||||
get = { _, instance ->
|
||||
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
get = {
|
||||
val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
ObjInt.of(st.count)
|
||||
},
|
||||
set = { _, instance, value ->
|
||||
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
set = { value ->
|
||||
val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState
|
||||
st.count = (value as ObjInt).value
|
||||
}
|
||||
)
|
||||
|
||||
@ -20,7 +20,6 @@ import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.ScriptError
|
||||
import net.sergeych.lyng.eval
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -510,4 +509,64 @@ class TypesTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClassName() = runTest {
|
||||
eval("""
|
||||
class X {
|
||||
var x = 1
|
||||
}
|
||||
assert( X::class is Class)
|
||||
assertEquals("Class", X::class.name)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenericTypes() = runTest {
|
||||
eval("""
|
||||
fun t<T>(): String =
|
||||
when(T) {
|
||||
null -> "%s is Null"(T::class.name)
|
||||
is Object -> "%s is Object"(T::class.name)
|
||||
else -> throw "It should not happen"
|
||||
}
|
||||
assert( Int is Object)
|
||||
assertEquals( t<Int>(), "Class is Object")
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test fun testIndexer() = runTest {
|
||||
eval("""
|
||||
class Greeter {
|
||||
override fun getAt(name) = "Hello, %s!"(name)
|
||||
}
|
||||
assertEquals("Hello, Bob!",Greeter()["Bob"])
|
||||
val g = Greeter()
|
||||
assertEquals("Hello, Bob!",g["Bob"])
|
||||
|
||||
// it should work with objects too:
|
||||
object Polite {
|
||||
override fun getAt(name) = "How do you do, %s?"(name)
|
||||
}
|
||||
|
||||
assertEquals("How do you do, Bob?",Polite["Bob"])
|
||||
|
||||
""".trimIndent())
|
||||
}
|
||||
// @Test fun nonTrivialOperatorsTest() = runTest {
|
||||
// val s = Script.newScope()
|
||||
// s.eval("""
|
||||
// class Matrix<T>(val rows: Int, val cols: Int,initialValue:T?) {
|
||||
// val data
|
||||
// init {
|
||||
// val v = initalValue?
|
||||
// }
|
||||
// data = List(rows*cols) { initialValue } }
|
||||
// fun getAt(row: Int, col: Int) = data[row*cols+col]
|
||||
// fun setAt(row: Int, col: Int, value: T) { data[row*cols+col] = value }
|
||||
// }
|
||||
// val m = Matrix(1,1)
|
||||
//
|
||||
// """.trimIndent())
|
||||
// }
|
||||
}
|
||||
|
||||
@ -146,10 +146,22 @@ interface ClassBridgeBinder {
|
||||
var classData: Any?
|
||||
|
||||
fun init(block: suspend BridgeInstanceContext.(ScopeFacade) -> Unit)
|
||||
fun initWithInstance(block: suspend ScopeFacade.() -> Unit)
|
||||
@Deprecated("Use receiver-form initWithInstance and read thisObj from ScopeFacade")
|
||||
fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit)
|
||||
|
||||
fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj)
|
||||
@Deprecated("Use receiver-form addFun and read thisObj/args from ScopeFacade")
|
||||
fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj)
|
||||
fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj)
|
||||
@Deprecated("Use receiver-form addVal and read thisObj from ScopeFacade")
|
||||
fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj)
|
||||
fun addVar(
|
||||
name: String,
|
||||
get: suspend ScopeFacade.() -> Obj,
|
||||
set: suspend ScopeFacade.(Obj) -> Unit
|
||||
)
|
||||
@Deprecated("Use receiver-form addVar and read thisObj from ScopeFacade")
|
||||
fun addVar(
|
||||
name: String,
|
||||
get: suspend (ScopeFacade, Obj) -> Obj,
|
||||
|
||||
@ -39,34 +39,34 @@ LyngClassBridge.bind(className = "Foo", module = "bridge.mod", importManager = i
|
||||
data = CounterState(0)
|
||||
}
|
||||
|
||||
addFun("add") { _, _, args ->
|
||||
addFun("add") {
|
||||
val a = (args.list[0] as ObjInt).value
|
||||
val b = (args.list[1] as ObjInt).value
|
||||
ObjInt.of(a + b)
|
||||
}
|
||||
|
||||
addVal("status") { _, _ -> ObjString(classData as String) }
|
||||
addVal("status") { ObjString(classData as String) }
|
||||
|
||||
addVar(
|
||||
"count",
|
||||
get = { _, instance ->
|
||||
val st = (instance as ObjInstance).data as CounterState
|
||||
get = {
|
||||
val st = (thisObj as ObjInstance).data as CounterState
|
||||
ObjInt.of(st.count)
|
||||
},
|
||||
set = { _, instance, value ->
|
||||
val st = (instance as ObjInstance).data as CounterState
|
||||
set = { value ->
|
||||
val st = (thisObj as ObjInstance).data as CounterState
|
||||
st.count = (value as ObjInt).value
|
||||
}
|
||||
)
|
||||
|
||||
addFun("secret") { _, _, _ -> ObjInt.of(42) }
|
||||
addFun("ping") { _, _, _ -> ObjInt.of(7) }
|
||||
addFun("secret") { ObjInt.of(42) }
|
||||
addFun("ping") { ObjInt.of(7) }
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Visibility is respected by usual Lyng access rules; private extern members can be used only within class code.
|
||||
- Use `init { scope -> ... }` for receiver-style init, or `initWithInstance { scope, instance -> ... }` for explicit instance access.
|
||||
- Use `init { ... }` / `initWithInstance { ... }` with `ScopeFacade` receiver; access instance via `thisObj`.
|
||||
- `classData` and `instance.data` are Kotlin-only payloads and do not appear in Lyng reflection.
|
||||
- Binding after the first instance of a class is created throws a `ScriptError`.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user