Compare commits

...

42 Commits

Author SHA1 Message Date
Daniel Chao 7a7f3abc50 Migrate Gradle deprecations (#1745) 2026-07-08 12:05:19 -07:00
Dan Chao 25d86db416 Update changelog with release notes link 2026-07-08 10:29:48 -07:00
Dan Chao 997cfc9ae2 Start next dev iteration 2026-07-08 10:29:48 -07:00
Dan Chao 9916c23cfc Prepare 0.32.0 release 2026-07-08 10:29:48 -07:00
Daniel Chao c3d570c769 Fix docsite (#1740) 2026-07-08 09:45:07 -07:00
Daniel Chao 7499a89d83 Add 0.32 release notes (#1699)
This adds release notes for the 0.32 release
2026-07-08 09:38:37 -07:00
Daniel Chao 010d971af0 Add tests for empty IntSeq iteration (#1739) 2026-07-08 06:37:56 -07:00
Daniel Chao bd83e29641 Normalize asset paths in local dependencies (#1737)
This changes local asset path resolution so that `..` segments at the
dependency root just resolves to the dependency root.

This makes import resolution work the same between local and remote dependencies.
2026-07-08 05:43:36 -07:00
Daniel Chao 2ec62c99a7 Enforce stricter rules on reference access (#1718)
This changes the following:

1. If given a union type, the member must exist on each member of that type
2. If accessing a member off `Reference<D, Null>`, give a `Reference<D, Null>`

Also:
* Improve error messages thrown during member access.
* Add test around accessing members off of a function type
2026-07-06 19:24:14 -07:00
Daniel Chao 3fc97d8dad Allow projectpackage: resource reads by default in Gradle plugin (#1735)
Fix an inconsistency: they are allowed as imports, but not reads.
2026-07-06 14:41:52 -07:00
Daniel Chao eaf73af247 Improve thread safety for pkl:base (#1719)
Stdlib modules are singletons that are shared across multiple evaluators.
This improves thread safety by evaluating its `output.bytes` during
initialization, which initializes truffle nodes (e.g. TypeTestNode),
and also initializes the member cache of the module output of `pkl:base`
2026-07-06 11:19:29 -07:00
Daniel Chao 2ec83198f5 Fix replacements of types nested within nested constraints (#1731)
Also, adjust tests
2026-07-06 08:58:56 -07:00
dependabot[bot] 26c115aea6 Bump gradle-wrapper from 9.5.1 to 9.6.0 (#1727)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.5.1 to 9.6.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.5.1...v9.6.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-02 22:19:24 +00:00
Jen Basch 6a4d3acf21 Fix incorrect isNoopTypeCheck() calls (#1717)
`isNoopTypeCheck()` is not reliable when inside a TypeNode's constructor. This defers these calls to node execution time.
2026-07-02 14:24:55 -07:00
dependabot[bot] 08b0975af9 Bump actions/setup-java from 5.3.0 to 5.4.0 (#1726)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/ad2b38190b15e4d6bdf0c97fb4fca8412226d287...1bcf9fb12cf4aa7d266a90ae39939e61372fe520)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 11:29:36 -07:00
dependabot[bot] 1ad63d32b3 Bump nu.validator:validator from 26.5.29 to 26.6.24 (#1725)
Bumps [nu.validator:validator](https://github.com/validator/validator) from 26.5.29 to 26.6.24.
- [Release notes](https://github.com/validator/validator/releases)
- [Commits](https://github.com/validator/validator/compare/26.5.29...26.6.24)

---
updated-dependencies:
- dependency-name: nu.validator:validator
  dependency-version: 26.6.24
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 11:29:14 -07:00
dependabot[bot] f44ff9904d Bump commonMark from 0.28.0 to 0.29.0 (#1724)
Bumps `commonMark` from 0.28.0 to 0.29.0.

Updates `org.commonmark:commonmark` from 0.28.0 to 0.29.0
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.28.0...commonmark-parent-0.29.0)

Updates `org.commonmark:commonmark-ext-gfm-tables` from 0.28.0 to 0.29.0
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.28.0...commonmark-parent-0.29.0)

---
updated-dependencies:
- dependency-name: org.commonmark:commonmark
  dependency-version: 0.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.commonmark:commonmark-ext-gfm-tables
  dependency-version: 0.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 11:28:45 -07:00
Daniel Chao 45912c504f Fix incorrect member links (#1723)
This fixes various incorrect member links in the stdlib.
2026-07-02 11:04:01 -07:00
adityabagchi24 1c1a39e37f Fix type argument lost in constraint expressions within generic typealiases (#1709)
Co-authored-by: Daniel Chao <daniel.h.chao@gmail.com>
2026-07-01 10:10:03 -07:00
Jen Basch bfa6a989be Generalize TypeNode validation (#1715) 2026-06-30 08:50:35 -07:00
Jen Basch 70fc1d4ba3 Correctly handle type aliases with a type variable as the root (#1714) 2026-06-30 06:49:21 +00:00
Daniel Chao 139d1ae8ec Fix many editor diagnostics (#1707)
This addresses many IDE warnings resulting from the switch to JSpecify.

Also, this changes the behavior of exporting VmObject; there's no place
in our code that does not force a VmObject prior to export, so
the existing logic around handling nullable values has been removed.
2026-06-29 16:22:19 +00:00
Daniel Chao 742a8e88da Fix for-generator variable resolution in nested eager scope (#1706)
Fixes an issue around resolving for-generator variables in some cases
2026-06-29 09:07:51 -07:00
Islon Scherer 6239981869 Improve error message for aliased references (#1695) 2026-06-29 18:06:34 +02:00
dependabot[bot] f470903389 Bump EnricoMi/publish-unit-test-result-action from 2.23.0 to 2.24.0 (#1700)
Bumps [EnricoMi/publish-unit-test-result-action](https://github.com/enricomi/publish-unit-test-result-action) from 2.23.0 to 2.24.0.
- [Release notes](https://github.com/enricomi/publish-unit-test-result-action/releases)
- [Commits](https://github.com/enricomi/publish-unit-test-result-action/compare/c950f6fb443cb5af20a377fd0dfaa78838901040...d0a4676d0e0b938bc201470d88276b7c74c712b3)

---
updated-dependencies:
- dependency-name: EnricoMi/publish-unit-test-result-action
  dependency-version: 2.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-26 09:51:24 -07:00
Daniel Chao 158f709ed4 Adjust doc comments on ref.pkl (#1693)
This adjusts the doc comments in ref.pkl

* Fix incorrect code snippets
* Clean up examples
* Remove some sections
* Make phrasing consistent with the rest of the stdlib
2026-06-25 21:31:54 +00:00
dependabot[bot] 601a8f424c Bump jline from 4.1.3 to 4.2.1 (#1702)
Bumps `jline` from 4.1.3 to 4.2.1.

Updates `org.jline:jline-reader` from 4.1.3 to 4.2.1
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.1.3...4.2.1)

Updates `org.jline:jline-terminal` from 4.1.3 to 4.2.1
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.1.3...4.2.1)

Updates `org.jline:jline-terminal-jni` from 4.1.3 to 4.2.1
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.1.3...4.2.1)

---
updated-dependencies:
- dependency-name: org.jline:jline-reader
  dependency-version: 4.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jline:jline-terminal
  dependency-version: 4.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jline:jline-terminal-jni
  dependency-version: 4.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 14:13:41 -07:00
dependabot[bot] 34c58efe4c Bump actions/setup-java from 5.2.0 to 5.3.0 (#1701)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/be666c2fcd27ec809703dec50e508c2fdc7f6654...ad2b38190b15e4d6bdf0c97fb4fca8412226d287)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 13:51:17 -07:00
dependabot[bot] 7903a072ea Bump com.diffplug.spotless:spotless-plugin-gradle from 8.6.0 to 8.7.0 (#1704)
Bumps [com.diffplug.spotless:spotless-plugin-gradle](https://github.com/diffplug/spotless) from 8.6.0 to 8.7.0.
- [Release notes](https://github.com/diffplug/spotless/releases)
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](https://github.com/diffplug/spotless/compare/gradle/8.6.0...gradle/8.7.0)

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-plugin-gradle
  dependency-version: 8.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 13:50:50 -07:00
dependabot[bot] bda36f0c32 Bump com.uber.nullaway:nullaway from 0.13.6 to 0.13.7 (#1703)
Bumps [com.uber.nullaway:nullaway](https://github.com/uber/NullAway) from 0.13.6 to 0.13.7.
- [Release notes](https://github.com/uber/NullAway/releases)
- [Changelog](https://github.com/uber/NullAway/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber/NullAway/compare/v0.13.6...v0.13.7)

---
updated-dependencies:
- dependency-name: com.uber.nullaway:nullaway
  dependency-version: 0.13.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 13:50:28 -07:00
Islon Scherer d42e199da3 Fix wrong error key in ReflectNodes (#1694) 2026-06-25 09:21:03 +02:00
Daniel Chao f61a5cf541 Check if receiver is a module object in relativePathTo (#1650)
Closes #1649
2026-06-24 11:48:57 +02:00
Islon Scherer dd6939ab3f Add equals and hashcode to PType (#1691) 2026-06-24 11:31:11 +02:00
Minh Vu f7cac257ad Fix Windows GraalVM installation (#1687)
## What changed

This updates the GraalVM install task so Windows installs use ZIP extraction instead of the Unix tar path. Windows now publishes the extracted directory by moving it into place, while macOS and Linux keep the existing tar extraction and symlink-based install flow.

## Why

BuildInfo.GraalVm downloads .zip archives on Windows, but InstallGraalVm always ran tar --strip-components=1 -xzf and then tried to install by creating a symbolic link. That combination is fragile on Windows: the archive format does not match the extraction command, and symlink creation can require special privileges.
2026-06-23 15:36:01 -07:00
Daniel Chao 1bf00b84ea Revert "Reject abstract members in non-abstract classes" (#1688)
The changes made in this commit are good, but we're going to kick this out
to the next release.
This is because:

1. There's a couple more issues around the `abstract` modifier that is not
implemented yet, and need design considerations
2. These are breaking changes, and we want to minimize the amount of breakages
for users.
3. The main branch is still the develop branch for Pkl 0.32

We will apply a re-revert of this commit after Pkl 0.32 is released.
2026-06-23 09:46:14 -07:00
Daniel Chao 15f089b275 Implement toString() on VmReference (#1692)
This is still needed right now, because some methods call through to
`toString()`.
2026-06-23 09:45:13 -07:00
Jen Basch 8a43e51e6b SPICE-0020: Deferred, type-safe references (#1354) 2026-06-23 15:26:06 +02:00
Vladimir Matveev b3015a09cc Catching exceptions during property evaluation (#1684)
The eval task, unlike other tasks, creates the evaluator when the property is evaluated, not just when the task is executed (in the `getEffective*` properties). The problem here is that property evaluation can and will happen before the task is executed, because Gradle needs property values for caching and sometimes task dependency information.

Therefore, if due to misconfiguration or due to intentional configuration the evaluator cannot be created - for example, when the `PklProject` module doesn't exist, but the project directory is configured - then the task will fail with an exception which looks _very_ similar to a regular execution exception, but which actually is not, because it is thrown not during task execution, but during preparation for the execution.

This distinction matters a lot when you rely on conditional task execution. If you use the `onlyIf` predicate on tasks to define a condition for the task execution, this predicate will be evaluated before the task actions are run, but *after* properties are evaluated. Thus, if property evaluation fails with an exception, it will *look* as if the `onlyIf` predicate is completely ignored and not even evaluated, and the task action is run regardless of the predicate.

This error mode is extremely confusing and is actually wrong: if I use `onlyIf` to gate the task execution, I don't want *any* of its logic to run. In fact, in my case I specifically use `onlyIf` on the evaluation task to only execute it if a prerequisite is met, and the same predicate guards a bunch of other tasks which prepare the Pkl project, so my Pkl project isn't even generated if the predicate fails. But due to this behavior of properties evaluation which are not controlled by `onlyIf`, the project is still attempted to be evaluated, and this results in a very unexpected build failure.

The solution is to ignore the evaluation exception, because for all intents and purposes, if the project can't be properly evaluated, it will fail during the task execution as it should, so the values of output properties don't really matter as the task will never be run.
2026-06-22 14:30:27 -07:00
dependabot[bot] ffc755f9c1 Bump errorProne from 2.49.0 to 2.50.0 (#1685)
Bumps `errorProne` from 2.49.0 to 2.50.0.

Updates `com.google.errorprone:error_prone_core` from 2.49.0 to 2.50.0
- [Release notes](https://github.com/google/error-prone/releases)
- [Commits](https://github.com/google/error-prone/compare/v2.49.0...v2.50.0)

Updates `com.google.errorprone:error_prone_annotations` from 2.49.0 to 2.50.0
- [Release notes](https://github.com/google/error-prone/releases)
- [Commits](https://github.com/google/error-prone/compare/v2.49.0...v2.50.0)

---
updated-dependencies:
- dependency-name: com.google.errorprone:error_prone_core
  dependency-version: 2.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: com.google.errorprone:error_prone_annotations
  dependency-version: 2.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 10:33:28 -07:00
dependabot[bot] 112a89468f Bump com.uber.nullaway:nullaway from 0.13.4 to 0.13.6 (#1686)
Bumps [com.uber.nullaway:nullaway](https://github.com/uber/NullAway) from 0.13.4 to 0.13.6.
- [Release notes](https://github.com/uber/NullAway/releases)
- [Changelog](https://github.com/uber/NullAway/blob/master/CHANGELOG.md)
- [Commits](uber/NullAway@v0.13.4...v0.13.6)

---
updated-dependencies:
- dependency-name: com.uber.nullaway:nullaway
  dependency-version: 0.13.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 10:31:12 -07:00
Islon Scherer bfac0d66ed Reject relative package URIs (#1683) 2026-06-17 15:39:03 +02:00
Daniel Chao fc8fe86e5a Remove incorrectly placed index methods on Collection (#1681)
These methods aren't implemented in `Set`, and don't really make sense because `Set` types can't be accessed by index.

Note: although this removes methods, this actually isn't a breaking change:
1. Calling `Set.findIndex()` currently throws an error around "cannot invoke abstract method"
2. `List` and `Set` are the only subclasses of `Collection`.

The following code isn't breaking at runtime, although static analysis tooling (like our IDE plugins) will now flag this as an error:

```pkl
myCollection: Collection

idx = myCollection.indexOf(1)
```
2026-06-15 08:56:55 -07:00
283 changed files with 5203 additions and 1179 deletions
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: nothing
steps:
- name: EnricoMi/publish-unit-test-result-action@v2
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
- name: actions/checkout@v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: actions/create-github-app-token@v2
@@ -25,7 +25,7 @@ jobs:
- name: actions/download-artifact@v6
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- name: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- name: actions/upload-artifact@v5
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- name: dawidd6/action-download-artifact@v11
+18 -18
View File
@@ -23,7 +23,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -64,7 +64,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -103,7 +103,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -130,7 +130,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -170,7 +170,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -220,7 +220,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -266,7 +266,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -317,7 +317,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -363,7 +363,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -411,7 +411,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -529,7 +529,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -576,7 +576,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -622,7 +622,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -673,7 +673,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -719,7 +719,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -767,7 +767,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -885,7 +885,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -949,7 +949,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+20 -20
View File
@@ -21,7 +21,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -62,7 +62,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -101,7 +101,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -128,7 +128,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -168,7 +168,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -218,7 +218,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -264,7 +264,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -315,7 +315,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -361,7 +361,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -409,7 +409,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -527,7 +527,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -574,7 +574,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -620,7 +620,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -671,7 +671,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -717,7 +717,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -765,7 +765,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -883,7 +883,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -946,7 +946,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -983,7 +983,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -1018,7 +1018,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+14 -14
View File
@@ -17,7 +17,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -60,7 +60,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -103,7 +103,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -151,7 +151,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -203,7 +203,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -251,7 +251,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -301,7 +301,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -421,7 +421,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -469,7 +469,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -517,7 +517,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -569,7 +569,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -617,7 +617,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -667,7 +667,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -787,7 +787,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
+18 -18
View File
@@ -21,7 +21,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -62,7 +62,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -101,7 +101,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -128,7 +128,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -168,7 +168,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -218,7 +218,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -264,7 +264,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -315,7 +315,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -361,7 +361,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -409,7 +409,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -527,7 +527,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -574,7 +574,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -620,7 +620,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -671,7 +671,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -717,7 +717,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -765,7 +765,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -883,7 +883,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -947,7 +947,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+19 -19
View File
@@ -21,7 +21,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -62,7 +62,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -101,7 +101,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -128,7 +128,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -168,7 +168,7 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -219,7 +219,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -266,7 +266,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -318,7 +318,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -365,7 +365,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -414,7 +414,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -533,7 +533,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -581,7 +581,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -628,7 +628,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -680,7 +680,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -727,7 +727,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -776,7 +776,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -895,7 +895,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -959,7 +959,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
@@ -1043,7 +1043,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
name_is_regexp: true
run_id: ${{ github.event.workflow_run.id }}
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with:
commit: ${{ github.event.workflow_run.head_sha }}
comment_mode: 'off'
+2 -2
View File
@@ -19,8 +19,8 @@ plugins {
id("me.champeau.jmh")
}
val truffle: Configuration by configurations.creating
val graal: Configuration by configurations.creating
val truffle: Configuration = configurations.create("truffle")
val graal: Configuration = configurations.create("graal")
dependencies {
jmh(projects.pklCore)
+78 -19
View File
@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.nio.file.AtomicMoveNotSupportedException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.util.*
import java.util.zip.ZipInputStream
import javax.inject.Inject
import kotlin.io.path.createDirectories
import org.gradle.api.DefaultTask
@@ -41,19 +44,14 @@ constructor(
@TaskAction
@Suppress("unused")
fun run() {
// minimize chance of corruption by extract-to-random-dir-and-flip-symlink
// minimize chance of corruption by extract-to-random-dir-and-publish
val distroDir = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
try {
distroDir.createDirectories()
println("Extracting ${graalVm.get().downloadFile} into $distroDir")
// faster and more reliable than Gradle's `copy { from tarTree() }`
execOperations.exec {
workingDir = distroDir.toFile()
executable = "tar"
args("--strip-components=1", "-xzf", graalVm.get().downloadFile)
}
val os = org.gradle.internal.os.OperatingSystem.current()
if (os.isWindows) extractZip(distroDir) else extractTarGz(distroDir)
val distroBinDir =
if (os.isMacOsX) distroDir.resolve("Contents/Home/bin") else distroDir.resolve("bin")
@@ -70,17 +68,7 @@ constructor(
}
}
println("Creating symlink ${graalVm.get().installDir} for $distroDir")
val tempLink = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
Files.createSymbolicLink(tempLink, distroDir)
try {
Files.move(tempLink, graalVm.get().installDir.toPath(), StandardCopyOption.ATOMIC_MOVE)
} catch (e: Exception) {
try {
fileOperations.delete(tempLink.toFile())
} catch (ignored: Exception) {}
throw e
}
publishInstallDir(distroDir, os)
} catch (e: Exception) {
try {
fileOperations.delete(distroDir)
@@ -88,4 +76,75 @@ constructor(
throw e
}
}
private fun extractTarGz(distroDir: Path) {
// faster and more reliable than Gradle's `copy { from tarTree() }`
execOperations.exec {
workingDir = distroDir.toFile()
executable = "tar"
args("--strip-components=1", "-xzf", graalVm.get().downloadFile)
}
}
private fun extractZip(distroDir: Path) {
val targetDir = distroDir.toAbsolutePath().normalize()
ZipInputStream(graalVm.get().downloadFile.inputStream().buffered()).use { zipInput ->
var entry = zipInput.nextEntry
while (entry != null) {
try {
val strippedPath = stripFirstPathComponent(entry.name)
if (strippedPath != null) {
val target = targetDir.resolve(strippedPath).normalize()
require(target.startsWith(targetDir)) {
"GraalVM archive entry escapes destination directory: ${entry.name}"
}
if (entry.isDirectory) {
target.createDirectories()
} else {
target.parent?.createDirectories()
Files.copy(zipInput, target)
}
}
} finally {
zipInput.closeEntry()
}
entry = zipInput.nextEntry
}
}
}
private fun stripFirstPathComponent(path: String): String? {
val normalizedPath = path.replace('\\', '/').trimStart('/')
val separatorIndex = normalizedPath.indexOf('/')
if (separatorIndex == -1) return null
return normalizedPath.substring(separatorIndex + 1).takeIf { it.isNotEmpty() }
}
private fun publishInstallDir(distroDir: Path, os: org.gradle.internal.os.OperatingSystem) {
if (os.isWindows) {
println("Installing ${graalVm.get().installDir} from $distroDir")
moveAtomicallyOrRegularly(distroDir, graalVm.get().installDir.toPath())
return
}
println("Creating symlink ${graalVm.get().installDir} for $distroDir")
val tempLink = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
Files.createSymbolicLink(tempLink, distroDir)
try {
moveAtomicallyOrRegularly(tempLink, graalVm.get().installDir.toPath())
} catch (e: Exception) {
try {
fileOperations.delete(tempLink.toFile())
} catch (ignored: Exception) {}
throw e
}
}
private fun moveAtomicallyOrRegularly(source: Path, target: Path) {
try {
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE)
} catch (e: AtomicMoveNotSupportedException) {
Files.move(source, target)
}
}
}
+5 -3
View File
@@ -63,11 +63,13 @@ fun Project.configurePklPomMetadata() {
/** Configures POM validation task to check for unresolved versions and snapshots in releases. */
fun Project.configurePomValidation() {
val validatePom by tasks.registering {
val validatePom =
tasks.register("validatePom") {
if (tasks.findByName("generatePomFileForLibraryPublication") == null) {
return@registering
return@register
}
val generatePomFileForLibraryPublication by tasks.existing(GenerateMavenPom::class)
val generatePomFileForLibraryPublication =
tasks.named<GenerateMavenPom>("generatePomFileForLibraryPublication")
val outputFile =
layout.buildDirectory.file("validatePom") // dummy output to satisfy up-to-date check
@@ -79,7 +79,7 @@ plugins.withType(MavenPublishPlugin::class).configureEach {
}
}
val allDependencies by tasks.registering(DependencyReportTask::class)
val allDependencies = tasks.register<DependencyReportTask>("allDependencies")
tasks.withType(Test::class).configureEach {
System.getProperty("testReportsDir")?.let { reportsDir ->
@@ -132,8 +132,8 @@ tasks.shadowJar {
shadow { addShadowVariantIntoJavaComponent = false }
val testFatJar by
tasks.registering(Test::class) {
val testFatJar =
tasks.register<Test>("testFatJar") {
testClassesDirs = files(tasks.test.get().testClassesDirs)
classpath =
// compiled test classes
@@ -148,7 +148,8 @@ val testFatJar by
tasks.check { dependsOn(testFatJar) }
val validateFatJar by tasks.registering {
val validateFatJar =
tasks.register("validateFatJar") {
val outputFile = layout.buildDirectory.file("validateFatJar/result.txt")
inputs.files(tasks.shadowJar)
inputs.property("nonRelocations", nonRelocations)
@@ -177,18 +178,18 @@ val validateFatJar by tasks.registering {
throw GradleException("Found unshadowed files:\n" + unshadowedFiles.joinToString("\n"))
}
}
}
}
tasks.check { dependsOn(validateFatJar) }
val resolveSourcesJars by
tasks.registering(ResolveSourcesJars::class) {
val resolveSourcesJars =
tasks.register<ResolveSourcesJars>("resolveSourcesJars") {
configuration.set(configurations.runtimeClasspath)
outputDir.set(layout.buildDirectory.dir("resolveSourcesJars"))
}
val fatSourcesJar by
tasks.registering(MergeSourcesJars::class) {
val fatSourcesJar =
tasks.register<MergeSourcesJars>("fatSourcesJar") {
plugins.withId("pklJavaLibrary") { inputJars.from(tasks.named("sourcesJar")) }
inputJars.from(firstPartySourcesJarsConfiguration)
inputJars.from(resolveSourcesJars.map { fileTree(it.outputDir) })
@@ -213,7 +214,7 @@ publishing {
artifact(fatSourcesJar.flatMap { it.outputJar.asFile }) { classifier = "sources" }
plugins.withId("pklJavaLibrary") {
val javadocJar by tasks.existing(Jar::class)
val javadocJar = tasks.named<Jar>("javadocJar")
// Javadoc Jar is not fat (didn't invest effort)
artifact(javadocJar.flatMap { it.archiveFile }) { classifier = "javadoc" }
}
@@ -21,11 +21,15 @@ plugins { id("de.undercouch.download") }
val buildInfo = project.extensions.getByType<BuildInfo>()
// tries to minimize chance of corruption by download-to-temp-file-and-move
val downloadGraalVmAarch64 by
tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAarch64) }
val downloadGraalVmAarch64 =
tasks.register<Download>("downloadGraalVmAarch64") {
configureDownloadGraalVm(buildInfo.graalVmAarch64)
}
val downloadGraalVmAmd64 by
tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAmd64) }
val downloadGraalVmAmd64 =
tasks.register<Download>("downloadGraalVmAmd64") {
configureDownloadGraalVm(buildInfo.graalVmAmd64)
}
fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) {
onlyIf { !graalvm.installDir.exists() }
@@ -37,14 +41,14 @@ fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) {
tempAndMove(true)
}
val verifyGraalVmAarch64 by
tasks.registering(Verify::class) {
val verifyGraalVmAarch64 =
tasks.register<Verify>("verifyGraalVmAarch64") {
configureVerifyGraalVm(buildInfo.graalVmAarch64)
dependsOn(downloadGraalVmAarch64)
}
val verifyGraalVmAmd64 by
tasks.registering(Verify::class) {
val verifyGraalVmAmd64 =
tasks.register<Verify>("verifyGraalVmAmd64") {
configureVerifyGraalVm(buildInfo.graalVmAmd64)
dependsOn(downloadGraalVmAmd64)
}
@@ -60,15 +64,15 @@ fun Verify.configureVerifyGraalVm(graalvm: BuildInfo.GraalVm) {
}
@Suppress("unused")
val installGraalVmAarch64 by
tasks.registering(InstallGraalVm::class) {
val installGraalVmAarch64 =
tasks.register<InstallGraalVm>("installGraalVmAarch64") {
dependsOn(verifyGraalVmAarch64)
graalVm = buildInfo.graalVmAarch64
}
@Suppress("unused")
val installGraalVmAmd64 by
tasks.registering(InstallGraalVm::class) {
val installGraalVmAmd64 =
tasks.register<InstallGraalVm>("installGraalVmAmd64") {
dependsOn(verifyGraalVmAmd64)
graalVm = buildInfo.graalVmAmd64
}
@@ -47,8 +47,8 @@ dependencies {
}
}
val validateHtml by
tasks.registering(JavaExec::class) {
val validateHtml =
tasks.register<JavaExec>("validateHtml") {
val resultFile = layout.buildDirectory.file("validateHtml/result.txt")
inputs.files(htmlValidator.sources)
outputs.file(resultFile)
@@ -26,8 +26,8 @@ plugins {
val executableSpec = project.extensions.create("executable", ExecutableSpec::class.java)
val buildInfo = project.extensions.getByType<BuildInfo>()
val javaExecutable by
tasks.registering(ExecutableJar::class) {
val javaExecutable =
tasks.register<ExecutableJar>("javaExecutable") {
group = "build"
dependsOn(tasks.jar)
inJar = tasks.shadowJar.flatMap { it.archiveFile }
@@ -77,7 +77,8 @@ fun Task.setupTestStartJavaExecutable(launcher: Provider<JavaLauncher>? = null)
}
}
val testStartJavaExecutable by tasks.registering { setupTestStartJavaExecutable() }
val testStartJavaExecutable =
tasks.register("testStartJavaExecutable") { setupTestStartJavaExecutable() }
// Setup `testStartJavaExecutable` tasks for multi-JDK testing.
val testStartJavaExecutableOnOtherJdks =
@@ -30,14 +30,18 @@ plugins {
val executableSpec = project.extensions.getByType<ExecutableSpec>()
val buildInfo = project.extensions.getByType<BuildInfo>()
val stagedMacAmd64Executable: Configuration by configurations.creating
val stagedMacAarch64Executable: Configuration by configurations.creating
val stagedLinuxAmd64Executable: Configuration by configurations.creating
val stagedLinuxAarch64Executable: Configuration by configurations.creating
val stagedAlpineLinuxAmd64Executable: Configuration by configurations.creating
val stagedWindowsAmd64Executable: Configuration by configurations.creating
val stagedMacAmd64Executable: Configuration = configurations.create("stagedMacAmd64Executable")
val stagedMacAarch64Executable: Configuration = configurations.create("stagedMacAarch64Executable")
val stagedLinuxAmd64Executable: Configuration = configurations.create("stagedLinuxAmd64Executable")
val stagedLinuxAarch64Executable: Configuration =
configurations.create("stagedLinuxAarch64Executable")
val stagedAlpineLinuxAmd64Executable: Configuration =
configurations.create("stagedAlpineLinuxAmd64Executable")
val stagedWindowsAmd64Executable: Configuration =
configurations.create("stagedWindowsAmd64Executable")
val nativeImageClasspath by configurations.creating {
val nativeImageClasspath =
configurations.create("nativeImageClasspath") {
extendsFrom(configurations.runtimeClasspath.get())
// Ensure native-image version uses GraalVM C SDKs instead of Java FFI or JNA
// (comes from artifact `mordant-jvm-graal-ffi`).
@@ -45,7 +49,7 @@ val nativeImageClasspath by configurations.creating {
exclude("com.github.ajalt.mordant", "mordant-jvm-ffm-jvm")
exclude("com.github.ajalt.mordant", "mordant-jvm-jna")
exclude("com.github.ajalt.mordant", "mordant-jvm-jna-jvm")
}
}
val libs = the<LibrariesForLibs>()
@@ -85,32 +89,32 @@ private fun NativeImageBuild.setClasspath() {
classpath.from(nativeImageClasspath)
}
val macExecutableAmd64 by
tasks.registering(NativeImageBuild::class) {
val macExecutableAmd64 =
tasks.register<NativeImageBuild>("macExecutableAmd64") {
imageName = executableSpec.name.map { "$it-macos-amd64" }
mainClass = executableSpec.mainClass
amd64()
setClasspath()
}
val macExecutableAarch64 by
tasks.registering(NativeImageBuild::class) {
val macExecutableAarch64 =
tasks.register<NativeImageBuild>("macExecutableAarch64") {
imageName = executableSpec.name.map { "$it-macos-aarch64" }
mainClass = executableSpec.mainClass
aarch64()
setClasspath()
}
val linuxExecutableAmd64 by
tasks.registering(NativeImageBuild::class) {
val linuxExecutableAmd64 =
tasks.register<NativeImageBuild>("linuxExecutableAmd64") {
imageName = executableSpec.name.map { "$it-linux-amd64" }
mainClass = executableSpec.mainClass
amd64()
setClasspath()
}
val linuxExecutableAarch64 by
tasks.registering(NativeImageBuild::class) {
val linuxExecutableAarch64 =
tasks.register<NativeImageBuild>("linuxExecutableAarch64") {
imageName = executableSpec.name.map { "$it-linux-aarch64" }
mainClass = executableSpec.mainClass
aarch64()
@@ -120,8 +124,8 @@ val linuxExecutableAarch64 by
extraNativeImageArgs.add("-H:PageSize=65536")
}
val alpineExecutableAmd64 by
tasks.registering(NativeImageBuild::class) {
val alpineExecutableAmd64 =
tasks.register<NativeImageBuild>("alpineExecutableAmd64") {
imageName = executableSpec.name.map { "$it-alpine-linux-amd64" }
mainClass = executableSpec.mainClass
amd64()
@@ -129,17 +133,18 @@ val alpineExecutableAmd64 by
extraNativeImageArgs.addAll(listOf("--static", "--libc=musl"))
}
val windowsExecutableAmd64 by
tasks.registering(NativeImageBuild::class) {
val windowsExecutableAmd64 =
tasks.register<NativeImageBuild>("windowsExecutableAmd64") {
imageName = executableSpec.name.map { "$it-windows-amd64" }
mainClass = executableSpec.mainClass
amd64()
setClasspath()
}
val assembleNative by tasks.existing
val assembleNative = tasks.named("assembleNative")
val testStartNativeExecutable by tasks.registering {
val testStartNativeExecutable =
tasks.register("testStartNativeExecutable") {
dependsOn(assembleNative)
// dummy file for up-to-date checking
@@ -164,11 +169,12 @@ val testStartNativeExecutable by tasks.registering {
writeText("OK")
}
}
}
}
val requiredGlibcVersion: Version = Version.parse("2.17")
val checkGlibc by tasks.registering {
val checkGlibc =
tasks.register("checkGlibc") {
enabled = buildInfo.os.isLinux && !buildInfo.musl
dependsOn(assembleNative)
doLast {
@@ -195,7 +201,7 @@ val checkGlibc by tasks.registering {
)
}
}
}
}
// Expose underlying task's outputs
private fun <T : Task> Task.wraps(other: TaskProvider<T>) {
@@ -203,19 +209,24 @@ private fun <T : Task> Task.wraps(other: TaskProvider<T>) {
outputs.files(other)
}
val testNative by tasks.existing { dependsOn(testStartNativeExecutable, checkGlibc) }
val testNative = tasks.named("testNative") { dependsOn(testStartNativeExecutable, checkGlibc) }
val assembleNativeMacOsAarch64 by tasks.existing { wraps(macExecutableAarch64) }
val assembleNativeMacOsAarch64 =
tasks.named("assembleNativeMacOsAarch64") { wraps(macExecutableAarch64) }
val assembleNativeMacOsAmd64 by tasks.existing { wraps(macExecutableAmd64) }
val assembleNativeMacOsAmd64 = tasks.named("assembleNativeMacOsAmd64") { wraps(macExecutableAmd64) }
val assembleNativeLinuxAarch64 by tasks.existing { wraps(linuxExecutableAarch64) }
val assembleNativeLinuxAarch64 =
tasks.named("assembleNativeLinuxAarch64") { wraps(linuxExecutableAarch64) }
val assembleNativeLinuxAmd64 by tasks.existing { wraps(linuxExecutableAmd64) }
val assembleNativeLinuxAmd64 =
tasks.named("assembleNativeLinuxAmd64") { wraps(linuxExecutableAmd64) }
val assembleNativeAlpineLinuxAmd64 by tasks.existing { wraps(alpineExecutableAmd64) }
val assembleNativeAlpineLinuxAmd64 =
tasks.named("assembleNativeAlpineLinuxAmd64") { wraps(alpineExecutableAmd64) }
val assembleNativeWindowsAmd64 by tasks.existing { wraps(windowsExecutableAmd64) }
val assembleNativeWindowsAmd64 =
tasks.named("assembleNativeWindowsAmd64") { wraps(windowsExecutableAmd64) }
publishing {
publications {
@@ -13,29 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
val assembleNativeMacOsAarch64 by tasks.registering { group = "build" }
val assembleNativeMacOsAarch64 = tasks.register("assembleNativeMacOsAarch64") { group = "build" }
val assembleNativeMacOsAmd64 by tasks.registering { group = "build" }
val assembleNativeMacOsAmd64 = tasks.register("assembleNativeMacOsAmd64") { group = "build" }
val assembleNativeLinuxAarch64 by tasks.registering { group = "build" }
val assembleNativeLinuxAarch64 = tasks.register("assembleNativeLinuxAarch64") { group = "build" }
val assembleNativeLinuxAmd64 by tasks.registering { group = "build" }
val assembleNativeLinuxAmd64 = tasks.register("assembleNativeLinuxAmd64") { group = "build" }
val assembleNativeAlpineLinuxAmd64 by tasks.registering { group = "build" }
val assembleNativeAlpineLinuxAmd64 =
tasks.register("assembleNativeAlpineLinuxAmd64") { group = "build" }
val assembleNativeWindowsAmd64 by tasks.registering { group = "build" }
val assembleNativeWindowsAmd64 = tasks.register("assembleNativeWindowsAmd64") { group = "build" }
val testNativeMacOsAarch64 by tasks.registering { group = "verification" }
val testNativeMacOsAarch64 = tasks.register("testNativeMacOsAarch64") { group = "verification" }
val testNativeMacOsAmd64 by tasks.registering { group = "verification" }
val testNativeMacOsAmd64 = tasks.register("testNativeMacOsAmd64") { group = "verification" }
val testNativeLinuxAarch64 by tasks.registering { group = "verification" }
val testNativeLinuxAarch64 = tasks.register("testNativeLinuxAarch64") { group = "verification" }
val testNativeLinuxAmd64 by tasks.registering { group = "verification" }
val testNativeLinuxAmd64 = tasks.register("testNativeLinuxAmd64") { group = "verification" }
val testNativeAlpineLinuxAmd64 by tasks.registering { group = "verification" }
val testNativeAlpineLinuxAmd64 =
tasks.register("testNativeAlpineLinuxAmd64") { group = "verification" }
val testNativeWindowsAmd64 by tasks.registering { group = "verification" }
val testNativeWindowsAmd64 = tasks.register("testNativeWindowsAmd64") { group = "verification" }
val buildInfo = project.extensions.getByType<BuildInfo>()
@@ -44,7 +46,8 @@ private fun <T : Task> Task.wraps(other: TaskProvider<T>) {
outputs.files(other)
}
val assembleNative by tasks.registering {
val assembleNative =
tasks.register("assembleNative") {
group = "build"
if (!buildInfo.isCrossArchSupported && buildInfo.isCrossArch) {
@@ -62,7 +65,8 @@ val assembleNative by tasks.registering {
wraps(assembleNativeLinuxAarch64)
}
buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> {
if (buildInfo.musl) wraps(assembleNativeAlpineLinuxAmd64) else wraps(assembleNativeLinuxAmd64)
if (buildInfo.musl) wraps(assembleNativeAlpineLinuxAmd64)
else wraps(assembleNativeLinuxAmd64)
}
buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> {
wraps(assembleNativeWindowsAmd64)
@@ -75,9 +79,10 @@ val assembleNative by tasks.registering {
}
}
}
}
}
val testNative by tasks.registering {
val testNative =
tasks.register("testNative") {
group = "verification"
dependsOn(assembleNative)
@@ -96,7 +101,8 @@ val testNative by tasks.registering {
dependsOn(testNativeLinuxAarch64)
}
buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> {
if (buildInfo.musl) dependsOn(testNativeAlpineLinuxAmd64) else dependsOn(testNativeLinuxAmd64)
if (buildInfo.musl) dependsOn(testNativeAlpineLinuxAmd64)
else dependsOn(testNativeLinuxAmd64)
}
buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> {
dependsOn(testNativeWindowsAmd64)
@@ -109,14 +115,16 @@ val testNative by tasks.registering {
}
}
}
}
}
val checkNative by tasks.registering {
val checkNative =
tasks.register("checkNative") {
group = "verification"
dependsOn(testNative)
}
}
val buildNative by tasks.registering {
val buildNative =
tasks.register("buildNative") {
group = "build"
dependsOn(checkNative)
}
}
@@ -15,7 +15,7 @@
*/
plugins { id("com.diffplug.spotless") }
val pklFormatter by configurations.creating
val pklFormatter = configurations.create("pklFormatter")
dependencies { pklFormatter(rootProject.project("pkl-formatter")) }
+4 -3
View File
@@ -37,7 +37,8 @@ nexusPublishing {
}
}
val configureLateInitAnnotation by tasks.registering(ConfigureLateInitAnnotation::class)
val configureLateInitAnnotation =
tasks.register<ConfigureLateInitAnnotation>("configureLateInitAnnotation")
idea {
project {
@@ -53,9 +54,9 @@ idea {
}
}
val clean by tasks.existing { delete(layout.buildDirectory) }
val clean = tasks.named("clean") { delete(layout.buildDirectory) }
val printVersion by tasks.registering { doFirst { println(buildInfo.pklVersion) } }
val printVersion = tasks.register("printVersion") { doFirst { println(buildInfo.pklVersion) } }
val message =
"""
+1 -1
View File
@@ -1,6 +1,6 @@
name: main
title: Main Project
version: 0.32.0-dev
version: 0.33.0-dev
prerelease: true
nav:
- nav.adoc
@@ -3,7 +3,7 @@
// 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.32.0
:pkl-version-no-suffix: 0.33.0
// 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:
@@ -69,6 +69,7 @@ endif::[]
:uri-stdlib-baseModule: {uri-pkl-stdlib-docs}/base
:uri-stdlib-CommandModule: {uri-pkl-stdlib-docs}/Command
:uri-stdlib-refModule: {uri-pkl-stdlib-docs}/ref
:uri-stdlib-analyzeModule: {uri-pkl-stdlib-docs}/analyze
:uri-stdlib-jsonnetModule: {uri-pkl-stdlib-docs}/jsonnet
:uri-stdlib-reflectModule: {uri-pkl-stdlib-docs}/reflect
@@ -160,6 +161,8 @@ endif::[]
:uri-stdlib-Command-CountedFlag: {uri-stdlib-CommandModule}/CountedFlag
:uri-stdlib-Command-Argument: {uri-stdlib-CommandModule}/Argument
:uri-stdlib-Command-Import: {uri-stdlib-CommandModule}/Import
:uri-stdlib-ref-Reference: {uri-stdlib-baseModule}/Reference
:uri-stdlib-ref-Access: {uri-stdlib-baseModule}/Access
:uri-messagepack: https://msgpack.org/index.html
:uri-messagepack-spec: https://github.com/msgpack/msgpack/blob/master/spec.md
@@ -187,6 +187,15 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
|
|
|link:{uri-stdlib-ref-Reference}[Reference]
|`0x20`
|`<value>` (Typed)
|Domain
|`<value>`
|Data
|link:{uri-messagepack-array}[array]
|Array of link:{uri-stdlib-ref-Access}[`pkl.ref#Access`] values
|===
[[type-name-encoding]]
+4 -4
View File
@@ -56,7 +56,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven(url = "{uri-sonatype}")
maven(url = "{uri-snapshot-repo}")
endif::[]
}
----
@@ -75,7 +75,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven { url "{uri-sonatype}" }
maven { url "{uri-snapshot-repo}" }
endif::[]
}
----
@@ -99,9 +99,9 @@ ifndef::is-release-version[]
<repository>
<id>sonatype-s01</id>
<name>Sonatype S01</name>
<url>{uri-sonatype}</url>
<url>{uri-snapshot-repo}</url>
</repository>
</repositories>
</repositories>
endif::[]
</project>
----
@@ -45,7 +45,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven(url = "{uri-sonatype}")
maven(url = "{uri-snapshot-repo}")
endif::[]
}
----
@@ -66,7 +66,7 @@ repositories {
endif::[]
ifndef::is-release-version[]
repositories {
maven { url "{uri-sonatype}" }
maven { url "{uri-snapshot-repo}" }
}
endif::[]
----
@@ -92,7 +92,7 @@ ifndef::is-release-version[]
<repository>
<id>sonatype-s01</id>
<name>Sonatype S01</name>
<url>{uri-sonatype}</url>
<url>{uri-snapshot-repo}</url>
</repository>
</repositories>
endif::[]
@@ -46,7 +46,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven(url = "{uri-sonatype}")
maven(url = "{uri-snapshot-repo}")
endif::[]
}
----
@@ -65,7 +65,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven { url "{uri-sonatype}" }
maven { url "{uri-snapshot-repo}" }
endif::[]
}
----
@@ -34,7 +34,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven(url = "{uri-sonatype}")
maven(url = "{uri-snapshot-repo}")
endif::[]
}
----
@@ -53,7 +53,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven { url "{uri-sonatype}" }
maven { url "{uri-snapshot-repo}" }
endif::[]
}
----
@@ -77,7 +77,7 @@ ifndef::is-release-version[]
<repository>
<id>sonatype-s01</id>
<name>Sonatype S01</name>
<url>{uri-sonatype}</url>
<url>{uri-snapshot-repo}</url>
</repository>
</repositories>
endif::[]
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -44,7 +44,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven(url = "{uri-sonatype}")
maven(url = "{uri-snapshot-repo}")
endif::[]
}
----
@@ -63,7 +63,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven { url "{uri-sonatype}" }
maven { url "{uri-snapshot-repo}" }
endif::[]
}
----
@@ -87,7 +87,7 @@ ifndef::is-release-version[]
<repository>
<id>sonatype-s01</id>
<name>Sonatype S01</name>
<url>{uri-sonatype}</url>
<url>{uri-snapshot-repo}</url>
</repository>
</repositories>
endif::[]
+3 -3
View File
@@ -117,7 +117,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven(url = "{uri-sonatype}")
maven(url = "{uri-snapshot-repo}")
endif::[]
}
----
@@ -136,7 +136,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven { url "{uri-sonatype}" }
maven { url "{uri-snapshot-repo}" }
endif::[]
}
----
@@ -160,7 +160,7 @@ ifndef::is-release-version[]
<repository>
<id>sonatype-s01</id>
<name>Sonatype S01</name>
<url>{uri-sonatype}</url>
<url>{uri-snapshot-repo}</url>
</repository>
</repositories>
endif::[]
+2 -2
View File
@@ -51,7 +51,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven(url = "{uri-sonatype}")
maven(url = "{uri-snapshot-repo}")
endif::[]
}
}
@@ -76,7 +76,7 @@ ifdef::is-release-version[]
mavenCentral()
endif::[]
ifndef::is-release-version[]
maven { url "{uri-sonatype}" }
maven { url "{uri-snapshot-repo}" }
endif::[]
}
}
+510 -32
View File
@@ -1,67 +1,495 @@
= Pkl 0.32.0 Release Notes
= Pkl 0.32 Release Notes
:version: 0.32
:version-minor: 0.32.0
:release-date: TBD
:release-date: July 8th, 2026
:version-next: 0.33
:version-next-date: TBD
:version-next-date: October 2026
include::partial$intro.adoc[]
NOTE: This is the final release that distributes binaries for macOS/amd64.
== Highlights [small]#💖#
News you don't want to miss.
=== New data type: `Reference`
.XXX
[%collapsible]
This release introduces a new data type called `Reference` (https://github.com/apple/pkl/pull/1354[#1354], https://github.com/apple/pkl/pull/1691[#1691], https://github.com/apple/pkl/pull/1692[#1692], https://github.com/apple/pkl/pull/1693[#1693], https://github.com/apple/pkl/pull/1695[#1695]).
WARNING: This API is experimental. We are looking for feedback, and will aim to make this ready for production use in the next release.
A reference represents something whose actual value is unknown to Pkl, but nevertheless its type is checked.
The purpose of this data type is to improve how Pkl can be used to configure systems like GitHub Actions, Pulumi, Terraform, and more.
For example, let's assume that some system accepts YAML configuration that looks like so:
[source,yaml]
----
taskA:
uses: some-task
taskB:
uses: some-other-task
input: '${ taskA.output.number }'
----
In terms of YAML, `'${ taskA.output.number }'` is just a string.
However, semantically, it is an _expression_ in this target system, where `taskA.output.number` conveys some type.
Today, the only way to treat these values in Pkl is to also describe these as strings.
This introduces many shortcomings:
1. It leaves users vulnerable to syntax errors in this system.
2. It's hard to validate these beyond that they are a string.
3. Pkl has no understanding of the relationship between `taskA` and `taskB`.
With references, the above can be modeled in Pkl also as plain lookups:
[source,pkl]
----
taskA {
uses = "some-task"
}
taskB {
name = "some-other-task"
input = taskA.output.number
}
----
Library authors describe types as `ref.Reference<D, T>`, where `D` is the _domain_, and `T` is the _referent type_.
[source,pkl]
----
import "pkl:ref"
class MyDomain extends ref.Domain { // <1>
function renderReference(ref: ref.Reference<MyDomain, Any>): String =
"${ " + ref.getData() + "." + ref.getPath().join(".") + " }"
}
typealias MyReference<T> = ref.Reference<MyDomain, T> // <2>
class MyTask {
input: MyReference<Number>
}
----
<1> References exist inside a domain. The domain defines how these references should be stringified.
<2> A typealias can be used to improve ergonomics when declaring references that share the same domain.
Pkl will check that type arguments match up:
* The referent type must line up.
+
`MyReference<Int>` is not assignable to `MyReference<String>`.
* The domain must line up.
+
`ref.Reference<MyDomain, Int>` is not assignable to `ref.Reference<OtherDomain, Int>`.
Additionally, references give you synthetic members.
[source,pkl]
----
class Bird {
name: String
}
bird: MyReference<Bird>
birdName = bird.name // <1>
----
<1> Gives a `MyReference<String>`, because `class Bird` has property `name: String`
The synthetic member contains metadata about the path that was used to access it (`.name` in this
case).
This can be used to stringify this reference in that target domain.
To read more about references, consult the xref:language-reference:index.adoc#references[language reference].
To read through the design decisions made, consult the https://github.com/apple/pkl-evolution/blob/main/spices/SPICE-0020-type-safe-deferred-references.adoc[SPICE].
=== Custom HTTP Headers
Pkl can now attach custom HTTP headers to outbound requests, making it possible to access private or authenticated resources (https://github.com/apple/pkl/pull/1196[#1196], https://github.com/apple/pkl/pull/1584[#1584]).
For example, a CLI user can add an `Authorization` header either in settings.pkl, or the PklProject file:
[tabs]
====
XXX
~/.pkl/settings.pkl::
+
[source,pkl]
----
amends "pkl:settings"
http {
headers {
["https://my.private.server/**"] {
["Authorization"] = "Bearer my-secret-token"
}
}
}
----
PklProject::
+
[source,pkl]
----
amends "pkl:Project"
evaluatorSettings {
http {
headers {
["https://my.private.server/**"] {
["Authorization"] = "Bearer my-secret-token"
}
}
}
}
----
====
Each key in `headers` is a glob pattern matched against the request URL.
When a request is made, every matching pattern's headers are added to the request.
Headers can also be configured in other ways:
* In the pkl-gradle plugin
* As a CLI flag
* In the Java/Swift/Go/Kotlin evaluator APIs
* In the pkl-executor API
Header names must conform to RFC 7230 token syntax.
Certain reserved names (e.g. `host`, `connection`, `content-length`) and prefixes (`proxy-`, `sec-`) are forbidden.
Thank you to https://github.com/kyokuping[@kyokuping] for their contributions to this feature!
To learn more, consult https://github.com/apple/pkl-evolution/blob/main/spices/SPICE-0022-http-headers.adoc[SPICE-0022].
== Noteworthy [small]#🎶#
Ready when you need them.
=== Resolved evaluator settings
PklProject files now resolve evaluator settings that are file paths (https://github.com/apple/pkl/pull/1394[#1394]).
Some of the settings within `pkl:EvaluatorSettings` represent file paths.
However, these paths are resolved inconsistently:
* Pkl is inconsistent about what relative paths mean. `rootDir`, `moduleCacheDir`, and `modulePath` are relative to the project dir, while `ExternalReader.executable` is relative to the PWD.
* External readers defined in a PklProject have different behavior depending on the PWD.
* The logic for resolving these paths is dependent on the caller, rather than Pkl.
For CLI users, this means that the current working directory can affect how evaluator settings are loaded.
In Pkl 0.32, these paths are resolved entirely within Pkl, and resolved against the enclosing directory.
A new method `resolve()` is added to `pkl:EvaluatorSettings`, and a new property `resolvedEvaluatorSettings` is added to `pkl:Project`.
Language bindings are expected to use `resolvedEvaluatorSettings` when configuring the evaluator.
To read more about this change, consult https://github.com/apple/pkl-evolution/blob/main/spices/SPICE-0027-resolved-paths-in-pkl-evaluatorsettings.adoc[SPICE-0027].
=== Line continuations in multiline strings
When authoring multiline strings, it is now possible to use a _line continuation_ to break a single line's value over multiple lines (https://github.com/apple/pkl/pull/1507[#1507], https://github.com/apple/pkl/pull/1564[#1564]).
These two string snippets are logically identical:
[source,pkl]
----
str =
"""
Although the Dodo is extinct, \
the species will be remembered.
"""
----
[source%parsed,pkl]
----
str = "Although the Dodo is extinct, the species will be remembered."
----
To read more about this design, consult https://github.com/apple/pkl-evolution/blob/main/spices/SPICE-0028-multiline-string-continuation.adoc[SPICE-0028].
[[parse-time-variable-resolution]]
=== Parse-time variable resolution
Pkl now resolves variables at parse time (https://github.com/apple/pkl/pull/1429[#1429], https://github.com/apple/pkl/pull/1622[#1622], https://github.com/apple/pkl/pull/1634[#1634], https://github.com/apple/pkl/pull/1706[#1706]).
Currently, Pkl defers the resolution of variable names to when the node is executed.
In Pkl 0.32, these are resolved when the file is parsed.
This is a pre-requisite feature for many upcoming features, such as flat member syntax and method varargs.
Additionally, this enables some runtime optimizations, including let expressions.
[[let-expression-improvements]]
=== Let expression performance improvements
Performance improvements were made to the handling of xref:language-reference:index.adoc#let-expressions[let expressions] (https://github.com/apple/pkl/pull/1622[#1622], https://github.com/apple/pkl/pull/1634[#1634]).
Here is a sample benchmark:
[source,pkl]
----
amends "pkl:Benchmark"
microbenchmarks {
["let"] {
expression =
let (a = 1)
let (b = 2)
let (c = 3)
let (d = 4)
let (e = 1)
let (f = 2)
let (g = 3)
let (h = 4)
let (i = 1)
let (j = 2)
let (k = 3)
let (l = 4)
let (m = 1)
let (n = 2)
let (o = 3)
let (p = 4)
let (q = 1)
let (r = 2)
let (s = 3)
let (t = 4)
a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t
}
}
output {
renderer {
converters {
[Duration] = (it: Duration) -> super[Duration].apply(it.toUnit("ns"))
}
}
}
----
And here are the results when run on a MacBook Pro M4 Max machine: `jpkl` is about 12x faster, while native `pkl` is about 4000x faster:
|===
|Variant |Results (mean)
|jpkl
|1208.06.ns -> 97.76.ns
|pkl
|1275.96.ns -> 0.32.ns
|===
=== Test reporter
A test reporter option is added when running tests (https://github.com/apple/pkl/pull/1563[#1563], https://github.com/apple/pkl/pull/1597[#1597]).
A new CLI flag, `--test-reporter`, is now available to the `pkl test` and `pkl project package` commands.
This flag accepts the name of a test reporter to be used to format test result output.
There are two possible reporters:
* `spec` (default) - the current reporter
* `minimal` - a reporter that only emits failing tests
This option is also available to pkl-gradle users.
=== Dependency notation improvements
Pkl's evaluator now accepts https://pkl-lang.org/main/current/language-reference/index.html#dependency-notation[dependency notation URIs] as source modules (https://github.com/apple/pkl/pull/1595[#1595]).
In Pkl 0.31, we introduced xref:0.31.adoc#cli-dependency-notation[CLI support for dependency notation].
Now, this support has been extended to the evaluator itself.
This means that users of Pkl's various language bindings can also use dependency notation.
Additionally, the limitation on local dependencies has been dropped.
=== pkldoc supports single-package docsites
pkldoc has improved support for single-package documentation websites (https://github.com/apple/pkl/pull/1592[#1592]).
When a docsite has only one package name, and also no overview from a `docsite-info.pkl`, the resulting docsite does not have a top-level package index page.
Instead, it redirects to the package page, and omits the site-level element from the breadcrumb.
=== Allowed package imports in `PklProject`
PklProject files can now import xref:language-reference:index.adoc#package-asset-uri[package asset URIs] (https://github.com/apple/pkl/pull/1547[#1547]).
This means that it is now possible to centralize and share common configurations across multiple projects.
=== Java Library Changes
==== Switch to JSpecify annotations
Pkl has switched to using https://jspecify.dev[JSpecify] for annotating nullability (https://github.com/apple/pkl/pull/1515[#1515], https://github.com/apple/pkl/pull/1527[#1527], https://github.com/apple/pkl/pull/1528[#1528], https://github.com/apple/pkl/pull/1530[#1530], https://github.com/apple/pkl/pull/1544[#1544], https://github.com/apple/pkl/pull/1601[#1601], https://github.com/apple/pkl/pull/1607[#1607]).
Currently, Pkl uses annotations from the `com.google.code.findbugs:jsr305` library.
This suffers from some issues:
* The https://jcp.org/en/jsr/detail?id=305[JSR-305 proposal] itself is dormant and unlikely to be adopted.
* The `com.google.code.findbugs:jsr305` library contributes packages to `javax`, which violates the https://www.oracle.com/downloads/licenses/binary-code-license.html[Oracle Binary Code License Agreement].
In contrast, JSpecify is widely seen as the standard for nullability annotations, and is already understood by most tools.
This impacts Pkl's Java APIs; methods previously annotated using JSR-305 nullability annotations now use JSpecify.
Additionally, the Java code generator now emits JSpecify annotations by default.
==== New API: `org.pkl.config.java.ConfigDecoder`
A new API is introduced called `org.pkl.config.java.ConfigDecoder` (https://github.com/apple/pkl/pull/1533[#1533]).
Currently, the `org.pkl.config.java.Config` interface mixes configuration representation with decoding logic.
To better separate these concerns, a new API is introduced for decoding.
Example:
[source,java]
----
class Main {
Config getConfig() {
var decoder = new ConfigDecoderBuilder().preconfigured().build();
return decoder.decode(bytes);
}
}
----
==== New `asNullable` methods in `pkl.config.java`
New methods are added for decoding into nullable types (https://github.com/apple/pkl/pull/1544[#1544]).
The following methods are introduced:
* `org.pkl.config.java.Config.asNullable`
* `org.pkl.config.java.JavaType.pairOfNullableFirst`
* `org.pkl.config.java.JavaType.pairOfNullableSecond`
* `org.pkl.config.java.JavaType.pairOfNullableFirstAndSecond`
* `org.pkl.config.java.JavaType.arrayOfNullable`
* `org.pkl.config.java.JavaType.iterableOfNullable`
* `org.pkl.config.java.JavaType.collectionOfNullable`
* `org.pkl.config.java.JavaType.listOfNullable`
* `org.pkl.config.java.JavaType.setOfNullable`
* `org.pkl.config.java.JavaType.mapOfNullableKeys`
* `org.pkl.config.java.JavaType.mapOfNullableValues`
* `org.pkl.config.java.JavaType.mapOfNullableKeysAndValues`
NOTE: `org.pkl.config.java.Config.as` can also return a `null` value. In a future release, this method will have a runtime non-null assertion.
==== `pkl-formatter` migrated to Java
The `pkl-formatter` library has been migrated from Kotlin to Java (https://github.com/apple/pkl/pull/1514[#1514]).
Therefore, its Kotlin dependency is dropped.
=== Standard Library Changes
==== `pkl:EvaluatorSettings`
**Additions**
* New method: link:{uri-stdlib-evaluatorSettingsModule}#resolve()[`EvaluatorSettings.resolve()`]
* New method: link:{uri-stdlib-evaluatorSettingsModule}#resolveForOs()[`EvaluatorSettings.resolveForOs()`]
* New property: link:{uri-stdlib-evaluatorSettingsExternalReaderClass}#workingDir[`EvaluatorSettings#ExternalReader.workingDir`]
* New property: link:{uri-stdlib-evaluatorSettingsHttpClass}#headers[`EvaluatorSettings#Http.headers`]
* New typealias: link:{uri-stdlib-evaluatorSettingsModule}#HttpHeaders[`EvaluatorSettings.HttpHeaders`]
* New typealias: link:{uri-stdlib-evaluatorSettingsModule}#HttpHeaderName[`EvaluatorSettings.HttpHeaderName`]
* New typealias: link:{uri-stdlib-evaluatorSettingsModule}#HttpHeaderValue[`EvaluatorSettings.HttpHeaderValue`]
==== `pkl:base`
* New property: link:{uri-stdlib-baseModule}/String#isGlobPattern[`String.isGlobPattern`]
==== `pkl:Project`
**Additions**
* New property: link:{uri-stdlib-projectModule}#resolvedEvaluatorSettings[`Project.resolvedEvaluatorSettings`]
==== `pkl:ref`
* New module introduced.
=== pkl-gradle changes
The following changes have been made to pkl-gradle.
==== Support for external readers
External readers can now be configured when configuring evaluators (https://github.com/apple/pkl/pull/1578[#1578]).
For example:
[source,kotlin]
----
val myPklTask by pkl.evaluators.creating {
val foo by externalModuleReaders.creating {
executable = "pkl-foo-reader"
arguments = listOf("--bar", "baz")
}
}
----
==== Support for module/resource readers from SPI
Gradle users can now add module/resource readers by registering a service via the Java SPI mechanism (https://github.com/apple/pkl/pull/1581[#1581]).
==== Support for Gradle configuration cache
The plugin now supports the Gradle configuration cache feature (https://github.com/apple/pkl/issues/1425[#1425]).
Thanks to https://github.com/ffluk3[@ffluk3] for their contributions to this feature!
== Breaking Changes [small]#💔#
Things to watch out for when upgrading.
=== Name resolution changes
Variables are now <<parse-time-variable-resolution,resolved at parse time>>.
As part of this change, some names are resolved differently.
Take the following code:
[source,pkl]
----
qux = "outer"
foo {
when (cond) {
qux = "inner"
}
res = qux
}
----
Currently, `res = qux` will eval to either `res = "outer"` or `res = "inner"` depending on whether `cond` evals to `true` or not.
This is a lexical lookup that is either one level up, or zero levels up, depending on the result of code execution.
This type of resolution is not possible at parse time, because we do not know what `cond` executes to.
In Pkl 0.32, `res` will always resolve to the inner `qux`, and this snippet will throw if the when generator does not fire.
=== Java API changes
Changes have been made to the Java API.
The following breaking changes have been made to the Java API.
==== Removals and deprecations
* `org.pkl.config.java.Config#makeConfig`
+
Removed without replacement
The following APIs have been removed without replacement.
* `org.pkl.config.java.Config.fromPklBinary`
+
Deprecated, replaced with `org.pkl.config.java.ConfigDecoder`
* `org.pkl.config.java.Config#makeConfig` (pr:https://github.com/apple/pkl/pull/1531[])
* `org.pkl.config.java.mapper.NonNull`
+
Deprecated, replaced with `org.jspecify.annotations.NonNull`
The following APIs have been deprecated for removal.
* `org.pkl.core.evaluatorSettings.PklEvaluatorSettings.parse`
+
No longer accepts `pathNormalizer` argument
* `org.pkl.config.java.mapper.NonNull` (https://github.com/apple/pkl/pull/1607[#1607]).
==== Changes
* `org.pkl.core.evaluatorSettings.PklEvaluatorSettings.parse` no longer accepts a `pathNormalizer` argument.
* `org.pkl.formatter.Formatter`
** Pass `GrammarVersion` to constructor instead of each `format` method
** Replace `format(Path): String` with `format(Reader, Appendable)`
** Mark as `throws IOException`
** Deprecate methods that accept `GrammarVersion` as an argument
=== Loading rule changes in `pkl:EvaluatorSettings`
Breaking changes have been made to how evaluator settings are loaded (https://github.com/apple/pkl/pull/1394[#1394]).
Breaking changes have been made to how evaluator settings are loaded when using `PklProject` (https://github.com/apple/pkl/pull/1394[#1394]).
==== Loading rule changes for the external reader executable
@@ -76,18 +504,68 @@ The `--external-module-reader` and `--external-resource-reader` CLI flags will _
This makes this behavior consistent with how other settings work.
== Work In Progress [small]#🚆#
== Bug Fixes [small]#🐜#
They missed the train but deserve a mention.
The following bugs have been fixed.
.XXX
[%collapsible]
====
XXX
====
* Data race in MessagePack encoder during concurrent server sends (https://github.com/apple/pkl/issues/1486[#1486])
* pkl-doc library publishes incorrect Maven dependency scopes (https://github.com/apple/pkl/issues/1517[#1517])
* Re-using pklbinary#Renderer during rendering results in incorrect output (https://github.com/apple/pkl/pull/1525[#1525])
* Cannot glob using subpatterns inside a project dependency (https://github.com/apple/pkl/issues/1545[#1545])
* Project package command incompatible with glob imports on Windows (https://github.com/apple/pkl/issues/1556[#1556])
* Thrown PklBugException when using power assertions with unavailable source sections (https://github.com/apple/pkl/pull/1571[#1571])
* pkl-doc: DocGenerator never shuts down its thread pool (https://github.com/apple/pkl/issues/1583[#1583])
* Imports gathering Gradle task can fail with a confusing exception (https://github.com/apple/pkl/issues/1591[#1591])
* HTTP rewrite fails with PklBugException under tr-TR locale when host contains 'I' (https://github.com/apple/pkl/issues/1617[#1617])
* Formatter bug fixes (https://github.com/apple/pkl/pull/1619[#1619])
* Unrelated error message thrown when computing an error message (https://github.com/apple/pkl/pull/1629[#1629])
* pkl-binary serialization produces an incomplete msgpack value in the presence of local properties (https://github.com/apple/pkl/issues/1631[#1631]).
* Poor parser error message (https://github.com/apple/pkl/issues/1638[#1638])
* relativePathTo does not check if receiver is a module (https://github.com/apple/pkl/issues/1649[#1649])
* `Int` overflow on multiplication throws unexpected exception (https://github.com/apple/pkl/issues/1651[#1651])
* Incorrect eager check for `Map` type (https://github.com/apple/pkl/issues/1653[#1653])
* Incorrect `toRadixString()` for `math.minInt` (https://github.com/apple/pkl/issues/1655[#1655])
* `Evaluator.evaluateSchema()` throws if properties are annotated with `@ConvertProperty` (https://github.com/apple/pkl/issues/1657[#1657])
* `String.padStart` and `String.padEnd` return incorrect strings sometimes (https://github.com/apple/pkl/issues/1661[#1661])
* `String.getOrNull` crashes with certain strings (https://github.com/apple/pkl/issues/1662[#1662])
* Incorrect facts in `pkl:base` doc comments (https://github.com/apple/pkl/pull/1669[#1669])
* Index-based methods on Set aren't implemented (https://github.com/apple/pkl/issues/1682[#1682])
* pkl-gradle throws exception when a Pkl task can't create an evaluator during configuration time (https://github.com/apple/pkl/pull/1684[#1684])
* Unrelated exception gets thrown when validating elements/entries in objects (https://github.com/apple/pkl/issues/1697[#1697])
* Type argument is lost in constraint expressions within generic typealiases (https://github.com/apple/pkl/issues/1705[#1705])
* Typealiases with type var as root are broken (https://github.com/apple/pkl/issues/1711[#1711])
* Incorrect `List`/`Set`/`Map`/`Listing`/`Mapping`/union type check behavior through typealiases (https://github.com/apple/pkl/issues/1710[#1710])
* Type aliases with type var as root are broken (https://github.com/apple/pkl/issues/1711[#1711])
* Type checks through aliases are always run lazily even when they should be eager (https://github.com/apple/pkl/issues/1716[#1716])
* Improve thread safety when evaluating `pkl:base` (https://github.com/apple/pkl/pull/1719[#1719])
* Incorrect member links in doc comments (https://github.com/apple/pkl/pull/1723[#1723])
* pkl-gradle plugin does not allow `projectpackage:` reads by default (https://github.com/apple/pkl/issues/1734[#1734])
* `IntSeq` incorrectly emits on empty ``IntSeq``s (https://github.com/apple/pkl/issues/1738[#1738])
== Security Fixes [small]#🔒#
The following security vulnerabilities were fixed:
* Remote packages can read local filesystem items by escaping local dependency roots (https://github.com/apple/pkl/security/advisories/GHSA-fgvf-hh2w-cxff[GHSA-fgvf-hh2w-cxff], https://github.com/apple/pkl/pull/1737[#1737])
* Packages can be read/written outside the configured cache directory (https://github.com/apple/pkl/security/advisories/GHSA-87qh-25w9-mh34[GHSA-87qh-25w9-mh34], https://github.com/apple/pkl/pull/1683[#1683])
== Contributors [small]#🙏#
We would like to thank the contributors to this release (in alphabetical order):
* XXX
* https://github.com/04cb[@04cb]
* https://github.com/0xmrma[@0xmrma]
* https://github.com/adityabagchi24[@adityabagchi24]
* https://github.com/adityasingh2400[@adityasingh2400]
* https://github.com/bioball[@bioball]
* https://github.com/fallintoplace[@fallintoplace]
* https://github.com/ffluk3[@ffluk3]
* https://github.com/HT154[@HT154]
* https://github.com/KushalP[@KushalP]
* https://github.com/kyokuping[@kyokuping]
* https://github.com/ldaley[@ldaley]
* https://github.com/mirkoalicastro[@mirkoalicastro]
* https://github.com/netvl[@netvl]
* https://github.com/odenix[@odenix]
* https://github.com/stackoverflow[@stackoverflow]
* https://github.com/vinayakj592[@vinayakj592]
@@ -0,0 +1,30 @@
= Pkl 0.33 Release Notes
:version: 0.33
:version-minor: 0.33.0
:release-date: XXX
:version-next: 0.34
:version-next-date: February 2027
include::partial$intro.adoc[]
== Highlights [small]#💖#
=== XXX
== Noteworthy [small]#🎶#
=== XXX
== Breaking Changes [small]#💔#
=== XXX
== Bug Fixes [small]#🐜#
* XXX
== Contributors [small]#🙏#
We would like to thank the contributors to this release (in alphabetical order):
* XXX
@@ -1,8 +1,15 @@
= Changelog
include::ROOT:partial$component-attributes.adoc[]
[[release-0.33.0]]
== 0.33.0 (UNRELEASED)
xref:0.33.adoc[Release Notes]
[[release-0.32.0]]
== 0.32.0 (UNRELEASED)
== 0.32.0 (2026-07-08)
xref:0.32.adoc[Release Notes]
[[release-0.31.1]]
== 0.31.1 (2026-03-26)
@@ -38,6 +45,7 @@ Thank you to all the contributors for this release!
== 0.31.0 (2026-02-26)
xref:0.31.adoc[Release Notes]
[[release-0.30.2]]
== 0.30.2 (2025-12-15)
+1
View File
@@ -41,6 +41,7 @@
* xref:ROOT:evolution-and-roadmap.adoc[Evolution and Roadmap]
* xref:release-notes:index.adoc[Release Notes]
** xref:release-notes:0.33.adoc[0.33 Release Notes]
** xref:release-notes:0.32.adoc[0.32 Release Notes]
** xref:release-notes:0.31.adoc[0.31 Release Notes]
** xref:release-notes:0.30.adoc[0.30 Release Notes]
+1 -1
View File
@@ -1,7 +1,7 @@
# suppress inspection "UnusedProperty" for whole file
group=org.pkl-lang
version=0.32.0
version=0.33.0
# google-java-format requires jdk.compiler exports
org.gradle.jvmargs= \
+6 -6
View File
@@ -3,9 +3,9 @@ assertj = "3.27.7"
checksumPlugin = "1.4.0"
# 5.0.3 is the last version compatible with Kotlin 2.2
clikt = "5.0.3"
commonMark = "0.28.0"
commonMark = "0.29.0"
downloadTaskPlugin = "5.7.0"
errorProne = "2.49.0"
errorProne = "2.50.0"
errorPronePlugin = "5.1.0"
geantyref = "2.0.1"
#noinspection UnusedVersionCatalogEntry
@@ -32,7 +32,7 @@ ideaExtPlugin = "1.4.1"
javaPoet = "0.16.0"
javaxInject = "1"
jimfs = "1.3.1"
jline = "4.1.3"
jline = "4.2.1"
jmh = "1.37"
jmhPlugin = "0.7.3"
jspecify = "1.0.0"
@@ -54,14 +54,14 @@ ktfmt = "0.62"
log4j = "2.17.1"
msgpack = "0.9.12"
nexusPublishPlugin = "2.0.0"
nullaway = "0.13.4"
nullaway = "0.13.7"
nullawayPlugin = "3.0.0"
nuValidator = "26.5.29"
nuValidator = "26.6.24"
paguro = "3.10.3"
shadowPlugin = "9.4.2"
slf4j = "2.0.18"
snakeYaml = "3.0.1"
spotlessPlugin = "8.6.0"
spotlessPlugin = "8.7.0"
wiremock = "3.13.2"
[libraries] # ordered alphabetically
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bafc141b619ad6350fd975fc903156dd5c151998cc8b058e8c1044ab5f7b031f
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
distributionSha256Sum=bbaeb2fef8710818cf0e261201dab964c572f92b942812df0c3620d62a529a01
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
Vendored
+2 -2
View File
@@ -20,7 +20,7 @@
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
Vendored
+2 -2
View File
@@ -19,7 +19,7 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@@ -72,7 +72,7 @@ echo location of your Java installation. 1>&2
@rem Execute Gradle
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
+3 -3
View File
@@ -2,15 +2,15 @@
"catalogs": {},
"aliases": {
"pkl": {
"script-ref": "org.pkl-lang:pkl-cli-java:0.31.1",
"script-ref": "org.pkl-lang:pkl-cli-java:0.32.0",
"java-agents": []
},
"pkl-codegen-java": {
"script-ref": "org.pkl-lang:pkl-codegen-java:0.31.1",
"script-ref": "org.pkl-lang:pkl-codegen-java:0.32.0",
"java-agents": []
},
"pkl-codegen-kotlin": {
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.31.1",
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.32.0",
"java-agents": []
}
},
+3 -3
View File
@@ -83,8 +83,8 @@ tasks.shadowJar {
exclude("module-info.*")
}
val testJavaExecutable by
tasks.registering(Test::class) {
val testJavaExecutable =
tasks.register<Test>("testJavaExecutable") {
testClassesDirs = tasks.test.get().testClassesDirs
classpath =
// compiled test classes
@@ -138,7 +138,7 @@ fun Exec.useRootDirAndSuppressOutput() {
// 0.28 Preparing for JDK21 toolchains revealed that `testStartJavaExecutable` may pass, even though
// the evaluator fails. To catch this, we eval a simple expression using the fat jar.
val testEvalJavaExecutable by
val testEvalJavaExecutable =
setupJavaExecutableRun("testEvalJavaExecutable", evalTestFlags) { useRootDirAndSuppressOutput() }
// Run the same evaluator tests on all configured JDK test versions.
+3 -2
View File
@@ -19,10 +19,11 @@ plugins {
id("pklPublishLibrary")
}
val svmClasspath: Configuration by configurations.creating
val svmClasspath: Configuration = configurations.create("svmClasspath")
// used by pklNativeExecutable.gradle.kts
@Suppress("unused") val svm: SourceSet by sourceSets.creating { compileClasspath = svmClasspath }
@Suppress("unused")
val svm: SourceSet = sourceSets.create("svm") { compileClasspath = svmClasspath }
dependencies {
api(projects.pklCore)
+5 -5
View File
@@ -37,7 +37,7 @@ dependencies {
* These packages are used by PackageServer to serve assets when running LanguageSnippetTests and
* PackageResolversTest.
*/
val createTestPackages by tasks.registering
val createTestPackages = tasks.register("createTestPackages")
// make sure that declaring a dependency on this project suffices to have test fixtures generated
tasks.processResources {
@@ -92,8 +92,8 @@ val keystoreName = "localhost.p12"
val keystoreFile = keystoreDir.map { it.file(keystoreName) }
val certsFileName = "localhost.pem"
val generateKeys by
tasks.registering(JavaExec::class) {
val generateKeys =
tasks.register<JavaExec>("generateKeys") {
outputs.file(keystoreFile)
mainClass.set("sun.security.tools.keytool.Main")
args =
@@ -117,8 +117,8 @@ val generateKeys by
}
}
val exportCerts by
tasks.registering(JavaExec::class) {
val exportCerts =
tasks.register<JavaExec>("exportCerts") {
val outputFile = keystoreDir.map { it.file(certsFileName) }
dependsOn(generateKeys)
inputs.file(keystoreFile)
@@ -19,11 +19,11 @@ plugins {
id("pklPublishLibrary")
}
val pklConfigJava: Configuration by configurations.creating
val pklConfigJava: Configuration = configurations.create("pklConfigJava")
val pklConfigJavaAll: Configuration by configurations.creating
val pklConfigJavaAll: Configuration = configurations.create("pklConfigJavaAll")
val pklCodegenKotlin: Configuration by configurations.creating
val pklCodegenKotlin: Configuration = configurations.create("pklCodegenKotlin")
val buildInfo = project.extensions.getByType<BuildInfo>()
@@ -49,8 +49,8 @@ dependencies {
testImplementation(libs.geantyref)
}
val generateTestConfigClasses by
tasks.registering(JavaExec::class) {
val generateTestConfigClasses =
tasks.register<JavaExec>("generateTestConfigClasses") {
val outputDir = layout.buildDirectory.dir("testConfigClasses")
outputs.dir(outputDir)
inputs.dir("src/test/resources/codegenPkl")
+3 -2
View File
@@ -35,9 +35,10 @@ val externalReaderFixtureSourceSet: NamedDomainObjectProvider<SourceSet> =
runtimeClasspath += sourceSets.test.get().output + sourceSets.test.get().runtimeClasspath
}
val externalReaderFixtureImplementation: Configuration by configurations.getting {
val externalReaderFixtureImplementation: Configuration =
configurations.getByName("externalReaderFixtureImplementation") {
extendsFrom(configurations.testImplementation.get())
}
}
idea {
module {
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 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.
@@ -69,11 +69,11 @@ public record ImportGraph(Map<URI, Set<Import>> imports, Map<URI, URI> resolvedI
var value = entry.getValue();
var set = new TreeSet<Import>();
if (!(value instanceof JsArray array)) {
throw new FormatException("array", value.getClass());
throw new FormatException("array", value == null ? Void.class : value.getClass());
}
for (var elem : array) {
if (!(elem instanceof JsObject importObj)) {
throw new FormatException("object", elem.getClass());
throw new FormatException("object", elem == null ? Void.class : elem.getClass());
}
set.add(parseImport(importObj));
}
@@ -98,7 +98,7 @@ public record ImportGraph(Map<URI, Set<Import>> imports, Map<URI, URI> resolvedI
var key = new URI(entry.getKey());
var value = entry.getValue();
if (!(value instanceof String str)) {
throw new FormatException("string", value.getClass());
throw new FormatException("string", value == null ? Void.class : value.getClass());
}
var valueUri = new URI(str);
ret.put(key, valueUri);
@@ -109,6 +109,8 @@ final class JsonRenderer implements ValueRenderer {
"Values of type `Bytes` cannot be rendered as JSON. Value: %s", (Object) value));
}
// Pair coming from the runtime can never admit null (in-language null is represented as PNull)
@SuppressWarnings("DataFlowIssue")
@Override
public void visitPair(Pair<?, ?> value) {
try {
@@ -18,7 +18,6 @@ package org.pkl.core;
import java.net.URI;
import java.util.*;
import org.jspecify.annotations.Nullable;
import org.pkl.core.util.LateInit;
/** Describes the property, method and class members of a module. */
public final class ModuleSchema {
@@ -33,8 +32,8 @@ public final class ModuleSchema {
private final Map<String, TypeAlias> typeAliases;
private final Map<String, URI> imports;
@LateInit private Map<String, PClass> __allClasses;
@LateInit private Map<String, TypeAlias> __allTypeAliases;
private @Nullable Map<String, PClass> __allClasses;
private @Nullable Map<String, TypeAlias> __allTypeAliases;
/** Constructs a {@code ModuleSchema} instance. */
public ModuleSchema(
@@ -27,6 +27,7 @@ public final class PClass extends Member implements Value {
private final List<TypeParameter> typeParameters;
private final Map<String, Property> properties;
private final Map<String, Method> methods;
private final @Nullable PClass moduleClass;
private @Nullable PType supertype;
private @Nullable PClass superclass;
@@ -42,12 +43,14 @@ public final class PClass extends Member implements Value {
PClassInfo<?> classInfo,
List<TypeParameter> typeParameters,
Map<String, Property> properties,
Map<String, Method> methods) {
Map<String, Method> methods,
@Nullable PClass moduleClass) {
super(docComment, sourceLocation, modifiers, annotations, classInfo.getSimpleName());
this.classInfo = classInfo;
this.typeParameters = typeParameters;
this.properties = properties;
this.methods = methods;
this.moduleClass = moduleClass;
}
public void initSupertype(PType supertype, PClass superclass) {
@@ -57,7 +60,7 @@ public final class PClass extends Member implements Value {
/**
* Returns the name of the module that this class is declared in. Note that a module name is not
* guaranteed to be unique, especially if it not declared but inferred from the module URI.
* guaranteed to be unique, especially if it is not declared but inferred from the module URI.
*/
public String getModuleName() {
return classInfo.getModuleName();
@@ -119,6 +122,11 @@ public final class PClass extends Member implements Value {
return allMethods;
}
/** Returns the class's containing module's class, or this class if it is a module class. */
public PClass getModuleClass() {
return moduleClass != null ? moduleClass : this;
}
@Override
public void accept(ValueVisitor visitor) {
visitor.visitClass(this);
@@ -138,6 +146,10 @@ public final class PClass extends Member implements Value {
return getDisplayName();
}
public boolean isSubclassOf(PClass other) {
return this == other || getSuperclass() != null && getSuperclass().isSubclassOf(other);
}
public abstract static class ClassMember extends Member {
@Serial private static final long serialVersionUID = 0L;
@@ -39,6 +39,7 @@ public final class PClassInfo<T> implements Serializable {
public static final URI pklSemverUri = URI.create("pkl:semver");
public static final URI pklSettingsUri = URI.create("pkl:settings");
public static final URI pklProjectUri = URI.create("pkl:Project");
public static final URI pklRefUri = URI.create("pkl:ref");
public static final PClassInfo<Void> Any = pklBaseClassInfo("Any", Void.class);
public static final PClassInfo<PNull> Null = pklBaseClassInfo("Null", PNull.class);
@@ -82,9 +83,11 @@ public final class PClassInfo<T> implements Serializable {
public static final PClassInfo<PObject> Version =
new PClassInfo<>("pkl.semver", "Version", PObject.class, pklSemverUri);
public static final PClassInfo<PObject> Project =
new PClassInfo<>("pkl.Project", "ModuleClass", PObject.class, pklProjectUri);
new PClassInfo<>("pkl.Project", MODULE_CLASS_NAME, PObject.class, pklProjectUri);
public static final PClassInfo<PObject> Settings =
new PClassInfo<>("pkl.settings", "ModuleClass", PObject.class, pklSettingsUri);
new PClassInfo<>("pkl.settings", MODULE_CLASS_NAME, PObject.class, pklSettingsUri);
public static final PClassInfo<Reference> Reference =
new PClassInfo<>("pkl.ref", "Reference", Reference.class, pklRefUri);
public static final PClassInfo<Object> Unavailable =
new PClassInfo<>("unavailable", "unavailable", Object.class, URI.create("pkl:unavailable"));
+177 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 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;
import java.io.Serial;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/** A Pkl type as used in type annotations. */
public abstract class PType implements Serializable {
@@ -27,18 +28,33 @@ public abstract class PType implements Serializable {
public static final PType UNKNOWN =
new PType() {
@Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "unknown";
}
};
/** The bottom type. */
public static final PType NOTHING =
new PType() {
@Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "nothing";
}
};
/** The type of the enclosing module. */
public static final PType MODULE =
new PType() {
@Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "module";
}
};
private PType() {}
@@ -59,6 +75,22 @@ public abstract class PType implements Serializable {
public String getLiteral() {
return literal;
}
@Override
public String toString() {
return ValueFormatter.basic().formatStringValue(literal, "");
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof StringLiteral that && literal.equals(that.literal);
}
@Override
public int hashCode() {
return literal.hashCode();
}
}
public static final class Class extends PType {
@@ -92,6 +124,31 @@ public abstract class PType implements Serializable {
public List<PType> getTypeArguments() {
return typeArguments;
}
@Override
public String toString() {
var result = pClass.getDisplayName();
if (!typeArguments.isEmpty()) {
result +=
"<"
+ typeArguments.stream().map(Object::toString).collect(Collectors.joining(", "))
+ ">";
}
return result;
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Class that
&& pClass.equals(that.pClass)
&& typeArguments.equals(that.typeArguments);
}
@Override
public int hashCode() {
return 31 * pClass.hashCode() + typeArguments.hashCode();
}
}
public static final class Nullable extends PType {
@@ -106,6 +163,24 @@ public abstract class PType implements Serializable {
public PType getBaseType() {
return baseType;
}
@Override
public String toString() {
return baseType instanceof Function || baseType instanceof Union
? "(" + baseType + ")?"
: baseType + "?";
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Nullable that && baseType.equals(that.baseType);
}
@Override
public int hashCode() {
return baseType.hashCode();
}
}
public static final class Constrained extends PType {
@@ -126,6 +201,29 @@ public abstract class PType implements Serializable {
public List<String> getConstraints() {
return constraints;
}
@Override
public String toString() {
return (baseType instanceof Function || baseType instanceof Union
? "(" + baseType + ")"
: baseType)
+ "("
+ String.join(", ", constraints)
+ ")";
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Constrained that
&& baseType.equals(that.baseType)
&& constraints.equals(that.constraints);
}
@Override
public int hashCode() {
return 31 * baseType.hashCode() + constraints.hashCode();
}
}
public static final class Alias extends PType {
@@ -161,6 +259,31 @@ public abstract class PType implements Serializable {
public PType getAliasedType() {
return aliasedType;
}
@Override
public String toString() {
var result = typeAlias.getDisplayName();
if (!typeArguments.isEmpty()) {
result +=
"<"
+ typeArguments.stream().map(Object::toString).collect(Collectors.joining(", "))
+ ">";
}
return result;
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Alias that
&& typeAlias.equals(that.typeAlias)
&& typeArguments.equals(that.typeArguments);
}
@Override
public int hashCode() {
return 31 * typeAlias.hashCode() + typeArguments.hashCode();
}
}
public static final class Function extends PType {
@@ -181,6 +304,27 @@ public abstract class PType implements Serializable {
public PType getReturnType() {
return returnType;
}
@Override
public String toString() {
return "("
+ parameterTypes.stream().map(Object::toString).collect(Collectors.joining(", "))
+ ") -> "
+ returnType;
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Function that
&& parameterTypes.equals(that.parameterTypes)
&& returnType.equals(that.returnType);
}
@Override
public int hashCode() {
return 31 * parameterTypes.hashCode() + returnType.hashCode();
}
}
public static final class Union extends PType {
@@ -195,6 +339,22 @@ public abstract class PType implements Serializable {
public List<PType> getElementTypes() {
return elementTypes;
}
@Override
public String toString() {
return elementTypes.stream().map(Object::toString).collect(Collectors.joining(" | "));
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Union that && elementTypes.equals(that.elementTypes);
}
@Override
public int hashCode() {
return elementTypes.hashCode();
}
}
public static final class TypeVariable extends PType {
@@ -213,5 +373,21 @@ public abstract class PType implements Serializable {
public TypeParameter getTypeParameter() {
return typeParameter;
}
@Override
public String toString() {
return typeParameter.getName();
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof TypeVariable that && typeParameter.equals(that.typeParameter);
}
@Override
public int hashCode() {
return typeParameter.hashCode();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 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.
@@ -227,9 +227,7 @@ final class PcfRenderer implements ValueRenderer {
write(currIndent);
writeIdentifier(name);
if (value == null) { // unevaluated property
write(" = ?");
} else if (value instanceof Composite) {
if (value instanceof Composite) {
write(' ');
visit(value);
} else {
@@ -1,5 +1,5 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2025-2026 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,7 +21,9 @@ import java.io.UncheckedIOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.jspecify.annotations.Nullable;
import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker;
import org.pkl.core.runtime.BaseModule;
@@ -111,7 +113,7 @@ public class PklBinaryDecoder extends AbstractPklBinaryDecoder {
@Override
protected Object doDecodeMap(MapDecodeIterator iter) {
var map = CollectionUtils.newLinkedHashMap(iter.getSize());
Map<@Nullable Object, @Nullable Object> map = CollectionUtils.newLinkedHashMap(iter.getSize());
while (iter.hasNext()) {
var entry = iter.next();
map.put(entry.getFirst(), entry.getSecond());
@@ -54,6 +54,9 @@ final class PropertiesRenderer implements ValueRenderer {
} else if (value instanceof Map<?, ?> map) {
doVisitMap(null, map);
} else if (value instanceof Pair<?, ?> pair) {
// Pair coming from the runtime can never admit null (in-language null is represented as
// PNull)
//noinspection DataFlowIssue
doVisitKeyAndValue(null, pair.getFirst(), pair.getSecond());
} else {
throw new RendererException(
@@ -177,6 +180,13 @@ final class PropertiesRenderer implements ValueRenderer {
"Values of type `Regex` cannot be rendered as Properties. Value: %s", value));
}
@Override
public String convertReference(Reference value) {
throw new RendererException(
String.format(
"Values of type `Reference` cannot be rendered as Properties. Value: %s", value));
}
private void doVisitMap(@Nullable String keyPrefix, Map<?, ?> map) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
doVisitKeyAndValue(keyPrefix, entry.getKey(), entry.getValue());
@@ -0,0 +1,101 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core;
import java.io.Serial;
import java.util.List;
public class Reference implements Value {
@Serial private static final long serialVersionUID = 0L;
private final Composite domain;
private final Object data;
private final List<Composite> path;
private final PType referentType;
/** Constructs a reference. */
public Reference(Composite domain, Object data, List<Composite> path, PType referentType) {
this.domain = domain;
this.data = data;
this.path = path;
this.referentType = referentType;
}
/** Returns the domain object of this reference. */
public Composite getDomain() {
return domain;
}
/** Returns the data object of this reference. */
public Object getData() {
return data;
}
/**
* Returns the access path of this reference.
*
* <p>All elements are exported {@code pkl.ref#Access} instances.
*/
public List<Composite> getPath() {
return path;
}
/** Returns the referent type of this reference. */
public PType getReferentType() {
return referentType;
}
@Override
public void accept(ValueVisitor visitor) {
visitor.visitReference(this);
}
@Override
public <T> T accept(ValueConverter<T> converter) {
return converter.convertReference(this);
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Reference reference)) {
return false;
}
return domain.equals(reference.domain)
&& data.equals(reference.data)
&& path.equals(reference.path)
&& referentType.equals(reference.referentType);
}
@Override
public PClassInfo<?> getClassInfo() {
return PClassInfo.Reference;
}
@Override
public int hashCode() {
int result = domain.hashCode();
result = 31 * result + data.hashCode();
result = 31 * result + path.hashCode();
result = 31 * result + referentType.hashCode();
return result;
}
@Override
public String toString() {
return super.toString();
}
}
@@ -28,6 +28,7 @@ public final class TypeAlias extends Member implements Value {
private final String moduleName;
private final String qualifiedName;
private final List<TypeParameter> typeParameters;
private final PClass moduleClass;
@LateInit private PType aliasedType;
@@ -39,11 +40,13 @@ public final class TypeAlias extends Member implements Value {
String simpleName,
String moduleName,
String qualifiedName,
List<TypeParameter> typeParameters) {
List<TypeParameter> typeParameters,
PClass moduleClass) {
super(docComment, sourceLocation, modifiers, annotations, simpleName);
this.moduleName = moduleName;
this.qualifiedName = qualifiedName;
this.typeParameters = typeParameters;
this.moduleClass = moduleClass;
}
public void initAliasedType(PType type) {
@@ -79,6 +82,10 @@ public final class TypeAlias extends Member implements Value {
return typeParameters;
}
public PClass getModuleClass() {
return moduleClass;
}
/** Returns the type that this type alias stands for. */
public PType getAliasedType() {
//noinspection ConstantValue
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.pkl.core;
import java.io.Serial;
import java.io.Serializable;
import org.pkl.core.util.LateInit;
import org.jspecify.annotations.Nullable;
/** A type parameter of a generic class, type alias, or method. */
public final class TypeParameter implements Serializable {
@@ -27,7 +27,7 @@ public final class TypeParameter implements Serializable {
private final String name;
private final int index;
@LateInit private volatile Member owner;
private volatile @Nullable Member owner;
public TypeParameter(Variance variance, String name, int index) {
this.variance = variance;
@@ -48,6 +48,7 @@ public final class TypeParameter implements Serializable {
/** Returns the generic class, type alias, or method that this type parameter belongs to. */
public Member getOwner() {
assert owner != null;
//noinspection DataFlowIssue
return owner;
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 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.
@@ -57,6 +57,8 @@ public interface ValueConverter<T> {
T convertRegex(Pattern value);
T convertReference(Reference value);
default T convert(Object value) {
if (value instanceof Value v) {
return (v.accept(this));
@@ -93,6 +93,10 @@ public interface ValueVisitor {
visitDefault(value);
}
default void visitReference(Reference value) {
visitDefault(value);
}
default void visit(Object value) {
if (value instanceof Value v) {
v.accept(this);
@@ -18,7 +18,6 @@ package org.pkl.core;
import java.util.*;
import java.util.regex.*;
import org.jspecify.annotations.Nullable;
import org.pkl.core.util.LateInit;
/**
* A <a href="https://semver.org/spec/v2.0.0.html">semantic version</a>.
@@ -59,7 +58,7 @@ public final class Version implements Comparable<Version> {
private final @Nullable String preRelease;
private final @Nullable String build;
@LateInit private volatile Identifier[] __preReleaseIdentifiers;
private volatile Identifier @Nullable [] __preReleaseIdentifiers;
/** Constructs a semantic version. */
public Version(
@@ -230,6 +229,7 @@ public final class Version implements Comparable<Version> {
: new Identifier(-1, str))
.toArray(Identifier[]::new);
}
//noinspection DataFlowIssue
return __preReleaseIdentifiers;
}
@@ -724,7 +724,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
// elem
// }
// }
return p.levelsUp() == 0
return p.levelsUp() == 0 && !p.needsFrameSkip()
? ReadExactFrameSlotNodeGen.create(sourceSection, p.slot())
: ReadFrameSlotNodeGen.create(
sourceSection, p.slot(), new GetEnclosingFrameNode(p.levelsUp()));
@@ -1476,8 +1476,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
var scope = (ModuleScope) symbolTable.getCurrentScope();
scope.setModifiers(modifiers);
checkAbstractMembersAllowed(modifiers, mod.getProperties(), mod.getMethods());
// visit imports first so that we already have the object member name available
var imports = mod.getImports();
var importMembers = new ObjectMember[imports.size()];
@@ -1722,7 +1720,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
List<ClassProperty> properties = bodyNode != null ? bodyNode.getProperties() : List.of();
List<ClassMethod> methods = bodyNode != null ? bodyNode.getMethods() : List.of();
registerClassScopeNames(scope, properties, methods);
checkAbstractMembersAllowed(modifiers, properties, methods);
var supertypeCtx = clazz.getSuperClass();
@@ -1813,30 +1810,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
};
}
private void checkAbstractMembersAllowed(
int enclosingModifiers, List<ClassProperty> properties, List<ClassMethod> methods) {
if (VmModifier.isAbstract(enclosingModifiers)) {
return;
}
for (var property : properties) {
checkMemberNotAbstract(property.getModifiers());
}
for (var method : methods) {
checkMemberNotAbstract(method.getModifiers());
}
}
private void checkMemberNotAbstract(List<Modifier> modifiers) {
for (var modifier : modifiers) {
if (modifier.getValue() == ModifierValue.ABSTRACT) {
throw exceptionBuilder()
.evalError("abstractMemberInNonAbstractClass")
.withSourceSection(createSourceSection(modifier.span()))
.build();
}
}
}
private UnresolvedPropertyNode[] doVisitClassProperties(
List<ClassProperty> propertyContexts, Set<String> propertyNames) {
var propertyNodes = new UnresolvedPropertyNode[propertyContexts.size()];
@@ -509,6 +509,7 @@ public final class SymbolTable {
private @Nullable <R> R resolveLexical(ResolutionFunction<R> fun) {
var levelsUp = 0;
var shouldSkip = false;
var skippedObjectScope = false;
for (var scope = this; scope != null; scope = scope.getParent()) {
// for headers resolve variables one scope up
if (scope instanceof EagerGeneratorScope) {
@@ -524,11 +525,24 @@ public final class SymbolTable {
if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) {
levelsUp++;
}
// An EagerGeneratorScope (for `when` predicates) skipped an ObjectScope.
// The ObjectScope may become a parse-time-invisible amend function at runtime,
// so for-gen variables at levelsUp == 0 need ReadFrameSlotNode
// (which calls skipInvisibleScopes) rather than ReadExactFrameSlotNode.
if (scope instanceof ObjectScope) {
skippedObjectScope = true;
}
shouldSkip = false;
continue;
}
var result = fun.apply(lex, levelsUp);
if (result != null) return result;
if (result != null) {
if (result instanceof ForGeneratorVariableOrLetBinding p && skippedObjectScope) {
//noinspection unchecked
return (R) new ForGeneratorVariableOrLetBinding(p.slot(), p.levelsUp(), true);
}
return result;
}
if (scope instanceof MethodScope
|| scope instanceof ForGeneratorScope
|| scope instanceof LetExpressionScope) {
@@ -798,7 +812,7 @@ public final class SymbolTable {
return null;
}
if (name.equals(binding.name())) {
return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp);
return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp, false);
}
return null;
}
@@ -910,10 +924,10 @@ public final class SymbolTable {
@Override
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
if (keyBinding != null && keyBinding.name().equals(name)) {
return new ForGeneratorVariableOrLetBinding(keyBinding.slot(), levelsUp);
return new ForGeneratorVariableOrLetBinding(keyBinding.slot(), levelsUp, false);
}
if (valueBinding != null && valueBinding.name().equals(name)) {
return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp);
return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp, false);
}
return null;
}
@@ -34,7 +34,8 @@ public sealed interface VariableResolution {
// method, lambda, object body param
record Parameter(int slot, int levelsUp) implements VariableResolution {}
record ForGeneratorVariableOrLetBinding(int slot, int levelsUp) implements VariableResolution {}
record ForGeneratorVariableOrLetBinding(int slot, int levelsUp, boolean needsFrameSkip)
implements VariableResolution {}
// Implicit base module lookup
record ImplicitBaseProperty() implements VariableResolution {}
@@ -23,7 +23,11 @@ import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.runtime.*;
import org.pkl.core.runtime.VmReference.VmReferenceAccessError;
import org.pkl.core.runtime.VmReference.VmReferenceAccessErrorType;
import org.pkl.core.util.ErrorMessages;
@NodeInfo(shortName = "[]")
public abstract class SubscriptNode extends BinaryExpressionNode {
@@ -100,6 +104,34 @@ public abstract class SubscriptNode extends BinaryExpressionNode {
return readMember(dynamic, key, callNode);
}
private @Nullable String getReferenceHint(
VmReference reference, VmReferenceAccessError err, Object key) {
var myType = reference.getReferentType();
if (err.getErrorType() != VmReferenceAccessErrorType.CANNOT_FIND_MEMBER) {
return null;
}
return err.getType().equals(myType)
? ErrorMessages.create("operatorNotDefined2", getShortName(), myType, VmUtils.getClass(key))
: ErrorMessages.create(
"operatorNotDefined3", getShortName(), myType, err.getType(), VmUtils.getClass(key));
}
@Specialization
protected VmReference eval(VmReference reference, Object key) {
try {
return reference.withSubscriptAccess(key);
} catch (VmReferenceAccessError err) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder()
.evalError(
"operatorNotDefined2", getShortName(), reference.exportType(), VmUtils.getClass(key))
.withProgramValue("Left operand", reference)
.withProgramValue("Right operand", key)
.withHint(getReferenceHint(reference, err, key))
.build();
}
}
@Specialization
protected long eval(VmBytes receiver, long index) {
if (index < 0 || index >= receiver.getLength()) {
@@ -1,5 +1,5 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,19 +20,19 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.UInt8TypeAliasTypeNode;
import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmUtils;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "Bytes()")
public final class BytesLiteralNode extends ExpressionNode {
@Children private final ExpressionNode[] elements;
@Child @LateInit private TypeNode typeNode;
@Child private @Nullable TypeNode typeNode;
public BytesLiteralNode(SourceSection sourceSection, ExpressionNode[] elements) {
super(sourceSection);
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmObjectLike;
@@ -34,7 +35,7 @@ public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionN
protected final int levelsUp;
private final boolean needsConst;
@Children private ExpressionNode[] argumentNodes;
@Child private DirectCallNode callNode;
@Child private @Nullable DirectCallNode callNode;
@CompilationFinal protected boolean isConstChecked;
protected AbstractInvokeMethodLexicalNode(
@@ -90,6 +91,7 @@ public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionN
callNode = DirectCallNode.create(getCallTarget(owner));
insert(callNode);
}
assert callNode != null;
return callNode;
}
}
@@ -19,17 +19,17 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
/** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */
public final class InferParentWithinMethodNode extends ExpressionNode {
private final VmLanguage language;
private final Identifier methodName;
@Child private ExpressionNode ownerNode;
@CompilationFinal @LateInit private Object inferredParent;
@Child private @Nullable ExpressionNode ownerNode;
@CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinMethodNode(
SourceSection sourceSection,
@@ -52,6 +52,7 @@ public final class InferParentWithinMethodNode extends ExpressionNode {
CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
assert owner.isPrototype();
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ObjectMethodNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
@@ -26,14 +27,13 @@ import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmDynamic;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmObjectLike;
import org.pkl.core.util.LateInit;
/** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */
public final class InferParentWithinObjectMethodNode extends ExpressionNode {
private final VmLanguage language;
private final Identifier localMethodName;
@Child private ExpressionNode ownerNode;
@CompilationFinal @LateInit private Object inferredParent;
@Child private @Nullable ExpressionNode ownerNode;
@CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinObjectMethodNode(
SourceSection sourceSection,
@@ -58,6 +58,7 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode {
CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
var member = owner.getMember(localMethodName);
@@ -18,6 +18,7 @@ package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.MemberLookupMode;
import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode;
@@ -49,8 +50,8 @@ public final class ReadAmbiguousLocalityPropertyNode extends ExpressionNode {
private final Identifier name;
private final int levelsUp;
private final boolean needsConst;
private @Child ExpressionNode readLocalPropertyNode;
private @Child ExpressionNode readPropertyNode;
@Child private @Nullable ExpressionNode readLocalPropertyNode;
@Child private @Nullable ExpressionNode readPropertyNode;
public ReadAmbiguousLocalityPropertyNode(
SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) {
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ObjectMember;
@@ -34,8 +35,8 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
private final Identifier name;
private final int levelsUp;
private final boolean needsConst;
@Child private DirectCallNode callNode;
@CompilationFinal private ObjectMember property;
@Child private @Nullable DirectCallNode callNode;
@CompilationFinal @Nullable private ObjectMember property;
public ReadLocalPropertyNode(
SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) {
@@ -91,6 +92,7 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
callNode = DirectCallNode.create(property.getCallTarget());
insert(callNode);
}
assert callNode != null;
return callNode;
}
}
@@ -24,10 +24,13 @@ import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PType;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.MemberLookupMode;
import org.pkl.core.ast.member.ClassProperty;
import org.pkl.core.runtime.*;
import org.pkl.core.runtime.VmReference.VmReferenceAccessError;
import org.pkl.core.util.ErrorMessages;
@NodeInfo(shortName = ".")
@ImportStatic(BaseModule.class)
@@ -61,6 +64,40 @@ public abstract class ReadPropertyNode extends ExpressionNode {
this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false);
}
protected String getReferenceErrorHint(VmReference reference, VmReferenceAccessError err) {
var myType = reference.getReferentType();
return switch (err.getErrorType()) {
case CANNOT_FIND_MEMBER ->
err.getType().equals(myType)
? ErrorMessages.create("cannotFindPropertyInType", propertyName, myType)
: ErrorMessages.create(
"cannotFindPropertyInType2", propertyName, myType, err.getType());
case EXTERNAL_MEMBER ->
ErrorMessages.create("cannotReferenceExternalProperty", propertyName, err.getType());
case DEFAULT_MEMBER ->
ErrorMessages.create(
"cannotReferenceDefaultProperty",
((PType.Class) err.getType()).getPClass().getSimpleName());
case EXTERNAL_CLASS ->
ErrorMessages.create(
"cannotReferencePropertyInExternalClass", propertyName, err.getType());
};
}
@Specialization
protected VmReference evalReference(VmReference receiver) {
assert lookupMode == MemberLookupMode.EXPLICIT_RECEIVER;
try {
return receiver.withPropertyAccess(propertyName);
} catch (VmReferenceAccessError err) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder()
.evalError("cannotFindPropertyInObjectNoHint", propertyName, receiver.exportType())
.withHint(getReferenceErrorHint(receiver, err))
.build();
}
}
// This method effectively covers `VmObject receiver` but is implemented in a more
// efficient way. See:
// https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/#strategy-2-java-interfaces
@@ -23,6 +23,7 @@ import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import java.io.IOException;
import java.net.URI;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.ast.member.SharedMemberNode;
import org.pkl.core.externalreader.ExternalReaderProcessException;
@@ -35,13 +36,12 @@ import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmObjectBuilder;
import org.pkl.core.util.GlobResolver;
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "import*")
public class ImportGlobNode extends AbstractImportNode {
private final String globPattern;
@Child @LateInit private SharedMemberNode memberNode;
@CompilationFinal @LateInit private VmMapping cachedResult;
@Child private @Nullable SharedMemberNode memberNode;
@CompilationFinal private @Nullable VmMapping cachedResult;
public ImportGlobNode(
SourceSection sourceSection,
@@ -21,6 +21,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import java.net.URI;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.http.HttpClientException;
import org.pkl.core.module.ResolvedModuleKey;
@@ -28,13 +29,12 @@ import org.pkl.core.packages.PackageLoadError;
import org.pkl.core.runtime.VmContext;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmTyped;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "import")
public final class ImportNode extends AbstractImportNode {
private final VmLanguage language;
@CompilationFinal @LateInit private VmTyped importedModule;
@CompilationFinal private @Nullable VmTyped importedModule;
public ImportNode(
VmLanguage language,
@@ -24,6 +24,7 @@ import com.oracle.truffle.api.source.SourceSection;
import java.io.IOException;
import java.net.URISyntaxException;
import org.graalvm.collections.EconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.ast.member.SharedMemberNode;
import org.pkl.core.externalreader.ExternalReaderProcessException;
@@ -36,12 +37,11 @@ import org.pkl.core.runtime.VmObjectBuilder;
import org.pkl.core.util.GlobResolver;
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
import org.pkl.core.util.IoUtils;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "read*")
public abstract class ReadGlobNode extends AbstractReadNode {
private final EconomicMap<String, VmMapping> cachedResults = EconomicMap.create();
@Child @LateInit private SharedMemberNode memberNode;
@Child private @Nullable SharedMemberNode memberNode;
protected ReadGlobNode(SourceSection sourceSection, ModuleKey currentModule) {
super(sourceSection, currentModule);
@@ -67,6 +67,7 @@ public abstract class ReadGlobNode extends AbstractReadNode {
@TruffleBoundary
public Object read(String globPattern) {
var cachedResult = cachedResults.get(globPattern);
//noinspection ConstantValue
if (cachedResult != null) return cachedResult;
// use same check as for globbed imports (see AstBuilder)
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import 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.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.MemberLookupMode;
@@ -62,10 +63,18 @@ public abstract class ToStringNode extends UnaryExpressionNode {
VmTyped value,
@Cached(value = "createInvokeNode()", neverDefault = true)
InvokeMethodVirtualNode invokeNode) {
return (String) invokeNode.executeWith(frame, value, value.getVmClass());
}
@Specialization
protected String evalReference(
VirtualFrame frame,
VmReference value,
@Cached(value = "createReferenceCallNode(value)", neverDefault = true)
DirectCallNode callNode) {
return (String) callNode.call(value, value.getVmClass().getPrototype());
}
@Fallback
@Override
@TruffleBoundary
@@ -74,7 +83,6 @@ public abstract class ToStringNode extends UnaryExpressionNode {
}
protected InvokeMethodVirtualNode createInvokeNode() {
//noinspection ConstantConditions
return InvokeMethodVirtualNodeGen.create(
sourceSection,
Identifier.TO_STRING,
@@ -83,4 +91,10 @@ public abstract class ToStringNode extends UnaryExpressionNode {
null,
null);
}
protected DirectCallNode createReferenceCallNode(VmReference reference) {
var toStringMethod = reference.getVmClass().getDeclaredMethod(Identifier.TO_STRING);
assert toStringMethod != null;
return DirectCallNode.create(toStringMethod.getCallTarget());
}
}
@@ -26,6 +26,7 @@ import org.pkl.core.TypeParameter;
import org.pkl.core.ast.VmModifier;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
public final class ClassMethod extends ClassMember {
private final List<TypeParameter> typeParameters;
@@ -33,7 +34,7 @@ public final class ClassMethod extends ClassMember {
// null = not deprecated, "" = no/empty message in the @Deprecated body
private final @Nullable String deprecation;
@CompilationFinal private FunctionNode functionNode;
@CompilationFinal @LateInit private FunctionNode functionNode;
public ClassMethod(
SourceSection sourceSection,
@@ -61,6 +62,7 @@ public final class ClassMethod extends ClassMember {
}
public void initFunctionNode(FunctionNode functionNode) {
//noinspection ConstantValue
assert this.functionNode == null;
this.functionNode = functionNode;
}
@@ -84,6 +84,7 @@ public final class ClassNode extends ExpressionNode {
// Caching of classes also guarantees that classes are singletons and can be compared by
// identity,
// which improves efficiency and performance (for example in shape checks).
//noinspection ConstantValue
if (cachedClass != null) return cachedClass;
CompilerDirectives.transferToInterpreter();
@@ -18,7 +18,6 @@ package org.pkl.core.ast.member;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.PklRootNode;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.VmLanguage;
@@ -46,7 +45,7 @@ public final class ListingOrMappingTypeCastNode extends PklRootNode {
}
@Override
public @Nullable String getName() {
public String getName() {
return qualifiedName;
}
@@ -23,12 +23,11 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.util.LateInit;
public final class LocalTypedPropertyNode extends RegularMemberNode {
private final VmLanguage language;
@Child private UnresolvedTypeNode unresolvedTypeNode;
@Child @LateInit private TypeNode typeNode;
@Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child private @Nullable TypeNode typeNode;
private @Nullable Object defaultValue;
private boolean defaultValueInitialized;
@@ -47,6 +46,7 @@ public final class LocalTypedPropertyNode extends RegularMemberNode {
public @Nullable Object getDefaultValue(VirtualFrame frame) {
if (!defaultValueInitialized) {
assert typeNode != null;
defaultValue =
typeNode.createDefaultValue(
frame, language, member.getHeaderSection(), member.getQualifiedName());
@@ -59,6 +59,7 @@ public final class LocalTypedPropertyNode extends RegularMemberNode {
protected Object executeImpl(VirtualFrame frame) {
if (typeNode == null) {
CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null;
}
@@ -25,7 +25,6 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
public final class ObjectMethodNode extends RegularMemberNode {
private final VmLanguage language;
@@ -33,7 +32,7 @@ public final class ObjectMethodNode extends RegularMemberNode {
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
@Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode;
@CompilationFinal @LateInit private FunctionNode functionNode;
@CompilationFinal private @Nullable FunctionNode functionNode;
public ObjectMethodNode(
VmLanguage language,
@@ -31,7 +31,7 @@ public final class UnresolvedFunctionNode extends PklNode {
private final int parameterCount;
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
@Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode;
private final ExpressionNode bodyNode;
@Child private ExpressionNode bodyNode;
public UnresolvedFunctionNode(
VmLanguage language,
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
@@ -26,7 +27,7 @@ import org.pkl.core.util.LateInit;
/** Resolves `<type>` to the type's default value in `new <type> { ... }`. */
public final class GetParentForTypeNode extends ExpressionNode {
@Child private UnresolvedTypeNode unresolvedTypeNode;
@Child private TypeNode typeNode;
@Child private @Nullable TypeNode typeNode;
private final String qualifiedName;
@CompilationFinal @LateInit Object defaultValue;
@@ -49,6 +50,7 @@ public final class GetParentForTypeNode extends ExpressionNode {
@Override
public Object executeGeneric(VirtualFrame frame) {
//noinspection ConstantValue
if (defaultValue != null) return defaultValue;
CompilerDirectives.transferToInterpreterAndInvalidate();
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 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,14 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "as")
public final class TypeCastNode extends ExpressionNode {
@Child private ExpressionNode valueNode;
@Child private UnresolvedTypeNode unresolvedTypeNode;
@Child @LateInit private TypeNode typeNode;
@Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child private @Nullable TypeNode typeNode;
public TypeCastNode(
SourceSection sourceSection,
@@ -43,6 +43,7 @@ public final class TypeCastNode extends ExpressionNode {
// don't compile unresolvedTypeNode.execute()
// invalidation is done by insert()
CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null;
}
@@ -27,7 +27,10 @@ import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeUtil;
import com.oracle.truffle.api.source.SourceSection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -38,6 +41,7 @@ import org.jspecify.annotations.Nullable;
import org.pkl.core.PType;
import org.pkl.core.PType.StringLiteral;
import org.pkl.core.PklBugException;
import org.pkl.core.StackFrame;
import org.pkl.core.TypeParameter;
import org.pkl.core.ast.*;
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
@@ -54,6 +58,7 @@ import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.EconomicSets;
import org.pkl.core.util.LateInit;
import org.pkl.core.util.MutableBoolean;
import org.pkl.core.util.MutableReference;
public abstract class TypeNode extends PklNode {
@@ -234,7 +239,7 @@ public abstract class TypeNode extends PklNode {
public abstract static class FrameSlotTypeNode extends TypeNode {
@CompilationFinal protected int slot = -1;
@CompilationFinal @Child protected WriteFrameSlotNode writeFrameSlotNode;
@CompilationFinal @Child protected @Nullable WriteFrameSlotNode writeFrameSlotNode;
protected FrameSlotTypeNode(SourceSection sourceSection) {
super(sourceSection);
@@ -844,19 +849,14 @@ public abstract class TypeNode extends PklNode {
public static class UnionTypeNode extends WriteFrameSlotTypeNode {
@Children final TypeNode[] elementTypeNodes;
private final boolean skipElementTypeChecks;
private final int defaultIndex;
public UnionTypeNode(
SourceSection sourceSection,
int defaultIndex,
TypeNode[] elementTypeNodes,
boolean skipElementTypeChecks) {
SourceSection sourceSection, int defaultIndex, TypeNode[] elementTypeNodes) {
super(sourceSection);
assert elementTypeNodes.length > 0;
this.elementTypeNodes = elementTypeNodes;
this.defaultIndex = defaultIndex;
this.skipElementTypeChecks = skipElementTypeChecks;
}
@Override
@@ -874,7 +874,10 @@ public abstract class TypeNode extends PklNode {
@Override
public boolean isNoopTypeCheck() {
return skipElementTypeChecks;
for (var element : elementTypeNodes) {
if (!element.isNoopTypeCheck()) return false;
}
return true;
}
@Override
@@ -980,8 +983,6 @@ public abstract class TypeNode extends PklNode {
@Fallback
@ExplodeLoop
protected Object executeLazily(VirtualFrame frame, Object value) {
if (skipElementTypeChecks) return value;
// escape analysis should remove this allocation in compiled code
var typeMismatches = new VmTypeMismatchException[elementTypeNodes.length];
@@ -1032,8 +1033,6 @@ public abstract class TypeNode extends PklNode {
@Override
public Object executeEagerly(VirtualFrame frame, Object value) {
if (skipElementTypeChecks) return value;
// escape analysis should remove this allocation in compiled code
var typeMismatches = new VmTypeMismatchException[elementTypeNodes.length];
@@ -1157,7 +1156,6 @@ public abstract class TypeNode extends PklNode {
@Child private TypeNode elementTypeNode;
public CollectionTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) {
super(sourceSection);
this.elementTypeNode = elementTypeNode;
}
@@ -1267,12 +1265,10 @@ public abstract class TypeNode extends PklNode {
public static final class ListTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode elementTypeNode;
private final boolean skipElementTypeChecks;
public ListTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) {
super(sourceSection);
this.elementTypeNode = elementTypeNode;
skipElementTypeChecks = elementTypeNode.isNoopTypeCheck();
}
@Override
@@ -1317,7 +1313,7 @@ public abstract class TypeNode extends PklNode {
if (!(value instanceof VmList vmList)) {
throw typeMismatch(value, BaseModule.getListClass());
}
if (skipElementTypeChecks) return vmList;
if (elementTypeNode.isNoopTypeCheck()) return vmList;
for (var elem : vmList) {
elementTypeNode.executeEagerly(frame, elem);
@@ -1334,7 +1330,7 @@ public abstract class TypeNode extends PklNode {
if (!(value instanceof VmList vmList)) {
throw typeMismatch(value, BaseModule.getListClass());
}
if (skipElementTypeChecks) return vmList;
if (elementTypeNode.isNoopTypeCheck()) return vmList;
var ret = vmList;
var idx = 0;
@@ -1366,12 +1362,10 @@ public abstract class TypeNode extends PklNode {
public abstract static class SetTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode elementTypeNode;
private final boolean skipElementTypeChecks;
protected SetTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) {
super(sourceSection);
this.elementTypeNode = elementTypeNode;
skipElementTypeChecks = elementTypeNode.isNoopTypeCheck();
}
@Override
@@ -1421,7 +1415,7 @@ public abstract class TypeNode extends PklNode {
@Specialization
protected Object eval(VirtualFrame frame, VmSet value) {
if (skipElementTypeChecks) return value;
if (elementTypeNode.isNoopTypeCheck()) return value;
for (var elem : value) {
// no point doing a lazy check because set members have their hash code computed, which
// necessarily deep-forces them.
@@ -1446,14 +1440,11 @@ public abstract class TypeNode extends PklNode {
public static final class MapTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode keyTypeNode;
@Child private TypeNode valueTypeNode;
private final boolean skipEntryTypeChecks;
public MapTypeNode(SourceSection sourceSection, TypeNode keyTypeNode, TypeNode valueTypeNode) {
super(sourceSection);
this.keyTypeNode = keyTypeNode;
this.valueTypeNode = valueTypeNode;
skipEntryTypeChecks = keyTypeNode.isNoopTypeCheck() && valueTypeNode.isNoopTypeCheck();
}
@Override
@@ -1526,7 +1517,7 @@ public abstract class TypeNode extends PklNode {
}
private Object eval(VirtualFrame frame, VmMap value) {
if (skipEntryTypeChecks) return value;
if (keyTypeNode.isNoopTypeCheck() && valueTypeNode.isNoopTypeCheck()) return value;
var ret = value;
for (var entry : value) {
@@ -1543,7 +1534,7 @@ public abstract class TypeNode extends PklNode {
}
private Object evalEager(VirtualFrame frame, VmMap value) {
if (skipEntryTypeChecks) return value;
if (keyTypeNode.isNoopTypeCheck() && valueTypeNode.isNoopTypeCheck()) return value;
for (var entry : value) {
keyTypeNode.executeEagerly(frame, VmUtils.getKey(entry));
valueTypeNode.executeEagerly(frame, VmUtils.getValue(entry));
@@ -1707,9 +1698,6 @@ public abstract class TypeNode extends PklNode {
@Child protected TypeNode valueTypeNode;
@Child @Nullable protected ListingOrMappingTypeCastNode valueTypeCastNode;
private final boolean skipKeyTypeChecks;
private final boolean skipValueTypeChecks;
protected ListingOrMappingTypeNode(
SourceSection sourceSection,
VmLanguage language,
@@ -1720,9 +1708,6 @@ public abstract class TypeNode extends PklNode {
this.language = language;
this.keyTypeNode = keyTypeNode;
this.valueTypeNode = valueTypeNode;
skipKeyTypeChecks = keyTypeNode == null || keyTypeNode.isNoopTypeCheck();
skipValueTypeChecks = valueTypeNode.isNoopTypeCheck();
}
private boolean isListing() {
@@ -1844,7 +1829,11 @@ public abstract class TypeNode extends PklNode {
}
protected void doEagerCheck(VirtualFrame frame, VmObject object) {
doEagerCheck(frame, object, skipKeyTypeChecks, skipValueTypeChecks);
doEagerCheck(
frame,
object,
keyTypeNode == null || keyTypeNode.isNoopTypeCheck(),
valueTypeNode.isNoopTypeCheck());
}
protected void doEagerCheck(
@@ -2127,13 +2116,136 @@ public abstract class TypeNode extends PklNode {
}
}
public abstract static class ReferenceTypeNode extends ValidatingObjectSlotTypeNode {
@Child private TypeNode domainTypeNode;
@Child private TypeNode referentTypeNode;
@Child private ExpressionNode getModuleNode;
public ReferenceTypeNode(
SourceSection sourceSection, TypeNode domainTypeNode, TypeNode referentTypeNode) {
super(sourceSection);
this.domainTypeNode = domainTypeNode;
this.referentTypeNode = referentTypeNode;
this.getModuleNode = new GetModuleNode(sourceSection);
validate();
}
@Override
public final String getValidationErrorKey() {
return "invalidReferenceTypeAnnotationWithConstraint";
}
@Override
protected final @Nullable Node getViolatingNode() {
// constraints may not be used in Reference type annotation referents
// walk the type and throw if any part of the referent is constrained
var violation = new MutableReference<Node>(null);
referentTypeNode.acceptTypeNode(
true,
(typeNode) -> {
if (typeNode instanceof ConstrainedTypeNode) {
violation.set(typeNode);
return false;
}
return true;
});
return violation.getOrNull();
}
@Override
protected final boolean isIncludedInTrace(Node node) {
return node instanceof ReferenceTypeNode || node instanceof ConstrainedTypeNode;
}
@Specialization
protected Object eval(VirtualFrame frame, VmReference value) {
if (domainTypeNode.isNoopTypeCheck() && referentTypeNode.isNoopTypeCheck()) {
return value;
}
try {
domainTypeNode.execute(frame, value.getDomain());
} catch (VmTypeMismatchException e) {
CompilerDirectives.transferToInterpreter();
throw new VmTypeMismatchException.Reference(
sourceSection,
value,
TypeNode.export(domainTypeNode),
TypeNode.export(referentTypeNode));
}
var module = (VmTyped) getModuleNode.executeGeneric(frame);
return doEval(value, module);
}
@TruffleBoundary
private Object doEval(VmReference value, VmTyped module) {
var referentType = TypeNode.export(referentTypeNode);
if (value.referentTypeIsSubtypeOf(referentType, module.getVmClass().export())) {
return value;
}
throw new VmTypeMismatchException.Reference(
sourceSection, value, TypeNode.export(domainTypeNode), referentType);
}
@Fallback
protected Object fallback(Object value) {
throw typeMismatch(value, RefModule.getReferenceClass());
}
@Override
protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer) {
if (visitTypeArguments)
return consumer.accept(this)
&& consumer.accept(domainTypeNode)
&& consumer.accept(referentTypeNode);
return consumer.accept(this);
}
@Override
public VmClass getVmClass() {
return RefModule.getReferenceClass();
}
@Override
public VmList getTypeArgumentMirrors() {
return VmList.of(domainTypeNode.getMirror(), referentTypeNode.getMirror());
}
@Override
protected boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof ReferenceTypeNode referenceTypeNode)) {
return false;
}
return referentTypeNode.isEquivalentTo(referenceTypeNode.referentTypeNode);
}
@Override
public boolean isNoopTypeCheck() {
return domainTypeNode.isNoopTypeCheck() && referentTypeNode.isNoopTypeCheck();
}
@Override
protected PType doExport() {
return new PType.Class(
RefModule.getReferenceClass().export(),
domainTypeNode.doExport(),
referentTypeNode.doExport());
}
@Override
protected boolean isParametric() {
return true;
}
}
public static final class PairTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode firstTypeNode;
@Child private TypeNode secondTypeNode;
public PairTypeNode(
SourceSection sourceSection, TypeNode firstTypeNode, TypeNode secondTypeNode) {
super(sourceSection);
this.firstTypeNode = firstTypeNode;
this.secondTypeNode = secondTypeNode;
@@ -2585,12 +2697,23 @@ public abstract class TypeNode extends PklNode {
this.typeAlias = typeAlias;
this.typeArgumentNodes = typeArgumentNodes;
aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes);
aliasedTypeNode.accept(
node -> {
if (node instanceof ValidatingObjectSlotTypeNode typeNode) {
typeNode.validate(this);
}
return true;
});
}
public TypeNode getAliasedTypeNode() {
return aliasedTypeNode;
}
public VmTypeAlias getTypeAlias() {
return typeAlias;
}
@Override
public FrameSlotKind getFrameSlotKind() {
return aliasedTypeNode.getFrameSlotKind();
@@ -2633,6 +2756,21 @@ public abstract class TypeNode extends PklNode {
}
}
@Override
public Object executeEagerly(VirtualFrame frame, Object value) {
var prevOwner = VmUtils.getOwner(frame);
var prevReceiver = VmUtils.getReceiver(frame);
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
try {
return aliasedTypeNode.executeEagerly(frame, value);
} finally {
setOwner(frame, prevOwner);
setReceiver(frame, prevReceiver);
}
}
/** See docstring on {@link TypeAliasTypeNode#executeLazily}. */
@Override
public Object executeAndSet(VirtualFrame frame, Object value) {
@@ -3081,6 +3219,78 @@ public abstract class TypeNode extends PklNode {
}
}
public abstract static class ValidatingObjectSlotTypeNode extends ObjectSlotTypeNode {
protected ValidatingObjectSlotTypeNode(SourceSection sourceSection) {
super(sourceSection);
}
protected abstract String getValidationErrorKey();
protected abstract @Nullable Node getViolatingNode();
protected final void validate() {
var violation = getViolatingNode();
if (violation == null) return;
throw exceptionBuilder()
.evalError(getValidationErrorKey())
.withLeadingStackFrames(buildLeadingFrames(violation, getSourceSection(), null))
.build();
}
public final void validate(TypeAliasTypeNode outermostAliasNode) {
var violation = getViolatingNode();
if (violation == null) return;
throw exceptionBuilder()
.withLocation(outermostAliasNode)
.evalError(getValidationErrorKey())
.withLeadingStackFrames(
buildLeadingFrames(
violation,
outermostAliasNode.getSourceSection(),
outermostAliasNode.getTypeAlias()))
.build();
}
protected abstract boolean isIncludedInTrace(Node node);
private List<StackFrame> buildLeadingFrames(
Node violatingNode, SourceSection usageSection, @Nullable VmTypeAlias outermostAlias) {
var frames = new ArrayList<StackFrame>();
for (var node = violatingNode; node != null; node = node.getParent()) {
if (!(node instanceof TypeAliasTypeNode || isIncludedInTrace(node))) continue;
var section = node.getSourceSection();
if (section == null || !section.isAvailable() || isWithin(usageSection, section)) {
continue;
}
var owner = ownerAlias(node, outermostAlias);
if (owner != null) {
frames.add(VmUtils.createStackFrame(section, owner.getQualifiedName()));
}
}
return frames;
}
/**
* The type alias whose body contains {@code node}: the nearest enclosing alias, else the
* outermost alias being instantiated (which is {@code null} when no alias is used).
*/
@SuppressWarnings("DataFlowIssue")
private static @Nullable VmTypeAlias ownerAlias(
Node node, @Nullable VmTypeAlias outermostAlias) {
var parent = NodeUtil.findParent(node, TypeAliasTypeNode.class);
//noinspection ConstantValue
return parent != null ? parent.getTypeAlias() : outermostAlias;
}
private static boolean isWithin(SourceSection outer, SourceSection inner) {
return inner.getSource().equals(outer.getSource())
&& inner.getCharIndex() >= outer.getCharIndex()
&& inner.getCharEndIndex() <= outer.getCharEndIndex();
}
}
private static @Nullable Object createDefaultValue(VmClass clazz) {
if (clazz.isInstantiable()) {
if (clazz.isListingClass()) return VmListing.empty();
@@ -26,7 +26,7 @@ import org.pkl.core.runtime.VmLanguage;
@NodeInfo(shortName = "is")
public final class TypeTestNode extends ExpressionNode {
@Child private ExpressionNode valueNode;
@Child private UnresolvedTypeNode unresolvedTypeNode;
@Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child private @Nullable TypeNode typeNode;
public TypeTestNode(
@@ -49,6 +49,7 @@ public final class TypeTestNode extends ExpressionNode {
// don't compile unresolvedTypeNode.execute()
// invalidation is done by insert()
CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null;
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 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.
@@ -39,7 +39,7 @@ public abstract class UnresolvedTypeNode extends PklNode {
private final VmLanguage language;
@Child UnresolvedTypeNode childNode;
TypeConstraintNode[] constraintCheckNodes;
@Children TypeConstraintNode[] constraintCheckNodes;
public Constrained(
SourceSection sourceSection,
@@ -287,6 +287,13 @@ public abstract class UnresolvedTypeNode extends PklNode {
return new VarArgsTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
}
if (clazz.isReferenceClass()) {
return ReferenceTypeNodeGen.create(
sourceSection,
typeArgumentNodes[0].execute(frame),
typeArgumentNodes[1].execute(frame));
}
throw exceptionBuilder()
.evalError("notAParameterizableClass", clazz.getDisplayName())
.withSourceSection(typeArgumentNodes[0].sourceSection)
@@ -354,16 +361,13 @@ public abstract class UnresolvedTypeNode extends PklNode {
CompilerDirectives.transferToInterpreter();
var elementTypeNodes = new TypeNode[unresolvedElementTypeNodes.length];
var skipElementTypeChecks = true;
for (var i = 0; i < elementTypeNodes.length; i++) {
var elementTypeNode = unresolvedElementTypeNodes[i].execute(frame);
elementTypeNodes[i] = elementTypeNode;
skipElementTypeChecks &= elementTypeNode.isNoopTypeCheck();
}
return new UnionTypeNode(
sourceSection, defaultIndex, elementTypeNodes, skipElementTypeChecks);
return new UnionTypeNode(sourceSection, defaultIndex, elementTypeNodes);
}
}
@@ -422,6 +426,10 @@ public abstract class UnresolvedTypeNode extends PklNode {
this.typeParameter = typeParameter;
}
public int getTypeParameterIndex() {
return typeParameter.getIndex();
}
@Override
public TypeNode execute(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();
@@ -429,4 +437,23 @@ public abstract class UnresolvedTypeNode extends PklNode {
return new TypeVariableNode(sourceSection, typeParameter);
}
}
/**
* An unresolved type node that is pre-resolved to a concrete type node. Used during type alias
* instantiation to replace type variable references inside constraint expressions (e.g., {@code
* every((it) -> it is T)}) with the corresponding concrete type argument.
*/
public static final class Resolved extends UnresolvedTypeNode {
@Child private TypeNode typeNode;
public Resolved(SourceSection sourceSection, TypeNode typeNode) {
super(sourceSection);
this.typeNode = typeNode;
}
@Override
public TypeNode execute(VirtualFrame frame) {
return typeNode;
}
}
}
@@ -23,6 +23,7 @@ import com.oracle.truffle.api.source.SourceSection;
import java.util.*;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PType;
import org.pkl.core.StackFrame;
import org.pkl.core.ValueFormatter;
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
@@ -333,4 +334,44 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
return false;
}
}
public static final class Reference extends VmTypeMismatchException {
private final PType expectedDomainType;
private final PType expectedReferentType;
public Reference(
SourceSection sourceSection,
VmReference actualValue,
PType expectedDomainType,
PType expectedReferentType) {
super(sourceSection, actualValue);
this.expectedDomainType = expectedDomainType;
this.expectedReferentType = expectedReferentType;
}
@Override
public void buildMessage(
AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
builder
.append(
ErrorMessages.createIndented(
"typeMismatch",
indent,
new PType.Class(
RefModule.getReferenceClass().export(),
expectedDomainType,
expectedReferentType),
((VmReference) actualValue).exportType()))
.append("\n")
.append(indent)
.append("Value: ")
.append(VmValueRenderer.singleLine(80 - indent.length()).render(actualValue));
}
@Override
protected Boolean hasHint() {
return false;
}
}
}
@@ -103,7 +103,7 @@ final class ExternalResourceResolverImpl implements ExternalResourceResolver {
readResponses.computeIfAbsent(
baseUri,
(uri) -> {
var future = new CompletableFuture<byte[]>();
var future = new CompletableFuture<byte @Nullable []>();
var request =
new ReadResourceRequest(requestIdGenerator.nextLong(), evaluatorId, uri);
try {
@@ -46,7 +46,6 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.util.ErrorMessages;
import org.pkl.core.util.Exceptions;
@@ -93,7 +92,7 @@ final class JdkHttpClient implements HttpClient {
HttpRequest request,
BodyHandler<T> responseBodyHandler,
HttpRequestChecker httpRequestChecker)
throws IOException, SecurityManagerException {
throws IOException {
try {
return underlying.send(request, responseBodyHandler);
} catch (ConnectException e) {
@@ -84,7 +84,7 @@ public final class Messages {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -93,12 +93,14 @@ public class PathElement {
}
/** Returns the element at path {@code basePath}, given a {@link Path}. */
@SuppressWarnings("DataFlowIssue") // incorrect analysis; this can return null.
public @Nullable TreePathElement getElement(Path basePath) {
var path = basePath.normalize();
var element = this;
for (var i = 0; i < path.getNameCount(); i++) {
var part = path.getName(i).toString();
element = element.getChildren().get(part);
//noinspection ConstantValue
if (element == null) {
return null;
}
@@ -183,6 +183,7 @@ public final class ProjectDependenciesManager {
public Map<String, Dependency> getLocalPackageDependencies(PackageUri packageUri) {
ensureDependenciesInitialized();
var dep = localPackageDependencies.get(packageUri);
//noinspection ConstantValue
assert dep != null;
return dep;
}
@@ -53,9 +53,11 @@ public abstract class Dependency {
}
public URI resolveAssetUri(URI projectBaseUri, PackageAssetUri packageAssetUri) {
// drop 1 to remove leading `/`
var assetPath = packageAssetUri.getAssetPath().substring(1);
var resolvedPath = path.resolve(assetPath);
// copy how remote dependencies work; the `..` segment at the root just normalizes to the
// root.
var assetPath = Path.of(packageAssetUri.getAssetPath()).normalize();
var relativePath = Path.of("/").relativize(assetPath);
var resolvedPath = path.resolve(relativePath);
var normalized = IoUtils.toNormalizedPathString(resolvedPath);
try {
var relativeUri = new URI(null, null, normalized, null);
@@ -67,7 +69,7 @@ public abstract class Dependency {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -107,7 +109,7 @@ public abstract class Dependency {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -217,6 +217,7 @@ public final class DependencyMetadata {
var map = new HashMap<>();
for (var kv : value) {
var kvObj = (JsObject) kv;
assert kvObj != null;
map.put(parsePObject(kvObj.get("key")), parsePObject(kvObj.get("value")));
}
return map;
@@ -294,7 +295,7 @@ public final class DependencyMetadata {
var ret = new ArrayList<String>(arr.size());
for (var elem : arr) {
if (!(elem instanceof String string)) {
throw new FormatException("string", elem.getClass());
throw new FormatException("string", elem != null ? elem.getClass() : Void.class);
}
ret.add(string);
}
@@ -414,7 +415,7 @@ public final class DependencyMetadata {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -624,8 +625,8 @@ public final class DependencyMetadata {
jsonWriter.endObject();
}
private void writeGenericObject(Object value) throws IOException {
if (value instanceof PNull) {
private void writeGenericObject(@Nullable Object value) throws IOException {
if (value == null || value instanceof PNull) {
jsonWriter.nullValue();
} else if (value instanceof PObject pObject) {
writePObject(pObject);
@@ -18,6 +18,7 @@ package org.pkl.core.packages;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException;
import org.pkl.core.Version;
import org.pkl.core.util.ErrorMessages;
@@ -98,7 +99,7 @@ public final class PackageAssetUri {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -45,6 +45,7 @@ import java.util.stream.StreamSupport;
import java.util.zip.ZipInputStream;
import org.graalvm.collections.EconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException;
import org.pkl.core.SecurityManager;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.http.HttpClient;
@@ -456,10 +457,17 @@ final class PackageResolvers {
}
private Path getRelativePath(PackageUri uri) {
return Path.of(
var relativePath =
Path.of(
CACHE_DIR_PREFIX,
IoUtils.encodePath(uri.getUri().getAuthority()),
getEffectivePackageUriPath(uri));
// ensure the derived path cannot escape the cache directory
var resolved = cacheDir.resolve(relativePath).normalize();
if (!resolved.startsWith(cacheDir.normalize())) {
throw new PklBugException("Package URI escapes the cache directory");
}
return relativePath;
}
private String getLastSegmentName(PackageUri packageUri) {
@@ -61,6 +61,13 @@ public final class PackageUri {
throw new URISyntaxException(
uri.toString(), ErrorMessages.create("missingPathInPackageUri", uri));
}
// reject `..` segments, percent-encoded or not
for (var segment : path.split("/", -1)) {
if (segment.equals("..")) {
throw new URISyntaxException(
uri.toString(), ErrorMessages.create("invalidRelativePathInPackageUri"));
}
}
var versionIdx = path.lastIndexOf('@');
if (versionIdx == -1) {
throw new URISyntaxException(
@@ -111,7 +118,7 @@ public final class PackageUri {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 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,6 +17,7 @@ package org.pkl.core.project;
import java.net.URI;
import java.net.URISyntaxException;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException;
import org.pkl.core.packages.PackageUri;
import org.pkl.core.util.ErrorMessages;
@@ -84,7 +85,7 @@ public record CanonicalPackageUri(URI baseUri, int majorVersion) {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -199,7 +199,7 @@ public final class Project {
sb.append("\n│");
}
sb.append("\n│ ");
sb.append(uri.toString());
sb.append(uri);
}
sb.append("\n└─");
}
@@ -477,7 +477,7 @@ public final class Project {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -611,7 +611,7 @@ public final class Project {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -141,6 +141,7 @@ public final class ProjectDependenciesResolver {
private void updateDependency(Dependency dependency) {
var canonicalPackageUri = CanonicalPackageUri.fromPackageUri(dependency.getPackageUri());
var currentDependency = resolvedDependencies.get(canonicalPackageUri);
//noinspection ConstantValue
if (currentDependency == null
|| currentDependency.getVersion().compareTo(dependency.getVersion()) < 0) {
EconomicMaps.put(resolvedDependencies, canonicalPackageUri, dependency);

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