This enables defining declarative key and/or value transformations in
cases where neither `Class`- nor path-based converters can be applied
gracefully. It is also the only way to express transforming the
resulting property names in `Typed` objects without applying a converter
to the entire containing type, which is cumbersome at best.
SPICE: https://github.com/apple/pkl-evolution/pull/26
This adds power assertions to Pkl!
This implements the SPICE described in
https://github.com/apple/pkl-evolution/pull/29
This follows the power assertions style of reporting also found in
Groovy, Kotlin, and others.
* Literal values are not emitted in the diagram
* Stdlib constructors of literals like `List(1, 2)` are also considered
literals
Power assertions are added to:
* Failing type constraints
* Failing test facts
Power assertions are implemented as a truffle instrument to observe
execution.
When an assertion fails, the instrument is created and the assertion is
run again to observe facts.
This incurs runtime overhead to collect facts, but has no impact on code
in the non-error case.
---------
Co-authored-by: Islon Scherer <islonscherer@gmail.com>
Fixes#1309
The issue was that super calls were blocked inside let expressions
because:
1. The compiler's isClassMemberScope() check didn't skip over lambda
scopes created by let expressions
2. The runtime's findSupermethod() didn't traverse past VmFunction
owners to find the actual class prototype
Changes:
- SymbolTable.java: Updated isClassMemberScope() to skip lambda scopes
before checking if the parent is a class or module scope
- InvokeSuperMethodNode.java: Updated findSupermethod() to skip
VmFunction owners when looking for the class prototype
Added regression tests covering:
- Super method calls inside let expressions
- Super property access inside let expressions
- Nested let expressions with super calls
---------
Co-authored-by: Jen Basch <jbasch@apple.com>
Adds convenience methods `isNotEmpty` and `isNotBlank`. This borrows the
same methods from Kotlin.
This helps users write more fluent constraints, for example,
`foo.isNotEmpty.implies(bar)`.
Adds:
* List#isNotEmpty
* Map#isNotEmpty
* Set#isNotEmpty
* Mapping#isNotEmpty
* Listing#isNotEmpty
* String#isNotEmpty
* String#isNotBlank
This fixes an issue where URLs with schemes that contain `+`, `-`, and
`.` would not be parsed correctly.
For example, `foo+bar:///?baz.pkl` would turn into
`foo+bar:///%3Fbaz.pkl`. The query param is lost, and turned into the
path.
Implements a binary renderer for Pkl values, which is a lossless capturing of Pkl data.
This follows the pkl binary format that is already used with `pkl server` calls, and is
made available as a Java API and also an in-language API.
Also, introduces a binary parser into the corresponding `PObject` types in Java.
The backslash needs to be escaped when rendering double-quoted YAML strings.
In addition, this escapes the following characters:
* next line (0x85)
* nbsp (0xa0)
This makes changes to avoid a needless breaking change.
It preserves code like `new Resource { base64 = someValue }.sha256`,
and also code that renders `Resource` into static formats.
Co-authored-by: Islon Scherer <islonscherer@gmail.com>
This PR adds methods to Listing and Mapping that can be used to retrieve members. If the member for the index/key isn't present, it applies default to the index/key. In both cases, this is essentially sugar for getOrNull(<index/key>) ?? default.apply(<index/key>).
Co-authored-by: Daniel Chao <daniel.h.chao@gmail.com>
This introduces a new `Bytes` standard library class, for working with
binary data.
* Add Bytes class to the standard library
* Change CLI to eval `output.bytes`
* Change code generators to map Bytes to respective underlying type
* Add subscript and concat operator support
* Add binary encoding for Bytes
* Add PCF and Plist rendering for Bytes
Co-authored-by: Kushal Pisavadia <kushi.p@gmail.com>
Opaque file URIs are URIs whose scheme-specific part does not start with `/`.
For example, `file:foo/bar.txt` is an opaque URI.
Currently, this has the unintentional behavior of: look for file `foo/bar.txt` from the process working directory.
These are effectively dynamics imports; from a single import, we can't statically analyze what it resolves as.
According to RFC-8089, File URIs must have paths that start with `/`. So, these are actually _not valid URIs_.
See the grammar defined in https://datatracker.ietf.org/doc/html/rfc8089#section-2
This changes Pkl's behavior so that these URIs are treated as errors.
If a generator object literal doesn't add any object members, we
can simply use the parent in its place.
Co-authored-by: Kushal Pisavadia <kushi.p@gmail.com>
This changes the language to check all types eagerly when within a type constraint.
This addresses two regressions in the language:
1. Type constraints are too relaxed (listing/mapping parameters may not be checked)
2. Failing type constraints hide members that were forced during execution of the constraint
If the object member is const, it only needs to be executed once, and all children in the prototype chain can use its cached value.
There is a possible other optimization here, not included in this PR: we can avoid adding these values to the child object's cachedMembers.
Motivation:
* fix known bugs and limitations of for-generators
* improve code health by removing complex workarounds
Changes:
* simplify AstBuilder code related to for-generators
* track for-generators via `SymbolTable.enterForGenerator()`
* add `RestoreForBindingsNode` during initial AST construction
instead of calling `MemberNode.replaceBody()` later on
* simplify some unnecessarily complex code
* remove workarounds and band-aids such as:
* `isInIterable`
* `executeAndSetEagerly`
* adding dummy slots in `AmendFunctionNode`
* overhaul implementation of for-generators
* store keys and values of for-generator iterations in regular instead of auxiliary frame slots
* set them via `TypeNode.executeAndSet()`
* `ResolveVariableNode` no longer needs to search auxiliary slots
* `Read(Enclosing)AuxiliarySlot` is no longer needed
* at the start of each for-generator iteration, create a new `VirtualFrame`
that is a copy of the current frame (arguments + slots)
and stores the iteration key and value in additional slots.
* execute for-generator iteration with the newly created frame
* `childNode.execute(newFrame)`
* Pkl objects created during the iteration will materialize this frame
* store newly created frames in `owner.extraStorage`
if their for-generator slots may be accessed when a generated member is executed
* resolving variable names to for-generator variables at parse time would make this analysis more precise
* when a generated member is executed,
* retrieve the corresponding frame stored in `owner.extraStorage`
* copy the retrieved frame's for-generator slots into slots of the current frame
Result:
* for-generators are implemented in a correct, reasonably simple, and reasonably efficient way
* complexity is fully contained within package `generator` and `AstBuilder`
* for-generator keys and values can be accessed from all nested scopes:
* key and value expressions of generated members
* condition expressions of nested when-generators
* iterable expressions of nested for-generators
* for-generator keys and values can be accessed from within objects created by the expressions listed above
* sibling for-generators can use the same key/value variable names
* parent/child for-generators can use the same key/value variable names
* fixes https://github.com/apple/pkl/issues/741
Motivation:
- simplify implementation of lazy type checking
- fix correctness issues of lazy type checking (#785)
Changes:
- implement listing/mapping type cast via amendment (`parent`) instead of delegation (`delegate`)
- handle type checking of *computed* elements/entries in the same way as type checking of computed properties
- ElementOrEntryNode is the equivalent of TypeCheckedPropertyNode
- remove fields VmListingOrMapping.delegate/typeNodeFrame/cachedMembers/checkedMembers
- fix#785 by executing all type casts between a member's owner and receiver
- fix#823 by storing owner and receiver directly
instead of storing the mutable frame containing them (typeNodeFrame)
- remove overrides of VmObject methods that are no longer required
- good for Truffle partial evaluation and JVM inlining
- revert a85a173faa except for added tests
- move `VmUtils.setOwner` and `VmUtils.setReceiver` and make them private
- these methods aren't generally safe to use
Result:
- simpler code with greater optimization potential
- VmListingOrMapping can now have both a type node and new members
- fewer changes to surrounding code
- smaller memory footprint
- better performance in some cases
- fixes https://github.com/apple/pkl/issues/785
- fixes https://github.com/apple/pkl/issues/823
Potential future optimizations:
- avoid lazy type checking overhead for untyped listings/mappings
- improve efficiency of forcing a typed listing/mapping
- currently, lazy type checking will traverse the parent chain once per member,
reducing the performance benefit of shallow-forcing
a listing/mapping over evaluating each member individually
- avoid creating an intermediate untyped listing/mapping in the following cases:
- `new Listing<X> {...}`
- amendment of `property: Listing<X>`