Add resolved export metadata and fix imported accessors
This commit is contained in:
1 parent
1a22b60b7d
commit
898ebfa536
14 files changed
+530
-37
No files matched your search
@@ -249,4 +249,39 @@ class ScriptImportPreparationTest {
|
||||
val result = script.execute(manager.newStdScope()) as ObjString
|
||||
assertEquals("<html><h3>Imported</h3></html>", result.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun importedExtensionPropertyAccessorsUseDeclaringModuleScope() = runTest {
|
||||
val manager = Script.defaultImportManager.copy().apply {
|
||||
addTextPackages(
|
||||
"""
|
||||
package imported.extprop
|
||||
|
||||
private var prefix = "module"
|
||||
|
||||
private fun decorate(value: String): String = prefix + ":" + value
|
||||
|
||||
var String.decorated: String
|
||||
get() = decorate(this)
|
||||
set(value) { prefix = value }
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
val script = Compiler.compile(
|
||||
Source(
|
||||
"<extension-property-import>",
|
||||
"""
|
||||
import imported.extprop
|
||||
|
||||
val before = "ok".decorated
|
||||
"ignored".decorated = "updated"
|
||||
before + "|" + "ok".decorated
|
||||
""".trimIndent()
|
||||
),
|
||||
manager
|
||||
)
|
||||
|
||||
val result = script.execute(manager.newStdScope()) as ObjString
|
||||
assertEquals("module:ok|updated:ok", result.value)
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sergeych.lyng
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.bridge.globalBinder
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjString
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ImportedExternExtensionPropertyTest {
|
||||
@Test
|
||||
fun importedExtensionGetterCanCallExternFromItsImportedRuntimeModule() = runTest {
|
||||
val imports = Script.defaultImportManager.copy()
|
||||
imports.addPackage("test.contract.runtime") { module ->
|
||||
module.eval(
|
||||
"""
|
||||
package test.contract.runtime
|
||||
|
||||
class ContractContext(val label: String)
|
||||
class ContractRegistryBase
|
||||
class ContractRegistry : ContractRegistryBase
|
||||
|
||||
extern val Contracts: ContractRegistry
|
||||
extern fun contractHandle(nameOrId: String): ContractContext
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val contextClass = module.resolve(
|
||||
requireNotNull(module["ContractContext"]),
|
||||
"ContractContext"
|
||||
) as ObjClass
|
||||
val registryClass = module.resolve(
|
||||
requireNotNull(module["ContractRegistry"]),
|
||||
"ContractRegistry"
|
||||
) as ObjClass
|
||||
val context = contextClass.callOn(
|
||||
module.createChildScope(args = Arguments(ObjString("bound")))
|
||||
)
|
||||
val registry = registryClass.callOn(module.createChildScope())
|
||||
module.globalBinder().bindGlobalVarRaw("Contracts", get = { registry })
|
||||
module.globalBinder().bindGlobalFunRaw("contractHandle") { _, args ->
|
||||
assertEquals("Alpha", (args.firstAndOnly() as ObjString).value)
|
||||
context
|
||||
}
|
||||
}
|
||||
imports.addTextPackages(
|
||||
"""
|
||||
package test.contract.Alpha
|
||||
import test.contract.runtime
|
||||
|
||||
class AlphaContractAbi(val target: ContractContext)
|
||||
|
||||
val ContractRegistryBase.AlphaHandle: ContractContext
|
||||
get() = contractHandle("Alpha")
|
||||
|
||||
val ContractRegistryBase.Alpha: AlphaContractAbi
|
||||
get() = AlphaContractAbi(contractHandle("Alpha"))
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val scope = imports.newStdScope()
|
||||
val handleResult = scope.eval(
|
||||
"""
|
||||
import test.contract.runtime
|
||||
import test.contract.Alpha
|
||||
|
||||
Contracts.AlphaHandle.label
|
||||
""".trimIndent()
|
||||
) as ObjString
|
||||
assertEquals("bound", handleResult.value)
|
||||
|
||||
val result = scope.eval(
|
||||
"""
|
||||
import test.contract.runtime
|
||||
import test.contract.Alpha
|
||||
|
||||
Contracts.Alpha.target.label
|
||||
""".trimIndent()
|
||||
) as ObjString
|
||||
|
||||
assertEquals("bound", result.value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sergeych.lyng
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertSame
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ResolvedFunctionMetadataTest {
|
||||
|
||||
@Test
|
||||
fun resolvesCompleteExportSignatureWithoutExecutingSource() = runTest {
|
||||
val source = Source(
|
||||
"metadata.lyng",
|
||||
"""
|
||||
@Export
|
||||
fun exported<T: Object = String>(
|
||||
head: T,
|
||||
rest: T...,
|
||||
limit: Int = 7,
|
||||
suffix: String = "!",
|
||||
): List<T> { [] }
|
||||
|
||||
fun helper(value: String): String { value }
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val functions = Compiler.resolveFunctionMetadata(source, Script.defaultImportManager)
|
||||
val exported = functions.single { it.name == "exported" }
|
||||
val helper = functions.single { it.name == "helper" }
|
||||
|
||||
assertEquals(listOf("Export"), exported.annotations)
|
||||
assertTrue(helper.annotations.isEmpty())
|
||||
|
||||
assertEquals("metadata.lyng", exported.namePos.source.fileName)
|
||||
assertSame(source, exported.namePos.source)
|
||||
assertEquals("exported", exported.namePos.currentLine.substring(exported.namePos.column, exported.namePos.column + 8))
|
||||
assertSame(source, exported.declarationPos.source)
|
||||
|
||||
val typeParameter = exported.typeParams.single()
|
||||
assertEquals("T", typeParameter.name)
|
||||
assertEquals(TypeDecl.Variance.Invariant, typeParameter.variance)
|
||||
assertSimple(typeParameter.bound, "Object")
|
||||
assertSimple(typeParameter.defaultType, "String")
|
||||
|
||||
assertEquals(listOf("head", "rest", "limit", "suffix"), exported.parameters.map { it.name })
|
||||
assertTypeVariable(exported.parameters[0].type, "T")
|
||||
assertFalse(exported.parameters[0].isEllipsis)
|
||||
assertFalse(exported.parameters[0].hasDefault)
|
||||
assertNull(exported.parameters[0].defaultSource)
|
||||
|
||||
assertTypeVariable(exported.parameters[1].type, "T")
|
||||
assertTrue(exported.parameters[1].isEllipsis)
|
||||
assertFalse(exported.parameters[1].hasDefault)
|
||||
assertNull(exported.parameters[1].defaultSource)
|
||||
|
||||
assertSimple(exported.parameters[2].type, "Int")
|
||||
assertFalse(exported.parameters[2].isEllipsis)
|
||||
assertTrue(exported.parameters[2].hasDefault)
|
||||
assertEquals("7", exported.parameters[2].defaultSource)
|
||||
assertTrue(exported.parameters[3].hasDefault)
|
||||
assertEquals("\"!\"", exported.parameters[3].defaultSource)
|
||||
|
||||
val returnType = assertIs<TypeDecl.Generic>(exported.returnType)
|
||||
assertEquals("List", returnType.name)
|
||||
assertEquals(1, returnType.args.size)
|
||||
assertTypeVariable(returnType.args.single(), "T")
|
||||
|
||||
val functionType = assertIs<TypeDecl.Function>(exported.functionType)
|
||||
assertNull(functionType.receiver)
|
||||
assertTrue(functionType.contextReceivers.isEmpty())
|
||||
assertEquals(4, functionType.params.size)
|
||||
assertTypeVariable(functionType.params[0], "T")
|
||||
val variadicType = assertIs<TypeDecl.Ellipsis>(functionType.params[1])
|
||||
assertTypeVariable(variadicType.elementType, "T")
|
||||
assertSimple(functionType.params[2], "Int")
|
||||
assertGenericListOfTypeVariable(functionType.returnType, "T")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reportsCompilerInferredReturnType() = runTest {
|
||||
val source = Source(
|
||||
"inferred.lyng",
|
||||
"""
|
||||
@Export
|
||||
fun increment(value: Int) = value + 1
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val function = Compiler.resolveFunctionMetadata(source, Script.defaultImportManager).single()
|
||||
|
||||
assertEquals("increment", function.name)
|
||||
assertEquals(listOf("Export"), function.annotations)
|
||||
assertSimple(function.returnType, "Int")
|
||||
assertSimple(assertIs<TypeDecl.Function>(function.functionType).returnType, "Int")
|
||||
}
|
||||
|
||||
private fun assertSimple(type: TypeDecl?, name: String, nullable: Boolean = false) {
|
||||
val simple = assertIs<TypeDecl.Simple>(type)
|
||||
assertEquals(name, simple.name)
|
||||
assertEquals(nullable, simple.isNullable)
|
||||
}
|
||||
|
||||
private fun assertTypeVariable(type: TypeDecl?, name: String, nullable: Boolean = false) {
|
||||
val variable = assertIs<TypeDecl.TypeVar>(type)
|
||||
assertEquals(name, variable.name)
|
||||
assertEquals(nullable, variable.isNullable)
|
||||
}
|
||||
|
||||
private fun assertGenericListOfTypeVariable(type: TypeDecl?, variableName: String) {
|
||||
val generic = assertIs<TypeDecl.Generic>(type)
|
||||
assertEquals("List", generic.name)
|
||||
assertEquals(1, generic.args.size)
|
||||
assertTypeVariable(generic.args.single(), variableName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sergeych.lyng
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.obj.ObjString
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
|
||||
class VariadicParameterTypingTest {
|
||||
@Test
|
||||
fun typedVariadicParameterIsAListInsideFunctionBody() = runTest {
|
||||
val result = Script.newScope().eval(
|
||||
"""
|
||||
fun join(prefix: String, values: Int...): String =
|
||||
prefix + values.joinToString(",")
|
||||
|
||||
fun sum(values: Int...): Int {
|
||||
var result = 0
|
||||
for (value in values) result += value
|
||||
return result
|
||||
}
|
||||
|
||||
join("n=", 1, 2, 3) + "|" + sum(4, 5, 6)
|
||||
""".trimIndent()
|
||||
) as ObjString
|
||||
|
||||
assertEquals("n=1,2,3|15", result.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variadicMetadataKeepsElementTypeInEllipsisSignature() = runTest {
|
||||
val metadata = Compiler.resolveFunctionMetadata(
|
||||
Source("variadic-metadata", "fun join(values: Int...): String = values.joinToString(\",\")"),
|
||||
Script.defaultImportManager,
|
||||
).single()
|
||||
|
||||
val parameter = metadata.parameters.single()
|
||||
assertEquals("Int", (parameter.type as TypeDecl.Simple).name)
|
||||
val signatureType = assertIs<TypeDecl.Ellipsis>(metadata.functionType.params.single())
|
||||
assertEquals("Int", (signatureType.elementType as TypeDecl.Simple).name)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user