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
@@ -138,6 +138,7 @@ endif::[]
:uri-stdlib-Function3: {uri-stdlib-baseModule}/Function3 :uri-stdlib-Function3: {uri-stdlib-baseModule}/Function3
:uri-stdlib-Function4: {uri-stdlib-baseModule}/Function4 :uri-stdlib-Function4: {uri-stdlib-baseModule}/Function4
:uri-stdlib-Function5: {uri-stdlib-baseModule}/Function5 :uri-stdlib-Function5: {uri-stdlib-baseModule}/Function5
:uri-stdlib-Bytes: {uri-stdlib-baseModule}/Bytes
:uri-stdlib-Resource: {uri-stdlib-baseModule}/Resource :uri-stdlib-Resource: {uri-stdlib-baseModule}/Resource
:uri-stdlib-outputFiles: {uri-stdlib-baseModule}/ModuleOutput#files :uri-stdlib-outputFiles: {uri-stdlib-baseModule}/ModuleOutput#files
@@ -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]] [[object-members]]
@@ -3684,6 +3684,41 @@ res5 = map.getOrNull("Falcon") // <5>
<4> result: `2` <4> result: `2`
<5> result: `null` <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]]
=== Regular Expressions === Regular Expressions
@@ -4671,6 +4706,10 @@ The following types are iterable:
|entry key (`Key`) |entry key (`Key`)
|entry value (`Value`) |entry value (`Value`)
|`Bytes`
|element index (`Int`)
|element value (`UInt8`)
|`Listing<Element>` |`Listing<Element>`
|element index (`Int`) |element index (`Int`)
|element value (`Element`) |element value (`Element`)
@@ -4751,6 +4790,9 @@ The following table describes how different iterables turn into object members:
| `IntSeq` | `IntSeq`
| Element | Element
| `Bytes`
| Element
|=== |===
These types can only be spread into enclosing objects that support that member type. These types can only be spread into enclosing objects that support that member type.
@@ -16,20 +16,23 @@
package org.pkl.cli package org.pkl.cli
import java.io.File import java.io.File
import java.io.Reader import java.io.InputStream
import java.io.Writer import java.io.OutputStream
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardOpenOption import java.nio.file.StandardOpenOption
import kotlin.io.path.createParentDirectories import kotlin.io.path.createParentDirectories
import kotlin.io.path.exists import kotlin.io.path.exists
import kotlin.io.path.isDirectory import kotlin.io.path.isDirectory
import kotlin.io.path.writeBytes
import org.pkl.commons.cli.CliCommand import org.pkl.commons.cli.CliCommand
import org.pkl.commons.cli.CliException import org.pkl.commons.cli.CliException
import org.pkl.commons.currentWorkingDir import org.pkl.commons.currentWorkingDir
import org.pkl.commons.writeString import org.pkl.commons.writeString
import org.pkl.core.Closeables import org.pkl.core.Closeables
import org.pkl.core.Evaluator
import org.pkl.core.EvaluatorBuilder import org.pkl.core.EvaluatorBuilder
import org.pkl.core.ModuleSource import org.pkl.core.ModuleSource
import org.pkl.core.PklException import org.pkl.core.PklException
@@ -48,8 +51,8 @@ constructor(
private val options: CliEvaluatorOptions, private val options: CliEvaluatorOptions,
// use System.{in,out}() rather than System.console() // use System.{in,out}() rather than System.console()
// because the latter returns null when output is sent through a unix pipe // because the latter returns null when output is sent through a unix pipe
private val consoleReader: Reader = System.`in`.reader(), private val inputStream: InputStream = System.`in`,
private val consoleWriter: Writer = System.out.writer(), private val outputStream: OutputStream = System.out,
) : CliCommand(options.base) { ) : CliCommand(options.base) {
/** /**
* Output files for the modules to be evaluated. Returns `null` if `options.outputPath` is `null` * 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]. * Evaluates source modules according to [options].
* *
* If [CliEvaluatorOptions.outputPath] is set, each module's `output.text` is written to the * If [CliEvaluatorOptions.outputPath] is set, each module's `output.bytes` is written to the
* module's [output file][outputFiles]. If [CliEvaluatorOptions.multipleFileOutputPath] is set, * module's [output file][outputFiles].
* 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 * If [CliEvaluatorOptions.multipleFileOutputPath] is set, each module's `output.files` are
* standard out). * 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. * 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) { private fun writeOutput(builder: EvaluatorBuilder) {
val evaluator = builder.setOutputFormat(options.outputFormat).build() val evaluator = builder.setOutputFormat(options.outputFormat).build()
evaluator.use { evaluator.use { ev ->
val outputFiles = fileOutputPaths val outputFiles = fileOutputPaths
if (outputFiles != null) { if (outputFiles != null) {
// files that we've written non-empty output to // files that we've written non-empty output to
@@ -151,18 +174,18 @@ constructor(
val writtenFiles = mutableSetOf<Path>() val writtenFiles = mutableSetOf<Path>()
for ((moduleUri, outputFile) in outputFiles) { for ((moduleUri, outputFile) in outputFiles) {
val moduleSource = toModuleSource(moduleUri, consoleReader) val moduleSource = toModuleSource(moduleUri, inputStream)
val output = evaluator.evaluateExpressionString(moduleSource, options.expression)
if (Files.isDirectory(outputFile)) { if (Files.isDirectory(outputFile)) {
throw CliException( throw CliException(
"Output file `$outputFile` is a directory. " + "Output file `$outputFile` is a directory. " +
"Did you mean `--multiple-file-output-path`?" "Did you mean `--multiple-file-output-path`?"
) )
} }
val output = ev.evalOutput(moduleSource)
outputFile.createParentDirectories() outputFile.createParentDirectories()
if (!writtenFiles.contains(outputFile)) { if (!writtenFiles.contains(outputFile)) {
// write file even if output is empty to overwrite output from previous runs // write file even if output is empty to overwrite output from previous runs
outputFile.writeString(output) outputFile.writeBytes(output)
if (output.isNotEmpty()) { if (output.isNotEmpty()) {
writtenFiles.add(outputFile) writtenFiles.add(outputFile)
} }
@@ -174,34 +197,49 @@ constructor(
StandardOpenOption.WRITE, StandardOpenOption.WRITE,
StandardOpenOption.APPEND, StandardOpenOption.APPEND,
) )
outputFile.writeString( outputFile.writeBytes(output, StandardOpenOption.WRITE, StandardOpenOption.APPEND)
output,
Charsets.UTF_8,
StandardOpenOption.WRITE,
StandardOpenOption.APPEND,
)
} }
} }
} }
} else { } else {
var outputWritten = false var outputWritten = false
for (moduleUri in options.base.normalizedSourceModules) { for (moduleUri in options.base.normalizedSourceModules) {
val moduleSource = toModuleSource(moduleUri, consoleReader) val moduleSource = toModuleSource(moduleUri, inputStream)
val output = evaluator.evaluateExpressionString(moduleSource, options.expression) if (options.expression != null) {
if (output.isNotEmpty()) { val output = evaluator.evaluateExpressionString(moduleSource, options.expression)
if (outputWritten) consoleWriter.appendLine(options.moduleOutputSeparator) if (output.isNotEmpty()) {
consoleWriter.write(output) if (outputWritten) outputStream.writeLine(options.moduleOutputSeparator)
consoleWriter.flush() outputStream.writeText(output)
outputWritten = true 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) = private fun OutputStream.writeText(text: String) = write(text.toByteArray())
if (uri == VmUtils.REPL_TEXT_URI) ModuleSource.create(uri, reader.readText())
else ModuleSource.uri(uri) 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) { private fun checkPathSpec(pathSpec: String) {
val illegal = pathSpec.indexOfFirst { IoUtils.isReservedFilenameChar(it) && it != '/' } val illegal = pathSpec.indexOfFirst { IoUtils.isReservedFilenameChar(it) && it != '/' }
@@ -223,7 +261,7 @@ constructor(
if (outputDir.exists() && !outputDir.isDirectory()) { if (outputDir.exists() && !outputDir.isDirectory()) {
throw CliException("Output path `$outputDir` exists and is not a directory.") 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) val output = evaluator.evaluateOutputFiles(moduleSource)
for ((pathSpec, fileOutput) in output) { for ((pathSpec, fileOutput) in output) {
checkPathSpec(pathSpec) checkPathSpec(pathSpec)
@@ -247,12 +285,12 @@ constructor(
} }
writtenFiles[realPath] = OutputFile(pathSpec, moduleUri) writtenFiles[realPath] = OutputFile(pathSpec, moduleUri)
realPath.createParentDirectories() realPath.createParentDirectories()
realPath.writeString(fileOutput.text) realPath.writeBytes(fileOutput.bytes)
consoleWriter.write( outputStream.writeText(
IoUtils.relativize(resolvedPath, currentWorkingDir).toString() + IoUtils.relativize(resolvedPath, currentWorkingDir).toString() +
IoUtils.getLineSeparator() IoUtils.getLineSeparator()
) )
consoleWriter.flush() outputStream.flush()
} }
} }
} }
@@ -74,9 +74,9 @@ data class CliEvaluatorOptions(
* *
* If set, the said expression is evaluated under the context of the enclosing module. * 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 { companion object {
@@ -18,10 +18,11 @@ package org.pkl.cli
import com.github.tomakehurst.wiremock.client.WireMock.* import com.github.tomakehurst.wiremock.client.WireMock.*
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.github.tomakehurst.wiremock.junit5.WireMockTest
import java.io.StringReader import java.io.ByteArrayInputStream
import java.io.StringWriter import java.io.ByteArrayOutputStream
import java.net.ServerSocket import java.net.ServerSocket
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.time.Duration import java.time.Duration
@@ -29,22 +30,20 @@ import java.util.regex.Pattern
import kotlin.io.path.* import kotlin.io.path.*
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.*
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.condition.DisabledOnOs import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.EnabledOnOs import org.junit.jupiter.api.condition.EnabledOnOs
import org.junit.jupiter.api.condition.OS import org.junit.jupiter.api.condition.OS
import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.api.io.TempDir
import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource import org.junit.jupiter.params.provider.EnumSource
import org.pkl.commons.*
import org.pkl.commons.cli.CliBaseOptions import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException import org.pkl.commons.cli.CliException
import org.pkl.commons.readString
import org.pkl.commons.test.FileTestUtils import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer 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.OutputFormat
import org.pkl.core.SecurityManagers import org.pkl.core.SecurityManagers
import org.pkl.core.util.IoUtils import org.pkl.core.util.IoUtils
@@ -157,7 +156,6 @@ person:
) )
assertThat(outputFiles).hasSize(1) assertThat(outputFiles).hasSize(1)
@Suppress("HttpUrlsUsage")
checkOutputFile( checkOutputFile(
outputFiles[0], outputFiles[0],
"test.plist", "test.plist",
@@ -469,8 +467,8 @@ result = someLib.x
@Test @Test
fun `take input from stdin`() { fun `take input from stdin`() {
val stdin = StringReader(defaultContents) val stdin = ByteArrayInputStream(defaultContents.toByteArray(StandardCharsets.UTF_8))
val stdout = StringWriter() val stdout = ByteArrayOutputStream()
val evaluator = val evaluator =
CliEvaluator( CliEvaluator(
CliEvaluatorOptions( CliEvaluatorOptions(
@@ -481,7 +479,8 @@ result = someLib.x
stdout, stdout,
) )
evaluator.run() 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 @Test
@@ -1101,9 +1100,9 @@ result = someLib.x
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir), CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir),
expression = "foo", expression = "foo",
) )
val buffer = StringWriter() val buffer = ByteArrayOutputStream()
CliEvaluator(options, consoleWriter = buffer).run() CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString()) assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo( .isEqualTo(
""" """
new Dynamic { bar = 1 } new Dynamic { bar = 1 }
@@ -1132,9 +1131,9 @@ result = someLib.x
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir), CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir),
expression = "person", expression = "person",
) )
val buffer = StringWriter() val buffer = ByteArrayOutputStream()
CliEvaluator(options, consoleWriter = buffer).run() CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString()).isEqualTo("Person(Frodo)") assertThat(buffer.toString(StandardCharsets.UTF_8)).isEqualTo("Person(Frodo)")
} }
@Test @Test
@@ -1154,9 +1153,10 @@ result = someLib.x
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir), CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir),
expression = "person", expression = "person",
) )
val buffer = StringWriter() val buffer = ByteArrayOutputStream()
CliEvaluator(options, consoleWriter = buffer).run() CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString()).isEqualTo("new Dynamic { friend { name = \"Bilbo\" } }") assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo("new Dynamic { friend { name = \"Bilbo\" } }")
} }
@Test @Test
@@ -1182,9 +1182,9 @@ result = someLib.x
CliEvaluatorOptions( CliEvaluatorOptions(
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir, noProject = true) CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir, noProject = true)
) )
val buffer = StringWriter() val buffer = ByteArrayOutputStream()
CliEvaluator(options, consoleWriter = buffer).run() CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString()).isEqualTo("res = 1\n") assertThat(buffer.toString(StandardCharsets.UTF_8)).isEqualTo("res = 1\n")
} }
@Test @Test
@@ -1215,9 +1215,9 @@ result = someLib.x
) )
val options = val options =
CliEvaluatorOptions(CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir)) CliEvaluatorOptions(CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir))
val buffer = StringWriter() val buffer = ByteArrayOutputStream()
CliEvaluator(options, consoleWriter = buffer).run() CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString()) assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo( .isEqualTo(
""" """
res { res {
@@ -1242,7 +1242,7 @@ result = someLib.x
""" """
.trimIndent(), .trimIndent(),
) )
val buffer = StringWriter() val buffer = ByteArrayOutputStream()
val options = val options =
CliEvaluatorOptions( CliEvaluatorOptions(
CliBaseOptions( CliBaseOptions(
@@ -1254,8 +1254,8 @@ result = someLib.x
testPort = packageServer.port, testPort = packageServer.port,
) )
) )
CliEvaluator(options, consoleWriter = buffer).run() CliEvaluator(options, outputStream = buffer).run()
assertThat(buffer.toString()) assertThat(buffer.toString(StandardCharsets.UTF_8))
.isEqualTo( .isEqualTo(
""" """
res { res {
@@ -1590,10 +1590,10 @@ result = someLib.x
} }
private fun evalToConsole(options: CliEvaluatorOptions): String { private fun evalToConsole(options: CliEvaluatorOptions): String {
val reader = StringReader("") val reader = ByteArrayInputStream(byteArrayOf())
val writer = StringWriter() val writer = ByteArrayOutputStream()
CliEvaluator(options, reader, writer).run() CliEvaluator(options, reader, writer).run()
return writer.toString() return writer.toString(StandardCharsets.UTF_8)
} }
private fun checkOutputFile(file: Path, name: String, contents: String) { private fun checkOutputFile(file: Path, name: String, contents: String) {
@@ -342,13 +342,22 @@ class JavaCodeGenerator(
var builderSize = 50 var builderSize = 50
val appendBuilder = CodeBlock.builder() val appendBuilder = CodeBlock.builder()
for (propertyName in allProperties.keys) { for ((propertyName, property) in allProperties) {
builderSize += 50 builderSize += 50
appendBuilder.addStatement( if (property.type.isBytesClass) {
"appendProperty(builder, \$S, this.\$N)", appendBuilder.addStatement(
propertyName, "appendProperty(builder, \$S, \$T.toString(this.\$N))",
propertyName, propertyName,
) Arrays::class.java,
propertyName,
)
} else {
appendBuilder.addStatement(
"appendProperty(builder, \$S, this.\$N)",
propertyName,
propertyName,
)
}
} }
builder 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 = private fun PType.toJavaPoetName(nullable: Boolean = false, boxed: Boolean = false): TypeName =
when (this) { when (this) {
PType.UNKNOWN -> OBJECT.nullableIf(nullable) PType.UNKNOWN -> OBJECT.nullableIf(nullable)
@@ -744,6 +756,7 @@ class JavaCodeGenerator(
PClassInfo.Float -> TypeName.DOUBLE.boxIf(boxed).nullableIf(nullable) PClassInfo.Float -> TypeName.DOUBLE.boxIf(boxed).nullableIf(nullable)
PClassInfo.Duration -> DURATION.nullableIf(nullable) PClassInfo.Duration -> DURATION.nullableIf(nullable)
PClassInfo.DataSize -> DATA_SIZE.nullableIf(nullable) PClassInfo.DataSize -> DATA_SIZE.nullableIf(nullable)
PClassInfo.Bytes -> ArrayTypeName.of(TypeName.BYTE)
PClassInfo.Pair -> PClassInfo.Pair ->
ParameterizedTypeName.get( ParameterizedTypeName.get(
PAIR, PAIR,
@@ -87,6 +87,7 @@ class JavaCodeGeneratorTest {
any: Any any: Any
nonNull: NonNull nonNull: NonNull
enum: Direction enum: Direction
bytes: Bytes
} }
class Other { class Other {
@@ -202,6 +203,7 @@ class JavaCodeGeneratorTest {
name = pigeon name = pigeon
} }
_enum = north _enum = north
bytes = [1, 2, 3, 4]
} }
""" """
.trimIndent() .trimIndent()
@@ -542,6 +544,7 @@ class JavaCodeGeneratorTest {
assertThat(readProperty(propertyTypes, "regex")).isInstanceOf(Pattern::class.java) assertThat(readProperty(propertyTypes, "regex")).isInstanceOf(Pattern::class.java)
assertThat(readProperty(propertyTypes, "any")).isEqualTo(other) assertThat(readProperty(propertyTypes, "any")).isEqualTo(other)
assertThat(readProperty(propertyTypes, "nonNull")).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? = private fun readProperty(obj: Any, property: String): Any? =
@@ -2313,6 +2316,7 @@ class JavaCodeGeneratorTest {
other, other,
other, other,
enumValue, enumValue,
byteArrayOf(1, 2, 3, 4),
) )
return other to propertyTypes return other to propertyTypes
@@ -4,6 +4,7 @@ import java.lang.Object;
import java.lang.Override; import java.lang.Override;
import java.lang.String; import java.lang.String;
import java.lang.StringBuilder; import java.lang.StringBuilder;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -86,6 +87,8 @@ public final class Mod {
public final @NonNull Direction _enum; public final @NonNull Direction _enum;
public final byte[] bytes;
public PropertyTypes(@Named("boolean") boolean _boolean, @Named("int") long _int, public PropertyTypes(@Named("boolean") boolean _boolean, @Named("int") long _int,
@Named("float") double _float, @Named("string") @NonNull String string, @Named("float") double _float, @Named("string") @NonNull String string,
@Named("duration") @NonNull Duration duration, @Named("duration") @NonNull Duration duration,
@@ -106,7 +109,7 @@ public final class Mod {
@Named("container2") @NonNull Map<@NonNull String, @NonNull Other> container2, @Named("container2") @NonNull Map<@NonNull String, @NonNull Other> container2,
@Named("other") @NonNull Other other, @Named("regex") @NonNull Pattern regex, @Named("other") @NonNull Other other, @Named("regex") @NonNull Pattern regex,
@Named("any") Object any, @Named("nonNull") @NonNull Object nonNull, @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._boolean = _boolean;
this._int = _int; this._int = _int;
this._float = _float; this._float = _float;
@@ -134,114 +137,119 @@ public final class Mod {
this.any = any; this.any = any;
this.nonNull = nonNull; this.nonNull = nonNull;
this._enum = _enum; this._enum = _enum;
this.bytes = bytes;
} }
public PropertyTypes withBoolean(boolean _boolean) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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 @Override
@@ -277,6 +285,7 @@ public final class Mod {
if (!Objects.equals(this.any, other.any)) return false; if (!Objects.equals(this.any, other.any)) return false;
if (!Objects.equals(this.nonNull, other.nonNull)) return false; if (!Objects.equals(this.nonNull, other.nonNull)) return false;
if (!Objects.equals(this._enum, other._enum)) return false; if (!Objects.equals(this._enum, other._enum)) return false;
if (!Objects.equals(this.bytes, other.bytes)) return false;
return true; return true;
} }
@@ -310,12 +319,13 @@ public final class Mod {
result = 31 * result + Objects.hashCode(this.any); result = 31 * result + Objects.hashCode(this.any);
result = 31 * result + Objects.hashCode(this.nonNull); result = 31 * result + Objects.hashCode(this.nonNull);
result = 31 * result + Objects.hashCode(this._enum); result = 31 * result + Objects.hashCode(this._enum);
result = 31 * result + Objects.hashCode(this.bytes);
return result; return result;
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(1400); StringBuilder builder = new StringBuilder(1450);
builder.append(PropertyTypes.class.getSimpleName()).append(" {"); builder.append(PropertyTypes.class.getSimpleName()).append(" {");
appendProperty(builder, "_boolean", this._boolean); appendProperty(builder, "_boolean", this._boolean);
appendProperty(builder, "_int", this._int); appendProperty(builder, "_int", this._int);
@@ -344,6 +354,7 @@ public final class Mod {
appendProperty(builder, "any", this.any); appendProperty(builder, "any", this.any);
appendProperty(builder, "nonNull", this.nonNull); appendProperty(builder, "nonNull", this.nonNull);
appendProperty(builder, "_enum", this._enum); appendProperty(builder, "_enum", this._enum);
appendProperty(builder, "bytes", Arrays.toString(this.bytes));
builder.append("\n}"); builder.append("\n}");
return builder.toString(); return builder.toString();
} }
@@ -226,6 +226,9 @@ class KotlinCodeGenerator(
fun PClass.Property.isRegex(): Boolean = fun PClass.Property.isRegex(): Boolean =
(this.type as? PType.Class)?.pClass?.info == PClassInfo.Regex (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 { fun generateConstructor(): FunSpec {
val builder = FunSpec.constructorBuilder() val builder = FunSpec.constructorBuilder()
for ((name, property) in allProperties) { for ((name, property) in allProperties) {
@@ -302,12 +305,20 @@ class KotlinCodeGenerator(
.addStatement("other as %T", kotlinPoetClassName) .addStatement("other as %T", kotlinPoetClassName)
for ((propertyName, property) in allProperties) { for ((propertyName, property) in allProperties) {
val accessor = if (property.isRegex()) "%N.pattern" else "%N" if (property.isByteArray()) {
builder.addStatement( builder.addStatement(
"if (this.$accessor != other.$accessor) return false", "if (!this.%N.contentEquals(other.%N)) return false",
propertyName, propertyName,
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") builder.addStatement("return true")
@@ -322,14 +333,18 @@ class KotlinCodeGenerator(
.addStatement("var result = 1") .addStatement("var result = 1")
for ((propertyName, property) in allProperties) { for ((propertyName, property) in allProperties) {
val accessor = if (property.isRegex()) "this.%N.pattern" else "this.%N" if (property.isByteArray()) {
// use Objects.hashCode() because Kotlin's Any?.hashCode() builder.addStatement("result = 31 * result + this.%N.contentHashCode()", propertyName)
// doesn't work for platform types (will get NPE if null) } else {
builder.addStatement( val accessor = if (property.isRegex()) "this.%N.pattern" else "this.%N"
"result = 31 * result + %T.hashCode($accessor)", // use Objects.hashCode() because Kotlin's Any?.hashCode()
Objects::class, // doesn't work for platform types (will get NPE if null)
propertyName, builder.addStatement(
) "result = 31 * result + %T.hashCode($accessor)",
Objects::class,
propertyName,
)
}
} }
builder.addStatement("return result") builder.addStatement("return result")
@@ -347,10 +362,15 @@ class KotlinCodeGenerator(
.apply { .apply {
add("%L", pClass.toKotlinPoetName().simpleName) add("%L", pClass.toKotlinPoetName().simpleName)
add("(") 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(if (index == 0) "%L" else ", %L", propertyName)
add("=$") if (property.isByteArray()) {
add("%N", propertyName) add("=\${%T.toString(%L)}", Arrays::class, propertyName)
} else {
add("=$")
add("%N", propertyName)
}
} }
add(")") add(")")
} }
@@ -488,16 +508,16 @@ class KotlinCodeGenerator(
builder.addKdoc(renderAsKdoc(docComment)) builder.addKdoc(renderAsKdoc(docComment))
} }
var hasRegex = false var hasRegexOrByteArray = false
for ((name, property) in properties) { for ((name, property) in properties) {
hasRegex = hasRegex || property.isRegex() hasRegexOrByteArray = hasRegexOrByteArray || property.isRegex() || property.isByteArray()
builder.addProperty(generateProperty(name, property)) builder.addProperty(generateProperty(name, property))
} }
// kotlin.text.Regex (and java.util.regex.Pattern) defines equality as identity. // Regex and ByteArray define equaltiy as identity.
// To match Pkl semantics and compare regexes by their String pattern, // To match Pkl semantics, override equals and hashCode if the data class has a property of
// override equals and hashCode if the data class has a property of type Regex. // type Regex or ByteArray.
if (hasRegex) { if (hasRegexOrByteArray) {
builder.addFunction(generateEqualsMethod()).addFunction(generateHashCodeMethod()) builder.addFunction(generateEqualsMethod()).addFunction(generateHashCodeMethod())
} }
@@ -632,6 +652,7 @@ class KotlinCodeGenerator(
PClassInfo.Float -> DOUBLE PClassInfo.Float -> DOUBLE
PClassInfo.Duration -> DURATION PClassInfo.Duration -> DURATION
PClassInfo.DataSize -> DATA_SIZE PClassInfo.DataSize -> DATA_SIZE
PClassInfo.Bytes -> BYTE_ARRAY
PClassInfo.Pair -> PClassInfo.Pair ->
KOTLIN_PAIR.parameterizedBy( KOTLIN_PAIR.parameterizedBy(
if (typeArguments.isEmpty()) ANY_NULL else typeArguments[0].toKotlinPoetName(), if (typeArguments.isEmpty()) ANY_NULL else typeArguments[0].toKotlinPoetName(),
@@ -120,6 +120,7 @@ class KotlinCodeGeneratorTest {
any: Any any: Any
nonNull: NonNull nonNull: NonNull
enum: Direction enum: Direction
bytes: Bytes
} }
open class Other { open class Other {
@@ -193,7 +194,6 @@ class KotlinCodeGeneratorTest {
@Test @Test
fun testToString() { fun testToString() {
val (_, propertyTypes) = instantiateOtherAndPropertyTypes() val (_, propertyTypes) = instantiateOtherAndPropertyTypes()
assertThat(propertyTypes.toString()) assertThat(propertyTypes.toString())
.isEqualTo( .isEqualTo(
"""PropertyTypes(boolean=true, int=42, float=42.3, string=string, duration=5.min, """ + """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), """ + """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)}, 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), """ + """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, "regex")).isInstanceOf(Regex::class.java)
assertThat(readProperty(propertyTypes, "any")).isEqualTo(other) assertThat(readProperty(propertyTypes, "any")).isEqualTo(other)
assertThat(readProperty(propertyTypes, "nonNull")).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? { private fun readProperty(receiver: Any, name: String): Any? {
@@ -571,6 +573,25 @@ class KotlinCodeGeneratorTest {
.contains("result = 31 * result + Objects.hashCode(this.name.pattern)") .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 @Test
fun `recursive types`() { fun `recursive types`() {
val kotlinCode = val kotlinCode =
@@ -2078,6 +2099,7 @@ class KotlinCodeGeneratorTest {
other, other,
other, other,
enumValue, enumValue,
byteArrayOf(1, 2, 3, 4),
) )
return other to propertyTypes return other to propertyTypes
@@ -1,8 +1,10 @@
package my package my
import java.util.Arrays
import java.util.Objects import java.util.Objects
import kotlin.Any import kotlin.Any
import kotlin.Boolean import kotlin.Boolean
import kotlin.ByteArray
import kotlin.Double import kotlin.Double
import kotlin.Int import kotlin.Int
import kotlin.Long import kotlin.Long
@@ -46,7 +48,8 @@ object Mod {
open val regex: Regex, open val regex: Regex,
open val any: Any?, open val any: Any?,
open val nonNull: Any, open val nonNull: Any,
open val enum: Direction open val enum: Direction,
open val bytes: ByteArray
) { ) {
open fun copy( open fun copy(
boolean: Boolean = this.boolean, boolean: Boolean = this.boolean,
@@ -75,10 +78,11 @@ object Mod {
regex: Regex = this.regex, regex: Regex = this.regex,
any: Any? = this.any, any: Any? = this.any,
nonNull: Any = this.nonNull, 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, ): PropertyTypes = PropertyTypes(boolean, int, float, string, duration, durationUnit, dataSize,
dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, 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 { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@@ -111,6 +115,7 @@ object Mod {
if (this.any != other.any) return false if (this.any != other.any) return false
if (this.nonNull != other.nonNull) return false if (this.nonNull != other.nonNull) return false
if (this.enum != other.enum) return false if (this.enum != other.enum) return false
if (!this.bytes.contentEquals(other.bytes)) return false
return true return true
} }
@@ -143,11 +148,12 @@ object Mod {
result = 31 * result + Objects.hashCode(this.any) result = 31 * result + Objects.hashCode(this.any)
result = 31 * result + Objects.hashCode(this.nonNull) result = 31 * result + Objects.hashCode(this.nonNull)
result = 31 * result + Objects.hashCode(this.enum) result = 31 * result + Objects.hashCode(this.enum)
result = 31 * result + this.bytes.contentHashCode()
return result return result
} }
override fun toString(): String = 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( open class Other(
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Class, Object.class, Converter.identity()),
Conversion.of(PClassInfo.Regex, Pattern.class, Converter.identity()), Conversion.of(PClassInfo.Regex, Pattern.class, Converter.identity()),
Conversion.of(PClassInfo.Regex, Object.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 // PClassInfo.Null -> Object.class is covered by PNullToAny (returns null rather than
// PNull.getInstance()) // PNull.getInstance())
); );
@@ -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});
}
}
@@ -0,0 +1,2 @@
ex1 = Bytes()
ex2 = Bytes(1, 2, 3, 4)
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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); 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. * Evaluates a module's {@code output.value} property.
* *
@@ -143,6 +152,10 @@ public interface Evaluator extends AutoCloseable {
* <td>TypeAlias</td> * <td>TypeAlias</td>
* <td>{@link TypeAlias}</td> * <td>{@link TypeAlias}</td>
* </tr> * </tr>
* <tr>
* <td>Bytes</td>
* <td>{@code byte[]}</td>
* </tr>
* </tbody> * </tbody>
* </table> * </table>
* *
@@ -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 @Override
public Object evaluateOutputValue(ModuleSource moduleSource) { public Object evaluateOutputValue(ModuleSource moduleSource) {
return doEvaluate( return doEvaluate(
@@ -183,32 +193,31 @@ public class EvaluatorImpl implements Evaluator {
@Override @Override
public Object evaluateExpression(ModuleSource moduleSource, String expression) { public Object evaluateExpression(ModuleSource moduleSource, String expression) {
// optimization: if the expression is `output.text` or `output.value` (the common cases), read // optimization: if the expression is `output.text`, `output.value` or `output.bytes` (the
// members directly instead of creating new truffle nodes. // common cases), read members directly instead of creating new truffle nodes.
if (expression.equals("output.text")) { return switch (expression) {
return evaluateOutputText(moduleSource); case "output.text" -> evaluateOutputText(moduleSource);
} case "output.value" -> evaluateOutputValue(moduleSource);
if (expression.equals("output.value")) { case "output.bytes" -> evaluateOutputBytes(moduleSource);
return evaluateOutputValue(moduleSource); default ->
} doEvaluate(
return doEvaluate( moduleSource,
moduleSource, (module) -> {
(module) -> { var expressionResult =
var expressionResult = VmUtils.evaluateExpression(module, expression, securityManager, moduleResolver);
VmUtils.evaluateExpression(module, expression, securityManager, moduleResolver); if (expressionResult instanceof VmValue value) {
if (expressionResult instanceof VmValue value) { value.force(false);
value.force(false); return value.export();
return value.export(); }
} return expressionResult;
return expressionResult; });
}); };
} }
@Override @Override
public String evaluateExpressionString(ModuleSource moduleSource, String expression) { public String evaluateExpressionString(ModuleSource moduleSource, String expression) {
// optimization: if the expression is `output.text` (the common case), read members // optimization: if the expression is `output.text` (the common case), read members
// directly // directly instead of creating new truffle nodes.
// instead of creating new truffle nodes.
if (expression.equals("output.text")) { if (expression.equals("output.text")) {
return evaluateOutputText(moduleSource); return evaluateOutputText(moduleSource);
} }
@@ -280,6 +289,10 @@ public class EvaluatorImpl implements Evaluator {
return doEvaluate(() -> VmUtils.readTextProperty(fileOutput)); return doEvaluate(() -> VmUtils.readTextProperty(fileOutput));
} }
byte[] evaluateOutputBytes(VmTyped fileOutput) {
return doEvaluate(() -> VmUtils.readBytesProperty(fileOutput).export());
}
private <T> T doEvaluate(Supplier<T> supplier) { private <T> T doEvaluate(Supplier<T> supplier) {
@Nullable TimeoutTask timeoutTask = null; @Nullable TimeoutTask timeoutTask = null;
logger.clear(); logger.clear();
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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. * 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(); String getText();
/**
* Returns the byte contents of this file.
*
* @throws PklException if an error occurs during evaluation.
*/
byte[] getBytes();
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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); 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);
}
}
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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)); 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 @Override
public void visitPair(Pair<?, ?> value) { public void visitPair(Pair<?, ?> value) {
try { try {
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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<Double> Float = pklBaseClassInfo("Float", double.class);
public static final PClassInfo<Duration> Duration = pklBaseClassInfo("Duration", Duration.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<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<Pair> Pair = pklBaseClassInfo("Pair", Pair.class);
public static final PClassInfo<Void> Collection = pklBaseClassInfo("Collection", Void.class); public static final PClassInfo<Void> Collection = pklBaseClassInfo("Collection", Void.class);
public static final PClassInfo<ArrayList> List = pklBaseClassInfo("List", ArrayList.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 Set) return (PClassInfo<T>) Set;
if (value instanceof Map) return (PClassInfo<T>) Map; if (value instanceof Map) return (PClassInfo<T>) Map;
if (value instanceof Pattern) return (PClassInfo<T>) Regex; if (value instanceof Pattern) return (PClassInfo<T>) Regex;
if (value instanceof byte[]) return (PClassInfo<T>) Bytes;
throw new IllegalArgumentException("Not a Pkl value: " + value); throw new IllegalArgumentException("Not a Pkl value: " + value);
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.pkl.core.util.ArrayCharEscaper; import org.pkl.core.util.ArrayCharEscaper;
import org.pkl.core.util.ByteArrayUtils;
// To instantiate this class, use ValueRenderers.plist(). // To instantiate this class, use ValueRenderers.plist().
final class PListRenderer implements ValueRenderer { final class PListRenderer implements ValueRenderer {
@@ -136,6 +137,13 @@ final class PListRenderer implements ValueRenderer {
value)); value));
} }
@Override
public void visitBytes(byte[] value) {
write("<data>");
write(ByteArrayUtils.base64(value));
write("</data>");
}
@Override @Override
public void visitPair(Pair<?, ?> value) { public void visitPair(Pair<?, ?> value) {
doVisitIterable(value, false); doVisitIterable(value, false);
@@ -92,6 +92,21 @@ final class PcfRenderer implements ValueRenderer {
write(value.toString()); 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 @Override
public void visitPair(Pair<?, ?> value) { public void visitPair(Pair<?, ?> value) {
doVisitIterable(value, "Pair("); doVisitIterable(value, "Pair(");
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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)); "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 @Override
public String convertPair(Pair<?, ?> value) { public String convertPair(Pair<?, ?> value) {
throw new RendererException( throw new RendererException(
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 convertDataSize(DataSize value);
T convertBytes(byte[] value);
T convertPair(Pair<?, ?> value); T convertPair(Pair<?, ?> value);
T convertList(List<?> value); T convertList(List<?> value);
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -53,6 +53,10 @@ public interface ValueVisitor {
visitDefault(value); visitDefault(value);
} }
default void visitBytes(byte[] value) {
visitDefault(value);
}
default void visitPair(Pair<?, ?> value) { default void visitPair(Pair<?, ?> value) {
visitDefault(value); visitDefault(value);
} }
@@ -108,6 +112,8 @@ public interface ValueVisitor {
visitMap(map); visitMap(map);
} else if (value instanceof Pattern pattern) { } else if (value instanceof Pattern pattern) {
visitRegex(pattern); visitRegex(pattern);
} else if (value instanceof byte[] bytes) {
visitBytes(bytes);
} else { } else {
throw new IllegalArgumentException("Cannot visit value with unexpected type: " + value); throw new IllegalArgumentException("Cannot visit value with unexpected type: " + value);
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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)); 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 @Override
public void visitPair(Pair<?, ?> value) { public void visitPair(Pair<?, ?> value) {
doVisitIterable(value, null); doVisitIterable(value, null);
@@ -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();
}
}
@@ -29,6 +29,7 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicMap;
@@ -37,6 +38,7 @@ import org.pkl.core.PklBugException;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.TypeParameter; import org.pkl.core.TypeParameter;
import org.pkl.core.TypeParameter.Variance; import org.pkl.core.TypeParameter.Variance;
import org.pkl.core.ast.ByteConstantValueNode;
import org.pkl.core.ast.ConstantNode; import org.pkl.core.ast.ConstantNode;
import org.pkl.core.ast.ConstantValueNode; import org.pkl.core.ast.ConstantValueNode;
import org.pkl.core.ast.ExpressionNode; 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.GeneratorWhenNode;
import org.pkl.core.ast.expression.generator.RestoreForBindingsNode; import org.pkl.core.ast.expression.generator.RestoreForBindingsNode;
import org.pkl.core.ast.expression.literal.AmendModuleNodeGen; 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.CheckIsAnnotationClassNode;
import org.pkl.core.ast.expression.literal.ConstantEntriesLiteralNodeGen; import org.pkl.core.ast.expression.literal.ConstantEntriesLiteralNodeGen;
import org.pkl.core.ast.expression.literal.ElementsEntriesLiteralNodeGen; 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.BaseModule;
import org.pkl.core.runtime.ModuleInfo; import org.pkl.core.runtime.ModuleInfo;
import org.pkl.core.runtime.ModuleResolver; import org.pkl.core.runtime.ModuleResolver;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmClass; import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmContext; import org.pkl.core.runtime.VmContext;
import org.pkl.core.runtime.VmDataSize; import org.pkl.core.runtime.VmDataSize;
@@ -524,9 +528,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
} }
} }
@Override private <T> T parseNumber(IntLiteralExpr expr, BiFunction<String, Integer, T> parser) {
public IntLiteralNode visitIntLiteralExpr(IntLiteralExpr expr) {
var section = createSourceSection(expr);
var text = remove_(expr.getNumber()); var text = remove_(expr.getNumber());
var radix = 10; var radix = 10;
@@ -547,11 +549,17 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
// also moves negation from runtime to parse time // also moves negation from runtime to parse time
text = "-" + text; text = "-" + text;
} }
return parser.apply(text, radix);
}
@Override
public IntLiteralNode visitIntLiteralExpr(IntLiteralExpr expr) {
var section = createSourceSection(expr);
try { try {
var num = Long.parseLong(text, radix); var num = parseNumber(expr, Long::parseLong);
return new IntLiteralNode(section, num); return new IntLiteralNode(section, num);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
var text = expr.getNumber();
throw exceptionBuilder().evalError("intTooLarge", text).withSourceSection(section).build(); 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: 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 // TODO: support qualified calls (e.g., `import "pkl:base"; x =
// correctness // base.List()/Set()/Map()/Bytes()`) for correctness
if (identifier == org.pkl.core.runtime.Identifier.LIST) { if (identifier == org.pkl.core.runtime.Identifier.LIST) {
return doVisitListLiteral(expr, argList); return doVisitListLiteral(expr, argList);
} }
@@ -651,6 +659,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
return doVisitMapLiteral(expr, argList); return doVisitMapLiteral(expr, argList);
} }
if (identifier == org.pkl.core.runtime.Identifier.BYTES_CONSTRUCTOR) {
return doVisitBytesLiteral(expr, argList);
}
var scope = symbolTable.getCurrentScope(); var scope = symbolTable.getCurrentScope();
return new ResolveMethodNode( return new ResolveMethodNode(
@@ -1083,6 +1095,18 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
: new MapLiteralNode(createSourceSection(expr), keyAndValueNodes.first); : 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) { private Pair<ExpressionNode[], Boolean> createCollectionArgumentNodes(ArgumentList exprs) {
var args = exprs.getArguments(); var args = exprs.getArguments();
var elementNodes = new ExpressionNode[args.size()]; var elementNodes = new ExpressionNode[args.size()];
@@ -1097,6 +1121,32 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
return Pair.of(elementNodes, isConstantNodes); 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) { public GeneratorMemberNode visitObjectMember(org.pkl.parser.syntax.ObjectMember member) {
return (GeneratorMemberNode) member.accept(this); return (GeneratorMemberNode) member.accept(this);
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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) { protected VmMap eval(VmMap left, VmMap right) {
return left.concatenate(right); return left.concatenate(right);
} }
@Specialization
protected VmBytes eval(VmBytes left, VmBytes right) {
return left.concatenate(right);
}
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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); 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) { private Object readMember(VmObject object, Object key, IndirectCallNode callNode) {
var result = VmUtils.readMemberOrNull(object, key, callNode); var result = VmUtils.readMemberOrNull(object, key, callNode);
if (result != null) return result; if (result != null) return result;
@@ -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 @Fallback
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Object iterable) { protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Object iterable) {
@@ -29,6 +29,7 @@ import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.runtime.BaseModule; import org.pkl.core.runtime.BaseModule;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.Iterators.TruffleIterator; import org.pkl.core.runtime.Iterators.TruffleIterator;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmClass; import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmCollection; import org.pkl.core.runtime.VmCollection;
import org.pkl.core.runtime.VmDynamic; import org.pkl.core.runtime.VmDynamic;
@@ -161,6 +162,16 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
doEvalIntSeq(frame, parent, data, iterable); 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 @Fallback
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Object iterable) { 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); 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) { private void cannotHaveMember(VmClass clazz, ObjectMember member) {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
var builder = exceptionBuilder(); var builder = exceptionBuilder();
@@ -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);
}
}
@@ -2153,52 +2153,71 @@ public abstract class TypeNode extends PklNode {
} }
} }
public static final class UIntTypeAliasTypeNode extends IntSlotTypeNode { protected abstract static class IntMaskSlotTypeNode extends IntSlotTypeNode {
private final VmTypeAlias typeAlias; protected final long mask;
private final long mask;
public UIntTypeAliasTypeNode(VmTypeAlias typeAlias, long mask) {
IntMaskSlotTypeNode(long mask) {
super(VmUtils.unavailableSourceSection()); super(VmUtils.unavailableSourceSection());
this.typeAlias = typeAlias;
this.mask = mask; this.mask = mask;
} }
@Override @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 (value instanceof Long l) {
if ((l & mask) == l) return value; if ((l & mask) == l) return value;
throw new VmTypeMismatchException.Constraint(typeAlias.getConstraintSection(), value); throw new VmTypeMismatchException.Constraint(typealias.getConstraintSection(), value);
} }
throw new VmTypeMismatchException.Simple( throw new VmTypeMismatchException.Simple(
typeAlias.getBaseTypeSection(), value, BaseModule.getIntClass()); typealias.getBaseTypeSection(), value, BaseModule.getIntClass());
} }
@Override @Override
public VmClass getVmClass() { public final VmClass getVmClass() {
return BaseModule.getIntClass(); 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 @Override
public VmTypeAlias getVmTypeAlias() { public VmTypeAlias getVmTypeAlias() {
return typeAlias; return typeAlias;
} }
}
@Override public static final class UInt8TypeAliasTypeNode extends IntMaskSlotTypeNode {
public VmTyped getMirror() { public UInt8TypeAliasTypeNode() {
return MirrorFactories.typeAliasTypeFactory.create(this); super(0x00000000000000FFL);
} }
@Override @Override
public boolean doIsEquivalentTo(TypeNode other) { public VmTypeAlias getVmTypeAlias() {
return other instanceof UIntTypeAliasTypeNode aliasTypeNode && mask == aliasTypeNode.mask; return BaseModule.getUInt8TypeAlias();
}
@Override
protected boolean acceptTypeNode(TypeNodeConsumer consumer) {
return consumer.accept(this);
} }
} }
@@ -174,7 +174,7 @@ public abstract class UnresolvedTypeNode extends PklNode {
case "Int8": case "Int8":
return new Int8TypeAliasTypeNode(); return new Int8TypeAliasTypeNode();
case "UInt8": case "UInt8":
return new UIntTypeAliasTypeNode(alias, 0x00000000000000FFL); return new UInt8TypeAliasTypeNode();
case "Int16": case "Int16":
return new Int16TypeAliasTypeNode(); return new Int16TypeAliasTypeNode();
case "UInt16": case "UInt16":
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64; import org.pkl.core.util.ByteArrayUtils;
/** An external (file, HTTP, etc.) resource. */ /** An external (file, HTTP, etc.) resource. */
public record Resource(URI uri, byte[] bytes) { 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. */ /** Returns the content of this resource in Base64. */
public String getBase64() { public String getBase64() {
return Base64.getEncoder().encodeToString(bytes); return ByteArrayUtils.base64(bytes);
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; return DataSizeClass.instance;
} }
public static VmClass getBytesClass() {
return BytesClass.instance;
}
public static VmClass getIntSeqClass() { public static VmClass getIntSeqClass() {
return IntSeqClass.instance; return IntSeqClass.instance;
} }
@@ -219,6 +223,10 @@ public final class BaseModule extends StdLibModule {
return MixinTypeAlias.instance; return MixinTypeAlias.instance;
} }
public static VmTypeAlias getUInt8TypeAlias() {
return UInt8TypeAlias.instance;
}
private static final class AnyClass { private static final class AnyClass {
static final VmClass instance = loadClass("Any"); static final VmClass instance = loadClass("Any");
} }
@@ -259,6 +267,10 @@ public final class BaseModule extends StdLibModule {
static final VmClass instance = loadClass("DataSize"); static final VmClass instance = loadClass("DataSize");
} }
private static final class BytesClass {
static final VmClass instance = loadClass("Bytes");
}
private static final class IntSeqClass { private static final class IntSeqClass {
static final VmClass instance = loadClass("IntSeq"); static final VmClass instance = loadClass("IntSeq");
} }
@@ -383,6 +395,10 @@ public final class BaseModule extends StdLibModule {
static final VmTypeAlias instance = loadTypeAlias("Int32"); static final VmTypeAlias instance = loadTypeAlias("Int32");
} }
private static final class UInt8TypeAlias {
static final VmTypeAlias instance = loadTypeAlias("UInt8");
}
private static final class MixinTypeAlias { private static final class MixinTypeAlias {
static final VmTypeAlias instance = loadTypeAlias("Mixin"); static final VmTypeAlias instance = loadTypeAlias("Mixin");
} }
@@ -75,6 +75,11 @@ public final class Identifier implements Comparable<Identifier> {
// XmlCData} // XmlCData}
public static final Identifier TEXT = get("text"); 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 // members of pkl.base#ModuleOutput, pkl.base#Resource, pkl.base#String
public static final Identifier BASE64 = get("base64"); public static final Identifier BASE64 = get("base64");
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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) new VmObjectFactory<Resource>(BaseModule::getResourceClass)
.addProperty("uri", resource -> resource.uri().toString()) .addProperty("uri", resource -> resource.uri().toString())
.addProperty("text", Resource::getText) .addProperty("text", Resource::getText)
.addProperty("base64", Resource::getBase64); .addProperty("bytes", resource -> new VmBytes(resource.bytes()));
} }
@TruffleBoundary @TruffleBoundary
@@ -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;
}
};
}
}
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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()); 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 @TruffleBoundary
public static VmList create(Object[] elements, int length) { public static VmList create(Object[] elements, int length) {
if (elements.length == 0) return EMPTY; if (elements.length == 0) return EMPTY;
@@ -175,6 +175,10 @@ public final class VmUtils {
return (String) VmUtils.readMember((VmObjectLike) receiver, Identifier.TEXT); return (String) VmUtils.readMember((VmObjectLike) receiver, Identifier.TEXT);
} }
public static VmBytes readBytesProperty(VmObjectLike receiver) {
return (VmBytes) VmUtils.readMember(receiver, Identifier.BYTES);
}
@TruffleBoundary @TruffleBoundary
public static Object readMember(VmObjectLike receiver, Object memberKey) { public static Object readMember(VmObjectLike receiver, Object memberKey) {
var result = readMemberOrNull(receiver, memberKey); var result = readMemberOrNull(receiver, memberKey);
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 convertDataSize(VmDataSize value, Iterable<Object> path);
T convertBytes(VmBytes vmBytes, Iterable<Object> path);
T convertIntSeq(VmIntSeq value, Iterable<Object> path); T convertIntSeq(VmIntSeq value, Iterable<Object> path);
T convertList(VmList value, Iterable<Object> path); T convertList(VmList value, Iterable<Object> path);
@@ -114,6 +114,45 @@ public final class VmValueRenderer {
append(value); 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 @Override
public void visitPair(VmPair value) { public void visitPair(VmPair value) {
append("Pair("); append("Pair(");
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 visitDataSize(VmDataSize value);
void visitBytes(VmBytes value);
void visitIntSeq(VmIntSeq value); void visitIntSeq(VmIntSeq value);
void visitList(VmList value); void visitList(VmList value);
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 floatConverter;
private final @Nullable VmFunction durationConverter; private final @Nullable VmFunction durationConverter;
private final @Nullable VmFunction dataSizeConverter; private final @Nullable VmFunction dataSizeConverter;
private final @Nullable VmFunction bytesConverter;
private final @Nullable VmFunction intSeqConverter; private final @Nullable VmFunction intSeqConverter;
private final @Nullable VmFunction listConverter; private final @Nullable VmFunction listConverter;
private final @Nullable VmFunction setConverter; private final @Nullable VmFunction setConverter;
@@ -56,6 +57,7 @@ public final class PklConverter implements VmValueConverter<Object> {
floatConverter = typeConverters.get(BaseModule.getFloatClass()); floatConverter = typeConverters.get(BaseModule.getFloatClass());
durationConverter = typeConverters.get(BaseModule.getDurationClass()); durationConverter = typeConverters.get(BaseModule.getDurationClass());
dataSizeConverter = typeConverters.get(BaseModule.getDataSizeClass()); dataSizeConverter = typeConverters.get(BaseModule.getDataSizeClass());
bytesConverter = typeConverters.get(BaseModule.getBytesClass());
intSeqConverter = typeConverters.get(BaseModule.getIntSeqClass()); intSeqConverter = typeConverters.get(BaseModule.getIntSeqClass());
listConverter = typeConverters.get(BaseModule.getListClass()); listConverter = typeConverters.get(BaseModule.getListClass());
setConverter = typeConverters.get(BaseModule.getSetClass()); setConverter = typeConverters.get(BaseModule.getSetClass());
@@ -100,6 +102,11 @@ public final class PklConverter implements VmValueConverter<Object> {
return doConvert(value, path, dataSizeConverter); return doConvert(value, path, dataSizeConverter);
} }
@Override
public Object convertBytes(VmBytes value, Iterable<Object> path) {
return doConvert(value, path, bytesConverter);
}
@Override @Override
public Object convertIntSeq(VmIntSeq value, Iterable<Object> path) { public Object convertIntSeq(VmIntSeq value, Iterable<Object> path) {
return doConvert(value, path, intSeqConverter); return doConvert(value, path, intSeqConverter);
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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); 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();
}
}
} }
@@ -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();
}
}
}
}
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -126,6 +126,11 @@ public final class JsonRendererNodes {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
} }
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override @Override
public void visitRegex(VmRegex value) { public void visitRegex(VmRegex value) {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
@@ -18,17 +18,22 @@ package org.pkl.core.stdlib.base;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.LoopNode;
import org.pkl.core.ast.expression.binary.*; import org.pkl.core.ast.expression.binary.*;
import org.pkl.core.ast.internal.IsInstanceOfNode; import org.pkl.core.ast.internal.IsInstanceOfNode;
import org.pkl.core.ast.internal.IsInstanceOfNodeGen; import org.pkl.core.ast.internal.IsInstanceOfNodeGen;
import org.pkl.core.ast.lambda.*; 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.runtime.*;
import org.pkl.core.stdlib.*; import org.pkl.core.stdlib.*;
import org.pkl.core.stdlib.base.CollectionNodes.CompareByNode; import org.pkl.core.stdlib.base.CollectionNodes.CompareByNode;
import org.pkl.core.stdlib.base.CollectionNodes.CompareNode; import org.pkl.core.stdlib.base.CollectionNodes.CompareNode;
import org.pkl.core.stdlib.base.CollectionNodes.CompareWithNode; import org.pkl.core.stdlib.base.CollectionNodes.CompareWithNode;
import org.pkl.core.util.EconomicSets; import org.pkl.core.util.EconomicSets;
import org.pkl.core.util.LateInit;
// duplication between ListNodes and SetNodes is "intentional" // duplication between ListNodes and SetNodes is "intentional"
// (sharing nodes between VmCollection subtypes results in // (sharing nodes between VmCollection subtypes results in
@@ -1321,4 +1326,34 @@ public final class ListNodes {
return self.toDynamic(); 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);
}
}
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 // keep in sync with org.pkl.core.PListRenderer
@SuppressWarnings("HttpUrlsUsage")
private static final class PListRenderer extends AbstractRenderer { private static final class PListRenderer extends AbstractRenderer {
// it's safe (though not required) to escape all the following characters in XML text nodes // 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(); .build();
} }
@Override
public void visitBytes(VmBytes value) {
builder.append("<data>");
builder.append(value.base64());
builder.append("</data>");
}
@Override @Override
public void visitRegex(VmRegex value) { public void visitRegex(VmRegex value) {
throw new VmExceptionBuilder() throw new VmExceptionBuilder()
@@ -17,6 +17,7 @@ package org.pkl.core.stdlib.base;
import org.pkl.core.ValueFormatter; import org.pkl.core.ValueFormatter;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmDataSize; import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration; import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic; import org.pkl.core.runtime.VmDynamic;
@@ -99,6 +100,11 @@ public final class PcfRenderer extends AbstractRenderer {
builder.append(value); builder.append(value);
} }
@Override
public void visitBytes(VmBytes value) {
builder.append(value);
}
@Override @Override
public void visitPair(VmPair value) { public void visitPair(VmPair value) {
builder.append("Pair("); builder.append("Pair(");
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmDataSize; import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration; import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic; import org.pkl.core.runtime.VmDynamic;
@@ -117,6 +118,11 @@ public final class PropertiesRendererNodes {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
} }
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override @Override
public void visitIntSeq(VmIntSeq value) { public void visitIntSeq(VmIntSeq value) {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
@@ -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);
}
}
}
@@ -19,9 +19,11 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.LoopNode;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.regex.*; import java.util.regex.*;
import org.pkl.core.PklBugException;
import org.pkl.core.ast.lambda.ApplyVmFunction1Node; import org.pkl.core.ast.lambda.ApplyVmFunction1Node;
import org.pkl.core.ast.lambda.ApplyVmFunction1NodeGen; import org.pkl.core.ast.lambda.ApplyVmFunction1NodeGen;
import org.pkl.core.runtime.*; 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 { public abstract static class chars extends ExternalPropertyNode {
@Specialization @Specialization
@TruffleBoundary @TruffleBoundary
@@ -900,7 +915,7 @@ public final class StringNodes {
@TruffleBoundary @TruffleBoundary
@Specialization @Specialization
protected String eval(String self) { 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 @TruffleBoundary
private static String substringFrom(String string, int start) { private static String substringFrom(String string, int start) {
return string.substring(start); return string.substring(start);
@@ -18,6 +18,7 @@ package org.pkl.core.stdlib.base;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmCollection; import org.pkl.core.runtime.VmCollection;
import org.pkl.core.runtime.VmDataSize; import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration; import org.pkl.core.runtime.VmDuration;
@@ -177,6 +178,11 @@ public final class YamlRendererNodes {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
} }
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override @Override
public void visitRegex(VmRegex value) { public void visitRegex(VmRegex value) {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 java.util.regex.Pattern;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.JsonnetModule; import org.pkl.core.runtime.JsonnetModule;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmDataSize; import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration; import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic; import org.pkl.core.runtime.VmDynamic;
@@ -199,6 +200,11 @@ public final class RendererNodes {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
} }
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override @Override
public void visitIntSeq(VmIntSeq value) { public void visitIntSeq(VmIntSeq value) {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
@@ -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.TypeNode.UnionTypeNode;
import org.pkl.core.ast.type.VmTypeMismatchException; import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmClass; import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmDataSize; import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration; import org.pkl.core.runtime.VmDuration;
@@ -522,6 +523,14 @@ public final class RendererNodes {
.build(); .build();
} }
@Override
public void visitBytes(VmBytes value) {
throw new VmExceptionBuilder()
.evalError("cannotRenderTypeAddConverter", "Bytes", "Protobuf")
.withProgramValue("Value", value)
.build();
}
@Override @Override
public void visitIntSeq(VmIntSeq value) { public void visitIntSeq(VmIntSeq value) {
writePropertyName(); writePropertyName();
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -154,6 +154,11 @@ public final class RendererNodes {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
} }
@Override
public void visitBytes(VmBytes value) {
cannotRenderTypeAddConverter(value);
}
@Override @Override
public void visitPair(VmPair value) { public void visitPair(VmPair value) {
cannotRenderTypeAddConverter(value); cannotRenderTypeAddConverter(value);
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import org.pkl.core.runtime.VmExceptionBuilder; import org.pkl.core.runtime.VmExceptionBuilder;
public final class ByteArrayUtils { public final class ByteArrayUtils {
private ByteArrayUtils() {} private ByteArrayUtils() {}
public static String base64(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
public static String md5(byte[] input) { public static String md5(byte[] input) {
return hash(input, "MD5"); return hash(input, "MD5");
} }
@@ -1064,3 +1064,9 @@ External reader process has already terminated.
invalidOpaqueFileUri=\ invalidOpaqueFileUri=\
File URIs must have a path that starts with `/` (e.g. file:/path/to/my_module.pkl).\n\ 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:"). To resolve relative paths, remove the scheme prefix (remove "file:").
invalidStringBase64=\
`{0}` is not in base64 encoding.
characterCodingException=\
Invalid bytes for charset "{0}".
@@ -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)
}
}
@@ -358,6 +358,13 @@ examples {
List().toDynamic() List().toDynamic()
} }
["toBytes()"] {
list1.toBytes()
List().toBytes()
module.catch(() -> List(null).toBytes())
module.catch(() -> List(-1).toBytes())
}
["filterNonNull()"] { ["filterNonNull()"] {
list1.filterNonNull() list1.filterNonNull()
List().filterNonNull() List().filterNonNull()
@@ -18,6 +18,12 @@ facts {
!str1.isBlank !str1.isBlank
} }
["isBase64"] {
"".isBase64
"AQIDBA==".isBase64
!"hello there".isBase64
}
["lastIndex"] { ["lastIndex"] {
for (s in List(str1, str2, str3)) { for (s in List(str1, str2, str3)) {
s.length == s.lastIndex + 1 s.length == s.lastIndex + 1
@@ -29,6 +35,10 @@ facts {
quickBrownFox.base64.base64Decoded == quickBrownFox quickBrownFox.base64.base64Decoded == quickBrownFox
} }
["base64DecodedBytes"] {
"AQIDBA==".base64DecodedBytes == Bytes(1, 2, 3, 4)
}
["contains()"] { ["contains()"] {
str1.contains(str2) str1.contains(str2)
str1.contains(str1) str1.contains(str1)
@@ -487,4 +497,19 @@ examples {
["base64Decoded"] { ["base64Decoded"] {
module.catch(() -> "~~~".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")
}
} }
@@ -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()
}
}
@@ -37,5 +37,3 @@ examples {
module.catch(() -> list1[3]) module.catch(() -> list1[3])
} }
} }
@@ -0,0 +1 @@
res = Bytes(0xc0, 0xc1).decodeToString("UTF-8")
@@ -0,0 +1 @@
foo = Bytes(0, 1, 2, 3, 0xffff)
@@ -0,0 +1,3 @@
local num = 0xfff
res = Bytes(0, 1, 2, 3, num)
@@ -0,0 +1 @@
bytes = Bytes(0, 1, 2, 3, 0xffff)
@@ -0,0 +1 @@
res = List(1, 2, 3, 0xffff).toBytes()
@@ -326,6 +326,18 @@ res11d = new Dynamic {
for (i, n in IntSeq(1, 3)) { Pair(i, n) } 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 { local dynamicWithOnlyProperties = new Dynamic {
foo = "Foo!" foo = "Foo!"
bar = 42 bar = 42
@@ -15,6 +15,7 @@ local list: List<Int> = List(1, 2, 3)
local map: Map<String, Int> = Map("zz", 1, "yy", 2) local map: Map<String, Int> = Map("zz", 1, "yy", 2)
local intseq: IntSeq = IntSeq(0, 5).step(2) local intseq: IntSeq = IntSeq(0, 5).step(2)
local set: Set<Int> = Set(10, 20, 30) local set: Set<Int> = Set(10, 20, 30)
local bytes: Bytes = Bytes(1, 2, 3, 4)
examples { examples {
["inferred Dynamic parent"] { ["inferred Dynamic parent"] {
@@ -29,6 +30,7 @@ examples {
...map ...map
...intseq ...intseq
...set ...set
...bytes
} }
} }
["explicit Dynamic type"] { ["explicit Dynamic type"] {
@@ -43,6 +45,7 @@ examples {
...map ...map
...intseq ...intseq
...set ...set
...bytes
} }
} }
["legacy syntax"] { ["legacy syntax"] {
@@ -52,6 +55,7 @@ examples {
(Dynamic) { ...map } (Dynamic) { ...map }
(Dynamic) { ...intseq } (Dynamic) { ...intseq }
(Dynamic) { ...set } (Dynamic) { ...set }
(Dynamic) { ...bytes }
} }
} }
@@ -6,6 +6,7 @@ local list: List<Int> = List(1, 2, 3)
local map: Map<String, Int> = Map("a", 1, "b", 2) local map: Map<String, Int> = Map("a", 1, "b", 2)
local intseq: IntSeq = IntSeq(0, 5).step(2) local intseq: IntSeq = IntSeq(0, 5).step(2)
local set: Set<Int> = Set(10, 20, 30) local set: Set<Int> = Set(10, 20, 30)
local bytes: Bytes = Bytes(1, 2, 3, 4)
res1: Listing = new { res1: Listing = new {
0 0
@@ -72,3 +73,15 @@ res16 = new Listing {
} }
res17 = res16.length res17 = res16.length
res18 = new Listing {
...bytes
}
res19 = new Listing {
0
...bytes
0
0
0
}
@@ -15,6 +15,7 @@ local list: List<Int> = List(1, 2, 3)
local map: Map<String, Int> = Map("a", 1, "b", 2) local map: Map<String, Int> = Map("a", 1, "b", 2)
local intseq: IntSeq = IntSeq(0, 5).step(2) local intseq: IntSeq = IntSeq(0, 5).step(2)
local set: Set<Int> = Set(10, 20, 30) local set: Set<Int> = Set(10, 20, 30)
local bytes: Bytes = Bytes(1, 2, 3, 4)
class Foo { class Foo {
names: Listing<String> names: Listing<String>
@@ -108,3 +109,5 @@ local foos = (makeFoos(List("bar"))) {
res17 = new Mapping { res17 = new Mapping {
...foos ...foos
} }
res18 = test.catch(() -> new Mapping { ...bytes })
@@ -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
}
}
@@ -303,6 +303,12 @@ examples {
} }
new {} new {}
} }
["toBytes()"] {
Bytes(1, 2, 3)
Bytes()
"Expected value of type `Int`, but got `null`."
"Type constraint `isBetween(0, 255)` violated. Value: -1"
}
["filterNonNull()"] { ["filterNonNull()"] {
List(1, 2, 3) List(1, 2, 3)
List() List()
@@ -13,9 +13,9 @@ alias {
members = List(new { members = List(new {
referent { referent {
location { location {
line = 1040 line = 1056
column = 1 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 = """ docComment = """
A boolean value, either [true] or [false]. A boolean value, either [true] or [false].
@@ -197,9 +197,9 @@ alias {
properties = Map() properties = Map()
methods = Map("xor", new { methods = Map("xor", new {
location { location {
line = 1050 line = 1066
column = 3 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 = """ docComment = """
Tells if exactly one of [this] and [other] is [true] (exclusive or). Tells if exactly one of [this] and [other] is [true] (exclusive or).
@@ -221,9 +221,9 @@ alias {
}) })
}, "implies", new { }, "implies", new {
location { location {
line = 1063 line = 1079
column = 3 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 = """ docComment = """
Tells if [this] implies [other] (logical consequence). Tells if [this] implies [other] (logical consequence).
@@ -251,9 +251,9 @@ alias {
}, new { }, new {
referent { referent {
location { location {
line = 1076 line = 1092
column = 1 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 = """ docComment = """
A sequence of Unicode characters (code points). A sequence of Unicode characters (code points).
@@ -433,9 +433,9 @@ alias {
} }
properties = Map("length", new { properties = Map("length", new {
location { location {
line = 1087 line = 1103
column = 3 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 = """ docComment = """
The number of characters in this string. The number of characters in this string.
@@ -458,9 +458,9 @@ alias {
name = "length" name = "length"
}, "lastIndex", new { }, "lastIndex", new {
location { location {
line = 1100 line = 1116
column = 3 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 = """ docComment = """
The index of the last character in this string (same as `length - 1`). The index of the last character in this string (same as `length - 1`).
@@ -480,9 +480,9 @@ alias {
name = "lastIndex" name = "lastIndex"
}, "isEmpty", new { }, "isEmpty", new {
location { location {
line = 1110 line = 1126
column = 3 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 = """ docComment = """
Tells whether this string is empty. Tells whether this string is empty.
@@ -499,9 +499,9 @@ alias {
name = "isEmpty" name = "isEmpty"
}, "isBlank", new { }, "isBlank", new {
location { location {
line = 1121 line = 1137
column = 3 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 = """ docComment = """
Tells if all characters in this string have Unicode property "White_Space". Tells if all characters in this string have Unicode property "White_Space".
@@ -519,19 +519,39 @@ alias {
name = "isBlank" name = "isBlank"
}, "isRegex", new { }, "isRegex", new {
location { location {
line = 1124 line = 1140
column = 3 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]." docComment = "Tells if this string is a valid regular expression according to [Regex]."
annotations = List() annotations = List()
modifiers = Set() modifiers = Set()
name = "isRegex" 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 { }, "md5", new {
location { location {
line = 1131 line = 1157
column = 3 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 = """ docComment = """
The [MD5](https://en.wikipedia.org/wiki/MD5) The [MD5](https://en.wikipedia.org/wiki/MD5)
@@ -545,9 +565,9 @@ alias {
name = "md5" name = "md5"
}, "sha1", new { }, "sha1", new {
location { location {
line = 1137 line = 1163
column = 3 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 = """ docComment = """
The [SHA-1](https://en.wikipedia.org/wiki/SHA-1) The [SHA-1](https://en.wikipedia.org/wiki/SHA-1)
@@ -560,9 +580,9 @@ alias {
name = "sha1" name = "sha1"
}, "sha256", new { }, "sha256", new {
location { location {
line = 1142 line = 1168
column = 3 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 = """ docComment = """
The [SHA-256](https://en.wikipedia.org/wiki/SHA-2) The [SHA-256](https://en.wikipedia.org/wiki/SHA-2)
@@ -574,9 +594,9 @@ alias {
name = "sha256" name = "sha256"
}, "sha256Int", new { }, "sha256Int", new {
location { location {
line = 1146 line = 1172
column = 3 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 = """ docComment = """
The first 64 bits of the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) The first 64 bits of the [SHA-256](https://en.wikipedia.org/wiki/SHA-2)
@@ -587,9 +607,9 @@ alias {
name = "sha256Int" name = "sha256Int"
}, "base64", new { }, "base64", new {
location { location {
line = 1149 line = 1175
column = 3 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." docComment = "The Base64 encoding of this string's UTF-8 byte sequence."
annotations = List() annotations = List()
@@ -597,9 +617,9 @@ alias {
name = "base64" name = "base64"
}, "base64Decoded", new { }, "base64Decoded", new {
location { location {
line = 1157 line = 1183
column = 3 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 = """ docComment = """
The inverse of [base64]. The inverse of [base64].
@@ -612,11 +632,30 @@ alias {
annotations = List() annotations = List()
modifiers = Set() modifiers = Set()
name = "base64Decoded" 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 { }, "chars", new {
location { location {
line = 1165 line = 1200
column = 3 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 = """ docComment = """
The Unicode characters in this string. The Unicode characters in this string.
@@ -631,9 +670,9 @@ alias {
name = "chars" name = "chars"
}, "codePoints", new { }, "codePoints", new {
location { location {
line = 1173 line = 1208
column = 3 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 = """ docComment = """
The Unicode code points in this string. The Unicode code points in this string.
@@ -649,9 +688,9 @@ alias {
}) })
methods = Map("getOrNull", new { methods = Map("getOrNull", new {
location { location {
line = 1185 line = 1220
column = 3 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 = """ docComment = """
Returns the character at [index], or [null] if [index] is out of range. Returns the character at [index], or [null] if [index] is out of range.
@@ -674,9 +713,9 @@ alias {
}) })
}, "substring", new { }, "substring", new {
location { location {
line = 1199 line = 1234
column = 3 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 = """ docComment = """
Returns the substring from [start] until [exclusiveEnd]. Returns the substring from [start] until [exclusiveEnd].
@@ -703,9 +742,9 @@ alias {
}) })
}, "substringOrNull", new { }, "substringOrNull", new {
location { location {
line = 1217 line = 1252
column = 3 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 = """ docComment = """
Returns the substring from [start] until [exclusiveEnd]. Returns the substring from [start] until [exclusiveEnd].
@@ -736,9 +775,9 @@ alias {
}) })
}, "repeat", new { }, "repeat", new {
location { location {
line = 1227 line = 1262
column = 3 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 = """ docComment = """
Concatenates [count] copies of this string. Concatenates [count] copies of this string.
@@ -759,9 +798,9 @@ alias {
}) })
}, "contains", new { }, "contains", new {
location { location {
line = 1230 line = 1265
column = 3 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]." docComment = "Tells whether this string contains [pattern]."
annotations = List() annotations = List()
@@ -773,9 +812,9 @@ alias {
}) })
}, "matches", new { }, "matches", new {
location { location {
line = 1234 line = 1269
column = 3 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." docComment = "Tells whether this string matches [regex] in its entirety."
annotations = List(new { annotations = List(new {
@@ -791,9 +830,9 @@ alias {
}) })
}, "startsWith", new { }, "startsWith", new {
location { location {
line = 1237 line = 1272
column = 3 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]." docComment = "Tells whether this string starts with [pattern]."
annotations = List() annotations = List()
@@ -805,9 +844,9 @@ alias {
}) })
}, "endsWith", new { }, "endsWith", new {
location { location {
line = 1240 line = 1275
column = 3 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]." docComment = "Tells whether this string ends with [pattern]."
annotations = List() annotations = List()
@@ -819,9 +858,9 @@ alias {
}) })
}, "indexOf", new { }, "indexOf", new {
location { location {
line = 1246 line = 1281
column = 3 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 = """ docComment = """
Returns the zero-based index of the first occurrence of [pattern] Returns the zero-based index of the first occurrence of [pattern]
@@ -838,9 +877,9 @@ alias {
}) })
}, "indexOfOrNull", new { }, "indexOfOrNull", new {
location { location {
line = 1250 line = 1285
column = 3 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 = """ docComment = """
Returns the zero-based index of the first occurrence of [pattern] Returns the zero-based index of the first occurrence of [pattern]
@@ -855,9 +894,9 @@ alias {
}) })
}, "lastIndexOf", new { }, "lastIndexOf", new {
location { location {
line = 1256 line = 1291
column = 3 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 = """ docComment = """
Returns the zero-based index of the last occurrence of [pattern] Returns the zero-based index of the last occurrence of [pattern]
@@ -874,9 +913,9 @@ alias {
}) })
}, "lastIndexOfOrNull", new { }, "lastIndexOfOrNull", new {
location { location {
line = 1260 line = 1295
column = 3 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 = """ docComment = """
Returns the zero-based index of the last occurrence of [pattern] Returns the zero-based index of the last occurrence of [pattern]
@@ -891,9 +930,9 @@ alias {
}) })
}, "take", new { }, "take", new {
location { location {
line = 1266 line = 1301
column = 3 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 = """ docComment = """
Returns the first [n] characters of this string. Returns the first [n] characters of this string.
@@ -913,9 +952,9 @@ alias {
}) })
}, "takeWhile", new { }, "takeWhile", new {
location { location {
line = 1269 line = 1304
column = 3 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]." docComment = "Returns the longest prefix of this string that satisfies [predicate]."
annotations = List() annotations = List()
@@ -927,9 +966,9 @@ alias {
}) })
}, "takeLast", new { }, "takeLast", new {
location { location {
line = 1274 line = 1309
column = 3 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 = """ docComment = """
Returns the last [n] characters of this string. Returns the last [n] characters of this string.
@@ -945,9 +984,9 @@ alias {
}) })
}, "takeLastWhile", new { }, "takeLastWhile", new {
location { location {
line = 1277 line = 1312
column = 3 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]." docComment = "Returns the longest suffix of this string that satisfies [predicate]."
annotations = List() annotations = List()
@@ -959,9 +998,9 @@ alias {
}) })
}, "drop", new { }, "drop", new {
location { location {
line = 1283 line = 1318
column = 3 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 = """ docComment = """
Removes the first [n] characters of this string. Removes the first [n] characters of this string.
@@ -981,9 +1020,9 @@ alias {
}) })
}, "dropWhile", new { }, "dropWhile", new {
location { location {
line = 1287 line = 1322
column = 3 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]." docComment = "Removes the longest prefix of this string that satisfies [predicate]."
annotations = List(new { annotations = List(new {
@@ -999,9 +1038,9 @@ alias {
}) })
}, "dropLast", new { }, "dropLast", new {
location { location {
line = 1293 line = 1328
column = 3 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 = """ docComment = """
Removes the last [n] characters of this string. Removes the last [n] characters of this string.
@@ -1021,9 +1060,9 @@ alias {
}) })
}, "dropLastWhile", new { }, "dropLastWhile", new {
location { location {
line = 1297 line = 1332
column = 3 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]." docComment = "Removes the longest suffix of this string that satisfies [predicate]."
annotations = List(new { annotations = List(new {
@@ -1039,9 +1078,9 @@ alias {
}) })
}, "replaceFirst", new { }, "replaceFirst", new {
location { location {
line = 1302 line = 1337
column = 3 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 = """ docComment = """
Replaces the first occurrence of [pattern] in this string with [replacement]. Replaces the first occurrence of [pattern] in this string with [replacement].
@@ -1059,9 +1098,9 @@ alias {
}) })
}, "replaceLast", new { }, "replaceLast", new {
location { location {
line = 1307 line = 1342
column = 3 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 = """ docComment = """
Replaces the last occurrence of [pattern] in this string with [replacement]. Replaces the last occurrence of [pattern] in this string with [replacement].
@@ -1079,9 +1118,9 @@ alias {
}) })
}, "replaceAll", new { }, "replaceAll", new {
location { location {
line = 1312 line = 1347
column = 3 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 = """ docComment = """
Replaces all occurrences of [pattern] in this string with [replacement]. Replaces all occurrences of [pattern] in this string with [replacement].
@@ -1099,9 +1138,9 @@ alias {
}) })
}, "replaceFirstMapped", new { }, "replaceFirstMapped", new {
location { location {
line = 1317 line = 1352
column = 3 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 = """ docComment = """
Replaces the first occurrence of [pattern] in this string with the return value of [mapper]. Replaces the first occurrence of [pattern] in this string with the return value of [mapper].
@@ -1119,9 +1158,9 @@ alias {
}) })
}, "replaceLastMapped", new { }, "replaceLastMapped", new {
location { location {
line = 1322 line = 1357
column = 3 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 = """ docComment = """
Replaces the last occurrence of [pattern] in this string with the return value of [mapper]. Replaces the last occurrence of [pattern] in this string with the return value of [mapper].
@@ -1139,9 +1178,9 @@ alias {
}) })
}, "replaceAllMapped", new { }, "replaceAllMapped", new {
location { location {
line = 1327 line = 1362
column = 3 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 = """ docComment = """
Replaces all occurrences of [pattern] in this string with the return value of [mapper]. Replaces all occurrences of [pattern] in this string with the return value of [mapper].
@@ -1159,9 +1198,9 @@ alias {
}) })
}, "replaceRange", new { }, "replaceRange", new {
location { location {
line = 1332 line = 1367
column = 3 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 = """ docComment = """
Replaces the characters between [start] and [exclusiveEnd] with [replacement]. Replaces the characters between [start] and [exclusiveEnd] with [replacement].
@@ -1181,9 +1220,9 @@ alias {
}) })
}, "toUpperCase", new { }, "toUpperCase", new {
location { location {
line = 1335 line = 1370
column = 3 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." docComment = "Performs a locale-independent character-by-character conversion of this string to uppercase."
annotations = List() annotations = List()
@@ -1193,9 +1232,9 @@ alias {
parameters = Map() parameters = Map()
}, "toLowerCase", new { }, "toLowerCase", new {
location { location {
line = 1338 line = 1373
column = 3 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." docComment = "Performs a locale-independent character-by-character conversion of this string to lowercase."
annotations = List() annotations = List()
@@ -1205,9 +1244,9 @@ alias {
parameters = Map() parameters = Map()
}, "reverse", new { }, "reverse", new {
location { location {
line = 1341 line = 1376
column = 3 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." docComment = "Reverses the order of characters in this string."
annotations = List() annotations = List()
@@ -1217,9 +1256,9 @@ alias {
parameters = Map() parameters = Map()
}, "trim", new { }, "trim", new {
location { location {
line = 1345 line = 1380
column = 3 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." docComment = "Removes any leading and trailing characters with Unicode property \"White_Space\" from this string."
annotations = List(new { annotations = List(new {
@@ -1233,9 +1272,9 @@ alias {
parameters = Map() parameters = Map()
}, "trimStart", new { }, "trimStart", new {
location { location {
line = 1349 line = 1384
column = 3 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." docComment = "Removes any leading characters with Unicode property \"White_Space\" from this string."
annotations = List(new { annotations = List(new {
@@ -1253,9 +1292,9 @@ alias {
parameters = Map() parameters = Map()
}, "trimEnd", new { }, "trimEnd", new {
location { location {
line = 1353 line = 1388
column = 3 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." docComment = "Removes any trailing characters with Unicode property \"White_Space\" from this string."
annotations = List(new { annotations = List(new {
@@ -1273,9 +1312,9 @@ alias {
parameters = Map() parameters = Map()
}, "padStart", new { }, "padStart", new {
location { location {
line = 1359 line = 1394
column = 3 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 = """ docComment = """
Increases the length of this string to [width] by adding leading [char]s. Increases the length of this string to [width] by adding leading [char]s.
@@ -1297,9 +1336,9 @@ alias {
}) })
}, "padEnd", new { }, "padEnd", new {
location { location {
line = 1365 line = 1400
column = 3 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 = """ docComment = """
Increases the length of this string to [width] by adding trailing [char]s. Increases the length of this string to [width] by adding trailing [char]s.
@@ -1321,9 +1360,9 @@ alias {
}) })
}, "split", new { }, "split", new {
location { location {
line = 1368 line = 1403
column = 3 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]." docComment = "Splits this string around matches of [pattern]."
annotations = List() annotations = List()
@@ -1335,9 +1374,9 @@ alias {
}) })
}, "splitLimit", new { }, "splitLimit", new {
location { location {
line = 1383 line = 1418
column = 3 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 = """ docComment = """
Splits this string matches of [pattern], up to [limit] substrings. Splits this string matches of [pattern], up to [limit] substrings.
@@ -1366,9 +1405,9 @@ alias {
}) })
}, "capitalize", new { }, "capitalize", new {
location { location {
line = 1393 line = 1428
column = 3 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 = """ docComment = """
Converts the first character of this string to title case. Converts the first character of this string to title case.
@@ -1387,9 +1426,9 @@ alias {
parameters = Map() parameters = Map()
}, "decapitalize", new { }, "decapitalize", new {
location { location {
line = 1403 line = 1438
column = 3 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 = """ docComment = """
Converts the first character of this string to lower case. Converts the first character of this string to lower case.
@@ -1408,9 +1447,9 @@ alias {
parameters = Map() parameters = Map()
}, "toInt", new { }, "toInt", new {
location { location {
line = 1409 line = 1444
column = 3 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 = """ docComment = """
Parses this string as a signed decimal (base 10) integer. Parses this string as a signed decimal (base 10) integer.
@@ -1425,9 +1464,9 @@ alias {
parameters = Map() parameters = Map()
}, "toIntOrNull", new { }, "toIntOrNull", new {
location { location {
line = 1415 line = 1450
column = 3 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 = """ docComment = """
Parses this string as a signed decimal (base 10) integer. Parses this string as a signed decimal (base 10) integer.
@@ -1442,9 +1481,9 @@ alias {
parameters = Map() parameters = Map()
}, "toFloat", new { }, "toFloat", new {
location { location {
line = 1420 line = 1455
column = 3 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 = """ docComment = """
Parses this string as a floating point number. Parses this string as a floating point number.
@@ -1458,9 +1497,9 @@ alias {
parameters = Map() parameters = Map()
}, "toFloatOrNull", new { }, "toFloatOrNull", new {
location { location {
line = 1425 line = 1460
column = 3 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 = """ docComment = """
Parses this string as a floating point number. Parses this string as a floating point number.
@@ -1474,9 +1513,9 @@ alias {
parameters = Map() parameters = Map()
}, "toBoolean", new { }, "toBoolean", new {
location { location {
line = 1430 line = 1465
column = 3 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 = """ docComment = """
Parses `"true"` to [true] and `"false"` to [false] (case-insensitive). Parses `"true"` to [true] and `"false"` to [false] (case-insensitive).
@@ -1490,9 +1529,9 @@ alias {
parameters = Map() parameters = Map()
}, "toBooleanOrNull", new { }, "toBooleanOrNull", new {
location { location {
line = 1435 line = 1470
column = 3 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 = """ docComment = """
Parses `"true"` to [true] and `"false"` to [false] (case-insensitive). Parses `"true"` to [true] and `"false"` to [false] (case-insensitive).
@@ -1504,6 +1543,29 @@ alias {
name = "toBooleanOrNull" name = "toBooleanOrNull"
typeParameters = List() typeParameters = List()
parameters = Map() 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() typeArguments = List()
@@ -1524,9 +1586,9 @@ rec {
typeParameters = List() typeParameters = List()
superclass { superclass {
location { location {
line = 1721 line = 1773
column = 1 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 = """ docComment = """
Base class for objects whose members are described by a class definition. Base class for objects whose members are described by a class definition.
@@ -1539,9 +1601,9 @@ rec {
typeParameters = List() typeParameters = List()
superclass { superclass {
location { location {
line = 1716 line = 1768
column = 1 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 = """ docComment = """
A composite value containing members (properties, elements, entries). A composite value containing members (properties, elements, entries).
@@ -1765,9 +1827,9 @@ rec {
supertype { supertype {
referent { referent {
location { location {
line = 1716 line = 1768
column = 1 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 = """ docComment = """
A composite value containing members (properties, elements, entries). A composite value containing members (properties, elements, entries).
@@ -1993,9 +2055,9 @@ rec {
properties = Map() properties = Map()
methods = Map("hasProperty", new { methods = Map("hasProperty", new {
location { location {
line = 1723 line = 1775
column = 3 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]." docComment = "Tells if this object has a property with the given [name]."
annotations = List() annotations = List()
@@ -2007,9 +2069,9 @@ rec {
}) })
}, "getProperty", new { }, "getProperty", new {
location { location {
line = 1728 line = 1780
column = 3 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 = """ docComment = """
Returns the value of the property with the given [name]. Returns the value of the property with the given [name].
@@ -2025,9 +2087,9 @@ rec {
}) })
}, "getPropertyOrNull", new { }, "getPropertyOrNull", new {
location { location {
line = 1733 line = 1785
column = 3 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 = """ docComment = """
Returns the value of the property with the given [name]. Returns the value of the property with the given [name].
@@ -2043,9 +2105,9 @@ rec {
}) })
}, "toDynamic", new { }, "toDynamic", new {
location { location {
line = 1736 line = 1788
column = 3 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." docComment = "Converts this object to a [Dynamic] object."
annotations = List() annotations = List()
@@ -2055,9 +2117,9 @@ rec {
parameters = Map() parameters = Map()
}, "toMap", new { }, "toMap", new {
location { location {
line = 1739 line = 1791
column = 3 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]." docComment = "Converts this object to a [Map]."
annotations = List() annotations = List()
@@ -2070,9 +2132,9 @@ rec {
supertype { supertype {
referent { referent {
location { location {
line = 1721 line = 1773
column = 1 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 = """ docComment = """
Base class for objects whose members are described by a class definition. Base class for objects whose members are described by a class definition.
@@ -2085,9 +2147,9 @@ rec {
typeParameters = List() typeParameters = List()
superclass { superclass {
location { location {
line = 1716 line = 1768
column = 1 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 = """ docComment = """
A composite value containing members (properties, elements, entries). A composite value containing members (properties, elements, entries).
@@ -2311,9 +2373,9 @@ rec {
supertype { supertype {
referent { referent {
location { location {
line = 1716 line = 1768
column = 1 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 = """ docComment = """
A composite value containing members (properties, elements, entries). A composite value containing members (properties, elements, entries).
@@ -2539,9 +2601,9 @@ rec {
properties = Map() properties = Map()
methods = Map("hasProperty", new { methods = Map("hasProperty", new {
location { location {
line = 1723 line = 1775
column = 3 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]." docComment = "Tells if this object has a property with the given [name]."
annotations = List() annotations = List()
@@ -2553,9 +2615,9 @@ rec {
}) })
}, "getProperty", new { }, "getProperty", new {
location { location {
line = 1728 line = 1780
column = 3 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 = """ docComment = """
Returns the value of the property with the given [name]. Returns the value of the property with the given [name].
@@ -2571,9 +2633,9 @@ rec {
}) })
}, "getPropertyOrNull", new { }, "getPropertyOrNull", new {
location { location {
line = 1733 line = 1785
column = 3 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 = """ docComment = """
Returns the value of the property with the given [name]. Returns the value of the property with the given [name].
@@ -2589,9 +2651,9 @@ rec {
}) })
}, "toDynamic", new { }, "toDynamic", new {
location { location {
line = 1736 line = 1788
column = 3 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." docComment = "Converts this object to a [Dynamic] object."
annotations = List() annotations = List()
@@ -2601,9 +2663,9 @@ rec {
parameters = Map() parameters = Map()
}, "toMap", new { }, "toMap", new {
location { location {
line = 1739 line = 1791
column = 3 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]." docComment = "Converts this object to a [Map]."
annotations = List() annotations = List()
@@ -9,6 +9,11 @@ facts {
true true
true true
} }
["isBase64"] {
true
true
true
}
["lastIndex"] { ["lastIndex"] {
true true
true true
@@ -18,6 +23,9 @@ facts {
true true
true true
} }
["base64DecodedBytes"] {
true
}
["contains()"] { ["contains()"] {
true true
true true
@@ -417,4 +425,17 @@ examples {
["base64Decoded"] { ["base64Decoded"] {
"Illegal base64 character 7e String: \"~~~\"" "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)
}
} }
@@ -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)
}
}
@@ -22,7 +22,7 @@ examples {
file1 file1
""" """
base64 = "ZmlsZTEK" bytes = Bytes(102, 105, 108, 101, 49, 10)
} }
"Cannot find resource `other.txt`." "Cannot find resource `other.txt`."
new { new {
@@ -31,7 +31,7 @@ examples {
file1 file1
""" """
base64 = "ZmlsZTEK" bytes = Bytes(102, 105, 108, 101, 49, 10)
} }
null null
} }
@@ -42,7 +42,7 @@ examples {
resource resource
""" """
base64 = "cmVzb3VyY2UK" bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
} }
new { new {
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt" uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
@@ -50,7 +50,7 @@ examples {
resource resource
""" """
base64 = "cmVzb3VyY2UK" bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
} }
} }
["read non-allowed resource"] { ["read non-allowed resource"] {
@@ -64,7 +64,7 @@ examples {
file1 file1
""" """
base64 = "ZmlsZTEK" bytes = Bytes(102, 105, 108, 101, 49, 10)
} }
new { new {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt" uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
@@ -72,7 +72,7 @@ examples {
file2 file2
""" """
base64 = "ZmlsZTIK" bytes = Bytes(102, 105, 108, 101, 50, 10)
} }
} }
["read different resources with same relative resource URI"] { ["read different resources with same relative resource URI"] {
@@ -82,7 +82,7 @@ examples {
resource resource
""" """
base64 = "cmVzb3VyY2UK" bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
} }
new { new {
uri = "file:///$snippetsDir/input-helper/basic/read/child/resource.txt" uri = "file:///$snippetsDir/input-helper/basic/read/child/resource.txt"
@@ -90,7 +90,7 @@ examples {
child resource child resource
""" """
base64 = "Y2hpbGQgcmVzb3VyY2UK" bytes = Bytes(99, 104, 105, 108, 100, 32, 114, 101, 115, 111, 117, 114, 99, 101, 10)
} }
} }
} }
@@ -4,7 +4,7 @@ examples {
["../../input-helper/globtest/module with [weird] ~!characters.pkl"] { ["../../input-helper/globtest/module with [weird] ~!characters.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/module%20with%20%5Bweird%5D%20~!characters.pkl" uri = "file:///$snippetsDir/input-helper/globtest/module%20with%20%5Bweird%5D%20~!characters.pkl"
text = "" text = ""
base64 = "" bytes = Bytes()
} }
["../../input-helper/globtest/moduleA.pkl"] { ["../../input-helper/globtest/moduleA.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleA.pkl" uri = "file:///$snippetsDir/input-helper/globtest/moduleA.pkl"
@@ -12,7 +12,7 @@ examples {
name = "moduleA" 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"] { ["../../input-helper/globtest/moduleB.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleB.pkl" uri = "file:///$snippetsDir/input-helper/globtest/moduleB.pkl"
@@ -20,7 +20,7 @@ examples {
name = "moduleB" 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"] { ["../../input-helper/globtest/child/moduleC.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/child/moduleC.pkl" uri = "file:///$snippetsDir/input-helper/globtest/child/moduleC.pkl"
@@ -28,7 +28,7 @@ examples {
name = "child/moduleC" 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 { new {
@@ -38,7 +38,7 @@ examples {
file1 file1
""" """
base64 = "ZmlsZTEK" bytes = Bytes(102, 105, 108, 101, 49, 10)
} }
["globtest/file2.txt"] { ["globtest/file2.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt" uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
@@ -46,7 +46,7 @@ examples {
file2 file2
""" """
base64 = "ZmlsZTIK" bytes = Bytes(102, 105, 108, 101, 50, 10)
} }
} }
} }
@@ -55,22 +55,22 @@ examples {
["../../input-helper/globtest/module with [weird] ~!characters.pkl"] { ["../../input-helper/globtest/module with [weird] ~!characters.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/module%20with%20%5Bweird%5D%20~!characters.pkl" uri = "file:///$snippetsDir/input-helper/globtest/module%20with%20%5Bweird%5D%20~!characters.pkl"
text = "hi" text = "hi"
base64 = "" bytes = Bytes()
} }
["../../input-helper/globtest/moduleA.pkl"] { ["../../input-helper/globtest/moduleA.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleA.pkl" uri = "file:///$snippetsDir/input-helper/globtest/moduleA.pkl"
text = "hi" 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"] { ["../../input-helper/globtest/moduleB.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/moduleB.pkl" uri = "file:///$snippetsDir/input-helper/globtest/moduleB.pkl"
text = "hi" 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"] { ["../../input-helper/globtest/child/moduleC.pkl"] {
uri = "file:///$snippetsDir/input-helper/globtest/child/moduleC.pkl" uri = "file:///$snippetsDir/input-helper/globtest/child/moduleC.pkl"
text = "hi" 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 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"] { ["package://localhost:0/birds@0.5.0#/allFruit.pkl"] {
uri = "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") 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"] { ["package://localhost:0/birds@0.5.0#/catalog.pkl"] {
uri = "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") 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"] { ["package://localhost:0/birds@0.5.0#/catalog/Ostrich.pkl"] {
uri = "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"] { ["package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"] {
uri = "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 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"] { ["package://localhost:0/birds@0.5.0#/some/dir/Bird.pkl"] {
uri = "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 file1
""" """
base64 = "ZmlsZTEK" bytes = Bytes(102, 105, 108, 101, 49, 10)
} }
["globtest/file2.txt"] { ["globtest/file2.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt" uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
@@ -216,7 +216,7 @@ examples {
file2 file2
""" """
base64 = "ZmlsZTIK" bytes = Bytes(102, 105, 108, 101, 50, 10)
} }
} }
new { new {
@@ -226,7 +226,7 @@ examples {
file1 file1
""" """
base64 = "ZmlsZTEK" bytes = Bytes(102, 105, 108, 101, 49, 10)
} }
} }
new { new {
@@ -236,7 +236,7 @@ examples {
file2 file2
""" """
base64 = "ZmlsZTIK" bytes = Bytes(102, 105, 108, 101, 50, 10)
} }
} }
} }
@@ -248,7 +248,7 @@ examples {
resource resource
""" """
base64 = "cmVzb3VyY2UK" bytes = Bytes(114, 101, 115, 111, 117, 114, 99, 101, 10)
} }
} }
new { new {
@@ -258,7 +258,7 @@ examples {
child resource child resource
""" """
base64 = "Y2hpbGQgcmVzb3VyY2UK" bytes = Bytes(99, 104, 105, 108, 100, 32, 114, 101, 115, 111, 117, 114, 99, 101, 10)
} }
} }
} }
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -226,6 +226,13 @@ res11d {
Pair(1, 2) Pair(1, 2)
Pair(2, 3) Pair(2, 3)
} }
res12a {
1
2
3
4
}
res12b {}
valueForOverProperties { valueForOverProperties {
["Foo!"] = "Foo!" ["Foo!"] = "Foo!"
[42] = 42 [42] = 42
@@ -37,6 +37,10 @@ examples {
10 10
20 20
30 30
1
2
3
4
} }
} }
["explicit Dynamic type"] { ["explicit Dynamic type"] {
@@ -69,6 +73,10 @@ examples {
10 10
20 20
30 30
1
2
3
4
} }
} }
["legacy syntax"] { ["legacy syntax"] {
@@ -105,5 +113,11 @@ examples {
20 20
30 30
} }
new {
1
2
3
4
}
} }
} }
@@ -103,3 +103,19 @@ res16 {
0 0
} }
res17 = 7 res17 = 7
res18 {
1
2
3
4
}
res19 {
0
1
2
3
4
0
0
0
}
@@ -55,3 +55,4 @@ res17 {
} }
} }
} }
res18 = "Cannot spread value of type `Bytes` into object of type `Mapping`. Value: Bytes(1, 2, 3, 4)"
@@ -7,6 +7,11 @@ at invalidAmend6#resource (file:///$snippetsDir/input/modules/invalidAmend6.pkl)
Available properties: Available properties:
base64 base64
bytes
md5
sha1
sha256
sha256Int
text text
uri uri
@@ -40,7 +40,7 @@ examples {
favoriteFruit: Fruit 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 { new {
uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl" uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -54,7 +54,7 @@ examples {
favoriteFruit = apple 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 { new {
uri = "package://localhost:0/birds@0.5.0#/catalog/Ostrich.pkl" 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)
} }
} }
} }
@@ -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"] { ["package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"] {
uri = "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 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"] { ["catalog/Swallow.pkl"] {
uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl" uri = "package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -98,7 +98,7 @@ examples {
favoriteFruit = apple 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" 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)
} }
} }
} }
@@ -44,7 +44,7 @@ examples {
Swallow.pkl 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 { new {
uri = "projectpackage://localhost:0/birds@0.5.0#/" uri = "projectpackage://localhost:0/birds@0.5.0#/"
@@ -56,7 +56,7 @@ examples {
some 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)
} }
} }
} }
@@ -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"] { ["@birds/catalog/Swallow.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl" uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -76,7 +76,7 @@ examples {
favoriteFruit = apple 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 { new {
@@ -92,7 +92,7 @@ examples {
favoriteFruit: Fruit 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"] { ["@birds/allFruit.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/allFruit.pkl" uri = "projectpackage://localhost:0/birds@0.5.0#/allFruit.pkl"
@@ -103,7 +103,7 @@ examples {
fruitFiles = read*("@fruities/catalog/*.pkl") 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"] { ["@birds/catalog.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog.pkl" uri = "projectpackage://localhost:0/birds@0.5.0#/catalog.pkl"
@@ -114,7 +114,7 @@ examples {
catalogFiles = read*("catalog/*.pkl") 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 { new {
@@ -130,7 +130,7 @@ examples {
favoriteFruit: Fruit 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"] { ["@birds/allFruit.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/allFruit.pkl" uri = "projectpackage://localhost:0/birds@0.5.0#/allFruit.pkl"
@@ -141,7 +141,7 @@ examples {
fruitFiles = read*("@fruities/catalog/*.pkl") 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"] { ["@birds/catalog.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog.pkl" uri = "projectpackage://localhost:0/birds@0.5.0#/catalog.pkl"
@@ -152,7 +152,7 @@ examples {
catalogFiles = read*("catalog/*.pkl") 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"] { ["@birds/catalog/Ostrich.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/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"] { ["@birds/catalog/Swallow.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl" uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -180,7 +180,7 @@ examples {
favoriteFruit = apple 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"] { ["@birds/some/dir/Bird.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/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"] { ["catalog/Swallow.pkl"] {
uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl" uri = "projectpackage://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"
@@ -226,7 +226,7 @@ examples {
favoriteFruit = apple 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"] { ["package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"] {
uri = "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 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)
} }
} }
} }
@@ -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"] { ["@project2/PklProject.deps.json"] {
uri = "file:/$snippetsDir/input/projects/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"] { ["@project2/penguin.pkl"] {
uri = "file:/$snippetsDir/input/projects/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)
} }
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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)) val output = evaluator.evaluateOutputFiles(text(program))
assertThat(output.keys).isEqualTo(setOf("foo.yml", "bar.yml", "bar/biz.yml", "bar/../bark.yml")) 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"]?.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"]?.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"]?.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"]?.text).isEqualTo("bark: bark bark")
assertThat(output["bar/../bark.yml"]?.bytes).isEqualTo("bark: bark bark".toByteArray())
} }
@Test @Test
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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") val none = VmNull.withDefault("default")
assertThat(renderer.render(none)).isEqualTo("null") 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