mirror of
https://github.com/apple/pkl.git
synced 2026-05-25 16:19:20 +02:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48a710f439 | |||
| 15d85b0660 | |||
| aeace8bb3c | |||
| 7b850dd6d9 | |||
| c2096f633b | |||
| d6ba021e12 | |||
| 077497d9b8 | |||
| 552b301451 | |||
| d3ac4b288c |
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
name: main
|
name: main
|
||||||
title: Main Project
|
title: Main Project
|
||||||
version: 0.27.0-dev
|
version: 0.27.1
|
||||||
prerelease: true
|
prerelease: false
|
||||||
nav:
|
nav:
|
||||||
- nav.adoc
|
- nav.adoc
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
// the following attributes must be updated immediately before a release
|
// the following attributes must be updated immediately before a release
|
||||||
|
|
||||||
// pkl version corresponding to current git commit without -dev suffix or git hash
|
// pkl version corresponding to current git commit without -dev suffix or git hash
|
||||||
:pkl-version-no-suffix: 0.27.0
|
:pkl-version-no-suffix: 0.27.1
|
||||||
// tells whether pkl version corresponding to current git commit
|
// tells whether pkl version corresponding to current git commit
|
||||||
// is a release version (:is-release-version: '') or dev version (:!is-release-version:)
|
// is a release version (:is-release-version: '') or dev version (:!is-release-version:)
|
||||||
:!is-release-version:
|
:is-release-version: ''
|
||||||
|
|
||||||
// the remaining attributes do not need to be updated regularly
|
// the remaining attributes do not need to be updated regularly
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
= Pkl 0.27 Release Notes
|
= Pkl 0.27 Release Notes
|
||||||
:version: 0.27
|
:version: 0.27
|
||||||
:version-minor: 0.27.0
|
:version-minor: 0.27.1
|
||||||
:release-date: November 5th, 2024
|
:release-date: November 5th, 2024
|
||||||
|
|
||||||
include::ROOT:partial$component-attributes.adoc[]
|
include::ROOT:partial$component-attributes.adoc[]
|
||||||
@@ -377,7 +377,7 @@ These are:
|
|||||||
|
|
||||||
The `String` to `Number` converter methods, for example, {uri-stdlib-StringToInt}[`String.toInt()`], can now handle underscore separators (https://github.com/apple/pkl/pull/578[#578], https://github.com/apple/pkl/pull/580[#580]).
|
The `String` to `Number` converter methods, for example, {uri-stdlib-StringToInt}[`String.toInt()`], can now handle underscore separators (https://github.com/apple/pkl/pull/578[#578], https://github.com/apple/pkl/pull/580[#580]).
|
||||||
|
|
||||||
This better aligns with the source code representation of file:///Users/danielchao/code/apple/pkl-lang.org/build/local/main/current/language-reference/index.html#integers[number literals].
|
This better aligns with the source code representation of xref:language-reference:index.adoc#integers[number literals].
|
||||||
|
|
||||||
[source,pkl]
|
[source,pkl]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -1,6 +1,28 @@
|
|||||||
= Changelog
|
= Changelog
|
||||||
include::ROOT:partial$component-attributes.adoc[]
|
include::ROOT:partial$component-attributes.adoc[]
|
||||||
|
|
||||||
|
[[release-0.27.1]]
|
||||||
|
== 0.27.1 (2024-12-06)
|
||||||
|
|
||||||
|
=== Fixes
|
||||||
|
|
||||||
|
- Fixes a broken "number literals" link in the 0.27 release notes (https://github.com/apple/pkl/pull/784[#784]).
|
||||||
|
- Fixes a possible deadlock during external reader process close (https://github.com/apple/pkl/pull/786[#786]).
|
||||||
|
- Fixes counting elements with computed indices multiple times in length computation of listings (https://github.com/apple/pkl/pull/797[#797]).
|
||||||
|
- Fixes non Pkl modules being reported in GatherImports task, leading to plugin failures (https://github.com/apple/pkl/pull/821[#821]).
|
||||||
|
- Fixes a problem where the delegate chain of type casts for Listing/Mapping get unreasonably big, even though the type nodes are the same, which may lead to a stack overflow or performance degradation (https://github.com/apple/pkl/pull/826[#826]).
|
||||||
|
- Fixes incorrect scoping of type variables in lazy Listing/Mapping type checking in cross-module typealiases (https://github.com/apple/pkl/pull/789[#789]).
|
||||||
|
- Fixes regression in type checking logic for Listing/Mapping (https://github.com/apple/pkl/pull/789[#789]).
|
||||||
|
|
||||||
|
=== Contributors ❤️
|
||||||
|
|
||||||
|
Thank you to all the contributors for this release!
|
||||||
|
|
||||||
|
* link:https://github.com/GUI[@GUI]
|
||||||
|
* link:https://github.com/HT154[@HT154]
|
||||||
|
* link:https://github.com/odenix[@odenix] (formerly @translatenix)
|
||||||
|
|
||||||
|
|
||||||
[[release-0.27.0]]
|
[[release-0.27.0]]
|
||||||
== 0.27.0 (2024-11-05)
|
== 0.27.0 (2024-11-05)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# suppress inspection "UnusedProperty" for whole file
|
# suppress inspection "UnusedProperty" for whole file
|
||||||
|
|
||||||
group=org.pkl-lang
|
group=org.pkl-lang
|
||||||
version=0.27.0
|
version=0.27.1
|
||||||
|
|
||||||
# google-java-format requires jdk.compiler exports
|
# google-java-format requires jdk.compiler exports
|
||||||
org.gradle.jvmargs= \
|
org.gradle.jvmargs= \
|
||||||
|
|||||||
+3
-3
@@ -2,15 +2,15 @@
|
|||||||
"catalogs": {},
|
"catalogs": {},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"pkl": {
|
"pkl": {
|
||||||
"script-ref": "org.pkl-lang:pkl-cli-java:0.26.3",
|
"script-ref": "org.pkl-lang:pkl-cli-java:0.27.1",
|
||||||
"java-agents": []
|
"java-agents": []
|
||||||
},
|
},
|
||||||
"pkl-codegen-java": {
|
"pkl-codegen-java": {
|
||||||
"script-ref": "org.pkl-lang:pkl-codegen-java:0.26.3",
|
"script-ref": "org.pkl-lang:pkl-codegen-java:0.27.1",
|
||||||
"java-agents": []
|
"java-agents": []
|
||||||
},
|
},
|
||||||
"pkl-codegen-kotlin": {
|
"pkl-codegen-kotlin": {
|
||||||
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.26.3",
|
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.27.1",
|
||||||
"java-agents": []
|
"java-agents": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.nio.file.Path
|
|||||||
import java.util.stream.Collectors
|
import java.util.stream.Collectors
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.*
|
||||||
import org.assertj.core.api.Assertions.fail
|
import org.assertj.core.api.Assertions.fail
|
||||||
|
import org.opentest4j.AssertionFailedError
|
||||||
import org.pkl.commons.*
|
import org.pkl.commons.*
|
||||||
|
|
||||||
object FileTestUtils {
|
object FileTestUtils {
|
||||||
@@ -110,5 +111,11 @@ data class SnippetOutcome(val expectedOutFile: Path, val actual: String, val suc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun failWithDiff(message: String): Nothing =
|
private fun failWithDiff(message: String): Nothing =
|
||||||
|
if (System.getProperty("sun.java.command", "").contains("intellij")) {
|
||||||
|
// IntelliJ only shows diffs for AssertionFailedError
|
||||||
|
throw AssertionFailedError(message, expected, actual)
|
||||||
|
} else {
|
||||||
|
// Gradle test logging/report only shows diffs for PklAssertionFailedError
|
||||||
throw PklAssertionFailedError(message, expected, actual)
|
throw PklAssertionFailedError(message, expected, actual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1221,7 +1221,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
member.initConstantValue(constantNode);
|
member.initConstantValue(constantNode);
|
||||||
} else {
|
} else {
|
||||||
member.initMemberNode(
|
member.initMemberNode(
|
||||||
new UntypedObjectMemberNode(
|
ElementOrEntryNodeGen.create(
|
||||||
language, scope.buildFrameDescriptor(), member, elementNode));
|
language, scope.buildFrameDescriptor(), member, elementNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1278,7 +1278,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
member.initConstantValue(constantNode);
|
member.initConstantValue(constantNode);
|
||||||
} else {
|
} else {
|
||||||
member.initMemberNode(
|
member.initMemberNode(
|
||||||
new UntypedObjectMemberNode(
|
ElementOrEntryNodeGen.create(
|
||||||
language, scope.buildFrameDescriptor(), member, valueNode));
|
language, scope.buildFrameDescriptor(), member, valueNode));
|
||||||
}
|
}
|
||||||
} else { // ["key"] { ... }
|
} else { // ["key"] { ... }
|
||||||
@@ -1287,7 +1287,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
objectBodyCtxs,
|
objectBodyCtxs,
|
||||||
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
|
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
|
||||||
member.initMemberNode(
|
member.initMemberNode(
|
||||||
new UntypedObjectMemberNode(
|
ElementOrEntryNodeGen.create(
|
||||||
language, scope.buildFrameDescriptor(), member, objectBody));
|
language, scope.buildFrameDescriptor(), member, objectBody));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2446,6 +2446,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
|
|
||||||
return new UnresolvedTypeNode.Parameterized(
|
return new UnresolvedTypeNode.Parameterized(
|
||||||
createSourceSection(ctx),
|
createSourceSection(ctx),
|
||||||
|
language,
|
||||||
doVisitTypeName(idCtx),
|
doVisitTypeName(idCtx),
|
||||||
argCtx.ts.stream().map(this::visitType).toArray(UnresolvedTypeNode[]::new));
|
argCtx.ts.stream().map(this::visitType).toArray(UnresolvedTypeNode[]::new));
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -103,7 +103,7 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
|
|||||||
var callTarget = member.getCallTarget();
|
var callTarget = member.getCallTarget();
|
||||||
value = callTarget.call(parent, owner, key);
|
value = callTarget.call(parent, owner, key);
|
||||||
}
|
}
|
||||||
owner.setCachedValue(key, value, member);
|
owner.setCachedValue(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.setAuxiliarySlot(customThisSlot, value);
|
frame.setAuxiliarySlot(customThisSlot, value);
|
||||||
|
|||||||
@@ -43,15 +43,15 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
@Children private final ExpressionNode[] keyNodes;
|
@Children private final ExpressionNode[] keyNodes;
|
||||||
private final ObjectMember[] values;
|
private final ObjectMember[] values;
|
||||||
|
|
||||||
public EntriesLiteralNode(
|
protected EntriesLiteralNode(
|
||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
VmLanguage language,
|
VmLanguage language,
|
||||||
// contains local properties and default property (if present)
|
|
||||||
// does *not* contain entries with constant keys to maintain definition order of entries
|
|
||||||
String qualifiedScopeName,
|
String qualifiedScopeName,
|
||||||
boolean isCustomThisScope,
|
boolean isCustomThisScope,
|
||||||
@Nullable FrameDescriptor parametersDescriptor,
|
@Nullable FrameDescriptor parametersDescriptor,
|
||||||
UnresolvedTypeNode[] parameterTypes,
|
UnresolvedTypeNode[] parameterTypes,
|
||||||
|
// contains local properties and default property (if present)
|
||||||
|
// does *not* contain entries with constant keys to maintain definition order of entries
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
ExpressionNode[] keyNodes,
|
ExpressionNode[] keyNodes,
|
||||||
ObjectMember[] values) {
|
ObjectMember[] values) {
|
||||||
@@ -103,7 +103,8 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
frame.materialize(),
|
frame.materialize(),
|
||||||
parent,
|
parent,
|
||||||
createListMembers(frame, parent.getLength()),
|
createListMembers(frame, parent.getLength()),
|
||||||
parent.getLength() + keyNodes.length);
|
// `[x] = y` overrides existing element and doesn't increase length
|
||||||
|
parent.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
|
|||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = callNode.call(objReceiver, owner, property.getName());
|
result = callNode.call(objReceiver, owner, property.getName());
|
||||||
objReceiver.setCachedValue(property, result, property);
|
objReceiver.setCachedValue(property, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -184,12 +184,12 @@ public final class ResolveVariableNode extends ExpressionNode {
|
|||||||
if (member != null) {
|
if (member != null) {
|
||||||
var constantValue = member.getConstantValue();
|
var constantValue = member.getConstantValue();
|
||||||
if (constantValue != null) {
|
if (constantValue != null) {
|
||||||
baseModule.setCachedValue(variableName, constantValue, member);
|
baseModule.setCachedValue(variableName, constantValue);
|
||||||
return new ConstantValueNode(sourceSection, constantValue);
|
return new ConstantValueNode(sourceSection, constantValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
var computedValue = member.getCallTarget().call(baseModule, baseModule);
|
var computedValue = member.getCallTarget().call(baseModule, baseModule);
|
||||||
baseModule.setCachedValue(variableName, computedValue, member);
|
baseModule.setCachedValue(variableName, computedValue);
|
||||||
return new ConstantValueNode(sourceSection, computedValue);
|
return new ConstantValueNode(sourceSection, computedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2024 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.ast.member;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.dsl.Cached;
|
||||||
|
import com.oracle.truffle.api.dsl.Cached.Shared;
|
||||||
|
import com.oracle.truffle.api.dsl.Executed;
|
||||||
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||||
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
|
import org.pkl.core.ast.expression.primary.GetReceiverNode;
|
||||||
|
import org.pkl.core.runtime.VmDynamic;
|
||||||
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
|
import org.pkl.core.runtime.VmListing;
|
||||||
|
import org.pkl.core.runtime.VmMapping;
|
||||||
|
import org.pkl.core.runtime.VmUtils;
|
||||||
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
|
/** Equivalent of {@link TypedPropertyNode} for elements/entries. */
|
||||||
|
public abstract class ElementOrEntryNode extends RegularMemberNode {
|
||||||
|
@Child @Executed protected ExpressionNode receiverNode = new GetReceiverNode();
|
||||||
|
|
||||||
|
protected ElementOrEntryNode(
|
||||||
|
@Nullable VmLanguage language,
|
||||||
|
FrameDescriptor descriptor,
|
||||||
|
ObjectMember member,
|
||||||
|
ExpressionNode bodyNode) {
|
||||||
|
|
||||||
|
super(language, descriptor, member, bodyNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Specialization
|
||||||
|
protected Object evalListing(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmListing receiver,
|
||||||
|
@Cached("create()") @Shared("callNode") IndirectCallNode callNode) {
|
||||||
|
var result = executeBody(frame);
|
||||||
|
return VmUtils.shouldRunTypeCheck(frame)
|
||||||
|
? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null)
|
||||||
|
: result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Specialization
|
||||||
|
protected Object evalMapping(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmMapping receiver,
|
||||||
|
@Cached("create()") @Shared("callNode") IndirectCallNode callNode) {
|
||||||
|
var result = executeBody(frame);
|
||||||
|
return VmUtils.shouldRunTypeCheck(frame)
|
||||||
|
? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null)
|
||||||
|
: result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Specialization
|
||||||
|
protected Object evalDynamic(VirtualFrame frame, VmDynamic ignored) {
|
||||||
|
return executeBody(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ import org.pkl.core.runtime.VmLanguage;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
/** Performs a typecast on a Mapping entry value, or a Listing element. */
|
/** Performs a typecast on a Mapping entry value, or a Listing element. */
|
||||||
public class ListingOrMappingTypeCastNode extends PklRootNode {
|
public final class ListingOrMappingTypeCastNode extends PklRootNode {
|
||||||
|
|
||||||
@Child private TypeNode typeNode;
|
@Child private TypeNode typeNode;
|
||||||
private final String qualifiedName;
|
private final String qualifiedName;
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public abstract class ResolveDeclaredTypeNode extends ExpressionNode {
|
|||||||
var result = module.getCachedValue(importName);
|
var result = module.getCachedValue(importName);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = callNode.call(member.getCallTarget(), module, module, importName);
|
result = callNode.call(member.getCallTarget(), module, module, importName);
|
||||||
module.setCachedValue(importName, result, member);
|
module.setCachedValue(importName, result);
|
||||||
}
|
}
|
||||||
return (VmTyped) result;
|
return (VmTyped) result;
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ public abstract class ResolveDeclaredTypeNode extends ExpressionNode {
|
|||||||
var result = module.getCachedValue(typeName);
|
var result = module.getCachedValue(typeName);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = callNode.call(member.getCallTarget(), module, module, typeName);
|
result = callNode.call(member.getCallTarget(), module, module, typeName);
|
||||||
module.setCachedValue(typeName, result, member);
|
module.setCachedValue(typeName, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
|||||||
import com.oracle.truffle.api.dsl.Cached;
|
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.Frame;
|
||||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
import com.oracle.truffle.api.frame.FrameSlotKind;
|
import com.oracle.truffle.api.frame.FrameSlotKind;
|
||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
@@ -161,7 +162,11 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Tells if this typenode is the same typecheck as the other typenode. */
|
/** Tells if this typenode is the same typecheck as the other typenode. */
|
||||||
public abstract boolean isEquivalentTo(TypeNode other);
|
public boolean isEquivalentTo(TypeNode other) {
|
||||||
|
return this == other || doIsEquivalentTo(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean doIsEquivalentTo(TypeNode other);
|
||||||
|
|
||||||
public @Nullable VmClass getVmClass() {
|
public @Nullable VmClass getVmClass() {
|
||||||
return null;
|
return null;
|
||||||
@@ -304,7 +309,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof UnknownTypeNode;
|
return other instanceof UnknownTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +376,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof NothingTypeNode;
|
return other instanceof NothingTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,7 +413,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof FinalModuleTypeNode finalModuleTypeNode)) {
|
if (!(other instanceof FinalModuleTypeNode finalModuleTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -460,7 +465,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof NonFinalModuleTypeNode nonFinalModuleTypeNode)) {
|
if (!(other instanceof NonFinalModuleTypeNode nonFinalModuleTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -496,7 +501,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof StringLiteralTypeNode stringLiteralTypeNode)) {
|
if (!(other instanceof StringLiteralTypeNode stringLiteralTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -551,7 +556,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof TypedTypeNode;
|
return other instanceof TypedTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +591,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof DynamicTypeNode;
|
return other instanceof DynamicTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,7 +641,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof FinalClassTypeNode finalClassTypeNode)) {
|
if (!(other instanceof FinalClassTypeNode finalClassTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -705,7 +710,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof NonFinalClassTypeNode nonFinalClassTypeNode)) {
|
if (!(other instanceof NonFinalClassTypeNode nonFinalClassTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -772,7 +777,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof NullableTypeNode nullableTypeNode)) {
|
if (!(other instanceof NullableTypeNode nullableTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -842,7 +847,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ExplodeLoop
|
@ExplodeLoop
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof UnionTypeNode unionTypeNode)) {
|
if (!(other instanceof UnionTypeNode unionTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1016,7 +1021,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof UnionOfStringLiteralsTypeNode unionOfStringLiteralsTypeNode)) {
|
if (!(other instanceof UnionOfStringLiteralsTypeNode unionOfStringLiteralsTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1092,7 +1097,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1223,7 +1228,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof ListTypeNode listTypeNode)) {
|
if (!(other instanceof ListTypeNode listTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1268,7 +1273,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof SetTypeNode setTypeNode)) {
|
if (!(other instanceof SetTypeNode setTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1360,7 +1365,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof MapTypeNode mapTypeNode)) {
|
if (!(other instanceof MapTypeNode mapTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1414,8 +1419,9 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class ListingTypeNode extends ListingOrMappingTypeNode {
|
public static final class ListingTypeNode extends ListingOrMappingTypeNode {
|
||||||
public ListingTypeNode(SourceSection sourceSection, TypeNode valueTypeNode) {
|
public ListingTypeNode(
|
||||||
super(sourceSection, null, valueTypeNode);
|
SourceSection sourceSection, VmLanguage language, TypeNode valueTypeNode) {
|
||||||
|
super(sourceSection, language, null, valueTypeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1423,7 +1429,17 @@ public abstract class TypeNode extends PklNode {
|
|||||||
if (!(value instanceof VmListing vmListing)) {
|
if (!(value instanceof VmListing vmListing)) {
|
||||||
throw typeMismatch(value, BaseModule.getListingClass());
|
throw typeMismatch(value, BaseModule.getListingClass());
|
||||||
}
|
}
|
||||||
return doTypeCast(frame, vmListing);
|
if (vmListing.isValueTypeKnownSubtypeOf(valueTypeNode)) {
|
||||||
|
return vmListing;
|
||||||
|
}
|
||||||
|
return new VmListing(
|
||||||
|
vmListing.getEnclosingFrame(),
|
||||||
|
vmListing,
|
||||||
|
EconomicMaps.emptyMap(),
|
||||||
|
vmListing.getLength(),
|
||||||
|
getValueTypeCastNode(),
|
||||||
|
VmUtils.getReceiver(frame),
|
||||||
|
VmUtils.getOwner(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1451,7 +1467,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof ListingTypeNode listingTypeNode)) {
|
if (!(other instanceof ListingTypeNode listingTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1466,9 +1482,12 @@ public abstract class TypeNode extends PklNode {
|
|||||||
|
|
||||||
public static final class MappingTypeNode extends ListingOrMappingTypeNode {
|
public static final class MappingTypeNode extends ListingOrMappingTypeNode {
|
||||||
public MappingTypeNode(
|
public MappingTypeNode(
|
||||||
SourceSection sourceSection, TypeNode keyTypeNode, TypeNode valueTypeNode) {
|
SourceSection sourceSection,
|
||||||
|
VmLanguage language,
|
||||||
|
TypeNode keyTypeNode,
|
||||||
|
TypeNode valueTypeNode) {
|
||||||
|
|
||||||
super(sourceSection, keyTypeNode, valueTypeNode);
|
super(sourceSection, language, keyTypeNode, valueTypeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1478,7 +1497,16 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
// execute type checks on mapping keys
|
// execute type checks on mapping keys
|
||||||
doEagerCheck(frame, vmMapping, false, true);
|
doEagerCheck(frame, vmMapping, false, true);
|
||||||
return doTypeCast(frame, vmMapping);
|
if (vmMapping.isValueTypeKnownSubtypeOf(valueTypeNode)) {
|
||||||
|
return vmMapping;
|
||||||
|
}
|
||||||
|
return new VmMapping(
|
||||||
|
vmMapping.getEnclosingFrame(),
|
||||||
|
vmMapping,
|
||||||
|
EconomicMaps.emptyMap(),
|
||||||
|
getValueTypeCastNode(),
|
||||||
|
VmUtils.getReceiver(frame),
|
||||||
|
VmUtils.getOwner(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1509,7 +1537,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof MappingTypeNode mappingTypeNode)) {
|
if (!(other instanceof MappingTypeNode mappingTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1526,17 +1554,22 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class ListingOrMappingTypeNode extends ObjectSlotTypeNode {
|
public abstract static class ListingOrMappingTypeNode extends ObjectSlotTypeNode {
|
||||||
|
private final VmLanguage language;
|
||||||
@Child protected @Nullable TypeNode keyTypeNode;
|
@Child protected @Nullable TypeNode keyTypeNode;
|
||||||
@Child protected TypeNode valueTypeNode;
|
@Child protected TypeNode valueTypeNode;
|
||||||
@Child @Nullable protected ListingOrMappingTypeCastNode listingOrMappingTypeCastNode;
|
@Child @Nullable protected ListingOrMappingTypeCastNode valueTypeCastNode;
|
||||||
|
|
||||||
private final boolean skipKeyTypeChecks;
|
private final boolean skipKeyTypeChecks;
|
||||||
private final boolean skipValueTypeChecks;
|
private final boolean skipValueTypeChecks;
|
||||||
|
|
||||||
protected ListingOrMappingTypeNode(
|
protected ListingOrMappingTypeNode(
|
||||||
SourceSection sourceSection, @Nullable TypeNode keyTypeNode, TypeNode valueTypeNode) {
|
SourceSection sourceSection,
|
||||||
|
VmLanguage language,
|
||||||
|
@Nullable TypeNode keyTypeNode,
|
||||||
|
TypeNode valueTypeNode) {
|
||||||
|
|
||||||
super(sourceSection);
|
super(sourceSection);
|
||||||
|
this.language = language;
|
||||||
this.keyTypeNode = keyTypeNode;
|
this.keyTypeNode = keyTypeNode;
|
||||||
this.valueTypeNode = valueTypeNode;
|
this.valueTypeNode = valueTypeNode;
|
||||||
|
|
||||||
@@ -1556,17 +1589,14 @@ public abstract class TypeNode extends PklNode {
|
|||||||
return valueTypeNode;
|
return valueTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ListingOrMappingTypeCastNode getListingOrMappingTypeCastNode() {
|
protected ListingOrMappingTypeCastNode getValueTypeCastNode() {
|
||||||
if (listingOrMappingTypeCastNode == null) {
|
if (valueTypeCastNode == null) {
|
||||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||||
listingOrMappingTypeCastNode =
|
valueTypeCastNode =
|
||||||
new ListingOrMappingTypeCastNode(
|
new ListingOrMappingTypeCastNode(
|
||||||
VmLanguage.get(this),
|
language, new FrameDescriptor(), valueTypeNode, getRootNode().getName());
|
||||||
getRootNode().getFrameDescriptor(),
|
|
||||||
valueTypeNode,
|
|
||||||
getRootNode().getName());
|
|
||||||
}
|
}
|
||||||
return listingOrMappingTypeCastNode;
|
return valueTypeCastNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// either (if defaultMemberValue != null):
|
// either (if defaultMemberValue != null):
|
||||||
@@ -1647,15 +1677,6 @@ public abstract class TypeNode extends PklNode {
|
|||||||
EconomicMaps.of(Identifier.DEFAULT, defaultMember));
|
EconomicMaps.of(Identifier.DEFAULT, defaultMember));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T extends VmListingOrMapping<T>> T doTypeCast(VirtualFrame frame, T original) {
|
|
||||||
// optimization: don't create new object if the original already has the same typecheck, or if
|
|
||||||
// this typecheck is a no-op.
|
|
||||||
if (isNoopTypeCheck() || original.hasSameChecksAs(valueTypeNode)) {
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
return original.withCheckedMembers(getListingOrMappingTypeCastNode(), frame.materialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doEagerCheck(VirtualFrame frame, VmObject object) {
|
protected void doEagerCheck(VirtualFrame frame, VmObject object) {
|
||||||
doEagerCheck(frame, object, skipKeyTypeChecks, skipValueTypeChecks);
|
doEagerCheck(frame, object, skipKeyTypeChecks, skipValueTypeChecks);
|
||||||
}
|
}
|
||||||
@@ -1700,7 +1721,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
var callTarget = member.getCallTarget();
|
var callTarget = member.getCallTarget();
|
||||||
memberValue = callTarget.call(object, owner, memberKey);
|
memberValue = callTarget.call(object, owner, memberKey);
|
||||||
}
|
}
|
||||||
object.setCachedValue(memberKey, memberValue, member);
|
object.setCachedValue(memberKey, memberValue);
|
||||||
}
|
}
|
||||||
valueTypeNode.executeEagerly(frame, memberValue);
|
valueTypeNode.executeEagerly(frame, memberValue);
|
||||||
}
|
}
|
||||||
@@ -1748,7 +1769,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ExplodeLoop
|
@ExplodeLoop
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof FunctionTypeNode functionTypeNode)) {
|
if (!(other instanceof FunctionTypeNode functionTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1845,7 +1866,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof FunctionClassTypeNode functionClassTypeNode)) {
|
if (!(other instanceof FunctionClassTypeNode functionClassTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1883,7 +1904,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ExplodeLoop
|
@ExplodeLoop
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof FunctionNClassTypeNode functionNClassTypeNode)) {
|
if (!(other instanceof FunctionNClassTypeNode functionNClassTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1986,7 +2007,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof PairTypeNode pairTypeNode)) {
|
if (!(other instanceof PairTypeNode pairTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2043,7 +2064,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if (!(other instanceof VarArgsTypeNode varArgsTypeNode)) {
|
if (!(other instanceof VarArgsTypeNode varArgsTypeNode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2095,7 +2116,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof TypeVariableNode;
|
return other instanceof TypeVariableNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2135,7 +2156,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof NonNullTypeAliasTypeNode;
|
return other instanceof NonNullTypeAliasTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2184,7 +2205,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof UIntTypeAliasTypeNode aliasTypeNode && mask == aliasTypeNode.mask;
|
return other instanceof UIntTypeAliasTypeNode aliasTypeNode && mask == aliasTypeNode.mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2228,7 +2249,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof Int8TypeAliasTypeNode;
|
return other instanceof Int8TypeAliasTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2272,7 +2293,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof Int16TypeAliasTypeNode;
|
return other instanceof Int16TypeAliasTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2316,7 +2337,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof Int32TypeAliasTypeNode;
|
return other instanceof Int32TypeAliasTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2387,14 +2408,14 @@ public abstract class TypeNode extends PklNode {
|
|||||||
public Object execute(VirtualFrame frame, Object value) {
|
public Object execute(VirtualFrame frame, Object value) {
|
||||||
var prevOwner = VmUtils.getOwner(frame);
|
var prevOwner = VmUtils.getOwner(frame);
|
||||||
var prevReceiver = VmUtils.getReceiver(frame);
|
var prevReceiver = VmUtils.getReceiver(frame);
|
||||||
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
||||||
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return aliasedTypeNode.execute(frame, value);
|
return aliasedTypeNode.execute(frame, value);
|
||||||
} finally {
|
} finally {
|
||||||
VmUtils.setOwner(frame, prevOwner);
|
setOwner(frame, prevOwner);
|
||||||
VmUtils.setReceiver(frame, prevReceiver);
|
setReceiver(frame, prevReceiver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2403,14 +2424,14 @@ public abstract class TypeNode extends PklNode {
|
|||||||
public Object executeAndSet(VirtualFrame frame, Object value) {
|
public Object executeAndSet(VirtualFrame frame, Object value) {
|
||||||
var prevOwner = VmUtils.getOwner(frame);
|
var prevOwner = VmUtils.getOwner(frame);
|
||||||
var prevReceiver = VmUtils.getReceiver(frame);
|
var prevReceiver = VmUtils.getReceiver(frame);
|
||||||
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
||||||
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return aliasedTypeNode.executeAndSet(frame, value);
|
return aliasedTypeNode.executeAndSet(frame, value);
|
||||||
} finally {
|
} finally {
|
||||||
VmUtils.setOwner(frame, prevOwner);
|
setOwner(frame, prevOwner);
|
||||||
VmUtils.setReceiver(frame, prevReceiver);
|
setReceiver(frame, prevReceiver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2419,14 +2440,14 @@ public abstract class TypeNode extends PklNode {
|
|||||||
public Object executeEagerlyAndSet(VirtualFrame frame, Object value) {
|
public Object executeEagerlyAndSet(VirtualFrame frame, Object value) {
|
||||||
var prevOwner = VmUtils.getOwner(frame);
|
var prevOwner = VmUtils.getOwner(frame);
|
||||||
var prevReceiver = VmUtils.getReceiver(frame);
|
var prevReceiver = VmUtils.getReceiver(frame);
|
||||||
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
||||||
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return aliasedTypeNode.executeEagerlyAndSet(frame, value);
|
return aliasedTypeNode.executeEagerlyAndSet(frame, value);
|
||||||
} finally {
|
} finally {
|
||||||
VmUtils.setOwner(frame, prevOwner);
|
setOwner(frame, prevOwner);
|
||||||
VmUtils.setReceiver(frame, prevReceiver);
|
setReceiver(frame, prevReceiver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2471,7 +2492,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
if ((other instanceof TypeAliasTypeNode typeAliasTypeNode)) {
|
if ((other instanceof TypeAliasTypeNode typeAliasTypeNode)) {
|
||||||
return aliasedTypeNode.isEquivalentTo(typeAliasTypeNode.aliasedTypeNode);
|
return aliasedTypeNode.isEquivalentTo(typeAliasTypeNode.aliasedTypeNode);
|
||||||
}
|
}
|
||||||
@@ -2498,6 +2519,22 @@ public abstract class TypeNode extends PklNode {
|
|||||||
protected boolean isParametric() {
|
protected boolean isParametric() {
|
||||||
return typeArgumentNodes.length > 0;
|
return typeArgumentNodes.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that mutating a frame's receiver and owner argument is very risky
|
||||||
|
// because any VmObject instantiated within the same root node execution
|
||||||
|
// holds a reference to (not immutable snapshot of) the frame
|
||||||
|
// via VmObjectLike.enclosingFrame.
|
||||||
|
// *Maybe* this works out for TypeAliasTypeNode because an object instantiated
|
||||||
|
// within a type constraint doesn't escape the constraint expression.
|
||||||
|
// If mutating receiver and owner can't be avoided, it would be safer
|
||||||
|
// to have VmObjectLike store them directly instead of storing enclosingFrame.
|
||||||
|
private static void setReceiver(Frame frame, Object receiver) {
|
||||||
|
frame.getArguments()[0] = receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setOwner(Frame frame, VmObjectLike owner) {
|
||||||
|
frame.getArguments()[1] = owner;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class ConstrainedTypeNode extends TypeNode {
|
public static final class ConstrainedTypeNode extends TypeNode {
|
||||||
@@ -2581,7 +2618,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
// consider constrained types as always different
|
// consider constrained types as always different
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2622,7 +2659,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof AnyTypeNode;
|
return other instanceof AnyTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2650,7 +2687,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof StringTypeNode;
|
return other instanceof StringTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2714,7 +2751,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof NumberTypeNode;
|
return other instanceof NumberTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2742,7 +2779,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof IntTypeNode;
|
return other instanceof IntTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2787,7 +2824,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof FloatTypeNode;
|
return other instanceof FloatTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2832,7 +2869,7 @@ public abstract class TypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalentTo(TypeNode other) {
|
public boolean doIsEquivalentTo(TypeNode other) {
|
||||||
return other instanceof BooleanTypeNode;
|
return other instanceof BooleanTypeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,14 +197,17 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class Parameterized extends UnresolvedTypeNode {
|
public static final class Parameterized extends UnresolvedTypeNode {
|
||||||
|
private final VmLanguage language;
|
||||||
@Child private ExpressionNode resolveTypeNode;
|
@Child private ExpressionNode resolveTypeNode;
|
||||||
@Children private final UnresolvedTypeNode[] typeArgumentNodes;
|
@Children private final UnresolvedTypeNode[] typeArgumentNodes;
|
||||||
|
|
||||||
public Parameterized(
|
public Parameterized(
|
||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
|
VmLanguage language,
|
||||||
ExpressionNode resolveTypeNode,
|
ExpressionNode resolveTypeNode,
|
||||||
UnresolvedTypeNode[] typeArgumentNodes) {
|
UnresolvedTypeNode[] typeArgumentNodes) {
|
||||||
super(sourceSection);
|
super(sourceSection);
|
||||||
|
this.language = language;
|
||||||
this.resolveTypeNode = resolveTypeNode;
|
this.resolveTypeNode = resolveTypeNode;
|
||||||
this.typeArgumentNodes = typeArgumentNodes;
|
this.typeArgumentNodes = typeArgumentNodes;
|
||||||
}
|
}
|
||||||
@@ -238,12 +241,13 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (clazz.isListingClass()) {
|
if (clazz.isListingClass()) {
|
||||||
return new ListingTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
|
return new ListingTypeNode(sourceSection, language, typeArgumentNodes[0].execute(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clazz.isMappingClass()) {
|
if (clazz.isMappingClass()) {
|
||||||
return new MappingTypeNode(
|
return new MappingTypeNode(
|
||||||
sourceSection,
|
sourceSection,
|
||||||
|
language,
|
||||||
typeArgumentNodes[0].execute(frame),
|
typeArgumentNodes[0].execute(frame),
|
||||||
typeArgumentNodes[1].execute(frame));
|
typeArgumentNodes[1].execute(frame));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,16 @@ package org.pkl.core.externalreader;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ProcessBuilder.Redirect;
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import org.pkl.core.Duration;
|
|
||||||
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings.ExternalReader;
|
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings.ExternalReader;
|
||||||
import org.pkl.core.externalreader.ExternalReaderMessages.*;
|
import org.pkl.core.externalreader.ExternalReaderMessages.*;
|
||||||
import org.pkl.core.messaging.MessageTransport;
|
import org.pkl.core.messaging.MessageTransport;
|
||||||
@@ -152,40 +151,23 @@ final class ExternalReaderProcessImpl implements ExternalReaderProcess {
|
|||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
if (closed) return;
|
||||||
closed = true;
|
closed = true;
|
||||||
if (process == null || !process.isAlive()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (transport != null) {
|
if (transport != null && process != null && process.isAlive()) {
|
||||||
transport.send(new CloseExternalProcess());
|
transport.send(new CloseExternalProcess());
|
||||||
transport.close();
|
process.waitFor(CLOSE_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
// forcefully stop the process after the timeout
|
|
||||||
// note that both transport.close() and process.destroy() are safe to call multiple times
|
|
||||||
new Timer()
|
|
||||||
.schedule(
|
|
||||||
new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (process != null) {
|
|
||||||
transport.close();
|
|
||||||
process.destroyForcibly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
CLOSE_TIMEOUT.inWholeMillis());
|
|
||||||
|
|
||||||
// block on process exit
|
|
||||||
process.onExit().get();
|
|
||||||
} catch (Exception e) {
|
|
||||||
transport.close();
|
|
||||||
process.destroyForcibly();
|
|
||||||
} finally {
|
} finally {
|
||||||
process = null;
|
if (process != null) {
|
||||||
transport = null;
|
// no-op unless process is alive
|
||||||
|
process.destroyForcibly();
|
||||||
|
}
|
||||||
|
if (transport != null) {
|
||||||
|
transport.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public final class VmFunction extends VmObjectLike {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
|
public void setCachedValue(Object key, Object value) {
|
||||||
throw new VmExceptionBuilder()
|
throw new VmExceptionBuilder()
|
||||||
.bug("Class `VmFunction` does not support method `setCachedValue()`.")
|
.bug("Class `VmFunction` does not support method `setCachedValue()`.")
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import org.pkl.core.util.Nullable;
|
|||||||
@TruffleLanguage.Registration(
|
@TruffleLanguage.Registration(
|
||||||
id = "pkl",
|
id = "pkl",
|
||||||
name = "Pkl",
|
name = "Pkl",
|
||||||
version = "0.27.0-dev",
|
version = "0.27.1",
|
||||||
characterMimeTypes = VmLanguage.MIME_TYPE,
|
characterMimeTypes = VmLanguage.MIME_TYPE,
|
||||||
contextPolicy = ContextPolicy.SHARED)
|
contextPolicy = ContextPolicy.SHARED)
|
||||||
public final class VmLanguage extends TruffleLanguage<VmContext> {
|
public final class VmLanguage extends TruffleLanguage<VmContext> {
|
||||||
|
|||||||
@@ -19,14 +19,13 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
|||||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||||
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.EconomicMaps;
|
import org.pkl.core.util.EconomicMaps;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public final class VmListing extends VmListingOrMapping<VmListing> {
|
public final class VmListing extends VmListingOrMapping {
|
||||||
private static final class EmptyHolder {
|
private static final class EmptyHolder {
|
||||||
private static final VmListing EMPTY =
|
private static final VmListing EMPTY =
|
||||||
new VmListing(
|
new VmListing(
|
||||||
@@ -47,7 +46,7 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
|
|||||||
VmObject parent,
|
VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
int length) {
|
int length) {
|
||||||
super(enclosingFrame, Objects.requireNonNull(parent), members, null, null, null);
|
super(enclosingFrame, parent, members);
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,16 +55,10 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
|
|||||||
VmObject parent,
|
VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
int length,
|
int length,
|
||||||
@Nullable VmListing delegate,
|
ListingOrMappingTypeCastNode typeCastNode,
|
||||||
ListingOrMappingTypeCastNode typeCheckNode,
|
Object typeCheckReceiver,
|
||||||
MaterializedFrame typeNodeFrame) {
|
VmObjectLike typeCheckOwner) {
|
||||||
super(
|
super(enclosingFrame, parent, members, typeCastNode, typeCheckReceiver, typeCheckOwner);
|
||||||
enclosingFrame,
|
|
||||||
Objects.requireNonNull(parent),
|
|
||||||
members,
|
|
||||||
delegate,
|
|
||||||
typeCheckNode,
|
|
||||||
typeNodeFrame);
|
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,20 +110,6 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
|
|||||||
return converter.convertListing(this, path);
|
return converter.convertListing(this, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public VmListing withCheckedMembers(
|
|
||||||
ListingOrMappingTypeCastNode typeCheckNode, MaterializedFrame typeNodeFrame) {
|
|
||||||
|
|
||||||
return new VmListing(
|
|
||||||
getEnclosingFrame(),
|
|
||||||
Objects.requireNonNull(parent),
|
|
||||||
members,
|
|
||||||
length,
|
|
||||||
this,
|
|
||||||
typeCheckNode,
|
|
||||||
typeNodeFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
@@ -142,10 +121,14 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
|
|||||||
force(false);
|
force(false);
|
||||||
other.force(false);
|
other.force(false);
|
||||||
|
|
||||||
for (var i = 0L; i < length; i++) {
|
var cursor = cachedValues.getEntries();
|
||||||
var value = getCachedValue(i);
|
while (cursor.advance()) {
|
||||||
|
var key = cursor.getKey();
|
||||||
|
if (key instanceof Identifier) continue;
|
||||||
|
|
||||||
|
var value = cursor.getValue();
|
||||||
assert value != null;
|
assert value != null;
|
||||||
var otherValue = other.getCachedValue(i);
|
var otherValue = other.getCachedValue(key);
|
||||||
if (!value.equals(otherValue)) return false;
|
if (!value.equals(otherValue)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,14 +139,16 @@ public final class VmListing extends VmListingOrMapping<VmListing> {
|
|||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (cachedHash != 0) return cachedHash;
|
if (cachedHash != 0) return cachedHash;
|
||||||
// It's possible that the delegate has already computed its hash code.
|
|
||||||
// If so, we can go ahead and use it.
|
|
||||||
if (delegate != null && delegate.cachedHash != 0) return delegate.cachedHash;
|
|
||||||
|
|
||||||
force(false);
|
force(false);
|
||||||
var result = 0;
|
var result = 0;
|
||||||
for (var i = 0L; i < length; i++) {
|
var cursor = cachedValues.getEntries();
|
||||||
var value = getCachedValue(i);
|
|
||||||
|
while (cursor.advance()) {
|
||||||
|
var key = cursor.getKey();
|
||||||
|
if (key instanceof Identifier) continue;
|
||||||
|
|
||||||
|
var value = cursor.getValue();
|
||||||
assert value != null;
|
assert value != null;
|
||||||
result = 31 * result + value.hashCode();
|
result = 31 * result + value.hashCode();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,159 +16,122 @@
|
|||||||
package org.pkl.core.runtime;
|
package org.pkl.core.runtime;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||||
import org.graalvm.collections.EconomicMap;
|
|
||||||
import org.graalvm.collections.EconomicSet;
|
|
||||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||||
import org.pkl.core.PklBugException;
|
|
||||||
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.ast.type.TypeNode;
|
import org.pkl.core.ast.type.TypeNode;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
import org.pkl.core.util.EconomicMaps;
|
||||||
import org.pkl.core.util.EconomicSets;
|
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public abstract class VmListingOrMapping<SELF extends VmListingOrMapping<SELF>> extends VmObject {
|
public abstract class VmListingOrMapping extends VmObject {
|
||||||
|
// reified type of listing elements and mapping values
|
||||||
/**
|
|
||||||
* A Listing or Mapping typecast creates a new object that contains a new typecheck node, and
|
|
||||||
* delegates member lookups to this delegate.
|
|
||||||
*/
|
|
||||||
protected final @Nullable SELF delegate;
|
|
||||||
|
|
||||||
private final @Nullable ListingOrMappingTypeCastNode typeCastNode;
|
private final @Nullable ListingOrMappingTypeCastNode typeCastNode;
|
||||||
private final MaterializedFrame typeNodeFrame;
|
private final @Nullable Object typeCheckReceiver;
|
||||||
private final EconomicMap<Object, ObjectMember> cachedMembers = EconomicMaps.create();
|
private final @Nullable VmObjectLike typeCheckOwner;
|
||||||
private final EconomicSet<Object> checkedMembers = EconomicSets.create();
|
|
||||||
|
public VmListingOrMapping(
|
||||||
|
MaterializedFrame enclosingFrame,
|
||||||
|
@Nullable VmObject parent,
|
||||||
|
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
||||||
|
super(enclosingFrame, parent, members);
|
||||||
|
typeCastNode = null;
|
||||||
|
typeCheckReceiver = null;
|
||||||
|
typeCheckOwner = null;
|
||||||
|
}
|
||||||
|
|
||||||
public VmListingOrMapping(
|
public VmListingOrMapping(
|
||||||
MaterializedFrame enclosingFrame,
|
MaterializedFrame enclosingFrame,
|
||||||
@Nullable VmObject parent,
|
@Nullable VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
@Nullable SELF delegate,
|
ListingOrMappingTypeCastNode typeCastNode,
|
||||||
@Nullable ListingOrMappingTypeCastNode typeCastNode,
|
Object typeCheckReceiver,
|
||||||
@Nullable MaterializedFrame typeNodeFrame) {
|
VmObjectLike typeCheckOwner) {
|
||||||
super(enclosingFrame, parent, members);
|
super(enclosingFrame, parent, members);
|
||||||
this.delegate = delegate;
|
|
||||||
this.typeCastNode = typeCastNode;
|
this.typeCastNode = typeCastNode;
|
||||||
this.typeNodeFrame = typeNodeFrame;
|
this.typeCheckReceiver = typeCheckReceiver;
|
||||||
|
this.typeCheckOwner = typeCheckOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectMember findMember(Object key) {
|
// Recursively executes type casts between `owner` and `this` and returns the resulting value.
|
||||||
var member = EconomicMaps.get(cachedMembers, key);
|
public final Object executeTypeCasts(
|
||||||
if (member != null) {
|
Object value,
|
||||||
return member;
|
VmObjectLike owner,
|
||||||
}
|
IndirectCallNode callNode,
|
||||||
if (delegate != null) {
|
// if non-null, a stack frame for this member is inserted if a type cast fails
|
||||||
return delegate.findMember(key);
|
@Nullable ObjectMember member,
|
||||||
}
|
// Next type cast to be performed by the caller.
|
||||||
// member is guaranteed to exist; this is only called if `getCachedValue()` returns non-null
|
// Avoids repeating the same type cast in some cases.
|
||||||
// and `setCachedValue` will record the object member in `cachedMembers`.
|
@Nullable ListingOrMappingTypeCastNode nextTypeCastNode) {
|
||||||
throw PklBugException.unreachableCode();
|
var newNextTypeCastNode = typeCastNode != null ? typeCastNode : nextTypeCastNode;
|
||||||
}
|
@SuppressWarnings("DataFlowIssue")
|
||||||
|
var result =
|
||||||
public @Nullable ListingOrMappingTypeCastNode getTypeCastNode() {
|
this == owner
|
||||||
return typeCastNode;
|
? value
|
||||||
}
|
: ((VmListingOrMapping) parent)
|
||||||
|
.executeTypeCasts(value, owner, callNode, member, newNextTypeCastNode);
|
||||||
@Override
|
if (typeCastNode == null || typeCastNode == nextTypeCastNode) return result;
|
||||||
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
|
|
||||||
super.setCachedValue(key, value, objectMember);
|
|
||||||
EconomicMaps.put(cachedMembers, key, objectMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasCachedValue(Object key) {
|
|
||||||
return super.hasCachedValue(key) || delegate != null && delegate.hasCachedValue(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Object getCachedValue(Object key) {
|
|
||||||
var myCachedValue = super.getCachedValue(key);
|
|
||||||
if (myCachedValue != null || delegate == null) {
|
|
||||||
return myCachedValue;
|
|
||||||
}
|
|
||||||
var memberValue = delegate.getCachedValue(key);
|
|
||||||
// if this object member appears inside `checkedMembers`, we have already checked its type
|
|
||||||
// and can safely return it.
|
|
||||||
if (EconomicSets.contains(checkedMembers, key)) {
|
|
||||||
return memberValue;
|
|
||||||
}
|
|
||||||
if (memberValue == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// If a cached value already exists on the delegate, run a typecast on it.
|
|
||||||
// optimization: don't use `VmUtils.findMember` to avoid iterating over all members
|
|
||||||
var objectMember = findMember(key);
|
|
||||||
var ret = typecastObjectMember(objectMember, memberValue, IndirectCallNode.getUncached());
|
|
||||||
if (ret != memberValue) {
|
|
||||||
EconomicMaps.put(cachedValues, key, ret);
|
|
||||||
} else {
|
|
||||||
// optimization: don't add to own cached values if typecast results in the same value
|
|
||||||
EconomicSets.add(checkedMembers, key);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getExtraStorage() {
|
|
||||||
if (delegate != null) {
|
|
||||||
return delegate.getExtraStorage();
|
|
||||||
}
|
|
||||||
assert extraStorage != null;
|
|
||||||
return extraStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Perform a typecast on this member, */
|
|
||||||
public Object typecastObjectMember(
|
|
||||||
ObjectMember member, Object memberValue, IndirectCallNode callNode) {
|
|
||||||
if (!(member.isEntry() || member.isElement()) || typeCastNode == null) {
|
|
||||||
return memberValue;
|
|
||||||
}
|
|
||||||
assert typeNodeFrame != null;
|
|
||||||
var ret = memberValue;
|
|
||||||
if (delegate != null) {
|
|
||||||
ret = delegate.typecastObjectMember(member, ret, callNode);
|
|
||||||
}
|
|
||||||
var callTarget = typeCastNode.getCallTarget();
|
var callTarget = typeCastNode.getCallTarget();
|
||||||
try {
|
try {
|
||||||
return callNode.call(
|
return callNode.call(callTarget, typeCheckReceiver, typeCheckOwner, result);
|
||||||
callTarget, VmUtils.getReceiver(typeNodeFrame), VmUtils.getOwner(typeNodeFrame), ret);
|
} catch (VmException e) {
|
||||||
} catch (VmException vmException) {
|
|
||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
// treat typecheck as part of the call stack to read the original member if there is a
|
if (member != null) {
|
||||||
// source section for it.
|
VmUtils.insertStackFrame(member, callTarget, e);
|
||||||
var sourceSection = member.getBodySection();
|
|
||||||
if (!sourceSection.isAvailable()) {
|
|
||||||
sourceSection = member.getSourceSection();
|
|
||||||
}
|
}
|
||||||
if (sourceSection.isAvailable()) {
|
throw e;
|
||||||
vmException
|
|
||||||
.getInsertedStackFrames()
|
|
||||||
.put(callTarget, VmUtils.createStackFrame(sourceSection, member.getQualifiedName()));
|
|
||||||
}
|
|
||||||
throw vmException;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract SELF withCheckedMembers(
|
@Override
|
||||||
ListingOrMappingTypeCastNode typeCastNode, MaterializedFrame typeNodeFrame);
|
@TruffleBoundary
|
||||||
|
public final @Nullable Object getCachedValue(Object key) {
|
||||||
|
var result = EconomicMaps.get(cachedValues, key);
|
||||||
|
// if this object has members, `this[key]` may differ from `parent[key]`, so stop the search
|
||||||
|
if (result != null || !members.isEmpty()) return result;
|
||||||
|
|
||||||
/** Tells if this mapping/listing runs the same typechecks as {@code typeNode}. */
|
// Optimization: Recursively steal value from parent cache to avoid computing it multiple times.
|
||||||
public boolean hasSameChecksAs(TypeNode typeNode) {
|
// The current implementation has the following limitations and drawbacks:
|
||||||
|
// * It only works if a parent has, coincidentally, already cached `key`.
|
||||||
|
// * It turns getCachedValue() into an operation that isn't guaranteed to be fast and fail-safe.
|
||||||
|
// * It requires making VmObject.getCachedValue() non-final,
|
||||||
|
// which is unfavorable for Truffle partial evaluation and JVM inlining.
|
||||||
|
// * It may not be worth its cost for constant members and members that are cheap to compute.
|
||||||
|
|
||||||
|
assert parent != null; // VmListingOrMapping always has a parent
|
||||||
|
result = parent.getCachedValue(key);
|
||||||
|
if (result == null) return null;
|
||||||
|
|
||||||
|
if (typeCastNode != null && !(key instanceof Identifier)) {
|
||||||
|
var callNode = IndirectCallNode.getUncached();
|
||||||
|
var callTarget = typeCastNode.getCallTarget();
|
||||||
|
try {
|
||||||
|
result = callNode.call(callTarget, typeCheckReceiver, typeCheckOwner, result);
|
||||||
|
} catch (VmException e) {
|
||||||
|
var member = VmUtils.findMember(parent, key);
|
||||||
|
assert member != null; // already found the member's cached value
|
||||||
|
VmUtils.insertStackFrame(member, callTarget, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCachedValue(key, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether the value type of this listing/mapping is known to be a subtype of {@code
|
||||||
|
* typeNode}. If {@code true}, type checks of individual values can be elided because
|
||||||
|
* listings/mappings are covariant in their value type.
|
||||||
|
*/
|
||||||
|
public final boolean isValueTypeKnownSubtypeOf(TypeNode typeNode) {
|
||||||
|
if (typeNode.isNoopTypeCheck()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (typeCastNode == null) {
|
if (typeCastNode == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (typeCastNode.getTypeNode().isEquivalentTo(typeNode)) {
|
return typeCastNode.getTypeNode().isEquivalentTo(typeNode);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// we can say the check is the same if the delegate has this check.
|
|
||||||
// when `Listing<Any>` delegates to `Listing<UInt>`, it has the same checks as a `UInt`
|
|
||||||
// typenode.
|
|
||||||
if (delegate != null) {
|
|
||||||
return delegate.hasSameChecksAs(typeNode);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package org.pkl.core.runtime;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||||
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
||||||
@@ -27,7 +26,7 @@ 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.LateInit;
|
||||||
|
|
||||||
public final class VmMapping extends VmListingOrMapping<VmMapping> {
|
public final class VmMapping extends VmListingOrMapping {
|
||||||
|
|
||||||
private int cachedEntryCount = -1;
|
private int cachedEntryCount = -1;
|
||||||
|
|
||||||
@@ -50,24 +49,17 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
|
|||||||
MaterializedFrame enclosingFrame,
|
MaterializedFrame enclosingFrame,
|
||||||
VmObject parent,
|
VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
||||||
|
super(enclosingFrame, parent, members);
|
||||||
super(enclosingFrame, Objects.requireNonNull(parent), members, null, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VmMapping(
|
public VmMapping(
|
||||||
MaterializedFrame enclosingFrame,
|
MaterializedFrame enclosingFrame,
|
||||||
VmObject parent,
|
VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
VmMapping delegate,
|
ListingOrMappingTypeCastNode typeCastNode,
|
||||||
ListingOrMappingTypeCastNode typeCheckNode,
|
Object typeCheckReceiver,
|
||||||
MaterializedFrame typeNodeFrame) {
|
VmObjectLike typeCheckOwner) {
|
||||||
super(
|
super(enclosingFrame, parent, members, typeCastNode, typeCheckReceiver, typeCheckOwner);
|
||||||
enclosingFrame,
|
|
||||||
Objects.requireNonNull(parent),
|
|
||||||
members,
|
|
||||||
delegate,
|
|
||||||
typeCheckNode,
|
|
||||||
typeNodeFrame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDefaultProperty(Object propertyKey) {
|
public static boolean isDefaultProperty(Object propertyKey) {
|
||||||
@@ -81,16 +73,12 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
|
|||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public VmSet getAllKeys() {
|
public VmSet getAllKeys() {
|
||||||
if (delegate != null) {
|
|
||||||
return delegate.getAllKeys();
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (__allKeys == null) {
|
if (__allKeys == null) {
|
||||||
// building upon parent's `getAllKeys()` should improve at least worst case efficiency
|
// building upon parent's `getAllKeys()` should improve at least worst case efficiency
|
||||||
var parentKeys =
|
var parentKeys = parent instanceof VmMapping mapping ? mapping.getAllKeys() : VmSet.EMPTY;
|
||||||
getParent() instanceof VmMapping mapping ? mapping.getAllKeys() : VmSet.EMPTY;
|
|
||||||
var builder = VmSet.builder(parentKeys);
|
var builder = VmSet.builder(parentKeys);
|
||||||
for (var cursor = getMembers().getEntries(); cursor.advance(); ) {
|
for (var cursor = members.getEntries(); cursor.advance(); ) {
|
||||||
var member = cursor.getValue();
|
var member = cursor.getValue();
|
||||||
if (!member.isEntry()) continue;
|
if (!member.isEntry()) continue;
|
||||||
builder.add(cursor.getKey());
|
builder.add(cursor.getKey());
|
||||||
@@ -133,12 +121,17 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
|
|||||||
if (this == obj) return true;
|
if (this == obj) return true;
|
||||||
if (!(obj instanceof VmMapping other)) return false;
|
if (!(obj instanceof VmMapping other)) return false;
|
||||||
|
|
||||||
if (getEntryCount() != other.getEntryCount()) return false;
|
|
||||||
// could use shallow force, but deep force is cached
|
// could use shallow force, but deep force is cached
|
||||||
force(false);
|
force(false);
|
||||||
other.force(false);
|
other.force(false);
|
||||||
for (var key : getAllKeys()) {
|
if (getEntryCount() != other.getEntryCount()) return false;
|
||||||
var value = getCachedValue(key);
|
|
||||||
|
var cursor = cachedValues.getEntries();
|
||||||
|
while (cursor.advance()) {
|
||||||
|
Object key = cursor.getKey();
|
||||||
|
if (key instanceof Identifier) continue;
|
||||||
|
|
||||||
|
var value = cursor.getValue();
|
||||||
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;
|
||||||
@@ -151,38 +144,34 @@ public final class VmMapping extends VmListingOrMapping<VmMapping> {
|
|||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (cachedHash != 0) return cachedHash;
|
if (cachedHash != 0) return cachedHash;
|
||||||
// It's possible that the delegate has already computed its hash code.
|
|
||||||
// If so, we can go ahead and use it.
|
|
||||||
if (delegate != null && delegate.cachedHash != 0) return delegate.cachedHash;
|
|
||||||
|
|
||||||
force(false);
|
force(false);
|
||||||
var result = 0;
|
var result = 0;
|
||||||
for (var key : getAllKeys()) {
|
var cursor = cachedValues.getEntries();
|
||||||
|
|
||||||
|
while (cursor.advance()) {
|
||||||
|
var key = cursor.getKey();
|
||||||
if (key instanceof Identifier) continue;
|
if (key instanceof Identifier) continue;
|
||||||
var value = getCachedValue(key);
|
|
||||||
|
var value = cursor.getValue();
|
||||||
assert value != null;
|
assert value != null;
|
||||||
result += key.hashCode() ^ value.hashCode();
|
result += key.hashCode() ^ value.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedHash = result;
|
cachedHash = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assumes mapping has been forced
|
||||||
public int getEntryCount() {
|
public int getEntryCount() {
|
||||||
if (cachedEntryCount != -1) return cachedEntryCount;
|
if (cachedEntryCount != -1) return cachedEntryCount;
|
||||||
cachedEntryCount = getAllKeys().getLength();
|
|
||||||
return cachedEntryCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
var result = 0;
|
||||||
@TruffleBoundary
|
for (var key : cachedValues.getKeys()) {
|
||||||
public VmMapping withCheckedMembers(
|
if (key instanceof Identifier) continue;
|
||||||
ListingOrMappingTypeCastNode typeCheckNode, MaterializedFrame typeNodeFrame) {
|
result += 1;
|
||||||
return new VmMapping(
|
}
|
||||||
getEnclosingFrame(),
|
cachedEntryCount = result;
|
||||||
Objects.requireNonNull(getParent()),
|
return result;
|
||||||
getMembers(),
|
|
||||||
this,
|
|
||||||
typeCheckNode,
|
|
||||||
typeNodeFrame);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,12 +87,12 @@ public abstract class VmObject extends VmObjectLike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
|
public final void setCachedValue(Object key, Object value) {
|
||||||
EconomicMaps.put(cachedValues, key, value);
|
EconomicMaps.put(cachedValues, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCachedValue(Object key) {
|
public final boolean hasCachedValue(Object key) {
|
||||||
return EconomicMaps.containsKey(cachedValues, key);
|
return EconomicMaps.containsKey(cachedValues, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public abstract class VmObjectLike extends VmValue {
|
|||||||
* receiver.
|
* receiver.
|
||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public abstract void setCachedValue(Object key, Object value, ObjectMember objectMember);
|
public abstract void setCachedValue(Object key, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefer this method over {@link #getCachedValue} if the value is not required. (There is no
|
* Prefer this method over {@link #getCachedValue} if the value is not required. (There is no
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.pkl.core.runtime;
|
package org.pkl.core.runtime;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.CallTarget;
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.Truffle;
|
import com.oracle.truffle.api.Truffle;
|
||||||
@@ -134,10 +135,6 @@ public final class VmUtils {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setReceiver(Frame frame, Object receiver) {
|
|
||||||
frame.getArguments()[0] = receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VmObjectLike getObjectReceiver(Frame frame) {
|
public static VmObjectLike getObjectReceiver(Frame frame) {
|
||||||
return (VmObjectLike) getReceiver(frame);
|
return (VmObjectLike) getReceiver(frame);
|
||||||
}
|
}
|
||||||
@@ -158,10 +155,6 @@ public final class VmUtils {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setOwner(Frame frame, VmObjectLike owner) {
|
|
||||||
frame.getArguments()[1] = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a `ObjectMember`'s key while executing the corresponding `MemberNode`. */
|
/** Returns a `ObjectMember`'s key while executing the corresponding `MemberNode`. */
|
||||||
public static Object getMemberKey(Frame frame) {
|
public static Object getMemberKey(Frame frame) {
|
||||||
return frame.getArguments()[2];
|
return frame.getArguments()[2];
|
||||||
@@ -261,17 +254,17 @@ public final class VmUtils {
|
|||||||
|
|
||||||
final var constantValue = member.getConstantValue();
|
final var constantValue = member.getConstantValue();
|
||||||
if (constantValue != null) {
|
if (constantValue != null) {
|
||||||
var ret = constantValue;
|
var result = constantValue;
|
||||||
// for a property, do a type check
|
// for a property, Listing element, or Mapping value, do a type check
|
||||||
if (member.isProp()) {
|
if (member.isProp()) {
|
||||||
var property = receiver.getVmClass().getProperty(member.getName());
|
var property = receiver.getVmClass().getProperty(member.getName());
|
||||||
if (property != null && property.getTypeNode() != null) {
|
if (property != null && property.getTypeNode() != null) {
|
||||||
var callTarget = property.getTypeNode().getCallTarget();
|
var callTarget = property.getTypeNode().getCallTarget();
|
||||||
try {
|
try {
|
||||||
if (checkType) {
|
if (checkType) {
|
||||||
ret = callNode.call(callTarget, receiver, property.getOwner(), constantValue);
|
result = callNode.call(callTarget, receiver, property.getOwner(), constantValue);
|
||||||
} else {
|
} else {
|
||||||
ret =
|
result =
|
||||||
callNode.call(
|
callNode.call(
|
||||||
callTarget,
|
callTarget,
|
||||||
receiver,
|
receiver,
|
||||||
@@ -281,6 +274,32 @@ public final class VmUtils {
|
|||||||
}
|
}
|
||||||
} catch (VmException e) {
|
} catch (VmException e) {
|
||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
|
insertStackFrame(member, callTarget, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (receiver instanceof VmListingOrMapping listingOrMapping
|
||||||
|
&& owner instanceof VmListingOrMapping) {
|
||||||
|
// `owner instanceof VmListingOrMapping` guards against
|
||||||
|
// PropertiesRenderer amending VmDynamic with VmListing (hack?)
|
||||||
|
result = listingOrMapping.executeTypeCasts(constantValue, owner, callNode, member, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
receiver.setCachedValue(memberKey, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var callTarget = member.getCallTarget();
|
||||||
|
Object result;
|
||||||
|
if (checkType) {
|
||||||
|
result = callNode.call(callTarget, receiver, owner, memberKey);
|
||||||
|
} else {
|
||||||
|
result = callNode.call(callTarget, receiver, owner, memberKey, VmUtils.SKIP_TYPECHECK_MARKER);
|
||||||
|
}
|
||||||
|
receiver.setCachedValue(memberKey, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// A failed property type check looks as follows in the stack trace:
|
// A failed property type check looks as follows in the stack trace:
|
||||||
// x: Int(isPositive)
|
// x: Int(isPositive)
|
||||||
// at ...
|
// at ...
|
||||||
@@ -290,35 +309,17 @@ public final class VmUtils {
|
|||||||
// there's no root node for it and hence no stack trace element.
|
// there's no root node for it and hence no stack trace element.
|
||||||
// In this case, insert a stack trace element to make the stack trace look the same
|
// In this case, insert a stack trace element to make the stack trace look the same
|
||||||
// as in the non-constant case. (Parse-time constants are an internal optimization.)
|
// as in the non-constant case. (Parse-time constants are an internal optimization.)
|
||||||
e.getInsertedStackFrames()
|
public static void insertStackFrame(
|
||||||
.put(
|
ObjectMember member, CallTarget location, VmException exception) {
|
||||||
callTarget,
|
var sourceSection = member.getBodySection();
|
||||||
createStackFrame(member.getBodySection(), member.getQualifiedName()));
|
if (!sourceSection.isAvailable()) {
|
||||||
throw e;
|
sourceSection = member.getSourceSection();
|
||||||
}
|
}
|
||||||
|
if (sourceSection.isAvailable()) {
|
||||||
|
exception
|
||||||
|
.getInsertedStackFrames()
|
||||||
|
.put(location, createStackFrame(sourceSection, member.getQualifiedName()));
|
||||||
}
|
}
|
||||||
} else if (receiver instanceof VmListingOrMapping<?> vmListingOrMapping) {
|
|
||||||
if (owner != receiver && owner instanceof VmListingOrMapping<?> vmListingOrMappingOwner) {
|
|
||||||
ret = vmListingOrMappingOwner.typecastObjectMember(member, ret, callNode);
|
|
||||||
}
|
|
||||||
ret = vmListingOrMapping.typecastObjectMember(member, ret, callNode);
|
|
||||||
}
|
|
||||||
receiver.setCachedValue(memberKey, ret, member);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var callTarget = member.getCallTarget();
|
|
||||||
Object ret;
|
|
||||||
if (checkType) {
|
|
||||||
ret = callNode.call(callTarget, receiver, owner, memberKey);
|
|
||||||
} else {
|
|
||||||
ret = callNode.call(callTarget, receiver, owner, memberKey, VmUtils.SKIP_TYPECHECK_MARKER);
|
|
||||||
}
|
|
||||||
if (receiver instanceof VmListingOrMapping<?> vmListingOrMapping) {
|
|
||||||
ret = vmListingOrMapping.typecastObjectMember(member, ret, callNode);
|
|
||||||
}
|
|
||||||
receiver.setCachedValue(memberKey, ret, member);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable ObjectMember findMember(VmObjectLike receiver, Object memberKey) {
|
public static @Nullable ObjectMember findMember(VmObjectLike receiver, Object memberKey) {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import org.pkl.core.runtime.VmDuration;
|
|||||||
import org.pkl.core.runtime.VmDynamic;
|
import org.pkl.core.runtime.VmDynamic;
|
||||||
import org.pkl.core.runtime.VmExceptionBuilder;
|
import org.pkl.core.runtime.VmExceptionBuilder;
|
||||||
import org.pkl.core.runtime.VmIntSeq;
|
import org.pkl.core.runtime.VmIntSeq;
|
||||||
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
import org.pkl.core.runtime.VmList;
|
import org.pkl.core.runtime.VmList;
|
||||||
import org.pkl.core.runtime.VmListing;
|
import org.pkl.core.runtime.VmListing;
|
||||||
import org.pkl.core.runtime.VmMap;
|
import org.pkl.core.runtime.VmMap;
|
||||||
@@ -573,7 +574,8 @@ public final class RendererNodes {
|
|||||||
type =
|
type =
|
||||||
requiresWrapper()
|
requiresWrapper()
|
||||||
? null
|
? null
|
||||||
: new ListingTypeNode(VmUtils.unavailableSourceSection(), valueType);
|
: new ListingTypeNode(
|
||||||
|
VmUtils.unavailableSourceSection(), VmLanguage.get(null), valueType);
|
||||||
return type;
|
return type;
|
||||||
} else if (type instanceof MappingTypeNode mappingType) {
|
} else if (type instanceof MappingTypeNode mappingType) {
|
||||||
var keyType = resolveType(mappingType.getKeyTypeNode());
|
var keyType = resolveType(mappingType.getKeyTypeNode());
|
||||||
@@ -587,7 +589,9 @@ public final class RendererNodes {
|
|||||||
}
|
}
|
||||||
var valueType = resolveType(mappingType.getValueTypeNode());
|
var valueType = resolveType(mappingType.getValueTypeNode());
|
||||||
assert valueType != null : "Incomplete or malformed Mapping type";
|
assert valueType != null : "Incomplete or malformed Mapping type";
|
||||||
mappingType = new MappingTypeNode(VmUtils.unavailableSourceSection(), keyType, valueType);
|
mappingType =
|
||||||
|
new MappingTypeNode(
|
||||||
|
VmUtils.unavailableSourceSection(), VmLanguage.get(null), keyType, valueType);
|
||||||
|
|
||||||
type = requiresWrapper() ? null : mappingType;
|
type = requiresWrapper() ? null : mappingType;
|
||||||
return type;
|
return type;
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ import org.graalvm.collections.UnmodifiableMapCursor;
|
|||||||
public final class EconomicMaps {
|
public final class EconomicMaps {
|
||||||
private EconomicMaps() {}
|
private EconomicMaps() {}
|
||||||
|
|
||||||
|
public static <K, V> UnmodifiableEconomicMap<K, V> emptyMap() {
|
||||||
|
return EconomicMap.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public static <K, V> EconomicMap<K, V> create() {
|
public static <K, V> EconomicMap<K, V> create() {
|
||||||
return EconomicMap.create();
|
return EconomicMap.create();
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
function isValid(str): Boolean = str.startsWith("a")
|
||||||
|
|
||||||
|
foo: Listing<String(isValid(this))>(isDistinct)
|
||||||
@@ -27,12 +27,20 @@ local altered: Listing<Person> = (base) {
|
|||||||
[0] { name = "Wood Pigeon" }
|
[0] { name = "Wood Pigeon" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local computedIndex: Listing<Person> = (base) {
|
||||||
|
[computeIndex()] { name = "Wood Pigeon" }
|
||||||
|
}
|
||||||
|
|
||||||
|
local function computeIndex() = 0
|
||||||
|
|
||||||
facts {
|
facts {
|
||||||
["isEmpty"] {
|
["isEmpty"] {
|
||||||
empty.isEmpty
|
empty.isEmpty
|
||||||
empty2.isEmpty
|
empty2.isEmpty
|
||||||
!base.isEmpty
|
!base.isEmpty
|
||||||
!derived.isEmpty
|
!derived.isEmpty
|
||||||
|
!altered.isEmpty
|
||||||
|
!computedIndex.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
["lastIndex"] {
|
["lastIndex"] {
|
||||||
@@ -41,6 +49,8 @@ facts {
|
|||||||
base.lastIndex == 2
|
base.lastIndex == 2
|
||||||
derived.lastIndex == 4
|
derived.lastIndex == 4
|
||||||
duplicate.lastIndex == 5
|
duplicate.lastIndex == 5
|
||||||
|
altered.lastIndex == 2
|
||||||
|
computedIndex.lastIndex == 2
|
||||||
}
|
}
|
||||||
|
|
||||||
["isDistinct"] {
|
["isDistinct"] {
|
||||||
@@ -49,6 +59,8 @@ facts {
|
|||||||
base.isDistinct
|
base.isDistinct
|
||||||
derived.isDistinct
|
derived.isDistinct
|
||||||
!duplicate.isDistinct
|
!duplicate.isDistinct
|
||||||
|
altered.isDistinct
|
||||||
|
computedIndex.isDistinct
|
||||||
}
|
}
|
||||||
|
|
||||||
["isDistinctBy()"] {
|
["isDistinctBy()"] {
|
||||||
@@ -57,18 +69,24 @@ facts {
|
|||||||
base.isDistinctBy((it) -> it)
|
base.isDistinctBy((it) -> it)
|
||||||
derived.isDistinctBy((it) -> it)
|
derived.isDistinctBy((it) -> it)
|
||||||
!duplicate.isDistinctBy((it) -> it)
|
!duplicate.isDistinctBy((it) -> it)
|
||||||
|
altered.isDistinctBy((it) -> it)
|
||||||
|
computedIndex.isDistinctBy((it) -> it)
|
||||||
|
|
||||||
empty.isDistinctBy((it) -> it.name)
|
empty.isDistinctBy((it) -> it.name)
|
||||||
empty2.isDistinctBy((it) -> it.name)
|
empty2.isDistinctBy((it) -> it.name)
|
||||||
base.isDistinctBy((it) -> it.name)
|
base.isDistinctBy((it) -> it.name)
|
||||||
derived.isDistinctBy((it) -> it.name)
|
derived.isDistinctBy((it) -> it.name)
|
||||||
!duplicate.isDistinctBy((it) -> it.name)
|
!duplicate.isDistinctBy((it) -> it.name)
|
||||||
|
altered.isDistinctBy((it) -> it.name)
|
||||||
|
computedIndex.isDistinctBy((it) -> it.name)
|
||||||
|
|
||||||
empty.isDistinctBy((it) -> it.getClass())
|
empty.isDistinctBy((it) -> it.getClass())
|
||||||
empty2.isDistinctBy((it) -> it.getClass())
|
empty2.isDistinctBy((it) -> it.getClass())
|
||||||
!base.isDistinctBy((it) -> it.getClass())
|
!base.isDistinctBy((it) -> it.getClass())
|
||||||
!derived.isDistinctBy((it) -> it.getClass())
|
!derived.isDistinctBy((it) -> it.getClass())
|
||||||
!duplicate.isDistinctBy((it) -> it.getClass())
|
!duplicate.isDistinctBy((it) -> it.getClass())
|
||||||
|
!altered.isDistinctBy((it) -> it.getClass())
|
||||||
|
!computedIndex.isDistinctBy((it) -> it.getClass())
|
||||||
}
|
}
|
||||||
|
|
||||||
["getOrNull"] {
|
["getOrNull"] {
|
||||||
@@ -85,24 +103,32 @@ facts {
|
|||||||
module.catch(() -> empty.first) == "Expected a non-empty Listing."
|
module.catch(() -> empty.first) == "Expected a non-empty Listing."
|
||||||
base.first == base[0]
|
base.first == base[0]
|
||||||
derived.first == base[0]
|
derived.first == base[0]
|
||||||
|
altered.first != base[0]
|
||||||
|
computedIndex.first == altered.first
|
||||||
}
|
}
|
||||||
|
|
||||||
["firstOrNull"] {
|
["firstOrNull"] {
|
||||||
empty.firstOrNull == null
|
empty.firstOrNull == null
|
||||||
base.firstOrNull == base[0]
|
base.firstOrNull == base[0]
|
||||||
derived.firstOrNull == base[0]
|
derived.firstOrNull == base[0]
|
||||||
|
altered.firstOrNull != base[0]
|
||||||
|
computedIndex.firstOrNull == altered.first
|
||||||
}
|
}
|
||||||
|
|
||||||
["last"] {
|
["last"] {
|
||||||
module.catch(() -> empty.last) == "Expected a non-empty Listing."
|
module.catch(() -> empty.last) == "Expected a non-empty Listing."
|
||||||
base.last == base[2]
|
base.last == base[2]
|
||||||
derived.last == derived[4]
|
derived.last == derived[4]
|
||||||
|
altered.last == base[2]
|
||||||
|
computedIndex.last == base[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
["lastOrNull"] {
|
["lastOrNull"] {
|
||||||
empty.lastOrNull == null
|
empty.lastOrNull == null
|
||||||
base.lastOrNull == base[2]
|
base.lastOrNull == base[2]
|
||||||
derived.lastOrNull == derived[4]
|
derived.lastOrNull == derived[4]
|
||||||
|
altered.lastOrNull == base[2]
|
||||||
|
computedIndex.lastOrNull == base[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
["single"] {
|
["single"] {
|
||||||
@@ -135,6 +161,7 @@ facts {
|
|||||||
derived.contains(base[1])
|
derived.contains(base[1])
|
||||||
derived.contains(derived[3])
|
derived.contains(derived[3])
|
||||||
!altered.contains(base[0])
|
!altered.contains(base[0])
|
||||||
|
!computedIndex.contains(base[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +171,17 @@ examples {
|
|||||||
empty2.length
|
empty2.length
|
||||||
base.length
|
base.length
|
||||||
derived.length
|
derived.length
|
||||||
|
altered.length
|
||||||
|
computedIndex.length
|
||||||
|
|
||||||
|
local elementsAndEntries = (base) {
|
||||||
|
new { name = "" }
|
||||||
|
[2] { name = "" }
|
||||||
|
[computeIndex()] { name = "" }
|
||||||
|
new { name = "" }
|
||||||
|
[1 + 0] { name = "" }
|
||||||
|
}
|
||||||
|
elementsAndEntries.length
|
||||||
}
|
}
|
||||||
|
|
||||||
["toList()"] {
|
["toList()"] {
|
||||||
@@ -152,6 +190,8 @@ examples {
|
|||||||
base.toList()
|
base.toList()
|
||||||
derived.toList()
|
derived.toList()
|
||||||
duplicate.toList()
|
duplicate.toList()
|
||||||
|
altered.toList()
|
||||||
|
computedIndex.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
["toSet()"] {
|
["toSet()"] {
|
||||||
@@ -160,6 +200,8 @@ examples {
|
|||||||
base.toSet()
|
base.toSet()
|
||||||
derived.toSet()
|
derived.toSet()
|
||||||
duplicate.toSet()
|
duplicate.toSet()
|
||||||
|
altered.toSet()
|
||||||
|
computedIndex.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
["distinct"] {
|
["distinct"] {
|
||||||
@@ -168,6 +210,8 @@ examples {
|
|||||||
base.distinct
|
base.distinct
|
||||||
derived.distinct
|
derived.distinct
|
||||||
duplicate.distinct
|
duplicate.distinct
|
||||||
|
altered.distinct
|
||||||
|
computedIndex.distinct
|
||||||
}
|
}
|
||||||
|
|
||||||
["distinctBy()"] {
|
["distinctBy()"] {
|
||||||
@@ -176,36 +220,48 @@ examples {
|
|||||||
base.distinctBy((it) -> it)
|
base.distinctBy((it) -> it)
|
||||||
derived.distinctBy((it) -> it)
|
derived.distinctBy((it) -> it)
|
||||||
duplicate.distinctBy((it) -> it)
|
duplicate.distinctBy((it) -> it)
|
||||||
|
altered.distinctBy((it) -> it)
|
||||||
|
computedIndex.distinctBy((it) -> it)
|
||||||
|
|
||||||
empty.distinctBy((it) -> it.name)
|
empty.distinctBy((it) -> it.name)
|
||||||
empty2.distinctBy((it) -> it.name)
|
empty2.distinctBy((it) -> it.name)
|
||||||
base.distinctBy((it) -> it.name)
|
base.distinctBy((it) -> it.name)
|
||||||
derived.distinctBy((it) -> it.name)
|
derived.distinctBy((it) -> it.name)
|
||||||
duplicate.distinctBy((it) -> it.name)
|
duplicate.distinctBy((it) -> it.name)
|
||||||
|
altered.distinctBy((it) -> it.name)
|
||||||
|
computedIndex.distinctBy((it) -> it.name)
|
||||||
|
|
||||||
empty.distinctBy((it) -> it.getClass())
|
empty.distinctBy((it) -> it.getClass())
|
||||||
empty2.distinctBy((it) -> it.getClass())
|
empty2.distinctBy((it) -> it.getClass())
|
||||||
base.distinctBy((it) -> it.getClass())
|
base.distinctBy((it) -> it.getClass())
|
||||||
derived.distinctBy((it) -> it.getClass())
|
derived.distinctBy((it) -> it.getClass())
|
||||||
duplicate.distinctBy((it) -> it.getClass())
|
duplicate.distinctBy((it) -> it.getClass())
|
||||||
|
altered.distinctBy((it) -> it.getClass())
|
||||||
|
computedIndex.distinctBy((it) -> it.getClass())
|
||||||
}
|
}
|
||||||
|
|
||||||
["fold"] {
|
["fold"] {
|
||||||
empty.fold(List(), (l, e) -> l.add(e))
|
empty.fold(List(), (l, e) -> l.add(e))
|
||||||
base.fold(List(), (l, e) -> l.add(e))
|
base.fold(List(), (l, e) -> l.add(e))
|
||||||
derived.fold(List(), (l, e) -> l.add(e))
|
derived.fold(List(), (l, e) -> l.add(e))
|
||||||
|
altered.fold(List(), (l, e) -> l.add(e))
|
||||||
|
computedIndex.fold(List(), (l, e) -> l.add(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
["foldIndexed"] {
|
["foldIndexed"] {
|
||||||
empty.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
empty.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
||||||
base.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
base.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
||||||
derived.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
derived.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
||||||
|
altered.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
||||||
|
computedIndex.foldIndexed(List(), (i, l, e) -> l.add(Pair(i, e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
local baseNum = new Listing { 1; 2; 3 }
|
local baseNum = new Listing { 1; 2; 3 }
|
||||||
local baseString = new Listing { "Pigeon"; "Barn Owl"; "Parrot" }
|
local baseString = new Listing { "Pigeon"; "Barn Owl"; "Parrot" }
|
||||||
local derivedString = (baseString) { "Albatross"; "Elf Owl" }
|
local derivedString = (baseString) { "Albatross"; "Elf Owl" }
|
||||||
|
local alteredString = (baseString) { [0] = "Wood Pigeon" }
|
||||||
|
local computedIndexString = (baseString) { [computeIndex()] = "Wood Pigeon" }
|
||||||
|
|
||||||
["join"] {
|
["join"] {
|
||||||
empty.join("")
|
empty.join("")
|
||||||
@@ -215,5 +271,9 @@ examples {
|
|||||||
baseString.join("---")
|
baseString.join("---")
|
||||||
derivedString.join("")
|
derivedString.join("")
|
||||||
derivedString.join("\n")
|
derivedString.join("\n")
|
||||||
|
alteredString.join("")
|
||||||
|
alteredString.join("\n")
|
||||||
|
computedIndexString.join("")
|
||||||
|
computedIndexString.join("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
local a = new Listing { new Listing { 0 } }
|
||||||
|
local b = a as Listing<Listing<String>>
|
||||||
|
local c = (b) { new Listing { 1 } }
|
||||||
|
local d = c as Listing<Listing<Int>>
|
||||||
|
|
||||||
|
result = d
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
local a = new Listing { new Foo {} }
|
||||||
|
local b = (a) { new Bar {} }
|
||||||
|
local c = b as Listing<Bar>
|
||||||
|
local d = (c) { new Foo {} }
|
||||||
|
local e = d as Listing<Foo>
|
||||||
|
|
||||||
|
result = e
|
||||||
|
|
||||||
|
open class Foo
|
||||||
|
class Bar extends Foo
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
local a = new Mapping { [0] = new Foo {} }
|
||||||
|
local b = (a) { [1] = new Bar {} }
|
||||||
|
local c = b as Mapping<Int, Bar>
|
||||||
|
local d = (c) { [2] = new Foo {} }
|
||||||
|
local e = d as Mapping<Int, Foo>
|
||||||
|
|
||||||
|
result = e
|
||||||
|
|
||||||
|
open class Foo
|
||||||
|
class Bar extends Foo
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
foo1: Listing<String> = new { "hello" }
|
||||||
|
foo2: Listing<String|Int> = foo1
|
||||||
|
|
||||||
|
res1 = foo1.isDistinct
|
||||||
|
// steals isDistinct from foo1's VmListing.cachedValues but must not
|
||||||
|
// perform a String|Int type check because isDistinct is not an element
|
||||||
|
res2 = foo2.isDistinct
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
amends "../../input-helper/listings/cacheStealingTypeCheck.pkl"
|
||||||
|
|
||||||
|
// this test covers a regression where the wrong receiver
|
||||||
|
// and owner was used to typecheck a stolen value
|
||||||
|
foo {
|
||||||
|
"abcdx"
|
||||||
|
"ax"
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
const local lastName = "Birdo"
|
||||||
|
|
||||||
|
typealias Birds = Listing<String(endsWith(lastName))>
|
||||||
|
|
||||||
|
typealias Birds2 = Pair<Listing<String(endsWith(lastName))>, Int>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import "pkl:test"
|
import "pkl:test"
|
||||||
|
|
||||||
|
import "helpers/originalTypealias.pkl"
|
||||||
|
|
||||||
typealias Simple = String
|
typealias Simple = String
|
||||||
const function simple(arg: Simple): Simple = arg
|
const function simple(arg: Simple): Simple = arg
|
||||||
|
|
||||||
@@ -105,3 +107,8 @@ res19: LocalTypeAlias = "abc"
|
|||||||
typealias VeryComposite = Pair<Composite, Composite>
|
typealias VeryComposite = Pair<Composite, Composite>
|
||||||
|
|
||||||
res20: VeryComposite = Pair(Map("abc", List("def")), Map("abc", List("def")))
|
res20: VeryComposite = Pair(Map("abc", List("def")), Map("abc", List("def")))
|
||||||
|
|
||||||
|
// typealiases should be executed in their original context
|
||||||
|
res21: originalTypealias.Birds = new { "John Birdo" }
|
||||||
|
|
||||||
|
res22: originalTypealias.Birds2 = Pair(new Listing { "John Birdo" }, 0)
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import "helpers/originalTypealias.pkl"
|
||||||
|
|
||||||
|
res: originalTypealias.Birds = new { "Jimmy Bird" }
|
||||||
@@ -4,6 +4,8 @@ facts {
|
|||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["lastIndex"] {
|
["lastIndex"] {
|
||||||
true
|
true
|
||||||
@@ -11,6 +13,8 @@ facts {
|
|||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["isDistinct"] {
|
["isDistinct"] {
|
||||||
true
|
true
|
||||||
@@ -18,6 +22,8 @@ facts {
|
|||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["isDistinctBy()"] {
|
["isDistinctBy()"] {
|
||||||
true
|
true
|
||||||
@@ -35,6 +41,12 @@ facts {
|
|||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["getOrNull"] {
|
["getOrNull"] {
|
||||||
true
|
true
|
||||||
@@ -49,21 +61,29 @@ facts {
|
|||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["firstOrNull"] {
|
["firstOrNull"] {
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["last"] {
|
["last"] {
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["lastOrNull"] {
|
["lastOrNull"] {
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
["single"] {
|
["single"] {
|
||||||
true
|
true
|
||||||
@@ -91,6 +111,7 @@ facts {
|
|||||||
true
|
true
|
||||||
true
|
true
|
||||||
true
|
true
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
examples {
|
examples {
|
||||||
@@ -99,6 +120,9 @@ examples {
|
|||||||
0
|
0
|
||||||
3
|
3
|
||||||
5
|
5
|
||||||
|
3
|
||||||
|
3
|
||||||
|
5
|
||||||
}
|
}
|
||||||
["toList()"] {
|
["toList()"] {
|
||||||
List()
|
List()
|
||||||
@@ -134,6 +158,20 @@ examples {
|
|||||||
}, new {
|
}, new {
|
||||||
name = "Elf Owl"
|
name = "Elf Owl"
|
||||||
})
|
})
|
||||||
|
List(new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}, new {
|
||||||
|
name = "Parrot"
|
||||||
|
})
|
||||||
|
List(new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}, new {
|
||||||
|
name = "Parrot"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
["toSet()"] {
|
["toSet()"] {
|
||||||
Set()
|
Set()
|
||||||
@@ -167,6 +205,20 @@ examples {
|
|||||||
}, new {
|
}, new {
|
||||||
name = "Elf Owl"
|
name = "Elf Owl"
|
||||||
})
|
})
|
||||||
|
Set(new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}, new {
|
||||||
|
name = "Parrot"
|
||||||
|
})
|
||||||
|
Set(new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}, new {
|
||||||
|
name = "Parrot"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
["distinct"] {
|
["distinct"] {
|
||||||
new {}
|
new {}
|
||||||
@@ -216,6 +268,28 @@ examples {
|
|||||||
name = "Elf Owl"
|
name = "Elf Owl"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Parrot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Parrot"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
["distinctBy()"] {
|
["distinctBy()"] {
|
||||||
new {}
|
new {}
|
||||||
@@ -265,6 +339,28 @@ examples {
|
|||||||
name = "Elf Owl"
|
name = "Elf Owl"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Parrot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Parrot"
|
||||||
|
}
|
||||||
|
}
|
||||||
new {}
|
new {}
|
||||||
new {}
|
new {}
|
||||||
new {
|
new {
|
||||||
@@ -312,6 +408,28 @@ examples {
|
|||||||
name = "Elf Owl"
|
name = "Elf Owl"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Parrot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Parrot"
|
||||||
|
}
|
||||||
|
}
|
||||||
new {}
|
new {}
|
||||||
new {}
|
new {}
|
||||||
new {
|
new {
|
||||||
@@ -329,6 +447,16 @@ examples {
|
|||||||
name = "Pigeon"
|
name = "Pigeon"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
["fold"] {
|
["fold"] {
|
||||||
List()
|
List()
|
||||||
@@ -350,6 +478,20 @@ examples {
|
|||||||
}, new {
|
}, new {
|
||||||
name = "Elf Owl"
|
name = "Elf Owl"
|
||||||
})
|
})
|
||||||
|
List(new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}, new {
|
||||||
|
name = "Parrot"
|
||||||
|
})
|
||||||
|
List(new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}, new {
|
||||||
|
name = "Parrot"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
["foldIndexed"] {
|
["foldIndexed"] {
|
||||||
List()
|
List()
|
||||||
@@ -371,6 +513,20 @@ examples {
|
|||||||
}), Pair(4, new {
|
}), Pair(4, new {
|
||||||
name = "Elf Owl"
|
name = "Elf Owl"
|
||||||
}))
|
}))
|
||||||
|
List(Pair(0, new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}), Pair(1, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}), Pair(2, new {
|
||||||
|
name = "Parrot"
|
||||||
|
}))
|
||||||
|
List(Pair(0, new {
|
||||||
|
name = "Wood Pigeon"
|
||||||
|
}), Pair(1, new {
|
||||||
|
name = "Barn Owl"
|
||||||
|
}), Pair(2, new {
|
||||||
|
name = "Parrot"
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
["join"] {
|
["join"] {
|
||||||
""
|
""
|
||||||
@@ -386,5 +542,17 @@ examples {
|
|||||||
Albatross
|
Albatross
|
||||||
Elf Owl
|
Elf Owl
|
||||||
"""
|
"""
|
||||||
|
"Wood PigeonBarn OwlParrot"
|
||||||
|
"""
|
||||||
|
Wood Pigeon
|
||||||
|
Barn Owl
|
||||||
|
Parrot
|
||||||
|
"""
|
||||||
|
"Wood PigeonBarn OwlParrot"
|
||||||
|
"""
|
||||||
|
Wood Pigeon
|
||||||
|
Barn Owl
|
||||||
|
Parrot
|
||||||
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Expected value of type `String`, but got type `Int`.
|
||||||
|
Value: 0
|
||||||
|
|
||||||
|
x | local b = a as Listing<Listing<String>>
|
||||||
|
^^^^^^
|
||||||
|
at listingTypeCheckError8#b (file:///$snippetsDir/input/errors/listingTypeCheckError8.pkl)
|
||||||
|
|
||||||
|
x | local a = new Listing { new Listing { 0 } }
|
||||||
|
^
|
||||||
|
at listingTypeCheckError8#a[#1][#1] (file:///$snippetsDir/input/errors/listingTypeCheckError8.pkl)
|
||||||
|
|
||||||
|
xxx | text = renderer.renderDocument(value)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at pkl.base#Module.output.text (pkl:base)
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Expected value of type `listingTypeCheckError9#Bar`, but got type `listingTypeCheckError9#Foo`.
|
||||||
|
Value: new Foo {}
|
||||||
|
|
||||||
|
x | local c = b as Listing<Bar>
|
||||||
|
^^^
|
||||||
|
at listingTypeCheckError9#c (file:///$snippetsDir/input/errors/listingTypeCheckError9.pkl)
|
||||||
|
|
||||||
|
x | local a = new Listing { new Foo {} }
|
||||||
|
^^^^^^^^^^
|
||||||
|
at listingTypeCheckError9#a[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError9.pkl)
|
||||||
|
|
||||||
|
xxx | text = renderer.renderDocument(value)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at pkl.base#Module.output.text (pkl:base)
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Expected value of type `mappingTypeCheckError11#Bar`, but got type `mappingTypeCheckError11#Foo`.
|
||||||
|
Value: new Foo {}
|
||||||
|
|
||||||
|
x | local c = b as Mapping<Int, Bar>
|
||||||
|
^^^
|
||||||
|
at mappingTypeCheckError11#c (file:///$snippetsDir/input/errors/mappingTypeCheckError11.pkl)
|
||||||
|
|
||||||
|
x | local a = new Mapping { [0] = new Foo {} }
|
||||||
|
^^^^^^^^^^
|
||||||
|
at mappingTypeCheckError11#a[0] (file:///$snippetsDir/input/errors/mappingTypeCheckError11.pkl)
|
||||||
|
|
||||||
|
xxx | text = renderer.renderDocument(value)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at pkl.base#Module.output.text (pkl:base)
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
foo1 {
|
||||||
|
"hello"
|
||||||
|
}
|
||||||
|
foo2 {
|
||||||
|
"hello"
|
||||||
|
}
|
||||||
|
res1 = true
|
||||||
|
res2 = true
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
foo {
|
||||||
|
"abcdx"
|
||||||
|
"ax"
|
||||||
|
}
|
||||||
@@ -38,3 +38,9 @@ res18 = "Expected value of type `Duration`, but got type `DataSize`. Value: 5.mb
|
|||||||
res18b = "Expected value of type `Duration`, but got type `DataSize`. Value: 5.mb"
|
res18b = "Expected value of type `Duration`, but got type `DataSize`. Value: 5.mb"
|
||||||
res19 = "abc"
|
res19 = "abc"
|
||||||
res20 = Pair(Map("abc", List("def")), Map("abc", List("def")))
|
res20 = Pair(Map("abc", List("def")), Map("abc", List("def")))
|
||||||
|
res21 {
|
||||||
|
"John Birdo"
|
||||||
|
}
|
||||||
|
res22 = Pair(new {
|
||||||
|
"John Birdo"
|
||||||
|
}, 0)
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Type constraint `endsWith(lastName)` violated.
|
||||||
|
Value: "Jimmy Bird"
|
||||||
|
|
||||||
|
x | typealias Birds = Listing<String(endsWith(lastName))>
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
at typeAliasContext#res (file:///$snippetsDir/input/types/helpers/originalTypealias.pkl)
|
||||||
|
|
||||||
|
x | res: originalTypealias.Birds = new { "Jimmy Bird" }
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
at typeAliasContext#res[#1] (file:///$snippetsDir/input/types/typeAliasContext.pkl)
|
||||||
|
|
||||||
|
xxx | text = renderer.renderDocument(value)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at pkl.base#Module.output.text (pkl:base)
|
||||||
@@ -117,7 +117,7 @@ class DocGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun DocPackage.deletePackageDir() {
|
private fun DocPackage.deletePackageDir() {
|
||||||
outputDir.resolve("$name/$version").deleteRecursively()
|
outputDir.resolve(IoUtils.encodePath("$name/$version")).deleteRecursively()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSymlinks(currentPackagesData: List<PackageData>) {
|
private fun createSymlinks(currentPackagesData: List<PackageData>) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package org.pkl.gradle;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -27,6 +28,7 @@ import org.gradle.api.GradleException;
|
|||||||
import org.gradle.api.NamedDomainObjectContainer;
|
import org.gradle.api.NamedDomainObjectContainer;
|
||||||
import org.gradle.api.Plugin;
|
import org.gradle.api.Plugin;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Transformer;
|
||||||
import org.gradle.api.file.SourceDirectorySet;
|
import org.gradle.api.file.SourceDirectorySet;
|
||||||
import org.gradle.api.plugins.Convention;
|
import org.gradle.api.plugins.Convention;
|
||||||
import org.gradle.api.provider.Provider;
|
import org.gradle.api.provider.Provider;
|
||||||
@@ -64,6 +66,7 @@ import org.pkl.gradle.task.PkldocTask;
|
|||||||
import org.pkl.gradle.task.ProjectPackageTask;
|
import org.pkl.gradle.task.ProjectPackageTask;
|
||||||
import org.pkl.gradle.task.ProjectResolveTask;
|
import org.pkl.gradle.task.ProjectResolveTask;
|
||||||
import org.pkl.gradle.task.TestTask;
|
import org.pkl.gradle.task.TestTask;
|
||||||
|
import org.pkl.gradle.utils.PluginUtils;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class PklPlugin implements Plugin<Project> {
|
public class PklPlugin implements Plugin<Project> {
|
||||||
@@ -456,6 +459,9 @@ public class PklPlugin implements Plugin<Project> {
|
|||||||
|
|
||||||
private List<File> getTransitiveModules(AnalyzeImportsTask analyzeTask) {
|
private List<File> getTransitiveModules(AnalyzeImportsTask analyzeTask) {
|
||||||
var outputFile = analyzeTask.getOutputFile().get().getAsFile().toPath();
|
var outputFile = analyzeTask.getOutputFile().get().getAsFile().toPath();
|
||||||
|
if (!analyzeTask.getOnlyIf().isSatisfiedBy(analyzeTask)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
var contents = Files.readString(outputFile);
|
var contents = Files.readString(outputFile);
|
||||||
ImportGraph importGraph = ImportGraph.parseFromJson(contents);
|
ImportGraph importGraph = ImportGraph.parseFromJson(contents);
|
||||||
@@ -470,9 +476,16 @@ public class PklPlugin implements Plugin<Project> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private <T extends ModulesTask, S extends ModulesSpec> void configureModulesTask(
|
private <T extends ModulesTask, S extends ModulesSpec> void configureModulesTask(
|
||||||
T task, S spec, @Nullable TaskProvider<AnalyzeImportsTask> analyzeImportsTask) {
|
T task,
|
||||||
|
S spec,
|
||||||
|
@Nullable TaskProvider<AnalyzeImportsTask> analyzeImportsTask,
|
||||||
|
@Nullable Transformer<List<?>, List<?>> mapSourceModules) {
|
||||||
configureBaseTask(task, spec);
|
configureBaseTask(task, spec);
|
||||||
|
if (mapSourceModules != null) {
|
||||||
|
task.getSourceModules().set(spec.getSourceModules().map(mapSourceModules));
|
||||||
|
} else {
|
||||||
task.getSourceModules().set(spec.getSourceModules());
|
task.getSourceModules().set(spec.getSourceModules());
|
||||||
|
}
|
||||||
task.getNoProject().set(spec.getNoProject());
|
task.getNoProject().set(spec.getNoProject());
|
||||||
task.getProjectDir().set(spec.getProjectDir());
|
task.getProjectDir().set(spec.getProjectDir());
|
||||||
task.getOmitProjectSettings().set(spec.getOmitProjectSettings());
|
task.getOmitProjectSettings().set(spec.getOmitProjectSettings());
|
||||||
@@ -484,6 +497,11 @@ public class PklPlugin implements Plugin<Project> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T extends ModulesTask, S extends ModulesSpec> void configureModulesTask(
|
||||||
|
T task, S spec, @Nullable TaskProvider<AnalyzeImportsTask> analyzeImportsTask) {
|
||||||
|
configureModulesTask(task, spec, analyzeImportsTask, null);
|
||||||
|
}
|
||||||
|
|
||||||
private TaskProvider<AnalyzeImportsTask> createAnalyzeImportsTask(ModulesSpec spec) {
|
private TaskProvider<AnalyzeImportsTask> createAnalyzeImportsTask(ModulesSpec spec) {
|
||||||
var outputFile =
|
var outputFile =
|
||||||
project
|
project
|
||||||
@@ -496,11 +514,26 @@ public class PklPlugin implements Plugin<Project> {
|
|||||||
spec.getName() + "GatherImports",
|
spec.getName() + "GatherImports",
|
||||||
AnalyzeImportsTask.class,
|
AnalyzeImportsTask.class,
|
||||||
task -> {
|
task -> {
|
||||||
configureModulesTask(task, spec, null);
|
configureModulesTask(
|
||||||
|
task,
|
||||||
|
spec,
|
||||||
|
null,
|
||||||
|
(modules) ->
|
||||||
|
// only need to analyze imports of file-based modules; it's unlikely that a
|
||||||
|
// non-file-based module will import a file-based module due to security
|
||||||
|
// manager trust levels (see
|
||||||
|
// org.pkl.core.SecurityManagers.getDefaultTrustLevel).
|
||||||
|
modules.stream()
|
||||||
|
.map(PluginUtils::parseModuleNotationToUri)
|
||||||
|
.filter(
|
||||||
|
(it) ->
|
||||||
|
it.getScheme() == null || it.getScheme().equalsIgnoreCase("file"))
|
||||||
|
.toList());
|
||||||
task.setDescription("Compute the set of imports declared by input modules");
|
task.setDescription("Compute the set of imports declared by input modules");
|
||||||
task.setGroup("build");
|
task.setGroup("build");
|
||||||
task.getOutputFormat().set(OutputFormat.JSON.toString());
|
task.getOutputFormat().set(OutputFormat.JSON.toString());
|
||||||
task.getOutputFile().set(outputFile);
|
task.getOutputFile().set(outputFile);
|
||||||
|
task.onlyIf(ignored -> !task.getSourceModules().get().isEmpty());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ package org.pkl.gradle.task;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.InvalidPathException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -31,10 +28,8 @@ import java.util.regex.Pattern;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
import org.gradle.api.InvalidUserDataException;
|
|
||||||
import org.gradle.api.file.ConfigurableFileCollection;
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
import org.gradle.api.file.DirectoryProperty;
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
import org.gradle.api.file.FileSystemLocation;
|
|
||||||
import org.gradle.api.model.ObjectFactory;
|
import org.gradle.api.model.ObjectFactory;
|
||||||
import org.gradle.api.provider.ListProperty;
|
import org.gradle.api.provider.ListProperty;
|
||||||
import org.gradle.api.provider.MapProperty;
|
import org.gradle.api.provider.MapProperty;
|
||||||
@@ -49,9 +44,9 @@ import org.gradle.api.tasks.Optional;
|
|||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
import org.pkl.commons.cli.CliBaseOptions;
|
import org.pkl.commons.cli.CliBaseOptions;
|
||||||
import org.pkl.core.evaluatorSettings.Color;
|
import org.pkl.core.evaluatorSettings.Color;
|
||||||
import org.pkl.core.util.IoUtils;
|
|
||||||
import org.pkl.core.util.LateInit;
|
import org.pkl.core.util.LateInit;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
import org.pkl.gradle.utils.PluginUtils;
|
||||||
|
|
||||||
public abstract class BasePklTask extends DefaultTask {
|
public abstract class BasePklTask extends DefaultTask {
|
||||||
@Input
|
@Input
|
||||||
@@ -74,7 +69,7 @@ public abstract class BasePklTask extends DefaultTask {
|
|||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
public Provider<Object> getParsedSettingsModule() {
|
public Provider<Object> getParsedSettingsModule() {
|
||||||
return getSettingsModule().map(this::parseModuleNotation);
|
return getSettingsModule().map(PluginUtils::parseModuleNotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@InputFile
|
@InputFile
|
||||||
@@ -165,7 +160,7 @@ public abstract class BasePklTask extends DefaultTask {
|
|||||||
parseModulePath(),
|
parseModulePath(),
|
||||||
getProject().getProjectDir().toPath(),
|
getProject().getProjectDir().toPath(),
|
||||||
mapAndGetOrNull(getEvalRootDirPath(), Paths::get),
|
mapAndGetOrNull(getEvalRootDirPath(), Paths::get),
|
||||||
mapAndGetOrNull(getSettingsModule(), this::parseModuleNotationToUri),
|
mapAndGetOrNull(getSettingsModule(), PluginUtils::parseModuleNotationToUri),
|
||||||
null,
|
null,
|
||||||
getEvalTimeout().getOrNull(),
|
getEvalTimeout().getOrNull(),
|
||||||
mapAndGetOrNull(getModuleCacheDir(), it1 -> it1.getAsFile().toPath()),
|
mapAndGetOrNull(getModuleCacheDir(), it1 -> it1.getAsFile().toPath()),
|
||||||
@@ -199,100 +194,6 @@ public abstract class BasePklTask extends DefaultTask {
|
|||||||
return getModulePath().getFiles().stream().map(File::toPath).collect(Collectors.toList());
|
return getModulePath().getFiles().stream().map(File::toPath).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the specified source module notation into a "parsed" notation which is then used for
|
|
||||||
* input path tracking and as an argument for the CLI API.
|
|
||||||
*
|
|
||||||
* <p>This method accepts the following input types:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link URI} - used as is.
|
|
||||||
* <li>{@link File} - used as is.
|
|
||||||
* <li>{@link Path} - converted to a {@link File}. This conversion may fail because not all
|
|
||||||
* {@link Path}s point to the local file system.
|
|
||||||
* <li>{@link URL} - converted to a {@link URI}. This conversion may fail because {@link URL}
|
|
||||||
* allows for URLs which are not compliant URIs.
|
|
||||||
* <li>{@link CharSequence} - first, converted to a string. If this string is "URI-like" (see
|
|
||||||
* {@link IoUtils#isUriLike(String)}), then we attempt to parse it as a {@link URI}, which
|
|
||||||
* may fail. Otherwise, we attempt to parse it as a {@link Path}, which is then converted to
|
|
||||||
* a {@link File} (both of these operations may fail).
|
|
||||||
* <li>{@link FileSystemLocation} - converted to a {@link File} via the {@link
|
|
||||||
* FileSystemLocation#getAsFile()} method.
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* In case the returned value is determined to be a {@link URI}, then this URI is first checked
|
|
||||||
* for whether its scheme is {@code file}, like {@code file:///example/path}. In such case, this
|
|
||||||
* method returns a {@link File} corresponding to the file path in the URI. Otherwise, a {@link
|
|
||||||
* URI} instance is returned.
|
|
||||||
*
|
|
||||||
* @throws InvalidUserDataException In case the input is none of the types described above, or
|
|
||||||
* when the underlying value cannot be parsed correctly.
|
|
||||||
*/
|
|
||||||
protected Object parseModuleNotation(Object notation) {
|
|
||||||
if (notation instanceof URI uri) {
|
|
||||||
if ("file".equals(uri.getScheme())) {
|
|
||||||
return new File(uri.getPath());
|
|
||||||
}
|
|
||||||
return uri;
|
|
||||||
} else if (notation instanceof File) {
|
|
||||||
return notation;
|
|
||||||
} else if (notation instanceof Path path) {
|
|
||||||
try {
|
|
||||||
return path.toFile();
|
|
||||||
} catch (UnsupportedOperationException e) {
|
|
||||||
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + notation, e);
|
|
||||||
}
|
|
||||||
} else if (notation instanceof URL url) {
|
|
||||||
try {
|
|
||||||
return parseModuleNotation(url.toURI());
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + notation, e);
|
|
||||||
}
|
|
||||||
} else if (notation instanceof CharSequence) {
|
|
||||||
var s = notation.toString();
|
|
||||||
if (IoUtils.isUriLike(s)) {
|
|
||||||
try {
|
|
||||||
return parseModuleNotation(IoUtils.toUri(s));
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + s, e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return Paths.get(s).toFile();
|
|
||||||
} catch (InvalidPathException | UnsupportedOperationException e) {
|
|
||||||
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + s, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (notation instanceof FileSystemLocation location) {
|
|
||||||
return location.getAsFile();
|
|
||||||
} else {
|
|
||||||
throw new InvalidUserDataException(
|
|
||||||
"Unsupported value of type "
|
|
||||||
+ notation.getClass()
|
|
||||||
+ " used as a module path: "
|
|
||||||
+ notation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected URI parseModuleNotationToUri(Object m) {
|
|
||||||
var parsed1 = parseModuleNotation(m);
|
|
||||||
return parsedModuleNotationToUri(parsed1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts either a file or a URI to a URI. We convert a file to a URI via the {@link
|
|
||||||
* IoUtils#createUri(String)} because other ways of conversion can make relative paths into
|
|
||||||
* absolute URIs, which may break module loading.
|
|
||||||
*/
|
|
||||||
private URI parsedModuleNotationToUri(Object notation) {
|
|
||||||
if (notation instanceof File file) {
|
|
||||||
return IoUtils.createUri(file.getPath());
|
|
||||||
} else if (notation instanceof URI uri) {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Invalid parsed module notation: " + notation);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<Pattern> patternsFromStrings(List<String> patterns) {
|
protected List<Pattern> patternsFromStrings(List<String> patterns) {
|
||||||
return patterns.stream().map(Pattern::compile).collect(Collectors.toList());
|
return patterns.stream().map(Pattern::compile).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ import org.gradle.api.tasks.Optional;
|
|||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
import org.pkl.commons.cli.CliBaseOptions;
|
import org.pkl.commons.cli.CliBaseOptions;
|
||||||
import org.pkl.core.evaluatorSettings.Color;
|
import org.pkl.core.evaluatorSettings.Color;
|
||||||
import org.pkl.core.util.IoUtils;
|
|
||||||
import org.pkl.core.util.Pair;
|
import org.pkl.core.util.Pair;
|
||||||
|
import org.pkl.gradle.utils.PluginUtils;
|
||||||
|
|
||||||
public abstract class ModulesTask extends BasePklTask {
|
public abstract class ModulesTask extends BasePklTask {
|
||||||
// We expose the contents of this property as task inputs via the sourceModuleFiles
|
// We expose the contents of this property as task inputs via the sourceModuleFiles
|
||||||
@@ -84,7 +84,7 @@ public abstract class ModulesTask extends BasePklTask {
|
|||||||
@Override
|
@Override
|
||||||
protected List<URI> getSourceModulesAsUris() {
|
protected List<URI> getSourceModulesAsUris() {
|
||||||
return getSourceModules().get().stream()
|
return getSourceModules().get().stream()
|
||||||
.map(this::parseModuleNotationToUri)
|
.map(PluginUtils::parseModuleNotationToUri)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ public abstract class ModulesTask extends BasePklTask {
|
|||||||
var files = new ArrayList<File>();
|
var files = new ArrayList<File>();
|
||||||
var uris = new ArrayList<URI>();
|
var uris = new ArrayList<URI>();
|
||||||
for (var m : modules) {
|
for (var m : modules) {
|
||||||
var parsed = parseModuleNotation(m);
|
var parsed = PluginUtils.parseModuleNotation(m);
|
||||||
if (parsed instanceof File file) {
|
if (parsed instanceof File file) {
|
||||||
files.add(file);
|
files.add(file);
|
||||||
} else if (parsed instanceof URI uri) {
|
} else if (parsed instanceof URI uri) {
|
||||||
@@ -127,28 +127,6 @@ public abstract class ModulesTask extends BasePklTask {
|
|||||||
return Pair.of(files, uris);
|
return Pair.of(files, uris);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts either a file or a URI to a URI. We convert a relative file to a URI via the {@link
|
|
||||||
* IoUtils#createUri(String)} because other ways of conversion can make relative paths into
|
|
||||||
* absolute URIs, which may break module loading.
|
|
||||||
*/
|
|
||||||
private URI parsedModuleNotationToUri(Object notation) {
|
|
||||||
if (notation instanceof File file) {
|
|
||||||
if (file.isAbsolute()) {
|
|
||||||
return file.toPath().toUri();
|
|
||||||
}
|
|
||||||
return IoUtils.createUri(IoUtils.toNormalizedPathString(file.toPath()));
|
|
||||||
} else if (notation instanceof URI uri) {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Invalid parsed module notation: " + notation);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected URI parseModuleNotationToUri(Object m) {
|
|
||||||
var parsed1 = parseModuleNotation(m);
|
|
||||||
return parsedModuleNotationToUri(parsed1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
@Override
|
@Override
|
||||||
public void runTask() {
|
public void runTask() {
|
||||||
@@ -172,7 +150,7 @@ public abstract class ModulesTask extends BasePklTask {
|
|||||||
parseModulePath(),
|
parseModulePath(),
|
||||||
getProject().getProjectDir().toPath(),
|
getProject().getProjectDir().toPath(),
|
||||||
mapAndGetOrNull(getEvalRootDirPath(), Paths::get),
|
mapAndGetOrNull(getEvalRootDirPath(), Paths::get),
|
||||||
mapAndGetOrNull(getSettingsModule(), this::parseModuleNotationToUri),
|
mapAndGetOrNull(getSettingsModule(), PluginUtils::parseModuleNotationToUri),
|
||||||
getProjectDir().isPresent() ? getProjectDir().get().getAsFile().toPath() : null,
|
getProjectDir().isPresent() ? getProjectDir().get().getAsFile().toPath() : null,
|
||||||
getEvalTimeout().getOrNull(),
|
getEvalTimeout().getOrNull(),
|
||||||
mapAndGetOrNull(getModuleCacheDir(), it1 -> it1.getAsFile().toPath()),
|
mapAndGetOrNull(getModuleCacheDir(), it1 -> it1.getAsFile().toPath()),
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2024 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.gradle.utils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.InvalidPathException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import org.gradle.api.InvalidUserDataException;
|
||||||
|
import org.gradle.api.file.FileSystemLocation;
|
||||||
|
import org.pkl.core.util.IoUtils;
|
||||||
|
|
||||||
|
public class PluginUtils {
|
||||||
|
private PluginUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the specified source module notation into a "parsed" notation which is then used for
|
||||||
|
* input path tracking and as an argument for the CLI API.
|
||||||
|
*
|
||||||
|
* <p>This method accepts the following input types:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link URI} - used as is.
|
||||||
|
* <li>{@link File} - used as is.
|
||||||
|
* <li>{@link Path} - converted to a {@link File}. This conversion may fail because not all
|
||||||
|
* {@link Path}s point to the local file system.
|
||||||
|
* <li>{@link URL} - converted to a {@link URI}. This conversion may fail because {@link URL}
|
||||||
|
* allows for URLs which are not compliant URIs.
|
||||||
|
* <li>{@link CharSequence} - first, converted to a string. If this string is "URI-like" (see
|
||||||
|
* {@link IoUtils#isUriLike(String)}), then we attempt to parse it as a {@link URI}, which
|
||||||
|
* may fail. Otherwise, we attempt to parse it as a {@link Path}, which is then converted to
|
||||||
|
* a {@link File} (both of these operations may fail).
|
||||||
|
* <li>{@link FileSystemLocation} - converted to a {@link File} via the {@link
|
||||||
|
* FileSystemLocation#getAsFile()} method.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* In case the returned value is determined to be a {@link URI}, then this URI is first checked
|
||||||
|
* for whether its scheme is {@code file}, like {@code file:///example/path}. In such case, this
|
||||||
|
* method returns a {@link File} corresponding to the file path in the URI. Otherwise, a {@link
|
||||||
|
* URI} instance is returned.
|
||||||
|
*
|
||||||
|
* @throws InvalidUserDataException In case the input is none of the types described above, or
|
||||||
|
* when the underlying value cannot be parsed correctly.
|
||||||
|
*/
|
||||||
|
public static Object parseModuleNotation(Object notation) {
|
||||||
|
if (notation instanceof URI uri) {
|
||||||
|
if ("file".equals(uri.getScheme())) {
|
||||||
|
return new File(uri.getPath());
|
||||||
|
}
|
||||||
|
return uri;
|
||||||
|
} else if (notation instanceof File) {
|
||||||
|
return notation;
|
||||||
|
} else if (notation instanceof Path path) {
|
||||||
|
try {
|
||||||
|
return path.toFile();
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + notation, e);
|
||||||
|
}
|
||||||
|
} else if (notation instanceof URL url) {
|
||||||
|
try {
|
||||||
|
return parseModuleNotation(url.toURI());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + notation, e);
|
||||||
|
}
|
||||||
|
} else if (notation instanceof CharSequence) {
|
||||||
|
var s = notation.toString();
|
||||||
|
if (IoUtils.isUriLike(s)) {
|
||||||
|
try {
|
||||||
|
return parseModuleNotation(IoUtils.toUri(s));
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new InvalidUserDataException("Failed to parse Pkl module URI: " + s, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return Paths.get(s).toFile();
|
||||||
|
} catch (InvalidPathException | UnsupportedOperationException e) {
|
||||||
|
throw new InvalidUserDataException("Failed to parse Pkl module file path: " + s, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (notation instanceof FileSystemLocation location) {
|
||||||
|
return location.getAsFile();
|
||||||
|
} else {
|
||||||
|
throw new InvalidUserDataException(
|
||||||
|
"Unsupported value of type "
|
||||||
|
+ notation.getClass()
|
||||||
|
+ " used as a module path: "
|
||||||
|
+ notation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts either a file or a URI to a URI. We convert a relative file to a URI via the {@link
|
||||||
|
* IoUtils#createUri(String)} because other ways of conversion can make relative paths into
|
||||||
|
* absolute URIs, which may break module loading.
|
||||||
|
*/
|
||||||
|
public static URI parsedModuleNotationToUri(Object notation) {
|
||||||
|
if (notation instanceof File file) {
|
||||||
|
if (file.isAbsolute()) {
|
||||||
|
return file.toPath().toUri();
|
||||||
|
}
|
||||||
|
return IoUtils.createUri(IoUtils.toNormalizedPathString(file.toPath()));
|
||||||
|
} else if (notation instanceof URI uri) {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid parsed module notation: " + notation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URI parseModuleNotationToUri(Object m) {
|
||||||
|
var parsed1 = PluginUtils.parseModuleNotation(m);
|
||||||
|
return parsedModuleNotationToUri(parsed1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@NonnullByDefault
|
||||||
|
package org.pkl.gradle.utils;
|
||||||
|
|
||||||
|
import org.pkl.core.util.NonnullByDefault;
|
||||||
@@ -15,13 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.pkl.gradle
|
package org.pkl.gradle
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.readText
|
import kotlin.io.path.readText
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.io.TempDir
|
||||||
|
import org.pkl.commons.test.PackageServer
|
||||||
|
|
||||||
class PkldocGeneratorsTest : AbstractTest() {
|
class PkldocGeneratorsTest : AbstractTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `generate docs`() {
|
fun `generate docs`(@TempDir tempDir: Path) {
|
||||||
|
PackageServer.populateCacheDir(tempDir)
|
||||||
writeFile(
|
writeFile(
|
||||||
"build.gradle",
|
"build.gradle",
|
||||||
"""
|
"""
|
||||||
@@ -32,7 +36,8 @@ class PkldocGeneratorsTest : AbstractTest() {
|
|||||||
pkl {
|
pkl {
|
||||||
pkldocGenerators {
|
pkldocGenerators {
|
||||||
pkldoc {
|
pkldoc {
|
||||||
sourceModules = ["person.pkl", "doc-package-info.pkl"]
|
moduleCacheDir = file("${tempDir.toUri()}")
|
||||||
|
sourceModules = ["package://localhost:0/birds@0.5.0", "person.pkl", "doc-package-info.pkl"]
|
||||||
outputDir = file("build/pkldoc")
|
outputDir = file("build/pkldoc")
|
||||||
settingsModule = "pkl:settings"
|
settingsModule = "pkl:settings"
|
||||||
}
|
}
|
||||||
@@ -94,6 +99,39 @@ class PkldocGeneratorsTest : AbstractTest() {
|
|||||||
checkTextContains(moduleFile.readText(), "<html>", "Person", "Address", "other")
|
checkTextContains(moduleFile.readText(), "<html>", "Person", "Address", "other")
|
||||||
checkTextContains(personFile.readText(), "<html>", "name", "addresses")
|
checkTextContains(personFile.readText(), "<html>", "name", "addresses")
|
||||||
checkTextContains(addressFile.readText(), "<html>", "street", "zip")
|
checkTextContains(addressFile.readText(), "<html>", "street", "zip")
|
||||||
|
|
||||||
|
val birdsPackageFile = baseDir.resolve("localhost(3a)0/birds/0.5.0/index.html")
|
||||||
|
assertThat(birdsPackageFile).exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate docs only for package`(@TempDir tempDir: Path) {
|
||||||
|
PackageServer.populateCacheDir(tempDir)
|
||||||
|
writeFile(
|
||||||
|
"build.gradle",
|
||||||
|
"""
|
||||||
|
plugins {
|
||||||
|
id "org.pkl-lang"
|
||||||
|
}
|
||||||
|
|
||||||
|
pkl {
|
||||||
|
pkldocGenerators {
|
||||||
|
pkldoc {
|
||||||
|
moduleCacheDir = file("${tempDir.toUri()}")
|
||||||
|
sourceModules = ["package://localhost:0/birds@0.5.0"]
|
||||||
|
outputDir = file("build/pkldoc")
|
||||||
|
settingsModule = "pkl:settings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
runTask("pkldoc")
|
||||||
|
|
||||||
|
val baseDir = testProjectDir.resolve("build/pkldoc")
|
||||||
|
val birdsPackageFile = baseDir.resolve("localhost(3a)0/birds/0.5.0/index.html")
|
||||||
|
assertThat(birdsPackageFile).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user