Skip to content

Commit aa42423

Browse files
committed
Add restrictions against referencing Mapping/Listing/Dynamic.default, Module.output, and any property of an external class
1 parent 14745e7 commit aa42423

15 files changed

Lines changed: 153 additions & 12 deletions

File tree

pkl-core/src/main/java/org/pkl/core/runtime/VmReference.java

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.oracle.truffle.api.frame.VirtualFrame;
1919
import com.oracle.truffle.api.source.SourceSection;
2020
import java.util.ArrayList;
21+
import java.util.Comparator;
2122
import java.util.HashSet;
2223
import java.util.List;
2324
import java.util.Set;
@@ -183,21 +184,30 @@ private static void getCandidatePropertyType(PType type, String property, Set<PT
183184
result.add(type);
184185
return;
185186
}
186-
if (!(type instanceof PType.Class clazz)) {
187-
return;
188-
}
187+
// restriction: only class types can have their properties referenced
188+
if (!(type instanceof PType.Class clazz)) return;
189+
// restriction: cannot reference properties of external classes
190+
if (clazz.getPClass().isExternal()) return;
189191
if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) {
190-
result.add(PType.UNKNOWN);
192+
// restriction: cannot reference Dynamic.default
193+
if (!property.equals("default")) result.add(PType.UNKNOWN);
191194
return;
192195
}
196+
// restriction: cannot reference Listing/Mapping.default
193197
if (clazz.getPClass().getInfo() == PClassInfo.Listing
194-
|| clazz.getPClass().getInfo() == PClassInfo.List
195-
|| clazz.getPClass().getInfo() == PClassInfo.Mapping
196-
|| clazz.getPClass().getInfo() == PClassInfo.Map) {
198+
|| clazz.getPClass().getInfo() == PClassInfo.Mapping) {
197199
return;
198200
}
199-
// Typed
201+
// restriction: cannot reference Module.output.
202+
// generalized: properties originally defined in external classes; the only extant example.
203+
// This is implemented specifically because this is the only case where an external class
204+
// containing a property can be subclassed.
205+
// And this can't check prop.getOwner().isExternal() because fully overriding the property with
206+
// a new type annotation means the owner isn't Module.
207+
if (clazz.getPClass().isModuleClass() && property.equals("output")) return;
208+
200209
var prop = clazz.getPClass().getAllProperties().get(property);
210+
// restriction: cannot reference external properties
201211
if (prop == null || prop.isExternal()) {
202212
return;
203213
}
@@ -376,9 +386,11 @@ public Reference export() {
376386
}
377387

378388
public PType exportReferentType() {
379-
return candidateTypes.size() == 1
380-
? candidateTypes.iterator().next()
381-
: new PType.Union(List.copyOf(candidateTypes));
389+
if (candidateTypes.size() == 1) return candidateTypes.iterator().next();
390+
var types = new ArrayList<>(candidateTypes);
391+
// sort multiple candidate types to ensure stable output
392+
types.sort(Comparator.comparing(Object::toString));
393+
return new PType.Union(types);
382394
}
383395

384396
public PType exportType() {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo: String
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
foo: String
2+
3+
hidden output: ModuleOutput = new {
4+
text = foo
5+
}

pkl-core/src/test/files/LanguageSnippetTests/input/api/reference.pkl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,4 @@ j: K = new {
113113

114114
refInterpolation = "\(aRef.outputs.someListing[1])"
115115
kInterpolation = "\(k)"
116+
aValuesJoined = k.aValues.join("\n").replaceAll(Regex("@[a-z0-9]+"), "@<addr>")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import "pkl:ref"
2+
3+
class D extends ref.Domain {
4+
function referenceToString(data: Any, path: List<ref.Access>): String = throw("not supported")
5+
}
6+
local d: D = new {}
7+
8+
test = ref.Reference(d, Mapping, "").default
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import "pkl:ref"
2+
3+
class D extends ref.Domain {
4+
function referenceToString(data: Any, path: List<ref.Access>): String = throw("not supported")
5+
}
6+
local d: D = new {}
7+
8+
test = ref.Reference(d, Listing, "").default
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import "pkl:ref"
2+
3+
class D extends ref.Domain {
4+
function referenceToString(data: Any, path: List<ref.Access>): String = throw("not supported")
5+
}
6+
local d: D = new {}
7+
8+
test = ref.Reference(d, Dynamic, "").default
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import "pkl:ref"
2+
3+
import ".../input-helper/errors/ReferencedModule.pkl"
4+
5+
class D extends ref.Domain {
6+
function referenceToString(data: Any, path: List<ref.Access>): String = throw("not supported")
7+
}
8+
local d: D = new {}
9+
10+
test = ref.Reference(d, ReferencedModule.getClass(), "").output
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import "pkl:ref"
2+
3+
import ".../input-helper/errors/ReferencedModuleWithOutputOverride.pkl"
4+
5+
class D extends ref.Domain {
6+
function referenceToString(data: Any, path: List<ref.Access>): String = throw("not supported")
7+
}
8+
local d: D = new {}
9+
10+
test = ref.Reference(d, ReferencedModuleWithOutputOverride.getClass(), "").output

pkl-core/src/test/files/LanguageSnippetTests/output/api/reference.pcf

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,12 @@ j {
4848
splitUnion = "${a.outputs.someListing}"
4949
}
5050
refInterpolation = "${a.outputs.someListing[1]}"
51-
kInterpolation = "new K { aId = Reference(new D {}, String, new A { name = \"a\"; id = \"some-a-value\" }).id; bId = Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).id; aProperties = Reference(new D {}, reference#AProperties, new A { name = \"a\"; id = \"some-a-value\" }).outputs; bProperties = Reference(new D {}, reference#BProperties, new B { name = \"b\"; id = \"some-b-value\" }).outputs; aValues { Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.foo; Reference(new D {}, Null | Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someMapping[\"key\"]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someMap[new MapKey { k = 123 }]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someListing[0]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someList[9223372036854775807]; Reference(new D {}, Int, new B { name = \"b\"; id = \"some-b-value\" }).outputs.nonString }; bValues { Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.foo; Reference(new D {}, Null | String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someMapping[\"key\"]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someMap[new MapKey { k = 123 }]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someListing[0]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someList[9223372036854775807]; Reference(new D {}, String, new A { name = \"a\"; id = \"some-a-value\" }).outputs.nonInt }; splitUnion = null }"
51+
kInterpolation = "new K { aId = Reference(new D {}, String, new A { name = \"a\"; id = \"some-a-value\" }).id; bId = Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).id; aProperties = Reference(new D {}, reference#AProperties, new A { name = \"a\"; id = \"some-a-value\" }).outputs; bProperties = Reference(new D {}, reference#BProperties, new B { name = \"b\"; id = \"some-b-value\" }).outputs; aValues { Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.foo; Reference(new D {}, Int | Null, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someMapping[\"key\"]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someMap[new MapKey { k = 123 }]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someListing[0]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someList[9223372036854775807]; Reference(new D {}, Int, new B { name = \"b\"; id = \"some-b-value\" }).outputs.nonString }; bValues { Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.foo; Reference(new D {}, Null | String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someMapping[\"key\"]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someMap[new MapKey { k = 123 }]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someListing[0]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someList[9223372036854775807]; Reference(new D {}, String, new A { name = \"a\"; id = \"some-a-value\" }).outputs.nonInt }; splitUnion = null }"
52+
aValuesJoined = """
53+
org.pkl.core.runtime.VmReference@<addr>
54+
org.pkl.core.runtime.VmReference@<addr>
55+
org.pkl.core.runtime.VmReference@<addr>
56+
org.pkl.core.runtime.VmReference@<addr>
57+
org.pkl.core.runtime.VmReference@<addr>
58+
org.pkl.core.runtime.VmReference@<addr>
59+
"""

0 commit comments

Comments
 (0)