mirror of
https://github.com/apple/pkl.git
synced 2026-01-11 22:30:54 +01:00
Implement Pkl binary renderer and parser (#1203)
Implements a binary renderer for Pkl values, which is a lossless capturing of Pkl data. This follows the pkl binary format that is already used with `pkl server` calls, and is made available as a Java API and also an in-language API. Also, introduces a binary parser into the corresponding `PObject` types in Java.
This commit is contained in:
@@ -28,17 +28,7 @@ dependencies {
|
||||
testImplementation(projects.pklCommonsTest)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
inputs
|
||||
.dir("src/test/files/SnippetTests/input")
|
||||
.withPropertyName("snippetTestsInput")
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
inputs
|
||||
.dir("src/test/files/SnippetTests/output")
|
||||
.withPropertyName("snippetTestsOutput")
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
exclude("**/NativeServerTest.*")
|
||||
}
|
||||
tasks.test { exclude("**/NativeServerTest.*") }
|
||||
|
||||
private fun Test.configureNativeTest() {
|
||||
testClassesDirs = files(tasks.test.get().testClassesDirs)
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.server
|
||||
|
||||
import java.nio.file.Path
|
||||
import java.time.Duration
|
||||
import org.msgpack.core.MessagePacker
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.ast.member.ObjectMember
|
||||
import org.pkl.core.evaluatorSettings.TraceMode
|
||||
import org.pkl.core.http.HttpClient
|
||||
import org.pkl.core.module.ModuleKeyFactory
|
||||
import org.pkl.core.project.DeclaredDependencies
|
||||
import org.pkl.core.resource.ResourceReader
|
||||
import org.pkl.core.runtime.*
|
||||
|
||||
internal class BinaryEvaluator(
|
||||
transformer: StackFrameTransformer,
|
||||
manager: SecurityManager,
|
||||
httpClient: HttpClient,
|
||||
logger: Logger,
|
||||
factories: Collection<ModuleKeyFactory?>,
|
||||
readers: Collection<ResourceReader?>,
|
||||
environmentVariables: Map<String, String>,
|
||||
externalProperties: Map<String, String>,
|
||||
timeout: Duration?,
|
||||
moduleCacheDir: Path?,
|
||||
declaredDependencies: DeclaredDependencies?,
|
||||
outputFormat: String?,
|
||||
traceMode: TraceMode,
|
||||
) :
|
||||
EvaluatorImpl(
|
||||
transformer,
|
||||
false,
|
||||
manager,
|
||||
httpClient,
|
||||
logger,
|
||||
factories,
|
||||
readers,
|
||||
environmentVariables,
|
||||
externalProperties,
|
||||
timeout,
|
||||
moduleCacheDir,
|
||||
declaredDependencies,
|
||||
outputFormat,
|
||||
traceMode,
|
||||
) {
|
||||
fun evaluate(moduleSource: ModuleSource, expression: String?): ByteArray {
|
||||
return doEvaluate(moduleSource) { module ->
|
||||
val evalResult =
|
||||
expression?.let { VmUtils.evaluateExpression(module, it, securityManager, moduleResolver) }
|
||||
?: module
|
||||
VmValue.force(evalResult, false)
|
||||
threadLocalBufferPacker
|
||||
.get()
|
||||
.apply {
|
||||
clear()
|
||||
ValueEncoder(this).visit(evalResult)
|
||||
}
|
||||
.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
private class ValueEncoder(private val packer: MessagePacker) : VmValueVisitor {
|
||||
companion object {
|
||||
private const val CODE_OBJECT: Byte = 0x1
|
||||
private const val CODE_MAP: Byte = 0x2
|
||||
private const val CODE_MAPPING: Byte = 0x3
|
||||
private const val CODE_LIST: Byte = 0x4
|
||||
private const val CODE_LISTING: Byte = 0x5
|
||||
private const val CODE_SET: Byte = 0x6
|
||||
private const val CODE_DURATION: Byte = 0x7
|
||||
private const val CODE_DATASIZE: Byte = 0x8
|
||||
private const val CODE_PAIR: Byte = 0x9
|
||||
private const val CODE_INTSEQ: Byte = 0xA
|
||||
private const val CODE_REGEX: Byte = 0xB
|
||||
private const val CODE_CLASS: Byte = 0xC
|
||||
private const val CODE_TYPEALIAS: Byte = 0xD
|
||||
private const val CODE_FUNCTION: Byte = 0xE
|
||||
private const val CODE_BYTES: Byte = 0xF
|
||||
private const val CODE_PROPERTY: Byte = 0x10
|
||||
private const val CODE_ENTRY: Byte = 0x11
|
||||
private const val CODE_ELEMENT: Byte = 0x12
|
||||
}
|
||||
|
||||
override fun visitString(value: String) {
|
||||
packer.packString(value)
|
||||
}
|
||||
|
||||
override fun visitBoolean(value: Boolean) {
|
||||
packer.packBoolean(value)
|
||||
}
|
||||
|
||||
override fun visitInt(value: Long) {
|
||||
packer.packLong(value)
|
||||
}
|
||||
|
||||
override fun visitFloat(value: Double) {
|
||||
packer.packDouble(value)
|
||||
}
|
||||
|
||||
override fun visitDuration(value: VmDuration) {
|
||||
packer.packArrayHeader(3)
|
||||
packer.packInt(CODE_DURATION.toInt())
|
||||
packer.packDouble(value.value)
|
||||
packer.packString(value.unit.toString())
|
||||
}
|
||||
|
||||
override fun visitDataSize(value: VmDataSize) {
|
||||
packer.packArrayHeader(3)
|
||||
packer.packInt(CODE_DATASIZE.toInt())
|
||||
packer.packDouble(value.value)
|
||||
packer.packString(value.unit.toString())
|
||||
}
|
||||
|
||||
override fun visitBytes(value: VmBytes) {
|
||||
packer.packArrayHeader(2)
|
||||
packer.packInt(CODE_BYTES.toInt())
|
||||
packer.packBinaryHeader(value.bytes.size)
|
||||
packer.addPayload(value.bytes)
|
||||
}
|
||||
|
||||
override fun visitIntSeq(value: VmIntSeq) {
|
||||
packer.packArrayHeader(4)
|
||||
packer.packInt(CODE_INTSEQ.toInt())
|
||||
packer.packLong(value.start)
|
||||
packer.packLong(value.end)
|
||||
packer.packLong(value.step)
|
||||
}
|
||||
|
||||
private fun doVisitCollection(length: Int, value: Iterable<Any>) {
|
||||
packer.packArrayHeader(length)
|
||||
for (elem in value) {
|
||||
visit(elem)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitList(value: VmList) {
|
||||
packer.packArrayHeader(2)
|
||||
packer.packInt(CODE_LIST.toInt())
|
||||
doVisitCollection(value.length, value)
|
||||
}
|
||||
|
||||
override fun visitSet(value: VmSet) {
|
||||
packer.packArrayHeader(2)
|
||||
packer.packInt(CODE_SET.toInt())
|
||||
doVisitCollection(value.length, value)
|
||||
}
|
||||
|
||||
override fun visitMap(value: VmMap) {
|
||||
packer.packArrayHeader(2)
|
||||
packer.packInt(CODE_MAP.toInt())
|
||||
packer.packMapHeader(value.length)
|
||||
for ((k, v) in value) {
|
||||
visit(k)
|
||||
visit(v)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTyped(value: VmTyped) {
|
||||
packObjectPreamble(value)
|
||||
packer.packArrayHeader(value.vmClass.allRegularPropertyNames.size())
|
||||
value.iterateAlreadyForcedMemberValues(this::doVisitObjectMember)
|
||||
}
|
||||
|
||||
private fun doVisitObjectMember(key: Any, member: ObjectMember, value: Any): Boolean {
|
||||
if (member.isClass || member.isTypeAlias) return true
|
||||
|
||||
packer.packArrayHeader(3)
|
||||
when {
|
||||
member.isProp -> {
|
||||
packer.packInt(CODE_PROPERTY.toInt())
|
||||
packer.packString(key.toString())
|
||||
}
|
||||
member.isEntry -> {
|
||||
packer.packInt(CODE_ENTRY.toInt())
|
||||
visit(key)
|
||||
}
|
||||
else -> {
|
||||
packer.packInt(CODE_ELEMENT.toInt())
|
||||
packer.packLong(key as Long)
|
||||
}
|
||||
}
|
||||
visit(value)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun visitDynamic(value: VmDynamic) {
|
||||
packObjectPreamble(value)
|
||||
packer.packArrayHeader(value.regularMemberCount)
|
||||
value.iterateAlreadyForcedMemberValues(this::doVisitObjectMember)
|
||||
}
|
||||
|
||||
private fun packObjectPreamble(value: VmObjectLike) {
|
||||
packer.packArrayHeader(4)
|
||||
packer.packInt(CODE_OBJECT.toInt())
|
||||
packer.packString(value.vmClass.displayName)
|
||||
packer.packString(value.vmClass.module.moduleInfo.moduleKey.uri.toString())
|
||||
}
|
||||
|
||||
override fun visitListing(value: VmListing) {
|
||||
packer.packArrayHeader(2)
|
||||
packer.packInt(CODE_LISTING.toInt())
|
||||
packer.packArrayHeader(value.length)
|
||||
value.iterateAlreadyForcedMemberValues { _, _, memberValue ->
|
||||
visit(memberValue)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitMapping(value: VmMapping) {
|
||||
packer.packArrayHeader(2)
|
||||
packer.packInt(CODE_MAPPING.toInt())
|
||||
packer.packMapHeader(value.length.toInt())
|
||||
value.iterateAlreadyForcedMemberValues { key, _, memberValue ->
|
||||
visit(key)
|
||||
visit(memberValue)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClass(value: VmClass) {
|
||||
packer.packArrayHeader(1)
|
||||
packer.packInt(CODE_CLASS.toInt())
|
||||
}
|
||||
|
||||
override fun visitTypeAlias(value: VmTypeAlias) {
|
||||
packer.packArrayHeader(1)
|
||||
packer.packInt(CODE_TYPEALIAS.toInt())
|
||||
}
|
||||
|
||||
override fun visitPair(value: VmPair) {
|
||||
packer.packArrayHeader(3)
|
||||
packer.packInt(CODE_PAIR.toInt())
|
||||
visit(value.first)
|
||||
visit(value.second)
|
||||
}
|
||||
|
||||
override fun visitRegex(value: VmRegex) {
|
||||
packer.packArrayHeader(2)
|
||||
packer.packInt(CODE_REGEX.toInt())
|
||||
packer.packString(value.pattern.pattern())
|
||||
}
|
||||
|
||||
override fun visitNull(value: VmNull) {
|
||||
packer.packNil()
|
||||
}
|
||||
|
||||
override fun visitFunction(value: VmFunction) {
|
||||
packer.packArrayHeader(1)
|
||||
packer.packInt(CODE_FUNCTION.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import java.util.regex.Pattern
|
||||
import kotlin.random.Random
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
|
||||
import org.pkl.core.evaluatorSettings.TraceMode
|
||||
import org.pkl.core.externalreader.ExternalReaderProcess
|
||||
import org.pkl.core.externalreader.ExternalResourceResolver
|
||||
import org.pkl.core.externalreader.ModuleReaderSpec
|
||||
@@ -44,7 +43,7 @@ import org.pkl.core.resource.ResourceReaders
|
||||
import org.pkl.core.util.IoUtils
|
||||
|
||||
class Server(private val transport: MessageTransport) : AutoCloseable {
|
||||
private val evaluators: MutableMap<Long, BinaryEvaluator> = ConcurrentHashMap()
|
||||
private val evaluators: MutableMap<Long, Evaluator> = ConcurrentHashMap()
|
||||
|
||||
// https://github.com/jano7/executor would be the perfect executor here
|
||||
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
|
||||
@@ -98,7 +97,7 @@ class Server(private val transport: MessageTransport) : AutoCloseable {
|
||||
}
|
||||
|
||||
private fun handleCreateEvaluator(message: CreateEvaluatorRequest) {
|
||||
val evaluatorId = Random.Default.nextLong()
|
||||
val evaluatorId = Random.nextLong()
|
||||
val baseResponse = CreateEvaluatorResponse(message.requestId(), null, null)
|
||||
|
||||
val evaluator =
|
||||
@@ -126,7 +125,8 @@ class Server(private val transport: MessageTransport) : AutoCloseable {
|
||||
|
||||
executor.execute {
|
||||
try {
|
||||
val resp = evaluator.evaluate(ModuleSource.create(msg.moduleUri, msg.moduleText), msg.expr)
|
||||
val src = ModuleSource.create(msg.moduleUri, msg.moduleText)
|
||||
val resp = evaluator.evaluateExpressionPklBinary(src, msg.expr ?: "module")
|
||||
transport.send(baseResponse.copy(result = resp))
|
||||
} catch (e: PklBugException) {
|
||||
transport.send(baseResponse.copy(error = e.toString()))
|
||||
@@ -183,53 +183,52 @@ class Server(private val transport: MessageTransport) : AutoCloseable {
|
||||
)
|
||||
}
|
||||
|
||||
private fun createEvaluator(message: CreateEvaluatorRequest, evaluatorId: Long): BinaryEvaluator {
|
||||
private fun createEvaluator(message: CreateEvaluatorRequest, evaluatorId: Long): Evaluator {
|
||||
val modulePaths = message.modulePaths ?: emptyList()
|
||||
val resolver = ModulePathResolver(modulePaths)
|
||||
|
||||
try {
|
||||
val modulePaths = message.modulePaths ?: emptyList()
|
||||
val resolver = ModulePathResolver(modulePaths)
|
||||
val allowedModules = message.allowedModules?.map { Pattern.compile(it) } ?: emptyList()
|
||||
val allowedResources = message.allowedResources?.map { Pattern.compile(it) } ?: emptyList()
|
||||
val rootDir = message.rootDir
|
||||
val env = message.env ?: emptyMap()
|
||||
val properties = message.properties ?: emptyMap()
|
||||
val timeout = message.timeout
|
||||
val cacheDir = message.cacheDir
|
||||
val httpClient =
|
||||
with(HttpClient.builder()) {
|
||||
message.http?.proxy?.let { proxy ->
|
||||
setProxy(proxy.address, proxy.noProxy ?: listOf())
|
||||
proxy.address?.let(IoUtils::setSystemProxy)
|
||||
proxy.noProxy?.let { System.setProperty("http.nonProxyHosts", it.joinToString("|")) }
|
||||
return with(EvaluatorBuilder.unconfigured()) {
|
||||
setStackFrameTransformer(StackFrameTransformers.defaultTransformer)
|
||||
color = false
|
||||
httpClient =
|
||||
with(HttpClient.builder()) {
|
||||
message.http?.proxy?.let { proxy ->
|
||||
setProxy(proxy.address, proxy.noProxy ?: listOf())
|
||||
proxy.address?.let(IoUtils::setSystemProxy)
|
||||
proxy.noProxy?.let { System.setProperty("http.nonProxyHosts", it.joinToString("|")) }
|
||||
}
|
||||
message.http?.caCertificates?.let(::addCertificates)
|
||||
message.http?.rewrites?.let(::setRewrites)
|
||||
buildLazily()
|
||||
}
|
||||
message.http?.caCertificates?.let(::addCertificates)
|
||||
message.http?.rewrites?.let(::setRewrites)
|
||||
buildLazily()
|
||||
}
|
||||
val dependencies =
|
||||
securityManager =
|
||||
with(SecurityManagers.standardBuilder()) {
|
||||
message.allowedModules?.let { patterns ->
|
||||
setAllowedModules(patterns.map { Pattern.compile(it) })
|
||||
}
|
||||
message.allowedResources?.let { patterns ->
|
||||
setAllowedResources(patterns.map { Pattern.compile(it) })
|
||||
}
|
||||
setRootDir(message.rootDir)
|
||||
build()
|
||||
}
|
||||
logger = ClientLogger(evaluatorId, transport)
|
||||
addModuleKeyFactories(createModuleKeyFactories(message, evaluatorId, resolver))
|
||||
addResourceReaders(createResourceReaders(message, evaluatorId, resolver))
|
||||
message.env?.let { environmentVariables = it }
|
||||
message.properties?.let { externalProperties = it }
|
||||
timeout = message.timeout
|
||||
moduleCacheDir = message.cacheDir
|
||||
message.project?.let { proj ->
|
||||
buildDeclaredDependencies(proj.projectFileUri, proj.dependencies, null)
|
||||
val dependencies = buildDeclaredDependencies(proj.projectFileUri, proj.dependencies, null)
|
||||
log("Got dependencies: $dependencies")
|
||||
setProjectDependencies(dependencies)
|
||||
}
|
||||
log("Got dependencies: $dependencies")
|
||||
return BinaryEvaluator(
|
||||
StackFrameTransformers.defaultTransformer,
|
||||
SecurityManagers.standard(
|
||||
allowedModules,
|
||||
allowedResources,
|
||||
SecurityManagers.defaultTrustLevels,
|
||||
rootDir,
|
||||
),
|
||||
httpClient,
|
||||
ClientLogger(evaluatorId, transport),
|
||||
createModuleKeyFactories(message, evaluatorId, resolver),
|
||||
createResourceReaders(message, evaluatorId, resolver),
|
||||
env,
|
||||
properties,
|
||||
timeout,
|
||||
cacheDir,
|
||||
dependencies,
|
||||
message.outputFormat,
|
||||
message.traceMode ?: TraceMode.COMPACT,
|
||||
)
|
||||
outputFormat = message.outputFormat
|
||||
message.traceMode?.let { traceMode = it }
|
||||
build()
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw ProtocolException(e.message ?: "Failed to create an evalutor. $e", e)
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
res1 = "bar"
|
||||
res2 = ""
|
||||
res3 = 1
|
||||
res4 = 2.3
|
||||
res5 = true
|
||||
res6 = false
|
||||
res7 = null
|
||||
@@ -1,25 +0,0 @@
|
||||
module com.foo.bar.MyModule
|
||||
|
||||
class Person {
|
||||
firstName: String
|
||||
lastName: String
|
||||
age: Int
|
||||
}
|
||||
|
||||
barnOwl: Person = new {
|
||||
firstName = "Barn Owl"
|
||||
lastName = "Bird"
|
||||
age = 38
|
||||
}
|
||||
|
||||
pigeon: Person = new {
|
||||
firstName = "Pigeon"
|
||||
lastName = "Bird"
|
||||
age = 41
|
||||
}
|
||||
|
||||
typealias MyPerson = Person
|
||||
|
||||
personClass = Person
|
||||
|
||||
personTypeAlias = MyPerson
|
||||
@@ -1,11 +0,0 @@
|
||||
res1: DataSize = 1.b
|
||||
res2: DataSize = 2.kb
|
||||
res3: DataSize = 3.kib
|
||||
res4: DataSize = 4.mb
|
||||
res5: DataSize = 5.mib
|
||||
res6: DataSize = 6.gb
|
||||
res7: DataSize = 7.gib
|
||||
res8: DataSize = 8.tb
|
||||
res9: DataSize = 9.tib
|
||||
res10: DataSize = 10.pb
|
||||
res11: DataSize = 11.pib
|
||||
@@ -1,7 +0,0 @@
|
||||
res1 = 1.ns
|
||||
res2 = 2.us
|
||||
res3 = 3.ms
|
||||
res4 = 4.s
|
||||
res5 = 5.min
|
||||
res6 = 6.h
|
||||
res7 = 7.d
|
||||
@@ -1,2 +0,0 @@
|
||||
res1 = IntSeq(1, 3)
|
||||
res2 = IntSeq(1, 4).step(5)
|
||||
@@ -1,6 +0,0 @@
|
||||
res1: List<Int> = List(1, 3, 5, 7)
|
||||
res2: Listing<Int> = new { 2; 4; 6; 8 }
|
||||
res3: List<Int> = List()
|
||||
res4: Listing<Int> = new {}
|
||||
res5: List<List<Int>> = List(List(1, 2))
|
||||
res6: Listing<Listing<Int>> = new { new { 1; 2 } }
|
||||
@@ -1,21 +0,0 @@
|
||||
res1: Map = Map("foo", 1, "bar", 2)
|
||||
res2: Mapping = new {
|
||||
["foo"] = 1
|
||||
["bar"] = 2
|
||||
}
|
||||
res3: Mapping = new {
|
||||
["childMap"] = new Mapping {
|
||||
["childFoo"] = 3
|
||||
}
|
||||
}
|
||||
res4: Mapping = new {
|
||||
[Map("foo", 1)] = new Mapping {
|
||||
["bar"] = 2
|
||||
}
|
||||
}
|
||||
// https://github.com/apple/pkl/issues/1151
|
||||
res5: Mapping = new {
|
||||
local self = this
|
||||
["foo"] = new Dynamic { name = "foo" }
|
||||
["bar"] = new Dynamic { name = self["foo"].name + "bar" }
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
res1 = Pair(1, 2)
|
||||
res2 = Pair("foo", "bar")
|
||||
@@ -1,3 +0,0 @@
|
||||
res1 = Regex("abc")
|
||||
res2 = Regex("")
|
||||
res3 = Regex("(?m)^abc$")
|
||||
@@ -1,3 +0,0 @@
|
||||
res1: Set<Int> = Set(1, 3, 5, 7)
|
||||
res2: Set<Int> = Set()
|
||||
res3: Set<Any> = Set(1, true, "", null)
|
||||
@@ -1,32 +0,0 @@
|
||||
- 1
|
||||
- basic
|
||||
- file:///$snippetsDir/input/basic.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
- bar
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
- ''
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
- 1
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
- 2.3
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
- true
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
- false
|
||||
-
|
||||
- 16
|
||||
- res7
|
||||
- null
|
||||
@@ -1,54 +0,0 @@
|
||||
- 1
|
||||
- com.foo.bar.MyModule
|
||||
- file:///$snippetsDir/input/classes.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- barnOwl
|
||||
-
|
||||
- 1
|
||||
- com.foo.bar.MyModule#Person
|
||||
- file:///$snippetsDir/input/classes.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- firstName
|
||||
- Barn Owl
|
||||
-
|
||||
- 16
|
||||
- lastName
|
||||
- Bird
|
||||
-
|
||||
- 16
|
||||
- age
|
||||
- 38
|
||||
-
|
||||
- 16
|
||||
- pigeon
|
||||
-
|
||||
- 1
|
||||
- com.foo.bar.MyModule#Person
|
||||
- file:///$snippetsDir/input/classes.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- firstName
|
||||
- Pigeon
|
||||
-
|
||||
- 16
|
||||
- lastName
|
||||
- Bird
|
||||
-
|
||||
- 16
|
||||
- age
|
||||
- 41
|
||||
-
|
||||
- 16
|
||||
- personClass
|
||||
-
|
||||
- 12
|
||||
-
|
||||
- 16
|
||||
- personTypeAlias
|
||||
-
|
||||
- 13
|
||||
@@ -1,81 +0,0 @@
|
||||
- 1
|
||||
- datasize
|
||||
- file:///$snippetsDir/input/datasize.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 8
|
||||
- 1.0
|
||||
- b
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 8
|
||||
- 2.0
|
||||
- kb
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 8
|
||||
- 3.0
|
||||
- kib
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 8
|
||||
- 4.0
|
||||
- mb
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 8
|
||||
- 5.0
|
||||
- mib
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
-
|
||||
- 8
|
||||
- 6.0
|
||||
- gb
|
||||
-
|
||||
- 16
|
||||
- res7
|
||||
-
|
||||
- 8
|
||||
- 7.0
|
||||
- gib
|
||||
-
|
||||
- 16
|
||||
- res8
|
||||
-
|
||||
- 8
|
||||
- 8.0
|
||||
- tb
|
||||
-
|
||||
- 16
|
||||
- res9
|
||||
-
|
||||
- 8
|
||||
- 9.0
|
||||
- tib
|
||||
-
|
||||
- 16
|
||||
- res10
|
||||
-
|
||||
- 8
|
||||
- 10.0
|
||||
- pb
|
||||
-
|
||||
- 16
|
||||
- res11
|
||||
-
|
||||
- 8
|
||||
- 11.0
|
||||
- pib
|
||||
@@ -1,53 +0,0 @@
|
||||
- 1
|
||||
- duration
|
||||
- file:///$snippetsDir/input/duration.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 7
|
||||
- 1.0
|
||||
- ns
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 7
|
||||
- 2.0
|
||||
- us
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 7
|
||||
- 3.0
|
||||
- ms
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 7
|
||||
- 4.0
|
||||
- s
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 7
|
||||
- 5.0
|
||||
- min
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
-
|
||||
- 7
|
||||
- 6.0
|
||||
- h
|
||||
-
|
||||
- 16
|
||||
- res7
|
||||
-
|
||||
- 7
|
||||
- 7.0
|
||||
- d
|
||||
@@ -1,20 +0,0 @@
|
||||
- 1
|
||||
- intseq
|
||||
- file:///$snippetsDir/input/intseq.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 10
|
||||
- 1
|
||||
- 3
|
||||
- 1
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 10
|
||||
- 1
|
||||
- 4
|
||||
- 5
|
||||
@@ -1,58 +0,0 @@
|
||||
- 1
|
||||
- list
|
||||
- file:///$snippetsDir/input/list.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 4
|
||||
-
|
||||
- 1
|
||||
- 3
|
||||
- 5
|
||||
- 7
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 5
|
||||
-
|
||||
- 2
|
||||
- 4
|
||||
- 6
|
||||
- 8
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 4
|
||||
- []
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 5
|
||||
- []
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 4
|
||||
-
|
||||
-
|
||||
- 4
|
||||
-
|
||||
- 1
|
||||
- 2
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
-
|
||||
- 5
|
||||
-
|
||||
-
|
||||
- 5
|
||||
-
|
||||
- 1
|
||||
- 2
|
||||
@@ -1,68 +0,0 @@
|
||||
- 1
|
||||
- map
|
||||
- file:///$snippetsDir/input/map.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 2
|
||||
-
|
||||
foo: 1
|
||||
bar: 2
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 3
|
||||
-
|
||||
foo: 1
|
||||
bar: 2
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 3
|
||||
-
|
||||
childMap:
|
||||
- 3
|
||||
-
|
||||
childFoo: 3
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 3
|
||||
-
|
||||
?
|
||||
- 2
|
||||
-
|
||||
foo: 1
|
||||
:
|
||||
- 3
|
||||
-
|
||||
bar: 2
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 3
|
||||
-
|
||||
foo:
|
||||
- 1
|
||||
- Dynamic
|
||||
- pkl:base
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- name
|
||||
- foo
|
||||
bar:
|
||||
- 1
|
||||
- Dynamic
|
||||
- pkl:base
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- name
|
||||
- foobar
|
||||
@@ -1,18 +0,0 @@
|
||||
- 1
|
||||
- pair
|
||||
- file:///$snippetsDir/input/pair.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 9
|
||||
- 1
|
||||
- 2
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 9
|
||||
- foo
|
||||
- bar
|
||||
@@ -1,22 +0,0 @@
|
||||
- 1
|
||||
- regex
|
||||
- file:///$snippetsDir/input/regex.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 11
|
||||
- abc
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 11
|
||||
- ''
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 11
|
||||
- (?m)^abc$
|
||||
@@ -1,30 +0,0 @@
|
||||
- 1
|
||||
- set
|
||||
- file:///$snippetsDir/input/set.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 6
|
||||
-
|
||||
- 1
|
||||
- 3
|
||||
- 5
|
||||
- 7
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 6
|
||||
- []
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 6
|
||||
-
|
||||
- 1
|
||||
- true
|
||||
- ''
|
||||
- null
|
||||
@@ -308,13 +308,13 @@ abstract class AbstractServerTest {
|
||||
)
|
||||
)
|
||||
val evaluateResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluateResponse.result?.debugYaml)
|
||||
assertThat(evaluateResponse.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
- 6
|
||||
-
|
||||
- bird:/foo.txt
|
||||
- bird:/subdir/bar.txt
|
||||
- 'bird:/foo.txt'
|
||||
- 'bird:/subdir/bar.txt'
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -346,7 +346,7 @@ abstract class AbstractServerTest {
|
||||
)
|
||||
)
|
||||
val evaluateResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluateResponse.result?.debugYaml)
|
||||
assertThat(evaluateResponse.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
- 6
|
||||
@@ -547,11 +547,11 @@ abstract class AbstractServerTest {
|
||||
"""
|
||||
- 6
|
||||
-
|
||||
- bird:/Person.pkl
|
||||
- bird:/birds/parrot.pkl
|
||||
- bird:/birds/pigeon.pkl
|
||||
- bird:/majesticBirds/barnOwl.pkl
|
||||
- bird:/majesticBirds/elfOwl.pkl
|
||||
- 'bird:/Person.pkl'
|
||||
- 'bird:/birds/parrot.pkl'
|
||||
- 'bird:/birds/pigeon.pkl'
|
||||
- 'bird:/majesticBirds/barnOwl.pkl'
|
||||
- 'bird:/majesticBirds/elfOwl.pkl'
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -643,7 +643,7 @@ abstract class AbstractServerTest {
|
||||
val response = client.receive<EvaluateResponse>()
|
||||
assertThat(response.error).isNull()
|
||||
val tripleQuote = "\"\"\""
|
||||
assertThat(response.result?.debugYaml)
|
||||
assertThat(response.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
@@ -666,6 +666,7 @@ abstract class AbstractServerTest {
|
||||
res3 {
|
||||
ressy = "the module2 output"
|
||||
}
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -713,7 +714,7 @@ abstract class AbstractServerTest {
|
||||
)
|
||||
|
||||
val evaluatorResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluatorResponse.result?.debugYaml).isEqualTo("1")
|
||||
assertThat(evaluatorResponse.result?.debugRendering).isEqualTo("1")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -753,13 +754,14 @@ abstract class AbstractServerTest {
|
||||
|
||||
val evaluateResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluateResponse.result).isNotNull
|
||||
assertThat(evaluateResponse.result?.debugYaml)
|
||||
assertThat(evaluateResponse.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
firstName = "Pigeon"
|
||||
lastName = "Bird"
|
||||
fullName = "Pigeon Bird"
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -793,13 +795,14 @@ abstract class AbstractServerTest {
|
||||
|
||||
val response12 = client.receive<EvaluateResponse>()
|
||||
assertThat(response12.result).isNotNull
|
||||
assertThat(response12.result?.debugYaml)
|
||||
assertThat(response12.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
firstName = "Pigeon"
|
||||
lastName = "Bird"
|
||||
fullName = "Pigeon Bird"
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -823,13 +826,14 @@ abstract class AbstractServerTest {
|
||||
|
||||
val response22 = client.receive<EvaluateResponse>()
|
||||
assertThat(response22.result).isNotNull
|
||||
assertThat(response22.result?.debugYaml)
|
||||
assertThat(response22.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
firstName = "Parrot"
|
||||
lastName = "Bird"
|
||||
fullName = "Parrot Bird"
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -996,9 +1000,6 @@ abstract class AbstractServerTest {
|
||||
.contains("Rewrite rule must end with '/', but was 'https://example.com'")
|
||||
}
|
||||
|
||||
private val ByteArray.debugYaml
|
||||
get() = MessagePackDebugRenderer(this).output.trimIndent()
|
||||
|
||||
private fun TestTransport.sendCreateEvaluatorRequest(
|
||||
requestId: Long = 123,
|
||||
resourceReaders: List<ResourceReaderSpec> = listOf(),
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.server
|
||||
|
||||
import java.nio.file.Path
|
||||
import kotlin.Pair
|
||||
import kotlin.reflect.KClass
|
||||
import org.junit.platform.commons.annotation.Testable
|
||||
import org.pkl.commons.test.InputOutputTestEngine
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.evaluatorSettings.TraceMode
|
||||
import org.pkl.core.http.HttpClient
|
||||
import org.pkl.core.module.ModuleKeyFactories
|
||||
|
||||
@Testable class BinaryEvaluatorSnippetTests
|
||||
|
||||
class BinaryEvaluatorSnippetTestEngine : InputOutputTestEngine() {
|
||||
override val testClass: KClass<*> = BinaryEvaluatorSnippetTests::class
|
||||
|
||||
private val snippetsDir = rootProjectDir.resolve("pkl-server/src/test/files/SnippetTests")
|
||||
|
||||
private val outputDir = snippetsDir.resolve("output")
|
||||
|
||||
override val inputDir: Path = snippetsDir.resolve("input")
|
||||
|
||||
override val isInputFile: (Path) -> Boolean = { true }
|
||||
|
||||
override fun expectedOutputFileFor(inputFile: Path): Path {
|
||||
val relativePath = inputDir.relativize(inputFile).toString()
|
||||
return outputDir.resolve(relativePath.dropLast(3) + "yaml")
|
||||
}
|
||||
|
||||
private val evaluator =
|
||||
BinaryEvaluator(
|
||||
StackFrameTransformers.empty,
|
||||
SecurityManagers.defaultManager,
|
||||
HttpClient.dummyClient(),
|
||||
Loggers.stdErr(),
|
||||
listOf(ModuleKeyFactories.file),
|
||||
listOf(),
|
||||
mapOf(),
|
||||
mapOf(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TraceMode.COMPACT,
|
||||
)
|
||||
|
||||
private fun String.stripFilePaths() =
|
||||
replace(snippetsDir.toUri().toString(), "file:///\$snippetsDir/")
|
||||
|
||||
override fun generateOutputFor(inputFile: Path): Pair<Boolean, String> {
|
||||
val bytes = evaluator.evaluate(ModuleSource.path(inputFile), null)
|
||||
return true to bytes.debugRendering.stripFilePaths()
|
||||
}
|
||||
}
|
||||
|
||||
val ByteArray.debugRendering: String
|
||||
get() = MessagePackDebugRenderer(this).output
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.server
|
||||
|
||||
import java.nio.file.Path
|
||||
import java.util.regex.Pattern
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.evaluatorSettings.TraceMode
|
||||
import org.pkl.core.http.HttpClient
|
||||
import org.pkl.core.module.ModuleKeyFactories
|
||||
import org.pkl.core.resource.ResourceReaders
|
||||
|
||||
class BinaryEvaluatorTest {
|
||||
private val evaluator =
|
||||
BinaryEvaluator(
|
||||
StackFrameTransformers.defaultTransformer,
|
||||
SecurityManagers.standard(
|
||||
listOf(Pattern.compile(".*")),
|
||||
listOf(Pattern.compile(".*")),
|
||||
SecurityManagers.defaultTrustLevels,
|
||||
Path.of(""),
|
||||
),
|
||||
HttpClient.dummyClient(),
|
||||
Loggers.noop(),
|
||||
listOf(ModuleKeyFactories.standardLibrary),
|
||||
listOf(ResourceReaders.environmentVariable(), ResourceReaders.externalProperty()),
|
||||
mapOf(),
|
||||
mapOf(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TraceMode.COMPACT,
|
||||
)
|
||||
|
||||
private fun evaluate(text: String, expression: String?) =
|
||||
evaluator.evaluate(ModuleSource.text(text), expression)
|
||||
|
||||
@Test
|
||||
fun `evaluate whole module`() {
|
||||
val bytes = evaluate("foo = 1", null)
|
||||
assertThat(bytes.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
- 1
|
||||
- text
|
||||
- repl:text
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- foo
|
||||
- 1
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate subpath`() {
|
||||
val bytes =
|
||||
evaluate(
|
||||
"""
|
||||
foo {
|
||||
bar = 2
|
||||
}
|
||||
"""
|
||||
.trimIndent(),
|
||||
"foo.bar",
|
||||
)
|
||||
|
||||
assertThat(bytes.asInt()).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate output text`() {
|
||||
val bytes =
|
||||
evaluate(
|
||||
"""
|
||||
foo {
|
||||
bar = 2
|
||||
}
|
||||
|
||||
output {
|
||||
renderer = new YamlRenderer {}
|
||||
}
|
||||
"""
|
||||
.trimIndent(),
|
||||
"output.text",
|
||||
)
|
||||
|
||||
assertThat(bytes.asString())
|
||||
.isEqualTo(
|
||||
"""
|
||||
foo:
|
||||
bar: 2
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate let expression`() {
|
||||
val bytes = evaluate("foo = 1", "let (bar = 2) foo + bar")
|
||||
|
||||
assertThat(bytes.asInt()).isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate import expression`() {
|
||||
val bytes = evaluate("", """import("pkl:release").current.documentation.homepage""")
|
||||
|
||||
assertThat(bytes.asString()).startsWith("https://pkl-lang.org/")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate expression with invalid syntax`() {
|
||||
val error = assertThrows<PklException> { evaluate("foo = 1", "<>!!!") }
|
||||
|
||||
assertThat(error).hasMessageContaining("Unexpected token")
|
||||
assertThat(error).hasMessageContaining("<>!!!")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate non-expression`() {
|
||||
val error = assertThrows<PklException> { evaluate("bar = 2", "bar = 15") }
|
||||
|
||||
assertThat(error).hasMessageContaining("Unexpected token")
|
||||
assertThat(error).hasMessageContaining("bar = 15")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate semantically invalid expression`() {
|
||||
val error = assertThrows<PklException> { evaluate("foo = 1", "foo as String") }
|
||||
|
||||
assertThat(error).hasMessageContaining("Expected value of type `String`, but got type `Int`")
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.server
|
||||
|
||||
import java.lang.IllegalStateException
|
||||
import org.msgpack.core.MessagePack
|
||||
import org.msgpack.core.MessageUnpacker
|
||||
import org.msgpack.value.ValueType
|
||||
import org.pkl.core.util.yaml.YamlEmitter
|
||||
|
||||
/** Renders MessagePack structures in YAML. */
|
||||
class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
private val unpacker: MessageUnpacker = MessagePack.newDefaultUnpacker(bytes)
|
||||
private val currIndent = StringBuilder("")
|
||||
private val sb = StringBuilder()
|
||||
private val indent = " "
|
||||
private val yamlEmitter = YamlEmitter.create(sb, "1.2", indent)
|
||||
|
||||
private fun incIndent() {
|
||||
currIndent.append(indent)
|
||||
}
|
||||
|
||||
private fun decIndent() {
|
||||
currIndent.setLength(currIndent.length - indent.length)
|
||||
}
|
||||
|
||||
private fun newline() {
|
||||
sb.append("\n")
|
||||
sb.append(currIndent)
|
||||
}
|
||||
|
||||
private fun renderKey() {
|
||||
val mf = unpacker.nextFormat
|
||||
when (mf.valueType!!) {
|
||||
ValueType.STRING -> yamlEmitter.emit(unpacker.unpackString(), currIndent, true)
|
||||
ValueType.MAP,
|
||||
ValueType.ARRAY -> {
|
||||
sb.append("? ")
|
||||
incIndent()
|
||||
renderValue()
|
||||
decIndent()
|
||||
newline()
|
||||
}
|
||||
else -> renderValue()
|
||||
}
|
||||
sb.append(": ")
|
||||
}
|
||||
|
||||
private fun renderValue() {
|
||||
val mf = unpacker.nextFormat
|
||||
when (mf.valueType!!) {
|
||||
ValueType.INTEGER,
|
||||
ValueType.FLOAT,
|
||||
ValueType.BOOLEAN,
|
||||
ValueType.NIL -> sb.append(unpacker.unpackValue().toJson())
|
||||
ValueType.STRING -> yamlEmitter.emit(unpacker.unpackString(), currIndent, false)
|
||||
ValueType.ARRAY -> {
|
||||
val size = unpacker.unpackArrayHeader()
|
||||
if (size == 0) {
|
||||
sb.append("[]")
|
||||
return
|
||||
}
|
||||
for (i in 0 until size) {
|
||||
newline()
|
||||
sb.append("- ")
|
||||
incIndent()
|
||||
renderValue()
|
||||
decIndent()
|
||||
}
|
||||
}
|
||||
ValueType.MAP -> {
|
||||
val size = unpacker.unpackMapHeader()
|
||||
if (size == 0) {
|
||||
sb.append("{}")
|
||||
return
|
||||
}
|
||||
for (i in 0 until size) {
|
||||
newline()
|
||||
renderKey()
|
||||
incIndent()
|
||||
renderValue()
|
||||
decIndent()
|
||||
}
|
||||
}
|
||||
ValueType.BINARY,
|
||||
ValueType.EXTENSION -> throw IllegalStateException("Unexpected value type ${mf.valueType}")
|
||||
}
|
||||
}
|
||||
|
||||
val output: String by lazy {
|
||||
renderValue()
|
||||
sb.toString().removePrefix("\n")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.msgpack.core.MessagePack
|
||||
import org.msgpack.value.ImmutableValue
|
||||
import org.pkl.commons.test.MessagePackDebugRenderer
|
||||
|
||||
fun ByteArray.unpack(): ImmutableValue = MessagePack.newDefaultUnpacker(this).unpackValue()
|
||||
|
||||
@@ -27,6 +28,9 @@ fun ByteArray.asInt(): Int = unpack().asIntegerValue().asInt()
|
||||
|
||||
fun ByteArray.asString(): String = unpack().asStringValue().asString()
|
||||
|
||||
val ByteArray.debugRendering: String
|
||||
get() = MessagePackDebugRenderer(this).output
|
||||
|
||||
fun createDirectExecutor(): ExecutorService =
|
||||
object : AbstractExecutorService() {
|
||||
override fun execute(command: Runnable) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
org.pkl.server.BinaryEvaluatorSnippetTestEngine
|
||||
Reference in New Issue
Block a user