Introduces Bytes class (#1019)

This introduces a new `Bytes` standard library class, for working with
binary data.

* Add Bytes class to the standard library
* Change CLI to eval `output.bytes`
* Change code generators to map Bytes to respective underlying type
* Add subscript and concat operator support
* Add binary encoding for Bytes
* Add PCF and Plist rendering for Bytes

Co-authored-by: Kushal Pisavadia <kushi.p@gmail.com>
This commit is contained in:
Daniel Chao
2025-06-11 16:23:55 -07:00
committed by GitHub
parent 3bd8a88506
commit e9320557b7
104 changed files with 2210 additions and 545 deletions

View File

@@ -138,6 +138,7 @@ endif::[]
:uri-stdlib-Function3: {uri-stdlib-baseModule}/Function3
:uri-stdlib-Function4: {uri-stdlib-baseModule}/Function4
:uri-stdlib-Function5: {uri-stdlib-baseModule}/Function5
:uri-stdlib-Bytes: {uri-stdlib-baseModule}/Bytes
:uri-stdlib-Resource: {uri-stdlib-baseModule}/Resource
:uri-stdlib-outputFiles: {uri-stdlib-baseModule}/ModuleOutput#files

View File

@@ -161,6 +161,24 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
|
|
|link:{uri-stdlib-Function}[Function]
|`0xE`
|
|
|
|
|
|
|link:{uri-stdlib-Bytes}[Bytes]
|`0xF`
|link:{uri-messagepack-bin}[bin]
|Binary contents
|
|
|
|
|===
[[object-members]]

View File

@@ -3684,6 +3684,41 @@ res5 = map.getOrNull("Falcon") // <5>
<4> result: `2`
<5> result: `null`
[[bytes]]
=== Bytes
A value of type `Bytes` is a sequence of `UInt8` elements.
`Bytes` can be constructed by passing byte values into the constructor.
[source,pkl]
----
bytes1 = Bytes(0xff, 0x00, 0x3f) // <1>
bytes2 = Bytes() // <2>
----
<1> Result: a `Bytes` with 3 elements
<2> Result: an empty `Bytes`
`Bytes` can also be constructed from a base64-encoded string, via `base64DecodedBytes`:
[source,pkl]
----
bytes3 = "cGFycm90".base64DecodedBytes // <1>
----
<1> Result: `Bytes(112, 97, 114, 114, 111, 116)`
==== `Bytes` vs `List<UInt8>`
`Bytes` is similar to `List<UInt8>` in that they are both sequences of `UInt8` elements.
However, they are semantically distinct.
`Bytes` represent binary data, and is typically rendered differently.
For example, they are rendered as `<data>` tags when using `PListRenderer`.
`Bytes` also have different performance characteristics; a value of type `Bytes` tends to be managed as a contiguous memory block.
Thus, they are more compact and consume less memory.
However, they are not optimized for transformations.
For example, given two values of size `M` and `N`, concatenating two `Bytes` values allocates O(M + N) space, whereas concatenating two `List` values allocates O(1) space.
[[regular-expressions]]
=== Regular Expressions
@@ -4671,6 +4706,10 @@ The following types are iterable:
|entry key (`Key`)
|entry value (`Value`)
|`Bytes`
|element index (`Int`)
|element value (`UInt8`)
|`Listing<Element>`
|element index (`Int`)
|element value (`Element`)
@@ -4751,6 +4790,9 @@ The following table describes how different iterables turn into object members:
| `IntSeq`
| Element
| `Bytes`
| Element
|===
These types can only be spread into enclosing objects that support that member type.

View File

@@ -16,20 +16,23 @@
package org.pkl.cli
import java.io.File
import java.io.Reader
import java.io.Writer
import java.io.InputStream
import java.io.OutputStream
import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import kotlin.io.path.createParentDirectories
import kotlin.io.path.exists
import kotlin.io.path.isDirectory
import kotlin.io.path.writeBytes
import org.pkl.commons.cli.CliCommand
import org.pkl.commons.cli.CliException
import org.pkl.commons.currentWorkingDir
import org.pkl.commons.writeString
import org.pkl.core.Closeables
import org.pkl.core.Evaluator
import org.pkl.core.EvaluatorBuilder
import org.pkl.core.ModuleSource
import org.pkl.core.PklException
@@ -48,8 +51,8 @@ constructor(
private val options: CliEvaluatorOptions,
// use System.{in,out}() rather than System.console()
// because the latter returns null when output is sent through a unix pipe
private val consoleReader: Reader = System.`in`.reader(),
private val consoleWriter: Writer = System.out.writer(),
private val inputStream: InputStream = System.`in`,
private val outputStream: OutputStream = System.out,
) : CliCommand(options.base) {
/**
* Output files for the modules to be evaluated. Returns `null` if `options.outputPath` is `null`
@@ -84,11 +87,12 @@ constructor(
/**
* Evaluates source modules according to [options].
*
* If [CliEvaluatorOptions.outputPath] is set, each module's `output.text` is written to the
* module's [output file][outputFiles]. If [CliEvaluatorOptions.multipleFileOutputPath] is set,
* each module's `output.files` are written to the module's [output directory][outputDirectories].
* Otherwise, each module's `output.text` is written to [consoleWriter] (which defaults to
* standard out).
* If [CliEvaluatorOptions.outputPath] is set, each module's `output.bytes` is written to the
* module's [output file][outputFiles].
*
* If [CliEvaluatorOptions.multipleFileOutputPath] is set, each module's `output.files` are
* written to the module's [output directory][outputDirectories]. Otherwise, each module's
* `output.bytes` is written to [outputStream] (which defaults to standard out).
*
* Throws [CliException] in case of an error.
*/
@@ -139,10 +143,29 @@ constructor(
}
}
/** Renders each module's `output.text`, writing it to the specified output file. */
private fun Evaluator.writeOutput(moduleSource: ModuleSource, writeTo: Path): Boolean {
if (options.expression == null) {
val bytes = evaluateOutputBytes(moduleSource)
writeTo.writeBytes(bytes)
return bytes.isNotEmpty()
}
val text = evaluateExpressionString(moduleSource, options.expression)
writeTo.writeString(text)
return text.isNotEmpty()
}
private fun Evaluator.evalOutput(moduleSource: ModuleSource): ByteArray {
if (options.expression == null) {
return evaluateOutputBytes(moduleSource)
}
return evaluateExpressionString(moduleSource, options.expression)
.toByteArray(StandardCharsets.UTF_8)
}
/** Renders each module's `output.bytes`, writing it to the specified output file. */
private fun writeOutput(builder: EvaluatorBuilder) {
val evaluator = builder.setOutputFormat(options.outputFormat).build()
evaluator.use {
evaluator.use { ev ->
val outputFiles = fileOutputPaths
if (outputFiles != null) {
// files that we've written non-empty output to
@@ -151,18 +174,18 @@ constructor(
val writtenFiles = mutableSetOf<Path>()
for ((moduleUri, outputFile) in outputFiles) {
val moduleSource = toModuleSource(moduleUri, consoleReader)
val output = evaluator.evaluateExpressionString(moduleSource, options.expression)
val moduleSource = toModuleSource(moduleUri, inputStream)
if (Files.isDirectory(outputFile)) {
throw CliException(
"Output file `$outputFile` is a directory. " +
"Did you mean `--multiple-file-output-path`?"
)
}
val output = ev.evalOutput(moduleSource)
outputFile.createParentDirectories()
if (!writtenFiles.contains(outputFile)) {
// write file even if output is empty to overwrite output from previous runs
outputFile.writeString(output)
outputFile.writeBytes(output)
if (output.isNotEmpty()) {
writtenFiles.add(outputFile)
}
@@ -174,34 +197,49 @@ constructor(
StandardOpenOption.WRITE,
StandardOpenOption.APPEND,
)
outputFile.writeString(
output,
Charsets.UTF_8,
StandardOpenOption.WRITE,
StandardOpenOption.APPEND,
)
outputFile.writeBytes(output, StandardOpenOption.WRITE, StandardOpenOption.APPEND)
}
}
}
} else {
var outputWritten = false
for (moduleUri in options.base.normalizedSourceModules) {
val moduleSource = toModuleSource(moduleUri, consoleReader)
val output = evaluator.evaluateExpressionString(moduleSource, options.expression)
if (output.isNotEmpty()) {
if (outputWritten) consoleWriter.appendLine(options.moduleOutputSeparator)
consoleWriter.write(output)
consoleWriter.flush()
outputWritten = true
val moduleSource = toModuleSource(moduleUri, inputStream)
if (options.expression != null) {
val output = evaluator.evaluateExpressionString(moduleSource, options.expression)
if (output.isNotEmpty()) {
if (outputWritten) outputStream.writeLine(options.moduleOutputSeparator)
outputStream.writeText(output)
outputStream.flush()
outputWritten = true
}
} else {
val outputBytes = evaluator.evaluateOutputBytes(moduleSource)
if (outputBytes.isNotEmpty()) {
if (outputWritten) outputStream.writeLine(options.moduleOutputSeparator)
outputStream.write(outputBytes)
outputStream.flush()
outputWritten = true
}
}
}
}
}
}
private fun toModuleSource(uri: URI, reader: Reader) =
if (uri == VmUtils.REPL_TEXT_URI) ModuleSource.create(uri, reader.readText())
else ModuleSource.uri(uri)
private fun OutputStream.writeText(text: String) = write(text.toByteArray())
private fun OutputStream.writeLine(text: String) {
writeText(text)
writeText("\n")
}
private fun toModuleSource(uri: URI, reader: InputStream) =
if (uri == VmUtils.REPL_TEXT_URI) {
ModuleSource.create(uri, reader.readAllBytes().toString(StandardCharsets.UTF_8))
} else {
ModuleSource.uri(uri)
}
private fun checkPathSpec(pathSpec: String) {
val illegal = pathSpec.indexOfFirst { IoUtils.isReservedFilenameChar(it) && it != '/' }
@@ -223,7 +261,7 @@ constructor(
if (outputDir.exists() && !outputDir.isDirectory()) {
throw CliException("Output path `$outputDir` exists and is not a directory.")
}
val moduleSource = toModuleSource(moduleUri, consoleReader)
val moduleSource = toModuleSource(moduleUri, inputStream)
val output = evaluator.evaluateOutputFiles(moduleSource)
for ((pathSpec, fileOutput) in output) {
checkPathSpec(pathSpec)
@@ -247,12 +285,12 @@ constructor(
}
writtenFiles[realPath] = OutputFile(pathSpec, moduleUri)
realPath.createParentDirectories()
realPath.writeString(fileOutput.text)
consoleWriter.write(
realPath.writeBytes(fileOutput.bytes)
outputStream.writeText(
IoUtils.relativize(resolvedPath, currentWorkingDir).toString() +
IoUtils.getLineSeparator()
)
consoleWriter.flush()
outputStream.flush()
}
}
}

View File

@@ -74,9 +74,9 @@ data class CliEvaluatorOptions(
*
* If set, the said expression is evaluated under the context of the enclosing module.
*
* If unset, the module's `output.text` property evaluated.
* If unset, the module's `output.bytes` property is evaluated.
*/
val expression: String = "output.text",
val expression: String? = null,
) {
companion object {

View File

@@ -18,10 +18,11 @@ package org.pkl.cli
import com.github.tomakehurst.wiremock.client.WireMock.*
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
import com.github.tomakehurst.wiremock.junit5.WireMockTest
import java.io.StringReader
import java.io.StringWriter
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.net.ServerSocket
import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.time.Duration
@@ -29,22 +30,20 @@ import java.util.regex.Pattern
import kotlin.io.path.*
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.*
import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.EnabledOnOs
import org.junit.jupiter.api.condition.OS
import org.junit.jupiter.api.io.TempDir
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource
import org.pkl.commons.*
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
import org.pkl.commons.readString
import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer
import org.pkl.commons.toPath
import org.pkl.commons.writeString
import org.pkl.core.OutputFormat
import org.pkl.core.SecurityManagers
import org.pkl.core.util.IoUtils
@@ -157,7 +156,6 @@ person:
)
assertThat(outputFiles).hasSize(1)
@Suppress("HttpUrlsUsage")
checkOutputFile(
outputFiles[0],
"test.plist",
@@ -469,8 +467,8 @@ result = someLib.x
@Test
fun `take input from stdin`() {
val stdin = StringReader(defaultContents)
val stdout = StringWriter()
val stdin = ByteArrayInputStream(defaultContents.toByteArray(StandardCharsets.UTF_8))
val stdout = ByteArrayOutputStream()
val evaluator =
CliEvaluator(
CliEvaluatorOptions(
@@ -481,7 +479,8 @@ result = someLib.x
stdout,
)
evaluator.run()
assertThat(stdout.toString().trim()).isEqualTo(defaultContents.replace("20 + 10", "30").trim())
assertThat(stdout.toString(StandardCharsets.UTF_8).trim())
.isEqualTo(defaultContents.replace("20 + 10", "30").trim())
}
@Test
@@ -1101,9 +1100,9 @@ result = someLib.x
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir),
expression = "foo",
)
val buffer = StringWriter()
CliEvaluator(options, consoleWriter = buffer).run()
assertThat(buffer.toString())
val buffer = ByteArrayOutputStream()
CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo(
"""
new Dynamic { bar = 1 }
@@ -1132,9 +1131,9 @@ result = someLib.x
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir),
expression = "person",
)
val buffer = StringWriter()
CliEvaluator(options, consoleWriter = buffer).run()
assertThat(buffer.toString()).isEqualTo("Person(Frodo)")
val buffer = ByteArrayOutputStream()
CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString(StandardCharsets.UTF_8)).isEqualTo("Person(Frodo)")
}
@Test
@@ -1154,9 +1153,10 @@ result = someLib.x
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir),
expression = "person",
)
val buffer = StringWriter()
CliEvaluator(options, consoleWriter = buffer).run()
assertThat(buffer.toString()).isEqualTo("new Dynamic { friend { name = \"Bilbo\" } }")
val buffer = ByteArrayOutputStream()
CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo("new Dynamic { friend { name = \"Bilbo\" } }")
}
@Test
@@ -1182,9 +1182,9 @@ result = someLib.x
CliEvaluatorOptions(
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir, noProject = true)
)
val buffer = StringWriter()
CliEvaluator(options, consoleWriter = buffer).run()
assertThat(buffer.toString()).isEqualTo("res = 1\n")
val buffer = ByteArrayOutputStream()
CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString(StandardCharsets.UTF_8)).isEqualTo("res = 1\n")
}
@Test
@@ -1215,9 +1215,9 @@ result = someLib.x
)
val options =
CliEvaluatorOptions(CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir))
val buffer = StringWriter()
CliEvaluator(options, consoleWriter = buffer).run()
assertThat(buffer.toString())
val buffer = ByteArrayOutputStream()
CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo(
"""
res {
@@ -1242,7 +1242,7 @@ result = someLib.x
"""
.trimIndent(),
)
val buffer = StringWriter()
val buffer = ByteArrayOutputStream()
val options =
CliEvaluatorOptions(
CliBaseOptions(
@@ -1254,8 +1254,8 @@ result = someLib.x
testPort = packageServer.port,
)
)
CliEvaluator(options, consoleWriter = buffer).run()
assertThat(buffer.toString())
CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo(
"""
res {
@@ -1590,10 +1590,10 @@ result = someLib.x
}
private fun evalToConsole(options: CliEvaluatorOptions): String {
val reader = StringReader("")
val writer = StringWriter()
val reader = ByteArrayInputStream(byteArrayOf())
val writer = ByteArrayOutputStream()
CliEvaluator(options, reader, writer).run()
return writer.toString()
return writer.toString(StandardCharsets.UTF_8)
}
private fun checkOutputFile(file: Path, name: String, contents: String) {

View File

@@ -342,13 +342,22 @@ class JavaCodeGenerator(
var builderSize = 50
val appendBuilder = CodeBlock.builder()
for (propertyName in allProperties.keys) {
for ((propertyName, property) in allProperties) {
builderSize += 50
appendBuilder.addStatement(
"appendProperty(builder, \$S, this.\$N)",
propertyName,
propertyName,
)
if (property.type.isBytesClass) {
appendBuilder.addStatement(
"appendProperty(builder, \$S, \$T.toString(this.\$N))",
propertyName,
Arrays::class.java,
propertyName,
)
} else {
appendBuilder.addStatement(
"appendProperty(builder, \$S, this.\$N)",
propertyName,
propertyName,
)
}
}
builder
@@ -725,6 +734,9 @@ class JavaCodeGenerator(
}
}
private val PType.isBytesClass: Boolean
get() = this is PType.Class && this.pClass.info == PClassInfo.Bytes
private fun PType.toJavaPoetName(nullable: Boolean = false, boxed: Boolean = false): TypeName =
when (this) {
PType.UNKNOWN -> OBJECT.nullableIf(nullable)
@@ -744,6 +756,7 @@ class JavaCodeGenerator(
PClassInfo.Float -> TypeName.DOUBLE.boxIf(boxed).nullableIf(nullable)
PClassInfo.Duration -> DURATION.nullableIf(nullable)
PClassInfo.DataSize -> DATA_SIZE.nullableIf(nullable)
PClassInfo.Bytes -> ArrayTypeName.of(TypeName.BYTE)
PClassInfo.Pair ->
ParameterizedTypeName.get(
PAIR,

View File

@@ -87,6 +87,7 @@ class JavaCodeGeneratorTest {
any: Any
nonNull: NonNull
enum: Direction
bytes: Bytes
}
class Other {
@@ -202,6 +203,7 @@ class JavaCodeGeneratorTest {
name = pigeon
}
_enum = north
bytes = [1, 2, 3, 4]
}
"""
.trimIndent()
@@ -542,6 +544,7 @@ class JavaCodeGeneratorTest {
assertThat(readProperty(propertyTypes, "regex")).isInstanceOf(Pattern::class.java)
assertThat(readProperty(propertyTypes, "any")).isEqualTo(other)
assertThat(readProperty(propertyTypes, "nonNull")).isEqualTo(other)
assertThat(readProperty(propertyTypes, "bytes")).isEqualTo(byteArrayOf(1, 2, 3, 4))
}
private fun readProperty(obj: Any, property: String): Any? =
@@ -2313,6 +2316,7 @@ class JavaCodeGeneratorTest {
other,
other,
enumValue,
byteArrayOf(1, 2, 3, 4),
)
return other to propertyTypes

View File

@@ -4,6 +4,7 @@ import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -86,6 +87,8 @@ public final class Mod {
public final @NonNull Direction _enum;
public final byte[] bytes;
public PropertyTypes(@Named("boolean") boolean _boolean, @Named("int") long _int,
@Named("float") double _float, @Named("string") @NonNull String string,
@Named("duration") @NonNull Duration duration,
@@ -106,7 +109,7 @@ public final class Mod {
@Named("container2") @NonNull Map<@NonNull String, @NonNull Other> container2,
@Named("other") @NonNull Other other, @Named("regex") @NonNull Pattern regex,
@Named("any") Object any, @Named("nonNull") @NonNull Object nonNull,
@Named("enum") @NonNull Direction _enum) {
@Named("enum") @NonNull Direction _enum, @Named("bytes") byte[] bytes) {
this._boolean = _boolean;
this._int = _int;
this._float = _float;
@@ -134,114 +137,119 @@ public final class Mod {
this.any = any;
this.nonNull = nonNull;
this._enum = _enum;
this.bytes = bytes;
}
public PropertyTypes withBoolean(boolean _boolean) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withInt(long _int) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withFloat(double _float) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withString(@NonNull String string) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withDuration(@NonNull Duration duration) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withDurationUnit(@NonNull DurationUnit durationUnit) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withDataSize(@NonNull DataSize dataSize) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withDataSizeUnit(@NonNull DataSizeUnit dataSizeUnit) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withNullable(String nullable) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withNullable2(String nullable2) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withPair(@NonNull Pair<Object, Object> pair) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withPair2(@NonNull Pair<@NonNull String, @NonNull Other> pair2) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withColl(@NonNull Collection<Object> coll) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withColl2(@NonNull Collection<@NonNull Other> coll2) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withList(@NonNull List<Object> list) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withList2(@NonNull List<@NonNull Other> list2) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withSet(@NonNull Set<Object> set) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withSet2(@NonNull Set<@NonNull Other> set2) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withMap(@NonNull Map<Object, Object> map) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withMap2(@NonNull Map<@NonNull String, @NonNull Other> map2) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withContainer(@NonNull Map<Object, Object> container) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withContainer2(@NonNull Map<@NonNull String, @NonNull Other> container2) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withOther(@NonNull Other other) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withRegex(@NonNull Pattern regex) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withAny(Object any) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withNonNull(@NonNull Object nonNull) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withEnum(@NonNull Direction _enum) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
public PropertyTypes withBytes(byte[] bytes) {
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum, bytes);
}
@Override
@@ -277,6 +285,7 @@ public final class Mod {
if (!Objects.equals(this.any, other.any)) return false;
if (!Objects.equals(this.nonNull, other.nonNull)) return false;
if (!Objects.equals(this._enum, other._enum)) return false;
if (!Objects.equals(this.bytes, other.bytes)) return false;
return true;
}
@@ -310,12 +319,13 @@ public final class Mod {
result = 31 * result + Objects.hashCode(this.any);
result = 31 * result + Objects.hashCode(this.nonNull);
result = 31 * result + Objects.hashCode(this._enum);
result = 31 * result + Objects.hashCode(this.bytes);
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(1400);
StringBuilder builder = new StringBuilder(1450);
builder.append(PropertyTypes.class.getSimpleName()).append(" {");
appendProperty(builder, "_boolean", this._boolean);
appendProperty(builder, "_int", this._int);
@@ -344,6 +354,7 @@ public final class Mod {
appendProperty(builder, "any", this.any);
appendProperty(builder, "nonNull", this.nonNull);
appendProperty(builder, "_enum", this._enum);
appendProperty(builder, "bytes", Arrays.toString(this.bytes));
builder.append("\n}");
return builder.toString();
}

View File

@@ -226,6 +226,9 @@ class KotlinCodeGenerator(
fun PClass.Property.isRegex(): Boolean =
(this.type as? PType.Class)?.pClass?.info == PClassInfo.Regex
fun PClass.Property.isByteArray(): Boolean =
(this.type as? PType.Class)?.pClass?.info == PClassInfo.Bytes
fun generateConstructor(): FunSpec {
val builder = FunSpec.constructorBuilder()
for ((name, property) in allProperties) {
@@ -302,12 +305,20 @@ class KotlinCodeGenerator(
.addStatement("other as %T", kotlinPoetClassName)
for ((propertyName, property) in allProperties) {
val accessor = if (property.isRegex()) "%N.pattern" else "%N"
builder.addStatement(
"if (this.$accessor != other.$accessor) return false",
propertyName,
propertyName,
)
if (property.isByteArray()) {
builder.addStatement(
"if (!this.%N.contentEquals(other.%N)) return false",
propertyName,
propertyName,
)
} else {
val accessor = if (property.isRegex()) "%N.pattern" else "%N"
builder.addStatement(
"if (this.$accessor != other.$accessor) return false",
propertyName,
propertyName,
)
}
}
builder.addStatement("return true")
@@ -322,14 +333,18 @@ class KotlinCodeGenerator(
.addStatement("var result = 1")
for ((propertyName, property) in allProperties) {
val accessor = if (property.isRegex()) "this.%N.pattern" else "this.%N"
// use Objects.hashCode() because Kotlin's Any?.hashCode()
// doesn't work for platform types (will get NPE if null)
builder.addStatement(
"result = 31 * result + %T.hashCode($accessor)",
Objects::class,
propertyName,
)
if (property.isByteArray()) {
builder.addStatement("result = 31 * result + this.%N.contentHashCode()", propertyName)
} else {
val accessor = if (property.isRegex()) "this.%N.pattern" else "this.%N"
// use Objects.hashCode() because Kotlin's Any?.hashCode()
// doesn't work for platform types (will get NPE if null)
builder.addStatement(
"result = 31 * result + %T.hashCode($accessor)",
Objects::class,
propertyName,
)
}
}
builder.addStatement("return result")
@@ -347,10 +362,15 @@ class KotlinCodeGenerator(
.apply {
add("%L", pClass.toKotlinPoetName().simpleName)
add("(")
for ((index, propertyName) in allProperties.keys.withIndex()) {
for ((index, entry) in allProperties.entries.withIndex()) {
val (propertyName, property) = entry
add(if (index == 0) "%L" else ", %L", propertyName)
add("=$")
add("%N", propertyName)
if (property.isByteArray()) {
add("=\${%T.toString(%L)}", Arrays::class, propertyName)
} else {
add("=$")
add("%N", propertyName)
}
}
add(")")
}
@@ -488,16 +508,16 @@ class KotlinCodeGenerator(
builder.addKdoc(renderAsKdoc(docComment))
}
var hasRegex = false
var hasRegexOrByteArray = false
for ((name, property) in properties) {
hasRegex = hasRegex || property.isRegex()
hasRegexOrByteArray = hasRegexOrByteArray || property.isRegex() || property.isByteArray()
builder.addProperty(generateProperty(name, property))
}
// kotlin.text.Regex (and java.util.regex.Pattern) defines equality as identity.
// To match Pkl semantics and compare regexes by their String pattern,
// override equals and hashCode if the data class has a property of type Regex.
if (hasRegex) {
// Regex and ByteArray define equaltiy as identity.
// To match Pkl semantics, override equals and hashCode if the data class has a property of
// type Regex or ByteArray.
if (hasRegexOrByteArray) {
builder.addFunction(generateEqualsMethod()).addFunction(generateHashCodeMethod())
}
@@ -632,6 +652,7 @@ class KotlinCodeGenerator(
PClassInfo.Float -> DOUBLE
PClassInfo.Duration -> DURATION
PClassInfo.DataSize -> DATA_SIZE
PClassInfo.Bytes -> BYTE_ARRAY
PClassInfo.Pair ->
KOTLIN_PAIR.parameterizedBy(
if (typeArguments.isEmpty()) ANY_NULL else typeArguments[0].toKotlinPoetName(),

View File

@@ -120,6 +120,7 @@ class KotlinCodeGeneratorTest {
any: Any
nonNull: NonNull
enum: Direction
bytes: Bytes
}
open class Other {
@@ -193,7 +194,6 @@ class KotlinCodeGeneratorTest {
@Test
fun testToString() {
val (_, propertyTypes) = instantiateOtherAndPropertyTypes()
assertThat(propertyTypes.toString())
.isEqualTo(
"""PropertyTypes(boolean=true, int=42, float=42.3, string=string, duration=5.min, """ +
@@ -204,7 +204,8 @@ class KotlinCodeGeneratorTest {
"""set2=[Other(name=pigeon)], map={1=one, 2=two}, map2={one=Other(name=pigeon), """ +
"""two=Other(name=pigeon)}, container={1=one, 2=two}, container2={one=Other(name=pigeon), """ +
"""two=Other(name=pigeon)}, other=Other(name=pigeon), regex=(i?)\w*, any=Other(name=pigeon), """ +
"""nonNull=Other(name=pigeon), enum=north)"""
"""nonNull=Other(name=pigeon), enum=north, """ +
"""bytes=[1, 2, 3, 4])"""
)
}
@@ -412,6 +413,7 @@ class KotlinCodeGeneratorTest {
assertThat(readProperty(propertyTypes, "regex")).isInstanceOf(Regex::class.java)
assertThat(readProperty(propertyTypes, "any")).isEqualTo(other)
assertThat(readProperty(propertyTypes, "nonNull")).isEqualTo(other)
assertThat(readProperty(propertyTypes, "bytes")).isEqualTo(byteArrayOf(1, 2, 3, 4))
}
private fun readProperty(receiver: Any, name: String): Any? {
@@ -571,6 +573,25 @@ class KotlinCodeGeneratorTest {
.contains("result = 31 * result + Objects.hashCode(this.name.pattern)")
}
@Test
fun `data class with ByteArray property has custom equals and hashCode methods`() {
val kotlinCode =
generateKotlinCode(
"""
module my.mod
class Foo {
bytes: Bytes
}
"""
.trimIndent()
)
assertThat(kotlinCode)
.contains("if (!this.bytes.contentEquals(other.bytes)) return false")
.contains("result = 31 * result + this.bytes.contentHashCode()")
}
@Test
fun `recursive types`() {
val kotlinCode =
@@ -2078,6 +2099,7 @@ class KotlinCodeGeneratorTest {
other,
other,
enumValue,
byteArrayOf(1, 2, 3, 4),
)
return other to propertyTypes

View File

@@ -1,8 +1,10 @@
package my
import java.util.Arrays
import java.util.Objects
import kotlin.Any
import kotlin.Boolean
import kotlin.ByteArray
import kotlin.Double
import kotlin.Int
import kotlin.Long
@@ -46,7 +48,8 @@ object Mod {
open val regex: Regex,
open val any: Any?,
open val nonNull: Any,
open val enum: Direction
open val enum: Direction,
open val bytes: ByteArray
) {
open fun copy(
boolean: Boolean = this.boolean,
@@ -75,10 +78,11 @@ object Mod {
regex: Regex = this.regex,
any: Any? = this.any,
nonNull: Any = this.nonNull,
enum: Direction = this.enum
enum: Direction = this.enum,
bytes: ByteArray = this.bytes
): PropertyTypes = PropertyTypes(boolean, int, float, string, duration, durationUnit, dataSize,
dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map,
map2, container, container2, other, regex, any, nonNull, enum)
map2, container, container2, other, regex, any, nonNull, enum, bytes)
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -111,6 +115,7 @@ object Mod {
if (this.any != other.any) return false
if (this.nonNull != other.nonNull) return false
if (this.enum != other.enum) return false
if (!this.bytes.contentEquals(other.bytes)) return false
return true
}
@@ -143,11 +148,12 @@ object Mod {
result = 31 * result + Objects.hashCode(this.any)
result = 31 * result + Objects.hashCode(this.nonNull)
result = 31 * result + Objects.hashCode(this.enum)
result = 31 * result + this.bytes.contentHashCode()
return result
}
override fun toString(): String =
"""PropertyTypes(boolean=$boolean, int=$int, float=$float, string=$string, duration=$duration, durationUnit=$durationUnit, dataSize=$dataSize, dataSizeUnit=$dataSizeUnit, nullable=$nullable, nullable2=$nullable2, pair=$pair, pair2=$pair2, coll=$coll, coll2=$coll2, list=$list, list2=$list2, set=$set, set2=$set2, map=$map, map2=$map2, container=$container, container2=$container2, other=$other, regex=$regex, any=$any, nonNull=$nonNull, enum=$enum)"""
"""PropertyTypes(boolean=$boolean, int=$int, float=$float, string=$string, duration=$duration, durationUnit=$durationUnit, dataSize=$dataSize, dataSizeUnit=$dataSizeUnit, nullable=$nullable, nullable2=$nullable2, pair=$pair, pair2=$pair2, coll=$coll, coll2=$coll2, list=$list, list2=$list2, set=$set, set2=$set2, map=$map, map2=$map2, container=$container, container2=$container2, other=$other, regex=$regex, any=$any, nonNull=$nonNull, enum=$enum, bytes=${Arrays.toString(bytes)})"""
}
open class Other(

View File

@@ -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.
@@ -291,7 +291,8 @@ public final class Conversions {
Conversion.of(PClassInfo.Class, Object.class, Converter.identity()),
Conversion.of(PClassInfo.Regex, Pattern.class, Converter.identity()),
Conversion.of(PClassInfo.Regex, Object.class, Converter.identity()),
Conversion.of(PClassInfo.Null, PNull.class, Converter.identity())
Conversion.of(PClassInfo.Null, PNull.class, Converter.identity()),
Conversion.of(PClassInfo.Bytes, byte[].class, Converter.identity())
// PClassInfo.Null -> Object.class is covered by PNullToAny (returns null rather than
// PNull.getInstance())
);

View File

@@ -0,0 +1,46 @@
/*
* 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.config.java.mapper;
import static org.assertj.core.api.Assertions.assertThat;
import static org.pkl.core.ModuleSource.modulePath;
import org.junit.jupiter.api.Test;
import org.pkl.core.Evaluator;
import org.pkl.core.PModule;
public class BytesToByteArrayTest {
private static final Evaluator evaluator = Evaluator.preconfigured();
private static final PModule module =
evaluator.evaluate(modulePath("org/pkl/config/java/mapper/BytesToByteArrayTest.pkl"));
private static final ValueMapper mapper = ValueMapperBuilder.preconfigured().build();
@Test
public void ex1() {
var ex1 = module.getProperty("ex1");
var mapped = mapper.map(ex1, Types.arrayOf(byte.class));
assertThat(mapped).isEqualTo(new byte[] {});
}
@Test
public void ex2() {
var ex2 = module.getProperty("ex2");
var mapped = mapper.map(ex2, Types.arrayOf(byte.class));
assertThat(mapped).isEqualTo(new byte[] {1, 2, 3, 4});
}
}

View File

@@ -0,0 +1,2 @@
ex1 = Bytes()
ex2 = Bytes(1, 2, 3, 4)

View File

@@ -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,6 +53,15 @@ public interface Evaluator extends AutoCloseable {
*/
String evaluateOutputText(ModuleSource moduleSource);
/**
* Evaluates a module's {@code output.bytes} property.
*
* @throws PklException if an error occurs during evaluation
* @throws IllegalStateException if this evaluator has already been closed
* @since 0.29.0
*/
byte[] evaluateOutputBytes(ModuleSource moduleSource);
/**
* Evaluates a module's {@code output.value} property.
*
@@ -143,6 +152,10 @@ public interface Evaluator extends AutoCloseable {
* <td>TypeAlias</td>
* <td>{@link TypeAlias}</td>
* </tr>
* <tr>
* <td>Bytes</td>
* <td>{@code byte[]}</td>
* </tr>
* </tbody>
* </table>
*

View File

@@ -144,6 +144,16 @@ public class EvaluatorImpl implements Evaluator {
});
}
public byte[] evaluateOutputBytes(ModuleSource moduleSource) {
return doEvaluate(
moduleSource,
(module) -> {
var output = readModuleOutput(module);
var vmBytes = VmUtils.readBytesProperty(output);
return vmBytes.export();
});
}
@Override
public Object evaluateOutputValue(ModuleSource moduleSource) {
return doEvaluate(
@@ -183,32 +193,31 @@ public class EvaluatorImpl implements Evaluator {
@Override
public Object evaluateExpression(ModuleSource moduleSource, String expression) {
// optimization: if the expression is `output.text` or `output.value` (the common cases), read
// members directly instead of creating new truffle nodes.
if (expression.equals("output.text")) {
return evaluateOutputText(moduleSource);
}
if (expression.equals("output.value")) {
return evaluateOutputValue(moduleSource);
}
return doEvaluate(
moduleSource,
(module) -> {
var expressionResult =
VmUtils.evaluateExpression(module, expression, securityManager, moduleResolver);
if (expressionResult instanceof VmValue value) {
value.force(false);
return value.export();
}
return expressionResult;
});
// optimization: if the expression is `output.text`, `output.value` or `output.bytes` (the
// common cases), read members directly instead of creating new truffle nodes.
return switch (expression) {
case "output.text" -> evaluateOutputText(moduleSource);
case "output.value" -> evaluateOutputValue(moduleSource);
case "output.bytes" -> evaluateOutputBytes(moduleSource);
default ->
doEvaluate(
moduleSource,
(module) -> {
var expressionResult =
VmUtils.evaluateExpression(module, expression, securityManager, moduleResolver);
if (expressionResult instanceof VmValue value) {
value.force(false);
return value.export();
}
return expressionResult;
});
};
}
@Override
public String evaluateExpressionString(ModuleSource moduleSource, String expression) {
// optimization: if the expression is `output.text` (the common case), read members
// directly
// instead of creating new truffle nodes.
// directly instead of creating new truffle nodes.
if (expression.equals("output.text")) {
return evaluateOutputText(moduleSource);
}
@@ -280,6 +289,10 @@ public class EvaluatorImpl implements Evaluator {
return doEvaluate(() -> VmUtils.readTextProperty(fileOutput));
}
byte[] evaluateOutputBytes(VmTyped fileOutput) {
return doEvaluate(() -> VmUtils.readBytesProperty(fileOutput).export());
}
private <T> T doEvaluate(Supplier<T> supplier) {
@Nullable TimeoutTask timeoutTask = null;
logger.clear();

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,14 @@ public interface FileOutput {
/**
* Returns the text content of this file.
*
* @throws PklException if an error occurs during evaluation
* @throws PklException if an error occurs during evaluation.
*/
String getText();
/**
* Returns the byte contents of this file.
*
* @throws PklException if an error occurs during evaluation.
*/
byte[] getBytes();
}

View File

@@ -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.
@@ -44,4 +44,16 @@ final class FileOutputImpl implements FileOutput {
throw new PklBugException(e);
}
}
@Override
public byte[] getBytes() {
try {
return evaluator.evaluateOutputBytes(fileOutput);
} catch (PolyglotException e) {
if (e.isCancelled()) {
throw new PklException("The evaluator is no longer available", e);
}
throw new PklBugException(e);
}
}
}

View File

@@ -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.
@@ -102,6 +102,12 @@ final class JsonRenderer implements ValueRenderer {
String.format("Values of type `DataSize` cannot be rendered as JSON. Value: %s", value));
}
@Override
public void visitBytes(byte[] value) {
throw new RendererException(
String.format("Values of type `Bytes` cannot be rendered as JSON. Value: %s", value));
}
@Override
public void visitPair(Pair<?, ?> value) {
try {

View File

@@ -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.
@@ -49,6 +49,7 @@ public final class PClassInfo<T> implements Serializable {
public static final PClassInfo<Double> Float = pklBaseClassInfo("Float", double.class);
public static final PClassInfo<Duration> Duration = pklBaseClassInfo("Duration", Duration.class);
public static final PClassInfo<DataSize> DataSize = pklBaseClassInfo("DataSize", DataSize.class);
public static final PClassInfo<byte[]> Bytes = pklBaseClassInfo("Bytes", byte[].class);
public static final PClassInfo<Pair> Pair = pklBaseClassInfo("Pair", Pair.class);
public static final PClassInfo<Void> Collection = pklBaseClassInfo("Collection", Void.class);
public static final PClassInfo<ArrayList> List = pklBaseClassInfo("List", ArrayList.class);
@@ -115,6 +116,7 @@ public final class PClassInfo<T> implements Serializable {
if (value instanceof Set) return (PClassInfo<T>) Set;
if (value instanceof Map) return (PClassInfo<T>) Map;
if (value instanceof Pattern) return (PClassInfo<T>) Regex;
if (value instanceof byte[]) return (PClassInfo<T>) Bytes;
throw new IllegalArgumentException("Not a Pkl value: " + value);
}

View File

@@ -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.
@@ -21,6 +21,7 @@ import java.io.Writer;
import java.util.*;
import java.util.regex.Pattern;
import org.pkl.core.util.ArrayCharEscaper;
import org.pkl.core.util.ByteArrayUtils;
// To instantiate this class, use ValueRenderers.plist().
final class PListRenderer implements ValueRenderer {
@@ -136,6 +137,13 @@ final class PListRenderer implements ValueRenderer {
value));
}
@Override
public void visitBytes(byte[] value) {
write("<data>");
write(ByteArrayUtils.base64(value));
write("</data>");
}
@Override
public void visitPair(Pair<?, ?> value) {
doVisitIterable(value, false);

View File

@@ -92,6 +92,21 @@ final class PcfRenderer implements ValueRenderer {
write(value.toString());
}
@Override
public void visitBytes(byte[] value) {
write("Bytes(\"");
var isFirst = true;
for (var byt : value) {
if (isFirst) {
isFirst = false;
} else {
write(", ");
}
write(Integer.valueOf(Byte.toUnsignedInt(byt)).toString());
}
write(")");
}
@Override
public void visitPair(Pair<?, ?> value) {
doVisitIterable(value, "Pair(");

View File

@@ -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.
@@ -106,6 +106,13 @@ final class PropertiesRenderer implements ValueRenderer {
"Values of type `DataSize` cannot be rendered as Properties. Value: %s", value));
}
@Override
public String convertBytes(byte[] value) {
throw new RendererException(
String.format(
"Values of type `Bytes` cannot be rendered as Properties. Value: %s", value));
}
@Override
public String convertPair(Pair<?, ?> value) {
throw new RendererException(

View File

@@ -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.
@@ -37,6 +37,8 @@ public interface ValueConverter<T> {
T convertDataSize(DataSize value);
T convertBytes(byte[] value);
T convertPair(Pair<?, ?> value);
T convertList(List<?> value);

View File

@@ -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,6 +53,10 @@ public interface ValueVisitor {
visitDefault(value);
}
default void visitBytes(byte[] value) {
visitDefault(value);
}
default void visitPair(Pair<?, ?> value) {
visitDefault(value);
}
@@ -108,6 +112,8 @@ public interface ValueVisitor {
visitMap(map);
} else if (value instanceof Pattern pattern) {
visitRegex(pattern);
} else if (value instanceof byte[] bytes) {
visitBytes(bytes);
} else {
throw new IllegalArgumentException("Cannot visit value with unexpected type: " + value);
}

View File

@@ -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.
@@ -151,6 +151,12 @@ final class YamlRenderer implements ValueRenderer {
String.format("Values of type `DataSize` cannot be rendered as YAML. Value: %s", value));
}
@Override
public void visitBytes(byte[] value) {
throw new RendererException(
String.format("Values of type `Bytes` cannot be rendered as YAML. Value: %s", value));
}
@Override
public void visitPair(Pair<?, ?> value) {
doVisitIterable(value, null);

View File

@@ -0,0 +1,41 @@
/*
* 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.ast;
import com.oracle.truffle.api.frame.VirtualFrame;
public class ByteConstantValueNode extends ExpressionNode implements ConstantNode {
private final byte value;
public ByteConstantValueNode(byte value) {
this.value = value;
}
@Override
public Long getValue() {
return (long) value;
}
public byte getByteValue() {
return value;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
return getValue();
}
}

View File

@@ -29,6 +29,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.graalvm.collections.EconomicMap;
@@ -37,6 +38,7 @@ import org.pkl.core.PklBugException;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.TypeParameter;
import org.pkl.core.TypeParameter.Variance;
import org.pkl.core.ast.ByteConstantValueNode;
import org.pkl.core.ast.ConstantNode;
import org.pkl.core.ast.ConstantValueNode;
import org.pkl.core.ast.ExpressionNode;
@@ -77,6 +79,7 @@ import org.pkl.core.ast.expression.generator.GeneratorSpreadNodeGen;
import org.pkl.core.ast.expression.generator.GeneratorWhenNode;
import org.pkl.core.ast.expression.generator.RestoreForBindingsNode;
import org.pkl.core.ast.expression.literal.AmendModuleNodeGen;
import org.pkl.core.ast.expression.literal.BytesLiteralNode;
import org.pkl.core.ast.expression.literal.CheckIsAnnotationClassNode;
import org.pkl.core.ast.expression.literal.ConstantEntriesLiteralNodeGen;
import org.pkl.core.ast.expression.literal.ElementsEntriesLiteralNodeGen;
@@ -160,6 +163,7 @@ import org.pkl.core.packages.PackageLoadError;
import org.pkl.core.runtime.BaseModule;
import org.pkl.core.runtime.ModuleInfo;
import org.pkl.core.runtime.ModuleResolver;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmContext;
import org.pkl.core.runtime.VmDataSize;
@@ -524,9 +528,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
}
}
@Override
public IntLiteralNode visitIntLiteralExpr(IntLiteralExpr expr) {
var section = createSourceSection(expr);
private <T> T parseNumber(IntLiteralExpr expr, BiFunction<String, Integer, T> parser) {
var text = remove_(expr.getNumber());
var radix = 10;
@@ -547,11 +549,17 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
// also moves negation from runtime to parse time
text = "-" + text;
}
return parser.apply(text, radix);
}
@Override
public IntLiteralNode visitIntLiteralExpr(IntLiteralExpr expr) {
var section = createSourceSection(expr);
try {
var num = Long.parseLong(text, radix);
var num = parseNumber(expr, Long::parseLong);
return new IntLiteralNode(section, num);
} catch (NumberFormatException e) {
var text = expr.getNumber();
throw exceptionBuilder().evalError("intTooLarge", text).withSourceSection(section).build();
}
}
@@ -637,8 +645,8 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
}
// TODO: make sure that no user-defined List/Set/Map method is in scope
// TODO: support qualified calls (e.g., `import "pkl:base"; x = base.List()/Set()/Map()`) for
// correctness
// TODO: support qualified calls (e.g., `import "pkl:base"; x =
// base.List()/Set()/Map()/Bytes()`) for correctness
if (identifier == org.pkl.core.runtime.Identifier.LIST) {
return doVisitListLiteral(expr, argList);
}
@@ -651,6 +659,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
return doVisitMapLiteral(expr, argList);
}
if (identifier == org.pkl.core.runtime.Identifier.BYTES_CONSTRUCTOR) {
return doVisitBytesLiteral(expr, argList);
}
var scope = symbolTable.getCurrentScope();
return new ResolveMethodNode(
@@ -1083,6 +1095,18 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
: new MapLiteralNode(createSourceSection(expr), keyAndValueNodes.first);
}
private ExpressionNode doVisitBytesLiteral(Expr expr, ArgumentList argList) {
var elementNodes = createCollectionArgumentBytesNodes(argList);
if (elementNodes.first.length == 0) {
return new ConstantValueNode(VmBytes.EMPTY);
}
return elementNodes.second
? new ConstantValueNode(
createSourceSection(expr), VmBytes.createFromConstantNodes(elementNodes.first))
: new BytesLiteralNode(createSourceSection(expr), elementNodes.first);
}
private Pair<ExpressionNode[], Boolean> createCollectionArgumentNodes(ArgumentList exprs) {
var args = exprs.getArguments();
var elementNodes = new ExpressionNode[args.size()];
@@ -1097,6 +1121,32 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
return Pair.of(elementNodes, isConstantNodes);
}
private Pair<ExpressionNode[], Boolean> createCollectionArgumentBytesNodes(ArgumentList exprs) {
var args = exprs.getArguments();
var expressionNodes = new ExpressionNode[args.size()];
var isAllByteLiterals = true;
for (var i = 0; i < args.size(); i++) {
var expr = args.get(i);
if (expr instanceof IntLiteralExpr intLiteralExpr && isAllByteLiterals) {
try {
var byt = parseNumber(intLiteralExpr, Byte::parseByte);
expressionNodes[i] = new ByteConstantValueNode(byt);
} catch (NumberFormatException e) {
// proceed with initializing a constant value node; we'll throw an error inside
// BytesLiteralNode.
isAllByteLiterals = false;
expressionNodes[i] = visitExpr(expr);
}
} else {
isAllByteLiterals = false;
expressionNodes[i] = visitExpr(expr);
}
}
return Pair.of(expressionNodes, isAllByteLiterals);
}
public GeneratorMemberNode visitObjectMember(org.pkl.parser.syntax.ObjectMember member) {
return (GeneratorMemberNode) member.accept(this);
}

View File

@@ -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.
@@ -78,4 +78,9 @@ public abstract class AdditionNode extends BinaryExpressionNode {
protected VmMap eval(VmMap left, VmMap right) {
return left.concatenate(right);
}
@Specialization
protected VmBytes eval(VmBytes left, VmBytes right) {
return left.concatenate(right);
}
}

View File

@@ -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.
@@ -98,6 +98,18 @@ public abstract class SubscriptNode extends BinaryExpressionNode {
return readMember(dynamic, key, callNode);
}
@Specialization
protected long eval(VmBytes receiver, long index) {
if (index < 0 || index >= receiver.getLength()) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder()
.evalError("elementIndexOutOfRange", index, 0, receiver.getLength() - 1)
.withProgramValue("Value", receiver)
.build();
}
return receiver.get(index);
}
private Object readMember(VmObject object, Object key, IndirectCallNode callNode) {
var result = VmUtils.readMemberOrNull(object, key, callNode);
if (result != null) return result;

View File

@@ -118,6 +118,14 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
}
}
@Specialization
protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmBytes iterable) {
long idx = 0;
for (var byt : iterable.getBytes()) {
executeIteration(frame, parent, data, idx++, (long) byt);
}
}
@Fallback
@SuppressWarnings("unused")
protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Object iterable) {

View File

@@ -29,6 +29,7 @@ import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.runtime.BaseModule;
import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.Iterators.TruffleIterator;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmCollection;
import org.pkl.core.runtime.VmDynamic;
@@ -161,6 +162,16 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
doEvalIntSeq(frame, parent, data, iterable);
}
@Specialization
protected void eval(VirtualFrame frame, VmObject parent, ObjectData data, VmBytes iterable) {
doEvalBytes(frame, parent.getVmClass(), data, iterable);
}
@Specialization
protected void eval(VirtualFrame frame, VmClass parent, ObjectData data, VmBytes iterable) {
doEvalBytes(frame, parent, data, iterable);
}
@Fallback
@SuppressWarnings("unused")
protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Object iterable) {
@@ -266,6 +277,18 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
spreadIterable(frame, data, iterable);
}
private void doEvalBytes(VirtualFrame frame, VmClass parent, ObjectData data, VmBytes iterable) {
if (isTypedObjectClass(parent) || parent == getMappingClass()) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder()
.evalError("cannotSpreadObject", iterable.getVmClass(), parent)
.withHint("`Bytes` can only be spread into objects of type `Dynamic` and `Listing`.")
.withProgramValue("Value", iterable)
.build();
}
spreadIterable(frame, data, iterable);
}
private void cannotHaveMember(VmClass clazz, ObjectMember member) {
CompilerDirectives.transferToInterpreter();
var builder = exceptionBuilder();

View File

@@ -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.ast.expression.literal;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.UInt8TypeAliasTypeNode;
import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmUtils;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "Bytes()")
public final class BytesLiteralNode extends ExpressionNode {
@Children private final ExpressionNode[] elements;
@Child @LateInit private TypeNode typeNode;
public BytesLiteralNode(SourceSection sourceSection, ExpressionNode[] elements) {
super(sourceSection);
this.elements = elements;
}
private TypeNode getTypeNode() {
if (typeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
typeNode = new UInt8TypeAliasTypeNode();
}
return typeNode;
}
@Override
@ExplodeLoop
public Object executeGeneric(VirtualFrame frame) {
var bytes = new byte[elements.length];
var typeNode = getTypeNode();
for (var i = 0; i < elements.length; i++) {
var elem = elements[i];
try {
var result = (Long) typeNode.execute(frame, elem.executeGeneric(frame));
bytes[i] = result.byteValue();
} catch (VmTypeMismatchException err) {
// optimization: don't create a new stack frame to check the type, but pretend that one
// exists.
err.putInsertedStackFrame(
getRootNode().getCallTarget(),
VmUtils.createStackFrame(elem.getSourceSection(), getRootNode().getName()));
throw err.toVmException();
}
}
return new VmBytes(bytes);
}
}

View File

@@ -2153,52 +2153,71 @@ public abstract class TypeNode extends PklNode {
}
}
public static final class UIntTypeAliasTypeNode extends IntSlotTypeNode {
private final VmTypeAlias typeAlias;
private final long mask;
public UIntTypeAliasTypeNode(VmTypeAlias typeAlias, long mask) {
protected abstract static class IntMaskSlotTypeNode extends IntSlotTypeNode {
protected final long mask;
IntMaskSlotTypeNode(long mask) {
super(VmUtils.unavailableSourceSection());
this.typeAlias = typeAlias;
this.mask = mask;
}
@Override
protected Object executeLazily(VirtualFrame frame, Object value) {
protected final Object executeLazily(VirtualFrame frame, Object value) {
var typealias = getVmTypeAlias();
assert typealias != null;
if (value instanceof Long l) {
if ((l & mask) == l) return value;
throw new VmTypeMismatchException.Constraint(typeAlias.getConstraintSection(), value);
throw new VmTypeMismatchException.Constraint(typealias.getConstraintSection(), value);
}
throw new VmTypeMismatchException.Simple(
typeAlias.getBaseTypeSection(), value, BaseModule.getIntClass());
typealias.getBaseTypeSection(), value, BaseModule.getIntClass());
}
@Override
public VmClass getVmClass() {
public final VmClass getVmClass() {
return BaseModule.getIntClass();
}
@Override
public final VmTyped getMirror() {
return MirrorFactories.typeAliasTypeFactory.create(this);
}
@Override
public final boolean doIsEquivalentTo(TypeNode other) {
return other instanceof UIntTypeAliasTypeNode aliasTypeNode && mask == aliasTypeNode.mask;
}
@Override
protected final boolean acceptTypeNode(TypeNodeConsumer consumer) {
return consumer.accept(this);
}
}
public static final class UIntTypeAliasTypeNode extends IntMaskSlotTypeNode {
private final VmTypeAlias typeAlias;
public UIntTypeAliasTypeNode(VmTypeAlias typeAlias, long mask) {
super(mask);
this.typeAlias = typeAlias;
}
@Override
public VmTypeAlias getVmTypeAlias() {
return typeAlias;
}
}
@Override
public VmTyped getMirror() {
return MirrorFactories.typeAliasTypeFactory.create(this);
public static final class UInt8TypeAliasTypeNode extends IntMaskSlotTypeNode {
public UInt8TypeAliasTypeNode() {
super(0x00000000000000FFL);
}
@Override
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof UIntTypeAliasTypeNode aliasTypeNode && mask == aliasTypeNode.mask;
}
@Override
protected boolean acceptTypeNode(TypeNodeConsumer consumer) {
return consumer.accept(this);
public VmTypeAlias getVmTypeAlias() {
return BaseModule.getUInt8TypeAlias();
}
}

View File

@@ -174,7 +174,7 @@ public abstract class UnresolvedTypeNode extends PklNode {
case "Int8":
return new Int8TypeAliasTypeNode();
case "UInt8":
return new UIntTypeAliasTypeNode(alias, 0x00000000000000FFL);
return new UInt8TypeAliasTypeNode();
case "Int16":
return new Int16TypeAliasTypeNode();
case "UInt16":

View File

@@ -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.
@@ -17,7 +17,7 @@ package org.pkl.core.resource;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.pkl.core.util.ByteArrayUtils;
/** An external (file, HTTP, etc.) resource. */
public record Resource(URI uri, byte[] bytes) {
@@ -44,6 +44,6 @@ public record Resource(URI uri, byte[] bytes) {
/** Returns the content of this resource in Base64. */
public String getBase64() {
return Base64.getEncoder().encodeToString(bytes);
return ByteArrayUtils.base64(bytes);
}
}

View File

@@ -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.
@@ -71,6 +71,10 @@ public final class BaseModule extends StdLibModule {
return DataSizeClass.instance;
}
public static VmClass getBytesClass() {
return BytesClass.instance;
}
public static VmClass getIntSeqClass() {
return IntSeqClass.instance;
}
@@ -219,6 +223,10 @@ public final class BaseModule extends StdLibModule {
return MixinTypeAlias.instance;
}
public static VmTypeAlias getUInt8TypeAlias() {
return UInt8TypeAlias.instance;
}
private static final class AnyClass {
static final VmClass instance = loadClass("Any");
}
@@ -259,6 +267,10 @@ public final class BaseModule extends StdLibModule {
static final VmClass instance = loadClass("DataSize");
}
private static final class BytesClass {
static final VmClass instance = loadClass("Bytes");
}
private static final class IntSeqClass {
static final VmClass instance = loadClass("IntSeq");
}
@@ -383,6 +395,10 @@ public final class BaseModule extends StdLibModule {
static final VmTypeAlias instance = loadTypeAlias("Int32");
}
private static final class UInt8TypeAlias {
static final VmTypeAlias instance = loadTypeAlias("UInt8");
}
private static final class MixinTypeAlias {
static final VmTypeAlias instance = loadTypeAlias("Mixin");
}

View File

@@ -75,6 +75,11 @@ public final class Identifier implements Comparable<Identifier> {
// XmlCData}
public static final Identifier TEXT = get("text");
public static final Identifier BYTES_CONSTRUCTOR = get("Bytes");
// members of pkl.base#{FileOutput}
public static final Identifier BYTES = get("bytes");
// members of pkl.base#ModuleOutput, pkl.base#Resource, pkl.base#String
public static final Identifier BASE64 = get("base64");

View File

@@ -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 ResourceManager {
new VmObjectFactory<Resource>(BaseModule::getResourceClass)
.addProperty("uri", resource -> resource.uri().toString())
.addProperty("text", Resource::getText)
.addProperty("base64", Resource::getBase64);
.addProperty("bytes", resource -> new VmBytes(resource.bytes()));
}
@TruffleBoundary

View File

@@ -0,0 +1,208 @@
/*
* 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 com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.CompilerDirectives.ValueType;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.PrimitiveIterator;
import org.pkl.core.DataSizeUnit;
import org.pkl.core.ast.ByteConstantValueNode;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.util.ByteArrayUtils;
import org.pkl.core.util.Nullable;
@ValueType
public final class VmBytes extends VmValue implements Iterable<Long> {
private @Nullable VmList vmList;
private @Nullable String base64;
private @Nullable String hex;
private final byte[] bytes;
private @Nullable VmDataSize size;
public static VmBytes EMPTY = new VmBytes(new byte[0]);
@TruffleBoundary
public static VmBytes createFromConstantNodes(ExpressionNode[] elements) {
if (elements.length == 0) {
return EMPTY;
}
var bytes = new byte[elements.length];
for (var i = 0; i < elements.length; i++) {
var exprNode = elements[i];
// guaranteed by AstBuilder
assert exprNode instanceof ByteConstantValueNode;
bytes[i] = ((ByteConstantValueNode) exprNode).getByteValue();
}
return new VmBytes(bytes);
}
public VmBytes(byte[] bytes) {
this.bytes = bytes;
}
public VmBytes(VmList vmList, byte[] bytes) {
this.vmList = vmList;
this.bytes = bytes;
}
@Override
public VmClass getVmClass() {
return BaseModule.getBytesClass();
}
@Override
public void force(boolean allowUndefinedValues) {
// do nothing
}
@Override
public byte[] export() {
return bytes;
}
@Override
public void accept(VmValueVisitor visitor) {
visitor.visitBytes(this);
}
@Override
public <T> T accept(VmValueConverter<T> converter, Iterable<Object> path) {
return converter.convertBytes(this, path);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof VmBytes vmBytes)) {
return false;
}
return Arrays.equals(bytes, vmBytes.bytes);
}
@Override
public int hashCode() {
return Arrays.hashCode(bytes);
}
public byte[] getBytes() {
return bytes;
}
public long get(long index) {
return getBytes()[(int) index];
}
public VmBytes concatenate(VmBytes right) {
if (bytes.length == 0) return right;
if (right.bytes.length == 0) return this;
var newBytes = new byte[bytes.length + right.bytes.length];
System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
System.arraycopy(right.bytes, 0, newBytes, bytes.length, right.bytes.length);
return new VmBytes(newBytes);
}
public VmList toList() {
if (vmList == null) {
vmList = VmList.create(bytes);
}
return vmList;
}
public String base64() {
if (base64 == null) {
base64 = ByteArrayUtils.base64(bytes);
}
return base64;
}
public String hex() {
if (hex == null) {
hex = ByteArrayUtils.toHex(bytes);
}
return hex;
}
public int getLength() {
return bytes.length;
}
public VmDataSize getSize() {
if (size == null) {
if (getLength() == 0) {
// avoid log10(0), which gives us -Infinity
size = new VmDataSize(0, DataSizeUnit.BYTES);
} else {
var magnitude = (int) Math.floor(Math.log10(getLength()));
var unit =
switch (magnitude) {
case 0, 1, 2 -> DataSizeUnit.BYTES;
case 3, 4, 5 -> DataSizeUnit.KILOBYTES;
case 6, 7, 8 -> DataSizeUnit.MEGABYTES;
case 9, 10, 11 -> DataSizeUnit.GIGABYTES;
// in practice, can never happen (Java can only hold at most math.maxInt bytes).
case 12, 13, 14 -> DataSizeUnit.TERABYTES;
default -> DataSizeUnit.PETABYTES;
};
size = new VmDataSize(getLength(), DataSizeUnit.BYTES).convertTo(unit);
}
}
return size;
}
@Override
public String toString() {
var sb = new StringBuilder("Bytes(");
var isFirst = true;
for (var byt : bytes) {
if (isFirst) {
isFirst = false;
} else {
sb.append(", ");
}
sb.append(Byte.toUnsignedInt(byt));
}
sb.append(")");
return sb.toString();
}
@Override
public Iterator<Long> iterator() {
return new PrimitiveIterator.OfLong() {
int index = 0;
@Override
public boolean hasNext() {
return index < bytes.length;
}
@Override
public long nextLong() {
if (!hasNext()) {
CompilerDirectives.transferToInterpreter();
throw new NoSuchElementException();
}
var result = Byte.toUnsignedLong(bytes[index]);
index += 1;
return result;
}
};
}
}

View File

@@ -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.
@@ -98,6 +98,16 @@ public final class VmList extends VmCollection {
return new VmList(vector.immutable());
}
@TruffleBoundary
public static VmList create(byte[] elements) {
if (elements.length == 0) return EMPTY;
var vector = RrbTree.emptyMutable();
for (var elem : elements) {
vector.append(Byte.toUnsignedLong(elem));
}
return new VmList(vector.immutable());
}
@TruffleBoundary
public static VmList create(Object[] elements, int length) {
if (elements.length == 0) return EMPTY;

View File

@@ -175,6 +175,10 @@ public final class VmUtils {
return (String) VmUtils.readMember((VmObjectLike) receiver, Identifier.TEXT);
}
public static VmBytes readBytesProperty(VmObjectLike receiver) {
return (VmBytes) VmUtils.readMember(receiver, Identifier.BYTES);
}
@TruffleBoundary
public static Object readMember(VmObjectLike receiver, Object memberKey) {
var result = readMemberOrNull(receiver, memberKey);

View File

@@ -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.
@@ -52,6 +52,8 @@ public interface VmValueConverter<T> {
T convertDataSize(VmDataSize value, Iterable<Object> path);
T convertBytes(VmBytes vmBytes, Iterable<Object> path);
T convertIntSeq(VmIntSeq value, Iterable<Object> path);
T convertList(VmList value, Iterable<Object> path);

View File

@@ -114,6 +114,45 @@ public final class VmValueRenderer {
append(value);
}
private void renderByteSize(VmDataSize size) {
var value = size.getValue();
if (value % 1 == 0) {
append((int) value);
} else if ((value * 10) % 1 == 0) {
append(String.format("%.1f", value));
} else {
append(String.format("%.2f", value));
}
append(".");
append(size.getUnit());
}
@Override
public void visitBytes(VmBytes value) {
append("Bytes(");
// truncate bytes if over 8 bytes
renderByteElems(value, Math.min(value.getLength(), 8));
if (value.getLength() > 8) {
append(", ... <total size: ");
renderByteSize(value.getSize());
append(">");
}
append(")");
}
private void renderByteElems(VmBytes value, int limit) {
var isFirst = true;
var bytes = value.getBytes();
for (var i = 0; i < limit; i++) {
if (isFirst) {
isFirst = false;
} else {
append(", ");
}
append(Byte.toUnsignedInt(bytes[i]));
}
}
@Override
public void visitPair(VmPair value) {
append("Pair(");

View File

@@ -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.
@@ -30,6 +30,8 @@ public interface VmValueVisitor {
void visitDataSize(VmDataSize value);
void visitBytes(VmBytes value);
void visitIntSeq(VmIntSeq value);
void visitList(VmList value);

View File

@@ -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.
@@ -30,6 +30,7 @@ public final class PklConverter implements VmValueConverter<Object> {
private final @Nullable VmFunction floatConverter;
private final @Nullable VmFunction durationConverter;
private final @Nullable VmFunction dataSizeConverter;
private final @Nullable VmFunction bytesConverter;
private final @Nullable VmFunction intSeqConverter;
private final @Nullable VmFunction listConverter;
private final @Nullable VmFunction setConverter;
@@ -56,6 +57,7 @@ public final class PklConverter implements VmValueConverter<Object> {
floatConverter = typeConverters.get(BaseModule.getFloatClass());
durationConverter = typeConverters.get(BaseModule.getDurationClass());
dataSizeConverter = typeConverters.get(BaseModule.getDataSizeClass());
bytesConverter = typeConverters.get(BaseModule.getBytesClass());
intSeqConverter = typeConverters.get(BaseModule.getIntSeqClass());
listConverter = typeConverters.get(BaseModule.getListClass());
setConverter = typeConverters.get(BaseModule.getSetClass());
@@ -100,6 +102,11 @@ public final class PklConverter implements VmValueConverter<Object> {
return doConvert(value, path, dataSizeConverter);
}
@Override
public Object convertBytes(VmBytes value, Iterable<Object> path) {
return doConvert(value, path, bytesConverter);
}
@Override
public Object convertIntSeq(VmIntSeq value, Iterable<Object> path) {
return doConvert(value, path, intSeqConverter);

View File

@@ -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.
@@ -142,4 +142,13 @@ public final class BaseNodes {
return new VmIntSeq(first, second, 1L);
}
}
public abstract static class Bytes extends ExternalMethod1Node {
@Specialization
protected VmList eval(VirtualFrame frame, VmTyped self, Object args) {
// invocations of this method are handled specially in AstBuilder
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder().bug("Node `BaseNodes.Bytes` should never be executed.").build();
}
}
}

View File

@@ -0,0 +1,121 @@
/*
* 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.base;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmList;
import org.pkl.core.runtime.VmNull;
import org.pkl.core.stdlib.ExternalMethod0Node;
import org.pkl.core.stdlib.ExternalMethod1Node;
import org.pkl.core.stdlib.ExternalPropertyNode;
import org.pkl.core.util.ByteArrayUtils;
public final class BytesNodes {
private BytesNodes() {}
public abstract static class toList extends ExternalMethod0Node {
@Specialization
protected VmList eval(VmBytes self) {
return self.toList();
}
}
public abstract static class length extends ExternalPropertyNode {
@Specialization
protected long eval(VmBytes self) {
return self.getLength();
}
}
public abstract static class size extends ExternalPropertyNode {
@Specialization
protected VmDataSize eval(VmBytes self) {
return self.getSize();
}
}
public abstract static class base64 extends ExternalPropertyNode {
@Specialization
protected String eval(VmBytes self) {
return self.base64();
}
}
public abstract static class hex extends ExternalPropertyNode {
@Specialization
protected String eval(VmBytes self) {
return self.hex();
}
}
public abstract static class md5 extends ExternalPropertyNode {
@Specialization
protected String eval(VmBytes self) {
return ByteArrayUtils.md5(self.getBytes());
}
}
public abstract static class sha1 extends ExternalPropertyNode {
@Specialization
protected String eval(VmBytes self) {
return ByteArrayUtils.sha1(self.getBytes());
}
}
public abstract static class sha256 extends ExternalPropertyNode {
@Specialization
protected String eval(VmBytes self) {
return ByteArrayUtils.sha256(self.getBytes());
}
}
public abstract static class sha256Int extends ExternalPropertyNode {
@Specialization
protected long eval(VmBytes self) {
return ByteArrayUtils.sha256Int(self.getBytes());
}
}
public abstract static class getOrNull extends ExternalMethod1Node {
@Specialization
protected Object eval(VmBytes self, long index) {
if (index < 0 || index >= self.getLength()) {
return VmNull.withoutDefault();
}
return self.get(index);
}
}
public abstract static class decodeToString extends ExternalMethod1Node {
@Specialization
protected String eval(VmBytes self, String charset) {
try {
var byteBuffer = ByteBuffer.wrap(self.getBytes());
var decoder = Charset.forName(charset).newDecoder();
return decoder.decode(byteBuffer).toString();
} catch (CharacterCodingException e) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder().evalError("characterCodingException", charset).build();
}
}
}
}

View File

@@ -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.
@@ -126,6 +126,11 @@ public final class JsonRendererNodes {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitRegex(VmRegex value) {
cannotRenderTypeAddConverter(value);

View File

@@ -18,17 +18,22 @@ package org.pkl.core.stdlib.base;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode;
import org.pkl.core.ast.expression.binary.*;
import org.pkl.core.ast.internal.IsInstanceOfNode;
import org.pkl.core.ast.internal.IsInstanceOfNodeGen;
import org.pkl.core.ast.lambda.*;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.UInt8TypeAliasTypeNode;
import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.*;
import org.pkl.core.stdlib.*;
import org.pkl.core.stdlib.base.CollectionNodes.CompareByNode;
import org.pkl.core.stdlib.base.CollectionNodes.CompareNode;
import org.pkl.core.stdlib.base.CollectionNodes.CompareWithNode;
import org.pkl.core.util.EconomicSets;
import org.pkl.core.util.LateInit;
// duplication between ListNodes and SetNodes is "intentional"
// (sharing nodes between VmCollection subtypes results in
@@ -1321,4 +1326,34 @@ public final class ListNodes {
return self.toDynamic();
}
}
public abstract static class toBytes extends ExternalMethod0Node {
@Child @LateInit private TypeNode typeNode;
private TypeNode getTypeNode() {
if (typeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
typeNode = new UInt8TypeAliasTypeNode();
}
return typeNode;
}
@Specialization
protected VmBytes eval(VirtualFrame frame, VmList self) {
var typeNode = getTypeNode();
var bytes = new byte[self.getLength()];
try {
for (var i = 0; i < self.getLength(); i++) {
var elem = self.get(i);
var num = (Long) typeNode.executeEagerly(frame, elem);
bytes[i] = num.byteValue();
}
} catch (VmTypeMismatchException e) {
CompilerDirectives.transferToInterpreter();
throw e.toVmException();
}
return new VmBytes(self, bytes);
}
}
}

View File

@@ -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.
@@ -54,7 +54,6 @@ public final class PListRendererNodes {
}
// keep in sync with org.pkl.core.PListRenderer
@SuppressWarnings("HttpUrlsUsage")
private static final class PListRenderer extends AbstractRenderer {
// it's safe (though not required) to escape all the following characters in XML text nodes
@@ -124,6 +123,13 @@ public final class PListRendererNodes {
.build();
}
@Override
public void visitBytes(VmBytes value) {
builder.append("<data>");
builder.append(value.base64());
builder.append("</data>");
}
@Override
public void visitRegex(VmRegex value) {
throw new VmExceptionBuilder()

View File

@@ -17,6 +17,7 @@ package org.pkl.core.stdlib.base;
import org.pkl.core.ValueFormatter;
import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic;
@@ -99,6 +100,11 @@ public final class PcfRenderer extends AbstractRenderer {
builder.append(value);
}
@Override
public void visitBytes(VmBytes value) {
builder.append(value);
}
@Override
public void visitPair(VmPair value) {
builder.append("Pair(");

View File

@@ -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.
@@ -18,6 +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.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic;
@@ -117,6 +118,11 @@ public final class PropertiesRendererNodes {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitIntSeq(VmIntSeq value) {
cannotRenderTypeAddConverter(value);

View File

@@ -1,102 +0,0 @@
/*
* Copyright © 2024 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.base;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization;
import java.util.*;
import org.pkl.core.resource.Resource;
import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmTyped;
import org.pkl.core.runtime.VmUtils;
import org.pkl.core.stdlib.ExternalPropertyNode;
import org.pkl.core.util.ByteArrayUtils;
public final class ResourceNodes {
private ResourceNodes() {}
public abstract static class md5 extends ExternalPropertyNode {
@TruffleBoundary
@Specialization(guards = "self.hasExtraStorage()")
protected String evalWithExtraStorage(VmTyped self) {
var resource = (Resource) self.getExtraStorage();
return ByteArrayUtils.md5(resource.bytes());
}
@TruffleBoundary
@Specialization(guards = "!self.hasExtraStorage()")
protected String evalWithoutExtraStorage(VmTyped self) {
// `pkl.base#Resource` is designed to allow direct instantiation,
// in which case it isn't backed by a `org.pkl.core.resource.Resource`.
// It seems the best we can do here
// is to expect `pkl.base#Resource.base64` to be set and decode it.
var base64 = (String) VmUtils.readMember(self, Identifier.BASE64);
var bytes = Base64.getDecoder().decode(base64);
return ByteArrayUtils.md5(bytes);
}
}
public abstract static class sha1 extends ExternalPropertyNode {
@TruffleBoundary
@Specialization(guards = "self.hasExtraStorage()")
protected String evalWithExtraStorage(VmTyped self) {
var resource = (Resource) self.getExtraStorage();
return ByteArrayUtils.sha1(resource.bytes());
}
@TruffleBoundary
@Specialization(guards = "!self.hasExtraStorage()")
protected String evalWithoutExtraStorage(VmTyped self) {
var base64 = (String) VmUtils.readMember(self, Identifier.BASE64);
var bytes = Base64.getDecoder().decode(base64);
return ByteArrayUtils.sha1(bytes);
}
}
public abstract static class sha256 extends ExternalPropertyNode {
@TruffleBoundary
@Specialization(guards = "self.hasExtraStorage()")
protected String evalWithExtraStorage(VmTyped self) {
var resource = (Resource) self.getExtraStorage();
return ByteArrayUtils.sha256(resource.bytes());
}
@TruffleBoundary
@Specialization(guards = "!self.hasExtraStorage()")
protected String evalWithoutExtraStorage(VmTyped self) {
var base64 = (String) VmUtils.readMember(self, Identifier.BASE64);
var bytes = Base64.getDecoder().decode(base64);
return ByteArrayUtils.sha256(bytes);
}
}
public abstract static class sha256Int extends ExternalPropertyNode {
@TruffleBoundary
@Specialization(guards = "self.hasExtraStorage()")
protected long evalWithExtraStorage(VmTyped self) {
var resource = (Resource) self.getExtraStorage();
return ByteArrayUtils.sha256Int(resource.bytes());
}
@TruffleBoundary
@Specialization(guards = "!self.hasExtraStorage()")
protected long evalWithoutExtraStorage(VmTyped self) {
var base64 = (String) VmUtils.readMember(self, Identifier.BASE64);
var bytes = Base64.getDecoder().decode(base64);
return ByteArrayUtils.sha256Int(bytes);
}
}
}

View File

@@ -19,9 +19,11 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.LoopNode;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.*;
import org.pkl.core.PklBugException;
import org.pkl.core.ast.lambda.ApplyVmFunction1Node;
import org.pkl.core.ast.lambda.ApplyVmFunction1NodeGen;
import org.pkl.core.runtime.*;
@@ -149,6 +151,19 @@ public final class StringNodes {
}
}
public abstract static class isBase64 extends ExternalPropertyNode {
@Specialization
@TruffleBoundary
protected boolean eval(String self) {
try {
Base64.getDecoder().decode(self);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
}
public abstract static class chars extends ExternalPropertyNode {
@Specialization
@TruffleBoundary
@@ -900,7 +915,7 @@ public final class StringNodes {
@TruffleBoundary
@Specialization
protected String eval(String self) {
return Base64.getEncoder().encodeToString(self.getBytes(StandardCharsets.UTF_8));
return ByteArrayUtils.base64(self.getBytes(StandardCharsets.UTF_8));
}
}
@@ -920,6 +935,35 @@ public final class StringNodes {
}
}
public abstract static class base64DecodedBytes extends ExternalPropertyNode {
@TruffleBoundary
@Specialization
protected VmBytes eval(String self) {
try {
return new VmBytes(Base64.getDecoder().decode(self));
} catch (IllegalArgumentException e) {
throw exceptionBuilder()
.adhocEvalError(e.getMessage())
.withProgramValue("String", self)
.withCause(e)
.build();
}
}
}
public abstract static class encodeToBytes extends ExternalMethod1Node {
@TruffleBoundary
@Specialization
protected VmBytes eval(String self, String charsetName) {
try {
var bytes = self.getBytes(charsetName);
return new VmBytes(bytes);
} catch (UnsupportedEncodingException e) {
throw PklBugException.unreachableCode();
}
}
}
@TruffleBoundary
private static String substringFrom(String string, int start) {
return string.substring(start);

View File

@@ -18,6 +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.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmCollection;
import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration;
@@ -177,6 +178,11 @@ public final class YamlRendererNodes {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitRegex(VmRegex value) {
cannotRenderTypeAddConverter(value);

View File

@@ -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.
@@ -21,6 +21,7 @@ import java.util.Set;
import java.util.regex.Pattern;
import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.JsonnetModule;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic;
@@ -199,6 +200,11 @@ public final class RendererNodes {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitIntSeq(VmIntSeq value) {
cannotRenderTypeAddConverter(value);

View File

@@ -44,6 +44,7 @@ import org.pkl.core.ast.type.TypeNode.UnionOfStringLiteralsTypeNode;
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration;
@@ -522,6 +523,14 @@ public final class RendererNodes {
.build();
}
@Override
public void visitBytes(VmBytes value) {
throw new VmExceptionBuilder()
.evalError("cannotRenderTypeAddConverter", "Bytes", "Protobuf")
.withProgramValue("Value", value)
.build();
}
@Override
public void visitIntSeq(VmIntSeq value) {
writePropertyName();

View File

@@ -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.
@@ -154,6 +154,11 @@ public final class RendererNodes {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override
public void visitPair(VmPair value) {
cannotRenderTypeAddConverter(value);

View File

@@ -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.
@@ -17,11 +17,16 @@ package org.pkl.core.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import org.pkl.core.runtime.VmExceptionBuilder;
public final class ByteArrayUtils {
private ByteArrayUtils() {}
public static String base64(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
public static String md5(byte[] input) {
return hash(input, "MD5");
}

View File

@@ -1064,3 +1064,9 @@ External reader process has already terminated.
invalidOpaqueFileUri=\
File URIs must have a path that starts with `/` (e.g. file:/path/to/my_module.pkl).\n\
To resolve relative paths, remove the scheme prefix (remove "file:").
invalidStringBase64=\
`{0}` is not in base64 encoding.
characterCodingException=\
Invalid bytes for charset "{0}".

View File

@@ -0,0 +1,51 @@
amends "../snippetTest.pkl"
local megaBytes = IntSeq(0, 999).map((_) -> 0).toBytes()
examples {
["md5"] {
Bytes(1, 2, 3, 4).md5
Bytes().md5
}
["sha256"] {
Bytes(1, 2, 3, 4).sha256
Bytes().sha256
}
["sha1"] {
Bytes(1, 2, 3, 4).sha1
Bytes().sha1
}
["toString"] {
Bytes(1, 2, 3, 4).toString()
Bytes().toString()
}
["base64"] {
Bytes(1, 2, 3, 4).base64
Bytes().base64
"AQIDBA==".base64DecodedBytes.base64
}
["hex"] {
Bytes(1, 2, 3, 4).hex
Bytes().hex
}
["length"] {
Bytes().length
Bytes(1, 2, 3, 4).length
megaBytes.length
}
["size"] {
Bytes().size
Bytes(1, 2, 3, 4).size
megaBytes.size
}
["decodeToString()"] {
Bytes(0x66, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72).decodeToString("UTF-8")
"foo bar".encodeToBytes("UTF-8").decodeToString("UTF-8")
}
["getOrNull()"] {
Bytes(1, 2, 3).getOrNull(0)
Bytes(1, 2, 3).getOrNull(1)
Bytes(1, 2, 3).getOrNull(2)
Bytes(1, 2, 3).getOrNull(3)
}
}

View File

@@ -357,6 +357,13 @@ examples {
list1.toDynamic()
List().toDynamic()
}
["toBytes()"] {
list1.toBytes()
List().toBytes()
module.catch(() -> List(null).toBytes())
module.catch(() -> List(-1).toBytes())
}
["filterNonNull()"] {
list1.filterNonNull()

View File

@@ -18,6 +18,12 @@ facts {
!str1.isBlank
}
["isBase64"] {
"".isBase64
"AQIDBA==".isBase64
!"hello there".isBase64
}
["lastIndex"] {
for (s in List(str1, str2, str3)) {
s.length == s.lastIndex + 1
@@ -28,6 +34,10 @@ facts {
"".base64.base64Decoded == ""
quickBrownFox.base64.base64Decoded == quickBrownFox
}
["base64DecodedBytes"] {
"AQIDBA==".base64DecodedBytes == Bytes(1, 2, 3, 4)
}
["contains()"] {
str1.contains(str2)
@@ -487,4 +497,19 @@ examples {
["base64Decoded"] {
module.catch(() -> "~~~".base64Decoded)
}
["base64DecodedBytes"] {
module.catch(() -> "~~~".base64DecodedBytes)
"AQIDBA==".base64DecodedBytes
}
["encodeToBytes()"] {
"~~~".encodeToBytes("UTF-8")
"🏀".encodeToBytes("UTF-8")
"~~~".encodeToBytes("UTF-16")
"🏀".encodeToBytes("UTF-16")
"~~~".encodeToBytes("ISO-8859-1")
"🏀".encodeToBytes("ISO-8859-1")
"Parrot".encodeToBytes("UTF-8")
}
}

View File

@@ -0,0 +1,70 @@
amends ".../snippetTest.pkl"
local bytes1 = Bytes(1, 2, 3)
local bytes2 = Bytes(1, 2, 4)
examples {
["basic"] {
Bytes(0, 255)
}
["bytes from constant value nodes"] {
Bytes(0, 1, 2, 3, 4, 5, 6, 7)
Bytes(
0b000,
0b001,
0b010,
0b011,
0b100,
0b101,
0b110,
0b111
)
Bytes(0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)
Bytes(0o0, 0o1, 0o2, 0o3, 0o4, 0o5, 0o6, 0o7)
Bytes(0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)
}
["bytes from non-constant value nodes"] {
local one = 1
local two = 2
Bytes(one, two)
}
["incorrectly sized constant values"] {
module.catch(() -> Bytes(0, 1, 2, 0xffff))
}
["equality"] {
bytes1 == bytes1
bytes1 == bytes2
bytes1 == bytes1.toList()
bytes1 == Bytes(1, 2, 3)
}
["inequality"] {
bytes1 != bytes1
bytes1 != bytes2
bytes1 != bytes1.toList()
bytes1 != Bytes(1, 2, 3)
}
["addition"] {
bytes1 + bytes2
Bytes() + Bytes()
bytes1 + Bytes()
Bytes() + bytes2
module.catch(() -> bytes1 + bytes2.toList())
}
["subscript"] {
bytes1[0]
bytes1[1]
bytes1[2]
module.catch(() -> bytes1[3])
}
["all bytes"] {
IntSeq(0, 255).toList().toBytes()
}
}

View File

@@ -37,5 +37,3 @@ examples {
module.catch(() -> list1[3])
}
}

View File

@@ -0,0 +1 @@
res = Bytes(0xc0, 0xc1).decodeToString("UTF-8")

View File

@@ -0,0 +1 @@
foo = Bytes(0, 1, 2, 3, 0xffff)

View File

@@ -0,0 +1,3 @@
local num = 0xfff
res = Bytes(0, 1, 2, 3, num)

View File

@@ -0,0 +1 @@
bytes = Bytes(0, 1, 2, 3, 0xffff)

View File

@@ -0,0 +1 @@
res = List(1, 2, 3, 0xffff).toBytes()

View File

@@ -326,6 +326,18 @@ res11d = new Dynamic {
for (i, n in IntSeq(1, 3)) { Pair(i, n) }
}
res12a = new Listing {
for (i in Bytes(1, 2, 3, 4)) {
i
}
}
res12b = new Listing {
for (i in Bytes()) {
i
}
}
local dynamicWithOnlyProperties = new Dynamic {
foo = "Foo!"
bar = 42

View File

@@ -15,6 +15,7 @@ local list: List<Int> = List(1, 2, 3)
local map: Map<String, Int> = Map("zz", 1, "yy", 2)
local intseq: IntSeq = IntSeq(0, 5).step(2)
local set: Set<Int> = Set(10, 20, 30)
local bytes: Bytes = Bytes(1, 2, 3, 4)
examples {
["inferred Dynamic parent"] {
@@ -29,6 +30,7 @@ examples {
...map
...intseq
...set
...bytes
}
}
["explicit Dynamic type"] {
@@ -43,6 +45,7 @@ examples {
...map
...intseq
...set
...bytes
}
}
["legacy syntax"] {
@@ -52,6 +55,7 @@ examples {
(Dynamic) { ...map }
(Dynamic) { ...intseq }
(Dynamic) { ...set }
(Dynamic) { ...bytes }
}
}

View File

@@ -6,6 +6,7 @@ local list: List<Int> = List(1, 2, 3)
local map: Map<String, Int> = Map("a", 1, "b", 2)
local intseq: IntSeq = IntSeq(0, 5).step(2)
local set: Set<Int> = Set(10, 20, 30)
local bytes: Bytes = Bytes(1, 2, 3, 4)
res1: Listing = new {
0
@@ -72,3 +73,15 @@ res16 = new Listing {
}
res17 = res16.length
res18 = new Listing {
...bytes
}
res19 = new Listing {
0
...bytes
0
0
0
}

View File

@@ -15,6 +15,7 @@ local list: List<Int> = List(1, 2, 3)
local map: Map<String, Int> = Map("a", 1, "b", 2)
local intseq: IntSeq = IntSeq(0, 5).step(2)
local set: Set<Int> = Set(10, 20, 30)
local bytes: Bytes = Bytes(1, 2, 3, 4)
class Foo {
names: Listing<String>
@@ -108,3 +109,5 @@ local foos = (makeFoos(List("bar"))) {
res17 = new Mapping {
...foos
}
res18 = test.catch(() -> new Mapping { ...bytes })

View File

@@ -0,0 +1,47 @@
examples {
["md5"] {
"08d6c05a21512a79a1dfeb9d2a8f262f"
"d41d8cd98f00b204e9800998ecf8427e"
}
["sha256"] {
"9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a"
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
["sha1"] {
"12dada1fff4d4787ade3333147202c3b443e376f"
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
}
["toString"] {
"Bytes(1, 2, 3, 4)"
"Bytes()"
}
["base64"] {
"AQIDBA=="
""
"AQIDBA=="
}
["hex"] {
"01020304"
""
}
["length"] {
0
4
1000
}
["size"] {
0.b
4.b
1.kb
}
["decodeToString()"] {
"foo bar"
"foo bar"
}
["getOrNull()"] {
1
2
3
null
}
}

View File

@@ -303,6 +303,12 @@ examples {
}
new {}
}
["toBytes()"] {
Bytes(1, 2, 3)
Bytes()
"Expected value of type `Int`, but got `null`."
"Type constraint `isBetween(0, 255)` violated. Value: -1"
}
["filterNonNull()"] {
List(1, 2, 3)
List()

View File

@@ -13,9 +13,9 @@ alias {
members = List(new {
referent {
location {
line = 1040
line = 1056
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1040"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1056"
}
docComment = """
A boolean value, either [true] or [false].
@@ -197,9 +197,9 @@ alias {
properties = Map()
methods = Map("xor", new {
location {
line = 1050
line = 1066
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1050"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1066"
}
docComment = """
Tells if exactly one of [this] and [other] is [true] (exclusive or).
@@ -221,9 +221,9 @@ alias {
})
}, "implies", new {
location {
line = 1063
line = 1079
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1063"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1079"
}
docComment = """
Tells if [this] implies [other] (logical consequence).
@@ -251,9 +251,9 @@ alias {
}, new {
referent {
location {
line = 1076
line = 1092
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1076"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1092"
}
docComment = """
A sequence of Unicode characters (code points).
@@ -433,9 +433,9 @@ alias {
}
properties = Map("length", new {
location {
line = 1087
line = 1103
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1087"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1103"
}
docComment = """
The number of characters in this string.
@@ -458,9 +458,9 @@ alias {
name = "length"
}, "lastIndex", new {
location {
line = 1100
line = 1116
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1100"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1116"
}
docComment = """
The index of the last character in this string (same as `length - 1`).
@@ -480,9 +480,9 @@ alias {
name = "lastIndex"
}, "isEmpty", new {
location {
line = 1110
line = 1126
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1110"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1126"
}
docComment = """
Tells whether this string is empty.
@@ -499,9 +499,9 @@ alias {
name = "isEmpty"
}, "isBlank", new {
location {
line = 1121
line = 1137
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1121"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1137"
}
docComment = """
Tells if all characters in this string have Unicode property "White_Space".
@@ -519,19 +519,39 @@ alias {
name = "isBlank"
}, "isRegex", new {
location {
line = 1124
line = 1140
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1124"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1140"
}
docComment = "Tells if this string is a valid regular expression according to [Regex]."
annotations = List()
modifiers = Set()
name = "isRegex"
}, "isBase64", new {
location {
line = 1150
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1150"
}
docComment = """
Tells if this is a valid base64-encoded string.
Facts:
```
"AQIDBA==".isBase64
!"hello there".isBase64
```
"""
annotations = List(new {
version = "0.29.0"
})
modifiers = Set()
name = "isBase64"
}, "md5", new {
location {
line = 1131
line = 1157
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1131"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1157"
}
docComment = """
The [MD5](https://en.wikipedia.org/wiki/MD5)
@@ -545,9 +565,9 @@ alias {
name = "md5"
}, "sha1", new {
location {
line = 1137
line = 1163
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1137"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1163"
}
docComment = """
The [SHA-1](https://en.wikipedia.org/wiki/SHA-1)
@@ -560,9 +580,9 @@ alias {
name = "sha1"
}, "sha256", new {
location {
line = 1142
line = 1168
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1142"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1168"
}
docComment = """
The [SHA-256](https://en.wikipedia.org/wiki/SHA-2)
@@ -574,9 +594,9 @@ alias {
name = "sha256"
}, "sha256Int", new {
location {
line = 1146
line = 1172
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1146"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1172"
}
docComment = """
The first 64 bits of the [SHA-256](https://en.wikipedia.org/wiki/SHA-2)
@@ -587,9 +607,9 @@ alias {
name = "sha256Int"
}, "base64", new {
location {
line = 1149
line = 1175
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1149"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1175"
}
docComment = "The Base64 encoding of this string's UTF-8 byte sequence."
annotations = List()
@@ -597,9 +617,9 @@ alias {
name = "base64"
}, "base64Decoded", new {
location {
line = 1157
line = 1183
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1157"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1183"
}
docComment = """
The inverse of [base64].
@@ -612,11 +632,30 @@ alias {
annotations = List()
modifiers = Set()
name = "base64Decoded"
}, "base64DecodedBytes", new {
location {
line = 1192
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1192"
}
docComment = """
Converts this base64-format string into [Bytes].
Facts:
```
"AQIDBA==".base64DecodedBytes = Bytes(1, 2, 3, 4)
```
"""
annotations = List(new {
version = "0.29.0"
})
modifiers = Set()
name = "base64DecodedBytes"
}, "chars", new {
location {
line = 1165
line = 1200
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1165"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1200"
}
docComment = """
The Unicode characters in this string.
@@ -631,9 +670,9 @@ alias {
name = "chars"
}, "codePoints", new {
location {
line = 1173
line = 1208
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1173"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1208"
}
docComment = """
The Unicode code points in this string.
@@ -649,9 +688,9 @@ alias {
})
methods = Map("getOrNull", new {
location {
line = 1185
line = 1220
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1185"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1220"
}
docComment = """
Returns the character at [index], or [null] if [index] is out of range.
@@ -674,9 +713,9 @@ alias {
})
}, "substring", new {
location {
line = 1199
line = 1234
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1199"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1234"
}
docComment = """
Returns the substring from [start] until [exclusiveEnd].
@@ -703,9 +742,9 @@ alias {
})
}, "substringOrNull", new {
location {
line = 1217
line = 1252
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1217"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1252"
}
docComment = """
Returns the substring from [start] until [exclusiveEnd].
@@ -736,9 +775,9 @@ alias {
})
}, "repeat", new {
location {
line = 1227
line = 1262
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1227"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1262"
}
docComment = """
Concatenates [count] copies of this string.
@@ -759,9 +798,9 @@ alias {
})
}, "contains", new {
location {
line = 1230
line = 1265
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1230"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1265"
}
docComment = "Tells whether this string contains [pattern]."
annotations = List()
@@ -773,9 +812,9 @@ alias {
})
}, "matches", new {
location {
line = 1234
line = 1269
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1234"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1269"
}
docComment = "Tells whether this string matches [regex] in its entirety."
annotations = List(new {
@@ -791,9 +830,9 @@ alias {
})
}, "startsWith", new {
location {
line = 1237
line = 1272
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1237"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1272"
}
docComment = "Tells whether this string starts with [pattern]."
annotations = List()
@@ -805,9 +844,9 @@ alias {
})
}, "endsWith", new {
location {
line = 1240
line = 1275
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1240"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1275"
}
docComment = "Tells whether this string ends with [pattern]."
annotations = List()
@@ -819,9 +858,9 @@ alias {
})
}, "indexOf", new {
location {
line = 1246
line = 1281
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1246"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1281"
}
docComment = """
Returns the zero-based index of the first occurrence of [pattern]
@@ -838,9 +877,9 @@ alias {
})
}, "indexOfOrNull", new {
location {
line = 1250
line = 1285
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1250"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1285"
}
docComment = """
Returns the zero-based index of the first occurrence of [pattern]
@@ -855,9 +894,9 @@ alias {
})
}, "lastIndexOf", new {
location {
line = 1256
line = 1291
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1256"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1291"
}
docComment = """
Returns the zero-based index of the last occurrence of [pattern]
@@ -874,9 +913,9 @@ alias {
})
}, "lastIndexOfOrNull", new {
location {
line = 1260
line = 1295
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1260"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1295"
}
docComment = """
Returns the zero-based index of the last occurrence of [pattern]
@@ -891,9 +930,9 @@ alias {
})
}, "take", new {
location {
line = 1266
line = 1301
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1266"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1301"
}
docComment = """
Returns the first [n] characters of this string.
@@ -913,9 +952,9 @@ alias {
})
}, "takeWhile", new {
location {
line = 1269
line = 1304
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1269"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1304"
}
docComment = "Returns the longest prefix of this string that satisfies [predicate]."
annotations = List()
@@ -927,9 +966,9 @@ alias {
})
}, "takeLast", new {
location {
line = 1274
line = 1309
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1274"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1309"
}
docComment = """
Returns the last [n] characters of this string.
@@ -945,9 +984,9 @@ alias {
})
}, "takeLastWhile", new {
location {
line = 1277
line = 1312
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1277"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1312"
}
docComment = "Returns the longest suffix of this string that satisfies [predicate]."
annotations = List()
@@ -959,9 +998,9 @@ alias {
})
}, "drop", new {
location {
line = 1283
line = 1318
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1283"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1318"
}
docComment = """
Removes the first [n] characters of this string.
@@ -981,9 +1020,9 @@ alias {
})
}, "dropWhile", new {
location {
line = 1287
line = 1322
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1287"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1322"
}
docComment = "Removes the longest prefix of this string that satisfies [predicate]."
annotations = List(new {
@@ -999,9 +1038,9 @@ alias {
})
}, "dropLast", new {
location {
line = 1293
line = 1328
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1293"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1328"
}
docComment = """
Removes the last [n] characters of this string.
@@ -1021,9 +1060,9 @@ alias {
})
}, "dropLastWhile", new {
location {
line = 1297
line = 1332
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1297"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1332"
}
docComment = "Removes the longest suffix of this string that satisfies [predicate]."
annotations = List(new {
@@ -1039,9 +1078,9 @@ alias {
})
}, "replaceFirst", new {
location {
line = 1302
line = 1337
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1302"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1337"
}
docComment = """
Replaces the first occurrence of [pattern] in this string with [replacement].
@@ -1059,9 +1098,9 @@ alias {
})
}, "replaceLast", new {
location {
line = 1307
line = 1342
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1307"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1342"
}
docComment = """
Replaces the last occurrence of [pattern] in this string with [replacement].
@@ -1079,9 +1118,9 @@ alias {
})
}, "replaceAll", new {
location {
line = 1312
line = 1347
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1312"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1347"
}
docComment = """
Replaces all occurrences of [pattern] in this string with [replacement].
@@ -1099,9 +1138,9 @@ alias {
})
}, "replaceFirstMapped", new {
location {
line = 1317
line = 1352
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1317"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1352"
}
docComment = """
Replaces the first occurrence of [pattern] in this string with the return value of [mapper].
@@ -1119,9 +1158,9 @@ alias {
})
}, "replaceLastMapped", new {
location {
line = 1322
line = 1357
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1322"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1357"
}
docComment = """
Replaces the last occurrence of [pattern] in this string with the return value of [mapper].
@@ -1139,9 +1178,9 @@ alias {
})
}, "replaceAllMapped", new {
location {
line = 1327
line = 1362
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1327"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1362"
}
docComment = """
Replaces all occurrences of [pattern] in this string with the return value of [mapper].
@@ -1159,9 +1198,9 @@ alias {
})
}, "replaceRange", new {
location {
line = 1332
line = 1367
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1332"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1367"
}
docComment = """
Replaces the characters between [start] and [exclusiveEnd] with [replacement].
@@ -1181,9 +1220,9 @@ alias {
})
}, "toUpperCase", new {
location {
line = 1335
line = 1370
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1335"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1370"
}
docComment = "Performs a locale-independent character-by-character conversion of this string to uppercase."
annotations = List()
@@ -1193,9 +1232,9 @@ alias {
parameters = Map()
}, "toLowerCase", new {
location {
line = 1338
line = 1373
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1338"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1373"
}
docComment = "Performs a locale-independent character-by-character conversion of this string to lowercase."
annotations = List()
@@ -1205,9 +1244,9 @@ alias {
parameters = Map()
}, "reverse", new {
location {
line = 1341
line = 1376
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1341"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1376"
}
docComment = "Reverses the order of characters in this string."
annotations = List()
@@ -1217,9 +1256,9 @@ alias {
parameters = Map()
}, "trim", new {
location {
line = 1345
line = 1380
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1345"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1380"
}
docComment = "Removes any leading and trailing characters with Unicode property \"White_Space\" from this string."
annotations = List(new {
@@ -1233,9 +1272,9 @@ alias {
parameters = Map()
}, "trimStart", new {
location {
line = 1349
line = 1384
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1349"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1384"
}
docComment = "Removes any leading characters with Unicode property \"White_Space\" from this string."
annotations = List(new {
@@ -1253,9 +1292,9 @@ alias {
parameters = Map()
}, "trimEnd", new {
location {
line = 1353
line = 1388
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1353"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1388"
}
docComment = "Removes any trailing characters with Unicode property \"White_Space\" from this string."
annotations = List(new {
@@ -1273,9 +1312,9 @@ alias {
parameters = Map()
}, "padStart", new {
location {
line = 1359
line = 1394
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1359"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1394"
}
docComment = """
Increases the length of this string to [width] by adding leading [char]s.
@@ -1297,9 +1336,9 @@ alias {
})
}, "padEnd", new {
location {
line = 1365
line = 1400
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1365"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1400"
}
docComment = """
Increases the length of this string to [width] by adding trailing [char]s.
@@ -1321,9 +1360,9 @@ alias {
})
}, "split", new {
location {
line = 1368
line = 1403
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1368"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1403"
}
docComment = "Splits this string around matches of [pattern]."
annotations = List()
@@ -1335,9 +1374,9 @@ alias {
})
}, "splitLimit", new {
location {
line = 1383
line = 1418
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1383"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1418"
}
docComment = """
Splits this string matches of [pattern], up to [limit] substrings.
@@ -1366,9 +1405,9 @@ alias {
})
}, "capitalize", new {
location {
line = 1393
line = 1428
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1393"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1428"
}
docComment = """
Converts the first character of this string to title case.
@@ -1387,9 +1426,9 @@ alias {
parameters = Map()
}, "decapitalize", new {
location {
line = 1403
line = 1438
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1403"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1438"
}
docComment = """
Converts the first character of this string to lower case.
@@ -1408,9 +1447,9 @@ alias {
parameters = Map()
}, "toInt", new {
location {
line = 1409
line = 1444
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1409"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1444"
}
docComment = """
Parses this string as a signed decimal (base 10) integer.
@@ -1425,9 +1464,9 @@ alias {
parameters = Map()
}, "toIntOrNull", new {
location {
line = 1415
line = 1450
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1415"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1450"
}
docComment = """
Parses this string as a signed decimal (base 10) integer.
@@ -1442,9 +1481,9 @@ alias {
parameters = Map()
}, "toFloat", new {
location {
line = 1420
line = 1455
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1420"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1455"
}
docComment = """
Parses this string as a floating point number.
@@ -1458,9 +1497,9 @@ alias {
parameters = Map()
}, "toFloatOrNull", new {
location {
line = 1425
line = 1460
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1425"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1460"
}
docComment = """
Parses this string as a floating point number.
@@ -1474,9 +1513,9 @@ alias {
parameters = Map()
}, "toBoolean", new {
location {
line = 1430
line = 1465
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1430"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1465"
}
docComment = """
Parses `"true"` to [true] and `"false"` to [false] (case-insensitive).
@@ -1490,9 +1529,9 @@ alias {
parameters = Map()
}, "toBooleanOrNull", new {
location {
line = 1435
line = 1470
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1435"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1470"
}
docComment = """
Parses `"true"` to [true] and `"false"` to [false] (case-insensitive).
@@ -1504,6 +1543,29 @@ alias {
name = "toBooleanOrNull"
typeParameters = List()
parameters = Map()
}, "encodeToBytes", new {
location {
line = 1479
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1479"
}
docComment = """
Returns the bytes of this string, encoded using [charset].
Facts:
```
"Parrot".encodeToBytes("UTF-8") == Bytes(80, 97, 114, 114, 111, 116)
```
"""
annotations = List(new {
version = "0.29.0"
})
modifiers = Set()
name = "encodeToBytes"
typeParameters = List()
parameters = Map("charset", new {
name = "charset"
})
})
}
typeArguments = List()
@@ -1524,9 +1586,9 @@ rec {
typeParameters = List()
superclass {
location {
line = 1721
line = 1773
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1721"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1773"
}
docComment = """
Base class for objects whose members are described by a class definition.
@@ -1539,9 +1601,9 @@ rec {
typeParameters = List()
superclass {
location {
line = 1716
line = 1768
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1716"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1768"
}
docComment = """
A composite value containing members (properties, elements, entries).
@@ -1765,9 +1827,9 @@ rec {
supertype {
referent {
location {
line = 1716
line = 1768
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1716"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1768"
}
docComment = """
A composite value containing members (properties, elements, entries).
@@ -1993,9 +2055,9 @@ rec {
properties = Map()
methods = Map("hasProperty", new {
location {
line = 1723
line = 1775
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1723"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1775"
}
docComment = "Tells if this object has a property with the given [name]."
annotations = List()
@@ -2007,9 +2069,9 @@ rec {
})
}, "getProperty", new {
location {
line = 1728
line = 1780
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1728"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1780"
}
docComment = """
Returns the value of the property with the given [name].
@@ -2025,9 +2087,9 @@ rec {
})
}, "getPropertyOrNull", new {
location {
line = 1733
line = 1785
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1733"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1785"
}
docComment = """
Returns the value of the property with the given [name].
@@ -2043,9 +2105,9 @@ rec {
})
}, "toDynamic", new {
location {
line = 1736
line = 1788
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1736"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1788"
}
docComment = "Converts this object to a [Dynamic] object."
annotations = List()
@@ -2055,9 +2117,9 @@ rec {
parameters = Map()
}, "toMap", new {
location {
line = 1739
line = 1791
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1739"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1791"
}
docComment = "Converts this object to a [Map]."
annotations = List()
@@ -2070,9 +2132,9 @@ rec {
supertype {
referent {
location {
line = 1721
line = 1773
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1721"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1773"
}
docComment = """
Base class for objects whose members are described by a class definition.
@@ -2085,9 +2147,9 @@ rec {
typeParameters = List()
superclass {
location {
line = 1716
line = 1768
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1716"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1768"
}
docComment = """
A composite value containing members (properties, elements, entries).
@@ -2311,9 +2373,9 @@ rec {
supertype {
referent {
location {
line = 1716
line = 1768
column = 1
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1716"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1768"
}
docComment = """
A composite value containing members (properties, elements, entries).
@@ -2539,9 +2601,9 @@ rec {
properties = Map()
methods = Map("hasProperty", new {
location {
line = 1723
line = 1775
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1723"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1775"
}
docComment = "Tells if this object has a property with the given [name]."
annotations = List()
@@ -2553,9 +2615,9 @@ rec {
})
}, "getProperty", new {
location {
line = 1728
line = 1780
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1728"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1780"
}
docComment = """
Returns the value of the property with the given [name].
@@ -2571,9 +2633,9 @@ rec {
})
}, "getPropertyOrNull", new {
location {
line = 1733
line = 1785
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1733"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1785"
}
docComment = """
Returns the value of the property with the given [name].
@@ -2589,9 +2651,9 @@ rec {
})
}, "toDynamic", new {
location {
line = 1736
line = 1788
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1736"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1788"
}
docComment = "Converts this object to a [Dynamic] object."
annotations = List()
@@ -2601,9 +2663,9 @@ rec {
parameters = Map()
}, "toMap", new {
location {
line = 1739
line = 1791
column = 3
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1739"
displayUri = "https://github.com/apple/pkl/blob/$commitId/stdlib/base.pkl#L1791"
}
docComment = "Converts this object to a [Map]."
annotations = List()

View File

@@ -9,6 +9,11 @@ facts {
true
true
}
["isBase64"] {
true
true
true
}
["lastIndex"] {
true
true
@@ -18,6 +23,9 @@ facts {
true
true
}
["base64DecodedBytes"] {
true
}
["contains()"] {
true
true
@@ -417,4 +425,17 @@ examples {
["base64Decoded"] {
"Illegal base64 character 7e String: \"~~~\""
}
["base64DecodedBytes"] {
"Illegal base64 character 7e String: \"~~~\""
Bytes(1, 2, 3, 4)
}
["encodeToBytes()"] {
Bytes(126, 126, 126)
Bytes(240, 159, 143, 128)
Bytes(254, 255, 0, 126, 0, 126, 0, 126)
Bytes(254, 255, 216, 60, 223, 192)
Bytes(126, 126, 126)
Bytes(63)
Bytes(80, 97, 114, 114, 111, 116)
}
}

View File

@@ -0,0 +1,46 @@
examples {
["basic"] {
Bytes(0, 255)
}
["bytes from constant value nodes"] {
Bytes(0, 1, 2, 3, 4, 5, 6, 7)
Bytes(0, 1, 2, 3, 4, 5, 6, 7)
Bytes(0, 1, 2, 3, 4, 5, 6, 7)
Bytes(0, 1, 2, 3, 4, 5, 6, 7)
Bytes(0, 1, 2, 3, 4, 5, 6, 7)
}
["bytes from non-constant value nodes"] {
Bytes(1, 2)
}
["incorrectly sized constant values"] {
"Type constraint `isBetween(0, 255)` violated. Value: 65535"
}
["equality"] {
true
false
false
true
}
["inequality"] {
false
true
true
false
}
["addition"] {
Bytes(1, 2, 3, 1, 2, 4)
Bytes()
Bytes(1, 2, 3)
Bytes(1, 2, 4)
"Operator `+` is not defined for operand types `Bytes` and `List`. Left operand : Bytes(1, 2, 3) Right operand: List(1, 2, 4)"
}
["subscript"] {
1
2
3
"Element index `3` is out of range `0`..`2`. Value: Bytes(1, 2, 3)"
}
["all bytes"] {
Bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255)
}
}

View File

@@ -22,7 +22,7 @@ examples {
file1
"""
base64 = "ZmlsZTEK"
bytes = Bytes(102, 105, 108, 101, 49, 10)
}
"Cannot find resource `other.txt`."
new {
@@ -31,7 +31,7 @@ examples {
file1
"""
base64 = "ZmlsZTEK"
bytes = Bytes(102, 105, 108, 101, 49, 10)
}
null
}
@@ -42,7 +42,7 @@ examples {
resource
"""
base64 = "cmVzb3VyY2UK"
bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
}
new {
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
@@ -50,7 +50,7 @@ examples {
resource
"""
base64 = "cmVzb3VyY2UK"
bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
}
}
["read non-allowed resource"] {
@@ -64,7 +64,7 @@ examples {
file1
"""
base64 = "ZmlsZTEK"
bytes = Bytes(102, 105, 108, 101, 49, 10)
}
new {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
@@ -72,7 +72,7 @@ examples {
file2
"""
base64 = "ZmlsZTIK"
bytes = Bytes(102, 105, 108, 101, 50, 10)
}
}
["read different resources with same relative resource URI"] {
@@ -82,7 +82,7 @@ examples {
resource
"""
base64 = "cmVzb3VyY2UK"
bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
}
new {
uri = "file:///$snippetsDir/input-helper/basic/read/child/resource.txt"
@@ -90,7 +90,7 @@ examples {
child resource
"""
base64 = "Y2hpbGQgcmVzb3VyY2UK"
bytes = Bytes(99, 104, 105, 108, 100, 32, 114, 101, 115, 111, 117, 114, 99, 101, 10)
}
}
}

View File

@@ -4,7 +4,7 @@ examples {
["../../input-helper/globtest/module with [weird] ~!characters.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/module%20with%20%5Bweird%5D%20~!characters.pkl"
text = ""
base64 = ""
bytes = Bytes()
}
["../../input-helper/globtest/moduleA.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleA.pkl"
@@ -12,7 +12,7 @@ examples {
name = "moduleA"
"""
base64 = "bmFtZSA9ICJtb2R1bGVBIgo="
bytes = Bytes(110, 97, 109, 101, 32, 61, 32, 34, 109, 111, 100, 117, 108, 101, 65, 34, 10)
}
["../../input-helper/globtest/moduleB.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleB.pkl"
@@ -20,7 +20,7 @@ examples {
name = "moduleB"
"""
base64 = "bmFtZSA9ICJtb2R1bGVCIgo="
bytes = Bytes(110, 97, 109, 101, 32, 61, 32, 34, 109, 111, 100, 117, 108, 101, 66, 34, 10)
}
["../../input-helper/globtest/child/moduleC.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/child/moduleC.pkl"
@@ -28,7 +28,7 @@ examples {
name = "child/moduleC"
"""
base64 = "bmFtZSA9ICJjaGlsZC9tb2R1bGVDIgo="
bytes = Bytes(110, 97, 109, 101, 32, 61, 32, 34, 99, 104, 105, 108, 100, 47, 109, 111, 100, 117, 108, 101, 67, 34, 10)
}
}
new {
@@ -38,7 +38,7 @@ examples {
file1
"""
base64 = "ZmlsZTEK"
bytes = Bytes(102, 105, 108, 101, 49, 10)
}
["globtest/file2.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
@@ -46,7 +46,7 @@ examples {
file2
"""
base64 = "ZmlsZTIK"
bytes = Bytes(102, 105, 108, 101, 50, 10)
}
}
}
@@ -55,22 +55,22 @@ examples {
["../../input-helper/globtest/module with [weird] ~!characters.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/module%20with%20%5Bweird%5D%20~!characters.pkl"
text = "hi"
base64 = ""
bytes = Bytes()
}
["../../input-helper/globtest/moduleA.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleA.pkl"
text = "hi"
base64 = "bmFtZSA9ICJtb2R1bGVBIgo="
bytes = Bytes(110, 97, 109, 101, 32, 61, 32, 34, 109, 111, 100, 117, 108, 101, 65, 34, 10)
}
["../../input-helper/globtest/moduleB.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleB.pkl"
text = "hi"
base64 = "bmFtZSA9ICJtb2R1bGVCIgo="
bytes = Bytes(110, 97, 109, 101, 32, 61, 32, 34, 109, 111, 100, 117, 108, 101, 66, 34, 10)
}
["../../input-helper/globtest/child/moduleC.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/child/moduleC.pkl"
text = "hi"
base64 = "bmFtZSA9ICJjaGlsZC9tb2R1bGVDIgo="
bytes = Bytes(110, 97, 109, 101, 32, 61, 32, 34, 99, 104, 105, 108, 100, 47, 109, 111, 100, 117, 108, 101, 67, 34, 10)
}
}
}
@@ -132,7 +132,7 @@ examples {
favoriteFruit: Fruit
"""
base64 = "b3BlbiBtb2R1bGUgYmlyZHMuQmlyZAoKaW1wb3J0ICJAZnJ1aXRpZXMvRnJ1aXQucGtsIgoKbmFtZTogU3RyaW5nCgpmYXZvcml0ZUZydWl0OiBGcnVpdAo="
bytes = Bytes(111, 112, 101, 110, 32, 109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 66, 105, 114, 100, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 70, 114, 117, 105, 116, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 58, 32, 83, 116, 114, 105, 110, 103, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 58, 32, 70, 114, 117, 105, 116, 10)
}
["package://localhost:0/birds@0.5.0#/allFruit.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/allFruit.pkl"
@@ -143,7 +143,7 @@ examples {
fruitFiles = read*("@fruities/catalog/*.pkl")
"""
base64 = "bW9kdWxlIGJpcmRzLmFsbEZydWl0CgpmcnVpdCA9IGltcG9ydCooIkBmcnVpdGllcy9jYXRhbG9nLyoucGtsIikKZnJ1aXRGaWxlcyA9IHJlYWQqKCJAZnJ1aXRpZXMvY2F0YWxvZy8qLnBrbCIpCg=="
bytes = Bytes(109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 97, 108, 108, 70, 114, 117, 105, 116, 10, 10, 102, 114, 117, 105, 116, 32, 61, 32, 105, 109, 112, 111, 114, 116, 42, 40, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10, 102, 114, 117, 105, 116, 70, 105, 108, 101, 115, 32, 61, 32, 114, 101, 97, 100, 42, 40, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10)
}
["package://localhost:0/birds@0.5.0#/catalog.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/catalog.pkl"
@@ -154,7 +154,7 @@ examples {
catalogFiles = read*("catalog/*.pkl")
"""
base64 = "bW9kdWxlIGJpcmRzLmNhdGFsb2cKCmNhdGFsb2cgPSBpbXBvcnQqKCJjYXRhbG9nLyoucGtsIikKY2F0YWxvZ0ZpbGVzID0gcmVhZCooImNhdGFsb2cvKi5wa2wiKQo="
bytes = Bytes(109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 99, 97, 116, 97, 108, 111, 103, 10, 10, 99, 97, 116, 97, 108, 111, 103, 32, 61, 32, 105, 109, 112, 111, 114, 116, 42, 40, 34, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10, 99, 97, 116, 97, 108, 111, 103, 70, 105, 108, 101, 115, 32, 61, 32, 114, 101, 97, 100, 42, 40, 34, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10)
}
["package://localhost:0/birds@0.5.0#/catalog/Ostrich.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/catalog/Ostrich.pkl"
@@ -168,7 +168,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
["package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -182,7 +182,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
["package://localhost:0/birds@0.5.0#/some/dir/Bird.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/some/dir/Bird.pkl"
@@ -196,7 +196,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi4iCgpuYW1lID0gIkJpcmQiCgpmYXZvcml0ZUZydWl0IHsKICBuYW1lID0gIkZydWl0Igp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 46, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 66, 105, 114, 100, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 70, 114, 117, 105, 116, 34, 10, 125, 10)
}
}
}
@@ -208,7 +208,7 @@ examples {
file1
"""
base64 = "ZmlsZTEK"
bytes = Bytes(102, 105, 108, 101, 49, 10)
}
["globtest/file2.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
@@ -216,7 +216,7 @@ examples {
file2
"""
base64 = "ZmlsZTIK"
bytes = Bytes(102, 105, 108, 101, 50, 10)
}
}
new {
@@ -226,7 +226,7 @@ examples {
file1
"""
base64 = "ZmlsZTEK"
bytes = Bytes(102, 105, 108, 101, 49, 10)
}
}
new {
@@ -236,7 +236,7 @@ examples {
file2
"""
base64 = "ZmlsZTIK"
bytes = Bytes(102, 105, 108, 101, 50, 10)
}
}
}
@@ -248,7 +248,7 @@ examples {
resource
"""
base64 = "cmVzb3VyY2UK"
bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
}
}
new {
@@ -258,7 +258,7 @@ examples {
child resource
"""
base64 = "Y2hpbGQgcmVzb3VyY2UK"
bytes = Bytes(99, 104, 105, 108, 100, 32, 114, 101, 115, 111, 117, 114, 99, 101, 10)
}
}
}

View File

@@ -0,0 +1,10 @@
Pkl Error
Invalid bytes for charset "UTF-8".
x | res = Bytes(0xc0, 0xc1).decodeToString("UTF-8")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at decodingException#res (file:///$snippetsDir/input/errors/decodingException.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -0,0 +1,15 @@
Pkl Error
Type constraint `isBetween(0, 255)` violated.
Value: 65535
xxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at invalidBytes1#foo (pkl:base)
x | foo = Bytes(0, 1, 2, 3, 0xffff)
^^^^^^
at invalidBytes1#foo (file:///$snippetsDir/input/errors/invalidBytes1.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -0,0 +1,15 @@
Pkl Error
Type constraint `isBetween(0, 255)` violated.
Value: 4095
xxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at invalidBytes2#res (pkl:base)
x | res = Bytes(0, 1, 2, 3, num)
^^^
at invalidBytes2#res (file:///$snippetsDir/input/errors/invalidBytes2.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -0,0 +1,15 @@
Pkl Error
Type constraint `isBetween(0, 255)` violated.
Value: 65535
xxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at invalidBytes3#bytes (pkl:base)
x | bytes = Bytes(0, 1, 2, 3, 0xffff)
^^^^^^
at invalidBytes3#bytes (file:///$snippetsDir/input/errors/invalidBytes3.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -0,0 +1,15 @@
Pkl Error
Type constraint `isBetween(0, 255)` violated.
Value: 65535
xxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at pkl.base#List.toBytes (pkl:base)
x | res = List(1, 2, 3, 0xffff).toBytes()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at invalidBytes4#res (file:///$snippetsDir/input/errors/invalidBytes4.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -226,6 +226,13 @@ res11d {
Pair(1, 2)
Pair(2, 3)
}
res12a {
1
2
3
4
}
res12b {}
valueForOverProperties {
["Foo!"] = "Foo!"
[42] = 42

View File

@@ -37,6 +37,10 @@ examples {
10
20
30
1
2
3
4
}
}
["explicit Dynamic type"] {
@@ -69,6 +73,10 @@ examples {
10
20
30
1
2
3
4
}
}
["legacy syntax"] {
@@ -105,5 +113,11 @@ examples {
20
30
}
new {
1
2
3
4
}
}
}

View File

@@ -103,3 +103,19 @@ res16 {
0
}
res17 = 7
res18 {
1
2
3
4
}
res19 {
0
1
2
3
4
0
0
0
}

View File

@@ -55,3 +55,4 @@ res17 {
}
}
}
res18 = "Cannot spread value of type `Bytes` into object of type `Mapping`. Value: Bytes(1, 2, 3, 4)"

View File

@@ -7,6 +7,11 @@ at invalidAmend6#resource (file:///$snippetsDir/input/modules/invalidAmend6.pkl)
Available properties:
base64
bytes
md5
sha1
sha256
sha256Int
text
uri

View File

@@ -40,7 +40,7 @@ examples {
favoriteFruit: Fruit
"""
base64 = "b3BlbiBtb2R1bGUgYmlyZHMuQmlyZAoKaW1wb3J0ICJAZnJ1aXRpZXMvRnJ1aXQucGtsIgoKbmFtZTogU3RyaW5nCgpmYXZvcml0ZUZydWl0OiBGcnVpdAo="
bytes = Bytes(111, 112, 101, 110, 32, 109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 66, 105, 114, 100, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 70, 114, 117, 105, 116, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 58, 32, 83, 116, 114, 105, 110, 103, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 58, 32, 70, 114, 117, 105, 116, 10)
}
new {
uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -54,7 +54,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
new {
uri = "package://localhost:0/birds@0.5.0#/catalog/Ostrich.pkl"
@@ -68,7 +68,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
}
}

View File

@@ -52,7 +52,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
["package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -66,7 +66,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
}
}
@@ -84,7 +84,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
["catalog/Swallow.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -98,7 +98,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
}
}
@@ -112,7 +112,7 @@ examples {
name = "Apple"
"""
base64 = "YW1lbmRzICIuLi9GcnVpdC5wa2wiCgpuYW1lID0gIkFwcGxlIgo="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 70, 114, 117, 105, 116, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 65, 112, 112, 108, 101, 34, 10)
}
}
}

View File

@@ -44,7 +44,7 @@ examples {
Swallow.pkl
"""
base64 = "T3N0cmljaC5wa2wKU3dhbGxvdy5wa2wK"
bytes = Bytes(79, 115, 116, 114, 105, 99, 104, 46, 112, 107, 108, 10, 83, 119, 97, 108, 108, 111, 119, 46, 112, 107, 108, 10)
}
new {
uri = "projectpackage://localhost:0/birds@0.5.0#/"
@@ -56,7 +56,7 @@ examples {
some
"""
base64 = "QmlyZC5wa2wKYWxsRnJ1aXQucGtsCmNhdGFsb2cKY2F0YWxvZy5wa2wKc29tZQo="
bytes = Bytes(66, 105, 114, 100, 46, 112, 107, 108, 10, 97, 108, 108, 70, 114, 117, 105, 116, 46, 112, 107, 108, 10, 99, 97, 116, 97, 108, 111, 103, 10, 99, 97, 116, 97, 108, 111, 103, 46, 112, 107, 108, 10, 115, 111, 109, 101, 10)
}
}
}

View File

@@ -62,7 +62,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
["@birds/catalog/Swallow.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -76,7 +76,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
}
new {
@@ -92,7 +92,7 @@ examples {
favoriteFruit: Fruit
"""
base64 = "b3BlbiBtb2R1bGUgYmlyZHMuQmlyZAoKaW1wb3J0ICJAZnJ1aXRpZXMvRnJ1aXQucGtsIgoKbmFtZTogU3RyaW5nCgpmYXZvcml0ZUZydWl0OiBGcnVpdAo="
bytes = Bytes(111, 112, 101, 110, 32, 109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 66, 105, 114, 100, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 70, 114, 117, 105, 116, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 58, 32, 83, 116, 114, 105, 110, 103, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 58, 32, 70, 114, 117, 105, 116, 10)
}
["@birds/allFruit.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/allFruit.pkl"
@@ -103,7 +103,7 @@ examples {
fruitFiles = read*("@fruities/catalog/*.pkl")
"""
base64 = "bW9kdWxlIGJpcmRzLmFsbEZydWl0CgpmcnVpdCA9IGltcG9ydCooIkBmcnVpdGllcy9jYXRhbG9nLyoucGtsIikKZnJ1aXRGaWxlcyA9IHJlYWQqKCJAZnJ1aXRpZXMvY2F0YWxvZy8qLnBrbCIpCg=="
bytes = Bytes(109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 97, 108, 108, 70, 114, 117, 105, 116, 10, 10, 102, 114, 117, 105, 116, 32, 61, 32, 105, 109, 112, 111, 114, 116, 42, 40, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10, 102, 114, 117, 105, 116, 70, 105, 108, 101, 115, 32, 61, 32, 114, 101, 97, 100, 42, 40, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10)
}
["@birds/catalog.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog.pkl"
@@ -114,7 +114,7 @@ examples {
catalogFiles = read*("catalog/*.pkl")
"""
base64 = "bW9kdWxlIGJpcmRzLmNhdGFsb2cKCmNhdGFsb2cgPSBpbXBvcnQqKCJjYXRhbG9nLyoucGtsIikKY2F0YWxvZ0ZpbGVzID0gcmVhZCooImNhdGFsb2cvKi5wa2wiKQo="
bytes = Bytes(109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 99, 97, 116, 97, 108, 111, 103, 10, 10, 99, 97, 116, 97, 108, 111, 103, 32, 61, 32, 105, 109, 112, 111, 114, 116, 42, 40, 34, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10, 99, 97, 116, 97, 108, 111, 103, 70, 105, 108, 101, 115, 32, 61, 32, 114, 101, 97, 100, 42, 40, 34, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10)
}
}
new {
@@ -130,7 +130,7 @@ examples {
favoriteFruit: Fruit
"""
base64 = "b3BlbiBtb2R1bGUgYmlyZHMuQmlyZAoKaW1wb3J0ICJAZnJ1aXRpZXMvRnJ1aXQucGtsIgoKbmFtZTogU3RyaW5nCgpmYXZvcml0ZUZydWl0OiBGcnVpdAo="
bytes = Bytes(111, 112, 101, 110, 32, 109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 66, 105, 114, 100, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 70, 114, 117, 105, 116, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 58, 32, 83, 116, 114, 105, 110, 103, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 58, 32, 70, 114, 117, 105, 116, 10)
}
["@birds/allFruit.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/allFruit.pkl"
@@ -141,7 +141,7 @@ examples {
fruitFiles = read*("@fruities/catalog/*.pkl")
"""
base64 = "bW9kdWxlIGJpcmRzLmFsbEZydWl0CgpmcnVpdCA9IGltcG9ydCooIkBmcnVpdGllcy9jYXRhbG9nLyoucGtsIikKZnJ1aXRGaWxlcyA9IHJlYWQqKCJAZnJ1aXRpZXMvY2F0YWxvZy8qLnBrbCIpCg=="
bytes = Bytes(109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 97, 108, 108, 70, 114, 117, 105, 116, 10, 10, 102, 114, 117, 105, 116, 32, 61, 32, 105, 109, 112, 111, 114, 116, 42, 40, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10, 102, 114, 117, 105, 116, 70, 105, 108, 101, 115, 32, 61, 32, 114, 101, 97, 100, 42, 40, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10)
}
["@birds/catalog.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog.pkl"
@@ -152,7 +152,7 @@ examples {
catalogFiles = read*("catalog/*.pkl")
"""
base64 = "bW9kdWxlIGJpcmRzLmNhdGFsb2cKCmNhdGFsb2cgPSBpbXBvcnQqKCJjYXRhbG9nLyoucGtsIikKY2F0YWxvZ0ZpbGVzID0gcmVhZCooImNhdGFsb2cvKi5wa2wiKQo="
bytes = Bytes(109, 111, 100, 117, 108, 101, 32, 98, 105, 114, 100, 115, 46, 99, 97, 116, 97, 108, 111, 103, 10, 10, 99, 97, 116, 97, 108, 111, 103, 32, 61, 32, 105, 109, 112, 111, 114, 116, 42, 40, 34, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10, 99, 97, 116, 97, 108, 111, 103, 70, 105, 108, 101, 115, 32, 61, 32, 114, 101, 97, 100, 42, 40, 34, 99, 97, 116, 97, 108, 111, 103, 47, 42, 46, 112, 107, 108, 34, 41, 10)
}
["@birds/catalog/Ostrich.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Ostrich.pkl"
@@ -166,7 +166,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
["@birds/catalog/Swallow.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -180,7 +180,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
["@birds/some/dir/Bird.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/some/dir/Bird.pkl"
@@ -194,7 +194,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi4iCgpuYW1lID0gIkJpcmQiCgpmYXZvcml0ZUZydWl0IHsKICBuYW1lID0gIkZydWl0Igp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 46, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 66, 105, 114, 100, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 70, 114, 117, 105, 116, 34, 10, 125, 10)
}
}
}
@@ -212,7 +212,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
["catalog/Swallow.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -226,7 +226,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
}
}
@@ -244,7 +244,7 @@ examples {
}
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCm5hbWUgPSAiT3N0cmljaCIKCmZhdm9yaXRlRnJ1aXQgewogIG5hbWUgPSAiT3JhbmdlIgp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 79, 115, 116, 114, 105, 99, 104, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 79, 114, 97, 110, 103, 101, 34, 10, 125, 10)
}
["package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -258,7 +258,7 @@ examples {
favoriteFruit = apple
"""
base64 = "YW1lbmRzICIuLi9CaXJkLnBrbCIKCmltcG9ydCAiQGZydWl0aWVzL2NhdGFsb2cvYXBwbGUucGtsIgoKbmFtZSA9ICJTd2FsbG93IgoKZmF2b3JpdGVGcnVpdCA9IGFwcGxlCg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 46, 46, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 105, 109, 112, 111, 114, 116, 32, 34, 64, 102, 114, 117, 105, 116, 105, 101, 115, 47, 99, 97, 116, 97, 108, 111, 103, 47, 97, 112, 112, 108, 101, 46, 112, 107, 108, 34, 10, 10, 110, 97, 109, 101, 32, 61, 32, 34, 83, 119, 97, 108, 108, 111, 119, 34, 10, 10, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 61, 32, 97, 112, 112, 108, 101, 10)
}
}
}

View File

@@ -29,7 +29,7 @@ res2 {
}
"""
base64 = "YW1lbmRzICJwa2w6UHJvamVjdCIKCnBhY2thZ2UgewogIG5hbWUgPSAicHJvamVjdDIiCiAgYmFzZVVyaSA9ICJwYWNrYWdlOi8vbG9jYWxob3N0OjAvcHJvamVjdDIiCiAgdmVyc2lvbiA9ICIxLjAuMCIKICBwYWNrYWdlWmlwVXJsID0gImh0dHBzOi8vbG9jYWxob3N0OjAvcHJvamVjdDIvcHJvamVjdDItXCh2ZXJzaW9uKS56aXAiCn0KCmRlcGVuZGVuY2llcyB7CiAgWyJidXJkcyJdIHsKICAgIHVyaSA9ICJwYWNrYWdlOi8vbG9jYWxob3N0OjAvYmlyZHNAMC41LjAiCiAgfQp9Cg=="
bytes = Bytes(97, 109, 101, 110, 100, 115, 32, 34, 112, 107, 108, 58, 80, 114, 111, 106, 101, 99, 116, 34, 10, 10, 112, 97, 99, 107, 97, 103, 101, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 112, 114, 111, 106, 101, 99, 116, 50, 34, 10, 32, 32, 98, 97, 115, 101, 85, 114, 105, 32, 61, 32, 34, 112, 97, 99, 107, 97, 103, 101, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 48, 47, 112, 114, 111, 106, 101, 99, 116, 50, 34, 10, 32, 32, 118, 101, 114, 115, 105, 111, 110, 32, 61, 32, 34, 49, 46, 48, 46, 48, 34, 10, 32, 32, 112, 97, 99, 107, 97, 103, 101, 90, 105, 112, 85, 114, 108, 32, 61, 32, 34, 104, 116, 116, 112, 115, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 48, 47, 112, 114, 111, 106, 101, 99, 116, 50, 47, 112, 114, 111, 106, 101, 99, 116, 50, 45, 92, 40, 118, 101, 114, 115, 105, 111, 110, 41, 46, 122, 105, 112, 34, 10, 125, 10, 10, 100, 101, 112, 101, 110, 100, 101, 110, 99, 105, 101, 115, 32, 123, 10, 32, 32, 91, 34, 98, 117, 114, 100, 115, 34, 93, 32, 123, 10, 32, 32, 32, 32, 117, 114, 105, 32, 61, 32, 34, 112, 97, 99, 107, 97, 103, 101, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 48, 47, 98, 105, 114, 100, 115, 64, 48, 46, 53, 46, 48, 34, 10, 32, 32, 125, 10, 125, 10)
}
["@project2/PklProject.deps.json"] {
uri = "file:/$snippetsDir/input/projects/project2/PklProject.deps.json"
@@ -55,7 +55,7 @@ res2 {
}
"""
base64 = "ewogICJzY2hlbWFWZXJzaW9uIjogMSwKICAicmVzb2x2ZWREZXBlbmRlbmNpZXMiOiB7CiAgICAicGFja2FnZTovL2xvY2FsaG9zdDowL2JpcmRzQDAiOiB7CiAgICAgICJ0eXBlIjogInJlbW90ZSIsCiAgICAgICJ1cmkiOiAicHJvamVjdHBhY2thZ2U6Ly9sb2NhbGhvc3Q6MC9iaXJkc0AwLjUuMCIsCiAgICAgICJjaGVja3N1bXMiOiB7CiAgICAgICAgInNoYTI1NiI6ICIkc2tpcENoZWNrc3VtVmVyaWZpY2F0aW9uIgogICAgICB9CiAgICB9LAogICAgInBhY2thZ2U6Ly9sb2NhbGhvc3Q6MC9mcnVpdEAxIjogewogICAgICAidHlwZSI6ICJyZW1vdGUiLAogICAgICAidXJpIjogInByb2plY3RwYWNrYWdlOi8vbG9jYWxob3N0OjAvZnJ1aXRAMS4xLjAiLAogICAgICAiY2hlY2tzdW1zIjogewogICAgICAgICJzaGEyNTYiOiAiJHNraXBDaGVja3N1bVZlcmlmaWNhdGlvbiIKICAgICAgfQogICAgfQogIH0KfQo="
bytes = Bytes(123, 10, 32, 32, 34, 115, 99, 104, 101, 109, 97, 86, 101, 114, 115, 105, 111, 110, 34, 58, 32, 49, 44, 10, 32, 32, 34, 114, 101, 115, 111, 108, 118, 101, 100, 68, 101, 112, 101, 110, 100, 101, 110, 99, 105, 101, 115, 34, 58, 32, 123, 10, 32, 32, 32, 32, 34, 112, 97, 99, 107, 97, 103, 101, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 48, 47, 98, 105, 114, 100, 115, 64, 48, 34, 58, 32, 123, 10, 32, 32, 32, 32, 32, 32, 34, 116, 121, 112, 101, 34, 58, 32, 34, 114, 101, 109, 111, 116, 101, 34, 44, 10, 32, 32, 32, 32, 32, 32, 34, 117, 114, 105, 34, 58, 32, 34, 112, 114, 111, 106, 101, 99, 116, 112, 97, 99, 107, 97, 103, 101, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 48, 47, 98, 105, 114, 100, 115, 64, 48, 46, 53, 46, 48, 34, 44, 10, 32, 32, 32, 32, 32, 32, 34, 99, 104, 101, 99, 107, 115, 117, 109, 115, 34, 58, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 115, 104, 97, 50, 53, 54, 34, 58, 32, 34, 36, 115, 107, 105, 112, 67, 104, 101, 99, 107, 115, 117, 109, 86, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110, 34, 10, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 125, 44, 10, 32, 32, 32, 32, 34, 112, 97, 99, 107, 97, 103, 101, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 48, 47, 102, 114, 117, 105, 116, 64, 49, 34, 58, 32, 123, 10, 32, 32, 32, 32, 32, 32, 34, 116, 121, 112, 101, 34, 58, 32, 34, 114, 101, 109, 111, 116, 101, 34, 44, 10, 32, 32, 32, 32, 32, 32, 34, 117, 114, 105, 34, 58, 32, 34, 112, 114, 111, 106, 101, 99, 116, 112, 97, 99, 107, 97, 103, 101, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 48, 47, 102, 114, 117, 105, 116, 64, 49, 46, 49, 46, 48, 34, 44, 10, 32, 32, 32, 32, 32, 32, 34, 99, 104, 101, 99, 107, 115, 117, 109, 115, 34, 58, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 115, 104, 97, 50, 53, 54, 34, 58, 32, 34, 36, 115, 107, 105, 112, 67, 104, 101, 99, 107, 115, 117, 109, 86, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110, 34, 10, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 125, 10, 32, 32, 125, 10, 125, 10)
}
["@project2/penguin.pkl"] {
uri = "file:/$snippetsDir/input/projects/project2/penguin.pkl"
@@ -70,6 +70,6 @@ res2 {
}
"""
base64 = "aW1wb3J0ICJAYnVyZHMvQmlyZC5wa2wiCgpiaXJkOiBCaXJkID0gbmV3IHsKICBuYW1lID0gIlBlbmd1aW4iCiAgZmF2b3JpdGVGcnVpdCB7CiAgICBuYW1lID0gIkljZSBGcnVpdCIKICB9Cn0K"
bytes = Bytes(105, 109, 112, 111, 114, 116, 32, 34, 64, 98, 117, 114, 100, 115, 47, 66, 105, 114, 100, 46, 112, 107, 108, 34, 10, 10, 98, 105, 114, 100, 58, 32, 66, 105, 114, 100, 32, 61, 32, 110, 101, 119, 32, 123, 10, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 80, 101, 110, 103, 117, 105, 110, 34, 10, 32, 32, 102, 97, 118, 111, 114, 105, 116, 101, 70, 114, 117, 105, 116, 32, 123, 10, 32, 32, 32, 32, 110, 97, 109, 101, 32, 61, 32, 34, 73, 99, 101, 32, 70, 114, 117, 105, 116, 34, 10, 32, 32, 125, 10, 125, 10)
}
}

View File

@@ -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.
@@ -49,9 +49,13 @@ class EvaluateMultipleFileOutputTest {
val output = evaluator.evaluateOutputFiles(text(program))
assertThat(output.keys).isEqualTo(setOf("foo.yml", "bar.yml", "bar/biz.yml", "bar/../bark.yml"))
assertThat(output["foo.yml"]?.text).isEqualTo("foo: foo text")
assertThat(output["foo.yml"]?.bytes).isEqualTo("foo: foo text".toByteArray())
assertThat(output["bar.yml"]?.text).isEqualTo("bar: bar text")
assertThat(output["bar.yml"]?.bytes).isEqualTo("bar: bar text".toByteArray())
assertThat(output["bar/biz.yml"]?.text).isEqualTo("biz: bar biz")
assertThat(output["bar/biz.yml"]?.bytes).isEqualTo("biz: bar biz".toByteArray())
assertThat(output["bar/../bark.yml"]?.text).isEqualTo("bark: bark bark")
assertThat(output["bar/../bark.yml"]?.bytes).isEqualTo("bark: bark bark".toByteArray())
}
@Test

View File

@@ -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.
@@ -32,4 +32,26 @@ class VmValueRendererTest {
val none = VmNull.withDefault("default")
assertThat(renderer.render(none)).isEqualTo("null")
}
@Test
fun `render bytes`() {
val bytes = VmBytes((-128..127).map { it.toByte() }.toByteArray())
assertThat(renderer.render(bytes))
.isEqualTo("Bytes(128, 129, 130, 131, 132, 133, 134, 135, ... <total size: 256.b>)")
}
@Test
fun `render bytes - precisions`() {
val oneKbExactly = VmBytes(ByteArray(1000))
assertThat(renderer.render(oneKbExactly))
.isEqualTo("Bytes(0, 0, 0, 0, 0, 0, 0, 0, ... <total size: 1.kb>)")
val over1Kb = VmBytes(ByteArray(123467))
assertThat(renderer.render(over1Kb))
.isEqualTo("Bytes(0, 0, 0, 0, 0, 0, 0, 0, ... <total size: 123.47.kb>)")
val oneTenthPrecision = VmBytes(ByteArray(1100))
assertThat(renderer.render(oneTenthPrecision))
.isEqualTo("Bytes(0, 0, 0, 0, 0, 0, 0, 0, ... <total size: 1.1.kb>)")
}
}

Some files were not shown because too many files have changed in this diff Show More