20 Commits

Author SHA1 Message Date
Daniel Chao
e805a04a74 [docs] Add documetation about release/evolution/roadmap (#937) 2025-02-05 11:26:34 -08:00
Dan Chao
edf704b469 Run spotless apply 2025-01-22 09:04:02 -08:00
Dan Chao
d9cfb68048 Prepare 0.27.2 release 2025-01-22 08:57:27 -08:00
Daniel Chao
1e75001382 Add release notes for 0.27.2 (#894) 2025-01-21 11:56:33 -08:00
Dan Chao
376eb2bdbf Run spotless apply 2025-01-17 10:32:24 -08:00
Daniel Chao
56a3b0a193 Update license year (#871)
* Update license header file spec to use placeholder year
* Update spotless to use ratchet formatting (only format files that have changed)
2025-01-17 10:32:24 -08:00
Josh B
6c8e45b19a Fix NPE when handling ExternalReader specs with null arguments (#882) 2025-01-17 10:32:24 -08:00
Josh B
0bc1b7156e Fix page size for Linux AArch64 native executables (#875)
Graal Native Image is assuming 4k page size here, which is a naughty assumption to make in the modern Linux-on-ARM landscape.
Two very common hardware configurations require 16k minumum page size: the Raspberry Pi 5 and Asahi Linux (running on Apple Silicon hardware).

This change forces 64k pages for Linux/AArch64 native executables to guarantee compatibility with these platforms.
DEVELOPMENT.adoc is also updated to cover the additional dependencies required for building native executables on Linux.
2025-01-17 10:32:24 -08:00
Stefan M.
f0b961de81 Make Test Report locale independent (#868)
Format numbers with `.` decimals
2025-01-17 10:32:24 -08:00
Daniel Chao
3ece353e0c Download JDK for windows build (#851)
Don't use the system Java on Windows builds, instead download them from Adoptium.

Also:

* Fail job if curl returns a 4xx status code
* Add java version to `GradleJob`
2025-01-17 09:58:03 -08:00
Josh B
639cc2430e Fix CreateEvaluatorRequest decoding (#853)
Handle case when request specifies external reader with null arguments
2024-12-19 10:00:46 -08:00
Philip K.F. Hölzenspies
48a710f439 Prepare 0.27.1 release (#839) 2024-12-06 14:23:25 +00:00
Philip K.F. Hölzenspies
15d85b0660 Add release notes for 0.27.1 2024-12-06 13:25:39 +00:00
odenix
aeace8bb3c Improve lazy type checking of listings and mappings (#789)
Motivation:
- simplify implementation of lazy type checking
- fix correctness issues of lazy type checking (#785)

Changes:
- implement listing/mapping type cast via amendment (`parent`) instead of delegation (`delegate`)
- handle type checking of *computed* elements/entries in the same way as type checking of computed properties
  - ElementOrEntryNode is the equivalent of TypeCheckedPropertyNode
- remove fields VmListingOrMapping.delegate/typeNodeFrame/cachedMembers/checkedMembers
- fix #785 by executing all type casts between a member's owner and receiver
- fix #823 by storing owner and receiver directly
  instead of storing the mutable frame containing them (typeNodeFrame)
- remove overrides of VmObject methods that are no longer required
  - good for Truffle partial evaluation and JVM inlining
- revert a85a173faa except for added tests
- move `VmUtils.setOwner` and `VmUtils.setReceiver` and make them private
  - these methods aren't generally safe to use

Result:
- simpler code with greater optimization potential
  - VmListingOrMapping can now have both a type node and new members
- fewer changes to surrounding code
- smaller memory footprint
- better performance in some cases
- fixes https://github.com/apple/pkl/issues/785
- fixes https://github.com/apple/pkl/issues/823

Potential future optimizations:
- avoid lazy type checking overhead for untyped listings/mappings
- improve efficiency of forcing a typed listing/mapping
  - currently, lazy type checking will traverse the parent chain once per member,
    reducing the performance benefit of shallow-forcing
	  a listing/mapping over evaluating each member individually
- avoid creating an intermediate untyped listing/mapping in the following cases:
  - `new Listing<X> {...}`
  - amendment of `property: Listing<X>`
2024-12-06 14:00:23 +01:00
Islon Scherer
7b850dd6d9 Fix possible stack overflow in Listing/Mapping type checking (#826) 2024-12-06 13:58:29 +01:00
Daniel Chao
c2096f633b Exclude non file-based modules from synthesized *GatherImports task (#821)
This fixes an issue where certain modules tasks fail due to the plugin
attempting to analyze their imports, but the arguments may not actually be
Pkl modules.

For example, the pkldoc task accepts entire packages in its "sourceMoules" property.

This changes the gather imports logic to only analyze file-based modules.
This is also a performance improvement; non file-based modules are unlikely to import
files due to insufficient trust levels.

Also: fix a bug when generating pkldoc on Windows
2024-12-06 13:58:06 +01:00
translatenix
d6ba021e12 Fix length of listings with computed index (#797)
Motivation:
The following expression evaluates to 2 instead of 1:
new Listing { "value" } { [0 + 0] = "override" }.length

Changes:
- fix length computation in EntriesLiteralNode
- improve `api/listing` tests
- make snippet test failures diffable in IntelliJ

Result:
- fixes https://github.com/apple/pkl/issues/780
- improved dev experience in IntelliJ
2024-12-06 13:57:19 +01:00
Josh B
077497d9b8 Fix a possible deadlock during external reader process close (#786) (#787)
* Fix a possible deadlock during external reader process close

* Apply spotless

---------

Co-authored-by: Philip K.F. Hölzenspies <holzensp@gmail.com>
2024-11-08 13:18:13 -08:00
Nick Muerdter
552b301451 Fix broken link to documentation site in release notes (#784) 2024-11-05 12:58:43 -08:00
Dan Chao
d3ac4b288c Prepare 0.27.0 release 2024-11-05 09:51:48 -08:00
92 changed files with 1347 additions and 653 deletions

View File

@@ -108,10 +108,12 @@ local gradleCheckJobs: Mapping<String, GradleCheckJob> = new {
["gradle-check-jdk17"] {
javaVersion = "17.0"
isRelease = false
os = "linux"
}
["gradle-check-jdk21"] {
javaVersion = "21.0"
isRelease = false
os = "linux"
}
["gradle-check-jdk17-windows"] {
javaVersion = "17.0"

View File

@@ -39,7 +39,7 @@ jobs:
&& rm -rf /var/cache/dnf
# install jdk
curl -L \
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.9_9.tar.gz -o /tmp/jdk.tar.gz
mkdir /jdk \
@@ -52,7 +52,7 @@ jobs:
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
curl -L https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
curl -Lf https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
mkdir -p /tmp/dep_zlib-1.2.13 \
&& cd /tmp/dep_zlib-1.2.13 \
@@ -65,7 +65,7 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
curl -L https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
curl -Lf https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
mkdir -p /tmp/dep_musl-1.2.2 \
&& cd /tmp/dep_musl-1.2.2 \
@@ -135,7 +135,7 @@ jobs:
&& rm -rf /var/cache/dnf
# install jdk
curl -L \
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_aarch64_linux_hotspot_17.0.9_9.tar.gz -o /tmp/jdk.tar.gz
mkdir /jdk \
@@ -148,7 +148,7 @@ jobs:
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
curl -L https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
curl -Lf https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
mkdir -p /tmp/dep_zlib-1.2.13 \
&& cd /tmp/dep_zlib-1.2.13 \
@@ -196,7 +196,7 @@ jobs:
&& rm -rf /var/cache/dnf
# install jdk
curl -L \
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.9_9.tar.gz -o /tmp/jdk.tar.gz
mkdir /jdk \
@@ -209,7 +209,7 @@ jobs:
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
curl -L https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
curl -Lf https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
mkdir -p /tmp/dep_zlib-1.2.13 \
&& cd /tmp/dep_zlib-1.2.13 \
@@ -222,7 +222,7 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
curl -L https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
curl -Lf https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
mkdir -p /tmp/dep_musl-1.2.2 \
&& cd /tmp/dep_musl-1.2.2 \
@@ -262,6 +262,18 @@ jobs:
pkl-cli-windows-amd64-release:
steps:
- checkout
- run:
command: |-
# install jdk
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9.1/OpenJDK17U-jdk_x64_windows_hotspot_17.0.9_9.zip -o /tmp/jdk.zip
unzip /tmp/jdk.zip -d /tmp/jdk \
&& cd /tmp/jdk/jdk-* \
&& mkdir /jdk \
&& cp -r . /jdk
name: Set up environment
shell: bash.exe
- run:
command: |-
export PATH=~/staticdeps/bin:$PATH
@@ -276,6 +288,7 @@ jobs:
path: ~/test-results
environment:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
resource_class: windows.large
machine:
image: windows-server-2022-gui:current
@@ -315,7 +328,7 @@ jobs:
&& rm -rf /var/cache/dnf
# install jdk
curl -L \
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.9_9.tar.gz -o /tmp/jdk.tar.gz
mkdir /jdk \
@@ -328,7 +341,7 @@ jobs:
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
curl -L https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
curl -Lf https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
mkdir -p /tmp/dep_zlib-1.2.13 \
&& cd /tmp/dep_zlib-1.2.13 \
@@ -341,7 +354,7 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
curl -L https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
curl -Lf https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
mkdir -p /tmp/dep_musl-1.2.2 \
&& cd /tmp/dep_musl-1.2.2 \
@@ -411,7 +424,7 @@ jobs:
&& rm -rf /var/cache/dnf
# install jdk
curl -L \
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_aarch64_linux_hotspot_17.0.9_9.tar.gz -o /tmp/jdk.tar.gz
mkdir /jdk \
@@ -424,7 +437,7 @@ jobs:
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
curl -L https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
curl -Lf https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
mkdir -p /tmp/dep_zlib-1.2.13 \
&& cd /tmp/dep_zlib-1.2.13 \
@@ -472,7 +485,7 @@ jobs:
&& rm -rf /var/cache/dnf
# install jdk
curl -L \
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.9_9.tar.gz -o /tmp/jdk.tar.gz
mkdir /jdk \
@@ -485,7 +498,7 @@ jobs:
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
curl -L https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
curl -Lf https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz -o /tmp/zlib.tar.gz
mkdir -p /tmp/dep_zlib-1.2.13 \
&& cd /tmp/dep_zlib-1.2.13 \
@@ -498,7 +511,7 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
curl -L https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
curl -Lf https://musl.libc.org/releases/musl-1.2.2.tar.gz -o /tmp/musl.tar.gz
mkdir -p /tmp/dep_musl-1.2.2 \
&& cd /tmp/dep_musl-1.2.2 \
@@ -538,6 +551,18 @@ jobs:
pkl-cli-windows-amd64-snapshot:
steps:
- checkout
- run:
command: |-
# install jdk
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9.1/OpenJDK17U-jdk_x64_windows_hotspot_17.0.9_9.zip -o /tmp/jdk.zip
unzip /tmp/jdk.zip -d /tmp/jdk \
&& cd /tmp/jdk/jdk-* \
&& mkdir /jdk \
&& cp -r . /jdk
name: Set up environment
shell: bash.exe
- run:
command: |-
export PATH=~/staticdeps/bin:$PATH
@@ -552,6 +577,7 @@ jobs:
path: ~/test-results
environment:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
resource_class: windows.large
machine:
image: windows-server-2022-gui:current
@@ -582,6 +608,18 @@ jobs:
gradle-check-jdk17-windows:
steps:
- checkout
- run:
command: |-
# install jdk
curl -Lf \
https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9.1/OpenJDK17U-jdk_x64_windows_hotspot_17.0.9_9.zip -o /tmp/jdk.zip
unzip /tmp/jdk.zip -d /tmp/jdk \
&& cd /tmp/jdk/jdk-* \
&& mkdir /jdk \
&& cp -r . /jdk
name: Set up environment
shell: bash.exe
- run:
command: ./gradlew --info --stacktrace -DtestReportsDir=${HOME}/test-results check
name: gradle check
@@ -589,6 +627,7 @@ jobs:
path: ~/test-results
environment:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
resource_class: windows.large
machine:
image: windows-server-2022-gui:current

View File

@@ -17,7 +17,6 @@
extends "GradleJob.pkl"
import "package://pkg.pkl-lang.org/pkl-pantry/com.circleci.v2@1.1.2#/Config.pkl"
import "package://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.uri@1.0.0#/URI.pkl"
/// The architecture to use
arch: "amd64"|"aarch64"
@@ -25,13 +24,11 @@ arch: "amd64"|"aarch64"
/// Whether to link to musl. Otherwise, links to glibc.
musl: Boolean = false
javaVersion = "17.0"
local setupLinuxEnvironment: Config.RunStep =
let (jdkVersion = "17.0.9+9")
let (muslVersion = "1.2.2")
let (zlibVersion = "1.2.13")
let (jdkVersionEncoded = URI.encodeComponent(jdkVersion))
let (jdkVersionAlt = jdkVersion.replaceLast("+", "_"))
let (majorJdkVersion = jdkVersion.split(".").first)
new {
name = "Set up environment"
shell = "#!/bin/bash -exo pipefail"
@@ -43,8 +40,8 @@ local setupLinuxEnvironment: Config.RunStep =
&& rm -rf /var/cache/dnf
# install jdk
curl -L \
https://github.com/adoptium/temurin\#(majorJdkVersion)-binaries/releases/download/jdk-\#(jdkVersionEncoded)/OpenJDK\#(majorJdkVersion)U-jdk_\#(if (arch == "amd64") "x64" else "aarch64")_linux_hotspot_\#(jdkVersionAlt).tar.gz -o /tmp/jdk.tar.gz
curl -Lf \
https://github.com/adoptium/temurin\#(module.majorJdkVersion)-binaries/releases/download/\#(module.jdkGitHubReleaseName)/OpenJDK\#(module.majorJdkVersion)U-jdk_\#(if (arch == "amd64") "x64" else "aarch64")_linux_hotspot_\#(module.jdkVersionAlt).tar.gz -o /tmp/jdk.tar.gz
mkdir /jdk \
&& cd /jdk \
@@ -56,7 +53,7 @@ local setupLinuxEnvironment: Config.RunStep =
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
curl -L https://github.com/madler/zlib/releases/download/v\#(zlibVersion)/zlib-\#(zlibVersion).tar.gz -o /tmp/zlib.tar.gz
curl -Lf https://github.com/madler/zlib/releases/download/v\#(zlibVersion)/zlib-\#(zlibVersion).tar.gz -o /tmp/zlib.tar.gz
mkdir -p /tmp/dep_zlib-\#(zlibVersion) \
&& cd /tmp/dep_zlib-\#(zlibVersion) \
@@ -72,7 +69,7 @@ local setupLinuxEnvironment: Config.RunStep =
#"""
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
curl -L https://musl.libc.org/releases/musl-\#(muslVersion).tar.gz -o /tmp/musl.tar.gz
curl -Lf https://musl.libc.org/releases/musl-\#(muslVersion).tar.gz -o /tmp/musl.tar.gz
mkdir -p /tmp/dep_musl-\#(muslVersion) \
&& cd /tmp/dep_musl-\#(muslVersion) \
@@ -145,7 +142,7 @@ job {
resource_class = "macos.m1.large.gen1"
}
when (os == "linux") {
docker {
docker = new Listing<Config.DockerImage> {
new {
image = if (arch == "aarch64") "arm64v8/oraclelinux:8-slim" else "oraclelinux:8-slim"
}
@@ -160,5 +157,8 @@ job {
image = "windows-server-2022-gui:current"
}
resource_class = "windows.large"
environment {
["JAVA_HOME"] = "/jdk"
}
}
}

View File

@@ -19,13 +19,9 @@ import "package://pkg.pkl-lang.org/pkl-pantry/com.circleci.v2@1.1.2#/Config.pkl"
local self = this
command: String
javaVersion = "17.0"
job {
docker {
new { image = "cimg/openjdk:17.0" }
}
}
command: String
os = "linux"

View File

@@ -17,29 +17,9 @@ extends "GradleJob.pkl"
import "package://pkg.pkl-lang.org/pkl-pantry/com.circleci.v2@1.1.2#/Config.pkl"
javaVersion: "17.0"|"21.0"
os = "linux"
steps {
new Config.RunStep {
name = "gradle check"
command = "./gradlew \(module.gradleArgs) check"
}
}
job {
when (os == "linux") {
docker {
new {
image = "cimg/openjdk:\(javaVersion)"
}
}
}
when (os == "windows") {
machine {
image = "windows-server-2022-gui:current"
}
resource_class = "windows.large"
}
}

View File

@@ -16,6 +16,7 @@
abstract module GradleJob
import "package://pkg.pkl-lang.org/pkl-pantry/com.circleci.v2@1.1.2#/Config.pkl"
import "package://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.uri@1.0.3#/URI.pkl"
/// Whether this is a release build or not.
isRelease: Boolean = false
@@ -23,6 +24,25 @@ isRelease: Boolean = false
/// The OS to run on
os: "macOS"|"linux"|"windows"
/// The version of Java to use.
javaVersion: "17.0"|"21.0"
fixed javaVersionFull =
if (javaVersion == "17.0") "17.0.9+9"
else "21.0.5+11"
fixed jdkVersionAlt = javaVersionFull.replaceLast("+", "_")
fixed majorJdkVersion = javaVersionFull.split(".").first
fixed jdkGitHubReleaseName =
let (ver =
// 17.0.9+9 is missing some binaries (see https://github.com/adoptium/adoptium-support/issues/994)
if (javaVersionFull == "17.0.9+9" && os == "windows") "jdk-17.0.9+9.1"
else "jdk-\(javaVersionFull)"
)
URI.encodeComponent(ver)
fixed gradleArgs = new Listing {
"--info"
"--stacktrace"
@@ -37,9 +57,41 @@ steps: Listing<Config.Step>
job: Config.Job = new {
environment {
["LANG"] = "en_US.UTF-8"
when (os == "windows") {
["JAVA_HOME"] = "/jdk"
}
}
when (os == "linux") {
docker {
new {
image = "cimg/openjdk:\(javaVersion)"
}
}
}
when (os == "windows") {
machine {
image = "windows-server-2022-gui:current"
}
resource_class = "windows.large"
}
steps {
"checkout"
when (os == "windows") {
new Config.RunStep {
name = "Set up environment"
shell = "bash.exe"
command = #"""
# install jdk
curl -Lf \
https://github.com/adoptium/temurin\#(majorJdkVersion)-binaries/releases/download/\#(jdkGitHubReleaseName)/OpenJDK\#(majorJdkVersion)U-jdk_x64_windows_hotspot_\#(jdkVersionAlt).zip -o /tmp/jdk.zip
unzip /tmp/jdk.zip -d /tmp/jdk \
&& cd /tmp/jdk/jdk-* \
&& mkdir /jdk \
&& cp -r . /jdk
"""#
}
}
...module.steps
new Config.StoreTestResults {
path = "~/test-results"

View File

@@ -23,6 +23,8 @@ command: String
os = "linux"
javaVersion = "17.0"
steps {
new Config.RunStep {
name = module.name
@@ -31,9 +33,3 @@ steps {
"""
}
}
job {
docker {
new { image = "cimg/openjdk:17.0" }
}
}

View File

@@ -3,7 +3,8 @@
:uri-jenv: https://www.jenv.be
:uri-intellij: https://www.jetbrains.com/idea/download/
:uri-jdk: https://adoptopenjdk.net/releases.html?variant=openjdk17
:uri-native-prerequisites: https://www.graalvm.org/latest/getting-started/windows/#prerequisites-for-native-image-on-windows
:uri-native-prerequisites-linux: https://www.graalvm.org/latest/getting-started/linux/#prerequisites-for-native-image-on-linux
:uri-native-prerequisites-windows: https://www.graalvm.org/latest/getting-started/windows/#prerequisites-for-native-image-on-windows
== Setup
@@ -25,12 +26,20 @@ Enable _jenv_ plugins for better handling by `gradle`:
jenv enable-plugin gradle
jenv enable-plugin export
----
. (optional) If you've named the original apple/pkl git repository something other than `origin`, set env var `PKL_ORIGINAL_REMOTE_NAME` to that name in your `.bashrc`, `.zshrc`, `config.fish` or however you manage your local environment.
+
This will allow spotless to pick the correct starting branch when formatting source code files.
Otherwise, you might see that _every_ file has its copyright year updated.
=== Additional Linux Setup
. (optional) To build the native executable (`./gradlew buildNative`),
install {uri-native-prerequisites-linux}[Prerequisites For Native Image on Linux].
=== Additional Windows Setup
. (optional) Go to `System->For developers` and enable `Developer Mode`.
Otherwise, some tests may fail due to insufficient file system privileges.
. (optional) To build the native executable (`./gradlew buildNative`),
install {uri-native-prerequisites}[Prerequisites For Native Image on Windows].
install {uri-native-prerequisites-windows}[Prerequisites For Native Image on Windows].
== Common Build Commands

View File

@@ -1,4 +1,4 @@
Copyright © 2024 Apple Inc. and the Pkl project authors
Copyright © 2024-2025 Apple Inc. and the Pkl project authors
Portions of this software were originally based on 'SnakeYAML' developed by Andrey Somov.

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -132,7 +132,11 @@ private fun KotlinGradleExtension.configureFormatter() {
licenseHeaderFile(licenseHeaderFile, "([a-zA-Z]|@file|//)")
}
val originalRemoteName = System.getenv("PKL_ORIGINAL_REMOTE_NAME") ?: "origin"
spotless {
ratchetFrom = "$originalRemoteName/main"
// When building root project, format buildSrc files too.
// We need this because buildSrc is not a subproject of the root project, so a top-level
// `spotlessApply` will not trigger `buildSrc:spotlessApply`.

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © $YEAR 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © $YEAR 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.

View File

@@ -1,6 +1,6 @@
name: main
title: Main Project
version: 0.27.0-dev
prerelease: true
version: 0.27.2
prerelease: false
nav:
- nav.adoc

View File

@@ -0,0 +1,20 @@
= Evolution and Roadmap
:uri-pkl-roadmap: https://github.com/orgs/apple/projects/12/views/1
:uri-pkl-evolution: https://github.com/apple/pkl-evolution
== Evolution
Sometimes, a change to Pkl is large enough that it makes sense to create a proposal for the change so that it can be discussed in detail and vetted.
Pkl has a process for managing such designs in a repository called {uri-pkl-evolution}[Pkl Evolutiuon].
== Roadmap
To discover what might be coming in future versions, reference the {uri-pkl-roadmap}[Pkl Roadmap] project on GitHub.
The roadmap describes estimates.
The Pkl team aims to cut a release in February, June, and October of each year.
If an item is not complete by the release cutoff date, the roadmap item may be bumped to the next release.
Additionally, as priorities change, it is possible that items can be moved around, or backlogged.

View File

@@ -3,10 +3,10 @@
// the following attributes must be updated immediately before a release
// pkl version corresponding to current git commit without -dev suffix or git hash
:pkl-version-no-suffix: 0.27.0
:pkl-version-no-suffix: 0.27.2
// tells whether pkl version corresponding to current git commit
// is a release version (:is-release-version: '') or dev version (:!is-release-version:)
:!is-release-version:
:is-release-version: ''
// the remaining attributes do not need to be updated regularly

View File

@@ -1,6 +1,6 @@
= Pkl 0.27 Release Notes
:version: 0.27
:version-minor: 0.27.0
:version-minor: 0.27.2
:release-date: November 5th, 2024
include::ROOT:partial$component-attributes.adoc[]
@@ -377,7 +377,7 @@ These are:
The `String` to `Number` converter methods, for example, {uri-stdlib-StringToInt}[`String.toInt()`], can now handle underscore separators (https://github.com/apple/pkl/pull/578[#578], https://github.com/apple/pkl/pull/580[#580]).
This better aligns with the source code representation of file:///Users/danielchao/code/apple/pkl-lang.org/build/local/main/current/language-reference/index.html#integers[number literals].
This better aligns with the source code representation of xref:language-reference:index.adoc#integers[number literals].
[source,pkl]
----

View File

@@ -1,6 +1,44 @@
= Changelog
include::ROOT:partial$component-attributes.adoc[]
[[release-0.27.2]]
== 0.27.2 (2025-01-22)
=== Fixes
* Fixes issues where server mode message decoding might result in null pointer exceptions (https://github.com/apple/pkl/pull/853[#853], https://github.com/apple/pkl/pull/882[#882]).
* Fixes an issue where the test report outputs decimal numbers using local-specific decimals (https://github.com/apple/pkl/pull/868[#868]).
* Fixes an issue where the native executables might not run on some environments, resulting in an error like "Fatal error: Failed to create the main Isolate" (https://github.com/apple/pkl/pull/875[#875]).
=== Contributors ❤️
Thank you to all the contributors for this release!
* link:https://github.com/HT154[@HT154]
* link:https://github.com/StefMa[@StefMa]
[[release-0.27.1]]
== 0.27.1 (2024-12-06)
=== Fixes
- Fixes a broken "number literals" link in the 0.27 release notes (https://github.com/apple/pkl/pull/784[#784]).
- Fixes a possible deadlock during external reader process close (https://github.com/apple/pkl/pull/786[#786]).
- Fixes counting elements with computed indices multiple times in length computation of listings (https://github.com/apple/pkl/pull/797[#797]).
- Fixes non Pkl modules being reported in GatherImports task, leading to plugin failures (https://github.com/apple/pkl/pull/821[#821]).
- Fixes a problem where the delegate chain of type casts for Listing/Mapping get unreasonably big, even though the type nodes are the same, which may lead to a stack overflow or performance degradation (https://github.com/apple/pkl/pull/826[#826]).
- Fixes incorrect scoping of type variables in lazy Listing/Mapping type checking in cross-module typealiases (https://github.com/apple/pkl/pull/789[#789]).
- Fixes regression in type checking logic for Listing/Mapping (https://github.com/apple/pkl/pull/789[#789]).
=== Contributors ❤️
Thank you to all the contributors for this release!
* link:https://github.com/GUI[@GUI]
* link:https://github.com/HT154[@HT154]
* link:https://github.com/odenix[@odenix] (formerly @translatenix)
[[release-0.27.0]]
== 0.27.0 (2024-11-05)

View File

@@ -1,5 +1,7 @@
= Release Notes
The Pkl team aims to release a new version of Pkl in February, June, and October of each year.
* xref:0.27.adoc[0.27 Release Notes]
* xref:0.26.adoc[0.26 Release Notes]
* xref:0.25.adoc[0.25 Release Notes]

View File

@@ -38,6 +38,8 @@
* xref:ROOT:examples.adoc[Examples]
* xref:ROOT:evolution-and-roadmap.adoc[Evolution and Roadmap]
* xref:release-notes:index.adoc[Release Notes]
** xref:release-notes:0.27.adoc[0.27 Release Notes]
** xref:release-notes:0.26.adoc[0.26 Release Notes]

View File

@@ -1,7 +1,7 @@
# suppress inspection "UnusedProperty" for whole file
group=org.pkl-lang
version=0.27.0
version=0.27.2
# google-java-format requires jdk.compiler exports
org.gradle.jvmargs= \

View File

@@ -2,17 +2,17 @@
"catalogs": {},
"aliases": {
"pkl": {
"script-ref": "org.pkl-lang:pkl-cli-java:0.26.3",
"script-ref": "org.pkl-lang:pkl-cli-java:0.27.2",
"java-agents": []
},
"pkl-codegen-java": {
"script-ref": "org.pkl-lang:pkl-codegen-java:0.26.3",
"script-ref": "org.pkl-lang:pkl-codegen-java:0.27.2",
"java-agents": []
},
"pkl-codegen-kotlin": {
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.26.3",
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.27.2",
"java-agents": []
}
},
"templates": {}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -276,7 +276,12 @@ val linuxExecutableAarch64: TaskProvider<Exec> by
dependsOn(":installGraalVmAarch64")
configureExecutable(
buildInfo.graalVmAarch64,
layout.buildDirectory.file("executable/pkl-linux-aarch64")
layout.buildDirectory.file("executable/pkl-linux-aarch64"),
listOf(
// Ensure compatibility for kernels with page size set to 4k, 16k and 64k
// (e.g. Raspberry Pi 5, Asahi Linux)
"-H:PageSize=65536"
)
)
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.io.StringWriter
import java.io.Writer
import java.net.URI
import java.nio.file.Path
import java.util.Locale
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.Test
@@ -487,6 +488,50 @@ class CliTestRunnerTest {
)
}
@Test
fun `CliTestRunner locale independence test`(@TempDir tempDir: Path) {
val originalLocale = Locale.getDefault()
Locale.setDefault(Locale.GERMANY)
try {
val code =
"""
amends "pkl:test"
facts {
["localeTest"] {
1 == 1
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
val out = StringWriter()
val err = StringWriter()
val opts =
CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions()
val runner = CliTestRunner(opts, testOpts, consoleWriter = out, errWriter = err)
runner.run()
assertThat(out.toString().stripFileAndLines(tempDir))
.isEqualTo(
"""
module test
facts
✔ localeTest
100.0% tests pass [1 passed], 100.0% asserts pass [1 passed]
"""
.trimIndent()
)
assertThat(err.toString()).isEqualTo("")
} finally {
Locale.setDefault(originalLocale)
}
}
private fun String.stripFileAndLines(tmpDir: Path): String {
// handle platform differences in handling of file URIs
// (file:/// on *nix vs. file:/ on Windows)

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import java.nio.file.Path
import java.util.stream.Collectors
import kotlin.io.path.*
import org.assertj.core.api.Assertions.fail
import org.opentest4j.AssertionFailedError
import org.pkl.commons.*
object FileTestUtils {
@@ -110,5 +111,11 @@ data class SnippetOutcome(val expectedOutFile: Path, val actual: String, val suc
}
private fun failWithDiff(message: String): Nothing =
throw PklAssertionFailedError(message, expected, actual)
if (System.getProperty("sun.java.command", "").contains("intellij")) {
// IntelliJ only shows diffs for AssertionFailedError
throw AssertionFailedError(message, expected, actual)
} else {
// Gradle test logging/report only shows diffs for PklAssertionFailedError
throw PklAssertionFailedError(message, expected, actual)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1221,7 +1221,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
member.initConstantValue(constantNode);
} else {
member.initMemberNode(
new UntypedObjectMemberNode(
ElementOrEntryNodeGen.create(
language, scope.buildFrameDescriptor(), member, elementNode));
}
@@ -1278,7 +1278,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
member.initConstantValue(constantNode);
} else {
member.initMemberNode(
new UntypedObjectMemberNode(
ElementOrEntryNodeGen.create(
language, scope.buildFrameDescriptor(), member, valueNode));
}
} else { // ["key"] { ... }
@@ -1287,7 +1287,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
objectBodyCtxs,
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
member.initMemberNode(
new UntypedObjectMemberNode(
ElementOrEntryNodeGen.create(
language, scope.buildFrameDescriptor(), member, objectBody));
}
@@ -2446,6 +2446,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
return new UnresolvedTypeNode.Parameterized(
createSourceSection(ctx),
language,
doVisitTypeName(idCtx),
argCtx.ts.stream().map(this::visitType).toArray(UnresolvedTypeNode[]::new));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -103,7 +103,7 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
var callTarget = member.getCallTarget();
value = callTarget.call(parent, owner, key);
}
owner.setCachedValue(key, value, member);
owner.setCachedValue(key, value);
}
frame.setAuxiliarySlot(customThisSlot, value);

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,15 +43,15 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
@Children private final ExpressionNode[] keyNodes;
private final ObjectMember[] values;
public EntriesLiteralNode(
protected EntriesLiteralNode(
SourceSection sourceSection,
VmLanguage language,
// contains local properties and default property (if present)
// does *not* contain entries with constant keys to maintain definition order of entries
String qualifiedScopeName,
boolean isCustomThisScope,
@Nullable FrameDescriptor parametersDescriptor,
UnresolvedTypeNode[] parameterTypes,
// contains local properties and default property (if present)
// does *not* contain entries with constant keys to maintain definition order of entries
UnmodifiableEconomicMap<Object, ObjectMember> members,
ExpressionNode[] keyNodes,
ObjectMember[] values) {
@@ -103,7 +103,8 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
frame.materialize(),
parent,
createListMembers(frame, parent.getLength()),
parent.getLength() + keyNodes.length);
// `[x] = y` overrides existing element and doesn't increase length
parent.getLength());
}
@Specialization

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -71,7 +71,7 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
if (result == null) {
result = callNode.call(objReceiver, owner, property.getName());
objReceiver.setCachedValue(property, result, property);
objReceiver.setCachedValue(property, result);
}
return result;

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -184,12 +184,12 @@ public final class ResolveVariableNode extends ExpressionNode {
if (member != null) {
var constantValue = member.getConstantValue();
if (constantValue != null) {
baseModule.setCachedValue(variableName, constantValue, member);
baseModule.setCachedValue(variableName, constantValue);
return new ConstantValueNode(sourceSection, constantValue);
}
var computedValue = member.getCallTarget().call(baseModule, baseModule);
baseModule.setCachedValue(variableName, computedValue, member);
baseModule.setCachedValue(variableName, computedValue);
return new ConstantValueNode(sourceSection, computedValue);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.ast.member;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Executed;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.expression.primary.GetReceiverNode;
import org.pkl.core.runtime.VmDynamic;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmListing;
import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmUtils;
import org.pkl.core.util.Nullable;
/** Equivalent of {@link TypedPropertyNode} for elements/entries. */
public abstract class ElementOrEntryNode extends RegularMemberNode {
@Child @Executed protected ExpressionNode receiverNode = new GetReceiverNode();
protected ElementOrEntryNode(
@Nullable VmLanguage language,
FrameDescriptor descriptor,
ObjectMember member,
ExpressionNode bodyNode) {
super(language, descriptor, member, bodyNode);
}
@Specialization
protected Object evalListing(
VirtualFrame frame,
VmListing receiver,
@Cached("create()") @Shared("callNode") IndirectCallNode callNode) {
var result = executeBody(frame);
return VmUtils.shouldRunTypeCheck(frame)
? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null)
: result;
}
@Specialization
protected Object evalMapping(
VirtualFrame frame,
VmMapping receiver,
@Cached("create()") @Shared("callNode") IndirectCallNode callNode) {
var result = executeBody(frame);
return VmUtils.shouldRunTypeCheck(frame)
? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null)
: result;
}
@Specialization
protected Object evalDynamic(VirtualFrame frame, VmDynamic ignored) {
return executeBody(frame);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.util.Nullable;
/** Performs a typecast on a Mapping entry value, or a Listing element. */
public class ListingOrMappingTypeCastNode extends PklRootNode {
public final class ListingOrMappingTypeCastNode extends PklRootNode {
@Child private TypeNode typeNode;
private final String qualifiedName;

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@ public abstract class ResolveDeclaredTypeNode extends ExpressionNode {
var result = module.getCachedValue(importName);
if (result == null) {
result = callNode.call(member.getCallTarget(), module, module, importName);
module.setCachedValue(importName, result, member);
module.setCachedValue(importName, result);
}
return (VmTyped) result;
}
@@ -94,7 +94,7 @@ public abstract class ResolveDeclaredTypeNode extends ExpressionNode {
var result = module.getCachedValue(typeName);
if (result == null) {
result = callNode.call(member.getCallTarget(), module, module, typeName);
module.setCachedValue(typeName, result, member);
module.setCachedValue(typeName, result);
}
return result;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -161,7 +162,11 @@ public abstract class TypeNode extends PklNode {
}
/** Tells if this typenode is the same typecheck as the other typenode. */
public abstract boolean isEquivalentTo(TypeNode other);
public boolean isEquivalentTo(TypeNode other) {
return this == other || doIsEquivalentTo(other);
}
protected abstract boolean doIsEquivalentTo(TypeNode other);
public @Nullable VmClass getVmClass() {
return null;
@@ -304,7 +309,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof UnknownTypeNode;
}
@@ -371,7 +376,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof NothingTypeNode;
}
@@ -408,7 +413,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof FinalModuleTypeNode finalModuleTypeNode)) {
return false;
}
@@ -460,7 +465,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof NonFinalModuleTypeNode nonFinalModuleTypeNode)) {
return false;
}
@@ -496,7 +501,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof StringLiteralTypeNode stringLiteralTypeNode)) {
return false;
}
@@ -551,7 +556,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof TypedTypeNode;
}
@@ -586,7 +591,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof DynamicTypeNode;
}
@@ -636,7 +641,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof FinalClassTypeNode finalClassTypeNode)) {
return false;
}
@@ -705,7 +710,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof NonFinalClassTypeNode nonFinalClassTypeNode)) {
return false;
}
@@ -772,7 +777,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof NullableTypeNode nullableTypeNode)) {
return false;
}
@@ -842,7 +847,7 @@ public abstract class TypeNode extends PklNode {
@Override
@ExplodeLoop
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof UnionTypeNode unionTypeNode)) {
return false;
}
@@ -1016,7 +1021,7 @@ public abstract class TypeNode extends PklNode {
@Override
@TruffleBoundary
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof UnionOfStringLiteralsTypeNode unionOfStringLiteralsTypeNode)) {
return false;
}
@@ -1092,7 +1097,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return false;
}
@@ -1223,7 +1228,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof ListTypeNode listTypeNode)) {
return false;
}
@@ -1268,7 +1273,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof SetTypeNode setTypeNode)) {
return false;
}
@@ -1360,7 +1365,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof MapTypeNode mapTypeNode)) {
return false;
}
@@ -1414,8 +1419,9 @@ public abstract class TypeNode extends PklNode {
}
public static final class ListingTypeNode extends ListingOrMappingTypeNode {
public ListingTypeNode(SourceSection sourceSection, TypeNode valueTypeNode) {
super(sourceSection, null, valueTypeNode);
public ListingTypeNode(
SourceSection sourceSection, VmLanguage language, TypeNode valueTypeNode) {
super(sourceSection, language, null, valueTypeNode);
}
@Override
@@ -1423,7 +1429,17 @@ public abstract class TypeNode extends PklNode {
if (!(value instanceof VmListing vmListing)) {
throw typeMismatch(value, BaseModule.getListingClass());
}
return doTypeCast(frame, vmListing);
if (vmListing.isValueTypeKnownSubtypeOf(valueTypeNode)) {
return vmListing;
}
return new VmListing(
vmListing.getEnclosingFrame(),
vmListing,
EconomicMaps.emptyMap(),
vmListing.getLength(),
getValueTypeCastNode(),
VmUtils.getReceiver(frame),
VmUtils.getOwner(frame));
}
@Override
@@ -1451,7 +1467,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof ListingTypeNode listingTypeNode)) {
return false;
}
@@ -1466,9 +1482,12 @@ public abstract class TypeNode extends PklNode {
public static final class MappingTypeNode extends ListingOrMappingTypeNode {
public MappingTypeNode(
SourceSection sourceSection, TypeNode keyTypeNode, TypeNode valueTypeNode) {
SourceSection sourceSection,
VmLanguage language,
TypeNode keyTypeNode,
TypeNode valueTypeNode) {
super(sourceSection, keyTypeNode, valueTypeNode);
super(sourceSection, language, keyTypeNode, valueTypeNode);
}
@Override
@@ -1478,7 +1497,16 @@ public abstract class TypeNode extends PklNode {
}
// execute type checks on mapping keys
doEagerCheck(frame, vmMapping, false, true);
return doTypeCast(frame, vmMapping);
if (vmMapping.isValueTypeKnownSubtypeOf(valueTypeNode)) {
return vmMapping;
}
return new VmMapping(
vmMapping.getEnclosingFrame(),
vmMapping,
EconomicMaps.emptyMap(),
getValueTypeCastNode(),
VmUtils.getReceiver(frame),
VmUtils.getOwner(frame));
}
@Override
@@ -1509,7 +1537,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof MappingTypeNode mappingTypeNode)) {
return false;
}
@@ -1526,17 +1554,22 @@ public abstract class TypeNode extends PklNode {
}
public abstract static class ListingOrMappingTypeNode extends ObjectSlotTypeNode {
private final VmLanguage language;
@Child protected @Nullable TypeNode keyTypeNode;
@Child protected TypeNode valueTypeNode;
@Child @Nullable protected ListingOrMappingTypeCastNode listingOrMappingTypeCastNode;
@Child @Nullable protected ListingOrMappingTypeCastNode valueTypeCastNode;
private final boolean skipKeyTypeChecks;
private final boolean skipValueTypeChecks;
protected ListingOrMappingTypeNode(
SourceSection sourceSection, @Nullable TypeNode keyTypeNode, TypeNode valueTypeNode) {
SourceSection sourceSection,
VmLanguage language,
@Nullable TypeNode keyTypeNode,
TypeNode valueTypeNode) {
super(sourceSection);
this.language = language;
this.keyTypeNode = keyTypeNode;
this.valueTypeNode = valueTypeNode;
@@ -1556,17 +1589,14 @@ public abstract class TypeNode extends PklNode {
return valueTypeNode;
}
protected ListingOrMappingTypeCastNode getListingOrMappingTypeCastNode() {
if (listingOrMappingTypeCastNode == null) {
protected ListingOrMappingTypeCastNode getValueTypeCastNode() {
if (valueTypeCastNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
listingOrMappingTypeCastNode =
valueTypeCastNode =
new ListingOrMappingTypeCastNode(
VmLanguage.get(this),
getRootNode().getFrameDescriptor(),
valueTypeNode,
getRootNode().getName());
language, new FrameDescriptor(), valueTypeNode, getRootNode().getName());
}
return listingOrMappingTypeCastNode;
return valueTypeCastNode;
}
// either (if defaultMemberValue != null):
@@ -1647,15 +1677,6 @@ public abstract class TypeNode extends PklNode {
EconomicMaps.of(Identifier.DEFAULT, defaultMember));
}
protected <T extends VmListingOrMapping<T>> T doTypeCast(VirtualFrame frame, T original) {
// optimization: don't create new object if the original already has the same typecheck, or if
// this typecheck is a no-op.
if (isNoopTypeCheck() || original.hasSameChecksAs(valueTypeNode)) {
return original;
}
return original.withCheckedMembers(getListingOrMappingTypeCastNode(), frame.materialize());
}
protected void doEagerCheck(VirtualFrame frame, VmObject object) {
doEagerCheck(frame, object, skipKeyTypeChecks, skipValueTypeChecks);
}
@@ -1700,7 +1721,7 @@ public abstract class TypeNode extends PklNode {
var callTarget = member.getCallTarget();
memberValue = callTarget.call(object, owner, memberKey);
}
object.setCachedValue(memberKey, memberValue, member);
object.setCachedValue(memberKey, memberValue);
}
valueTypeNode.executeEagerly(frame, memberValue);
}
@@ -1748,7 +1769,7 @@ public abstract class TypeNode extends PklNode {
@Override
@ExplodeLoop
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof FunctionTypeNode functionTypeNode)) {
return false;
}
@@ -1845,7 +1866,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof FunctionClassTypeNode functionClassTypeNode)) {
return false;
}
@@ -1883,7 +1904,7 @@ public abstract class TypeNode extends PklNode {
@Override
@ExplodeLoop
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof FunctionNClassTypeNode functionNClassTypeNode)) {
return false;
}
@@ -1986,7 +2007,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof PairTypeNode pairTypeNode)) {
return false;
}
@@ -2043,7 +2064,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof VarArgsTypeNode varArgsTypeNode)) {
return false;
}
@@ -2095,7 +2116,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof TypeVariableNode;
}
@@ -2135,7 +2156,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof NonNullTypeAliasTypeNode;
}
@@ -2184,7 +2205,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof UIntTypeAliasTypeNode aliasTypeNode && mask == aliasTypeNode.mask;
}
@@ -2228,7 +2249,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof Int8TypeAliasTypeNode;
}
@@ -2272,7 +2293,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof Int16TypeAliasTypeNode;
}
@@ -2316,7 +2337,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof Int32TypeAliasTypeNode;
}
@@ -2387,14 +2408,14 @@ public abstract class TypeNode extends PklNode {
public Object execute(VirtualFrame frame, Object value) {
var prevOwner = VmUtils.getOwner(frame);
var prevReceiver = VmUtils.getReceiver(frame);
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
try {
return aliasedTypeNode.execute(frame, value);
} finally {
VmUtils.setOwner(frame, prevOwner);
VmUtils.setReceiver(frame, prevReceiver);
setOwner(frame, prevOwner);
setReceiver(frame, prevReceiver);
}
}
@@ -2403,14 +2424,14 @@ public abstract class TypeNode extends PklNode {
public Object executeAndSet(VirtualFrame frame, Object value) {
var prevOwner = VmUtils.getOwner(frame);
var prevReceiver = VmUtils.getReceiver(frame);
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
try {
return aliasedTypeNode.executeAndSet(frame, value);
} finally {
VmUtils.setOwner(frame, prevOwner);
VmUtils.setReceiver(frame, prevReceiver);
setOwner(frame, prevOwner);
setReceiver(frame, prevReceiver);
}
}
@@ -2419,14 +2440,14 @@ public abstract class TypeNode extends PklNode {
public Object executeEagerlyAndSet(VirtualFrame frame, Object value) {
var prevOwner = VmUtils.getOwner(frame);
var prevReceiver = VmUtils.getReceiver(frame);
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
try {
return aliasedTypeNode.executeEagerlyAndSet(frame, value);
} finally {
VmUtils.setOwner(frame, prevOwner);
VmUtils.setReceiver(frame, prevReceiver);
setOwner(frame, prevOwner);
setReceiver(frame, prevReceiver);
}
}
@@ -2471,7 +2492,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
if ((other instanceof TypeAliasTypeNode typeAliasTypeNode)) {
return aliasedTypeNode.isEquivalentTo(typeAliasTypeNode.aliasedTypeNode);
}
@@ -2498,6 +2519,22 @@ public abstract class TypeNode extends PklNode {
protected boolean isParametric() {
return typeArgumentNodes.length > 0;
}
// Note that mutating a frame's receiver and owner argument is very risky
// because any VmObject instantiated within the same root node execution
// holds a reference to (not immutable snapshot of) the frame
// via VmObjectLike.enclosingFrame.
// *Maybe* this works out for TypeAliasTypeNode because an object instantiated
// within a type constraint doesn't escape the constraint expression.
// If mutating receiver and owner can't be avoided, it would be safer
// to have VmObjectLike store them directly instead of storing enclosingFrame.
private static void setReceiver(Frame frame, Object receiver) {
frame.getArguments()[0] = receiver;
}
private static void setOwner(Frame frame, VmObjectLike owner) {
frame.getArguments()[1] = owner;
}
}
public static final class ConstrainedTypeNode extends TypeNode {
@@ -2581,7 +2618,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
// consider constrained types as always different
return false;
}
@@ -2622,7 +2659,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof AnyTypeNode;
}
@@ -2650,7 +2687,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof StringTypeNode;
}
@@ -2714,7 +2751,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof NumberTypeNode;
}
@@ -2742,7 +2779,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof IntTypeNode;
}
@@ -2787,7 +2824,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof FloatTypeNode;
}
@@ -2832,7 +2869,7 @@ public abstract class TypeNode extends PklNode {
}
@Override
public boolean isEquivalentTo(TypeNode other) {
public boolean doIsEquivalentTo(TypeNode other) {
return other instanceof BooleanTypeNode;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -197,14 +197,17 @@ public abstract class UnresolvedTypeNode extends PklNode {
}
public static final class Parameterized extends UnresolvedTypeNode {
private final VmLanguage language;
@Child private ExpressionNode resolveTypeNode;
@Children private final UnresolvedTypeNode[] typeArgumentNodes;
public Parameterized(
SourceSection sourceSection,
VmLanguage language,
ExpressionNode resolveTypeNode,
UnresolvedTypeNode[] typeArgumentNodes) {
super(sourceSection);
this.language = language;
this.resolveTypeNode = resolveTypeNode;
this.typeArgumentNodes = typeArgumentNodes;
}
@@ -238,12 +241,13 @@ public abstract class UnresolvedTypeNode extends PklNode {
}
if (clazz.isListingClass()) {
return new ListingTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
return new ListingTypeNode(sourceSection, language, typeArgumentNodes[0].execute(frame));
}
if (clazz.isMappingClass()) {
return new MappingTypeNode(
sourceSection,
language,
typeArgumentNodes[0].execute(frame),
typeArgumentNodes[1].execute(frame));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,17 +17,16 @@ package org.pkl.core.externalreader;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
import org.pkl.core.Duration;
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings.ExternalReader;
import org.pkl.core.externalreader.ExternalReaderMessages.*;
import org.pkl.core.messaging.MessageTransport;
@@ -108,7 +107,9 @@ final class ExternalReaderProcessImpl implements ExternalReaderProcess {
// This relies on Java/OS behavior around PATH resolution, absolute/relative paths, etc.
var command = new ArrayList<String>();
command.add(spec.executable());
command.addAll(spec.arguments());
if (spec.arguments() != null) {
command.addAll(spec.arguments());
}
var builder = new ProcessBuilder(command);
builder.redirectError(Redirect.INHERIT); // inherit stderr from this pkl process
@@ -152,40 +153,23 @@ final class ExternalReaderProcessImpl implements ExternalReaderProcess {
@Override
public void close() {
synchronized (lock) {
if (closed) return;
closed = true;
if (process == null || !process.isAlive()) {
return;
}
try {
if (transport != null) {
if (transport != null && process != null && process.isAlive()) {
transport.send(new CloseExternalProcess());
process.waitFor(CLOSE_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
}
} catch (Exception ignored) {
} finally {
if (process != null) {
// no-op unless process is alive
process.destroyForcibly();
}
if (transport != null) {
transport.close();
}
// forcefully stop the process after the timeout
// note that both transport.close() and process.destroy() are safe to call multiple times
new Timer()
.schedule(
new TimerTask() {
@Override
public void run() {
if (process != null) {
transport.close();
process.destroyForcibly();
}
}
},
CLOSE_TIMEOUT.inWholeMillis());
// block on process exit
process.onExit().get();
} catch (Exception e) {
transport.close();
process.destroyForcibly();
} finally {
process = null;
transport = null;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -113,7 +113,7 @@ public final class VmFunction extends VmObjectLike {
@Override
@TruffleBoundary
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
public void setCachedValue(Object key, Object value) {
throw new VmExceptionBuilder()
.bug("Class `VmFunction` does not support method `setCachedValue()`.")
.build();

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ import org.pkl.core.util.Nullable;
@TruffleLanguage.Registration(
id = "pkl",
name = "Pkl",
version = "0.27.0-dev",
version = "0.27.2",
characterMimeTypes = VmLanguage.MIME_TYPE,
contextPolicy = ContextPolicy.SHARED)
public final class VmLanguage extends TruffleLanguage<VmContext> {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,14 +19,13 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.graalvm.collections.UnmodifiableEconomicMap;
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.Nullable;
public final class VmListing extends VmListingOrMapping<VmListing> {
public final class VmListing extends VmListingOrMapping {
private static final class EmptyHolder {
private static final VmListing EMPTY =
new VmListing(
@@ -47,7 +46,7 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
VmObject parent,
UnmodifiableEconomicMap<Object, ObjectMember> members,
int length) {
super(enclosingFrame, Objects.requireNonNull(parent), members, null, null, null);
super(enclosingFrame, parent, members);
this.length = length;
}
@@ -56,16 +55,10 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
VmObject parent,
UnmodifiableEconomicMap<Object, ObjectMember> members,
int length,
@Nullable VmListing delegate,
ListingOrMappingTypeCastNode typeCheckNode,
MaterializedFrame typeNodeFrame) {
super(
enclosingFrame,
Objects.requireNonNull(parent),
members,
delegate,
typeCheckNode,
typeNodeFrame);
ListingOrMappingTypeCastNode typeCastNode,
Object typeCheckReceiver,
VmObjectLike typeCheckOwner) {
super(enclosingFrame, parent, members, typeCastNode, typeCheckReceiver, typeCheckOwner);
this.length = length;
}
@@ -117,20 +110,6 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
return converter.convertListing(this, path);
}
@Override
public VmListing withCheckedMembers(
ListingOrMappingTypeCastNode typeCheckNode, MaterializedFrame typeNodeFrame) {
return new VmListing(
getEnclosingFrame(),
Objects.requireNonNull(parent),
members,
length,
this,
typeCheckNode,
typeNodeFrame);
}
@Override
@TruffleBoundary
public boolean equals(@Nullable Object obj) {
@@ -142,10 +121,14 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
force(false);
other.force(false);
for (var i = 0L; i < length; i++) {
var value = getCachedValue(i);
var cursor = cachedValues.getEntries();
while (cursor.advance()) {
var key = cursor.getKey();
if (key instanceof Identifier) continue;
var value = cursor.getValue();
assert value != null;
var otherValue = other.getCachedValue(i);
var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false;
}
@@ -156,14 +139,16 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
@TruffleBoundary
public int hashCode() {
if (cachedHash != 0) return cachedHash;
// It's possible that the delegate has already computed its hash code.
// If so, we can go ahead and use it.
if (delegate != null && delegate.cachedHash != 0) return delegate.cachedHash;
force(false);
var result = 0;
for (var i = 0L; i < length; i++) {
var value = getCachedValue(i);
var cursor = cachedValues.getEntries();
while (cursor.advance()) {
var key = cursor.getKey();
if (key instanceof Identifier) continue;
var value = cursor.getValue();
assert value != null;
result = 31 * result + value.hashCode();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,159 +16,122 @@
package org.pkl.core.runtime;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.EconomicSet;
import org.graalvm.collections.UnmodifiableEconomicMap;
import org.pkl.core.PklBugException;
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.EconomicSets;
import org.pkl.core.util.Nullable;
public abstract class VmListingOrMapping<SELF extends VmListingOrMapping<SELF>> extends VmObject {
/**
* A Listing or Mapping typecast creates a new object that contains a new typecheck node, and
* delegates member lookups to this delegate.
*/
protected final @Nullable SELF delegate;
public abstract class VmListingOrMapping extends VmObject {
// reified type of listing elements and mapping values
private final @Nullable ListingOrMappingTypeCastNode typeCastNode;
private final MaterializedFrame typeNodeFrame;
private final EconomicMap<Object, ObjectMember> cachedMembers = EconomicMaps.create();
private final EconomicSet<Object> checkedMembers = EconomicSets.create();
private final @Nullable Object typeCheckReceiver;
private final @Nullable VmObjectLike typeCheckOwner;
public VmListingOrMapping(
MaterializedFrame enclosingFrame,
@Nullable VmObject parent,
UnmodifiableEconomicMap<Object, ObjectMember> members) {
super(enclosingFrame, parent, members);
typeCastNode = null;
typeCheckReceiver = null;
typeCheckOwner = null;
}
public VmListingOrMapping(
MaterializedFrame enclosingFrame,
@Nullable VmObject parent,
UnmodifiableEconomicMap<Object, ObjectMember> members,
@Nullable SELF delegate,
@Nullable ListingOrMappingTypeCastNode typeCastNode,
@Nullable MaterializedFrame typeNodeFrame) {
ListingOrMappingTypeCastNode typeCastNode,
Object typeCheckReceiver,
VmObjectLike typeCheckOwner) {
super(enclosingFrame, parent, members);
this.delegate = delegate;
this.typeCastNode = typeCastNode;
this.typeNodeFrame = typeNodeFrame;
this.typeCheckReceiver = typeCheckReceiver;
this.typeCheckOwner = typeCheckOwner;
}
ObjectMember findMember(Object key) {
var member = EconomicMaps.get(cachedMembers, key);
if (member != null) {
return member;
}
if (delegate != null) {
return delegate.findMember(key);
}
// member is guaranteed to exist; this is only called if `getCachedValue()` returns non-null
// and `setCachedValue` will record the object member in `cachedMembers`.
throw PklBugException.unreachableCode();
}
public @Nullable ListingOrMappingTypeCastNode getTypeCastNode() {
return typeCastNode;
}
@Override
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
super.setCachedValue(key, value, objectMember);
EconomicMaps.put(cachedMembers, key, objectMember);
}
@Override
public boolean hasCachedValue(Object key) {
return super.hasCachedValue(key) || delegate != null && delegate.hasCachedValue(key);
}
@Override
public @Nullable Object getCachedValue(Object key) {
var myCachedValue = super.getCachedValue(key);
if (myCachedValue != null || delegate == null) {
return myCachedValue;
}
var memberValue = delegate.getCachedValue(key);
// if this object member appears inside `checkedMembers`, we have already checked its type
// and can safely return it.
if (EconomicSets.contains(checkedMembers, key)) {
return memberValue;
}
if (memberValue == null) {
return null;
}
// If a cached value already exists on the delegate, run a typecast on it.
// optimization: don't use `VmUtils.findMember` to avoid iterating over all members
var objectMember = findMember(key);
var ret = typecastObjectMember(objectMember, memberValue, IndirectCallNode.getUncached());
if (ret != memberValue) {
EconomicMaps.put(cachedValues, key, ret);
} else {
// optimization: don't add to own cached values if typecast results in the same value
EconomicSets.add(checkedMembers, key);
}
return ret;
}
@Override
public Object getExtraStorage() {
if (delegate != null) {
return delegate.getExtraStorage();
}
assert extraStorage != null;
return extraStorage;
}
/** Perform a typecast on this member, */
public Object typecastObjectMember(
ObjectMember member, Object memberValue, IndirectCallNode callNode) {
if (!(member.isEntry() || member.isElement()) || typeCastNode == null) {
return memberValue;
}
assert typeNodeFrame != null;
var ret = memberValue;
if (delegate != null) {
ret = delegate.typecastObjectMember(member, ret, callNode);
}
// Recursively executes type casts between `owner` and `this` and returns the resulting value.
public final Object executeTypeCasts(
Object value,
VmObjectLike owner,
IndirectCallNode callNode,
// if non-null, a stack frame for this member is inserted if a type cast fails
@Nullable ObjectMember member,
// Next type cast to be performed by the caller.
// Avoids repeating the same type cast in some cases.
@Nullable ListingOrMappingTypeCastNode nextTypeCastNode) {
var newNextTypeCastNode = typeCastNode != null ? typeCastNode : nextTypeCastNode;
@SuppressWarnings("DataFlowIssue")
var result =
this == owner
? value
: ((VmListingOrMapping) parent)
.executeTypeCasts(value, owner, callNode, member, newNextTypeCastNode);
if (typeCastNode == null || typeCastNode == nextTypeCastNode) return result;
var callTarget = typeCastNode.getCallTarget();
try {
return callNode.call(
callTarget, VmUtils.getReceiver(typeNodeFrame), VmUtils.getOwner(typeNodeFrame), ret);
} catch (VmException vmException) {
return callNode.call(callTarget, typeCheckReceiver, typeCheckOwner, result);
} catch (VmException e) {
CompilerDirectives.transferToInterpreter();
// treat typecheck as part of the call stack to read the original member if there is a
// source section for it.
var sourceSection = member.getBodySection();
if (!sourceSection.isAvailable()) {
sourceSection = member.getSourceSection();
if (member != null) {
VmUtils.insertStackFrame(member, callTarget, e);
}
if (sourceSection.isAvailable()) {
vmException
.getInsertedStackFrames()
.put(callTarget, VmUtils.createStackFrame(sourceSection, member.getQualifiedName()));
}
throw vmException;
throw e;
}
}
public abstract SELF withCheckedMembers(
ListingOrMappingTypeCastNode typeCastNode, MaterializedFrame typeNodeFrame);
@Override
@TruffleBoundary
public final @Nullable Object getCachedValue(Object key) {
var result = EconomicMaps.get(cachedValues, key);
// if this object has members, `this[key]` may differ from `parent[key]`, so stop the search
if (result != null || !members.isEmpty()) return result;
/** Tells if this mapping/listing runs the same typechecks as {@code typeNode}. */
public boolean hasSameChecksAs(TypeNode typeNode) {
// Optimization: Recursively steal value from parent cache to avoid computing it multiple times.
// The current implementation has the following limitations and drawbacks:
// * It only works if a parent has, coincidentally, already cached `key`.
// * It turns getCachedValue() into an operation that isn't guaranteed to be fast and fail-safe.
// * It requires making VmObject.getCachedValue() non-final,
// which is unfavorable for Truffle partial evaluation and JVM inlining.
// * It may not be worth its cost for constant members and members that are cheap to compute.
assert parent != null; // VmListingOrMapping always has a parent
result = parent.getCachedValue(key);
if (result == null) return null;
if (typeCastNode != null && !(key instanceof Identifier)) {
var callNode = IndirectCallNode.getUncached();
var callTarget = typeCastNode.getCallTarget();
try {
result = callNode.call(callTarget, typeCheckReceiver, typeCheckOwner, result);
} catch (VmException e) {
var member = VmUtils.findMember(parent, key);
assert member != null; // already found the member's cached value
VmUtils.insertStackFrame(member, callTarget, e);
throw e;
}
}
setCachedValue(key, result);
return result;
}
/**
* Tells whether the value type of this listing/mapping is known to be a subtype of {@code
* typeNode}. If {@code true}, type checks of individual values can be elided because
* listings/mappings are covariant in their value type.
*/
public final boolean isValueTypeKnownSubtypeOf(TypeNode typeNode) {
if (typeNode.isNoopTypeCheck()) {
return true;
}
if (typeCastNode == null) {
return false;
}
if (typeCastNode.getTypeNode().isEquivalentTo(typeNode)) {
return true;
}
// we can say the check is the same if the delegate has this check.
// when `Listing<Any>` delegates to `Listing<UInt>`, it has the same checks as a `UInt`
// typenode.
if (delegate != null) {
return delegate.hasSameChecksAs(typeNode);
}
return false;
return typeCastNode.getTypeNode().isEquivalentTo(typeNode);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.pkl.core.runtime;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.Map;
import java.util.Objects;
import javax.annotation.concurrent.GuardedBy;
import org.graalvm.collections.UnmodifiableEconomicMap;
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
@@ -27,7 +26,7 @@ import org.pkl.core.util.CollectionUtils;
import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
public final class VmMapping extends VmListingOrMapping<VmMapping> {
public final class VmMapping extends VmListingOrMapping {
private int cachedEntryCount = -1;
@@ -50,24 +49,17 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
MaterializedFrame enclosingFrame,
VmObject parent,
UnmodifiableEconomicMap<Object, ObjectMember> members) {
super(enclosingFrame, Objects.requireNonNull(parent), members, null, null, null);
super(enclosingFrame, parent, members);
}
public VmMapping(
MaterializedFrame enclosingFrame,
VmObject parent,
UnmodifiableEconomicMap<Object, ObjectMember> members,
VmMapping delegate,
ListingOrMappingTypeCastNode typeCheckNode,
MaterializedFrame typeNodeFrame) {
super(
enclosingFrame,
Objects.requireNonNull(parent),
members,
delegate,
typeCheckNode,
typeNodeFrame);
ListingOrMappingTypeCastNode typeCastNode,
Object typeCheckReceiver,
VmObjectLike typeCheckOwner) {
super(enclosingFrame, parent, members, typeCastNode, typeCheckReceiver, typeCheckOwner);
}
public static boolean isDefaultProperty(Object propertyKey) {
@@ -81,16 +73,12 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
@TruffleBoundary
public VmSet getAllKeys() {
if (delegate != null) {
return delegate.getAllKeys();
}
synchronized (this) {
if (__allKeys == null) {
// building upon parent's `getAllKeys()` should improve at least worst case efficiency
var parentKeys =
getParent() instanceof VmMapping mapping ? mapping.getAllKeys() : VmSet.EMPTY;
var parentKeys = parent instanceof VmMapping mapping ? mapping.getAllKeys() : VmSet.EMPTY;
var builder = VmSet.builder(parentKeys);
for (var cursor = getMembers().getEntries(); cursor.advance(); ) {
for (var cursor = members.getEntries(); cursor.advance(); ) {
var member = cursor.getValue();
if (!member.isEntry()) continue;
builder.add(cursor.getKey());
@@ -133,12 +121,17 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
if (this == obj) return true;
if (!(obj instanceof VmMapping other)) return false;
if (getEntryCount() != other.getEntryCount()) return false;
// could use shallow force, but deep force is cached
force(false);
other.force(false);
for (var key : getAllKeys()) {
var value = getCachedValue(key);
if (getEntryCount() != other.getEntryCount()) return false;
var cursor = cachedValues.getEntries();
while (cursor.advance()) {
Object key = cursor.getKey();
if (key instanceof Identifier) continue;
var value = cursor.getValue();
assert value != null;
var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false;
@@ -151,38 +144,34 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
@TruffleBoundary
public int hashCode() {
if (cachedHash != 0) return cachedHash;
// It's possible that the delegate has already computed its hash code.
// If so, we can go ahead and use it.
if (delegate != null && delegate.cachedHash != 0) return delegate.cachedHash;
force(false);
var result = 0;
for (var key : getAllKeys()) {
var cursor = cachedValues.getEntries();
while (cursor.advance()) {
var key = cursor.getKey();
if (key instanceof Identifier) continue;
var value = getCachedValue(key);
var value = cursor.getValue();
assert value != null;
result += key.hashCode() ^ value.hashCode();
}
cachedHash = result;
return result;
}
// assumes mapping has been forced
public int getEntryCount() {
if (cachedEntryCount != -1) return cachedEntryCount;
cachedEntryCount = getAllKeys().getLength();
return cachedEntryCount;
}
@Override
@TruffleBoundary
public VmMapping withCheckedMembers(
ListingOrMappingTypeCastNode typeCheckNode, MaterializedFrame typeNodeFrame) {
return new VmMapping(
getEnclosingFrame(),
Objects.requireNonNull(getParent()),
getMembers(),
this,
typeCheckNode,
typeNodeFrame);
var result = 0;
for (var key : cachedValues.getKeys()) {
if (key instanceof Identifier) continue;
result += 1;
}
cachedEntryCount = result;
return result;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -87,12 +87,12 @@ public abstract class VmObject extends VmObjectLike {
}
@Override
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
public final void setCachedValue(Object key, Object value) {
EconomicMaps.put(cachedValues, key, value);
}
@Override
public boolean hasCachedValue(Object key) {
public final boolean hasCachedValue(Object key) {
return EconomicMaps.containsKey(cachedValues, key);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -96,7 +96,7 @@ public abstract class VmObjectLike extends VmValue {
* receiver.
*/
@TruffleBoundary
public abstract void setCachedValue(Object key, Object value, ObjectMember objectMember);
public abstract void setCachedValue(Object key, Object value);
/**
* Prefer this method over {@link #getCachedValue} if the value is not required. (There is no

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.pkl.core.runtime;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
@@ -134,10 +135,6 @@ public final class VmUtils {
return result;
}
public static void setReceiver(Frame frame, Object receiver) {
frame.getArguments()[0] = receiver;
}
public static VmObjectLike getObjectReceiver(Frame frame) {
return (VmObjectLike) getReceiver(frame);
}
@@ -158,10 +155,6 @@ public final class VmUtils {
return result;
}
public static void setOwner(Frame frame, VmObjectLike owner) {
frame.getArguments()[1] = owner;
}
/** Returns a `ObjectMember`'s key while executing the corresponding `MemberNode`. */
public static Object getMemberKey(Frame frame) {
return frame.getArguments()[2];
@@ -261,17 +254,17 @@ public final class VmUtils {
final var constantValue = member.getConstantValue();
if (constantValue != null) {
var ret = constantValue;
// for a property, do a type check
var result = constantValue;
// for a property, Listing element, or Mapping value, do a type check
if (member.isProp()) {
var property = receiver.getVmClass().getProperty(member.getName());
if (property != null && property.getTypeNode() != null) {
var callTarget = property.getTypeNode().getCallTarget();
try {
if (checkType) {
ret = callNode.call(callTarget, receiver, property.getOwner(), constantValue);
result = callNode.call(callTarget, receiver, property.getOwner(), constantValue);
} else {
ret =
result =
callNode.call(
callTarget,
receiver,
@@ -281,44 +274,52 @@ public final class VmUtils {
}
} catch (VmException e) {
CompilerDirectives.transferToInterpreter();
// A failed property type check looks as follows in the stack trace:
// x: Int(isPositive)
// at ...
// x = -10
// at ...
// However, if the value of `x` is a parse-time constant (as in `x = -10`),
// there's no root node for it and hence no stack trace element.
// In this case, insert a stack trace element to make the stack trace look the same
// as in the non-constant case. (Parse-time constants are an internal optimization.)
e.getInsertedStackFrames()
.put(
callTarget,
createStackFrame(member.getBodySection(), member.getQualifiedName()));
insertStackFrame(member, callTarget, e);
throw e;
}
}
} else if (receiver instanceof VmListingOrMapping<?> vmListingOrMapping) {
if (owner != receiver && owner instanceof VmListingOrMapping<?> vmListingOrMappingOwner) {
ret = vmListingOrMappingOwner.typecastObjectMember(member, ret, callNode);
}
ret = vmListingOrMapping.typecastObjectMember(member, ret, callNode);
} else if (receiver instanceof VmListingOrMapping listingOrMapping
&& owner instanceof VmListingOrMapping) {
// `owner instanceof VmListingOrMapping` guards against
// PropertiesRenderer amending VmDynamic with VmListing (hack?)
result = listingOrMapping.executeTypeCasts(constantValue, owner, callNode, member, null);
}
receiver.setCachedValue(memberKey, ret, member);
return ret;
receiver.setCachedValue(memberKey, result);
return result;
}
var callTarget = member.getCallTarget();
Object ret;
Object result;
if (checkType) {
ret = callNode.call(callTarget, receiver, owner, memberKey);
result = callNode.call(callTarget, receiver, owner, memberKey);
} else {
ret = callNode.call(callTarget, receiver, owner, memberKey, VmUtils.SKIP_TYPECHECK_MARKER);
result = callNode.call(callTarget, receiver, owner, memberKey, VmUtils.SKIP_TYPECHECK_MARKER);
}
if (receiver instanceof VmListingOrMapping<?> vmListingOrMapping) {
ret = vmListingOrMapping.typecastObjectMember(member, ret, callNode);
receiver.setCachedValue(memberKey, result);
return result;
}
// A failed property type check looks as follows in the stack trace:
// x: Int(isPositive)
// at ...
// x = -10
// at ...
// However, if the value of `x` is a parse-time constant (as in `x = -10`),
// there's no root node for it and hence no stack trace element.
// In this case, insert a stack trace element to make the stack trace look the same
// as in the non-constant case. (Parse-time constants are an internal optimization.)
public static void insertStackFrame(
ObjectMember member, CallTarget location, VmException exception) {
var sourceSection = member.getBodySection();
if (!sourceSection.isAvailable()) {
sourceSection = member.getSourceSection();
}
if (sourceSection.isAvailable()) {
exception
.getInsertedStackFrames()
.put(location, createStackFrame(sourceSection, member.getQualifiedName()));
}
receiver.setCachedValue(memberKey, ret, member);
return ret;
}
public static @Nullable ObjectMember findMember(VmObjectLike receiver, Object memberKey) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmDynamic;
import org.pkl.core.runtime.VmExceptionBuilder;
import org.pkl.core.runtime.VmIntSeq;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmList;
import org.pkl.core.runtime.VmListing;
import org.pkl.core.runtime.VmMap;
@@ -573,7 +574,8 @@ public final class RendererNodes {
type =
requiresWrapper()
? null
: new ListingTypeNode(VmUtils.unavailableSourceSection(), valueType);
: new ListingTypeNode(
VmUtils.unavailableSourceSection(), VmLanguage.get(null), valueType);
return type;
} else if (type instanceof MappingTypeNode mappingType) {
var keyType = resolveType(mappingType.getKeyTypeNode());
@@ -587,7 +589,9 @@ public final class RendererNodes {
}
var valueType = resolveType(mappingType.getValueTypeNode());
assert valueType != null : "Incomplete or malformed Mapping type";
mappingType = new MappingTypeNode(VmUtils.unavailableSourceSection(), keyType, valueType);
mappingType =
new MappingTypeNode(
VmUtils.unavailableSourceSection(), VmLanguage.get(null), keyType, valueType);
type = requiresWrapper() ? null : mappingType;
return type;

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.pkl.core.stdlib.test.report;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import org.pkl.core.TestResults;
import org.pkl.core.TestResults.TestResult;
@@ -144,7 +145,10 @@ public final class SimpleReport implements TestReport {
sb.append(
color,
() ->
sb.append(String.format("%.1f%%", passRate)).append(" ").append(kind).append(" pass"));
sb.append(String.format(Locale.ROOT, "%.1f%%", passRate))
.append(" ")
.append(kind)
.append(" pass"));
if (isFailed) {
sb.append(" [").append(failed).append('/').append(total).append(" failed]");

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,10 @@ import org.graalvm.collections.UnmodifiableMapCursor;
public final class EconomicMaps {
private EconomicMaps() {}
public static <K, V> UnmodifiableEconomicMap<K, V> emptyMap() {
return EconomicMap.emptyMap();
}
@TruffleBoundary
public static <K, V> EconomicMap<K, V> create() {
return EconomicMap.create();

View File

@@ -0,0 +1,3 @@
function isValid(str): Boolean = str.startsWith("a")
foo: Listing<String(isValid(this))>(isDistinct)

View File

@@ -27,12 +27,20 @@ local altered: Listing<Person> = (base) {
[0] { name = "Wood Pigeon" }
}
local computedIndex: Listing<Person> = (base) {
[computeIndex()] { name = "Wood Pigeon" }
}
local function computeIndex() = 0
facts {
["isEmpty"] {
empty.isEmpty
empty2.isEmpty
!base.isEmpty
!derived.isEmpty
!altered.isEmpty
!computedIndex.isEmpty
}
["lastIndex"] {
@@ -41,6 +49,8 @@ facts {
base.lastIndex == 2
derived.lastIndex == 4
duplicate.lastIndex == 5
altered.lastIndex == 2
computedIndex.lastIndex == 2
}
["isDistinct"] {
@@ -49,6 +59,8 @@ facts {
base.isDistinct
derived.isDistinct
!duplicate.isDistinct
altered.isDistinct
computedIndex.isDistinct
}
["isDistinctBy()"] {
@@ -57,18 +69,24 @@ facts {
base.isDistinctBy((it) -> it)
derived.isDistinctBy((it) -> it)
!duplicate.isDistinctBy((it) -> it)
altered.isDistinctBy((it) -> it)
computedIndex.isDistinctBy((it) -> it)
empty.isDistinctBy((it) -> it.name)
empty2.isDistinctBy((it) -> it.name)
base.isDistinctBy((it) -> it.name)
derived.isDistinctBy((it) -> it.name)
!duplicate.isDistinctBy((it) -> it.name)
altered.isDistinctBy((it) -> it.name)
computedIndex.isDistinctBy((it) -> it.name)
empty.isDistinctBy((it) -> it.getClass())
empty2.isDistinctBy((it) -> it.getClass())
!base.isDistinctBy((it) -> it.getClass())
!derived.isDistinctBy((it) -> it.getClass())
!duplicate.isDistinctBy((it) -> it.getClass())
!altered.isDistinctBy((it) -> it.getClass())
!computedIndex.isDistinctBy((it) -> it.getClass())
}
["getOrNull"] {
@@ -85,24 +103,32 @@ facts {
module.catch(() -> empty.first) == "Expected a non-empty Listing."
base.first == base[0]
derived.first == base[0]
altered.first != base[0]
computedIndex.first == altered.first
}
["firstOrNull"] {
empty.firstOrNull == null
base.firstOrNull == base[0]
derived.firstOrNull == base[0]
altered.firstOrNull != base[0]
computedIndex.firstOrNull == altered.first
}
["last"] {
module.catch(() -> empty.last) == "Expected a non-empty Listing."
base.last == base[2]
derived.last == derived[4]
altered.last == base[2]
computedIndex.last == base[2]
}
["lastOrNull"] {
empty.lastOrNull == null
base.lastOrNull == base[2]
derived.lastOrNull == derived[4]
altered.lastOrNull == base[2]
computedIndex.lastOrNull == base[2]
}
["single"] {
@@ -135,6 +161,7 @@ facts {
derived.contains(base[1])
derived.contains(derived[3])
!altered.contains(base[0])
!computedIndex.contains(base[0])
}
}
@@ -144,6 +171,17 @@ examples {
empty2.length
base.length
derived.length
altered.length
computedIndex.length
local elementsAndEntries = (base) {
new { name = "" }
[2] { name = "" }
[computeIndex()] { name = "" }
new { name = "" }
[1 + 0] { name = "" }
}
elementsAndEntries.length
}
["toList()"] {
@@ -152,6 +190,8 @@ examples {
base.toList()
derived.toList()
duplicate.toList()
altered.toList()
computedIndex.toList()
}
["toSet()"] {
@@ -160,6 +200,8 @@ examples {
base.toSet()
derived.toSet()
duplicate.toSet()
altered.toSet()
computedIndex.toSet()
}
["distinct"] {
@@ -168,6 +210,8 @@ examples {
base.distinct
derived.distinct
duplicate.distinct
altered.distinct
computedIndex.distinct
}
["distinctBy()"] {
@@ -176,36 +220,48 @@ examples {
base.distinctBy((it) -> it)
derived.distinctBy((it) -> it)
duplicate.distinctBy((it) -> it)
altered.distinctBy((it) -> it)
computedIndex.distinctBy((it) -> it)
empty.distinctBy((it) -> it.name)
empty2.distinctBy((it) -> it.name)
base.distinctBy((it) -> it.name)
derived.distinctBy((it) -> it.name)
duplicate.distinctBy((it) -> it.name)
altered.distinctBy((it) -> it.name)
computedIndex.distinctBy((it) -> it.name)
empty.distinctBy((it) -> it.getClass())
empty2.distinctBy((it) -> it.getClass())
base.distinctBy((it) -> it.getClass())
derived.distinctBy((it) -> it.getClass())
duplicate.distinctBy((it) -> it.getClass())
altered.distinctBy((it) -> it.getClass())
computedIndex.distinctBy((it) -> it.getClass())
}
["fold"] {
empty.fold(List(), (l, e) -> l.add(e))
base.fold(List(), (l, e) -> l.add(e))
derived.fold(List(), (l, e) -> l.add(e))
altered.fold(List(), (l, e) -> l.add(e))
computedIndex.fold(List(), (l, e) -> l.add(e))
}
["foldIndexed"] {
empty.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
base.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
derived.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
altered.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
computedIndex.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
}
local baseNum = new Listing { 1; 2; 3 }
local baseString = new Listing { "Pigeon"; "Barn Owl"; "Parrot" }
local derivedString = (baseString) { "Albatross"; "Elf Owl" }
local alteredString = (baseString) { [0] = "Wood Pigeon" }
local computedIndexString = (baseString) { [computeIndex()] = "Wood Pigeon" }
["join"] {
empty.join("")
@@ -215,5 +271,9 @@ examples {
baseString.join("---")
derivedString.join("")
derivedString.join("\n")
alteredString.join("")
alteredString.join("\n")
computedIndexString.join("")
computedIndexString.join("\n")
}
}

View File

@@ -0,0 +1,6 @@
local a = new Listing { new Listing { 0 } }
local b = a as Listing<Listing<String>>
local c = (b) { new Listing { 1 } }
local d = c as Listing<Listing<Int>>
result = d

View File

@@ -0,0 +1,10 @@
local a = new Listing { new Foo {} }
local b = (a) { new Bar {} }
local c = b as Listing<Bar>
local d = (c) { new Foo {} }
local e = d as Listing<Foo>
result = e
open class Foo
class Bar extends Foo

View File

@@ -0,0 +1,10 @@
local a = new Mapping { [0] = new Foo {} }
local b = (a) { [1] = new Bar {} }
local c = b as Mapping<Int, Bar>
local d = (c) { [2] = new Foo {} }
local e = d as Mapping<Int, Foo>
result = e
open class Foo
class Bar extends Foo

View File

@@ -0,0 +1,7 @@
foo1: Listing<String> = new { "hello" }
foo2: Listing<String|Int> = foo1
res1 = foo1.isDistinct
// steals isDistinct from foo1's VmListing.cachedValues but must not
// perform a String|Int type check because isDistinct is not an element
res2 = foo2.isDistinct

View File

@@ -0,0 +1,8 @@
amends "../../input-helper/listings/cacheStealingTypeCheck.pkl"
// this test covers a regression where the wrong receiver
// and owner was used to typecheck a stolen value
foo {
"abcdx"
"ax"
}

View File

@@ -0,0 +1,5 @@
const local lastName = "Birdo"
typealias Birds = Listing<String(endsWith(lastName))>
typealias Birds2 = Pair<Listing<String(endsWith(lastName))>, Int>

View File

@@ -1,5 +1,7 @@
import "pkl:test"
import "helpers/originalTypealias.pkl"
typealias Simple = String
const function simple(arg: Simple): Simple = arg
@@ -105,3 +107,8 @@ res19: LocalTypeAlias = "abc"
typealias VeryComposite = Pair<Composite, Composite>
res20: VeryComposite = Pair(Map("abc", List("def")), Map("abc", List("def")))
// typealiases should be executed in their original context
res21: originalTypealias.Birds = new { "John Birdo" }
res22: originalTypealias.Birds2 = Pair(new Listing { "John Birdo" }, 0)

View File

@@ -0,0 +1,3 @@
import "helpers/originalTypealias.pkl"
res: originalTypealias.Birds = new { "Jimmy Bird" }

View File

@@ -4,6 +4,8 @@ facts {
true
true
true
true
true
}
["lastIndex"] {
true
@@ -11,6 +13,8 @@ facts {
true
true
true
true
true
}
["isDistinct"] {
true
@@ -18,6 +22,8 @@ facts {
true
true
true
true
true
}
["isDistinctBy()"] {
true
@@ -35,6 +41,12 @@ facts {
true
true
true
true
true
true
true
true
true
}
["getOrNull"] {
true
@@ -49,21 +61,29 @@ facts {
true
true
true
true
true
}
["firstOrNull"] {
true
true
true
true
true
}
["last"] {
true
true
true
true
true
}
["lastOrNull"] {
true
true
true
true
true
}
["single"] {
true
@@ -91,6 +111,7 @@ facts {
true
true
true
true
}
}
examples {
@@ -99,6 +120,9 @@ examples {
0
3
5
3
3
5
}
["toList()"] {
List()
@@ -134,6 +158,20 @@ examples {
}, new {
name = "Elf Owl"
})
List(new {
name = "Wood Pigeon"
}, new {
name = "Barn Owl"
}, new {
name = "Parrot"
})
List(new {
name = "Wood Pigeon"
}, new {
name = "Barn Owl"
}, new {
name = "Parrot"
})
}
["toSet()"] {
Set()
@@ -167,6 +205,20 @@ examples {
}, new {
name = "Elf Owl"
})
Set(new {
name = "Wood Pigeon"
}, new {
name = "Barn Owl"
}, new {
name = "Parrot"
})
Set(new {
name = "Wood Pigeon"
}, new {
name = "Barn Owl"
}, new {
name = "Parrot"
})
}
["distinct"] {
new {}
@@ -216,6 +268,28 @@ examples {
name = "Elf Owl"
}
}
new {
new {
name = "Wood Pigeon"
}
new {
name = "Barn Owl"
}
new {
name = "Parrot"
}
}
new {
new {
name = "Wood Pigeon"
}
new {
name = "Barn Owl"
}
new {
name = "Parrot"
}
}
}
["distinctBy()"] {
new {}
@@ -265,6 +339,28 @@ examples {
name = "Elf Owl"
}
}
new {
new {
name = "Wood Pigeon"
}
new {
name = "Barn Owl"
}
new {
name = "Parrot"
}
}
new {
new {
name = "Wood Pigeon"
}
new {
name = "Barn Owl"
}
new {
name = "Parrot"
}
}
new {}
new {}
new {
@@ -312,6 +408,28 @@ examples {
name = "Elf Owl"
}
}
new {
new {
name = "Wood Pigeon"
}
new {
name = "Barn Owl"
}
new {
name = "Parrot"
}
}
new {
new {
name = "Wood Pigeon"
}
new {
name = "Barn Owl"
}
new {
name = "Parrot"
}
}
new {}
new {}
new {
@@ -329,6 +447,16 @@ examples {
name = "Pigeon"
}
}
new {
new {
name = "Wood Pigeon"
}
}
new {
new {
name = "Wood Pigeon"
}
}
}
["fold"] {
List()
@@ -350,6 +478,20 @@ examples {
}, new {
name = "Elf Owl"
})
List(new {
name = "Wood Pigeon"
}, new {
name = "Barn Owl"
}, new {
name = "Parrot"
})
List(new {
name = "Wood Pigeon"
}, new {
name = "Barn Owl"
}, new {
name = "Parrot"
})
}
["foldIndexed"] {
List()
@@ -371,6 +513,20 @@ examples {
}), Pair(4, new {
name = "Elf Owl"
}))
List(Pair(0, new {
name = "Wood Pigeon"
}), Pair(1, new {
name = "Barn Owl"
}), Pair(2, new {
name = "Parrot"
}))
List(Pair(0, new {
name = "Wood Pigeon"
}), Pair(1, new {
name = "Barn Owl"
}), Pair(2, new {
name = "Parrot"
}))
}
["join"] {
""
@@ -386,5 +542,17 @@ examples {
Albatross
Elf Owl
"""
"Wood PigeonBarn OwlParrot"
"""
Wood Pigeon
Barn Owl
Parrot
"""
"Wood PigeonBarn OwlParrot"
"""
Wood Pigeon
Barn Owl
Parrot
"""
}
}

View File

@@ -0,0 +1,15 @@
Pkl Error
Expected value of type `String`, but got type `Int`.
Value: 0
x | local b = a as Listing<Listing<String>>
^^^^^^
at listingTypeCheckError8#b (file:///$snippetsDir/input/errors/listingTypeCheckError8.pkl)
x | local a = new Listing { new Listing { 0 } }
^
at listingTypeCheckError8#a[#1][#1] (file:///$snippetsDir/input/errors/listingTypeCheckError8.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -0,0 +1,15 @@
Pkl Error
Expected value of type `listingTypeCheckError9#Bar`, but got type `listingTypeCheckError9#Foo`.
Value: new Foo {}
x | local c = b as Listing<Bar>
^^^
at listingTypeCheckError9#c (file:///$snippetsDir/input/errors/listingTypeCheckError9.pkl)
x | local a = new Listing { new Foo {} }
^^^^^^^^^^
at listingTypeCheckError9#a[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError9.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -0,0 +1,15 @@
Pkl Error
Expected value of type `mappingTypeCheckError11#Bar`, but got type `mappingTypeCheckError11#Foo`.
Value: new Foo {}
x | local c = b as Mapping<Int, Bar>
^^^
at mappingTypeCheckError11#c (file:///$snippetsDir/input/errors/mappingTypeCheckError11.pkl)
x | local a = new Mapping { [0] = new Foo {} }
^^^^^^^^^^
at mappingTypeCheckError11#a[0] (file:///$snippetsDir/input/errors/mappingTypeCheckError11.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -0,0 +1,8 @@
foo1 {
"hello"
}
foo2 {
"hello"
}
res1 = true
res2 = true

View File

@@ -0,0 +1,4 @@
foo {
"abcdx"
"ax"
}

View File

@@ -38,3 +38,9 @@ res18 = "Expected value of type `Duration`, but got type `DataSize`. Value: 5.mb
res18b = "Expected value of type `Duration`, but got type `DataSize`. Value: 5.mb"
res19 = "abc"
res20 = Pair(Map("abc", List("def")), Map("abc", List("def")))
res21 {
"John Birdo"
}
res22 = Pair(new {
"John Birdo"
}, 0)

View File

@@ -0,0 +1,15 @@
Pkl Error
Type constraint `endsWith(lastName)` violated.
Value: "Jimmy Bird"
x | typealias Birds = Listing<String(endsWith(lastName))>
^^^^^^^^^^^^^^^^^^
at typeAliasContext#res (file:///$snippetsDir/input/types/helpers/originalTypealias.pkl)
x | res: originalTypealias.Birds = new { "Jimmy Bird" }
^^^^^^^^^^^^
at typeAliasContext#res[#1] (file:///$snippetsDir/input/types/typeAliasContext.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -117,7 +117,7 @@ class DocGenerator(
}
private fun DocPackage.deletePackageDir() {
outputDir.resolve("$name/$version").deleteRecursively()
outputDir.resolve(IoUtils.encodePath("$name/$version")).deleteRecursively()
}
private fun createSymlinks(currentPackagesData: List<PackageData>) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.pkl.gradle;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
@@ -27,6 +28,7 @@ import org.gradle.api.GradleException;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Transformer;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.plugins.Convention;
import org.gradle.api.provider.Provider;
@@ -64,6 +66,7 @@ import org.pkl.gradle.task.PkldocTask;
import org.pkl.gradle.task.ProjectPackageTask;
import org.pkl.gradle.task.ProjectResolveTask;
import org.pkl.gradle.task.TestTask;
import org.pkl.gradle.utils.PluginUtils;
@SuppressWarnings("unused")
public class PklPlugin implements Plugin<Project> {
@@ -456,6 +459,9 @@ public class PklPlugin implements Plugin<Project> {
private List<File> getTransitiveModules(AnalyzeImportsTask analyzeTask) {
var outputFile = analyzeTask.getOutputFile().get().getAsFile().toPath();
if (!analyzeTask.getOnlyIf().isSatisfiedBy(analyzeTask)) {
return Collections.emptyList();
}
try {
var contents = Files.readString(outputFile);
ImportGraph importGraph = ImportGraph.parseFromJson(contents);
@@ -470,9 +476,16 @@ public class PklPlugin implements Plugin<Project> {
}
private <T extends ModulesTask, S extends ModulesSpec> void configureModulesTask(
T task, S spec, @Nullable TaskProvider<AnalyzeImportsTask> analyzeImportsTask) {
T task,
S spec,
@Nullable TaskProvider<AnalyzeImportsTask> analyzeImportsTask,
@Nullable Transformer<List<?>, List<?>> mapSourceModules) {
configureBaseTask(task, spec);
task.getSourceModules().set(spec.getSourceModules());
if (mapSourceModules != null) {
task.getSourceModules().set(spec.getSourceModules().map(mapSourceModules));
} else {
task.getSourceModules().set(spec.getSourceModules());
}
task.getNoProject().set(spec.getNoProject());
task.getProjectDir().set(spec.getProjectDir());
task.getOmitProjectSettings().set(spec.getOmitProjectSettings());
@@ -484,6 +497,11 @@ public class PklPlugin implements Plugin<Project> {
}
}
private <T extends ModulesTask, S extends ModulesSpec> void configureModulesTask(
T task, S spec, @Nullable TaskProvider<AnalyzeImportsTask> analyzeImportsTask) {
configureModulesTask(task, spec, analyzeImportsTask, null);
}
private TaskProvider<AnalyzeImportsTask> createAnalyzeImportsTask(ModulesSpec spec) {
var outputFile =
project
@@ -496,11 +514,26 @@ public class PklPlugin implements Plugin<Project> {
spec.getName() + "GatherImports",
AnalyzeImportsTask.class,
task -> {
configureModulesTask(task, spec, null);
configureModulesTask(
task,
spec,
null,
(modules) ->
// only need to analyze imports of file-based modules; it's unlikely that a
// non-file-based module will import a file-based module due to security
// manager trust levels (see
// org.pkl.core.SecurityManagers.getDefaultTrustLevel).
modules.stream()
.map(PluginUtils::parseModuleNotationToUri)
.filter(
(it) ->
it.getScheme() == null || it.getScheme().equalsIgnoreCase("file"))
.toList());
task.setDescription("Compute the set of imports declared by input modules");
task.setGroup("build");
task.getOutputFormat().set(OutputFormat.JSON.toString());
task.getOutputFile().set(outputFile);
task.onlyIf(ignored -> !task.getSourceModules().get().isEmpty());
});
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,6 @@ package org.pkl.gradle.task;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
@@ -31,10 +28,8 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.gradle.api.DefaultTask;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileSystemLocation;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.MapProperty;
@@ -49,9 +44,9 @@ import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction;
import org.pkl.commons.cli.CliBaseOptions;
import org.pkl.core.evaluatorSettings.Color;
import org.pkl.core.util.IoUtils;
import org.pkl.core.util.LateInit;
import org.pkl.core.util.Nullable;
import org.pkl.gradle.utils.PluginUtils;
public abstract class BasePklTask extends DefaultTask {
@Input
@@ -74,7 +69,7 @@ public abstract class BasePklTask extends DefaultTask {
@Internal
public Provider<Object> getParsedSettingsModule() {
return getSettingsModule().map(this::parseModuleNotation);
return getSettingsModule().map(PluginUtils::parseModuleNotation);
}
@InputFile
@@ -165,7 +160,7 @@ public abstract class BasePklTask extends DefaultTask {
parseModulePath(),
getProject().getProjectDir().toPath(),
mapAndGetOrNull(getEvalRootDirPath(), Paths::get),
mapAndGetOrNull(getSettingsModule(), this::parseModuleNotationToUri),
mapAndGetOrNull(getSettingsModule(), PluginUtils::parseModuleNotationToUri),
null,
getEvalTimeout().getOrNull(),
mapAndGetOrNull(getModuleCacheDir(), it1 -> it1.getAsFile().toPath()),
@@ -199,100 +194,6 @@ public abstract class BasePklTask extends DefaultTask {
return getModulePath().getFiles().stream().map(File::toPath).collect(Collectors.toList());
}
/**
* Parses the specified source module notation into a "parsed" notation which is then used for
* input path tracking and as an argument for the CLI API.
*
* <p>This method accepts the following input types:
*
* <ul>
* <li>{@link URI} - used as is.
* <li>{@link File} - used as is.
* <li>{@link Path} - converted to a {@link File}. This conversion may fail because not all
* {@link Path}s point to the local file system.
* <li>{@link URL} - converted to a {@link URI}. This conversion may fail because {@link URL}
* allows for URLs which are not compliant URIs.
* <li>{@link CharSequence} - first, converted to a string. If this string is "URI-like" (see
* {@link IoUtils#isUriLike(String)}), then we attempt to parse it as a {@link URI}, which
* may fail. Otherwise, we attempt to parse it as a {@link Path}, which is then converted to
* a {@link File} (both of these operations may fail).
* <li>{@link FileSystemLocation} - converted to a {@link File} via the {@link
* FileSystemLocation#getAsFile()} method.
* </ul>
*
* In case the returned value is determined to be a {@link URI}, then this URI is first checked
* for whether its scheme is {@code file}, like {@code file:///example/path}. In such case, this
* method returns a {@link File} corresponding to the file path in the URI. Otherwise, a {@link
* URI} instance is returned.
*
* @throws InvalidUserDataException In case the input is none of the types described above, or
* when the underlying value cannot be parsed correctly.
*/
protected Object parseModuleNotation(Object notation) {
if (notation instanceof URI uri) {
if ("file".equals(uri.getScheme())) {
return new File(uri.getPath());
}
return uri;
} else if (notation instanceof File) {
return notation;
} else if (notation instanceof Path path) {
try {
return path.toFile();
} catch (UnsupportedOperationException e) {
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + notation, e);
}
} else if (notation instanceof URL url) {
try {
return parseModuleNotation(url.toURI());
} catch (URISyntaxException e) {
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + notation, e);
}
} else if (notation instanceof CharSequence) {
var s = notation.toString();
if (IoUtils.isUriLike(s)) {
try {
return parseModuleNotation(IoUtils.toUri(s));
} catch (URISyntaxException e) {
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + s, e);
}
} else {
try {
return Paths.get(s).toFile();
} catch (InvalidPathException | UnsupportedOperationException e) {
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + s, e);
}
}
} else if (notation instanceof FileSystemLocation location) {
return location.getAsFile();
} else {
throw new InvalidUserDataException(
"Unsupported value of type "
+ notation.getClass()
+ " used as a module path: "
+ notation);
}
}
protected URI parseModuleNotationToUri(Object m) {
var parsed1 = parseModuleNotation(m);
return parsedModuleNotationToUri(parsed1);
}
/**
* Converts either a file or a URI to a URI. We convert a file to a URI via the {@link
* IoUtils#createUri(String)} because other ways of conversion can make relative paths into
* absolute URIs, which may break module loading.
*/
private URI parsedModuleNotationToUri(Object notation) {
if (notation instanceof File file) {
return IoUtils.createUri(file.getPath());
} else if (notation instanceof URI uri) {
return uri;
}
throw new IllegalArgumentException("Invalid parsed module notation: " + notation);
}
protected List<Pattern> patternsFromStrings(List<String> patterns) {
return patterns.stream().map(Pattern::compile).collect(Collectors.toList());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,8 +37,8 @@ import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction;
import org.pkl.commons.cli.CliBaseOptions;
import org.pkl.core.evaluatorSettings.Color;
import org.pkl.core.util.IoUtils;
import org.pkl.core.util.Pair;
import org.pkl.gradle.utils.PluginUtils;
public abstract class ModulesTask extends BasePklTask {
// We expose the contents of this property as task inputs via the sourceModuleFiles
@@ -84,7 +84,7 @@ public abstract class ModulesTask extends BasePklTask {
@Override
protected List<URI> getSourceModulesAsUris() {
return getSourceModules().get().stream()
.map(this::parseModuleNotationToUri)
.map(PluginUtils::parseModuleNotationToUri)
.collect(Collectors.toList());
}
@@ -117,7 +117,7 @@ public abstract class ModulesTask extends BasePklTask {
var files = new ArrayList<File>();
var uris = new ArrayList<URI>();
for (var m : modules) {
var parsed = parseModuleNotation(m);
var parsed = PluginUtils.parseModuleNotation(m);
if (parsed instanceof File file) {
files.add(file);
} else if (parsed instanceof URI uri) {
@@ -127,28 +127,6 @@ public abstract class ModulesTask extends BasePklTask {
return Pair.of(files, uris);
}
/**
* Converts either a file or a URI to a URI. We convert a relative file to a URI via the {@link
* IoUtils#createUri(String)} because other ways of conversion can make relative paths into
* absolute URIs, which may break module loading.
*/
private URI parsedModuleNotationToUri(Object notation) {
if (notation instanceof File file) {
if (file.isAbsolute()) {
return file.toPath().toUri();
}
return IoUtils.createUri(IoUtils.toNormalizedPathString(file.toPath()));
} else if (notation instanceof URI uri) {
return uri;
}
throw new IllegalArgumentException("Invalid parsed module notation: " + notation);
}
protected URI parseModuleNotationToUri(Object m) {
var parsed1 = parseModuleNotation(m);
return parsedModuleNotationToUri(parsed1);
}
@TaskAction
@Override
public void runTask() {
@@ -172,7 +150,7 @@ public abstract class ModulesTask extends BasePklTask {
parseModulePath(),
getProject().getProjectDir().toPath(),
mapAndGetOrNull(getEvalRootDirPath(), Paths::get),
mapAndGetOrNull(getSettingsModule(), this::parseModuleNotationToUri),
mapAndGetOrNull(getSettingsModule(), PluginUtils::parseModuleNotationToUri),
getProjectDir().isPresent() ? getProjectDir().get().getAsFile().toPath() : null,
getEvalTimeout().getOrNull(),
mapAndGetOrNull(getModuleCacheDir(), it1 -> it1.getAsFile().toPath()),

View File

@@ -0,0 +1,128 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.gradle.utils;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.file.FileSystemLocation;
import org.pkl.core.util.IoUtils;
public class PluginUtils {
private PluginUtils() {}
/**
* Parses the specified source module notation into a "parsed" notation which is then used for
* input path tracking and as an argument for the CLI API.
*
* <p>This method accepts the following input types:
*
* <ul>
* <li>{@link URI} - used as is.
* <li>{@link File} - used as is.
* <li>{@link Path} - converted to a {@link File}. This conversion may fail because not all
* {@link Path}s point to the local file system.
* <li>{@link URL} - converted to a {@link URI}. This conversion may fail because {@link URL}
* allows for URLs which are not compliant URIs.
* <li>{@link CharSequence} - first, converted to a string. If this string is "URI-like" (see
* {@link IoUtils#isUriLike(String)}), then we attempt to parse it as a {@link URI}, which
* may fail. Otherwise, we attempt to parse it as a {@link Path}, which is then converted to
* a {@link File} (both of these operations may fail).
* <li>{@link FileSystemLocation} - converted to a {@link File} via the {@link
* FileSystemLocation#getAsFile()} method.
* </ul>
*
* In case the returned value is determined to be a {@link URI}, then this URI is first checked
* for whether its scheme is {@code file}, like {@code file:///example/path}. In such case, this
* method returns a {@link File} corresponding to the file path in the URI. Otherwise, a {@link
* URI} instance is returned.
*
* @throws InvalidUserDataException In case the input is none of the types described above, or
* when the underlying value cannot be parsed correctly.
*/
public static Object parseModuleNotation(Object notation) {
if (notation instanceof URI uri) {
if ("file".equals(uri.getScheme())) {
return new File(uri.getPath());
}
return uri;
} else if (notation instanceof File) {
return notation;
} else if (notation instanceof Path path) {
try {
return path.toFile();
} catch (UnsupportedOperationException e) {
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + notation, e);
}
} else if (notation instanceof URL url) {
try {
return parseModuleNotation(url.toURI());
} catch (URISyntaxException e) {
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + notation, e);
}
} else if (notation instanceof CharSequence) {
var s = notation.toString();
if (IoUtils.isUriLike(s)) {
try {
return parseModuleNotation(IoUtils.toUri(s));
} catch (URISyntaxException e) {
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + s, e);
}
} else {
try {
return Paths.get(s).toFile();
} catch (InvalidPathException | UnsupportedOperationException e) {
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + s, e);
}
}
} else if (notation instanceof FileSystemLocation location) {
return location.getAsFile();
} else {
throw new InvalidUserDataException(
"Unsupported value of type "
+ notation.getClass()
+ " used as a module path: "
+ notation);
}
}
/**
* Converts either a file or a URI to a URI. We convert a relative file to a URI via the {@link
* IoUtils#createUri(String)} because other ways of conversion can make relative paths into
* absolute URIs, which may break module loading.
*/
public static URI parsedModuleNotationToUri(Object notation) {
if (notation instanceof File file) {
if (file.isAbsolute()) {
return file.toPath().toUri();
}
return IoUtils.createUri(IoUtils.toNormalizedPathString(file.toPath()));
} else if (notation instanceof URI uri) {
return uri;
}
throw new IllegalArgumentException("Invalid parsed module notation: " + notation);
}
public static URI parseModuleNotationToUri(Object m) {
var parsed1 = PluginUtils.parseModuleNotation(m);
return parsedModuleNotationToUri(parsed1);
}
}

View File

@@ -0,0 +1,4 @@
@NonnullByDefault
package org.pkl.gradle.utils;
import org.pkl.core.util.NonnullByDefault;

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,17 @@
*/
package org.pkl.gradle
import java.nio.file.Path
import kotlin.io.path.readText
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.test.PackageServer
class PkldocGeneratorsTest : AbstractTest() {
@Test
fun `generate docs`() {
fun `generate docs`(@TempDir tempDir: Path) {
PackageServer.populateCacheDir(tempDir)
writeFile(
"build.gradle",
"""
@@ -32,7 +36,8 @@ class PkldocGeneratorsTest : AbstractTest() {
pkl {
pkldocGenerators {
pkldoc {
sourceModules = ["person.pkl", "doc-package-info.pkl"]
moduleCacheDir = file("${tempDir.toUri()}")
sourceModules = ["package://localhost:0/birds@0.5.0", "person.pkl", "doc-package-info.pkl"]
outputDir = file("build/pkldoc")
settingsModule = "pkl:settings"
}
@@ -94,6 +99,39 @@ class PkldocGeneratorsTest : AbstractTest() {
checkTextContains(moduleFile.readText(), "<html>", "Person", "Address", "other")
checkTextContains(personFile.readText(), "<html>", "name", "addresses")
checkTextContains(addressFile.readText(), "<html>", "street", "zip")
val birdsPackageFile = baseDir.resolve("localhost(3a)0/birds/0.5.0/index.html")
assertThat(birdsPackageFile).exists()
}
@Test
fun `generate docs only for package`(@TempDir tempDir: Path) {
PackageServer.populateCacheDir(tempDir)
writeFile(
"build.gradle",
"""
plugins {
id "org.pkl-lang"
}
pkl {
pkldocGenerators {
pkldoc {
moduleCacheDir = file("${tempDir.toUri()}")
sourceModules = ["package://localhost:0/birds@0.5.0"]
outputDir = file("build/pkldoc")
settingsModule = "pkl:settings"
}
}
}
"""
)
runTask("pkldoc")
val baseDir = testProjectDir.resolve("build/pkldoc")
val birdsPackageFile = baseDir.resolve("localhost(3a)0/birds/0.5.0/index.html")
assertThat(birdsPackageFile).exists()
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -130,5 +130,5 @@ class ServerMessagePackDecoder(unpacker: MessageUnpacker) : BaseMessagePackDecod
}
private fun unpackExternalReader(map: Map<Value, Value>): ExternalReader =
ExternalReader(unpackString(map, "executable"), unpackStringListOrNull(map, "arguments")!!)
ExternalReader(unpackString(map, "executable"), unpackStringListOrNull(map, "arguments"))
}

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@
///
/// Warning: Although this module is ready for initial use,
/// benchmark results may be inaccurate or inconsistent.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.Benchmark
import "pkl:platform" as _platform

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@
/// @Deprecated { message = "Use `com.example.Birds.Parrot` instead" }
/// amends "pkl:PackageInfo"
/// ```
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.DocPackageInfo
import "pkl:reflect"

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@
///
/// title = "Title displayed in the header of each page"
/// ```
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.DocsiteInfo
import "pkl:reflect"

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// Common settings for Pkl's own evaluator.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
@Since { version = "0.26.0" }
module pkl.EvaluatorSettings

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -64,7 +64,7 @@
/// value = project
/// }
/// ```
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.Project
import "pkl:EvaluatorSettings" as EvaluatorSettingsModule

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@
/// These tools differentiate from [pkl:reflect][reflect] in that they parse Pkl modules, but do not
/// execute any code within these modules.
@Since { version = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.analyze
// used by doc comments

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
/// Fundamental properties, methods, and classes for writing Pkl programs.
///
/// Members of this module are automatically available in every Pkl module.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.base
import "pkl:jsonnet"

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// A JSON parser.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.json
/// A JSON parser.

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// A [Jsonnet](https://jsonnet.org) renderer.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.jsonnet
/// Constructs an [ImportStr].

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
///
/// Note that some mathematical functions, such as `sign()`, `abs()`, and `round()`,
/// are directly defined in classes [Number], [Int], and [Float].
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.math
/// The minimum [Int] value: `-9223372036854775808`.

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// Information about the platform that the current program runs on.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.platform
/// The platform that the current program runs on.

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
/// A renderer for [Protocol Buffers](https://developers.google.com/protocol-buffers).
/// Note: This module is _experimental_ and not ready for production use.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.protobuf
import "pkl:reflect"

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
/// - Documentation generators (such as *Pkldoc*)
/// - Code generators (such as *pkl-codegen-java* and *pkl-codegen-kotlin*)
/// - Domain-specific schema validators
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.reflect
import "pkl:base"

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// Information about the Pkl release that the current program runs on.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.release
import "pkl:semver"

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// Parsing, comparison, and manipulation of [semantic version](https://semver.org/spec/v2.0.0.html) numbers.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.semver
/// Tells whether [version] is a valid semantic version number.

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@
/// Every settings file must amend this module.
/// Unless CLI commands and build tool plugins are explicitly configured with a settings file,
/// they will use `~/.pkl/settings.pkl` or the defaults specified in this module.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.settings
import "pkl:EvaluatorSettings"

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// Utilities for generating shell scripts.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.shell
/// Escapes [str] by enclosing it in single quotes.

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
///
/// To write tests, amend this module and define [facts] or [examples] (or both).
/// To run tests, evaluate the amended module.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
open module pkl.test
/// Named groups of boolean expressions that are expected to evaluate to [true].

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// An XML renderer.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.xml
/// Renders values as XML.

View File

@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===//
/// A YAML 1.2 compliant YAML parser.
@ModuleInfo { minPklVersion = "0.27.0" }
@ModuleInfo { minPklVersion = "0.27.2" }
module pkl.yaml
/// A YAML parser.