Compare commits

...

25 Commits

Author SHA1 Message Date
dependabot[bot] 5f2977a8f6 Bump nu.validator:validator from 26.5.29 to 26.6.24
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>
2026-07-02 15:04:40 +00: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
199 changed files with 3749 additions and 643 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # 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'
+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)
}
}
}
@@ -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,7 +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]]
[NOTE]
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -5,7 +5,7 @@ checksumPlugin = "1.4.0"
clikt = "5.0.3"
commonMark = "0.28.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
@@ -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 {}
@@ -100,6 +100,20 @@ public abstract class SubscriptNode extends BinaryExpressionNode {
return readMember(dynamic, key, callNode);
}
@Specialization
protected VmReference eval(VmReference reference, Object key) {
var result = reference.withSubscriptAccess(key);
if (result != null) return result;
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder()
.evalError(
"operatorNotDefined2", getShortName(), reference.exportType(), VmUtils.getClass(key))
.withProgramValue("Left operand", reference)
.withProgramValue("Right operand", 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;
}
}
@@ -61,6 +61,18 @@ public abstract class ReadPropertyNode extends ExpressionNode {
this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false);
}
@Specialization
protected VmReference evalReference(VmReference receiver) {
assert lookupMode == MemberLookupMode.EXPLICIT_RECEIVER;
var result = receiver.withPropertyAccess(propertyName);
if (result != null) return result;
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder()
.evalError("cannotFindPropertyInReference", propertyName, receiver.exportType())
.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);
@@ -2127,6 +2132,130 @@ 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;
@@ -2585,12 +2714,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();
@@ -3081,6 +3221,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.
@@ -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)
@@ -422,6 +429,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 +440,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;
}
@@ -67,7 +67,7 @@ public abstract class Dependency {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -107,7 +107,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(
CACHE_DIR_PREFIX,
IoUtils.encodePath(uri.getUri().getAuthority()),
getEffectivePackageUriPath(uri));
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);
@@ -135,6 +135,7 @@ public final class ProjectDeps {
}
/** Given a declared dependency, return the resolved dependency. */
@SuppressWarnings("DataFlowIssue") // incorrect analysis
public @Nullable Dependency get(CanonicalPackageUri canonicalPackageUri) {
return resolvedDependencies.get(canonicalPackageUri);
}
@@ -150,7 +151,7 @@ public final class ProjectDeps {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -51,6 +51,8 @@ public final class FileSystemManager {
public static synchronized FileSystem getFileSystem(URI uri) throws IOException {
var fs = fileSystems.get(uri);
// incorrect nullability for `org.graalvm.collections.UnmodifiableEconomicMap`
//noinspection ConstantValue
if (fs != null) {
var count = counts.get(fs);
assert count != null;
@@ -165,6 +165,10 @@ public final class Identifier implements Comparable<Identifier> {
public static final Identifier ILLEGAL = get("`");
// members of pkl.ref
public static final Identifier PROPERTY = get("property");
public static final Identifier KEY = get("key");
// common in lambdas etc
public static final Identifier IT = get("it");
@@ -104,6 +104,8 @@ public final class ModuleCache {
return PlatformModule.getModule();
case "project":
return ProjectModule.getModule();
case "ref":
return RefModule.getModule();
case "reflect":
return ReflectModule.getModule();
case "release":
@@ -40,10 +40,10 @@ public final class ModuleInfo {
@LateInit private List<VmTyped> annotations;
@LateInit private VmTyped __mirror;
private @Nullable VmTyped __mirror;
private final Object mirrorLock = new Object();
@LateInit private ModuleSchema __moduleSchema;
private @Nullable ModuleSchema __moduleSchema;
private final Object moduleSchemaLock = new Object();
public ModuleInfo(
@@ -65,12 +65,12 @@ public final class ModuleInfo {
}
public void initAnnotations(List<VmTyped> annotations) {
//noinspection ConstantValue
assert this.annotations == null;
this.annotations = annotations;
}
public List<VmTyped> getAnnotations() {
assert annotations != null;
return annotations;
}
@@ -0,0 +1,53 @@
/*
* 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.runtime;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import java.net.URI;
public class RefModule extends StdLibModule {
private static final VmTyped instance = VmUtils.createEmptyModule();
static {
loadModule(URI.create("pkl:ref"), instance);
}
public static VmClass getReferenceClass() {
return ReferenceClass.instance;
}
public static VmClass getAccessClass() {
return AccessClass.instance;
}
private static final class ReferenceClass {
static final VmClass instance = loadClass("Reference");
}
private static final class AccessClass {
static final VmClass instance = loadClass("Access");
}
@TruffleBoundary
private static VmClass loadClass(String className) {
var theModule = getModule();
return (VmClass) VmUtils.readMember(theModule, Identifier.get(className));
}
public static VmTyped getModule() {
return instance;
}
}
@@ -38,6 +38,10 @@ final class StackTraceGenerator {
}
private List<StackFrame> capture() {
// frames that aren't part of the runtime call stack are
// shown ahead of the captured frames.
frames.addAll(exception.getLeadingStackFrames());
var truffleElements = TruffleStackTrace.getStackTrace(exception);
if (truffleElements.isEmpty()) {
addFrame(exception.getSourceSection(), exception.getMemberName());
@@ -101,8 +101,14 @@ public final class StackTraceRenderer {
if (hint != null) {
out.append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
} else {
assert hintBuilder != null;
out.append(AnsiTheme.ERROR_MESSAGE_HINT, () -> hintBuilder.accept(out, true));
out.append(
AnsiTheme.ERROR_MESSAGE_HINT,
() -> {
// nullaway needs this assertion
//noinspection ConstantValue
assert hintBuilder != null;
hintBuilder.accept(out, true);
});
}
out.append('\n');
}
@@ -491,11 +491,11 @@ public final class TestRunner {
sb.append("\n Expected: ");
appendLocation(sb, expectedLocation);
sb.append("\n ");
sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, expectedValue.replaceAll("\n", "\n "));
sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, expectedValue.replace("\n", "\n "));
sb.append("\n Actual: ");
appendLocation(sb, actualLocation);
sb.append("\n ");
sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, actualValue.replaceAll("\n", "\n "));
sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, actualValue.replace("\n", "\n "));
});
return new Failure("Example Failure", sb.toString());
}
@@ -38,7 +38,8 @@ public final class VmBugException extends VmException {
@Nullable SourceSection sourceSection,
@Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames) {
Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super(
message,
@@ -51,7 +52,8 @@ public final class VmBugException extends VmException {
sourceSection,
memberName,
hintBuilder,
insertedStackFrames);
insertedStackFrames,
leadingStackFrames);
}
@Override
@@ -445,6 +445,11 @@ public final class VmClass extends VmValue {
return isClass(BaseModule.getVarArgsClass(), "pkl.base#VarArgs");
}
@Idempotent
public boolean isReferenceClass() {
return isClass(RefModule.getReferenceClass(), "pkl.ref#Reference");
}
private boolean isClass(@Nullable VmClass clazz, String qualifiedClassName) {
// may be null during evaluation of base module
return clazz != null ? this == clazz : getQualifiedName().equals(qualifiedClassName);
@@ -621,49 +626,62 @@ public final class VmClass extends VmValue {
public PClass export() {
synchronized (pClassLock) {
//noinspection ConstantValue
if (__pClass == null) {
var exportedAnnotations = new ArrayList<PObject>();
var properties =
CollectionUtils.<String, PClass.Property>newLinkedHashMap(
EconomicMaps.size(declaredProperties));
var methods =
CollectionUtils.<String, PClass.Method>newLinkedHashMap(
EconomicMaps.size(declaredMethods));
if (__pClass != null) {
return __pClass;
}
// set pClass before exporting class members to prevent
// infinite recursion in case of cyclic references
__pClass =
new PClass(
VmUtils.exportDocComment(docComment),
new SourceLocation(headerSection.getStartLine(), sourceSection.getEndLine()),
VmModifier.export(modifiers, true),
exportedAnnotations,
classInfo,
typeParameters,
properties,
methods);
// if this is not a module class, export this class's module's class first to break the cycle
PClass moduleClass = null;
if (!classInfo.isModuleClass()) {
moduleClass = getModule().getVmClass().export();
}
// then if the cached value is still null, initialize it
if (__pClass != null) {
return __pClass;
}
for (var parameter : typeParameters) {
parameter.initOwner(__pClass);
var exportedAnnotations = new ArrayList<PObject>();
var properties =
CollectionUtils.<String, PClass.Property>newLinkedHashMap(
EconomicMaps.size(declaredProperties));
var methods =
CollectionUtils.<String, PClass.Method>newLinkedHashMap(
EconomicMaps.size(declaredMethods));
// set pClass before exporting class members to prevent
// infinite recursion in case of cyclic references
__pClass =
new PClass(
VmUtils.exportDocComment(docComment),
new SourceLocation(headerSection.getStartLine(), sourceSection.getEndLine()),
VmModifier.export(modifiers, true),
exportedAnnotations,
classInfo,
typeParameters,
properties,
methods,
moduleClass);
for (var parameter : typeParameters) {
parameter.initOwner(__pClass);
}
if (supertypeNode != null) {
assert superclass != null;
__pClass.initSupertype(TypeNode.export(supertypeNode), superclass.export());
}
VmUtils.exportAnnotations(annotations, exportedAnnotations);
for (var property : EconomicMaps.getValues(declaredProperties)) {
if (isClassPropertyDefinition(property)) {
properties.put(property.getName().toString(), property.export(__pClass));
}
}
if (supertypeNode != null) {
assert superclass != null;
__pClass.initSupertype(TypeNode.export(supertypeNode), superclass.export());
}
VmUtils.exportAnnotations(annotations, exportedAnnotations);
for (var property : EconomicMaps.getValues(declaredProperties)) {
if (isClassPropertyDefinition(property)) {
properties.put(property.getName().toString(), property.export(__pClass));
}
}
for (var method : EconomicMaps.getValues(declaredMethods)) {
if (method.isLocal()) continue;
methods.put(method.getName().toString(), method.export(__pClass));
}
for (var method : EconomicMaps.getValues(declaredMethods)) {
if (method.isLocal()) continue;
methods.put(method.getName().toString(), method.export(__pClass));
}
return __pClass;
@@ -107,6 +107,7 @@ public final class VmContext {
}
public void initialize(Holder holder) {
//noinspection ConstantValue
assert this.holder == null;
this.holder = holder;
}
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.Objects;
import org.graalvm.collections.UnmodifiableEconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PClassInfo;
import org.pkl.core.PObject;
import org.pkl.core.ast.member.ObjectMember;
@@ -75,12 +76,14 @@ public final class VmDynamic extends VmObject {
@Override
@TruffleBoundary
public PObject export() {
assert forced : "Value was not forced prior to export";
var properties =
CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues));
iterateMemberValues(
iterateAlreadyForcedMemberValues(
(key, member, value) -> {
properties.put(key.toString(), VmValue.exportNullable(value));
properties.put(key.toString(), VmValue.export(value));
return true;
});
@@ -99,9 +102,8 @@ public final class VmDynamic extends VmObject {
@Override
@TruffleBoundary
public boolean equals(Object obj) {
if (this == obj) // noinspection Contract
return true;
public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof VmDynamic other)) return false;
// could use shallow force, but deep force is cached
@@ -115,6 +117,7 @@ public final class VmDynamic extends VmObject {
if (isHiddenOrLocalProperty(key)) continue;
var value = cursor.getValue();
//noinspection ConstantValue
assert value != null;
var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false;
@@ -137,6 +140,7 @@ public final class VmDynamic extends VmObject {
if (isHiddenOrLocalProperty(key)) continue;
var value = cursor.getValue();
//noinspection ConstantValue
assert value != null;
result += key.hashCode() ^ value.hashCode();
}
@@ -37,7 +37,8 @@ public class VmEvalException extends VmException {
@Nullable SourceSection sourceSection,
@Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames) {
Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super(
message,
@@ -50,6 +51,7 @@ public class VmEvalException extends VmException {
sourceSection,
memberName,
hintBuilder,
insertedStackFrames);
insertedStackFrames,
leadingStackFrames);
}
}
@@ -34,6 +34,7 @@ public abstract class VmException extends AbstractTruffleException {
private final @Nullable SourceSection sourceSection;
private final @Nullable String memberName;
private final Map<CallTarget, StackFrame> insertedStackFrames;
private final List<StackFrame> leadingStackFrames;
@Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder;
@Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder;
@@ -48,7 +49,8 @@ public abstract class VmException extends AbstractTruffleException {
@Nullable SourceSection sourceSection,
@Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames) {
Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super(message, cause, UNLIMITED_STACK_TRACE, location);
assert message != null || messageBuilder != null;
this.messageBuilder = messageBuilder;
@@ -58,6 +60,7 @@ public abstract class VmException extends AbstractTruffleException {
this.sourceSection = sourceSection;
this.memberName = memberName;
this.insertedStackFrames = insertedStackFrames;
this.leadingStackFrames = leadingStackFrames;
this.hintBuilder = hintBuilder;
}
@@ -89,6 +92,14 @@ public abstract class VmException extends AbstractTruffleException {
return insertedStackFrames;
}
/**
* Stack frames to prepend to the rendered stack trace, ahead of the captured frames. Used to show
* source locations that aren't part of the runtime call stack.
*/
public final List<StackFrame> getLeadingStackFrames() {
return leadingStackFrames;
}
public @Nullable BiConsumer<AnsiStringBuilder, Boolean> getMessageBuilder() {
return messageBuilder;
}
@@ -99,6 +99,7 @@ public final class VmExceptionBuilder {
private @Nullable Node location;
private @Nullable SourceSection sourceSection;
private @Nullable String memberName;
private List<StackFrame> leadingStackFrames = List.of();
public VmExceptionBuilder typeMismatch(Object value, VmClass expectedType) {
if (value instanceof VmNull) {
@@ -359,6 +360,15 @@ public final class VmExceptionBuilder {
return this;
}
/**
* Frames to show ahead of the captured stack trace (see {@link
* VmException#getLeadingStackFrames()}).
*/
public VmExceptionBuilder withLeadingStackFrames(List<StackFrame> leadingStackFrames) {
this.leadingStackFrames = leadingStackFrames;
return this;
}
public VmException build() {
if (message != null && messageBuilder != null) {
throw new IllegalStateException("Both message and messageBuilder are set");
@@ -387,7 +397,8 @@ public final class VmExceptionBuilder {
sourceSection,
memberName,
hintBuilder,
effectiveInsertedStackFrames);
effectiveInsertedStackFrames,
leadingStackFrames);
case UNDEFINED_VALUE ->
new VmUndefinedValueException(
message,
@@ -401,7 +412,8 @@ public final class VmExceptionBuilder {
memberName,
hintBuilder,
receiver,
effectiveInsertedStackFrames);
effectiveInsertedStackFrames,
leadingStackFrames);
case BUG ->
new VmBugException(
message,
@@ -414,7 +426,8 @@ public final class VmExceptionBuilder {
sourceSection,
memberName,
hintBuilder,
effectiveInsertedStackFrames);
effectiveInsertedStackFrames,
leadingStackFrames);
case WRAPPED -> {
assert wrappedException != null;
yield new VmWrappedEvalException(
@@ -429,6 +442,7 @@ public final class VmExceptionBuilder {
memberName,
hintBuilder,
effectiveInsertedStackFrames,
leadingStackFrames,
wrappedException);
}
};
@@ -87,13 +87,15 @@ public final class VmListing extends VmListingOrMapping {
@Override
@TruffleBoundary
public List<Object> export() {
assert forced : "Value was not forced prior to export";
var properties = new ArrayList<>(EconomicMaps.size(cachedValues));
iterateMemberValues(
iterateAlreadyForcedMemberValues(
(key, prop, value) -> {
if (isDefaultProperty(key)) return true;
properties.add(VmValue.exportNullable(value));
properties.add(VmValue.export(value));
return true;
});
@@ -127,6 +129,7 @@ public final class VmListing extends VmListingOrMapping {
if (key instanceof Identifier) continue;
var value = cursor.getValue();
//noinspection ConstantValue
assert value != null;
var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false;
@@ -149,6 +152,7 @@ public final class VmListing extends VmListingOrMapping {
if (key instanceof Identifier) continue;
var value = cursor.getValue();
//noinspection ConstantValue
assert value != null;
result = 31 * result + value.hashCode();
}
@@ -147,6 +147,7 @@ public final class VmMap extends VmValue implements Iterable<Map.Entry<Object, O
for (var key : other.keyOrder) {
var value = other.map.get(key);
assert value != null;
if (!map.containsKey(key)) {
keyOrderBuilder.append(key);
@@ -21,19 +21,18 @@ import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.HashSet;
import java.util.Map;
import org.graalvm.collections.UnmodifiableEconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.util.CollectionUtils;
import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
import org.pkl.core.util.MutableLong;
public final class VmMapping extends VmListingOrMapping {
private long cachedLength = -1;
@GuardedBy("this")
@LateInit
private VmSet __allKeys;
private @Nullable VmSet __allKeys;
private static final class EmptyHolder {
private static final VmMapping EMPTY =
@@ -94,13 +93,15 @@ public final class VmMapping extends VmListingOrMapping {
@Override
@TruffleBoundary
public Map<Object, Object> export() {
assert forced : "Value was not forced prior to export";
var properties = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues));
iterateMemberValues(
iterateAlreadyForcedMemberValues(
(key, prop, value) -> {
if (isDefaultProperty(key)) return true;
properties.put(VmValue.export(key), VmValue.exportNullable(value));
properties.put(VmValue.export(key), VmValue.export(value));
return true;
});
@@ -119,9 +120,8 @@ public final class VmMapping extends VmListingOrMapping {
@Override
@TruffleBoundary
public boolean equals(Object obj) {
if (this == obj) // noinspection Contract
return true;
public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof VmMapping other)) return false;
// could use shallow force, but deep force is cached
@@ -135,6 +135,7 @@ public final class VmMapping extends VmListingOrMapping {
if (key instanceof Identifier) continue;
var value = cursor.getValue();
//noinspection ConstantValue
assert value != null;
var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false;
@@ -157,6 +158,7 @@ public final class VmMapping extends VmListingOrMapping {
if (key instanceof Identifier) continue;
var value = cursor.getValue();
//noinspection ConstantValue
assert value != null;
result += key.hashCode() ^ value.hashCode();
}
@@ -34,7 +34,7 @@ public abstract class VmObject extends VmObjectLike {
protected final EconomicMap<Object, Object> cachedValues;
protected int cachedHash;
private boolean forced;
protected boolean forced;
public VmObject(
MaterializedFrame enclosingFrame,
@@ -204,17 +204,20 @@ public abstract class VmObject extends VmObjectLike {
/**
* Exports this object's members. Skips local members, hidden members, class definitions, and type
* aliases. Members that haven't been forced have a `null` value.
* aliases.
*
* <p>Assumes that this object has already been forced.
*/
@TruffleBoundary
protected final Map<String, Object> exportMembers() {
var result = CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues));
Map<String, Object> result = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues));
assert forced : "Value was not forced prior to export";
iterateMemberValues(
iterateAlreadyForcedMemberValues(
(key, member, value) -> {
if (member.isClass() || member.isTypeAlias()) return true;
result.put(key.toString(), VmValue.exportNullable(value));
result.put(key.toString(), VmValue.export(value));
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.
@@ -87,4 +87,9 @@ public final class VmObjectBuilder {
members,
elementCount);
}
public VmTyped toTyped(VmClass clazz) {
return new VmTyped(
VmUtils.createEmptyMaterializedFrame(), clazz.getPrototype(), clazz, members);
}
}
@@ -324,6 +324,22 @@ public class VmPklBinaryEncoder extends AbstractRenderer {
}
}
@Override
public void visitReference(VmReference value) {
try {
packer.packArrayHeader(4);
packCode(PklBinaryCode.REFERENCE);
visit(value.getDomain());
visit(value.getData());
packer.packArrayHeader(value.getPath().size());
for (var access : value.getPath()) {
visit(access);
}
} catch (IOException e) {
throw PklBugException.unreachableCode();
}
}
@Override
protected void visitEntryKey(Object key, boolean isFirst) {
visit(key);
@@ -0,0 +1,493 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.runtime;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.nodes.DirectCallNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.pkl.core.Composite;
import org.pkl.core.PClass;
import org.pkl.core.PClassInfo;
import org.pkl.core.PType;
import org.pkl.core.PklBugException;
import org.pkl.core.Reference;
import org.pkl.core.TypeAlias;
import org.pkl.core.util.paguro.RrbTree;
import org.pkl.core.util.paguro.RrbTree.ImRrbt;
public final class VmReference extends VmValue {
private final VmTyped domain;
private final Object data;
private final ImRrbt<VmTyped> path;
// candidate types can only be: PType.Class, PType.Alias (only preservedAliasTypes),
// PType.StringLiteral, PType.UNKNOWN, or PType.Union (containing only the previous; flattened)
private final PType referentType;
private boolean forced = false;
private static VmTyped newAccess(@Nullable String property, @Nullable Object key) {
return new VmObjectBuilder()
.addProperty(Identifier.PROPERTY, property == null ? VmNull.withoutDefault() : property)
.addProperty(Identifier.KEY, key == null ? VmNull.withoutDefault() : key)
.toTyped(RefModule.getAccessClass());
}
@TruffleBoundary
public VmReference(VmTyped domain, VmClass clazz, Object data) {
this(
domain,
data,
RrbTree.empty(),
normalizeTypes(new PType.Class(clazz.export()), clazz.getModule().getVmClass().export()));
}
public VmReference(VmTyped domain, Object data, ImRrbt<VmTyped> path, PType referentType) {
this.domain = domain;
this.data = data;
this.referentType = referentType;
this.path = path;
}
public VmTyped getDomain() {
return domain;
}
public Object getData() {
return data;
}
public List<VmTyped> getPath() {
return path;
}
public PType getReferentType() {
return referentType;
}
// simplifies a type by:
// * erasing constraints
// * transforming T? into T|Null
// * dereferencing aliases (except for well-known stdlib alias types)
// * flattening unions
// * when moduleClass is supplied, replace PType.MODULE with appropriate PType.Class
// * drop PType.Function and PType.TypeVariable
private static PType normalizeTypes(PType type, PClass moduleClass) {
var types = new HashSet<PType>();
normalizeTypes(type, moduleClass, types);
return minimizeTypes(types);
}
private static PType minimizeTypes(Set<PType> types) {
if (types.size() == 1) return types.iterator().next();
// optimization: unknown allows all references, erase all candidates to only unknown
if (types.contains(PType.UNKNOWN)) return PType.UNKNOWN;
// optimization: All allows all references, erase all candidates to only All
if (containsClass(types, BaseModule.getAnyClass().export()))
return new PType.Class(BaseModule.getAnyClass().export());
var typesList = new ArrayList<>(types);
typesList.sort(Comparator.comparing(Object::toString));
return new PType.Union(typesList);
}
private static void normalizeTypes(PType type, PClass moduleClass, Set<PType> result) {
if (type == PType.UNKNOWN || type == PType.NOTHING || type instanceof PType.StringLiteral) {
result.add(type);
} else if (type instanceof PType.Class clazz) {
if (clazz.getTypeArguments().isEmpty()) {
// if a generic type is used without type arguments, it needs to be normalized so all args
// are unknown; i.e. with bare List/Map/etc. type annotations (via FinalClassTypeNode).
var typeParameterCount = clazz.getPClass().getTypeParameters().size();
result.add(
typeParameterCount == 0
? clazz
: new PType.Class(
clazz.getPClass(), Collections.nCopies(typeParameterCount, PType.UNKNOWN)));
} else {
var typeArgs = new ArrayList<PType>(clazz.getTypeArguments().size());
for (var arg : clazz.getTypeArguments()) {
typeArgs.add(normalizeTypes(arg, moduleClass));
}
result.add(new PType.Class(clazz.getPClass(), typeArgs));
}
} else if (type instanceof PType.Nullable nullable) {
normalizeTypes(nullable.getBaseType(), moduleClass, result);
result.add(new PType.Class(BaseModule.getNullClass().export()));
} else if (type instanceof PType.Constrained constrained) {
normalizeTypes(constrained.getBaseType(), moduleClass, result);
} else if (type instanceof PType.Alias alias) {
if (isPreservedTypeAlias(alias.getTypeAlias())) {
result.add(alias);
} else {
normalizeTypes(alias.getAliasedType(), alias.getTypeAlias().getModuleClass(), result);
}
} else if (type instanceof PType.Union union) {
for (var t : union.getElementTypes()) {
normalizeTypes(t, moduleClass, result);
}
} else if (type == PType.MODULE) {
result.add(new PType.Class(moduleClass));
}
}
private static Iterable<PType> iterateTypes(PType t) {
if (t instanceof PType.Union union) return union.getElementTypes();
return Collections.singleton(t);
}
public @Nullable VmReference withPropertyAccess(Identifier property) {
var propString = property.toString();
return withAccess(
(t, candidates) -> getCandidatePropertyType(t, propString, candidates),
() -> newAccess(property.toString(), null));
}
public @Nullable VmReference withSubscriptAccess(Object key) {
return withAccess(
(t, candidates) -> getCandidateSubscriptType(t, key, candidates),
() -> newAccess(null, key));
}
@TruffleBoundary
private @Nullable VmReference withAccess(
BiConsumer<PType, Set<PType>> checkCandidate, Supplier<VmTyped> makeAccess) {
Set<PType> candidates = new HashSet<>();
for (var t : iterateTypes(referentType)) {
checkCandidate.accept(t, candidates);
}
if (candidates.isEmpty()) {
return null; // no valid access found
}
return new VmReference(domain, data, path.append(makeAccess.get()), minimizeTypes(candidates));
}
@SuppressWarnings("DuplicatedCode")
private static void getCandidatePropertyType(PType type, String property, Set<PType> result) {
if (type == PType.UNKNOWN) {
result.add(type);
return;
}
// restriction: only class types can have their properties referenced
if (!(type instanceof PType.Class clazz)) return;
// restriction: cannot reference properties of external classes
if (clazz.getPClass().isExternal()) return;
if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) {
// restriction: cannot reference Dynamic.default
if (!property.equals("default")) result.add(PType.UNKNOWN);
return;
}
// restriction: cannot reference Listing/Mapping.default
if (clazz.getPClass().getInfo() == PClassInfo.Listing
|| clazz.getPClass().getInfo() == PClassInfo.Mapping) {
return;
}
// restriction: cannot reference Module.output.
// generalized: properties originally defined in external classes; the only extant example.
// This is implemented specifically because this is the only case where an external class
// containing a property can be subclassed.
// And this can't check prop.getOwner().isExternal() because fully overriding the property with
// a new type annotation means the owner isn't Module.
if (clazz.getPClass().isSubclassOf(BaseModule.getModuleClass().export())
&& property.equals("output")) return;
var prop = clazz.getPClass().getAllProperties().get(property);
// restriction: cannot reference external properties
if (prop == null || prop.isExternal()) {
return;
}
normalizeTypes(prop.getType(), clazz.getPClass().getModuleClass(), result);
}
private static PClassInfo<?> getClassInfo(Object value) {
if (value instanceof VmValue vmValue) return vmValue.getVmClass().getPClassInfo();
if (value instanceof String) return PClassInfo.String;
if (value instanceof Boolean) return PClassInfo.Boolean;
if (value instanceof Long) return PClassInfo.Int;
if (value instanceof Double) return PClassInfo.Float;
throw new PklBugException("Not a Pkl value: " + value.getClass());
}
@SuppressWarnings("DuplicatedCode")
private static void getCandidateSubscriptType(PType type, Object key, Set<PType> result) {
if (type == PType.UNKNOWN) {
result.add(type);
return;
}
if (!(type instanceof PType.Class clazz)) {
return;
}
if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) {
result.add(PType.UNKNOWN);
return;
}
if (clazz.getPClass().getInfo() == PClassInfo.Listing
|| clazz.getPClass().getInfo() == PClassInfo.List) {
if (key instanceof Long) {
normalizeTypes(clazz.getTypeArguments().get(0), clazz.getPClass().getModuleClass(), result);
}
return;
}
if (clazz.getPClass().getInfo() == PClassInfo.Mapping
|| clazz.getPClass().getInfo() == PClassInfo.Map) {
var typeArgs = clazz.getTypeArguments();
var keyTypes = normalizeTypes(typeArgs.get(0), clazz.getPClass().getModuleClass());
for (var kt : iterateTypes(keyTypes)) {
if (kt == PType.UNKNOWN
|| (kt instanceof PType.Class klazz && klazz.getPClass().getInfo() == getClassInfo(key))
|| (kt instanceof PType.StringLiteral stringLiteral
&& stringLiteral.getLiteral().equals(key))) {
normalizeTypes(typeArgs.get(1), clazz.getPClass().getModuleClass(), result);
return;
}
}
}
}
/**
* Tells if this reference's referent type is a subtype of {@code type}. Does not check domain.
*/
public boolean referentTypeIsSubtypeOf(PType type, PClass moduleClass) {
// fast path: if referent is unknown it can match any type check
if (referentType == PType.UNKNOWN) {
return true;
}
var checkType = normalizeTypes(type, moduleClass);
// fast path: short circuit if any referent is accepted
if (checkType == PType.UNKNOWN || isClass(checkType, BaseModule.getAnyClass().export())) {
return true;
}
// fast path: short circuit if nothing is accepted
if (checkType == PType.NOTHING) {
return false;
}
return isSubtype(referentType, checkType);
}
private static boolean containsClass(Set<PType> types, PClass pClass) {
for (var t : types) {
if (isClass(t, pClass)) return true;
}
return false;
}
private static boolean isClass(PType t, PClass pClass) {
return t instanceof PType.Class clazz && clazz.getPClass() == pClass;
}
private static boolean isSubtype(PType a, PType b) {
// checks if A is a subtype of B
// cases (A -> B)
// * A == B
// * StringLiteral -> StringLiteral: if literals are the same
// * StringLiteral -> Class: B is String
// * Char Alias -> Char Alias, StringLiteral (known single character)
// * Int Alias -> Class: B is a subtype of Number (Int|Float|Number)
// * Int Alias -> Alias
// * same alias
// * Int8 is Int16|Int32
// * Int16 is Int32
// * UInt8 is Int16|Int32|Uint16|UInt32|UInt
// * UInt16 is Int32|UInt32|UInt
// * UInt32 is UInt
// * Class -> Class: if same class or A is a subclass of B
// * if type args are present, must have equal number of them
// * for each pair of type args, check variance
// * invariant: A_i must be identical to B_i
// * covariant: A_i must be a subtype of B_i
// * contravariant: B_i must be a subtype of A_i
// * Union -> Union: Each elem of A must be a subtype of at least one elem of B
// * Non-union -> Union: A must be a subtype of at least one elem of B
if (a == b) return true;
if (a instanceof PType.StringLiteral aStr) {
if (b instanceof PType.StringLiteral bStr) {
return aStr.getLiteral().equals(bStr.getLiteral());
} else if (b instanceof PType.Class bClass) {
return bClass.getPClass() == BaseModule.getStringClass().export();
}
} else if (a instanceof PType.Alias aAlias) {
var aa = aAlias.getTypeAlias();
if (isIntTypeAlias(aa)) {
// special casing for stdlib Int typealiases
if (b instanceof PType.Class bClass) {
// A is an int alias, B is a Number (sub)class
return bClass.getPClass().isSubclassOf(BaseModule.getNumberClass().export());
} else if (b instanceof PType.Alias bAlias) {
var bb = bAlias.getTypeAlias();
if (aa == bb) {
return true;
}
if (aa == BaseModule.getInt8TypeAlias().export()) {
return bb == BaseModule.getInt16TypeAlias().export()
|| bb == BaseModule.getInt32TypeAlias().export();
} else if (aa == BaseModule.getInt16TypeAlias().export()) {
return bb == BaseModule.getInt32TypeAlias().export();
} else if (aa == BaseModule.getUInt8TypeAlias().export()) {
return bb == BaseModule.getInt16TypeAlias().export()
|| bb == BaseModule.getInt32TypeAlias().export()
|| bb == BaseModule.getUInt16TypeAlias().export()
|| bb == BaseModule.getUInt32TypeAlias().export()
|| bb == BaseModule.getUIntTypeAlias().export();
} else if (aa == BaseModule.getUInt16TypeAlias().export()) {
return bb == BaseModule.getInt32TypeAlias().export()
|| bb == BaseModule.getUInt32TypeAlias().export()
|| bb == BaseModule.getUIntTypeAlias().export();
} else if (aa == BaseModule.getUInt32TypeAlias().export()) {
return bb == BaseModule.getUIntTypeAlias().export();
}
}
}
} else if (a instanceof PType.Class aClass && b instanceof PType.Class bClass) {
if (!aClass.getPClass().isSubclassOf(bClass.getPClass())) {
return false;
}
var aArgs = aClass.getTypeArguments();
var bArgs = bClass.getTypeArguments();
var bParams = bClass.getPClass().getTypeParameters();
if (aArgs.size() != bArgs.size()) {
return false;
}
// check variance of type args pairwise
for (var i = 0; i < aArgs.size(); i++) {
if (!switch (bParams.get(i).getVariance()) {
case INVARIANT -> aArgs.get(i) == bArgs.get(i);
case COVARIANT -> isSubtype(aArgs.get(i), bArgs.get(i));
case CONTRAVARIANT -> isSubtype(bArgs.get(i), aArgs.get(i));
}) {
return false;
}
}
return true;
} else if (b instanceof PType.Union bUnion) {
if (a instanceof PType.Union aUnion) {
a:
for (var aElem : aUnion.getElementTypes()) {
for (var bElem : bUnion.getElementTypes()) {
if (isSubtype(aElem, bElem)) continue a;
}
return false;
}
return true;
} else {
for (var bElem : bUnion.getElementTypes()) {
if (isSubtype(a, bElem)) return true;
}
}
}
return false;
}
private static boolean isIntTypeAlias(TypeAlias t) {
return t == BaseModule.getInt8TypeAlias().export()
|| t == BaseModule.getInt16TypeAlias().export()
|| t == BaseModule.getInt32TypeAlias().export()
|| t == BaseModule.getUInt8TypeAlias().export()
|| t == BaseModule.getUInt16TypeAlias().export()
|| t == BaseModule.getUInt32TypeAlias().export()
|| t == BaseModule.getUIntTypeAlias().export();
}
private static boolean isPreservedTypeAlias(TypeAlias t) {
return isIntTypeAlias(t);
}
@Override
public VmClass getVmClass() {
return RefModule.getReferenceClass();
}
@Override
public void force(boolean allowUndefinedValues) {
if (forced) return;
forced = true;
domain.force(allowUndefinedValues);
VmValue.force(data, allowUndefinedValues);
for (var elem : path) {
elem.force(allowUndefinedValues);
}
}
@Override
public Reference export() {
var pathList = new ArrayList<Composite>(path.size());
for (var elem : path) {
pathList.add(elem.export());
}
return new Reference(domain.export(), VmValue.export(data), pathList, getReferentType());
}
public PType exportType() {
return new PType.Class(
RefModule.getReferenceClass().export(),
new PType.Class(domain.getVmClass().export()),
getReferentType());
}
@Override
public void accept(VmValueVisitor visitor) {
visitor.visitReference(this);
}
@Override
public <T> T accept(VmValueConverter<T> converter, Iterable<Object> path) {
return converter.convertReference(this, path);
}
@Override
public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (!(o instanceof VmReference that)) {
return false;
}
return domain.equals(that.domain)
&& data.equals(that.data)
&& path.equals(that.path)
&& referentType.equals(that.referentType);
}
@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;
}
// in-language calls _should_ all go through `ToStringNode`.
// however, some calls escape through to here currently (e.g. `Listing.join`).
@Override
public String toString() {
var toStringMethod = getVmClass().getDeclaredMethod(Identifier.TO_STRING);
assert toStringMethod != null;
var callNode = DirectCallNode.create(toStringMethod.getCallTarget());
return (String) callNode.call(this, getVmClass().getPrototype());
}
}
@@ -32,6 +32,7 @@ public final class VmStackOverflowException extends VmException {
null,
null,
null,
new HashMap<>());
new HashMap<>(),
List.of());
}
}
@@ -34,7 +34,7 @@ import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode;
import org.pkl.core.ast.type.TypeNode.TypeVariableNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
import org.pkl.core.util.LateInit;
import org.pkl.core.ast.type.UnresolvedTypeNode;
public final class VmTypeAlias extends VmValue {
private final SourceSection sourceSection;
@@ -48,17 +48,15 @@ public final class VmTypeAlias extends VmValue {
private final List<TypeParameter> typeParameters;
private final MaterializedFrame enclosingFrame;
@LateInit private TypeNode typeNode;
private @Nullable TypeNode typeNode;
@LateInit
@GuardedBy("pTypeAliasLock")
private TypeAlias __pTypeAlias;
private @Nullable TypeAlias __pTypeAlias;
private final Object pTypeAliasLock = new Object();
@LateInit
@GuardedBy("mirrorLock")
private VmTyped __mirror;
private @Nullable VmTyped __mirror;
private final Object mirrorLock = new Object();
@@ -86,7 +84,6 @@ public final class VmTypeAlias extends VmValue {
}
public void initTypeCheckNode(TypeNode typeNode) {
assert this.typeNode == null;
this.typeNode = typeNode;
}
@@ -100,6 +97,7 @@ public final class VmTypeAlias extends VmValue {
*/
@TruffleBoundary
public SourceSection getBaseTypeSection() {
assert typeNode != null;
if (typeNode instanceof ConstrainedTypeNode constrainedTypeNode) {
return constrainedTypeNode.getBaseTypeSection();
}
@@ -116,6 +114,7 @@ public final class VmTypeAlias extends VmValue {
*/
@TruffleBoundary
public SourceSection getConstraintSection() {
assert typeNode != null;
if (typeNode instanceof ConstrainedTypeNode) {
return ((ConstrainedTypeNode) typeNode).getFirstConstraintSection();
}
@@ -169,6 +168,7 @@ public final class VmTypeAlias extends VmValue {
}
public TypeNode getTypeNode() {
assert typeNode != null;
return typeNode;
}
@@ -178,14 +178,23 @@ public final class VmTypeAlias extends VmValue {
@TruffleBoundary
public TypeNode instantiate(TypeNode[] typeArgumentNodes) {
assert typeNode != null;
// Cloning the type node means that the entire type check remains within a single root node,
// which should be good for interpreted and compiled performance alike:
// * Fewer root nodes to call
// * ControlFlowException used to implement union types doesn't escape root node
if (typeParameters.isEmpty()) return (TypeNode) typeNode.deepCopy();
// handle if alias root is itself a type variable (https://github.com/apple/pkl/issues/1711)
if (typeNode instanceof TypeVariableNode typeVarNode) {
// no need to run validation since the arg itself has already been checked
return typeArgumentNodes.length == 0
? new UnknownTypeNode(sourceSection)
: typeArgumentNodes[typeVarNode.getTypeParameterIndex()];
}
var clone = (TypeNode) typeNode.deepCopy();
if (typeParameters.isEmpty()) return clone;
clone.accept(
node -> {
if (node instanceof TypeVariableNode typeVarNode) {
@@ -196,6 +205,15 @@ public final class VmTypeAlias extends VmValue {
typeArgumentNodes.length == 0
? new UnknownTypeNode(sourceSection)
: typeArgumentNodes[index]);
} else if (node instanceof UnresolvedTypeNode.TypeVariable unresolvedTypeVar) {
// Type variables inside constraint expressions (e.g. `every((it) -> it is T)`)
// are still unresolved at instantiation time. Replace them with a resolved
// unresolved type node that returns the concrete type argument.
var index = unresolvedTypeVar.getTypeParameterIndex();
node.replace(
typeArgumentNodes.length == 0
? new UnresolvedTypeNode.Unknown(sourceSection)
: new UnresolvedTypeNode.Resolved(sourceSection, typeArgumentNodes[index]));
}
return true;
});
@@ -228,7 +246,8 @@ public final class VmTypeAlias extends VmValue {
simpleName,
getModuleName(),
qualifiedName,
typeParameters);
typeParameters,
module.getVmClass().export());
for (var parameter : typeParameters) {
parameter.initOwner(__pTypeAlias);
@@ -264,6 +283,7 @@ public final class VmTypeAlias extends VmValue {
}
public VmTyped getTypeMirror() {
assert typeNode != null;
return typeNode.getMirror();
}
@@ -29,10 +29,9 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.expression.unary.ImportNode;
import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
public final class VmTyped extends VmObject {
@CompilationFinal @LateInit private VmClass clazz;
@CompilationFinal private @Nullable VmClass clazz;
public VmTyped(
MaterializedFrame enclosingFrame,
@@ -162,6 +161,8 @@ public final class VmTyped extends VmObject {
@Override
@TruffleBoundary
public Composite export() {
assert forced : "Value was not forced prior to export";
assert clazz != null;
if (!isModuleObject()) {
return new PObject(clazz.getPClassInfo(), exportMembers());
}
@@ -190,6 +191,7 @@ public final class VmTyped extends VmObject {
if (this == obj) return true;
if (!(obj instanceof VmTyped other)) return false;
assert clazz != null;
if (clazz != other.clazz) return false;
// could use shallow force, but deep force is cached
force(false);
@@ -210,6 +212,7 @@ public final class VmTyped extends VmObject {
public int hashCode() {
if (cachedHash != 0) return cachedHash;
assert clazz != null;
force(false);
var result = 0;
@@ -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.
@@ -40,6 +40,7 @@ import com.oracle.truffle.api.dsl.TypeSystem;
VmRegex.class,
VmTypeAlias.class,
VmObjectLike.class,
VmValue.class
VmReference.class,
VmValue.class,
})
public class VmTypes {}
@@ -43,7 +43,8 @@ public final class VmUndefinedValueException extends VmEvalException {
@Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
@Nullable Object receiver,
@Nullable Map<CallTarget, StackFrame> insertedStackFrames) {
@Nullable Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super(
message,
@@ -56,7 +57,8 @@ public final class VmUndefinedValueException extends VmEvalException {
sourceSection,
memberName,
hintBuilder,
insertedStackFrames == null ? Collections.emptyMap() : insertedStackFrames);
insertedStackFrames == null ? Collections.emptyMap() : insertedStackFrames,
leadingStackFrames);
this.receiver = receiver;
}
@@ -85,6 +85,8 @@ public interface VmValueConverter<T> {
T convertFunction(VmFunction value, Iterable<Object> path);
T convertReference(VmReference value, Iterable<Object> path);
/** Returns with an empty identifier if the second value is a RenderDirective */
Pair<Identifier, T> convertProperty(ClassProperty property, Object value, Iterable<Object> path);
@@ -271,6 +271,30 @@ public final class VmValueRenderer {
append("null");
}
@Override
public void visitReference(VmReference value) {
contexts.push(Context.EXPLICIT);
append("Reference(");
visit(value.getDomain());
append(", ");
append(value.getReferentType());
append(", ");
visit(value.getData());
append(")");
for (var elem : value.getPath()) {
var property = VmUtils.readMember(elem, Identifier.PROPERTY);
if (property instanceof String propName) {
append(".");
writeIdentifier(propName);
} else {
append("[");
visit(VmUtils.readMember(elem, Identifier.KEY));
append("]");
}
}
contexts.pop();
}
private void append(Object value) {
builder.append(value);
checkLengthLimit();
@@ -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.
@@ -60,6 +60,8 @@ public interface VmValueVisitor {
void visitFunction(VmFunction value);
void visitReference(VmReference value);
default void visit(Object value) {
Objects.requireNonNull(value, "Value to be visited must be non-null.");
@@ -41,6 +41,7 @@ public class VmWrappedEvalException extends VmEvalException {
@Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames,
VmException wrappedException) {
super(
message,
@@ -53,7 +54,8 @@ public class VmWrappedEvalException extends VmEvalException {
sourceSection,
memberName,
hintBuilder,
insertedStackFrames);
insertedStackFrames,
leadingStackFrames);
this.wrappedException = wrappedException;
}
@@ -34,6 +34,7 @@ import org.pkl.core.runtime.VmListing;
import org.pkl.core.runtime.VmMap;
import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmNull;
import org.pkl.core.runtime.VmReference;
import org.pkl.core.runtime.VmSet;
import org.pkl.core.runtime.VmTypeAlias;
import org.pkl.core.runtime.VmTyped;
@@ -397,6 +398,11 @@ public abstract class AbstractRenderer implements VmValueVisitor {
currSourceSection = prevSourceSection;
}
@Override
public void visitReference(VmReference value) {
cannotRenderTypeAddConverter(value);
}
protected void cannotRenderTypeAddConverter(VmValue value) {
var builder =
new VmExceptionBuilder()

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