mirror of
https://github.com/apple/pkl.git
synced 2026-03-28 11:51:58 +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:
@@ -36,7 +36,9 @@ For example, value `8` gets encoded as MessagePack `int8` format.
|
||||
== Non-primitives
|
||||
|
||||
All non-primitive values are encoded as MessagePack arrays.
|
||||
The first slot of the array designates the value's type. The remaining slots have fixed meanings depending on the type.
|
||||
The first slot of the array designates the value's type.
|
||||
The remaining slots have fixed meanings depending on the type.
|
||||
Additional slots may be added to types in future Pkl releases. Decoders *must* be designed to defensively discard values beyond the number of known slots for a type or provide meaningful error messages.
|
||||
|
||||
The array's length is the number of slots that are filled. For example, xref:{uri-stdlib-List}[List] is encoded as an MessagePack array with two elements.
|
||||
|
||||
@@ -46,16 +48,16 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
||code |type |description |type |description |type |description
|
||||
|
||||
|link:{uri-stdlib-Typed}[Typed], link:{uri-stdlib-Dynamic}[Dynamic]
|
||||
|`0x1`
|
||||
|`0x01`
|
||||
|link:{uri-messagepack-str}[str]
|
||||
|Fully qualified class name
|
||||
|<<type-name-encoding,Class name>>
|
||||
|link:{uri-messagepack-str}[str]
|
||||
|Enclosing module URI
|
||||
|link:{uri-messagepack-array}[array]
|
||||
|Array of <<object-members,object members>>
|
||||
|
||||
|link:{uri-stdlib-Map}[Map]
|
||||
|`0x2`
|
||||
|`0x02`
|
||||
|link:{uri-messagepack-map}[map]
|
||||
|Map of `<value>` to `<value>`
|
||||
|
|
||||
@@ -64,7 +66,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Mapping}[Mapping]
|
||||
|`0x3`
|
||||
|`0x03`
|
||||
|link:{uri-messagepack-map}[map]
|
||||
|Map of `<value>` to `<value>`
|
||||
|
|
||||
@@ -73,7 +75,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-List}[List]
|
||||
|`0x4`
|
||||
|`0x04`
|
||||
|link:{uri-messagepack-array}[array]
|
||||
|Array of `<value>`
|
||||
|
|
||||
@@ -82,7 +84,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Listing}[Listing]
|
||||
|`0x5`
|
||||
|`0x05`
|
||||
|link:{uri-messagepack-array}[array]
|
||||
|Array of `<value>`
|
||||
|
|
||||
@@ -91,7 +93,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Set}[Set]
|
||||
|`0x6`
|
||||
|`0x06`
|
||||
|link:{uri-messagepack-array}[array]
|
||||
|Array of `<value>`
|
||||
|
|
||||
@@ -100,7 +102,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Duration}[Duration]
|
||||
|`0x7`
|
||||
|`0x07`
|
||||
|{uri-messagepack-float}[float64]
|
||||
|Duration value
|
||||
|link:{uri-messagepack-str}[str]
|
||||
@@ -109,7 +111,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-DataSize}[DataSize]
|
||||
|`0x8`
|
||||
|`0x08`
|
||||
|link:{uri-messagepack-float}[float64]
|
||||
|Value (float64)
|
||||
|link:{uri-messagepack-str}[str]
|
||||
@@ -118,7 +120,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Pair}[Pair]
|
||||
|`0x9`
|
||||
|`0x09`
|
||||
|`<value>`
|
||||
|First value
|
||||
|`<value>`
|
||||
@@ -127,7 +129,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-IntSeq}[IntSeq]
|
||||
|`0xA`
|
||||
|`0x0A`
|
||||
|link:{uri-messagepack-int}[int]
|
||||
|Start
|
||||
|link:{uri-messagepack-int}[int]
|
||||
@@ -136,7 +138,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|Step
|
||||
|
||||
|link:{uri-stdlib-Regex}[Regex]
|
||||
|`0xB`
|
||||
|`0x0B`
|
||||
|link:{uri-messagepack-str}[str]
|
||||
|Regex string representation
|
||||
|
|
||||
@@ -145,25 +147,25 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Class}[Class]
|
||||
|`0xC`
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|`0x0C`
|
||||
|link:{uri-messagepack-str}[str]
|
||||
|<<type-name-encoding,Class name>>
|
||||
|link:{uri-messagepack-str}[str]
|
||||
|Module URI
|
||||
|
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-TypeAlias}[TypeAlias]
|
||||
|`0xD`
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|`0x0D`
|
||||
|link:{uri-messagepack-str}[str]
|
||||
|<<type-name-encoding,TypeAlias name>>
|
||||
|link:{uri-messagepack-str}[str]
|
||||
|Module URI
|
||||
|
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Function}[Function]
|
||||
|`0xE`
|
||||
|`0x0E`
|
||||
|
|
||||
|
|
||||
|
|
||||
@@ -172,7 +174,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|
||||
|link:{uri-stdlib-Bytes}[Bytes]
|
||||
|`0xF`
|
||||
|`0x0F`
|
||||
|link:{uri-messagepack-bin}[bin]
|
||||
|Binary contents
|
||||
|
|
||||
@@ -181,6 +183,19 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
||||
|
|
||||
|===
|
||||
|
||||
[[type-name-encoding]]
|
||||
[NOTE]
|
||||
====
|
||||
Type names have specific encoding rules:
|
||||
|
||||
* When the module URI is `pkl:base`:
|
||||
** If the type name is `ModuleClass`, this type represents the module class of `pkl:base`.
|
||||
** Otherwise, the type name corresponds to a type in `pkl:base`.
|
||||
* For all other module URIs:
|
||||
** When the type name contains `\#`, the string after the `#` character corresponds to a type in that module. The string before the `#` is the name of the module.
|
||||
** Otherwise, the type name is the name of the module and represents the class of the module.
|
||||
====
|
||||
|
||||
[[object-members]]
|
||||
== Object Members
|
||||
|
||||
@@ -212,4 +227,3 @@ Like non-primitive values, object members are encoded as MessagePack arrays, whe
|
||||
|`<value>`
|
||||
|element value
|
||||
|===
|
||||
|
||||
|
||||
@@ -47,5 +47,6 @@ org.junit.platform:junit-platform-commons:1.14.0=apiDependenciesMetadata,compile
|
||||
org.junit.platform:junit-platform-engine:1.14.0=apiDependenciesMetadata,compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.14.0=testRuntimeClasspath
|
||||
org.junit:junit-bom:5.14.0=apiDependenciesMetadata,compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.msgpack:msgpack-core:0.9.8=compileClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=apiDependenciesMetadata,compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
empty=annotationProcessor,compileOnlyDependenciesMetadata,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDefExtensions,sourcesJar,testAnnotationProcessor,testApiDependenciesMetadata,testCompileOnlyDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDefExtensions
|
||||
|
||||
@@ -28,6 +28,7 @@ dependencies {
|
||||
api(libs.junitParams)
|
||||
api(projects.pklCommons) // for convenience
|
||||
implementation(libs.assertj)
|
||||
implementation(libs.msgpack)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 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.
|
||||
@@ -13,13 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.server
|
||||
package org.pkl.commons.test
|
||||
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.*
|
||||
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) {
|
||||
@@ -27,7 +26,6 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
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)
|
||||
@@ -45,7 +43,7 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
private fun renderKey() {
|
||||
val mf = unpacker.nextFormat
|
||||
when (mf.valueType!!) {
|
||||
ValueType.STRING -> yamlEmitter.emit(unpacker.unpackString(), currIndent, true)
|
||||
ValueType.STRING -> emitString(unpacker.unpackString())
|
||||
ValueType.MAP,
|
||||
ValueType.ARRAY -> {
|
||||
sb.append("? ")
|
||||
@@ -66,14 +64,14 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
ValueType.FLOAT,
|
||||
ValueType.BOOLEAN,
|
||||
ValueType.NIL -> sb.append(unpacker.unpackValue().toJson())
|
||||
ValueType.STRING -> yamlEmitter.emit(unpacker.unpackString(), currIndent, false)
|
||||
ValueType.STRING -> emitString(unpacker.unpackString())
|
||||
ValueType.ARRAY -> {
|
||||
val size = unpacker.unpackArrayHeader()
|
||||
if (size == 0) {
|
||||
sb.append("[]")
|
||||
return
|
||||
}
|
||||
for (i in 0 until size) {
|
||||
repeat(size) {
|
||||
newline()
|
||||
sb.append("- ")
|
||||
incIndent()
|
||||
@@ -87,7 +85,7 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
sb.append("{}")
|
||||
return
|
||||
}
|
||||
for (i in 0 until size) {
|
||||
repeat(size) {
|
||||
newline()
|
||||
renderKey()
|
||||
incIndent()
|
||||
@@ -95,7 +93,12 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
decIndent()
|
||||
}
|
||||
}
|
||||
ValueType.BINARY,
|
||||
ValueType.BINARY -> {
|
||||
// https://yaml.org/type/binary.html
|
||||
sb.append("!!binary ")
|
||||
val size = unpacker.unpackBinaryHeader()
|
||||
emitString(Base64.getEncoder().encodeToString(unpacker.readPayload(size)))
|
||||
}
|
||||
ValueType.EXTENSION -> throw IllegalStateException("Unexpected value type ${mf.valueType}")
|
||||
}
|
||||
}
|
||||
@@ -104,4 +107,75 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
renderValue()
|
||||
sb.toString().removePrefix("\n")
|
||||
}
|
||||
|
||||
fun emitString(str: String) {
|
||||
val newlineIndex = str.indexOf('\n')
|
||||
if (newlineIndex < 0) {
|
||||
emitSingleLineString(str)
|
||||
} else {
|
||||
emitMultiLineString(str, newlineIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// adapted from org.pkl.core.util.yaml.YamlEmitter.emitSingleQuotedString
|
||||
fun emitSingleLineString(str: String) {
|
||||
sb.append('\'')
|
||||
|
||||
val singleQuoteIndex = str.indexOfFirst { it == '\'' }
|
||||
if (singleQuoteIndex == -1) {
|
||||
sb.append(str)
|
||||
} else {
|
||||
var start = 0
|
||||
val length = str.length
|
||||
for (i in singleQuoteIndex..<length) {
|
||||
if (str[i] == '\'') {
|
||||
sb.append(str, start, i).append("''")
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
if (start < length) {
|
||||
sb.append(str, start, length)
|
||||
}
|
||||
}
|
||||
|
||||
sb.append('\'')
|
||||
}
|
||||
|
||||
// adapted from org.pkl.core.util.yaml.YamlEmitter.emitSingleQuotedString
|
||||
fun emitMultiLineString(str: String, newlineIndex: Int) {
|
||||
currIndent.append(indent)
|
||||
|
||||
sb.append('|')
|
||||
if (str.first() == ' ') {
|
||||
sb.append(indent.length)
|
||||
}
|
||||
|
||||
val length = str.length
|
||||
if (str.last() == '\n') {
|
||||
if (length == 1 || str[length - 2] == '\n') {
|
||||
sb.append('+')
|
||||
}
|
||||
} else {
|
||||
sb.append('-')
|
||||
}
|
||||
|
||||
sb.append('\n')
|
||||
|
||||
var start = 0
|
||||
for (i in newlineIndex..<length) {
|
||||
if (str[i] != '\n') continue
|
||||
if (i == start) {
|
||||
// don't add leading indent before newline
|
||||
sb.append('\n')
|
||||
} else {
|
||||
sb.append(currIndent).append(str, start, i + 1)
|
||||
}
|
||||
start = i + 1
|
||||
}
|
||||
if (start < length) {
|
||||
sb.append(currIndent).append(str, start, length)
|
||||
}
|
||||
|
||||
currIndent.setLength(currIndent.length - indent.length)
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ org.junit.platform:junit-platform-commons:1.14.0=testCompileClasspath,testImplem
|
||||
org.junit.platform:junit-platform-engine:1.14.0=testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.14.0=testRuntimeClasspath
|
||||
org.junit:junit-bom:5.14.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.msgpack:msgpack-core:0.9.8=pklCodegenJava,runtimeClasspath,testRuntimeClasspath
|
||||
org.msgpack:msgpack-core:0.9.8=compileClasspath,pklCodegenJava,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.organicdesign:Paguro:3.10.3=pklCodegenJava,runtimeClasspath,testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.10=pklCodegenJava,runtimeClasspath,testRuntimeClasspath
|
||||
|
||||
@@ -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.
|
||||
@@ -82,6 +82,7 @@ dependencies {
|
||||
api(projects.pklCore)
|
||||
|
||||
implementation(libs.geantyref)
|
||||
implementation(libs.msgpack)
|
||||
|
||||
testImplementation(libs.javaxInject)
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -15,9 +15,14 @@
|
||||
*/
|
||||
package org.pkl.config.java;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import org.pkl.config.java.mapper.ConversionException;
|
||||
import org.pkl.config.java.mapper.ValueMapper;
|
||||
import org.pkl.core.Composite;
|
||||
import org.pkl.core.Evaluator;
|
||||
import org.pkl.core.PklBinaryDecoder;
|
||||
|
||||
/**
|
||||
* A root, intermediate, or leaf node in a configuration tree. Child nodes can be obtained by name
|
||||
@@ -67,4 +72,51 @@ public interface Config {
|
||||
* @throws ConversionException if the value cannot be converted to the given type
|
||||
*/
|
||||
<T> T as(JavaType<T> type);
|
||||
|
||||
/**
|
||||
* Decode a config from the supplied byte array.
|
||||
*
|
||||
* @return the encoded config
|
||||
*/
|
||||
static Config from(byte[] bytes, ValueMapper mapper) {
|
||||
return makeConfig(PklBinaryDecoder.decode(bytes), mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a config from the supplied byte array using a preconfigured {@link ValueMapper}.
|
||||
*
|
||||
* @return the encoded config
|
||||
*/
|
||||
static Config from(byte[] bytes) {
|
||||
return from(bytes, ValueMapper.preconfigured());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a config from the supplied {@link InputStream} using a preconfigured {@link
|
||||
* ValueMapper}.
|
||||
*
|
||||
* @return the encoded config
|
||||
*/
|
||||
static Config from(InputStream inputStream, ValueMapper mapper) {
|
||||
return makeConfig(PklBinaryDecoder.decode(inputStream), mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a config from the supplied {@link InputStream}.
|
||||
*
|
||||
* @return the encoded config
|
||||
*/
|
||||
static Config from(InputStream inputStream) {
|
||||
return from(inputStream, ValueMapper.preconfigured());
|
||||
}
|
||||
|
||||
private static Config makeConfig(Object decoded, ValueMapper mapper) {
|
||||
if (decoded instanceof Composite composite) {
|
||||
return new CompositeConfig("", mapper, composite);
|
||||
}
|
||||
if (decoded instanceof Map<?, ?> map) {
|
||||
return new MapConfig("", mapper, map);
|
||||
}
|
||||
return new LeafConfig("", mapper, decoded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.config.java;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pkl.config.java.mapper.Named;
|
||||
import org.pkl.config.java.mapper.Types;
|
||||
import org.pkl.core.PObject;
|
||||
|
||||
public abstract class AbstractConfigTest {
|
||||
|
||||
private final Config pigeonConfig = getPigeonConfig();
|
||||
private final Config pigeonModuleConfig = getPigeonModuleConfig();
|
||||
private final Config pairConfig = getPairConfig();
|
||||
private final Config mapConfig = getMapConfig();
|
||||
|
||||
protected abstract Config getPigeonConfig();
|
||||
|
||||
protected abstract Config getPigeonModuleConfig();
|
||||
|
||||
protected abstract Config getPairConfig();
|
||||
|
||||
protected abstract Config getMapConfig();
|
||||
|
||||
@Test
|
||||
public void navigate() {
|
||||
var pigeon = pigeonConfig.get("pigeon");
|
||||
assertThat(pigeon.getQualifiedName()).isEqualTo("pigeon");
|
||||
assertThat(pigeon.getRawValue()).isInstanceOf(PObject.class);
|
||||
|
||||
var address = pigeon.get("address");
|
||||
assertThat(address.getQualifiedName()).isEqualTo("pigeon.address");
|
||||
assertThat(address.getRawValue()).isInstanceOf(PObject.class);
|
||||
|
||||
var street = address.get("street");
|
||||
assertThat(street.getQualifiedName()).isEqualTo("pigeon.address.street");
|
||||
assertThat(street.getRawValue()).isInstanceOf(String.class);
|
||||
|
||||
assertThat(street.as(String.class)).isEqualTo("Fuzzy St.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void navigateToNonExistingObjectChild() {
|
||||
var pigeon = pigeonConfig.get("pigeon");
|
||||
var t = catchThrowable(() -> pigeon.get("non-existing"));
|
||||
|
||||
assertThat(t)
|
||||
.isInstanceOf(NoSuchChildException.class)
|
||||
.hasMessageStartingWith(
|
||||
"Node `pigeon` of type `pkl.base#Dynamic` "
|
||||
+ "does not have a property named `non-existing`.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void navigateToNonExistingMapChild() {
|
||||
var map = mapConfig.get("x");
|
||||
var t = catchThrowable(() -> map.get("non-existing"));
|
||||
|
||||
assertThat(t)
|
||||
.isInstanceOf(NoSuchChildException.class)
|
||||
.hasMessageStartingWith(
|
||||
"Node `x` of type `pkl.base#Map` does not have a key named `non-existing`.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void navigateToNonExistingLeafChild() {
|
||||
var age = pigeonConfig.get("pigeon").get("age");
|
||||
var t = catchThrowable(() -> age.get("non-existing"));
|
||||
|
||||
assertThat(t)
|
||||
.isInstanceOf(NoSuchChildException.class)
|
||||
.hasMessageStartingWith(
|
||||
"Leaf node `pigeon.age` of type `pkl.base#Int` does not have a child named `non-existing`.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertObjectToPojoByType() {
|
||||
Person pigeon = pigeonConfig.get("pigeon").as(Person.class);
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertObjectToPojoByJavaType() {
|
||||
var pigeon = pigeonConfig.get("pigeon").as(JavaType.of(Person.class));
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertModuleToPojoByType() {
|
||||
var pigeon = pigeonModuleConfig.as(Person.class);
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertModuleToPojoByJavaType() {
|
||||
var pigeon = pigeonModuleConfig.as(JavaType.of(Person.class));
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
private void checkPigeon(Person pigeon) {
|
||||
assertThat(pigeon).isNotNull();
|
||||
assertThat(pigeon.age).isEqualTo(30);
|
||||
assertThat(pigeon.friends).containsExactly("john", "mary");
|
||||
assertThat(pigeon.address.street).isEqualTo("Fuzzy St.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToParameterizedTypeByType() {
|
||||
Pair<Path, Integer> pair =
|
||||
pairConfig.get("x").as(Types.parameterizedType(Pair.class, Path.class, Integer.class));
|
||||
checkPair(pair);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToParameterizedTypeByJavaType() {
|
||||
var pair = pairConfig.get("x").as(new JavaType<Pair<Path, Integer>>() {});
|
||||
checkPair(pair);
|
||||
}
|
||||
|
||||
private void checkPair(Pair<?, ?> pair) {
|
||||
assertThat(pair).isNotNull();
|
||||
assertThat(pair.first).isEqualTo(Path.of("file/path"));
|
||||
assertThat(pair.second).isEqualTo(42);
|
||||
}
|
||||
|
||||
public static class Person {
|
||||
final int age;
|
||||
final List<String> friends;
|
||||
final Address address;
|
||||
|
||||
public Person(
|
||||
@Named("age") int age,
|
||||
@Named("friends") List<String> friends,
|
||||
@Named("address") Address address) {
|
||||
this.age = age;
|
||||
this.friends = friends;
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Address {
|
||||
final String street;
|
||||
|
||||
public Address(@Named("street") String street) {
|
||||
this.street = street;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pair<S, T> {
|
||||
final S first;
|
||||
final T second;
|
||||
|
||||
public Pair(@Named("first") S first, @Named("second") T second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.config.java;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class ConfigPklBinaryDecoderTest extends AbstractConfigTest {
|
||||
// generate via: pbpaste | ./pkl-cli/build/executable/jpkl eval /dev/stdin -f pkl-binary | base64
|
||||
|
||||
@Override
|
||||
protected Config getPigeonConfig() {
|
||||
// pigeon { age = 30; friends = List("john", "mary"); address { street = "Fuzzy St." } }
|
||||
return Config.from(
|
||||
Base64.getDecoder()
|
||||
.decode(
|
||||
"lAGkdGVzdNklZmlsZTovLy9Vc2Vycy9qYmFzY2gvc3JjL3BrbC90ZXN0LnBrbJGTEKZwaWdlb26UAadEeW5hbWljqHBrbDpiYXNlk5MQo2FnZR6TEKdmcmllbmRzkgSSpGpvaG6kbWFyeZMQp2FkZHJlc3OUAadEeW5hbWljqHBrbDpiYXNlkZMQpnN0cmVldKlGdXp6eSBTdC4="));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Config getPigeonModuleConfig() {
|
||||
// age = 30; friends = List("john", "mary"); address { street = "Fuzzy St." }
|
||||
return Config.from(
|
||||
Base64.getDecoder()
|
||||
.decode(
|
||||
"lAGlc3RkaW6xZmlsZTovLy9kZXYvc3RkaW6TkxCjYWdlHpMQp2ZyaWVuZHOSBJKkam9obqRtYXJ5kxCnYWRkcmVzc5QBp0R5bmFtaWOocGtsOmJhc2WRkxCmc3RyZWV0qUZ1enp5IFN0Lg=="));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Config getPairConfig() {
|
||||
// x { first = "file/path"; second = 42 }
|
||||
return Config.from(
|
||||
Base64.getDecoder()
|
||||
.decode(
|
||||
"lAGlc3RkaW6xZmlsZTovLy9kZXYvc3RkaW6RkxCheJQBp0R5bmFtaWOocGtsOmJhc2WSkxClZmlyc3SpZmlsZS9wYXRokxCmc2Vjb25kKg=="));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Config getMapConfig() {
|
||||
// x = Map("one", 1, "two", 2)
|
||||
return Config.from(
|
||||
Base64.getDecoder().decode("lAGlc3RkaW6xZmlsZTovLy9kZXYvc3RkaW6RkxCheJICgqNvbmUBo3R3bwI="));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 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.
|
||||
@@ -15,164 +15,31 @@
|
||||
*/
|
||||
package org.pkl.config.java;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.pkl.core.ModuleSource.text;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pkl.config.java.mapper.Named;
|
||||
import org.pkl.config.java.mapper.Types;
|
||||
import org.pkl.core.PObject;
|
||||
public class ConfigTest extends AbstractConfigTest {
|
||||
private static final ConfigEvaluator evaluator = ConfigEvaluator.preconfigured();
|
||||
|
||||
public class ConfigTest {
|
||||
private final ConfigEvaluator evaluator = ConfigEvaluator.preconfigured();
|
||||
|
||||
private final Config pigeonConfig =
|
||||
evaluator.evaluate(
|
||||
text(
|
||||
"pigeon { age = 30; friends = List(\"john\", \"mary\"); address { street = \"Fuzzy St.\" } }"));
|
||||
|
||||
private final Config pigeonModuleConfig =
|
||||
evaluator.evaluate(
|
||||
text("age = 30; friends = List(\"john\", \"mary\"); address { street = \"Fuzzy St.\" }"));
|
||||
|
||||
private final Config pairConfig =
|
||||
evaluator.evaluate(text("x { first = \"file/path\"; second = 42 }"));
|
||||
|
||||
private final Config mapConfig = evaluator.evaluate(text("x = Map(\"one\", 1, \"two\", 2)"));
|
||||
|
||||
@Test
|
||||
public void navigate() {
|
||||
var pigeon = pigeonConfig.get("pigeon");
|
||||
assertThat(pigeon.getQualifiedName()).isEqualTo("pigeon");
|
||||
assertThat(pigeon.getRawValue()).isInstanceOf(PObject.class);
|
||||
|
||||
var address = pigeon.get("address");
|
||||
assertThat(address.getQualifiedName()).isEqualTo("pigeon.address");
|
||||
assertThat(address.getRawValue()).isInstanceOf(PObject.class);
|
||||
|
||||
var street = address.get("street");
|
||||
assertThat(street.getQualifiedName()).isEqualTo("pigeon.address.street");
|
||||
assertThat(street.getRawValue()).isInstanceOf(String.class);
|
||||
|
||||
assertThat(street.as(String.class)).isEqualTo("Fuzzy St.");
|
||||
@Override
|
||||
protected Config getPigeonConfig() {
|
||||
return evaluator.evaluate(
|
||||
text(
|
||||
"pigeon { age = 30; friends = List(\"john\", \"mary\"); address { street = \"Fuzzy St.\" } }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void navigateToNonExistingObjectChild() {
|
||||
var pigeon = pigeonConfig.get("pigeon");
|
||||
var t = catchThrowable(() -> pigeon.get("non-existing"));
|
||||
|
||||
assertThat(t)
|
||||
.isInstanceOf(NoSuchChildException.class)
|
||||
.hasMessageStartingWith(
|
||||
"Node `pigeon` of type `pkl.base#Dynamic` "
|
||||
+ "does not have a property named `non-existing`.");
|
||||
@Override
|
||||
protected Config getPigeonModuleConfig() {
|
||||
return evaluator.evaluate(
|
||||
text("age = 30; friends = List(\"john\", \"mary\"); address { street = \"Fuzzy St.\" }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void navigateToNonExistingMapChild() {
|
||||
var map = mapConfig.get("x");
|
||||
var t = catchThrowable(() -> map.get("non-existing"));
|
||||
|
||||
assertThat(t)
|
||||
.isInstanceOf(NoSuchChildException.class)
|
||||
.hasMessageStartingWith(
|
||||
"Node `x` of type `pkl.base#Map` does not have a key named `non-existing`.");
|
||||
@Override
|
||||
protected Config getPairConfig() {
|
||||
return evaluator.evaluate(text("x { first = \"file/path\"; second = 42 }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void navigateToNonExistingLeafChild() {
|
||||
var age = pigeonConfig.get("pigeon").get("age");
|
||||
var t = catchThrowable(() -> age.get("non-existing"));
|
||||
|
||||
assertThat(t)
|
||||
.isInstanceOf(NoSuchChildException.class)
|
||||
.hasMessageStartingWith(
|
||||
"Leaf node `pigeon.age` of type `pkl.base#Int` does not have a child named `non-existing`.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertObjectToPojoByType() {
|
||||
Person pigeon = pigeonConfig.get("pigeon").as(Person.class);
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertObjectToPojoByJavaType() {
|
||||
var pigeon = pigeonConfig.get("pigeon").as(JavaType.of(Person.class));
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertModuleToPojoByType() {
|
||||
var pigeon = pigeonModuleConfig.as(Person.class);
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertModuleToPojoByJavaType() {
|
||||
var pigeon = pigeonModuleConfig.as(JavaType.of(Person.class));
|
||||
checkPigeon(pigeon);
|
||||
}
|
||||
|
||||
private void checkPigeon(Person pigeon) {
|
||||
assertThat(pigeon).isNotNull();
|
||||
assertThat(pigeon.age).isEqualTo(30);
|
||||
assertThat(pigeon.friends).containsExactly("john", "mary");
|
||||
assertThat(pigeon.address.street).isEqualTo("Fuzzy St.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToParameterizedTypeByType() {
|
||||
Pair<Path, Integer> pair =
|
||||
pairConfig.get("x").as(Types.parameterizedType(Pair.class, Path.class, Integer.class));
|
||||
checkPair(pair);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToParameterizedTypeByJavaType() {
|
||||
var pair = pairConfig.get("x").as(new JavaType<Pair<Path, Integer>>() {});
|
||||
checkPair(pair);
|
||||
}
|
||||
|
||||
private void checkPair(Pair<?, ?> pair) {
|
||||
assertThat(pair).isNotNull();
|
||||
assertThat(pair.first).isEqualTo(Path.of("file/path"));
|
||||
assertThat(pair.second).isEqualTo(42);
|
||||
}
|
||||
|
||||
public static class Person {
|
||||
final int age;
|
||||
final List<String> friends;
|
||||
final Address address;
|
||||
|
||||
public Person(
|
||||
@Named("age") int age,
|
||||
@Named("friends") List<String> friends,
|
||||
@Named("address") Address address) {
|
||||
this.age = age;
|
||||
this.friends = friends;
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Address {
|
||||
final String street;
|
||||
|
||||
public Address(@Named("street") String street) {
|
||||
this.street = street;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pair<S, T> {
|
||||
final S first;
|
||||
final T second;
|
||||
|
||||
public Pair(@Named("first") S first, @Named("second") T second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
@Override
|
||||
protected Config getMapConfig() {
|
||||
return evaluator.evaluate(text("x = Map(\"one\", 1, \"two\", 2)"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ org.junit.platform:junit-platform-commons:1.14.0=testCompileClasspath,testImplem
|
||||
org.junit.platform:junit-platform-engine:1.14.0=testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.14.0=testRuntimeClasspath
|
||||
org.junit:junit-bom:5.14.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.msgpack:msgpack-core:0.9.8=pklCodegenKotlin,testRuntimeClasspath
|
||||
org.msgpack:msgpack-core:0.9.8=compileClasspath,pklCodegenKotlin,pklConfigJava,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.organicdesign:Paguro:3.10.3=pklCodegenKotlin,testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.10=pklCodegenKotlin,testRuntimeClasspath
|
||||
|
||||
@@ -30,6 +30,7 @@ val buildInfo = project.extensions.getByType<BuildInfo>()
|
||||
dependencies {
|
||||
pklCodegenKotlin(projects.pklCodegenKotlin)
|
||||
implementation(libs.kotlinReflect)
|
||||
implementation(libs.msgpack)
|
||||
|
||||
// Don't declare a runtime dependency to pkl-config-java because Gradle cannot resolve
|
||||
// the correct publication (library vs fatJar) when generating the POM.
|
||||
|
||||
@@ -172,6 +172,15 @@ public interface Evaluator extends AutoCloseable {
|
||||
*/
|
||||
Object evaluateExpression(ModuleSource moduleSource, String expression);
|
||||
|
||||
/**
|
||||
* Evaluates the Pkl expression represented as {@code expression}, returning a byte array of the
|
||||
* <code>pkl-binary</code>-encoded representation of the result.
|
||||
*
|
||||
* @throws PklException if an error occurs during evaluation
|
||||
* @throws IllegalStateException if this evaluator has already been closed
|
||||
*/
|
||||
byte[] evaluateExpressionPklBinary(ModuleSource moduleSource, String expression);
|
||||
|
||||
/**
|
||||
* Evaluates the Pkl expression, returning the stringified result.
|
||||
*
|
||||
|
||||
@@ -28,6 +28,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.msgpack.core.MessageBufferPacker;
|
||||
import org.msgpack.core.MessagePack;
|
||||
import org.pkl.core.ast.ConstantValueNode;
|
||||
import org.pkl.core.ast.internal.ToStringNodeGen;
|
||||
import org.pkl.core.evaluatorSettings.TraceMode;
|
||||
@@ -48,6 +50,7 @@ import org.pkl.core.runtime.VmExceptionBuilder;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmMapping;
|
||||
import org.pkl.core.runtime.VmNull;
|
||||
import org.pkl.core.runtime.VmPklBinaryEncoder;
|
||||
import org.pkl.core.runtime.VmStackOverflowException;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
@@ -56,17 +59,18 @@ import org.pkl.core.runtime.VmValueRenderer;
|
||||
import org.pkl.core.util.ErrorMessages;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public class EvaluatorImpl implements Evaluator {
|
||||
protected final StackFrameTransformer frameTransformer;
|
||||
protected final boolean color;
|
||||
protected final ModuleResolver moduleResolver;
|
||||
protected final Context polyglotContext;
|
||||
protected final @Nullable Duration timeout;
|
||||
protected final @Nullable ScheduledExecutorService timeoutExecutor;
|
||||
protected final SecurityManager securityManager;
|
||||
protected final BufferedLogger logger;
|
||||
protected final PackageResolver packageResolver;
|
||||
public final class EvaluatorImpl implements Evaluator {
|
||||
private final StackFrameTransformer frameTransformer;
|
||||
private final boolean color;
|
||||
private final ModuleResolver moduleResolver;
|
||||
private final Context polyglotContext;
|
||||
private final @Nullable Duration timeout;
|
||||
private final @Nullable ScheduledExecutorService timeoutExecutor;
|
||||
private final SecurityManager securityManager;
|
||||
private final BufferedLogger logger;
|
||||
private final PackageResolver packageResolver;
|
||||
private final VmValueRenderer vmValueRenderer = VmValueRenderer.singleLine(1000);
|
||||
private @Nullable MessageBufferPacker messagePacker;
|
||||
|
||||
public EvaluatorImpl(
|
||||
StackFrameTransformer transformer,
|
||||
@@ -217,6 +221,37 @@ public class EvaluatorImpl implements Evaluator {
|
||||
};
|
||||
}
|
||||
|
||||
private MessageBufferPacker getMessagePacker() {
|
||||
if (messagePacker == null) {
|
||||
messagePacker = MessagePack.newDefaultBufferPacker();
|
||||
}
|
||||
messagePacker.clear();
|
||||
return messagePacker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] evaluateExpressionPklBinary(ModuleSource moduleSource, String expression) {
|
||||
return doEvaluate(
|
||||
moduleSource,
|
||||
(module) -> {
|
||||
var expressionResult =
|
||||
switch (expression) {
|
||||
case "module" -> module;
|
||||
case "output.text" -> VmUtils.readTextProperty(readModuleOutput(module));
|
||||
case "output.value" ->
|
||||
VmUtils.readMember(readModuleOutput(module), Identifier.VALUE);
|
||||
case "output.bytes" -> VmUtils.readBytesProperty(readModuleOutput(module));
|
||||
default ->
|
||||
VmUtils.evaluateExpression(module, expression, securityManager, moduleResolver);
|
||||
};
|
||||
VmValue.force(expressionResult, false);
|
||||
|
||||
var packer = getMessagePacker();
|
||||
new VmPklBinaryEncoder(packer).renderDocument(expressionResult);
|
||||
return packer.toByteArray();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String evaluateExpressionString(ModuleSource moduleSource, String expression) {
|
||||
// optimization: if the expression is `output.text` (the common case), read members
|
||||
@@ -363,7 +398,7 @@ public class EvaluatorImpl implements Evaluator {
|
||||
return evalResult;
|
||||
}
|
||||
|
||||
protected <T> T doEvaluate(ModuleSource moduleSource, Function<VmTyped, T> doEvaluate) {
|
||||
private <T> T doEvaluate(ModuleSource moduleSource, Function<VmTyped, T> doEvaluate) {
|
||||
return doEvaluate(
|
||||
() -> {
|
||||
var moduleKey = moduleResolver.resolve(moduleSource);
|
||||
|
||||
184
pkl-core/src/main/java/org/pkl/core/PklBinaryDecoder.java
Normal file
184
pkl-core/src/main/java/org/pkl/core/PklBinaryDecoder.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright © 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.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import org.msgpack.core.MessagePack;
|
||||
import org.msgpack.core.MessageUnpacker;
|
||||
import org.pkl.core.runtime.BaseModule;
|
||||
import org.pkl.core.util.CollectionUtils;
|
||||
import org.pkl.core.util.pklbinary.AbstractPklBinaryDecoder;
|
||||
|
||||
/**
|
||||
* A decoder/parser for <code>pkl-binary</code>.
|
||||
*
|
||||
* <p>For how pkl-binary turns Java, see {@link Value}.
|
||||
*/
|
||||
public class PklBinaryDecoder extends AbstractPklBinaryDecoder {
|
||||
|
||||
private PklBinaryDecoder(MessageUnpacker unpacker) {
|
||||
super(unpacker);
|
||||
}
|
||||
|
||||
/** Decode a value from the supplied byte array. */
|
||||
public static Object decode(byte[] bytes) {
|
||||
return new PklBinaryDecoder(MessagePack.newDefaultUnpacker(bytes)).decode();
|
||||
}
|
||||
|
||||
/** Decode a value from the supplied {@link InputStream}. */
|
||||
public static Object decode(InputStream inputStream) {
|
||||
return new PklBinaryDecoder(MessagePack.newDefaultUnpacker(inputStream)).decode();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RuntimeException doFail(Exception cause, long offset, List<String> path) {
|
||||
return new RuntimeException(
|
||||
String.format(
|
||||
"Exception while decoding binary data at offset %d, path [%s]",
|
||||
offset, String.join(", ", path)),
|
||||
cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RuntimeException doIOFail(IOException cause) {
|
||||
return new UncheckedIOException("IO exception during decoding", cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeNull() {
|
||||
return PNull.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeDuration(double value, DurationUnit unit) {
|
||||
return new Duration(value, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeObject(
|
||||
String className, URI moduleUri, DecodeIterator<DecodedObjectMember> iter) {
|
||||
var properties = CollectionUtils.<String, Object>newLinkedHashMap(iter.getSize());
|
||||
while (iter.hasNext()) {
|
||||
var member = iter.next();
|
||||
properties.put(member.key().toString(), member.value());
|
||||
}
|
||||
|
||||
if (moduleUri.equals(PClassInfo.pklBaseUri)) {
|
||||
// dynamic
|
||||
if (className.equals(BaseModule.getDynamicClass().getDisplayName())) {
|
||||
return new PObject(PClassInfo.Dynamic, properties);
|
||||
}
|
||||
|
||||
// pkl:base typed
|
||||
if (!className.equals(BaseModule.getModule().getVmClass().getDisplayName())) {
|
||||
return new PObject(PClassInfo.get("pkl.base", className, moduleUri), properties);
|
||||
}
|
||||
// fall through to module case
|
||||
}
|
||||
|
||||
// module
|
||||
var hashIndex = className.lastIndexOf("#");
|
||||
if (hashIndex < 0) {
|
||||
return new PModule(
|
||||
moduleUri, className, PClassInfo.get(className, "ModuleClass", moduleUri), properties);
|
||||
}
|
||||
|
||||
// non-pkl:base class
|
||||
return new PObject(
|
||||
PClassInfo.get(
|
||||
className.substring(0, hashIndex), className.substring(hashIndex + 1), moduleUri),
|
||||
properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeMap(MapDecodeIterator iter) {
|
||||
var map = CollectionUtils.newLinkedHashMap(iter.getSize());
|
||||
while (iter.hasNext()) {
|
||||
var entry = iter.next();
|
||||
map.put(entry.getFirst(), entry.getSecond());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeMapping(MapDecodeIterator iter) {
|
||||
return doDecodeMap(iter); // same exported result!
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeList(CollectionDecodeIterator iter) {
|
||||
var listing = new ArrayList<>(iter.getSize());
|
||||
while (iter.hasNext()) {
|
||||
listing.add(iter.next());
|
||||
}
|
||||
return listing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeListing(CollectionDecodeIterator iter) {
|
||||
return doDecodeList(iter); // same exported result!
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeSet(CollectionDecodeIterator iter) {
|
||||
var set = CollectionUtils.newLinkedHashSet(iter.getSize());
|
||||
while (iter.hasNext()) {
|
||||
set.add(iter.next());
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeDataSize(double value, DataSizeUnit unit) {
|
||||
return new DataSize(value, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodePair(Object first, Object second) {
|
||||
return new Pair<>(first, second);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeIntSeq(long start, long end, long step) {
|
||||
throw new DecodeException("Cannot decode IntSeq value");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeRegex(Pattern pattern) {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeClass(String qualifiedName, URI moduleUri) {
|
||||
throw new DecodeException("Cannot decode Class value");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeTypeAlias(String qualifiedName, URI moduleUri) {
|
||||
throw new DecodeException("Cannot decode TypeAlias value");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doDecodeBytes(byte[] bytes) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -53,7 +53,7 @@ public final class TypeAlias extends Member implements Value {
|
||||
|
||||
/**
|
||||
* Returns the name of the module that this type alias is declared in. Note that a module name is
|
||||
* not guaranteed to be unique, especially if it not declared but inferred from the module URI.
|
||||
* not guaranteed to be unique, especially if it is not declared but inferred from the module URI.
|
||||
*/
|
||||
public String getModuleName() {
|
||||
return moduleName;
|
||||
|
||||
@@ -143,6 +143,9 @@ public final class Identifier implements Comparable<Identifier> {
|
||||
// members of pkl.yaml
|
||||
public static final Identifier MAX_COLLECTION_ALIASES = get("maxCollectionAliases");
|
||||
|
||||
// members of pkl.encoding
|
||||
public static final Identifier IMPORTS = get("imports");
|
||||
|
||||
// common in lambdas etc
|
||||
public static final Identifier IT = get("it");
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -92,6 +92,8 @@ public final class ModuleCache {
|
||||
return BaseModule.getModule();
|
||||
case "Benchmark":
|
||||
return BenchmarkModule.getModule();
|
||||
case "pklbinary":
|
||||
return PklBinaryModule.getModule();
|
||||
case "jsonnet":
|
||||
return JsonnetModule.getModule();
|
||||
case "math":
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright © 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.core.runtime;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public class PklBinaryModule extends StdLibModule {
|
||||
private static final VmTyped instance = VmUtils.createEmptyModule();
|
||||
|
||||
static {
|
||||
loadModule(URI.create("pkl:pklbinary"), instance);
|
||||
}
|
||||
|
||||
public static VmTyped getModule() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* Copyright © 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.core.runtime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Deque;
|
||||
import org.msgpack.core.MessageBufferPacker;
|
||||
import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.pklbinary.PklBinaryCode;
|
||||
|
||||
/** An encoder/renderer for <code>pkl-binary</code></a> encoding. */
|
||||
public class VmPklBinaryEncoder extends AbstractRenderer {
|
||||
|
||||
// this type explicitly works with MessageBufferPacker:
|
||||
// * assumes no I/O during packing (in-memory writes only)
|
||||
// * IOExceptions are caught and assumed unreachable
|
||||
|
||||
private final MessageBufferPacker packer;
|
||||
|
||||
public VmPklBinaryEncoder(MessageBufferPacker packer, PklConverter converter) {
|
||||
super("pkl-binary", converter, false, false);
|
||||
this.packer = packer;
|
||||
}
|
||||
|
||||
public VmPklBinaryEncoder(MessageBufferPacker packer) {
|
||||
this(packer, new PklConverter(VmMapping.empty()));
|
||||
}
|
||||
|
||||
private void packCode(PklBinaryCode code) throws IOException {
|
||||
packer.packByte(code.getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitString(String value) {
|
||||
try {
|
||||
packer.packString(value);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBoolean(Boolean value) {
|
||||
try {
|
||||
packer.packBoolean(value);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInt(Long value) {
|
||||
try {
|
||||
packer.packLong(value);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFloat(Double value) {
|
||||
try {
|
||||
packer.packDouble(value);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDuration(VmDuration value) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.DURATION);
|
||||
packer.packDouble(value.getValue());
|
||||
packer.packString(value.getUnit().toString());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDataSize(VmDataSize value) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.DATASIZE);
|
||||
packer.packDouble(value.getValue());
|
||||
packer.packString(value.getUnit().toString());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBytes(VmBytes value) {
|
||||
try {
|
||||
packer.packArrayHeader(2);
|
||||
packCode(PklBinaryCode.BYTES);
|
||||
packer.packBinaryHeader(value.getBytes().length);
|
||||
packer.addPayload(value.getBytes());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIntSeq(VmIntSeq value) {
|
||||
try {
|
||||
packer.packArrayHeader(4);
|
||||
packCode(PklBinaryCode.INTSEQ);
|
||||
packer.packLong(value.start);
|
||||
packer.packLong(value.end);
|
||||
packer.packLong(value.step);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitDocument(Object value) {
|
||||
visit(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitTopLevelValue(Object value) {
|
||||
visit(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitRenderDirective(VmTyped value) {
|
||||
try {
|
||||
packer.writePayload(VmUtils.readBytesProperty(value).getBytes());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startDynamic(VmDynamic value) {
|
||||
startObject(value, value.getRegularMemberCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startTyped(VmTyped value) {
|
||||
startObject(value, value.getVmClass().getAllRegularPropertyNames().size());
|
||||
}
|
||||
|
||||
private void startObject(VmObjectLike value, int memberCount) {
|
||||
try {
|
||||
packer.packArrayHeader(4);
|
||||
packCode(PklBinaryCode.OBJECT);
|
||||
packer.packString(value.getVmClass().getDisplayName());
|
||||
packer.packString(
|
||||
value.getVmClass().getModule().getModuleInfo().getModuleKey().getUri().toString());
|
||||
packer.packArrayHeader(memberCount);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
private void startList(PklBinaryCode code, int length) {
|
||||
try {
|
||||
packer.packArrayHeader(2);
|
||||
packCode(code);
|
||||
packer.packArrayHeader(length);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
private void startMap(PklBinaryCode code, int length) {
|
||||
try {
|
||||
packer.packArrayHeader(2);
|
||||
packCode(code);
|
||||
packer.packMapHeader(length);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startListing(VmListing value) {
|
||||
startList(PklBinaryCode.LISTING, value.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startMapping(VmMapping value) {
|
||||
startMap(PklBinaryCode.MAPPING, (int) value.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startList(VmList value) {
|
||||
startList(PklBinaryCode.LIST, value.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startSet(VmSet value) {
|
||||
startList(PklBinaryCode.SET, value.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startMap(VmMap value) {
|
||||
startMap(PklBinaryCode.MAP, value.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitEntryKeyValue(
|
||||
Object key, boolean isFirst, Deque<Object> valuePath, Object value) {
|
||||
if (enclosingValue instanceof VmDynamic) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.ENTRY);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
super.visitEntryKeyValue(key, isFirst, valuePath, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitElement(long index, Object value, boolean isFirst) {
|
||||
if (enclosingValue instanceof VmDynamic) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.ELEMENT);
|
||||
packer.packLong(index);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
visit(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitProperty(Identifier name, Object value, boolean isFirst) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.PROPERTY);
|
||||
packer.packString(name.toString());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
visit(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(VmClass value) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.CLASS);
|
||||
packer.packString(value.getDisplayName());
|
||||
packer.packString(value.getModule().getModuleInfo().getModuleKey().getUri().toString());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeAlias(VmTypeAlias value) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.TYPEALIAS);
|
||||
packer.packString(value.getDisplayName());
|
||||
packer.packString(value.getModuleUri().toString());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPair(VmPair value) {
|
||||
try {
|
||||
packer.packArrayHeader(3);
|
||||
packCode(PklBinaryCode.PAIR);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
visit(value.getFirst());
|
||||
visit(value.getSecond());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRegex(VmRegex value) {
|
||||
try {
|
||||
packer.packArrayHeader(2);
|
||||
packCode(PklBinaryCode.REGEX);
|
||||
packer.packString(value.getPattern().pattern());
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNull(VmNull value) {
|
||||
try {
|
||||
packer.packNil();
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFunction(VmFunction value) {
|
||||
try {
|
||||
packer.packArrayHeader(1);
|
||||
packCode(PklBinaryCode.FUNCTION);
|
||||
} catch (IOException e) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitEntryKey(Object key, boolean isFirst) {
|
||||
visit(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitEntryValue(Object value) {
|
||||
visit(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endDynamic(VmDynamic value, boolean isEmpty) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endTyped(VmTyped value, boolean isEmpty) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endListing(VmListing value, boolean isEmpty) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endMapping(VmMapping value, boolean isEmpty) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endList(VmList value) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endSet(VmSet value) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endMap(VmMap value) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canRenderPropertyOrEntryOf(VmDynamic object) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,12 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.frame.Frame;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import org.pkl.core.Member.SourceLocation;
|
||||
import org.pkl.core.PClassInfo;
|
||||
import org.pkl.core.PObject;
|
||||
import org.pkl.core.TypeAlias;
|
||||
import org.pkl.core.TypeParameter;
|
||||
@@ -140,6 +142,10 @@ public final class VmTypeAlias extends VmValue {
|
||||
return module.getVmClass().getModuleName();
|
||||
}
|
||||
|
||||
public URI getModuleUri() {
|
||||
return module.getVmClass().getPClassInfo().getModuleUri();
|
||||
}
|
||||
|
||||
public VmTyped getModuleMirror() {
|
||||
return module.getModuleInfo().getMirror(module);
|
||||
}
|
||||
@@ -152,6 +158,12 @@ public final class VmTypeAlias extends VmValue {
|
||||
return qualifiedName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
// display `UInt` rather than `pkl.base#UInt`, etc.
|
||||
// based on PClassInfo.getDisplayName
|
||||
return getModuleUri().equals(PClassInfo.pklBaseUri) ? simpleName : qualifiedName;
|
||||
}
|
||||
|
||||
public int getTypeParameterCount() {
|
||||
return typeParameters.size();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -406,14 +406,6 @@ public final class VmUtils {
|
||||
return isCustomThisScope ? new CustomThisNode(sourceSection) : new ThisNode(sourceSection);
|
||||
}
|
||||
|
||||
public static boolean isRenderDirective(VmValue value) {
|
||||
return value.getVmClass() == BaseModule.getRenderDirectiveClass();
|
||||
}
|
||||
|
||||
public static boolean isRenderDirective(Object value) {
|
||||
return value instanceof VmTyped typed && isRenderDirective(typed);
|
||||
}
|
||||
|
||||
public static boolean isPcfRenderDirective(Object value) {
|
||||
return value instanceof VmTyped typed
|
||||
&& typed.getVmClass().getPClassInfo() == PClassInfo.PcfRenderDirective;
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import org.pkl.core.runtime.BaseModule;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmCollection;
|
||||
@@ -35,7 +36,6 @@ import org.pkl.core.runtime.VmSet;
|
||||
import org.pkl.core.runtime.VmTypeAlias;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUndefinedValueException;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.runtime.VmValue;
|
||||
import org.pkl.core.runtime.VmValueConverter;
|
||||
import org.pkl.core.runtime.VmValueVisitor;
|
||||
@@ -43,18 +43,11 @@ import org.pkl.core.util.LateInit;
|
||||
import org.pkl.core.util.MutableBoolean;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/** Base class for renderers that are part of the standard library. */
|
||||
public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
protected static final char LINE_BREAK = '\n';
|
||||
|
||||
/** The name of this renderer. */
|
||||
protected final String name;
|
||||
|
||||
protected final StringBuilder builder;
|
||||
|
||||
/** The indent to be used. */
|
||||
protected final String indent;
|
||||
|
||||
protected final PklConverter converter;
|
||||
|
||||
/** Whether to skip visiting properties with null value (after conversion). */
|
||||
@@ -65,33 +58,31 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
|
||||
@LateInit protected Deque<Object> currPath;
|
||||
|
||||
/** The value directly enclosing the currently visited value, if any. */
|
||||
protected @Nullable VmValue enclosingValue;
|
||||
|
||||
/** The value passed to either {@link #renderDocument(Object)} or {@link #renderValue(Object)}. */
|
||||
@LateInit private Object topLevelValue;
|
||||
|
||||
/** The current indent. Modified by {@link #increaseIndent()} and {@link #decreaseIndent()}. */
|
||||
protected final StringBuilder currIndent = new StringBuilder();
|
||||
/** The value directly enclosing the currently visited value, if any. */
|
||||
protected @Nullable VmValue enclosingValue;
|
||||
|
||||
/** The (closest) {@link SourceSection} of the value being visited, for better error messages. */
|
||||
protected @Nullable SourceSection currSourceSection = null;
|
||||
|
||||
public AbstractRenderer(
|
||||
String name,
|
||||
StringBuilder builder,
|
||||
String indent,
|
||||
PklConverter converter,
|
||||
boolean skipNullProperties,
|
||||
boolean skipNullEntries) {
|
||||
protected AbstractRenderer(
|
||||
String name, PklConverter converter, boolean skipNullProperties, boolean skipNullEntries) {
|
||||
this.name = name;
|
||||
this.builder = builder;
|
||||
this.indent = indent;
|
||||
this.converter = converter;
|
||||
this.skipNullProperties = skipNullProperties;
|
||||
this.skipNullEntries = skipNullEntries;
|
||||
}
|
||||
|
||||
protected boolean isRenderDirective(VmValue value) {
|
||||
return value.getVmClass() == BaseModule.getRenderDirectiveClass();
|
||||
}
|
||||
|
||||
protected boolean isRenderDirective(Object value) {
|
||||
return value instanceof VmTyped typed && isRenderDirective(typed);
|
||||
}
|
||||
|
||||
public final void renderDocument(Object value) {
|
||||
currPath = new ArrayDeque<>();
|
||||
currPath.push(VmValueConverter.TOP_LEVEL_VALUE);
|
||||
@@ -185,71 +176,22 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
|
||||
protected abstract void endMap(VmMap value);
|
||||
|
||||
private void doVisitProperty(
|
||||
Identifier name, Object value, SourceSection sourceSection, MutableBoolean isFirst) {
|
||||
var prevSourceSection = currSourceSection;
|
||||
currSourceSection = sourceSection;
|
||||
currPath.push(name);
|
||||
var convertedValue = converter.convert(value, currPath);
|
||||
if (!(skipNullProperties && convertedValue instanceof VmNull)) {
|
||||
visitProperty(name, convertedValue, isFirst.getAndSetFalse());
|
||||
}
|
||||
currPath.pop();
|
||||
currSourceSection = prevSourceSection;
|
||||
}
|
||||
|
||||
private void doVisitEntry(
|
||||
Object key, Object value, @Nullable SourceSection sourceSection, MutableBoolean isFirst) {
|
||||
var prevSourceSection = currSourceSection;
|
||||
if (sourceSection != null) {
|
||||
currSourceSection = sourceSection;
|
||||
}
|
||||
var valuePath = currPath;
|
||||
try {
|
||||
var convertedKey = converter.convert(key, List.of());
|
||||
valuePath.push(convertedKey);
|
||||
var convertedValue = converter.convert(value, valuePath);
|
||||
if (skipNullEntries && (convertedValue instanceof VmNull)) {
|
||||
return;
|
||||
}
|
||||
|
||||
currPath = new ArrayDeque<>();
|
||||
visitEntryKey(convertedKey, isFirst.getAndSetFalse());
|
||||
currPath = valuePath;
|
||||
visitEntryValue(convertedValue);
|
||||
} finally {
|
||||
valuePath.pop();
|
||||
currSourceSection = prevSourceSection;
|
||||
}
|
||||
}
|
||||
|
||||
private void doVisitElement(
|
||||
long index, Object value, @Nullable SourceSection sourceSection, boolean isFirst) {
|
||||
var prevSourceSection = currSourceSection;
|
||||
if (sourceSection != null) {
|
||||
currSourceSection = sourceSection;
|
||||
}
|
||||
currPath.push(index);
|
||||
var convertedValue = converter.convert(value, currPath);
|
||||
visitElement(index, convertedValue, isFirst);
|
||||
currPath.pop();
|
||||
currSourceSection = prevSourceSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTyped(VmTyped value) {
|
||||
// value.getParent().getMember(value);
|
||||
if (VmUtils.isRenderDirective(value)) {
|
||||
if (isRenderDirective(value)) {
|
||||
visitRenderDirective(value);
|
||||
return;
|
||||
}
|
||||
|
||||
value.force(false, false);
|
||||
startTyped(value);
|
||||
|
||||
var prevEnclosingValue = enclosingValue;
|
||||
enclosingValue = value;
|
||||
var isFirst = new MutableBoolean(true);
|
||||
|
||||
value.forceAndIterateMemberValues(
|
||||
value.iterateAlreadyForcedMemberValues(
|
||||
(memberKey, member, memberValue) -> {
|
||||
if (member.isClass() || member.isTypeAlias()) return true;
|
||||
assert member.isProp();
|
||||
@@ -263,6 +205,7 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
|
||||
@Override
|
||||
public final void visitDynamic(VmDynamic value) {
|
||||
value.force(false, false);
|
||||
startDynamic(value);
|
||||
|
||||
var prevEnclosingValue = enclosingValue;
|
||||
@@ -270,7 +213,7 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
var isFirst = new MutableBoolean(true);
|
||||
var canRenderPropertyOrEntry = canRenderPropertyOrEntryOf(value);
|
||||
|
||||
value.forceAndIterateMemberValues(
|
||||
value.iterateAlreadyForcedMemberValues(
|
||||
(memberKey, member, memberValue) -> {
|
||||
var sourceSection = member.getSourceSection();
|
||||
if (member.isProp()) {
|
||||
@@ -302,12 +245,14 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
|
||||
@Override
|
||||
public final void visitListing(VmListing value) {
|
||||
value.force(false, false);
|
||||
startListing(value);
|
||||
|
||||
var prevEnclosingValue = enclosingValue;
|
||||
enclosingValue = value;
|
||||
var isFirst = new MutableBoolean(true);
|
||||
|
||||
value.forceAndIterateMemberValues(
|
||||
value.iterateAlreadyForcedMemberValues(
|
||||
(memberKey, member, memberValue) -> {
|
||||
assert member.isElement();
|
||||
doVisitElement(
|
||||
@@ -321,12 +266,14 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
|
||||
@Override
|
||||
public final void visitMapping(VmMapping value) {
|
||||
value.force(false, false);
|
||||
startMapping(value);
|
||||
|
||||
var prevEnclosingValue = enclosingValue;
|
||||
enclosingValue = value;
|
||||
var isFirst = new MutableBoolean(true);
|
||||
|
||||
value.forceAndIterateMemberValues(
|
||||
value.iterateAlreadyForcedMemberValues(
|
||||
(memberKey, member, memberValue) -> {
|
||||
assert member.isEntry();
|
||||
doVisitEntry(memberKey, memberValue, member.getSourceSection(), isFirst);
|
||||
@@ -379,19 +326,60 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
endMap(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void visitTypeAlias(VmTypeAlias value) {
|
||||
cannotRenderTypeAddConverter(value);
|
||||
private void doVisitProperty(
|
||||
Identifier name, Object value, SourceSection sourceSection, MutableBoolean isFirst) {
|
||||
var prevSourceSection = currSourceSection;
|
||||
currSourceSection = sourceSection;
|
||||
currPath.push(name);
|
||||
var convertedValue = converter.convert(value, currPath);
|
||||
if (!(skipNullProperties && convertedValue instanceof VmNull)) {
|
||||
visitProperty(name, convertedValue, isFirst.getAndSetFalse());
|
||||
}
|
||||
currPath.pop();
|
||||
currSourceSection = prevSourceSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void visitClass(VmClass value) {
|
||||
cannotRenderTypeAddConverter(value);
|
||||
private void doVisitEntry(
|
||||
Object key, Object value, @Nullable SourceSection sourceSection, MutableBoolean isFirst) {
|
||||
var prevSourceSection = currSourceSection;
|
||||
if (sourceSection != null) {
|
||||
currSourceSection = sourceSection;
|
||||
}
|
||||
var valuePath = currPath;
|
||||
try {
|
||||
var convertedKey = converter.convert(key, List.of());
|
||||
valuePath.push(convertedKey);
|
||||
var convertedValue = converter.convert(value, valuePath);
|
||||
if (skipNullEntries && (convertedValue instanceof VmNull)) {
|
||||
return;
|
||||
}
|
||||
|
||||
visitEntryKeyValue(convertedKey, isFirst.getAndSetFalse(), valuePath, convertedValue);
|
||||
} finally {
|
||||
valuePath.pop();
|
||||
currSourceSection = prevSourceSection;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void visitFunction(VmFunction value) {
|
||||
cannotRenderTypeAddConverter(value);
|
||||
protected void visitEntryKeyValue(
|
||||
Object key, boolean isFirst, Deque<Object> valuePath, Object value) {
|
||||
currPath = new ArrayDeque<>();
|
||||
visitEntryKey(key, isFirst);
|
||||
currPath = valuePath;
|
||||
visitEntryValue(value);
|
||||
}
|
||||
|
||||
private void doVisitElement(
|
||||
long index, Object value, @Nullable SourceSection sourceSection, boolean isFirst) {
|
||||
var prevSourceSection = currSourceSection;
|
||||
if (sourceSection != null) {
|
||||
currSourceSection = sourceSection;
|
||||
}
|
||||
currPath.push(index);
|
||||
var convertedValue = converter.convert(value, currPath);
|
||||
visitElement(index, convertedValue, isFirst);
|
||||
currPath.pop();
|
||||
currSourceSection = prevSourceSection;
|
||||
}
|
||||
|
||||
protected void cannotRenderTypeAddConverter(VmValue value) {
|
||||
@@ -405,6 +393,21 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
throw builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeAlias(VmTypeAlias value) {
|
||||
cannotRenderTypeAddConverter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(VmClass value) {
|
||||
cannotRenderTypeAddConverter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFunction(VmFunction value) {
|
||||
cannotRenderTypeAddConverter(value);
|
||||
}
|
||||
|
||||
protected void cannotRenderNonStringKey(Object key) {
|
||||
assert enclosingValue != null;
|
||||
var isMap = enclosingValue instanceof VmMap;
|
||||
@@ -424,12 +427,4 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
||||
.withProgramValue("Key", key)
|
||||
.build();
|
||||
}
|
||||
|
||||
protected void increaseIndent() {
|
||||
currIndent.append(indent);
|
||||
}
|
||||
|
||||
protected void decreaseIndent() {
|
||||
currIndent.setLength(currIndent.length() - indent.length());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.core.stdlib;
|
||||
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
import org.pkl.core.runtime.VmTypeAlias;
|
||||
|
||||
/** Base class for renderers that are part of the standard library. */
|
||||
public abstract class AbstractStringRenderer extends AbstractRenderer {
|
||||
protected static final char LINE_BREAK = '\n';
|
||||
|
||||
protected final StringBuilder builder;
|
||||
|
||||
/** The indent to be used. */
|
||||
protected final String indent;
|
||||
|
||||
/** The current indent. Modified by {@link #increaseIndent()} and {@link #decreaseIndent()}. */
|
||||
protected final StringBuilder currIndent = new StringBuilder();
|
||||
|
||||
public AbstractStringRenderer(
|
||||
String name,
|
||||
StringBuilder builder,
|
||||
String indent,
|
||||
PklConverter converter,
|
||||
boolean skipNullProperties,
|
||||
boolean skipNullEntries) {
|
||||
super(name, converter, skipNullProperties, skipNullEntries);
|
||||
this.builder = builder;
|
||||
this.indent = indent;
|
||||
}
|
||||
|
||||
protected void increaseIndent() {
|
||||
currIndent.append(indent);
|
||||
}
|
||||
|
||||
protected void decreaseIndent() {
|
||||
currIndent.setLength(currIndent.length() - indent.length());
|
||||
}
|
||||
|
||||
// override these to mark them final
|
||||
|
||||
@Override
|
||||
public final void visitTypeAlias(VmTypeAlias value) {
|
||||
super.visitTypeAlias(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void visitClass(VmClass value) {
|
||||
super.visitClass(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void visitFunction(VmFunction value) {
|
||||
super.visitFunction(value);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ package org.pkl.core.stdlib.base;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.json.JsonEscaper;
|
||||
@@ -57,7 +57,7 @@ public final class JsonRendererNodes {
|
||||
return new JsonRenderer(builder, indent, converter, omitNullProperties);
|
||||
}
|
||||
|
||||
private static final class JsonRenderer extends AbstractRenderer {
|
||||
private static final class JsonRenderer extends AbstractStringRenderer {
|
||||
private final String separator;
|
||||
|
||||
private final JsonEscaper escaper = new JsonEscaper(false);
|
||||
@@ -212,7 +212,7 @@ public final class JsonRendererNodes {
|
||||
return;
|
||||
}
|
||||
|
||||
if (VmUtils.isRenderDirective(key)) {
|
||||
if (isRenderDirective(key)) {
|
||||
visitRenderDirective((VmTyped) key);
|
||||
builder.append(separator);
|
||||
return;
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.pkl.core.stdlib.base;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.ArrayCharEscaper;
|
||||
@@ -54,7 +54,7 @@ public final class PListRendererNodes {
|
||||
}
|
||||
|
||||
// keep in sync with org.pkl.core.PListRenderer
|
||||
private static final class PListRenderer extends AbstractRenderer {
|
||||
private static final class PListRenderer extends AbstractStringRenderer {
|
||||
|
||||
// it's safe (though not required) to escape all the following characters in XML text nodes
|
||||
private static final ArrayCharEscaper charEscaper =
|
||||
@@ -241,7 +241,7 @@ public final class PListRendererNodes {
|
||||
builder.append("<dict>").append(LINE_BREAK);
|
||||
}
|
||||
|
||||
if (VmUtils.isRenderDirective(key)) {
|
||||
if (isRenderDirective(key)) {
|
||||
key = VmUtils.readTextProperty(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,12 +34,12 @@ import org.pkl.core.runtime.VmRegex;
|
||||
import org.pkl.core.runtime.VmSet;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.LateInit;
|
||||
import org.pkl.parser.Lexer;
|
||||
|
||||
public final class PcfRenderer extends AbstractRenderer {
|
||||
public final class PcfRenderer extends AbstractStringRenderer {
|
||||
private final ValueFormatter valueFormatter;
|
||||
|
||||
private boolean isDocument;
|
||||
@@ -227,7 +227,7 @@ public final class PcfRenderer extends AbstractRenderer {
|
||||
}
|
||||
|
||||
private void visitStandaloneValue(Object value) {
|
||||
if (value instanceof VmObjectLike && !VmUtils.isRenderDirective(value)) {
|
||||
if (value instanceof VmObjectLike && !isRenderDirective(value)) {
|
||||
builder.append("new ");
|
||||
}
|
||||
visit(value);
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.runtime.VmValue;
|
||||
import org.pkl.core.runtime.VmValueConverter;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
@@ -74,7 +74,7 @@ public final class PropertiesRendererNodes {
|
||||
return new PropertiesRenderer(builder, omitNullProperties, restrictCharset, PklConverter);
|
||||
}
|
||||
|
||||
private static final class PropertiesRenderer extends AbstractRenderer {
|
||||
private static final class PropertiesRenderer extends AbstractStringRenderer {
|
||||
private final boolean restrictCharset;
|
||||
|
||||
private boolean isDocument;
|
||||
@@ -158,7 +158,7 @@ public final class PropertiesRendererNodes {
|
||||
.withProgramValue("Value", value)
|
||||
.build();
|
||||
}
|
||||
if (!VmUtils.isRenderDirective(value)) {
|
||||
if (!isRenderDirective(value)) {
|
||||
isDocument = true;
|
||||
}
|
||||
visit(value);
|
||||
@@ -170,7 +170,7 @@ public final class PropertiesRendererNodes {
|
||||
|| value instanceof VmTyped
|
||||
|| value instanceof VmMapping
|
||||
|| value instanceof VmDynamic)
|
||||
&& !VmUtils.isRenderDirective(value)) {
|
||||
&& !isRenderDirective(value)) {
|
||||
cannotRenderTypeAddConverter((VmValue) value);
|
||||
}
|
||||
isDocument = false;
|
||||
@@ -303,7 +303,7 @@ public final class PropertiesRendererNodes {
|
||||
if (isFollowing.get()) {
|
||||
builder.append('.');
|
||||
}
|
||||
if (VmUtils.isRenderDirective(path)) {
|
||||
if (isRenderDirective(path)) {
|
||||
builder.append(VmUtils.readTextProperty(path));
|
||||
} else {
|
||||
builder.append(
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.pkl.core.runtime.VmRegex;
|
||||
import org.pkl.core.runtime.VmSet;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.MutableBoolean;
|
||||
@@ -75,7 +75,7 @@ public final class YamlRendererNodes {
|
||||
builder, " ".repeat(indentWidth), converter, omitNullProperties, mode, isStream);
|
||||
}
|
||||
|
||||
private static final class YamlRenderer extends AbstractRenderer {
|
||||
private static final class YamlRenderer extends AbstractStringRenderer {
|
||||
private final boolean isStream;
|
||||
private final YamlEmitter emitter;
|
||||
private final String elementIndent;
|
||||
@@ -306,7 +306,7 @@ public final class YamlRendererNodes {
|
||||
return;
|
||||
}
|
||||
|
||||
if (VmUtils.isRenderDirective(key)) {
|
||||
if (isRenderDirective(key)) {
|
||||
visitRenderDirective((VmTyped) key);
|
||||
builder.append(':');
|
||||
return;
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.pkl.core.runtime.VmRegex;
|
||||
import org.pkl.core.runtime.VmSet;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.ArrayCharEscaper;
|
||||
@@ -72,7 +72,7 @@ public final class RendererNodes {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Renderer extends AbstractRenderer {
|
||||
private static final class Renderer extends AbstractStringRenderer {
|
||||
// Pattern for object fields that we can render without any quotes.
|
||||
// From: https://jsonnet.org/ref/spec.html#lexing
|
||||
private static final Pattern ID_PATTERN = Pattern.compile("[_a-zA-Z][_a-zA-Z0-9]*");
|
||||
@@ -288,7 +288,7 @@ public final class RendererNodes {
|
||||
builder.append(memberSeparator).append(currIndent);
|
||||
if (key instanceof String string) {
|
||||
renderAsFieldName(string);
|
||||
} else if (VmUtils.isRenderDirective(key)) {
|
||||
} else if (isRenderDirective(key)) {
|
||||
visitRenderDirective((VmTyped) key);
|
||||
builder.append(": ");
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright © 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.core.stdlib.pklbinary;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import org.msgpack.core.MessageBufferPacker;
|
||||
import org.msgpack.core.MessagePack;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmBytes;
|
||||
import org.pkl.core.runtime.VmMapping;
|
||||
import org.pkl.core.runtime.VmPklBinaryEncoder;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public final class RendererNodes {
|
||||
|
||||
private abstract static class RenderMethod extends ExternalMethod1Node {
|
||||
private @Nullable MessageBufferPacker messagePacker;
|
||||
|
||||
protected MessageBufferPacker getMessagePacker() {
|
||||
if (messagePacker == null) {
|
||||
messagePacker = MessagePack.newDefaultBufferPacker();
|
||||
}
|
||||
messagePacker.clear();
|
||||
return messagePacker;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class renderDocument extends RenderMethod {
|
||||
@Specialization
|
||||
@TruffleBoundary
|
||||
protected VmBytes eval(VmTyped self, Object value) {
|
||||
var packer = getMessagePacker();
|
||||
createRenderer(self, packer).renderDocument(value);
|
||||
return new VmBytes(packer.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class renderValue extends RenderMethod {
|
||||
@Specialization
|
||||
@TruffleBoundary
|
||||
protected VmBytes eval(VmTyped self, Object value) {
|
||||
var packer = getMessagePacker();
|
||||
createRenderer(self, packer).renderValue(value);
|
||||
return new VmBytes(packer.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
private static VmPklBinaryEncoder createRenderer(VmTyped self, MessageBufferPacker packer) {
|
||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
||||
var converter = new PklConverter(converters);
|
||||
return new VmPklBinaryEncoder(packer, converter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NonnullByDefault
|
||||
package org.pkl.core.stdlib.pklbinary;
|
||||
|
||||
import org.pkl.core.util.NonnullByDefault;
|
||||
@@ -63,7 +63,7 @@ import org.pkl.core.runtime.VmSet;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.runtime.VmValue;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.ArrayCharEscaper;
|
||||
@@ -140,7 +140,7 @@ public final class RendererNodes {
|
||||
return new ProtobufRenderer(builder, indent, new PklConverter(converters));
|
||||
}
|
||||
|
||||
private static final class ProtobufRenderer extends AbstractRenderer {
|
||||
private static final class ProtobufRenderer extends AbstractStringRenderer {
|
||||
private final Deque<Identifier> propertyPath = new ArrayDeque<>();
|
||||
private final Deque<Boolean> wrapperRequirement = new ArrayDeque<>();
|
||||
private final JsonEscaper jsonEscaper = new JsonEscaper(false);
|
||||
@@ -306,7 +306,7 @@ public final class RendererNodes {
|
||||
|
||||
@Override
|
||||
protected void visitEntryKey(Object key, boolean isFirst) {
|
||||
var isDirective = VmUtils.isRenderDirective(key);
|
||||
var isDirective = isRenderDirective(key);
|
||||
var isValidKey =
|
||||
isDirective || key instanceof Long || key instanceof Boolean || key instanceof String;
|
||||
if (!isValidKey) {
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.pkl.core.stdlib.xml;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.stdlib.AbstractRenderer;
|
||||
import org.pkl.core.stdlib.AbstractStringRenderer;
|
||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklConverter;
|
||||
import org.pkl.core.util.ArrayCharEscaper;
|
||||
@@ -60,7 +60,7 @@ public final class RendererNodes {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Renderer extends AbstractRenderer {
|
||||
public static final class Renderer extends AbstractStringRenderer {
|
||||
// it's safe (though not required) to escape all the following characters in text nodes and
|
||||
// attribute values
|
||||
private static final ArrayCharEscaper stringEscaper =
|
||||
@@ -266,7 +266,7 @@ public final class RendererNodes {
|
||||
renderXmlInline((VmTyped) value);
|
||||
} else if (isContent(value)) {
|
||||
visit(value);
|
||||
} else if (VmUtils.isRenderDirective(value)) {
|
||||
} else if (isRenderDirective(value)) {
|
||||
builder.append(VmUtils.readTextProperty(value));
|
||||
} else {
|
||||
writeXmlElement(VmUtils.getClass(value).getSimpleName(), null, value, true, true);
|
||||
@@ -293,7 +293,7 @@ public final class RendererNodes {
|
||||
} else {
|
||||
assert deferredKey != null;
|
||||
assert enclosingValue != null;
|
||||
if (VmUtils.isRenderDirective(deferredKey)) {
|
||||
if (isRenderDirective(deferredKey)) {
|
||||
writeXmlElement(VmUtils.readTextProperty(deferredKey), null, value, true, false);
|
||||
} else if (deferredKey instanceof String string) {
|
||||
writeXmlElement(string, null, value, true, true);
|
||||
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* Copyright © 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.core.util.pklbinary;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Formatter;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import org.msgpack.core.MessageInsufficientBufferException;
|
||||
import org.msgpack.core.MessagePackException;
|
||||
import org.msgpack.core.MessageUnpacker;
|
||||
import org.pkl.core.DataSizeUnit;
|
||||
import org.pkl.core.DurationUnit;
|
||||
import org.pkl.core.Pair;
|
||||
import org.pkl.core.util.LateInit;
|
||||
|
||||
/**
|
||||
* Base class for implementing a decoder/parser for the <a
|
||||
* href="https://pkl-lang.org/main/current/bindings-specification/binary-encoding.html"><code>
|
||||
* pkl-binary</code></a> encoding.
|
||||
*/
|
||||
public abstract class AbstractPklBinaryDecoder {
|
||||
private final MessageUnpacker unpacker;
|
||||
@LateInit protected Deque<Object> currPath;
|
||||
|
||||
protected AbstractPklBinaryDecoder(MessageUnpacker unpacker) {
|
||||
this.unpacker = unpacker;
|
||||
}
|
||||
|
||||
protected static class DecodeException extends RuntimeException {
|
||||
public DecodeException(String msg, Object... args) {
|
||||
super(new Formatter().format(msg, args).toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected final Object decode() {
|
||||
currPath = new ArrayDeque<>();
|
||||
try {
|
||||
try {
|
||||
return doDecode();
|
||||
} catch (MessageInsufficientBufferException e) {
|
||||
throw new DecodeException("Unexpected EOF", e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw doIOFail(e);
|
||||
} catch (MessagePackException | DecodeException e) {
|
||||
var path = new ArrayList<String>(currPath.size());
|
||||
for (var iter = currPath.descendingIterator(); iter.hasNext(); ) {
|
||||
path.add(iter.next().toString());
|
||||
}
|
||||
Collections.reverse(path);
|
||||
throw doFail(e, unpacker.getTotalReadBytes(), path);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertLength(PklBinaryCode type, int len, int expected) {
|
||||
if (len < expected) {
|
||||
throw new DecodeException(
|
||||
"Expected %s structure to have at least %d slots, found %d", type, expected + 1, len);
|
||||
}
|
||||
}
|
||||
|
||||
protected record DecodedObjectMember(PklBinaryCode type, Object key, Object value) {}
|
||||
|
||||
protected abstract RuntimeException doFail(Exception cause, long offset, List<String> path);
|
||||
|
||||
protected abstract RuntimeException doIOFail(IOException cause);
|
||||
|
||||
protected abstract Object doDecodeNull();
|
||||
|
||||
protected abstract Object doDecodeObject(
|
||||
String className, URI moduleUri, DecodeIterator<DecodedObjectMember> iter);
|
||||
|
||||
protected abstract Object doDecodeMap(MapDecodeIterator iter);
|
||||
|
||||
protected abstract Object doDecodeMapping(MapDecodeIterator iter);
|
||||
|
||||
protected abstract Object doDecodeList(CollectionDecodeIterator iter);
|
||||
|
||||
protected abstract Object doDecodeListing(CollectionDecodeIterator iter);
|
||||
|
||||
protected abstract Object doDecodeSet(CollectionDecodeIterator iter);
|
||||
|
||||
protected abstract Object doDecodeDuration(double value, DurationUnit unit);
|
||||
|
||||
protected abstract Object doDecodeDataSize(double value, DataSizeUnit unit);
|
||||
|
||||
protected abstract Object doDecodePair(Object first, Object second);
|
||||
|
||||
protected abstract Object doDecodeIntSeq(long start, long end, long step);
|
||||
|
||||
protected abstract Object doDecodeRegex(Pattern pattern);
|
||||
|
||||
protected abstract Object doDecodeClass(String qualifiedName, URI moduleUri);
|
||||
|
||||
protected abstract Object doDecodeTypeAlias(String qualifiedName, URI moduleUri);
|
||||
|
||||
protected Object doDecodeFunction() {
|
||||
throw new DecodeException("Cannot decode Function value");
|
||||
}
|
||||
|
||||
protected abstract Object doDecodeBytes(byte[] bytes);
|
||||
|
||||
private Object doDecode() throws IOException {
|
||||
if (!unpacker.hasNext()) {
|
||||
throw new DecodeException("Unexpected EOF");
|
||||
}
|
||||
|
||||
return switch (unpacker.getNextFormat().getValueType()) {
|
||||
// primitives
|
||||
case NIL -> {
|
||||
unpacker.unpackNil();
|
||||
yield doDecodeNull();
|
||||
}
|
||||
case STRING -> unpacker.unpackString();
|
||||
case INTEGER -> unpacker.unpackLong();
|
||||
case BOOLEAN -> unpacker.unpackBoolean();
|
||||
case FLOAT -> unpacker.unpackDouble();
|
||||
// non-primitive
|
||||
case ARRAY -> decodeNonPrimitive();
|
||||
// things we should never see outside a non-primitive
|
||||
case BINARY -> throw new DecodeException("Unexpected msgpack bin value");
|
||||
case MAP -> throw new DecodeException("Unexpected msgpack map value");
|
||||
case EXTENSION -> throw new DecodeException("Unexpected msgpack ext value");
|
||||
};
|
||||
}
|
||||
|
||||
private Object decodeNonPrimitive() throws IOException {
|
||||
var len = unpacker.unpackArrayHeader();
|
||||
if (len < 1) {
|
||||
throw new DecodeException("Unexpected empty object array value");
|
||||
}
|
||||
|
||||
var codeInt = unpacker.unpackInt();
|
||||
var code = PklBinaryCode.fromInt(codeInt);
|
||||
if (code == null) {
|
||||
throw new DecodeException("Unrecognized code 0x%x", (byte) codeInt);
|
||||
}
|
||||
|
||||
return switch (code) {
|
||||
case OBJECT -> decodeObject(len);
|
||||
case MAP -> decodeMap(len);
|
||||
case MAPPING -> decodeMapping(len);
|
||||
case LIST -> decodeList(len);
|
||||
case LISTING -> decodeListing(len);
|
||||
case SET -> decodeSet(len);
|
||||
case DURATION -> decodeDuration(len);
|
||||
case DATASIZE -> decodeDataSize(len);
|
||||
case PAIR -> decodePair(len);
|
||||
case INTSEQ -> decodeIntSeq(len);
|
||||
case REGEX -> decodeRegex(len);
|
||||
case CLASS -> decodeClass(len);
|
||||
case TYPEALIAS -> decodeTypeAlias(len);
|
||||
case FUNCTION -> decodeFunction(len);
|
||||
case BYTES -> decodeBytes(len);
|
||||
default -> throw new DecodeException("Unrecognized object code %s", code);
|
||||
};
|
||||
}
|
||||
|
||||
private Object decodeObject(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.OBJECT, len, 3);
|
||||
currPath.push("'object");
|
||||
|
||||
var className = unpacker.unpackString();
|
||||
if (className.isBlank()) {
|
||||
throw new DecodeException("Unexpected blank object class name");
|
||||
}
|
||||
var classModuleUriString = unpacker.unpackString();
|
||||
if (classModuleUriString.isBlank()) {
|
||||
throw new DecodeException("Unexpected blank object module URI");
|
||||
}
|
||||
var classModuleUri = URI.create(classModuleUriString);
|
||||
|
||||
var result =
|
||||
doDecodeObject(
|
||||
className, classModuleUri, new ObjectDecodeIterator(unpacker.unpackArrayHeader()));
|
||||
unpacker.skipValue(len - 4);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeMap(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.MAP, len, 1);
|
||||
currPath.push("'map");
|
||||
var result = doDecodeMap(new MapDecodeIterator(unpacker.unpackMapHeader()));
|
||||
unpacker.skipValue(len - 2);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeMapping(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.MAPPING, len, 1);
|
||||
currPath.push("'mapping");
|
||||
var result = doDecodeMapping(new MapDecodeIterator(unpacker.unpackMapHeader()));
|
||||
unpacker.skipValue(len - 2);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeList(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.LIST, len, 1);
|
||||
currPath.push("'list");
|
||||
var result = doDecodeList(new CollectionDecodeIterator(unpacker.unpackArrayHeader()));
|
||||
unpacker.skipValue(len - 2);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeListing(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.LISTING, len, 1);
|
||||
currPath.push("'listing");
|
||||
var result = doDecodeListing(new CollectionDecodeIterator(unpacker.unpackArrayHeader()));
|
||||
unpacker.skipValue(len - 2);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeSet(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.SET, len, 1);
|
||||
currPath.push("'set");
|
||||
var result = doDecodeSet(new CollectionDecodeIterator(unpacker.unpackArrayHeader()));
|
||||
currPath.pop();
|
||||
unpacker.skipValue(len - 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeDuration(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.DURATION, len, 2);
|
||||
currPath.push("'duration");
|
||||
var durationValue = unpacker.unpackDouble();
|
||||
var rawDurationUnit = unpacker.unpackString();
|
||||
var durationUnit = DurationUnit.parse(rawDurationUnit);
|
||||
if (durationUnit == null) {
|
||||
throw new DecodeException("Invalid Duration unit `%s`", rawDurationUnit);
|
||||
}
|
||||
var result = doDecodeDuration(durationValue, durationUnit);
|
||||
unpacker.skipValue(len - 3);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeDataSize(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.DATASIZE, len, 2);
|
||||
currPath.push("'datasize");
|
||||
var dataSizeValue = unpacker.unpackDouble();
|
||||
var rawDataSizeUnit = unpacker.unpackString();
|
||||
var dataSizeUnit = DataSizeUnit.parse(rawDataSizeUnit);
|
||||
if (dataSizeUnit == null) {
|
||||
throw new DecodeException("Invalid DataSize unit `%s`", rawDataSizeUnit);
|
||||
}
|
||||
var result = doDecodeDataSize(dataSizeValue, dataSizeUnit);
|
||||
unpacker.skipValue(len - 3);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodePair(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.PAIR, len, 2);
|
||||
currPath.push("'pair");
|
||||
currPath.push("'first");
|
||||
var first = doDecode();
|
||||
currPath.pop();
|
||||
currPath.push("'second");
|
||||
var second = doDecode();
|
||||
currPath.pop();
|
||||
var result = doDecodePair(first, second);
|
||||
unpacker.skipValue(len - 3);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeIntSeq(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.INTSEQ, len, 3);
|
||||
currPath.push("'intseq");
|
||||
var start = unpacker.unpackLong();
|
||||
var end = unpacker.unpackLong();
|
||||
var step = unpacker.unpackLong();
|
||||
var result = doDecodeIntSeq(start, end, step);
|
||||
unpacker.skipValue(len - 4);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeRegex(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.REGEX, len, 1);
|
||||
currPath.push("'regex");
|
||||
var result = doDecodeRegex(Pattern.compile(unpacker.unpackString()));
|
||||
unpacker.skipValue(len - 2);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeClass(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.CLASS, len, 2);
|
||||
currPath.push("'class");
|
||||
|
||||
var name = unpacker.unpackString();
|
||||
if (name.isBlank()) {
|
||||
throw new DecodeException("Unexpected blank class name");
|
||||
}
|
||||
var moduleUriString = unpacker.unpackString();
|
||||
if (moduleUriString.isBlank()) {
|
||||
throw new DecodeException("Unexpected blank class module URI");
|
||||
}
|
||||
var moduleUri = URI.create(moduleUriString);
|
||||
|
||||
var result = doDecodeClass(name, moduleUri);
|
||||
unpacker.skipValue(len - 3);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeTypeAlias(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.TYPEALIAS, len, 2);
|
||||
currPath.push("'typealias");
|
||||
|
||||
var name = unpacker.unpackString();
|
||||
if (name.isBlank()) {
|
||||
throw new DecodeException("Unexpected blank typealias name");
|
||||
}
|
||||
var moduleUriString = unpacker.unpackString();
|
||||
if (moduleUriString.isBlank()) {
|
||||
throw new DecodeException("Unexpected blank typealias module URI");
|
||||
}
|
||||
var moduleUri = URI.create(moduleUriString);
|
||||
|
||||
var result = doDecodeTypeAlias(name, moduleUri);
|
||||
unpacker.skipValue(len - 3);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeFunction(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.FUNCTION, len, 0);
|
||||
currPath.push("'function");
|
||||
var result = doDecodeFunction();
|
||||
unpacker.skipValue(len - 1);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object decodeBytes(int len) throws IOException {
|
||||
assertLength(PklBinaryCode.BYTES, len, 1);
|
||||
currPath.push("'bytes");
|
||||
var result = doDecodeBytes(unpacker.readPayload(unpacker.unpackBinaryHeader()));
|
||||
unpacker.skipValue(len - 2);
|
||||
currPath.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
// some silly iterator classes because next() needs to handle IOException
|
||||
|
||||
protected abstract class DecodeIterator<T> implements Iterator<T> {
|
||||
private final int size;
|
||||
private int idx = 0;
|
||||
|
||||
DecodeIterator(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return idx < size;
|
||||
}
|
||||
|
||||
public T next() {
|
||||
currPath.push(idx);
|
||||
try {
|
||||
return getNext();
|
||||
} catch (IOException e) {
|
||||
throw doIOFail(e);
|
||||
} finally {
|
||||
currPath.pop();
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
abstract T getNext() throws IOException;
|
||||
}
|
||||
|
||||
protected class ObjectDecodeIterator extends DecodeIterator<DecodedObjectMember> {
|
||||
ObjectDecodeIterator(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
DecodedObjectMember getNext() throws IOException {
|
||||
var memberLen = unpacker.unpackArrayHeader();
|
||||
if (memberLen != 3) {
|
||||
throw new DecodeException("Expected 3 fields in object member, found %d", memberLen);
|
||||
}
|
||||
var memberCodeInt = unpacker.unpackInt();
|
||||
var memberCode = PklBinaryCode.fromInt(memberCodeInt);
|
||||
if (memberCode == null) {
|
||||
throw new DecodeException("Unrecognized code 0x%x", (byte) memberCodeInt);
|
||||
}
|
||||
DecodedObjectMember member;
|
||||
switch (memberCode) {
|
||||
case PROPERTY -> {
|
||||
var propertyName = unpacker.unpackString();
|
||||
currPath.push(propertyName);
|
||||
member = new DecodedObjectMember(memberCode, propertyName, doDecode());
|
||||
}
|
||||
case ENTRY -> {
|
||||
var entryKey = doDecode();
|
||||
currPath.push(entryKey);
|
||||
member = new DecodedObjectMember(memberCode, entryKey, doDecode());
|
||||
}
|
||||
case ELEMENT -> {
|
||||
var elementIndex = unpacker.unpackLong();
|
||||
currPath.push(elementIndex);
|
||||
member = new DecodedObjectMember(memberCode, elementIndex, doDecode());
|
||||
}
|
||||
default -> throw new DecodeException("Unrecognized member code %s", memberCode);
|
||||
}
|
||||
currPath.pop();
|
||||
return member;
|
||||
}
|
||||
}
|
||||
|
||||
protected class CollectionDecodeIterator extends DecodeIterator<Object> {
|
||||
CollectionDecodeIterator(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
Object getNext() throws IOException {
|
||||
return doDecode();
|
||||
}
|
||||
}
|
||||
|
||||
protected class MapDecodeIterator extends DecodeIterator<Pair<Object, Object>> {
|
||||
MapDecodeIterator(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
Pair<Object, Object> getNext() throws IOException {
|
||||
var key = doDecode();
|
||||
currPath.push(key);
|
||||
var val = doDecode();
|
||||
currPath.pop();
|
||||
return new Pair<>(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright © 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.core.util.pklbinary;
|
||||
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public enum PklBinaryCode {
|
||||
OBJECT((byte) 0x01),
|
||||
MAP((byte) 0x02),
|
||||
MAPPING((byte) 0x03),
|
||||
LIST((byte) 0x04),
|
||||
LISTING((byte) 0x05),
|
||||
SET((byte) 0x06),
|
||||
DURATION((byte) 0x07),
|
||||
DATASIZE((byte) 0x08),
|
||||
PAIR((byte) 0x09),
|
||||
INTSEQ((byte) 0x0A),
|
||||
REGEX((byte) 0x0B),
|
||||
CLASS((byte) 0x0C),
|
||||
TYPEALIAS((byte) 0x0D),
|
||||
FUNCTION((byte) 0x0E),
|
||||
BYTES((byte) 0x0F),
|
||||
|
||||
PROPERTY((byte) 0x10),
|
||||
ENTRY((byte) 0x11),
|
||||
ELEMENT((byte) 0x12);
|
||||
|
||||
private final byte code;
|
||||
|
||||
PklBinaryCode(byte code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static @Nullable PklBinaryCode fromInt(int value) {
|
||||
return switch (value) {
|
||||
case 0x01 -> PklBinaryCode.OBJECT;
|
||||
case 0x02 -> PklBinaryCode.MAP;
|
||||
case 0x03 -> PklBinaryCode.MAPPING;
|
||||
case 0x04 -> PklBinaryCode.LIST;
|
||||
case 0x05 -> PklBinaryCode.LISTING;
|
||||
case 0x06 -> PklBinaryCode.SET;
|
||||
case 0x07 -> PklBinaryCode.DURATION;
|
||||
case 0x08 -> PklBinaryCode.DATASIZE;
|
||||
case 0x09 -> PklBinaryCode.PAIR;
|
||||
case 0x0A -> PklBinaryCode.INTSEQ;
|
||||
case 0x0B -> PklBinaryCode.REGEX;
|
||||
case 0x0C -> PklBinaryCode.CLASS;
|
||||
case 0x0D -> PklBinaryCode.TYPEALIAS;
|
||||
case 0x0E -> PklBinaryCode.FUNCTION;
|
||||
case 0x0F -> PklBinaryCode.BYTES;
|
||||
|
||||
case 0x10 -> PklBinaryCode.PROPERTY;
|
||||
case 0x11 -> PklBinaryCode.ENTRY;
|
||||
case 0x12 -> PklBinaryCode.ELEMENT;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NonnullByDefault
|
||||
package org.pkl.core.util.pklbinary;
|
||||
|
||||
import org.pkl.core.util.NonnullByDefault;
|
||||
48
pkl-core/src/test/files/LanguageSnippetTests/input/api/pklbinary1.msgpack.yaml.pkl
vendored
Normal file
48
pkl-core/src/test/files/LanguageSnippetTests/input/api/pklbinary1.msgpack.yaml.pkl
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
open module encoding1
|
||||
|
||||
import "pkl:pklbinary"
|
||||
import "pkl:base"
|
||||
|
||||
class Foo {
|
||||
dynamic: Dynamic = new {
|
||||
hello = "world"
|
||||
["hello"] = "world"
|
||||
"hello world"
|
||||
}
|
||||
string: String = "foo"
|
||||
map = Map("foo", "bar")
|
||||
mapping: Mapping = new { ["foo"] = "bar" }
|
||||
list = List("foo", "bar")
|
||||
listing: Listing = new { "foo"; 0 }
|
||||
set = Set("foo", "bar")
|
||||
duration = 123.h
|
||||
dataSize = 123.gib
|
||||
pair = Pair("foo", "bar")
|
||||
intSeq = IntSeq(123, 456)
|
||||
regex = Regex("foo.*")
|
||||
func: ((String, Int) -> Boolean)? =
|
||||
(a, b) -> a.sha256Int + b % 2 == 0
|
||||
bytes = Bytes(0x01, 0x02, 0x03)
|
||||
moduleClass: Class
|
||||
baseModuleClass: Class = base.getClass()
|
||||
pklbinaryModuleClass: Class = pklbinary.getClass()
|
||||
stdlibClass: Class = PcfRenderer
|
||||
someClass: Class = Foo
|
||||
stdlibTypealias: TypeAlias = UInt
|
||||
someTypealias: TypeAlias = Bar
|
||||
something: Any = new PcfRenderer {} // a non-external class from pkl:base
|
||||
}
|
||||
|
||||
typealias Bar = Mapping<String, String>
|
||||
|
||||
classInstance: Foo = new {
|
||||
moduleClass = Map(true, module.getClass())[true]
|
||||
}
|
||||
|
||||
hidden noFunc: Foo = (classInstance) { func = null }
|
||||
|
||||
hidden encoded: Bytes = new pklbinary.Renderer {}.renderValue(noFunc)
|
||||
|
||||
output {
|
||||
bytes = encoded
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1 = "bar"
|
||||
res2 = ""
|
||||
res3 = 1
|
||||
@@ -1,5 +1,7 @@
|
||||
module com.foo.bar.MyModule
|
||||
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
class Person {
|
||||
firstName: String
|
||||
lastName: String
|
||||
@@ -1,3 +1,5 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1: DataSize = 1.b
|
||||
res2: DataSize = 2.kb
|
||||
res3: DataSize = 3.kib
|
||||
@@ -1,3 +1,5 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1 = 1.ns
|
||||
res2 = 2.us
|
||||
res3 = 3.ms
|
||||
@@ -1,2 +1,4 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1 = IntSeq(1, 3)
|
||||
res2 = IntSeq(1, 4).step(5)
|
||||
@@ -1,3 +1,5 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1: List<Int> = List(1, 3, 5, 7)
|
||||
res2: Listing<Int> = new { 2; 4; 6; 8 }
|
||||
res3: List<Int> = List()
|
||||
@@ -1,3 +1,5 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1: Map = Map("foo", 1, "bar", 2)
|
||||
res2: Mapping = new {
|
||||
["foo"] = 1
|
||||
@@ -1,2 +1,4 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1 = Pair(1, 2)
|
||||
res2 = Pair("foo", "bar")
|
||||
@@ -1,3 +1,5 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1 = Regex("abc")
|
||||
res2 = Regex("")
|
||||
res3 = Regex("(?m)^abc$")
|
||||
@@ -1,3 +1,5 @@
|
||||
extends ".../pklbinaryTest.pkl"
|
||||
|
||||
res1: Set<Int> = Set(1, 3, 5, 7)
|
||||
res2: Set<Int> = Set()
|
||||
res3: Set<Any> = Set(1, true, "", null)
|
||||
13
pkl-core/src/test/files/LanguageSnippetTests/input/pklbinaryTest.pkl
vendored
Normal file
13
pkl-core/src/test/files/LanguageSnippetTests/input/pklbinaryTest.pkl
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
open module pklbinaryTest
|
||||
|
||||
import "pkl:pklbinary"
|
||||
|
||||
output {
|
||||
// ensure no output file written for this specific module, only children that add properties
|
||||
when (module.toMap().isEmpty) {
|
||||
text = ""
|
||||
} else {
|
||||
value = if (module.toMap().isEmpty) "" else module
|
||||
renderer = new pklbinary.Renderer {}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ at pkl.base#JsonRenderer.renderDocument (file:///$snippetsDir/input/api/jsonRend
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at pkl.jsonnet#Renderer.renderDocument (file:///$snippetsDir/input/api/jsonnetRe
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at pkl.base#PListRenderer.renderDocument (file:///$snippetsDir/input/api/pListRe
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at pkl.base#PcfRenderer.renderDocument (file:///$snippetsDir/input/api/pcfRender
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
190
pkl-core/src/test/files/LanguageSnippetTests/output/api/pklbinary1.msgpack.yaml
vendored
Normal file
190
pkl-core/src/test/files/LanguageSnippetTests/output/api/pklbinary1.msgpack.yaml
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
- 1
|
||||
- 'encoding1#Foo'
|
||||
- 'file:///$snippetsDir/input/api/pklbinary1.msgpack.yaml.pkl'
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- 'dynamic'
|
||||
-
|
||||
- 1
|
||||
- 'Dynamic'
|
||||
- 'pkl:base'
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- 'hello'
|
||||
- 'world'
|
||||
-
|
||||
- 17
|
||||
- 'hello'
|
||||
- 'world'
|
||||
-
|
||||
- 18
|
||||
- 0
|
||||
- 'hello world'
|
||||
-
|
||||
- 16
|
||||
- 'string'
|
||||
- 'foo'
|
||||
-
|
||||
- 16
|
||||
- 'map'
|
||||
-
|
||||
- 2
|
||||
-
|
||||
'foo': 'bar'
|
||||
-
|
||||
- 16
|
||||
- 'mapping'
|
||||
-
|
||||
- 3
|
||||
-
|
||||
'foo': 'bar'
|
||||
-
|
||||
- 16
|
||||
- 'list'
|
||||
-
|
||||
- 4
|
||||
-
|
||||
- 'foo'
|
||||
- 'bar'
|
||||
-
|
||||
- 16
|
||||
- 'listing'
|
||||
-
|
||||
- 5
|
||||
-
|
||||
- 'foo'
|
||||
- 0
|
||||
-
|
||||
- 16
|
||||
- 'set'
|
||||
-
|
||||
- 6
|
||||
-
|
||||
- 'foo'
|
||||
- 'bar'
|
||||
-
|
||||
- 16
|
||||
- 'duration'
|
||||
-
|
||||
- 7
|
||||
- 123.0
|
||||
- 'h'
|
||||
-
|
||||
- 16
|
||||
- 'dataSize'
|
||||
-
|
||||
- 8
|
||||
- 123.0
|
||||
- 'gib'
|
||||
-
|
||||
- 16
|
||||
- 'pair'
|
||||
-
|
||||
- 9
|
||||
- 'foo'
|
||||
- 'bar'
|
||||
-
|
||||
- 16
|
||||
- 'intSeq'
|
||||
-
|
||||
- 10
|
||||
- 123
|
||||
- 456
|
||||
- 1
|
||||
-
|
||||
- 16
|
||||
- 'regex'
|
||||
-
|
||||
- 11
|
||||
- 'foo.*'
|
||||
-
|
||||
- 16
|
||||
- 'func'
|
||||
- null
|
||||
-
|
||||
- 16
|
||||
- 'bytes'
|
||||
-
|
||||
- 15
|
||||
- !!binary 'AQID'
|
||||
-
|
||||
- 16
|
||||
- 'moduleClass'
|
||||
-
|
||||
- 12
|
||||
- 'encoding1'
|
||||
- 'file:///$snippetsDir/input/api/pklbinary1.msgpack.yaml.pkl'
|
||||
-
|
||||
- 16
|
||||
- 'baseModuleClass'
|
||||
-
|
||||
- 12
|
||||
- 'ModuleClass'
|
||||
- 'pkl:base'
|
||||
-
|
||||
- 16
|
||||
- 'pklbinaryModuleClass'
|
||||
-
|
||||
- 12
|
||||
- 'pkl.pklbinary'
|
||||
- 'pkl:pklbinary'
|
||||
-
|
||||
- 16
|
||||
- 'stdlibClass'
|
||||
-
|
||||
- 12
|
||||
- 'PcfRenderer'
|
||||
- 'pkl:base'
|
||||
-
|
||||
- 16
|
||||
- 'someClass'
|
||||
-
|
||||
- 12
|
||||
- 'encoding1#Foo'
|
||||
- 'file:///$snippetsDir/input/api/pklbinary1.msgpack.yaml.pkl'
|
||||
-
|
||||
- 16
|
||||
- 'stdlibTypealias'
|
||||
-
|
||||
- 13
|
||||
- 'UInt'
|
||||
- 'pkl:base'
|
||||
-
|
||||
- 16
|
||||
- 'someTypealias'
|
||||
-
|
||||
- 13
|
||||
- 'encoding1#Bar'
|
||||
- 'file:///$snippetsDir/input/api/pklbinary1.msgpack.yaml.pkl'
|
||||
-
|
||||
- 16
|
||||
- 'something'
|
||||
-
|
||||
- 1
|
||||
- 'PcfRenderer'
|
||||
- 'pkl:base'
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- 'converters'
|
||||
-
|
||||
- 3
|
||||
- {}
|
||||
-
|
||||
- 16
|
||||
- 'extension'
|
||||
- 'pcf'
|
||||
-
|
||||
- 16
|
||||
- 'indent'
|
||||
- ' '
|
||||
-
|
||||
- 16
|
||||
- 'omitNullProperties'
|
||||
- false
|
||||
-
|
||||
- 16
|
||||
- 'useCustomStringDelimiters'
|
||||
- false
|
||||
@@ -8,10 +8,10 @@ at propertiesRenderer10.properties#foo (file:///$snippetsDir/input/api/propertie
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at pkl.base#PropertiesRenderer.renderDocument (file:///$snippetsDir/input/api/pr
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,7 +8,7 @@ res7 = "Cannot render value of type `DataSize` as Properties. Value: 1.mb"
|
||||
res8 = "Cannot render value of type `List` as Properties. Value: List(\"pigeon\", \"parrot\")"
|
||||
res9 = "Cannot render value of type `Set` as Properties. Value: Set(\"pigeon\", \"parrot\")"
|
||||
res10 = "Cannot render value of type `Map` as Properties. Value: Map(\"name\", \"pigeon\", \"age\", 42)"
|
||||
res11 = "Cannot render value of type `Listing` as Properties. Value: new Listing { ?; ? }"
|
||||
res11 = "Cannot render value of type `Listing` as Properties. Value: new Listing { \"pigeon\"; \"parrot\" }"
|
||||
res12 = "Cannot render value of type `Mapping` as Properties. Value: new Mapping { [\"name\"] = ?; [\"age\"] = ? }"
|
||||
res13 = "Cannot render value of type `Dynamic` as Properties. Value: new Dynamic { name = ?; age = ? }"
|
||||
res14 = "Cannot render value of type `propertiesRenderer4#Person` as Properties. Value: new Person { name = ?; age = ? }"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,10 +8,10 @@ at pkl.xml#Renderer.renderDocument (file:///$snippetsDir/input/api/xmlRenderer8.
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
<name>Pigeon</name>
|
||||
<age>42</age>
|
||||
</pigeon2>
|
||||
<res2>`xml.Element` is not supported here. Value: new Dynamic { _isXmlElement = true; name = ?; attributes = ?; isBlockFormat =...</res2>
|
||||
<res3>`xml.Inline` is not supported here. Value: new Inline { value = ? }</res3>
|
||||
<res2>`xml.Element` is not supported here. Value: new Dynamic { _isXmlElement = true; name = "pigeon2"; attributes {}; isBlockF...</res2>
|
||||
<res3>`xml.Inline` is not supported here. Value: new Inline { value { name = ?; age = ? } }</res3>
|
||||
</root>
|
||||
|
||||
@@ -8,10 +8,10 @@ at pkl.base#YamlRenderer.renderDocument (file:///$snippetsDir/input/api/yamlRend
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -5,10 +5,10 @@ x | bar = throw("Something went wrong")
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at exceptions#foo.bar (file:///$snippetsDir/input/basic/exceptions.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at class3#person (file:///$snippetsDir/input/classes/class3.pkl)
|
||||
Did you mean any of the following?
|
||||
address
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ xx | max = 3
|
||||
^
|
||||
at constraints5#res2.max (file:///$snippetsDir/input/classes/constraints5.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at inheritanceError1#Derived (file:///$snippetsDir/input/classes/inheritanceErro
|
||||
By default, classes are closed for extension.
|
||||
To make a class extensible, add an `open` modifier: `open class MyClass { ... }`
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ Examples:
|
||||
* `123` uses literal syntax to create an instance of class `Int`.
|
||||
* `Pair(1, 2)` uses the `Pair()` constructor method to create an instance of class `Pair`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at invalidInstantiation2#res1 (file:///$snippetsDir/input/classes/invalidInstant
|
||||
Abstract classes cannot be instantiated.
|
||||
Instead, instantiate a concrete subclass.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ xx | a = "other"
|
||||
^^^^^^^
|
||||
at unionTypesErrorAlias#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorAlias.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -14,10 +14,10 @@ x | a = List(1, 3.14, 2)
|
||||
^^^^^^^^^^^^^^^^
|
||||
at unionTypesErrorDifferent1#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorDifferent1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -14,10 +14,10 @@ x | a = List(1, 2, 3)
|
||||
^^^^^^^^^^^^^
|
||||
at unionTypesErrorDifferent2#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorDifferent2.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -18,10 +18,10 @@ x | res1: Alias1|Alias2 = new Dynamic { }
|
||||
^^^^^^^^^^^^^^^
|
||||
at unionTypesErrorMultipleAliases#res1 (file:///$snippetsDir/input/classes/unionTypesErrorMultipleAliases.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -18,10 +18,10 @@ x | a = Map(
|
||||
^^^^
|
||||
at unionTypesErrorNested#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorNested.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ x | a = 42
|
||||
^^
|
||||
at unionTypesErrorSimple#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorSimple.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ x | a = "foox"
|
||||
^^^^^^
|
||||
at unionTypesErrorString1#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorString1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ xx | a = "other"
|
||||
^^^^^^^
|
||||
at unionTypesErrorString2#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorString2.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ x | name = 42
|
||||
^^
|
||||
at wrongType1#pigeon.name (file:///$snippetsDir/input/classes/wrongType1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ x | age = "42"
|
||||
^^^^
|
||||
at wrongType2#pigeon.age (file:///$snippetsDir/input/classes/wrongType2.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ xx | address = "Howdy St."
|
||||
^^^^^^^^^^^
|
||||
at wrongType3#person.address (file:///$snippetsDir/input/classes/wrongType3.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ xx | street = 4.gb
|
||||
^^^^
|
||||
at wrongType4#person.address.street (file:///$snippetsDir/input/classes/wrongType4.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ x | names: List<String>(!isEmpty)
|
||||
^^^^^
|
||||
at wrongType6#Person.names (file:///$snippetsDir/input/classes/wrongType6.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ x | res = analyze.importGraph(Set(reflect.Module(cannotFindModule).uri))
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at analyzeImportsCannotFindModule#res (file:///$snippetsDir/input/errors/analyzeImportsCannotFindModule.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ x | res = analyze.importGraph(Set(reflect.Module(invalidGlob).uri))
|
||||
^^^^^^^^^^^
|
||||
at analyzeImportsInvalidGlob#res (file:///$snippetsDir/input/errors/analyzeImportsInvalidGlob.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -7,10 +7,10 @@ at analyzeInvalidModuleUri#result (file:///$snippetsDir/input/errors/analyzeInva
|
||||
|
||||
Illegal character in path at index 3: foo <>
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -5,10 +5,10 @@ x | result = analyze.importGraph(Set("foo.pkl"))
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at analyzeRelativeModuleUri#result (file:///$snippetsDir/input/errors/analyzeRelativeModuleUri.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
The top-level value of a Pcf document must have type `Typed` or `Dynamic`, but got type `String`.
|
||||
Value: "anyConverterError"
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
This value was converted during rendering. Previous: new ModuleClass {}. After: "anyConverterError".
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -5,10 +5,10 @@ xx | bird {
|
||||
^^^^
|
||||
at cannotAmendFixedProperty1#n (file:///$snippetsDir/input/errors/cannotAmendFixedProperty1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -5,10 +5,10 @@ x | name = _name
|
||||
^^^^
|
||||
at cannotAssignFixedProperty1#p (file:///$snippetsDir/input/errors/cannotAssignFixedProperty1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ x | ...new Dynamic { name = "Osprey" }
|
||||
^^^^
|
||||
at cannotAssignFixedProperty3#p.name (file:///$snippetsDir/input/errors/cannotAssignFixedProperty3.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -12,10 +12,10 @@ x | value: nothing = "foo"
|
||||
^^^^^
|
||||
at cannotAssignToNothing#value (file:///$snippetsDir/input/errors/cannotAssignToNothing.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -7,10 +7,10 @@ at cannotChangeFixed1#Dog (file:///$snippetsDir/input/errors/cannotChangeFixed1.
|
||||
|
||||
Property `name` must be declared fixed, because it overrides a fixed property on parent class `cannotChangeFixed1#Animal`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -7,10 +7,10 @@ at cannotChangeFixed2#Dog (file:///$snippetsDir/input/errors/cannotChangeFixed2.
|
||||
|
||||
Property `name` cannot be declared fixed, because it overrides a non-fixed property on parent class `cannotChangeFixed2#Animal`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -10,10 +10,10 @@ Did you mean any of the following?
|
||||
"foyo"
|
||||
"xfoo"
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -15,6 +15,7 @@ pkl:EvaluatorSettings
|
||||
pkl:json
|
||||
pkl:jsonnet
|
||||
pkl:math
|
||||
pkl:pklbinary
|
||||
pkl:platform
|
||||
pkl:Project
|
||||
pkl:protobuf
|
||||
@@ -31,10 +32,10 @@ x | res1 = nonExisting.bar
|
||||
^^^^^^^^^^^
|
||||
at cannotFindStdLibModule#res1 (file:///$snippetsDir/input/errors/cannotFindStdLibModule.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at cannotInstantiateAbstractModule#res1 (file:///$snippetsDir/input/errors/canno
|
||||
Abstract classes cannot be instantiated.
|
||||
Instead, instantiate a concrete subclass.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -8,10 +8,10 @@ at pkl.base#PcfRenderer.renderDocument (file:///$snippetsDir/input/errors/cannot
|
||||
|
||||
Consider adding a converter to `output.converters`.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ To fix, do either of:
|
||||
1. Add modifier `const` to method `f1`
|
||||
2. Self-import this module, and reference this method from the import.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ To fix, do either of:
|
||||
1. Add modifier `const` to property `top`
|
||||
2. Self-import this module, and reference this property from the import.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ To fix, do either of:
|
||||
1. Add modifier `const` to property `notConst`
|
||||
2. Self-import this module, and reference this property from the import.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
@@ -9,10 +9,10 @@ To fix, do either of:
|
||||
1. Add modifier `const` to property `a`
|
||||
2. Self-import this module, and reference this property from the import.
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user