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 runs-on: nothing
steps: steps:
- name: EnricoMi/publish-unit-test-result-action@v2 - 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 - name: actions/checkout@v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: actions/create-github-app-token@v2 - name: actions/create-github-app-token@v2
@@ -25,7 +25,7 @@ jobs:
- name: actions/download-artifact@v6 - name: actions/download-artifact@v6
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- name: actions/setup-java@v5 - name: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
- name: actions/upload-artifact@v5 - name: actions/upload-artifact@v5
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- name: dawidd6/action-download-artifact@v11 - name: dawidd6/action-download-artifact@v11
+18 -18
View File
@@ -23,7 +23,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -64,7 +64,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -103,7 +103,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -130,7 +130,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -170,7 +170,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -220,7 +220,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -266,7 +266,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -317,7 +317,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -363,7 +363,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -411,7 +411,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -529,7 +529,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -576,7 +576,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -622,7 +622,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -673,7 +673,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -719,7 +719,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -767,7 +767,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -885,7 +885,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -949,7 +949,7 @@ jobs:
pattern: test-results-xml-* pattern: test-results-xml-*
- name: Publish test results - name: Publish test results
if: '!cancelled()' if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2 uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with: with:
comment_mode: 'off' comment_mode: 'off'
files: test-results-xml-*/**/*.xml files: test-results-xml-*/**/*.xml
+20 -20
View File
@@ -21,7 +21,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -62,7 +62,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -101,7 +101,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -128,7 +128,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -168,7 +168,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -218,7 +218,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -264,7 +264,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -315,7 +315,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -361,7 +361,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -409,7 +409,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -527,7 +527,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -574,7 +574,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -620,7 +620,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -671,7 +671,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -717,7 +717,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -765,7 +765,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -883,7 +883,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -946,7 +946,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -983,7 +983,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -1018,7 +1018,7 @@ jobs:
pattern: test-results-xml-* pattern: test-results-xml-*
- name: Publish test results - name: Publish test results
if: '!cancelled()' if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2 uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with: with:
comment_mode: 'off' comment_mode: 'off'
files: test-results-xml-*/**/*.xml files: test-results-xml-*/**/*.xml
+14 -14
View File
@@ -17,7 +17,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -60,7 +60,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -103,7 +103,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -151,7 +151,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -203,7 +203,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -251,7 +251,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -301,7 +301,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -421,7 +421,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -469,7 +469,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -517,7 +517,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -569,7 +569,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -617,7 +617,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -667,7 +667,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -787,7 +787,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
+18 -18
View File
@@ -21,7 +21,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -62,7 +62,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -101,7 +101,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -128,7 +128,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -168,7 +168,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -218,7 +218,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -264,7 +264,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -315,7 +315,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -361,7 +361,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -409,7 +409,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -527,7 +527,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -574,7 +574,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -620,7 +620,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -671,7 +671,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -717,7 +717,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -765,7 +765,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -883,7 +883,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -947,7 +947,7 @@ jobs:
pattern: test-results-xml-* pattern: test-results-xml-*
- name: Publish test results - name: Publish test results
if: '!cancelled()' if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2 uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with: with:
comment_mode: 'off' comment_mode: 'off'
files: test-results-xml-*/**/*.xml files: test-results-xml-*/**/*.xml
+19 -19
View File
@@ -21,7 +21,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -62,7 +62,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -101,7 +101,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -128,7 +128,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -168,7 +168,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -219,7 +219,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -266,7 +266,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -318,7 +318,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -365,7 +365,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -414,7 +414,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -533,7 +533,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -581,7 +581,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -628,7 +628,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -680,7 +680,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -727,7 +727,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -776,7 +776,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -895,7 +895,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -959,7 +959,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
with: with:
java-version: '25' java-version: '25'
distribution: temurin distribution: temurin
@@ -1043,7 +1043,7 @@ jobs:
pattern: test-results-xml-* pattern: test-results-xml-*
- name: Publish test results - name: Publish test results
if: '!cancelled()' if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2 uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with: with:
comment_mode: 'off' comment_mode: 'off'
files: test-results-xml-*/**/*.xml files: test-results-xml-*/**/*.xml
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
name_is_regexp: true name_is_regexp: true
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
- name: Publish test results - name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2 uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
with: with:
commit: ${{ github.event.workflow_run.head_sha }} commit: ${{ github.event.workflow_run.head_sha }}
comment_mode: 'off' comment_mode: 'off'
+78 -19
View File
@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import java.nio.file.AtomicMoveNotSupportedException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import java.util.* import java.util.*
import java.util.zip.ZipInputStream
import javax.inject.Inject import javax.inject.Inject
import kotlin.io.path.createDirectories import kotlin.io.path.createDirectories
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
@@ -41,19 +44,14 @@ constructor(
@TaskAction @TaskAction
@Suppress("unused") @Suppress("unused")
fun run() { 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()) val distroDir = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
try { try {
distroDir.createDirectories() distroDir.createDirectories()
println("Extracting ${graalVm.get().downloadFile} into $distroDir") 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() val os = org.gradle.internal.os.OperatingSystem.current()
if (os.isWindows) extractZip(distroDir) else extractTarGz(distroDir)
val distroBinDir = val distroBinDir =
if (os.isMacOsX) distroDir.resolve("Contents/Home/bin") else distroDir.resolve("bin") 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") publishInstallDir(distroDir, os)
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
}
} catch (e: Exception) { } catch (e: Exception) {
try { try {
fileOperations.delete(distroDir) fileOperations.delete(distroDir)
@@ -88,4 +76,75 @@ constructor(
throw e 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-baseModule: {uri-pkl-stdlib-docs}/base
:uri-stdlib-CommandModule: {uri-pkl-stdlib-docs}/Command :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-analyzeModule: {uri-pkl-stdlib-docs}/analyze
:uri-stdlib-jsonnetModule: {uri-pkl-stdlib-docs}/jsonnet :uri-stdlib-jsonnetModule: {uri-pkl-stdlib-docs}/jsonnet
:uri-stdlib-reflectModule: {uri-pkl-stdlib-docs}/reflect :uri-stdlib-reflectModule: {uri-pkl-stdlib-docs}/reflect
@@ -160,6 +161,8 @@ endif::[]
:uri-stdlib-Command-CountedFlag: {uri-stdlib-CommandModule}/CountedFlag :uri-stdlib-Command-CountedFlag: {uri-stdlib-CommandModule}/CountedFlag
:uri-stdlib-Command-Argument: {uri-stdlib-CommandModule}/Argument :uri-stdlib-Command-Argument: {uri-stdlib-CommandModule}/Argument
:uri-stdlib-Command-Import: {uri-stdlib-CommandModule}/Import :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: https://msgpack.org/index.html
:uri-messagepack-spec: https://github.com/msgpack/msgpack/blob/master/spec.md :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]] [[type-name-encoding]]
[NOTE] [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" clikt = "5.0.3"
commonMark = "0.28.0" commonMark = "0.28.0"
downloadTaskPlugin = "5.7.0" downloadTaskPlugin = "5.7.0"
errorProne = "2.49.0" errorProne = "2.50.0"
errorPronePlugin = "5.1.0" errorPronePlugin = "5.1.0"
geantyref = "2.0.1" geantyref = "2.0.1"
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
@@ -32,7 +32,7 @@ ideaExtPlugin = "1.4.1"
javaPoet = "0.16.0" javaPoet = "0.16.0"
javaxInject = "1" javaxInject = "1"
jimfs = "1.3.1" jimfs = "1.3.1"
jline = "4.1.3" jline = "4.2.1"
jmh = "1.37" jmh = "1.37"
jmhPlugin = "0.7.3" jmhPlugin = "0.7.3"
jspecify = "1.0.0" jspecify = "1.0.0"
@@ -54,14 +54,14 @@ ktfmt = "0.62"
log4j = "2.17.1" log4j = "2.17.1"
msgpack = "0.9.12" msgpack = "0.9.12"
nexusPublishPlugin = "2.0.0" nexusPublishPlugin = "2.0.0"
nullaway = "0.13.4" nullaway = "0.13.7"
nullawayPlugin = "3.0.0" nullawayPlugin = "3.0.0"
nuValidator = "26.5.29" nuValidator = "26.6.24"
paguro = "3.10.3" paguro = "3.10.3"
shadowPlugin = "9.4.2" shadowPlugin = "9.4.2"
slf4j = "2.0.18" slf4j = "2.0.18"
snakeYaml = "3.0.1" snakeYaml = "3.0.1"
spotlessPlugin = "8.6.0" spotlessPlugin = "8.7.0"
wiremock = "3.13.2" wiremock = "3.13.2"
[libraries] # ordered alphabetically [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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -69,11 +69,11 @@ public record ImportGraph(Map<URI, Set<Import>> imports, Map<URI, URI> resolvedI
var value = entry.getValue(); var value = entry.getValue();
var set = new TreeSet<Import>(); var set = new TreeSet<Import>();
if (!(value instanceof JsArray array)) { 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) { for (var elem : array) {
if (!(elem instanceof JsObject importObj)) { 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)); 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 key = new URI(entry.getKey());
var value = entry.getValue(); var value = entry.getValue();
if (!(value instanceof String str)) { 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); var valueUri = new URI(str);
ret.put(key, valueUri); 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)); "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 @Override
public void visitPair(Pair<?, ?> value) { public void visitPair(Pair<?, ?> value) {
try { try {
@@ -18,7 +18,6 @@ package org.pkl.core;
import java.net.URI; import java.net.URI;
import java.util.*; import java.util.*;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.util.LateInit;
/** Describes the property, method and class members of a module. */ /** Describes the property, method and class members of a module. */
public final class ModuleSchema { public final class ModuleSchema {
@@ -33,8 +32,8 @@ public final class ModuleSchema {
private final Map<String, TypeAlias> typeAliases; private final Map<String, TypeAlias> typeAliases;
private final Map<String, URI> imports; private final Map<String, URI> imports;
@LateInit private Map<String, PClass> __allClasses; private @Nullable Map<String, PClass> __allClasses;
@LateInit private Map<String, TypeAlias> __allTypeAliases; private @Nullable Map<String, TypeAlias> __allTypeAliases;
/** Constructs a {@code ModuleSchema} instance. */ /** Constructs a {@code ModuleSchema} instance. */
public ModuleSchema( public ModuleSchema(
@@ -27,6 +27,7 @@ public final class PClass extends Member implements Value {
private final List<TypeParameter> typeParameters; private final List<TypeParameter> typeParameters;
private final Map<String, Property> properties; private final Map<String, Property> properties;
private final Map<String, Method> methods; private final Map<String, Method> methods;
private final @Nullable PClass moduleClass;
private @Nullable PType supertype; private @Nullable PType supertype;
private @Nullable PClass superclass; private @Nullable PClass superclass;
@@ -42,12 +43,14 @@ public final class PClass extends Member implements Value {
PClassInfo<?> classInfo, PClassInfo<?> classInfo,
List<TypeParameter> typeParameters, List<TypeParameter> typeParameters,
Map<String, Property> properties, Map<String, Property> properties,
Map<String, Method> methods) { Map<String, Method> methods,
@Nullable PClass moduleClass) {
super(docComment, sourceLocation, modifiers, annotations, classInfo.getSimpleName()); super(docComment, sourceLocation, modifiers, annotations, classInfo.getSimpleName());
this.classInfo = classInfo; this.classInfo = classInfo;
this.typeParameters = typeParameters; this.typeParameters = typeParameters;
this.properties = properties; this.properties = properties;
this.methods = methods; this.methods = methods;
this.moduleClass = moduleClass;
} }
public void initSupertype(PType supertype, PClass superclass) { 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 * 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() { public String getModuleName() {
return classInfo.getModuleName(); return classInfo.getModuleName();
@@ -119,6 +122,11 @@ public final class PClass extends Member implements Value {
return allMethods; 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 @Override
public void accept(ValueVisitor visitor) { public void accept(ValueVisitor visitor) {
visitor.visitClass(this); visitor.visitClass(this);
@@ -138,6 +146,10 @@ public final class PClass extends Member implements Value {
return getDisplayName(); return getDisplayName();
} }
public boolean isSubclassOf(PClass other) {
return this == other || getSuperclass() != null && getSuperclass().isSubclassOf(other);
}
public abstract static class ClassMember extends Member { public abstract static class ClassMember extends Member {
@Serial private static final long serialVersionUID = 0L; @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 pklSemverUri = URI.create("pkl:semver");
public static final URI pklSettingsUri = URI.create("pkl:settings"); public static final URI pklSettingsUri = URI.create("pkl:settings");
public static final URI pklProjectUri = URI.create("pkl:Project"); 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<Void> Any = pklBaseClassInfo("Any", Void.class);
public static final PClassInfo<PNull> Null = pklBaseClassInfo("Null", PNull.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 = public static final PClassInfo<PObject> Version =
new PClassInfo<>("pkl.semver", "Version", PObject.class, pklSemverUri); new PClassInfo<>("pkl.semver", "Version", PObject.class, pklSemverUri);
public static final PClassInfo<PObject> Project = 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 = 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 = public static final PClassInfo<Object> Unavailable =
new PClassInfo<>("unavailable", "unavailable", Object.class, URI.create("pkl: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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.pkl.core;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
/** A Pkl type as used in type annotations. */ /** A Pkl type as used in type annotations. */
public abstract class PType implements Serializable { public abstract class PType implements Serializable {
@@ -27,18 +28,33 @@ public abstract class PType implements Serializable {
public static final PType UNKNOWN = public static final PType UNKNOWN =
new PType() { new PType() {
@Serial private static final long serialVersionUID = 0L; @Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "unknown";
}
}; };
/** The bottom type. */ /** The bottom type. */
public static final PType NOTHING = public static final PType NOTHING =
new PType() { new PType() {
@Serial private static final long serialVersionUID = 0L; @Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "nothing";
}
}; };
/** The type of the enclosing module. */ /** The type of the enclosing module. */
public static final PType MODULE = public static final PType MODULE =
new PType() { new PType() {
@Serial private static final long serialVersionUID = 0L; @Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "module";
}
}; };
private PType() {} private PType() {}
@@ -59,6 +75,22 @@ public abstract class PType implements Serializable {
public String getLiteral() { public String getLiteral() {
return literal; 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 { public static final class Class extends PType {
@@ -92,6 +124,31 @@ public abstract class PType implements Serializable {
public List<PType> getTypeArguments() { public List<PType> getTypeArguments() {
return typeArguments; 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 { public static final class Nullable extends PType {
@@ -106,6 +163,24 @@ public abstract class PType implements Serializable {
public PType getBaseType() { public PType getBaseType() {
return baseType; 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 { public static final class Constrained extends PType {
@@ -126,6 +201,29 @@ public abstract class PType implements Serializable {
public List<String> getConstraints() { public List<String> getConstraints() {
return constraints; 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 { public static final class Alias extends PType {
@@ -161,6 +259,31 @@ public abstract class PType implements Serializable {
public PType getAliasedType() { public PType getAliasedType() {
return aliasedType; 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 { public static final class Function extends PType {
@@ -181,6 +304,27 @@ public abstract class PType implements Serializable {
public PType getReturnType() { public PType getReturnType() {
return returnType; 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 { public static final class Union extends PType {
@@ -195,6 +339,22 @@ public abstract class PType implements Serializable {
public List<PType> getElementTypes() { public List<PType> getElementTypes() {
return elementTypes; 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 { public static final class TypeVariable extends PType {
@@ -213,5 +373,21 @@ public abstract class PType implements Serializable {
public TypeParameter getTypeParameter() { public TypeParameter getTypeParameter() {
return typeParameter; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -227,9 +227,7 @@ final class PcfRenderer implements ValueRenderer {
write(currIndent); write(currIndent);
writeIdentifier(name); writeIdentifier(name);
if (value == null) { // unevaluated property if (value instanceof Composite) {
write(" = ?");
} else if (value instanceof Composite) {
write(' '); write(' ');
visit(value); visit(value);
} else { } 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,7 +21,9 @@ import java.io.UncheckedIOException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.jspecify.annotations.Nullable;
import org.msgpack.core.MessagePack; import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker; import org.msgpack.core.MessageUnpacker;
import org.pkl.core.runtime.BaseModule; import org.pkl.core.runtime.BaseModule;
@@ -111,7 +113,7 @@ public class PklBinaryDecoder extends AbstractPklBinaryDecoder {
@Override @Override
protected Object doDecodeMap(MapDecodeIterator iter) { protected Object doDecodeMap(MapDecodeIterator iter) {
var map = CollectionUtils.newLinkedHashMap(iter.getSize()); Map<@Nullable Object, @Nullable Object> map = CollectionUtils.newLinkedHashMap(iter.getSize());
while (iter.hasNext()) { while (iter.hasNext()) {
var entry = iter.next(); var entry = iter.next();
map.put(entry.getFirst(), entry.getSecond()); map.put(entry.getFirst(), entry.getSecond());
@@ -54,6 +54,9 @@ final class PropertiesRenderer implements ValueRenderer {
} else if (value instanceof Map<?, ?> map) { } else if (value instanceof Map<?, ?> map) {
doVisitMap(null, map); doVisitMap(null, map);
} else if (value instanceof Pair<?, ?> pair) { } 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()); doVisitKeyAndValue(null, pair.getFirst(), pair.getSecond());
} else { } else {
throw new RendererException( throw new RendererException(
@@ -177,6 +180,13 @@ final class PropertiesRenderer implements ValueRenderer {
"Values of type `Regex` cannot be rendered as Properties. Value: %s", value)); "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) { private void doVisitMap(@Nullable String keyPrefix, Map<?, ?> map) {
for (Map.Entry<?, ?> entry : map.entrySet()) { for (Map.Entry<?, ?> entry : map.entrySet()) {
doVisitKeyAndValue(keyPrefix, entry.getKey(), entry.getValue()); 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 moduleName;
private final String qualifiedName; private final String qualifiedName;
private final List<TypeParameter> typeParameters; private final List<TypeParameter> typeParameters;
private final PClass moduleClass;
@LateInit private PType aliasedType; @LateInit private PType aliasedType;
@@ -39,11 +40,13 @@ public final class TypeAlias extends Member implements Value {
String simpleName, String simpleName,
String moduleName, String moduleName,
String qualifiedName, String qualifiedName,
List<TypeParameter> typeParameters) { List<TypeParameter> typeParameters,
PClass moduleClass) {
super(docComment, sourceLocation, modifiers, annotations, simpleName); super(docComment, sourceLocation, modifiers, annotations, simpleName);
this.moduleName = moduleName; this.moduleName = moduleName;
this.qualifiedName = qualifiedName; this.qualifiedName = qualifiedName;
this.typeParameters = typeParameters; this.typeParameters = typeParameters;
this.moduleClass = moduleClass;
} }
public void initAliasedType(PType type) { public void initAliasedType(PType type) {
@@ -79,6 +82,10 @@ public final class TypeAlias extends Member implements Value {
return typeParameters; return typeParameters;
} }
public PClass getModuleClass() {
return moduleClass;
}
/** Returns the type that this type alias stands for. */ /** Returns the type that this type alias stands for. */
public PType getAliasedType() { public PType getAliasedType() {
//noinspection ConstantValue //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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.pkl.core;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; 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. */ /** A type parameter of a generic class, type alias, or method. */
public final class TypeParameter implements Serializable { public final class TypeParameter implements Serializable {
@@ -27,7 +27,7 @@ public final class TypeParameter implements Serializable {
private final String name; private final String name;
private final int index; private final int index;
@LateInit private volatile Member owner; private volatile @Nullable Member owner;
public TypeParameter(Variance variance, String name, int index) { public TypeParameter(Variance variance, String name, int index) {
this.variance = variance; 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. */ /** Returns the generic class, type alias, or method that this type parameter belongs to. */
public Member getOwner() { public Member getOwner() {
assert owner != null; assert owner != null;
//noinspection DataFlowIssue
return owner; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -57,6 +57,8 @@ public interface ValueConverter<T> {
T convertRegex(Pattern value); T convertRegex(Pattern value);
T convertReference(Reference value);
default T convert(Object value) { default T convert(Object value) {
if (value instanceof Value v) { if (value instanceof Value v) {
return (v.accept(this)); return (v.accept(this));
@@ -93,6 +93,10 @@ public interface ValueVisitor {
visitDefault(value); visitDefault(value);
} }
default void visitReference(Reference value) {
visitDefault(value);
}
default void visit(Object value) { default void visit(Object value) {
if (value instanceof Value v) { if (value instanceof Value v) {
v.accept(this); v.accept(this);
@@ -18,7 +18,6 @@ package org.pkl.core;
import java.util.*; import java.util.*;
import java.util.regex.*; import java.util.regex.*;
import org.jspecify.annotations.Nullable; 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>. * 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 preRelease;
private final @Nullable String build; private final @Nullable String build;
@LateInit private volatile Identifier[] __preReleaseIdentifiers; private volatile Identifier @Nullable [] __preReleaseIdentifiers;
/** Constructs a semantic version. */ /** Constructs a semantic version. */
public Version( public Version(
@@ -230,6 +229,7 @@ public final class Version implements Comparable<Version> {
: new Identifier(-1, str)) : new Identifier(-1, str))
.toArray(Identifier[]::new); .toArray(Identifier[]::new);
} }
//noinspection DataFlowIssue
return __preReleaseIdentifiers; return __preReleaseIdentifiers;
} }
@@ -724,7 +724,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
// elem // elem
// } // }
// } // }
return p.levelsUp() == 0 return p.levelsUp() == 0 && !p.needsFrameSkip()
? ReadExactFrameSlotNodeGen.create(sourceSection, p.slot()) ? ReadExactFrameSlotNodeGen.create(sourceSection, p.slot())
: ReadFrameSlotNodeGen.create( : ReadFrameSlotNodeGen.create(
sourceSection, p.slot(), new GetEnclosingFrameNode(p.levelsUp())); sourceSection, p.slot(), new GetEnclosingFrameNode(p.levelsUp()));
@@ -1476,8 +1476,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
var scope = (ModuleScope) symbolTable.getCurrentScope(); var scope = (ModuleScope) symbolTable.getCurrentScope();
scope.setModifiers(modifiers); scope.setModifiers(modifiers);
checkAbstractMembersAllowed(modifiers, mod.getProperties(), mod.getMethods());
// visit imports first so that we already have the object member name available // visit imports first so that we already have the object member name available
var imports = mod.getImports(); var imports = mod.getImports();
var importMembers = new ObjectMember[imports.size()]; 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<ClassProperty> properties = bodyNode != null ? bodyNode.getProperties() : List.of();
List<ClassMethod> methods = bodyNode != null ? bodyNode.getMethods() : List.of(); List<ClassMethod> methods = bodyNode != null ? bodyNode.getMethods() : List.of();
registerClassScopeNames(scope, properties, methods); registerClassScopeNames(scope, properties, methods);
checkAbstractMembersAllowed(modifiers, properties, methods);
var supertypeCtx = clazz.getSuperClass(); 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( private UnresolvedPropertyNode[] doVisitClassProperties(
List<ClassProperty> propertyContexts, Set<String> propertyNames) { List<ClassProperty> propertyContexts, Set<String> propertyNames) {
var propertyNodes = new UnresolvedPropertyNode[propertyContexts.size()]; var propertyNodes = new UnresolvedPropertyNode[propertyContexts.size()];
@@ -509,6 +509,7 @@ public final class SymbolTable {
private @Nullable <R> R resolveLexical(ResolutionFunction<R> fun) { private @Nullable <R> R resolveLexical(ResolutionFunction<R> fun) {
var levelsUp = 0; var levelsUp = 0;
var shouldSkip = false; var shouldSkip = false;
var skippedObjectScope = false;
for (var scope = this; scope != null; scope = scope.getParent()) { for (var scope = this; scope != null; scope = scope.getParent()) {
// for headers resolve variables one scope up // for headers resolve variables one scope up
if (scope instanceof EagerGeneratorScope) { if (scope instanceof EagerGeneratorScope) {
@@ -524,11 +525,24 @@ public final class SymbolTable {
if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) { if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) {
levelsUp++; 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; shouldSkip = false;
continue; continue;
} }
var result = fun.apply(lex, levelsUp); 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 if (scope instanceof MethodScope
|| scope instanceof ForGeneratorScope || scope instanceof ForGeneratorScope
|| scope instanceof LetExpressionScope) { || scope instanceof LetExpressionScope) {
@@ -798,7 +812,7 @@ public final class SymbolTable {
return null; return null;
} }
if (name.equals(binding.name())) { if (name.equals(binding.name())) {
return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp); return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp, false);
} }
return null; return null;
} }
@@ -910,10 +924,10 @@ public final class SymbolTable {
@Override @Override
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) { public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
if (keyBinding != null && keyBinding.name().equals(name)) { 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)) { if (valueBinding != null && valueBinding.name().equals(name)) {
return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp); return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp, false);
} }
return null; return null;
} }
@@ -34,7 +34,8 @@ public sealed interface VariableResolution {
// method, lambda, object body param // method, lambda, object body param
record Parameter(int slot, int levelsUp) implements VariableResolution {} 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 // Implicit base module lookup
record ImplicitBaseProperty() implements VariableResolution {} record ImplicitBaseProperty() implements VariableResolution {}
@@ -100,6 +100,20 @@ public abstract class SubscriptNode extends BinaryExpressionNode {
return readMember(dynamic, key, callNode); 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 @Specialization
protected long eval(VmBytes receiver, long index) { protected long eval(VmBytes receiver, long index) {
if (index < 0 || index >= receiver.getLength()) { 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -20,19 +20,19 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.UInt8TypeAliasTypeNode; import org.pkl.core.ast.type.TypeNode.UInt8TypeAliasTypeNode;
import org.pkl.core.ast.type.VmTypeMismatchException; import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.VmBytes; import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmUtils; import org.pkl.core.runtime.VmUtils;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "Bytes()") @NodeInfo(shortName = "Bytes()")
public final class BytesLiteralNode extends ExpressionNode { public final class BytesLiteralNode extends ExpressionNode {
@Children private final ExpressionNode[] elements; @Children private final ExpressionNode[] elements;
@Child @LateInit private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
public BytesLiteralNode(SourceSection sourceSection, ExpressionNode[] elements) { public BytesLiteralNode(SourceSection sourceSection, ExpressionNode[] elements) {
super(sourceSection); 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.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
@@ -34,7 +35,7 @@ public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionN
protected final int levelsUp; protected final int levelsUp;
private final boolean needsConst; private final boolean needsConst;
@Children private ExpressionNode[] argumentNodes; @Children private ExpressionNode[] argumentNodes;
@Child private DirectCallNode callNode; @Child private @Nullable DirectCallNode callNode;
@CompilationFinal protected boolean isConstChecked; @CompilationFinal protected boolean isConstChecked;
protected AbstractInvokeMethodLexicalNode( protected AbstractInvokeMethodLexicalNode(
@@ -90,6 +91,7 @@ public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionN
callNode = DirectCallNode.create(getCallTarget(owner)); callNode = DirectCallNode.create(getCallTarget(owner));
insert(callNode); insert(callNode);
} }
assert callNode != null;
return callNode; return callNode;
} }
} }
@@ -19,17 +19,17 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
/** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */ /** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */
public final class InferParentWithinMethodNode extends ExpressionNode { public final class InferParentWithinMethodNode extends ExpressionNode {
private final VmLanguage language; private final VmLanguage language;
private final Identifier methodName; private final Identifier methodName;
@Child private ExpressionNode ownerNode; @Child private @Nullable ExpressionNode ownerNode;
@CompilationFinal @LateInit private Object inferredParent; @CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinMethodNode( public InferParentWithinMethodNode(
SourceSection sourceSection, SourceSection sourceSection,
@@ -52,6 +52,7 @@ public final class InferParentWithinMethodNode extends ExpressionNode {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame); var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
assert owner.isPrototype(); 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.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ObjectMethodNode; import org.pkl.core.ast.member.ObjectMethodNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; 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.VmDynamic;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
import org.pkl.core.util.LateInit;
/** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */ /** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */
public final class InferParentWithinObjectMethodNode extends ExpressionNode { public final class InferParentWithinObjectMethodNode extends ExpressionNode {
private final VmLanguage language; private final VmLanguage language;
private final Identifier localMethodName; private final Identifier localMethodName;
@Child private ExpressionNode ownerNode; @Child private @Nullable ExpressionNode ownerNode;
@CompilationFinal @LateInit private Object inferredParent; @CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinObjectMethodNode( public InferParentWithinObjectMethodNode(
SourceSection sourceSection, SourceSection sourceSection,
@@ -58,6 +58,7 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame); var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
var member = owner.getMember(localMethodName); 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.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.MemberLookupMode; import org.pkl.core.ast.MemberLookupMode;
import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode; import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode;
@@ -49,8 +50,8 @@ public final class ReadAmbiguousLocalityPropertyNode extends ExpressionNode {
private final Identifier name; private final Identifier name;
private final int levelsUp; private final int levelsUp;
private final boolean needsConst; private final boolean needsConst;
private @Child ExpressionNode readLocalPropertyNode; @Child private @Nullable ExpressionNode readLocalPropertyNode;
private @Child ExpressionNode readPropertyNode; @Child private @Nullable ExpressionNode readPropertyNode;
public ReadAmbiguousLocalityPropertyNode( public ReadAmbiguousLocalityPropertyNode(
SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) { 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.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException; import org.pkl.core.PklBugException;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
@@ -34,8 +35,8 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
private final Identifier name; private final Identifier name;
private final int levelsUp; private final int levelsUp;
private final boolean needsConst; private final boolean needsConst;
@Child private DirectCallNode callNode; @Child private @Nullable DirectCallNode callNode;
@CompilationFinal private ObjectMember property; @CompilationFinal @Nullable private ObjectMember property;
public ReadLocalPropertyNode( public ReadLocalPropertyNode(
SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) { SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) {
@@ -91,6 +92,7 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
callNode = DirectCallNode.create(property.getCallTarget()); callNode = DirectCallNode.create(property.getCallTarget());
insert(callNode); insert(callNode);
} }
assert callNode != null;
return callNode; return callNode;
} }
} }
@@ -61,6 +61,18 @@ public abstract class ReadPropertyNode extends ExpressionNode {
this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false); 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 // This method effectively covers `VmObject receiver` but is implemented in a more
// efficient way. See: // efficient way. See:
// https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/#strategy-2-java-interfaces // 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 com.oracle.truffle.api.source.SourceSection;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.ast.member.SharedMemberNode; import org.pkl.core.ast.member.SharedMemberNode;
import org.pkl.core.externalreader.ExternalReaderProcessException; 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.runtime.VmObjectBuilder;
import org.pkl.core.util.GlobResolver; import org.pkl.core.util.GlobResolver;
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException; import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "import*") @NodeInfo(shortName = "import*")
public class ImportGlobNode extends AbstractImportNode { public class ImportGlobNode extends AbstractImportNode {
private final String globPattern; private final String globPattern;
@Child @LateInit private SharedMemberNode memberNode; @Child private @Nullable SharedMemberNode memberNode;
@CompilationFinal @LateInit private VmMapping cachedResult; @CompilationFinal private @Nullable VmMapping cachedResult;
public ImportGlobNode( public ImportGlobNode(
SourceSection sourceSection, 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.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import java.net.URI; import java.net.URI;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.http.HttpClientException; import org.pkl.core.http.HttpClientException;
import org.pkl.core.module.ResolvedModuleKey; 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.VmContext;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmTyped; import org.pkl.core.runtime.VmTyped;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "import") @NodeInfo(shortName = "import")
public final class ImportNode extends AbstractImportNode { public final class ImportNode extends AbstractImportNode {
private final VmLanguage language; private final VmLanguage language;
@CompilationFinal @LateInit private VmTyped importedModule; @CompilationFinal private @Nullable VmTyped importedModule;
public ImportNode( public ImportNode(
VmLanguage language, VmLanguage language,
@@ -24,6 +24,7 @@ import com.oracle.truffle.api.source.SourceSection;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.ast.member.SharedMemberNode; import org.pkl.core.ast.member.SharedMemberNode;
import org.pkl.core.externalreader.ExternalReaderProcessException; 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;
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException; import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
import org.pkl.core.util.IoUtils; import org.pkl.core.util.IoUtils;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "read*") @NodeInfo(shortName = "read*")
public abstract class ReadGlobNode extends AbstractReadNode { public abstract class ReadGlobNode extends AbstractReadNode {
private final EconomicMap<String, VmMapping> cachedResults = EconomicMap.create(); private final EconomicMap<String, VmMapping> cachedResults = EconomicMap.create();
@Child @LateInit private SharedMemberNode memberNode; @Child private @Nullable SharedMemberNode memberNode;
protected ReadGlobNode(SourceSection sourceSection, ModuleKey currentModule) { protected ReadGlobNode(SourceSection sourceSection, ModuleKey currentModule) {
super(sourceSection, currentModule); super(sourceSection, currentModule);
@@ -67,6 +67,7 @@ public abstract class ReadGlobNode extends AbstractReadNode {
@TruffleBoundary @TruffleBoundary
public Object read(String globPattern) { public Object read(String globPattern) {
var cachedResult = cachedResults.get(globPattern); var cachedResult = cachedResults.get(globPattern);
//noinspection ConstantValue
if (cachedResult != null) return cachedResult; if (cachedResult != null) return cachedResult;
// use same check as for globbed imports (see AstBuilder) // 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.MemberLookupMode; import org.pkl.core.ast.MemberLookupMode;
@@ -62,10 +63,18 @@ public abstract class ToStringNode extends UnaryExpressionNode {
VmTyped value, VmTyped value,
@Cached(value = "createInvokeNode()", neverDefault = true) @Cached(value = "createInvokeNode()", neverDefault = true)
InvokeMethodVirtualNode invokeNode) { InvokeMethodVirtualNode invokeNode) {
return (String) invokeNode.executeWith(frame, value, value.getVmClass()); 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 @Fallback
@Override @Override
@TruffleBoundary @TruffleBoundary
@@ -74,7 +83,6 @@ public abstract class ToStringNode extends UnaryExpressionNode {
} }
protected InvokeMethodVirtualNode createInvokeNode() { protected InvokeMethodVirtualNode createInvokeNode() {
//noinspection ConstantConditions
return InvokeMethodVirtualNodeGen.create( return InvokeMethodVirtualNodeGen.create(
sourceSection, sourceSection,
Identifier.TO_STRING, Identifier.TO_STRING,
@@ -83,4 +91,10 @@ public abstract class ToStringNode extends UnaryExpressionNode {
null, null,
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.VmModifier;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
public final class ClassMethod extends ClassMember { public final class ClassMethod extends ClassMember {
private final List<TypeParameter> typeParameters; 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 // null = not deprecated, "" = no/empty message in the @Deprecated body
private final @Nullable String deprecation; private final @Nullable String deprecation;
@CompilationFinal private FunctionNode functionNode; @CompilationFinal @LateInit private FunctionNode functionNode;
public ClassMethod( public ClassMethod(
SourceSection sourceSection, SourceSection sourceSection,
@@ -61,6 +62,7 @@ public final class ClassMethod extends ClassMember {
} }
public void initFunctionNode(FunctionNode functionNode) { public void initFunctionNode(FunctionNode functionNode) {
//noinspection ConstantValue
assert this.functionNode == null; assert this.functionNode == null;
this.functionNode = functionNode; 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 // Caching of classes also guarantees that classes are singletons and can be compared by
// identity, // identity,
// which improves efficiency and performance (for example in shape checks). // which improves efficiency and performance (for example in shape checks).
//noinspection ConstantValue
if (cachedClass != null) return cachedClass; if (cachedClass != null) return cachedClass;
CompilerDirectives.transferToInterpreter(); 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.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.PklRootNode; import org.pkl.core.ast.PklRootNode;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
@@ -46,7 +45,7 @@ public final class ListingOrMappingTypeCastNode extends PklRootNode {
} }
@Override @Override
public @Nullable String getName() { public String getName() {
return qualifiedName; 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.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode; import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.util.LateInit;
public final class LocalTypedPropertyNode extends RegularMemberNode { public final class LocalTypedPropertyNode extends RegularMemberNode {
private final VmLanguage language; private final VmLanguage language;
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child @LateInit private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
private @Nullable Object defaultValue; private @Nullable Object defaultValue;
private boolean defaultValueInitialized; private boolean defaultValueInitialized;
@@ -47,6 +46,7 @@ public final class LocalTypedPropertyNode extends RegularMemberNode {
public @Nullable Object getDefaultValue(VirtualFrame frame) { public @Nullable Object getDefaultValue(VirtualFrame frame) {
if (!defaultValueInitialized) { if (!defaultValueInitialized) {
assert typeNode != null;
defaultValue = defaultValue =
typeNode.createDefaultValue( typeNode.createDefaultValue(
frame, language, member.getHeaderSection(), member.getQualifiedName()); frame, language, member.getHeaderSection(), member.getQualifiedName());
@@ -59,6 +59,7 @@ public final class LocalTypedPropertyNode extends RegularMemberNode {
protected Object executeImpl(VirtualFrame frame) { protected Object executeImpl(VirtualFrame frame) {
if (typeNode == null) { if (typeNode == null) {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame)); typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null; 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.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode; import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
public final class ObjectMethodNode extends RegularMemberNode { public final class ObjectMethodNode extends RegularMemberNode {
private final VmLanguage language; private final VmLanguage language;
@@ -33,7 +32,7 @@ public final class ObjectMethodNode extends RegularMemberNode {
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes; @Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
@Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode;
@CompilationFinal @LateInit private FunctionNode functionNode; @CompilationFinal private @Nullable FunctionNode functionNode;
public ObjectMethodNode( public ObjectMethodNode(
VmLanguage language, VmLanguage language,
@@ -31,7 +31,7 @@ public final class UnresolvedFunctionNode extends PklNode {
private final int parameterCount; private final int parameterCount;
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes; @Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
@Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode;
private final ExpressionNode bodyNode; @Child private ExpressionNode bodyNode;
public UnresolvedFunctionNode( public UnresolvedFunctionNode(
VmLanguage language, VmLanguage language,
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit; 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> { ... }`. */ /** Resolves `<type>` to the type's default value in `new <type> { ... }`. */
public final class GetParentForTypeNode extends ExpressionNode { public final class GetParentForTypeNode extends ExpressionNode {
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private UnresolvedTypeNode unresolvedTypeNode;
@Child private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
private final String qualifiedName; private final String qualifiedName;
@CompilationFinal @LateInit Object defaultValue; @CompilationFinal @LateInit Object defaultValue;
@@ -49,6 +50,7 @@ public final class GetParentForTypeNode extends ExpressionNode {
@Override @Override
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
//noinspection ConstantValue
if (defaultValue != null) return defaultValue; if (defaultValue != null) return defaultValue;
CompilerDirectives.transferToInterpreterAndInvalidate(); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -19,14 +19,14 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "as") @NodeInfo(shortName = "as")
public final class TypeCastNode extends ExpressionNode { public final class TypeCastNode extends ExpressionNode {
@Child private ExpressionNode valueNode; @Child private ExpressionNode valueNode;
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child @LateInit private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
public TypeCastNode( public TypeCastNode(
SourceSection sourceSection, SourceSection sourceSection,
@@ -43,6 +43,7 @@ public final class TypeCastNode extends ExpressionNode {
// don't compile unresolvedTypeNode.execute() // don't compile unresolvedTypeNode.execute()
// invalidation is done by insert() // invalidation is done by insert()
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame)); typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null; 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.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.LoopNode; 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 com.oracle.truffle.api.source.SourceSection;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -38,6 +41,7 @@ import org.jspecify.annotations.Nullable;
import org.pkl.core.PType; import org.pkl.core.PType;
import org.pkl.core.PType.StringLiteral; import org.pkl.core.PType.StringLiteral;
import org.pkl.core.PklBugException; import org.pkl.core.PklBugException;
import org.pkl.core.StackFrame;
import org.pkl.core.TypeParameter; import org.pkl.core.TypeParameter;
import org.pkl.core.ast.*; import org.pkl.core.ast.*;
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope; 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.EconomicSets;
import org.pkl.core.util.LateInit; import org.pkl.core.util.LateInit;
import org.pkl.core.util.MutableBoolean; import org.pkl.core.util.MutableBoolean;
import org.pkl.core.util.MutableReference;
public abstract class TypeNode extends PklNode { public abstract class TypeNode extends PklNode {
@@ -234,7 +239,7 @@ public abstract class TypeNode extends PklNode {
public abstract static class FrameSlotTypeNode extends TypeNode { public abstract static class FrameSlotTypeNode extends TypeNode {
@CompilationFinal protected int slot = -1; @CompilationFinal protected int slot = -1;
@CompilationFinal @Child protected WriteFrameSlotNode writeFrameSlotNode; @CompilationFinal @Child protected @Nullable WriteFrameSlotNode writeFrameSlotNode;
protected FrameSlotTypeNode(SourceSection sourceSection) { protected FrameSlotTypeNode(SourceSection sourceSection) {
super(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 { public static final class PairTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode firstTypeNode; @Child private TypeNode firstTypeNode;
@Child private TypeNode secondTypeNode; @Child private TypeNode secondTypeNode;
@@ -2585,12 +2714,23 @@ public abstract class TypeNode extends PklNode {
this.typeAlias = typeAlias; this.typeAlias = typeAlias;
this.typeArgumentNodes = typeArgumentNodes; this.typeArgumentNodes = typeArgumentNodes;
aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes); aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes);
aliasedTypeNode.accept(
node -> {
if (node instanceof ValidatingObjectSlotTypeNode typeNode) {
typeNode.validate(this);
}
return true;
});
} }
public TypeNode getAliasedTypeNode() { public TypeNode getAliasedTypeNode() {
return aliasedTypeNode; return aliasedTypeNode;
} }
public VmTypeAlias getTypeAlias() {
return typeAlias;
}
@Override @Override
public FrameSlotKind getFrameSlotKind() { public FrameSlotKind getFrameSlotKind() {
return aliasedTypeNode.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) { private static @Nullable Object createDefaultValue(VmClass clazz) {
if (clazz.isInstantiable()) { if (clazz.isInstantiable()) {
if (clazz.isListingClass()) return VmListing.empty(); if (clazz.isListingClass()) return VmListing.empty();
@@ -26,7 +26,7 @@ import org.pkl.core.runtime.VmLanguage;
@NodeInfo(shortName = "is") @NodeInfo(shortName = "is")
public final class TypeTestNode extends ExpressionNode { public final class TypeTestNode extends ExpressionNode {
@Child private ExpressionNode valueNode; @Child private ExpressionNode valueNode;
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child private @Nullable TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
public TypeTestNode( public TypeTestNode(
@@ -49,6 +49,7 @@ public final class TypeTestNode extends ExpressionNode {
// don't compile unresolvedTypeNode.execute() // don't compile unresolvedTypeNode.execute()
// invalidation is done by insert() // invalidation is done by insert()
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame)); typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -287,6 +287,13 @@ public abstract class UnresolvedTypeNode extends PklNode {
return new VarArgsTypeNode(sourceSection, typeArgumentNodes[0].execute(frame)); 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() throw exceptionBuilder()
.evalError("notAParameterizableClass", clazz.getDisplayName()) .evalError("notAParameterizableClass", clazz.getDisplayName())
.withSourceSection(typeArgumentNodes[0].sourceSection) .withSourceSection(typeArgumentNodes[0].sourceSection)
@@ -422,6 +429,10 @@ public abstract class UnresolvedTypeNode extends PklNode {
this.typeParameter = typeParameter; this.typeParameter = typeParameter;
} }
public int getTypeParameterIndex() {
return typeParameter.getIndex();
}
@Override @Override
public TypeNode execute(VirtualFrame frame) { public TypeNode execute(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
@@ -429,4 +440,23 @@ public abstract class UnresolvedTypeNode extends PklNode {
return new TypeVariableNode(sourceSection, typeParameter); 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.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.PType;
import org.pkl.core.StackFrame; import org.pkl.core.StackFrame;
import org.pkl.core.ValueFormatter; import org.pkl.core.ValueFormatter;
import org.pkl.core.ast.type.TypeNode.UnionTypeNode; import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
@@ -333,4 +334,44 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
return false; 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( readResponses.computeIfAbsent(
baseUri, baseUri,
(uri) -> { (uri) -> {
var future = new CompletableFuture<byte[]>(); var future = new CompletableFuture<byte @Nullable []>();
var request = var request =
new ReadResourceRequest(requestIdGenerator.nextLong(), evaluatorId, uri); new ReadResourceRequest(requestIdGenerator.nextLong(), evaluatorId, uri);
try { try {
@@ -46,7 +46,6 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.ErrorMessages;
import org.pkl.core.util.Exceptions; import org.pkl.core.util.Exceptions;
@@ -93,7 +92,7 @@ final class JdkHttpClient implements HttpClient {
HttpRequest request, HttpRequest request,
BodyHandler<T> responseBodyHandler, BodyHandler<T> responseBodyHandler,
HttpRequestChecker httpRequestChecker) HttpRequestChecker httpRequestChecker)
throws IOException, SecurityManagerException { throws IOException {
try { try {
return underlying.send(request, responseBodyHandler); return underlying.send(request, responseBodyHandler);
} catch (ConnectException e) { } catch (ConnectException e) {
@@ -84,7 +84,7 @@ public final class Messages {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -93,12 +93,14 @@ public class PathElement {
} }
/** Returns the element at path {@code basePath}, given a {@link Path}. */ /** 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) { public @Nullable TreePathElement getElement(Path basePath) {
var path = basePath.normalize(); var path = basePath.normalize();
var element = this; var element = this;
for (var i = 0; i < path.getNameCount(); i++) { for (var i = 0; i < path.getNameCount(); i++) {
var part = path.getName(i).toString(); var part = path.getName(i).toString();
element = element.getChildren().get(part); element = element.getChildren().get(part);
//noinspection ConstantValue
if (element == null) { if (element == null) {
return null; return null;
} }
@@ -183,6 +183,7 @@ public final class ProjectDependenciesManager {
public Map<String, Dependency> getLocalPackageDependencies(PackageUri packageUri) { public Map<String, Dependency> getLocalPackageDependencies(PackageUri packageUri) {
ensureDependenciesInitialized(); ensureDependenciesInitialized();
var dep = localPackageDependencies.get(packageUri); var dep = localPackageDependencies.get(packageUri);
//noinspection ConstantValue
assert dep != null; assert dep != null;
return dep; return dep;
} }
@@ -67,7 +67,7 @@ public abstract class Dependency {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -107,7 +107,7 @@ public abstract class Dependency {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -217,6 +217,7 @@ public final class DependencyMetadata {
var map = new HashMap<>(); var map = new HashMap<>();
for (var kv : value) { for (var kv : value) {
var kvObj = (JsObject) kv; var kvObj = (JsObject) kv;
assert kvObj != null;
map.put(parsePObject(kvObj.get("key")), parsePObject(kvObj.get("value"))); map.put(parsePObject(kvObj.get("key")), parsePObject(kvObj.get("value")));
} }
return map; return map;
@@ -294,7 +295,7 @@ public final class DependencyMetadata {
var ret = new ArrayList<String>(arr.size()); var ret = new ArrayList<String>(arr.size());
for (var elem : arr) { for (var elem : arr) {
if (!(elem instanceof String string)) { if (!(elem instanceof String string)) {
throw new FormatException("string", elem.getClass()); throw new FormatException("string", elem != null ? elem.getClass() : Void.class);
} }
ret.add(string); ret.add(string);
} }
@@ -414,7 +415,7 @@ public final class DependencyMetadata {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -624,8 +625,8 @@ public final class DependencyMetadata {
jsonWriter.endObject(); jsonWriter.endObject();
} }
private void writeGenericObject(Object value) throws IOException { private void writeGenericObject(@Nullable Object value) throws IOException {
if (value instanceof PNull) { if (value == null || value instanceof PNull) {
jsonWriter.nullValue(); jsonWriter.nullValue();
} else if (value instanceof PObject pObject) { } else if (value instanceof PObject pObject) {
writePObject(pObject); writePObject(pObject);
@@ -18,6 +18,7 @@ package org.pkl.core.packages;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Path; import java.nio.file.Path;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException; import org.pkl.core.PklBugException;
import org.pkl.core.Version; import org.pkl.core.Version;
import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.ErrorMessages;
@@ -98,7 +99,7 @@ public final class PackageAssetUri {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -45,6 +45,7 @@ import java.util.stream.StreamSupport;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicMap;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException;
import org.pkl.core.SecurityManager; import org.pkl.core.SecurityManager;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.http.HttpClient; import org.pkl.core.http.HttpClient;
@@ -456,10 +457,17 @@ final class PackageResolvers {
} }
private Path getRelativePath(PackageUri uri) { private Path getRelativePath(PackageUri uri) {
return Path.of( var relativePath =
CACHE_DIR_PREFIX, Path.of(
IoUtils.encodePath(uri.getUri().getAuthority()), CACHE_DIR_PREFIX,
getEffectivePackageUriPath(uri)); 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) { private String getLastSegmentName(PackageUri packageUri) {
@@ -61,6 +61,13 @@ public final class PackageUri {
throw new URISyntaxException( throw new URISyntaxException(
uri.toString(), ErrorMessages.create("missingPathInPackageUri", uri)); 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('@'); var versionIdx = path.lastIndexOf('@');
if (versionIdx == -1) { if (versionIdx == -1) {
throw new URISyntaxException( throw new URISyntaxException(
@@ -111,7 +118,7 @@ public final class PackageUri {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.pkl.core.project;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException; import org.pkl.core.PklBugException;
import org.pkl.core.packages.PackageUri; import org.pkl.core.packages.PackageUri;
import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.ErrorMessages;
@@ -84,7 +85,7 @@ public record CanonicalPackageUri(URI baseUri, int majorVersion) {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -199,7 +199,7 @@ public final class Project {
sb.append("\n│"); sb.append("\n│");
} }
sb.append("\n│ "); sb.append("\n│ ");
sb.append(uri.toString()); sb.append(uri);
} }
sb.append("\n└─"); sb.append("\n└─");
} }
@@ -477,7 +477,7 @@ public final class Project {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -611,7 +611,7 @@ public final class Project {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -141,6 +141,7 @@ public final class ProjectDependenciesResolver {
private void updateDependency(Dependency dependency) { private void updateDependency(Dependency dependency) {
var canonicalPackageUri = CanonicalPackageUri.fromPackageUri(dependency.getPackageUri()); var canonicalPackageUri = CanonicalPackageUri.fromPackageUri(dependency.getPackageUri());
var currentDependency = resolvedDependencies.get(canonicalPackageUri); var currentDependency = resolvedDependencies.get(canonicalPackageUri);
//noinspection ConstantValue
if (currentDependency == null if (currentDependency == null
|| currentDependency.getVersion().compareTo(dependency.getVersion()) < 0) { || currentDependency.getVersion().compareTo(dependency.getVersion()) < 0) {
EconomicMaps.put(resolvedDependencies, canonicalPackageUri, dependency); EconomicMaps.put(resolvedDependencies, canonicalPackageUri, dependency);
@@ -135,6 +135,7 @@ public final class ProjectDeps {
} }
/** Given a declared dependency, return the resolved dependency. */ /** Given a declared dependency, return the resolved dependency. */
@SuppressWarnings("DataFlowIssue") // incorrect analysis
public @Nullable Dependency get(CanonicalPackageUri canonicalPackageUri) { public @Nullable Dependency get(CanonicalPackageUri canonicalPackageUri) {
return resolvedDependencies.get(canonicalPackageUri); return resolvedDependencies.get(canonicalPackageUri);
} }
@@ -150,7 +151,7 @@ public final class ProjectDeps {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -51,6 +51,8 @@ public final class FileSystemManager {
public static synchronized FileSystem getFileSystem(URI uri) throws IOException { public static synchronized FileSystem getFileSystem(URI uri) throws IOException {
var fs = fileSystems.get(uri); var fs = fileSystems.get(uri);
// incorrect nullability for `org.graalvm.collections.UnmodifiableEconomicMap`
//noinspection ConstantValue
if (fs != null) { if (fs != null) {
var count = counts.get(fs); var count = counts.get(fs);
assert count != null; assert count != null;
@@ -165,6 +165,10 @@ public final class Identifier implements Comparable<Identifier> {
public static final Identifier ILLEGAL = get("`"); 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 // common in lambdas etc
public static final Identifier IT = get("it"); public static final Identifier IT = get("it");
@@ -104,6 +104,8 @@ public final class ModuleCache {
return PlatformModule.getModule(); return PlatformModule.getModule();
case "project": case "project":
return ProjectModule.getModule(); return ProjectModule.getModule();
case "ref":
return RefModule.getModule();
case "reflect": case "reflect":
return ReflectModule.getModule(); return ReflectModule.getModule();
case "release": case "release":
@@ -40,10 +40,10 @@ public final class ModuleInfo {
@LateInit private List<VmTyped> annotations; @LateInit private List<VmTyped> annotations;
@LateInit private VmTyped __mirror; private @Nullable VmTyped __mirror;
private final Object mirrorLock = new Object(); private final Object mirrorLock = new Object();
@LateInit private ModuleSchema __moduleSchema; private @Nullable ModuleSchema __moduleSchema;
private final Object moduleSchemaLock = new Object(); private final Object moduleSchemaLock = new Object();
public ModuleInfo( public ModuleInfo(
@@ -65,12 +65,12 @@ public final class ModuleInfo {
} }
public void initAnnotations(List<VmTyped> annotations) { public void initAnnotations(List<VmTyped> annotations) {
//noinspection ConstantValue
assert this.annotations == null; assert this.annotations == null;
this.annotations = annotations; this.annotations = annotations;
} }
public List<VmTyped> getAnnotations() { public List<VmTyped> getAnnotations() {
assert annotations != null;
return annotations; 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() { 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); var truffleElements = TruffleStackTrace.getStackTrace(exception);
if (truffleElements.isEmpty()) { if (truffleElements.isEmpty()) {
addFrame(exception.getSourceSection(), exception.getMemberName()); addFrame(exception.getSourceSection(), exception.getMemberName());
@@ -101,8 +101,14 @@ public final class StackTraceRenderer {
if (hint != null) { if (hint != null) {
out.append(AnsiTheme.ERROR_MESSAGE_HINT, hint); out.append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
} else { } else {
assert hintBuilder != null; out.append(
out.append(AnsiTheme.ERROR_MESSAGE_HINT, () -> hintBuilder.accept(out, true)); AnsiTheme.ERROR_MESSAGE_HINT,
() -> {
// nullaway needs this assertion
//noinspection ConstantValue
assert hintBuilder != null;
hintBuilder.accept(out, true);
});
} }
out.append('\n'); out.append('\n');
} }
@@ -491,11 +491,11 @@ public final class TestRunner {
sb.append("\n Expected: "); sb.append("\n Expected: ");
appendLocation(sb, expectedLocation); appendLocation(sb, expectedLocation);
sb.append("\n "); 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: "); sb.append("\n Actual: ");
appendLocation(sb, actualLocation); appendLocation(sb, actualLocation);
sb.append("\n "); 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()); return new Failure("Example Failure", sb.toString());
} }
@@ -38,7 +38,8 @@ public final class VmBugException extends VmException {
@Nullable SourceSection sourceSection, @Nullable SourceSection sourceSection,
@Nullable String memberName, @Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder, @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames) { Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super( super(
message, message,
@@ -51,7 +52,8 @@ public final class VmBugException extends VmException {
sourceSection, sourceSection,
memberName, memberName,
hintBuilder, hintBuilder,
insertedStackFrames); insertedStackFrames,
leadingStackFrames);
} }
@Override @Override
@@ -445,6 +445,11 @@ public final class VmClass extends VmValue {
return isClass(BaseModule.getVarArgsClass(), "pkl.base#VarArgs"); 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) { private boolean isClass(@Nullable VmClass clazz, String qualifiedClassName) {
// may be null during evaluation of base module // may be null during evaluation of base module
return clazz != null ? this == clazz : getQualifiedName().equals(qualifiedClassName); return clazz != null ? this == clazz : getQualifiedName().equals(qualifiedClassName);
@@ -621,49 +626,62 @@ public final class VmClass extends VmValue {
public PClass export() { public PClass export() {
synchronized (pClassLock) { synchronized (pClassLock) {
//noinspection ConstantValue //noinspection ConstantValue
if (__pClass == null) { if (__pClass != null) {
var exportedAnnotations = new ArrayList<PObject>(); return __pClass;
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 // if this is not a module class, export this class's module's class first to break the cycle
// infinite recursion in case of cyclic references PClass moduleClass = null;
__pClass = if (!classInfo.isModuleClass()) {
new PClass( moduleClass = getModule().getVmClass().export();
VmUtils.exportDocComment(docComment), }
new SourceLocation(headerSection.getStartLine(), sourceSection.getEndLine()), // then if the cached value is still null, initialize it
VmModifier.export(modifiers, true), if (__pClass != null) {
exportedAnnotations, return __pClass;
classInfo, }
typeParameters,
properties,
methods);
for (var parameter : typeParameters) { var exportedAnnotations = new ArrayList<PObject>();
parameter.initOwner(__pClass); 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) { for (var method : EconomicMaps.getValues(declaredMethods)) {
assert superclass != null; if (method.isLocal()) continue;
__pClass.initSupertype(TypeNode.export(supertypeNode), superclass.export()); methods.put(method.getName().toString(), method.export(__pClass));
}
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));
}
} }
return __pClass; return __pClass;
@@ -107,6 +107,7 @@ public final class VmContext {
} }
public void initialize(Holder holder) { public void initialize(Holder holder) {
//noinspection ConstantValue
assert this.holder == null; assert this.holder == null;
this.holder = holder; this.holder = holder;
} }
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.Objects; import java.util.Objects;
import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.collections.UnmodifiableEconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PClassInfo; import org.pkl.core.PClassInfo;
import org.pkl.core.PObject; import org.pkl.core.PObject;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
@@ -75,12 +76,14 @@ public final class VmDynamic extends VmObject {
@Override @Override
@TruffleBoundary @TruffleBoundary
public PObject export() { public PObject export() {
assert forced : "Value was not forced prior to export";
var properties = var properties =
CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues)); CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues));
iterateMemberValues( iterateAlreadyForcedMemberValues(
(key, member, value) -> { (key, member, value) -> {
properties.put(key.toString(), VmValue.exportNullable(value)); properties.put(key.toString(), VmValue.export(value));
return true; return true;
}); });
@@ -99,9 +102,8 @@ public final class VmDynamic extends VmObject {
@Override @Override
@TruffleBoundary @TruffleBoundary
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) // noinspection Contract if (this == obj) return true;
return true;
if (!(obj instanceof VmDynamic other)) return false; if (!(obj instanceof VmDynamic other)) return false;
// could use shallow force, but deep force is cached // could use shallow force, but deep force is cached
@@ -115,6 +117,7 @@ public final class VmDynamic extends VmObject {
if (isHiddenOrLocalProperty(key)) continue; if (isHiddenOrLocalProperty(key)) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
var otherValue = other.getCachedValue(key); var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false; if (!value.equals(otherValue)) return false;
@@ -137,6 +140,7 @@ public final class VmDynamic extends VmObject {
if (isHiddenOrLocalProperty(key)) continue; if (isHiddenOrLocalProperty(key)) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
result += key.hashCode() ^ value.hashCode(); result += key.hashCode() ^ value.hashCode();
} }
@@ -37,7 +37,8 @@ public class VmEvalException extends VmException {
@Nullable SourceSection sourceSection, @Nullable SourceSection sourceSection,
@Nullable String memberName, @Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder, @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames) { Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super( super(
message, message,
@@ -50,6 +51,7 @@ public class VmEvalException extends VmException {
sourceSection, sourceSection,
memberName, memberName,
hintBuilder, hintBuilder,
insertedStackFrames); insertedStackFrames,
leadingStackFrames);
} }
} }
@@ -34,6 +34,7 @@ public abstract class VmException extends AbstractTruffleException {
private final @Nullable SourceSection sourceSection; private final @Nullable SourceSection sourceSection;
private final @Nullable String memberName; private final @Nullable String memberName;
private final Map<CallTarget, StackFrame> insertedStackFrames; private final Map<CallTarget, StackFrame> insertedStackFrames;
private final List<StackFrame> leadingStackFrames;
@Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder; @Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder;
@Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder; @Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder;
@@ -48,7 +49,8 @@ public abstract class VmException extends AbstractTruffleException {
@Nullable SourceSection sourceSection, @Nullable SourceSection sourceSection,
@Nullable String memberName, @Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder, @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames) { Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super(message, cause, UNLIMITED_STACK_TRACE, location); super(message, cause, UNLIMITED_STACK_TRACE, location);
assert message != null || messageBuilder != null; assert message != null || messageBuilder != null;
this.messageBuilder = messageBuilder; this.messageBuilder = messageBuilder;
@@ -58,6 +60,7 @@ public abstract class VmException extends AbstractTruffleException {
this.sourceSection = sourceSection; this.sourceSection = sourceSection;
this.memberName = memberName; this.memberName = memberName;
this.insertedStackFrames = insertedStackFrames; this.insertedStackFrames = insertedStackFrames;
this.leadingStackFrames = leadingStackFrames;
this.hintBuilder = hintBuilder; this.hintBuilder = hintBuilder;
} }
@@ -89,6 +92,14 @@ public abstract class VmException extends AbstractTruffleException {
return insertedStackFrames; 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() { public @Nullable BiConsumer<AnsiStringBuilder, Boolean> getMessageBuilder() {
return messageBuilder; return messageBuilder;
} }
@@ -99,6 +99,7 @@ public final class VmExceptionBuilder {
private @Nullable Node location; private @Nullable Node location;
private @Nullable SourceSection sourceSection; private @Nullable SourceSection sourceSection;
private @Nullable String memberName; private @Nullable String memberName;
private List<StackFrame> leadingStackFrames = List.of();
public VmExceptionBuilder typeMismatch(Object value, VmClass expectedType) { public VmExceptionBuilder typeMismatch(Object value, VmClass expectedType) {
if (value instanceof VmNull) { if (value instanceof VmNull) {
@@ -359,6 +360,15 @@ public final class VmExceptionBuilder {
return this; 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() { public VmException build() {
if (message != null && messageBuilder != null) { if (message != null && messageBuilder != null) {
throw new IllegalStateException("Both message and messageBuilder are set"); throw new IllegalStateException("Both message and messageBuilder are set");
@@ -387,7 +397,8 @@ public final class VmExceptionBuilder {
sourceSection, sourceSection,
memberName, memberName,
hintBuilder, hintBuilder,
effectiveInsertedStackFrames); effectiveInsertedStackFrames,
leadingStackFrames);
case UNDEFINED_VALUE -> case UNDEFINED_VALUE ->
new VmUndefinedValueException( new VmUndefinedValueException(
message, message,
@@ -401,7 +412,8 @@ public final class VmExceptionBuilder {
memberName, memberName,
hintBuilder, hintBuilder,
receiver, receiver,
effectiveInsertedStackFrames); effectiveInsertedStackFrames,
leadingStackFrames);
case BUG -> case BUG ->
new VmBugException( new VmBugException(
message, message,
@@ -414,7 +426,8 @@ public final class VmExceptionBuilder {
sourceSection, sourceSection,
memberName, memberName,
hintBuilder, hintBuilder,
effectiveInsertedStackFrames); effectiveInsertedStackFrames,
leadingStackFrames);
case WRAPPED -> { case WRAPPED -> {
assert wrappedException != null; assert wrappedException != null;
yield new VmWrappedEvalException( yield new VmWrappedEvalException(
@@ -429,6 +442,7 @@ public final class VmExceptionBuilder {
memberName, memberName,
hintBuilder, hintBuilder,
effectiveInsertedStackFrames, effectiveInsertedStackFrames,
leadingStackFrames,
wrappedException); wrappedException);
} }
}; };
@@ -87,13 +87,15 @@ public final class VmListing extends VmListingOrMapping {
@Override @Override
@TruffleBoundary @TruffleBoundary
public List<Object> export() { public List<Object> export() {
assert forced : "Value was not forced prior to export";
var properties = new ArrayList<>(EconomicMaps.size(cachedValues)); var properties = new ArrayList<>(EconomicMaps.size(cachedValues));
iterateMemberValues( iterateAlreadyForcedMemberValues(
(key, prop, value) -> { (key, prop, value) -> {
if (isDefaultProperty(key)) return true; if (isDefaultProperty(key)) return true;
properties.add(VmValue.exportNullable(value)); properties.add(VmValue.export(value));
return true; return true;
}); });
@@ -127,6 +129,7 @@ public final class VmListing extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
var otherValue = other.getCachedValue(key); var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false; if (!value.equals(otherValue)) return false;
@@ -149,6 +152,7 @@ public final class VmListing extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
result = 31 * result + value.hashCode(); 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) { for (var key : other.keyOrder) {
var value = other.map.get(key); var value = other.map.get(key);
assert value != null;
if (!map.containsKey(key)) { if (!map.containsKey(key)) {
keyOrderBuilder.append(key); keyOrderBuilder.append(key);
@@ -21,19 +21,18 @@ import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.collections.UnmodifiableEconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode; import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.util.CollectionUtils; import org.pkl.core.util.CollectionUtils;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
import org.pkl.core.util.MutableLong; import org.pkl.core.util.MutableLong;
public final class VmMapping extends VmListingOrMapping { public final class VmMapping extends VmListingOrMapping {
private long cachedLength = -1; private long cachedLength = -1;
@GuardedBy("this") @GuardedBy("this")
@LateInit private @Nullable VmSet __allKeys;
private VmSet __allKeys;
private static final class EmptyHolder { private static final class EmptyHolder {
private static final VmMapping EMPTY = private static final VmMapping EMPTY =
@@ -94,13 +93,15 @@ public final class VmMapping extends VmListingOrMapping {
@Override @Override
@TruffleBoundary @TruffleBoundary
public Map<Object, Object> export() { public Map<Object, Object> export() {
assert forced : "Value was not forced prior to export";
var properties = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues)); var properties = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues));
iterateMemberValues( iterateAlreadyForcedMemberValues(
(key, prop, value) -> { (key, prop, value) -> {
if (isDefaultProperty(key)) return true; if (isDefaultProperty(key)) return true;
properties.put(VmValue.export(key), VmValue.exportNullable(value)); properties.put(VmValue.export(key), VmValue.export(value));
return true; return true;
}); });
@@ -119,9 +120,8 @@ public final class VmMapping extends VmListingOrMapping {
@Override @Override
@TruffleBoundary @TruffleBoundary
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) // noinspection Contract if (this == obj) return true;
return true;
if (!(obj instanceof VmMapping other)) return false; if (!(obj instanceof VmMapping other)) return false;
// could use shallow force, but deep force is cached // could use shallow force, but deep force is cached
@@ -135,6 +135,7 @@ public final class VmMapping extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
var otherValue = other.getCachedValue(key); var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false; if (!value.equals(otherValue)) return false;
@@ -157,6 +158,7 @@ public final class VmMapping extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
result += key.hashCode() ^ value.hashCode(); result += key.hashCode() ^ value.hashCode();
} }
@@ -34,7 +34,7 @@ public abstract class VmObject extends VmObjectLike {
protected final EconomicMap<Object, Object> cachedValues; protected final EconomicMap<Object, Object> cachedValues;
protected int cachedHash; protected int cachedHash;
private boolean forced; protected boolean forced;
public VmObject( public VmObject(
MaterializedFrame enclosingFrame, 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 * 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 @TruffleBoundary
protected final Map<String, Object> exportMembers() { 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) -> { (key, member, value) -> {
if (member.isClass() || member.isTypeAlias()) return true; if (member.isClass() || member.isTypeAlias()) return true;
result.put(key.toString(), VmValue.exportNullable(value)); result.put(key.toString(), VmValue.export(value));
return true; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -87,4 +87,9 @@ public final class VmObjectBuilder {
members, members,
elementCount); 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 @Override
protected void visitEntryKey(Object key, boolean isFirst) { protected void visitEntryKey(Object key, boolean isFirst) {
visit(key); 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, 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.ConstrainedTypeNode;
import org.pkl.core.ast.type.TypeNode.TypeVariableNode; import org.pkl.core.ast.type.TypeNode.TypeVariableNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; 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 { public final class VmTypeAlias extends VmValue {
private final SourceSection sourceSection; private final SourceSection sourceSection;
@@ -48,17 +48,15 @@ public final class VmTypeAlias extends VmValue {
private final List<TypeParameter> typeParameters; private final List<TypeParameter> typeParameters;
private final MaterializedFrame enclosingFrame; private final MaterializedFrame enclosingFrame;
@LateInit private TypeNode typeNode; private @Nullable TypeNode typeNode;
@LateInit
@GuardedBy("pTypeAliasLock") @GuardedBy("pTypeAliasLock")
private TypeAlias __pTypeAlias; private @Nullable TypeAlias __pTypeAlias;
private final Object pTypeAliasLock = new Object(); private final Object pTypeAliasLock = new Object();
@LateInit
@GuardedBy("mirrorLock") @GuardedBy("mirrorLock")
private VmTyped __mirror; private @Nullable VmTyped __mirror;
private final Object mirrorLock = new Object(); private final Object mirrorLock = new Object();
@@ -86,7 +84,6 @@ public final class VmTypeAlias extends VmValue {
} }
public void initTypeCheckNode(TypeNode typeNode) { public void initTypeCheckNode(TypeNode typeNode) {
assert this.typeNode == null;
this.typeNode = typeNode; this.typeNode = typeNode;
} }
@@ -100,6 +97,7 @@ public final class VmTypeAlias extends VmValue {
*/ */
@TruffleBoundary @TruffleBoundary
public SourceSection getBaseTypeSection() { public SourceSection getBaseTypeSection() {
assert typeNode != null;
if (typeNode instanceof ConstrainedTypeNode constrainedTypeNode) { if (typeNode instanceof ConstrainedTypeNode constrainedTypeNode) {
return constrainedTypeNode.getBaseTypeSection(); return constrainedTypeNode.getBaseTypeSection();
} }
@@ -116,6 +114,7 @@ public final class VmTypeAlias extends VmValue {
*/ */
@TruffleBoundary @TruffleBoundary
public SourceSection getConstraintSection() { public SourceSection getConstraintSection() {
assert typeNode != null;
if (typeNode instanceof ConstrainedTypeNode) { if (typeNode instanceof ConstrainedTypeNode) {
return ((ConstrainedTypeNode) typeNode).getFirstConstraintSection(); return ((ConstrainedTypeNode) typeNode).getFirstConstraintSection();
} }
@@ -169,6 +168,7 @@ public final class VmTypeAlias extends VmValue {
} }
public TypeNode getTypeNode() { public TypeNode getTypeNode() {
assert typeNode != null;
return typeNode; return typeNode;
} }
@@ -178,14 +178,23 @@ public final class VmTypeAlias extends VmValue {
@TruffleBoundary @TruffleBoundary
public TypeNode instantiate(TypeNode[] typeArgumentNodes) { public TypeNode instantiate(TypeNode[] typeArgumentNodes) {
assert typeNode != null;
// Cloning the type node means that the entire type check remains within a single root node, // 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: // which should be good for interpreted and compiled performance alike:
// * Fewer root nodes to call // * Fewer root nodes to call
// * ControlFlowException used to implement union types doesn't escape root node // * 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(); var clone = (TypeNode) typeNode.deepCopy();
if (typeParameters.isEmpty()) return clone;
clone.accept( clone.accept(
node -> { node -> {
if (node instanceof TypeVariableNode typeVarNode) { if (node instanceof TypeVariableNode typeVarNode) {
@@ -196,6 +205,15 @@ public final class VmTypeAlias extends VmValue {
typeArgumentNodes.length == 0 typeArgumentNodes.length == 0
? new UnknownTypeNode(sourceSection) ? new UnknownTypeNode(sourceSection)
: typeArgumentNodes[index]); : 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; return true;
}); });
@@ -228,7 +246,8 @@ public final class VmTypeAlias extends VmValue {
simpleName, simpleName,
getModuleName(), getModuleName(),
qualifiedName, qualifiedName,
typeParameters); typeParameters,
module.getVmClass().export());
for (var parameter : typeParameters) { for (var parameter : typeParameters) {
parameter.initOwner(__pTypeAlias); parameter.initOwner(__pTypeAlias);
@@ -264,6 +283,7 @@ public final class VmTypeAlias extends VmValue {
} }
public VmTyped getTypeMirror() { public VmTyped getTypeMirror() {
assert typeNode != null;
return typeNode.getMirror(); 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.expression.unary.ImportNode;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
public final class VmTyped extends VmObject { public final class VmTyped extends VmObject {
@CompilationFinal @LateInit private VmClass clazz; @CompilationFinal private @Nullable VmClass clazz;
public VmTyped( public VmTyped(
MaterializedFrame enclosingFrame, MaterializedFrame enclosingFrame,
@@ -162,6 +161,8 @@ public final class VmTyped extends VmObject {
@Override @Override
@TruffleBoundary @TruffleBoundary
public Composite export() { public Composite export() {
assert forced : "Value was not forced prior to export";
assert clazz != null;
if (!isModuleObject()) { if (!isModuleObject()) {
return new PObject(clazz.getPClassInfo(), exportMembers()); return new PObject(clazz.getPClassInfo(), exportMembers());
} }
@@ -190,6 +191,7 @@ public final class VmTyped extends VmObject {
if (this == obj) return true; if (this == obj) return true;
if (!(obj instanceof VmTyped other)) return false; if (!(obj instanceof VmTyped other)) return false;
assert clazz != null;
if (clazz != other.clazz) return false; if (clazz != other.clazz) return false;
// could use shallow force, but deep force is cached // could use shallow force, but deep force is cached
force(false); force(false);
@@ -210,6 +212,7 @@ public final class VmTyped extends VmObject {
public int hashCode() { public int hashCode() {
if (cachedHash != 0) return cachedHash; if (cachedHash != 0) return cachedHash;
assert clazz != null;
force(false); force(false);
var result = 0; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ import com.oracle.truffle.api.dsl.TypeSystem;
VmRegex.class, VmRegex.class,
VmTypeAlias.class, VmTypeAlias.class,
VmObjectLike.class, VmObjectLike.class,
VmValue.class VmReference.class,
VmValue.class,
}) })
public class VmTypes {} public class VmTypes {}
@@ -43,7 +43,8 @@ public final class VmUndefinedValueException extends VmEvalException {
@Nullable String memberName, @Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder, @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
@Nullable Object receiver, @Nullable Object receiver,
@Nullable Map<CallTarget, StackFrame> insertedStackFrames) { @Nullable Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames) {
super( super(
message, message,
@@ -56,7 +57,8 @@ public final class VmUndefinedValueException extends VmEvalException {
sourceSection, sourceSection,
memberName, memberName,
hintBuilder, hintBuilder,
insertedStackFrames == null ? Collections.emptyMap() : insertedStackFrames); insertedStackFrames == null ? Collections.emptyMap() : insertedStackFrames,
leadingStackFrames);
this.receiver = receiver; this.receiver = receiver;
} }
@@ -85,6 +85,8 @@ public interface VmValueConverter<T> {
T convertFunction(VmFunction value, Iterable<Object> path); 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 */ /** Returns with an empty identifier if the second value is a RenderDirective */
Pair<Identifier, T> convertProperty(ClassProperty property, Object value, Iterable<Object> path); Pair<Identifier, T> convertProperty(ClassProperty property, Object value, Iterable<Object> path);
@@ -271,6 +271,30 @@ public final class VmValueRenderer {
append("null"); 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) { private void append(Object value) {
builder.append(value); builder.append(value);
checkLengthLimit(); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -60,6 +60,8 @@ public interface VmValueVisitor {
void visitFunction(VmFunction value); void visitFunction(VmFunction value);
void visitReference(VmReference value);
default void visit(Object value) { default void visit(Object value) {
Objects.requireNonNull(value, "Value to be visited must be non-null."); Objects.requireNonNull(value, "Value to be visited must be non-null.");
@@ -41,6 +41,7 @@ public class VmWrappedEvalException extends VmEvalException {
@Nullable String memberName, @Nullable String memberName,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder, @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
Map<CallTarget, StackFrame> insertedStackFrames, Map<CallTarget, StackFrame> insertedStackFrames,
List<StackFrame> leadingStackFrames,
VmException wrappedException) { VmException wrappedException) {
super( super(
message, message,
@@ -53,7 +54,8 @@ public class VmWrappedEvalException extends VmEvalException {
sourceSection, sourceSection,
memberName, memberName,
hintBuilder, hintBuilder,
insertedStackFrames); insertedStackFrames,
leadingStackFrames);
this.wrappedException = wrappedException; this.wrappedException = wrappedException;
} }
@@ -34,6 +34,7 @@ import org.pkl.core.runtime.VmListing;
import org.pkl.core.runtime.VmMap; import org.pkl.core.runtime.VmMap;
import org.pkl.core.runtime.VmMapping; import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmNull; import org.pkl.core.runtime.VmNull;
import org.pkl.core.runtime.VmReference;
import org.pkl.core.runtime.VmSet; import org.pkl.core.runtime.VmSet;
import org.pkl.core.runtime.VmTypeAlias; import org.pkl.core.runtime.VmTypeAlias;
import org.pkl.core.runtime.VmTyped; import org.pkl.core.runtime.VmTyped;
@@ -397,6 +398,11 @@ public abstract class AbstractRenderer implements VmValueVisitor {
currSourceSection = prevSourceSection; currSourceSection = prevSourceSection;
} }
@Override
public void visitReference(VmReference value) {
cannotRenderTypeAddConverter(value);
}
protected void cannotRenderTypeAddConverter(VmValue value) { protected void cannotRenderTypeAddConverter(VmValue value) {
var builder = var builder =
new VmExceptionBuilder() new VmExceptionBuilder()

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