mirror of
https://github.com/apple/pkl.git
synced 2026-03-31 14:13:15 +02:00
Implement power assertions (#1384)
This adds power assertions to Pkl! This implements the SPICE described in https://github.com/apple/pkl-evolution/pull/29 This follows the power assertions style of reporting also found in Groovy, Kotlin, and others. * Literal values are not emitted in the diagram * Stdlib constructors of literals like `List(1, 2)` are also considered literals Power assertions are added to: * Failing type constraints * Failing test facts Power assertions are implemented as a truffle instrument to observe execution. When an assertion fails, the instrument is created and the assertion is run again to observe facts. This incurs runtime overhead to collect facts, but has no impact on code in the non-error case. --------- Co-authored-by: Islon Scherer <islonscherer@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,18 @@
|
||||
package org.pkl.core.ast;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
|
||||
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
|
||||
import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.instrumentation.Tag;
|
||||
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.runtime.PklTags;
|
||||
import org.pkl.core.runtime.VmTypesGen;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract class ExpressionNode extends PklNode {
|
||||
@GenerateWrapper
|
||||
public abstract class ExpressionNode extends PklNode implements InstrumentableNode {
|
||||
protected ExpressionNode(SourceSection sourceSection) {
|
||||
super(sourceSection);
|
||||
}
|
||||
@@ -43,4 +49,19 @@ public abstract class ExpressionNode extends PklNode {
|
||||
public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException {
|
||||
return VmTypesGen.expectBoolean(executeGeneric(frame));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTag(Class<? extends Tag> tag) {
|
||||
return tag == PklTags.Expression.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstrumentable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrapperNode createWrapper(ProbeNode probe) {
|
||||
return new ExpressionNodeWrapper(this, probe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@ package org.pkl.core.ast;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.TypeSystemReference;
|
||||
import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
@@ -38,7 +39,10 @@ public abstract class PklNode extends Node {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceSection getSourceSection() {
|
||||
public final SourceSection getSourceSection() {
|
||||
if (this instanceof WrapperNode wrapperNode) {
|
||||
return wrapperNode.getDelegateNode().getSourceSection();
|
||||
}
|
||||
return sourceSection;
|
||||
}
|
||||
|
||||
|
||||
@@ -2813,7 +2813,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
throw exceptionBuilder()
|
||||
.evalError(e.getMessage(), e.getMessageArguments())
|
||||
.withCause(e.getCause())
|
||||
.withHint(e.getHint())
|
||||
.withHintBuilder(e.getHintBuilder())
|
||||
.withSourceSection(createSourceSection(ctx))
|
||||
.build();
|
||||
} catch (ExternalReaderProcessException e) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,12 +15,13 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.binary;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
|
||||
public abstract class ComparatorNode extends BinaryExpressionNode {
|
||||
public ComparatorNode(SourceSection sourceSection) {
|
||||
protected ComparatorNode(SourceSection sourceSection) {
|
||||
super(sourceSection);
|
||||
}
|
||||
|
||||
public abstract boolean executeWith(Object left, Object right);
|
||||
public abstract boolean executeWith(VirtualFrame frame, Object left, Object right);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,12 +17,15 @@ package org.pkl.core.ast.expression.binary;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
|
||||
import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.runtime.VmDataSize;
|
||||
import org.pkl.core.runtime.VmDuration;
|
||||
|
||||
@NodeInfo(shortName = ">")
|
||||
@GenerateWrapper
|
||||
public abstract class GreaterThanNode extends ComparatorNode {
|
||||
protected GreaterThanNode(SourceSection sourceSection) {
|
||||
super(sourceSection);
|
||||
@@ -63,4 +66,9 @@ public abstract class GreaterThanNode extends ComparatorNode {
|
||||
protected boolean eval(VmDataSize left, VmDataSize right) {
|
||||
return left.compareTo(right) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrapperNode createWrapper(ProbeNode probe) {
|
||||
return new GreaterThanNodeWrapper(sourceSection, this, probe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,12 +17,15 @@ package org.pkl.core.ast.expression.binary;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
|
||||
import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.runtime.VmDataSize;
|
||||
import org.pkl.core.runtime.VmDuration;
|
||||
|
||||
@NodeInfo(shortName = "<")
|
||||
@GenerateWrapper
|
||||
public abstract class LessThanNode extends ComparatorNode {
|
||||
protected LessThanNode(SourceSection sourceSection) {
|
||||
super(sourceSection);
|
||||
@@ -63,4 +66,9 @@ public abstract class LessThanNode extends ComparatorNode {
|
||||
protected boolean eval(VmDataSize left, VmDataSize right) {
|
||||
return left.compareTo(right) < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrapperNode createWrapper(ProbeNode probe) {
|
||||
return new LessThanNodeWrapper(sourceSection, this, probe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,8 @@ import com.oracle.truffle.api.dsl.ImportStatic;
|
||||
import com.oracle.truffle.api.dsl.NodeChild;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
|
||||
import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
@@ -40,6 +42,7 @@ import org.pkl.core.runtime.VmFunction;
|
||||
@ImportStatic(Identifier.class)
|
||||
@NodeChild(value = "receiverNode", type = ExpressionNode.class)
|
||||
@NodeChild(value = "receiverClassNode", type = GetClassNode.class, executeWith = "receiverNode")
|
||||
@GenerateWrapper
|
||||
public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
protected final Identifier methodName;
|
||||
@Children private final ExpressionNode[] argumentNodes;
|
||||
@@ -173,6 +176,12 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrapperNode createWrapper(ProbeNode probe) {
|
||||
return new InvokeMethodVirtualNodeWrapper(
|
||||
sourceSection, methodName, argumentNodes, lookupMode, needsConst, this, probe);
|
||||
}
|
||||
|
||||
private void checkConst(ClassMethod method) {
|
||||
if (needsConst && !method.isConst()) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -78,4 +78,9 @@ public abstract class WriteFrameSlotNode extends ExpressionNode {
|
||||
var kind = frame.getFrameDescriptor().getSlotKind(slot);
|
||||
return kind == FrameSlotKind.Boolean || kind == FrameSlotKind.Illegal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstrumentable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,4 +47,9 @@ public abstract class BlackholeNode extends ExpressionNode {
|
||||
CompilerDirectives.blackhole(value);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstrumentable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,10 +19,13 @@ import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.NodeChild;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
|
||||
import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
|
||||
@GenerateWrapper
|
||||
// NOTE: needs to be kept in sync with VmUtils.getClass()
|
||||
@NodeChild(value = "valueNode", type = ExpressionNode.class)
|
||||
public abstract class GetClassNode extends ExpressionNode {
|
||||
@@ -76,4 +79,9 @@ public abstract class GetClassNode extends ExpressionNode {
|
||||
// - has a guard that triggers per-class respecialization
|
||||
return ((VmValue) value).getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrapperNode createWrapper(ProbeNode probe) {
|
||||
return new GetClassNodeWrapper(this, probe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.internal;
|
||||
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
|
||||
/**
|
||||
* A node that stands in for source sections that are intrinsified (e.g. `isBetween` in `typealias
|
||||
* Int8 = Int(isBetween(..))`.
|
||||
*/
|
||||
public class SyntheticNode extends PklNode {
|
||||
public SyntheticNode(SourceSection sourceSection) {
|
||||
super(sourceSection);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -86,4 +86,9 @@ public abstract class ApplyVmFunction1Node extends ExpressionNode {
|
||||
|
||||
return callNode.call(function.getCallTarget(), function.getThisValue(), function, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstrumentable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,11 +27,13 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.ast.lambda.ApplyVmFunction1Node;
|
||||
import org.pkl.core.runtime.BaseModule;
|
||||
import org.pkl.core.runtime.VmContext;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
@NodeChild(value = "bodyNode", type = ExpressionNode.class)
|
||||
public abstract class TypeConstraintNode extends PklNode {
|
||||
|
||||
@CompilationFinal private int customThisSlot = -1;
|
||||
|
||||
protected TypeConstraintNode(SourceSection sourceSection) {
|
||||
@@ -40,6 +42,8 @@ public abstract class TypeConstraintNode extends PklNode {
|
||||
|
||||
public abstract void execute(VirtualFrame frame);
|
||||
|
||||
protected abstract ExpressionNode getBodyNode();
|
||||
|
||||
public String export() {
|
||||
return getSourceSection().getCharacters().toString();
|
||||
}
|
||||
@@ -49,8 +53,15 @@ public abstract class TypeConstraintNode extends PklNode {
|
||||
initConstraintSlot(frame);
|
||||
|
||||
if (!result) {
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
sourceSection, frame.getAuxiliarySlot(customThisSlot));
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
try (var valueTracker = VmContext.get(this).getValueTrackerFactory().create()) {
|
||||
getBodyNode().executeGeneric(frame);
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
sourceSection,
|
||||
frame.getAuxiliarySlot(customThisSlot),
|
||||
sourceSection,
|
||||
valueTracker.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +75,12 @@ public abstract class TypeConstraintNode extends PklNode {
|
||||
var value = frame.getAuxiliarySlot(customThisSlot);
|
||||
var result = applyNode.executeBoolean(function, value);
|
||||
if (!result) {
|
||||
throw new VmTypeMismatchException.Constraint(sourceSection, value);
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
try (var valueTracker = VmContext.get(this).getValueTrackerFactory().create()) {
|
||||
applyNode.executeBoolean(function, value);
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
sourceSection, value, function.getRootNode().getSourceSection(), valueTracker.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.LoopNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.pkl.core.PType;
|
||||
@@ -40,6 +42,7 @@ import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.expression.primary.GetModuleNode;
|
||||
import org.pkl.core.ast.frame.WriteFrameSlotNode;
|
||||
import org.pkl.core.ast.frame.WriteFrameSlotNodeGen;
|
||||
import org.pkl.core.ast.internal.SyntheticNode;
|
||||
import org.pkl.core.ast.member.DefaultPropertyBodyNode;
|
||||
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
@@ -143,6 +146,14 @@ public abstract class TypeNode extends PklNode {
|
||||
/** Visit child type nodes of this type. */
|
||||
protected abstract boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer);
|
||||
|
||||
protected VmTypeMismatchException constraintException(Object value, SourceSection sourceSection) {
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
sourceSection,
|
||||
value,
|
||||
sourceSection,
|
||||
Map.of(new SyntheticNode(sourceSection), List.of(false)));
|
||||
}
|
||||
|
||||
public static TypeNode forClass(SourceSection sourceSection, VmClass clazz) {
|
||||
return clazz.isClosed()
|
||||
? new FinalClassTypeNode(sourceSection, clazz)
|
||||
@@ -1719,9 +1730,9 @@ public abstract class TypeNode extends PklNode {
|
||||
//noinspection ConstantConditions
|
||||
defaultMember.initConstantValue(
|
||||
new VmFunction(
|
||||
VmUtils.createEmptyMaterializedFrame(),
|
||||
// Assumption: don't need to set the correct `thisValue`
|
||||
// because it is guaranteed to be never accessed.
|
||||
VmUtils.createEmptyMaterializedFrame(),
|
||||
null,
|
||||
1,
|
||||
new SimpleRootNode(
|
||||
@@ -2232,8 +2243,8 @@ public abstract class TypeNode extends PklNode {
|
||||
@Override
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmNull) {
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
BaseModule.getNonNullTypeAlias().getConstraintSection(), value);
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
throw constraintException(value, BaseModule.getNonNullTypeAlias().getConstraintSection());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -2274,7 +2285,9 @@ public abstract class TypeNode extends PklNode {
|
||||
if (value instanceof Long l) {
|
||||
if ((l & mask) == l) return value;
|
||||
|
||||
throw new VmTypeMismatchException.Constraint(typealias.getConstraintSection(), value);
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
var sourceSection = typealias.getConstraintSection();
|
||||
throw constraintException(value, sourceSection);
|
||||
}
|
||||
|
||||
throw new VmTypeMismatchException.Simple(
|
||||
@@ -2337,8 +2350,9 @@ public abstract class TypeNode extends PklNode {
|
||||
if (value instanceof Long l) {
|
||||
if (l == l.byteValue()) return value;
|
||||
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
BaseModule.getInt8TypeAlias().getConstraintSection(), value);
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
var sourceSection = BaseModule.getInt8TypeAlias().getConstraintSection();
|
||||
throw constraintException(value, sourceSection);
|
||||
}
|
||||
|
||||
throw new VmTypeMismatchException.Simple(
|
||||
@@ -2381,8 +2395,9 @@ public abstract class TypeNode extends PklNode {
|
||||
if (value instanceof Long l) {
|
||||
if (l == l.shortValue()) return value;
|
||||
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
BaseModule.getInt16TypeAlias().getConstraintSection(), value);
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
var sourceSection = BaseModule.getInt16TypeAlias().getConstraintSection();
|
||||
throw constraintException(value, sourceSection);
|
||||
}
|
||||
|
||||
throw new VmTypeMismatchException.Simple(
|
||||
@@ -2425,10 +2440,11 @@ public abstract class TypeNode extends PklNode {
|
||||
if (value instanceof Long l) {
|
||||
if (l == l.intValue()) return value;
|
||||
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
BaseModule.getInt32TypeAlias().getConstraintSection(), value);
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
throw constraintException(value, BaseModule.getInt32TypeAlias().getConstraintSection());
|
||||
}
|
||||
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
throw new VmTypeMismatchException.Simple(
|
||||
BaseModule.getInt32TypeAlias().getBaseTypeSection(), value, BaseModule.getIntClass());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.pkl.core.ast.type;
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.nodes.ControlFlowException;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -26,6 +27,7 @@ import org.pkl.core.ValueFormatter;
|
||||
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.VmException.ProgramValue;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.ErrorMessages;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
@@ -36,6 +38,7 @@ import org.pkl.core.util.Nullable;
|
||||
* there aren't any.)
|
||||
*/
|
||||
public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
|
||||
protected final SourceSection sourceSection;
|
||||
protected final Object actualValue;
|
||||
protected @Nullable Map<CallTarget, StackFrame> insertedStackFrames = null;
|
||||
@@ -54,20 +57,36 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
public abstract void describe(StringBuilder builder, String indent);
|
||||
public abstract void buildMessage(
|
||||
AnsiStringBuilder builder, String indent, boolean withPowerAssertions);
|
||||
|
||||
@TruffleBoundary
|
||||
public abstract VmException toVmException();
|
||||
public void buildHint(AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {}
|
||||
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new VmExceptionBuilder();
|
||||
@TruffleBoundary
|
||||
public final VmException toVmException() {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
protected final VmExceptionBuilder exceptionBuilder() {
|
||||
var builder =
|
||||
new VmExceptionBuilder()
|
||||
.withSourceSection(sourceSection)
|
||||
.withMessageBuilder(
|
||||
(b, withPowerAssertions) -> buildMessage(b, "", withPowerAssertions));
|
||||
if (hasHint()) {
|
||||
builder.withHintBuilder((b, withPowerAssertions) -> buildHint(b, "", withPowerAssertions));
|
||||
}
|
||||
if (insertedStackFrames != null) {
|
||||
builder.withInsertedStackFrames(insertedStackFrames);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected abstract Boolean hasHint();
|
||||
|
||||
public static final class Simple extends VmTypeMismatchException {
|
||||
|
||||
private final Object expectedType;
|
||||
|
||||
public Simple(SourceSection sourceSection, Object actualValue, Object expectedType) {
|
||||
@@ -83,7 +102,8 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public void describe(StringBuilder builder, String indent) {
|
||||
public void buildMessage(
|
||||
AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
|
||||
String renderedType;
|
||||
var valueFormatter = ValueFormatter.basic();
|
||||
if (expectedType instanceof String string) {
|
||||
@@ -143,54 +163,48 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public VmException toVmException() {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new StringBuilder();
|
||||
describe(builder, "");
|
||||
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(builder.toString())
|
||||
.withSourceSection(sourceSection);
|
||||
protected Boolean hasHint() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Constraint extends VmTypeMismatchException {
|
||||
public Constraint(SourceSection sourceSection, Object actualValue) {
|
||||
|
||||
private final SourceSection constraintBodySourceSection;
|
||||
private final Map<Node, List<Object>> trackedValues;
|
||||
|
||||
public Constraint(
|
||||
SourceSection sourceSection,
|
||||
Object actualValue,
|
||||
SourceSection constraintBodySourceSection,
|
||||
Map<Node, List<Object>> trackedValues) {
|
||||
super(sourceSection, actualValue);
|
||||
this.constraintBodySourceSection = constraintBodySourceSection;
|
||||
this.trackedValues = trackedValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public void describe(StringBuilder builder, String indent) {
|
||||
public void buildMessage(
|
||||
AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
|
||||
var expression = sourceSection.getCharacters().toString();
|
||||
builder
|
||||
.append(
|
||||
ErrorMessages.createIndented(
|
||||
"typeConstraintViolated", indent, sourceSection.getCharacters().toString()))
|
||||
.append(ErrorMessages.createIndented("typeConstraintViolated", indent, expression))
|
||||
.append("\n")
|
||||
.append(indent)
|
||||
.append("Value: ")
|
||||
.append(VmValueRenderer.singleLine(80 - indent.length()).render(actualValue));
|
||||
if (!withPowerAssertions) {
|
||||
return;
|
||||
}
|
||||
builder.append("\n\n");
|
||||
PowerAssertions.render(
|
||||
builder, indent + " ", constraintBodySourceSection, trackedValues, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public VmException toVmException() {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new StringBuilder();
|
||||
describe(builder, "");
|
||||
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(builder.toString())
|
||||
.withSourceSection(sourceSection);
|
||||
protected Boolean hasHint() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,33 +223,15 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public void describe(StringBuilder builder, String indent) {
|
||||
describeSummary(builder, indent);
|
||||
describeDetails(builder, indent);
|
||||
protected Boolean hasHint() {
|
||||
var nonTrivialMatches = findNonTrivialMismatches();
|
||||
return !nonTrivialMatches.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public VmException toVmException() {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var summary = new StringBuilder();
|
||||
describeSummary(summary, "");
|
||||
|
||||
var details = new StringBuilder();
|
||||
describeDetails(details, "");
|
||||
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(summary.toString())
|
||||
.withSourceSection(sourceSection)
|
||||
.withHint(details.toString());
|
||||
}
|
||||
|
||||
private void describeSummary(StringBuilder builder, String indent) {
|
||||
public void buildMessage(
|
||||
AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
|
||||
var nonTrivialMismatches = findNonTrivialMismatches();
|
||||
|
||||
if (nonTrivialMismatches.isEmpty()) {
|
||||
@@ -267,12 +263,16 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
.append(VmValueRenderer.singleLine(80 - indent.length()).render(actualValue));
|
||||
}
|
||||
|
||||
private void describeDetails(StringBuilder builder, String indent) {
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public void buildHint(AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
|
||||
var nonTrivialMismatches = findNonTrivialMismatches();
|
||||
|
||||
var isPeerError = false;
|
||||
for (var idx : nonTrivialMismatches) {
|
||||
if (!indent.isEmpty() || isPeerError) builder.append("\n\n");
|
||||
if (isPeerError) {
|
||||
builder.append("\n\n");
|
||||
}
|
||||
isPeerError = true;
|
||||
builder
|
||||
.append(
|
||||
@@ -285,7 +285,12 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
.getCharacters()
|
||||
.toString()))
|
||||
.append("\n");
|
||||
children[idx].describe(builder, indent + " ");
|
||||
var child = children[idx];
|
||||
child.buildMessage(builder, indent + " ", withPowerAssertions);
|
||||
if (child.hasHint()) {
|
||||
builder.append("\n\n");
|
||||
child.buildHint(builder, indent + " ", withPowerAssertions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,13 +309,15 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
}
|
||||
|
||||
public static final class Nothing extends VmTypeMismatchException {
|
||||
|
||||
public Nothing(SourceSection sourceSection, Object actualValue) {
|
||||
super(sourceSection, actualValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public void describe(StringBuilder builder, String indent) {
|
||||
public void buildMessage(
|
||||
AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
|
||||
builder
|
||||
.append(
|
||||
ErrorMessages.createIndented(
|
||||
@@ -322,19 +329,8 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public VmException toVmException() {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new StringBuilder();
|
||||
describe(builder, "");
|
||||
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(builder.toString())
|
||||
.withSourceSection(sourceSection);
|
||||
protected Boolean hasHint() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,8 @@ public class ReplServer implements AutoCloseable {
|
||||
this.workingDir = workingDir;
|
||||
this.securityManager = securityManager;
|
||||
this.moduleResolver = new ModuleResolver(moduleKeyFactories);
|
||||
this.errorRenderer = new VmExceptionRenderer(new StackTraceRenderer(frameTransformer), color);
|
||||
this.errorRenderer =
|
||||
new VmExceptionRenderer(new StackTraceRenderer(frameTransformer), color, true);
|
||||
this.color = color;
|
||||
replState = new ReplState(createEmptyReplModule(BaseModule.getModuleClass().getPrototype()));
|
||||
|
||||
|
||||
27
pkl-core/src/main/java/org/pkl/core/runtime/PklTags.java
Normal file
27
pkl-core/src/main/java/org/pkl/core/runtime/PklTags.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.instrumentation.Tag;
|
||||
|
||||
public final class PklTags {
|
||||
private PklTags() {}
|
||||
|
||||
@Tag.Identifier("EXPRESSION")
|
||||
public static final class Expression extends Tag {
|
||||
private Expression() {}
|
||||
}
|
||||
}
|
||||
508
pkl-core/src/main/java/org/pkl/core/runtime/PowerAssertions.java
Normal file
508
pkl-core/src/main/java/org/pkl/core/runtime/PowerAssertions.java
Normal file
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import org.pkl.core.ast.ConstantValueNode;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinMethodNode;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinObjectMethodNode;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinPropertyNode;
|
||||
import org.pkl.core.ast.expression.member.InvokeMethodDirectNode;
|
||||
import org.pkl.core.ast.type.GetParentForTypeNode;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.AnsiStringBuilder.AnsiCode;
|
||||
import org.pkl.core.util.Nullable;
|
||||
import org.pkl.core.util.SyntaxHighlighter;
|
||||
import org.pkl.parser.Lexer;
|
||||
import org.pkl.parser.Parser;
|
||||
import org.pkl.parser.Token;
|
||||
import org.pkl.parser.syntax.Expr;
|
||||
import org.pkl.parser.syntax.Expr.BinaryOperatorExpr;
|
||||
import org.pkl.parser.syntax.Expr.BoolLiteralExpr;
|
||||
import org.pkl.parser.syntax.Expr.FloatLiteralExpr;
|
||||
import org.pkl.parser.syntax.Expr.FunctionLiteralExpr;
|
||||
import org.pkl.parser.syntax.Expr.IntLiteralExpr;
|
||||
import org.pkl.parser.syntax.Expr.LetExpr;
|
||||
import org.pkl.parser.syntax.Expr.NullLiteralExpr;
|
||||
import org.pkl.parser.syntax.Expr.QualifiedAccessExpr;
|
||||
import org.pkl.parser.syntax.Expr.StringLiteralExpr;
|
||||
import org.pkl.parser.syntax.Expr.SubscriptExpr;
|
||||
import org.pkl.parser.syntax.Expr.SuperAccessExpr;
|
||||
import org.pkl.parser.syntax.Expr.SuperSubscriptExpr;
|
||||
import org.pkl.parser.syntax.Expr.TypeCheckExpr;
|
||||
import org.pkl.parser.syntax.Expr.UnqualifiedAccessExpr;
|
||||
import org.pkl.parser.syntax.ObjectMember.ForGenerator;
|
||||
import org.pkl.parser.syntax.ObjectMember.MemberPredicate;
|
||||
import org.pkl.parser.syntax.StringPart.StringChars;
|
||||
import org.pkl.parser.syntax.StringPart.StringInterpolation;
|
||||
|
||||
public class PowerAssertions {
|
||||
private PowerAssertions() {}
|
||||
|
||||
private static final VmValueRenderer vmValueRenderer = VmValueRenderer.singleLine(100);
|
||||
private static final Parser parser = new Parser();
|
||||
|
||||
public static void render(
|
||||
AnsiStringBuilder out,
|
||||
String indent,
|
||||
SourceSection sourceSection,
|
||||
Map<Node, List<Object>> trackedValues,
|
||||
@Nullable Consumer<AnsiStringBuilder> firstFrameSuffix) {
|
||||
out.appendSandboxed(
|
||||
() -> {
|
||||
var lines = lines(sourceSection);
|
||||
var layerEntries = getLayerEntries(trackedValues, sourceSection);
|
||||
var indentation =
|
||||
lines.size() == 1
|
||||
? 0
|
||||
: Collections.min(
|
||||
lines.stream()
|
||||
.skip(1)
|
||||
.map((it) -> leadingWhitespace(it.getCharacters()))
|
||||
.toList());
|
||||
var sourceLines = lines(sourceSection);
|
||||
var renderedMarkers = false;
|
||||
for (var i = 0; i < sourceLines.size(); i++) {
|
||||
if (renderedMarkers) {
|
||||
out.append("\n\n");
|
||||
}
|
||||
var line = sourceLines.get(i);
|
||||
var offset = i == 0 ? line.getStartColumn() - 1 : indentation;
|
||||
renderedMarkers =
|
||||
renderLine(
|
||||
out, indent, line, layerEntries, offset, i == 0 ? firstFrameSuffix : null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isInForGeneratorOrLambdaOrPredicate(
|
||||
org.pkl.parser.syntax.Node myNode, Expr rootExpr) {
|
||||
var parent = myNode.parent();
|
||||
while (parent != null) {
|
||||
if (parent instanceof FunctionLiteralExpr) {
|
||||
// okay to show power assert if this lambda is the root constraint expr (this was a lambda
|
||||
// passed in as a a constraint)
|
||||
return !parent.equals(rootExpr);
|
||||
}
|
||||
if (parent instanceof MemberPredicate) {
|
||||
return true;
|
||||
}
|
||||
// okay if it's in expr section of for generator
|
||||
if (parent instanceof ForGenerator forGenerator) {
|
||||
return !forGenerator.getExpr().span().contains(myNode.span());
|
||||
}
|
||||
parent = parent.parent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isLiteral(org.pkl.parser.syntax.Node parserNode) {
|
||||
if (parserNode instanceof IntLiteralExpr
|
||||
|| parserNode instanceof FloatLiteralExpr
|
||||
|| parserNode instanceof BoolLiteralExpr
|
||||
|| parserNode instanceof NullLiteralExpr) {
|
||||
return true;
|
||||
}
|
||||
if (parserNode instanceof StringLiteralExpr stringLiteralExpr) {
|
||||
return !stringLiteralExpr.hasInterpolation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// tells if this method is call to a literal value
|
||||
// treats method calls like `List(1, 2, 3)` as literal values.
|
||||
private static boolean isLiteral(Node truffleNode, org.pkl.parser.syntax.Node parserNode) {
|
||||
if (isLiteral(parserNode)) {
|
||||
return true;
|
||||
}
|
||||
if (truffleNode instanceof ConstantValueNode) {
|
||||
if (parserNode instanceof UnqualifiedAccessExpr unqualifiedAccessExpr) {
|
||||
// Assumption: if we have both ConstantValueNode, and the parser node is
|
||||
// UnqualifiedAccessExpr with arguments, then this must be a `List()`, `Map()`, etc.
|
||||
//
|
||||
// Note: reading a local property whose value is a constant will also turn into a
|
||||
// ConstantValueNode.
|
||||
return unqualifiedAccessExpr.getArgumentList() != null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// assumption: only calls to methods within the base module are direct method calls.
|
||||
if (!(truffleNode instanceof InvokeMethodDirectNode)) {
|
||||
return false;
|
||||
}
|
||||
var accessExpr = (UnqualifiedAccessExpr) parserNode;
|
||||
// assumption: "literal" values are method calls that are uppercased. e.g. IntSeq(..), Pair(..)
|
||||
if (!Character.isUpperCase(accessExpr.getIdentifier().getValue().charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
var argsList = accessExpr.getArgumentList();
|
||||
assert argsList != null;
|
||||
return argsList.getArguments().stream().allMatch(PowerAssertions::isLiteral);
|
||||
}
|
||||
|
||||
private static boolean shouldHide(
|
||||
Node truffleNode, org.pkl.parser.syntax.Node parserNode, Object value, Expr rootExpr) {
|
||||
// literal values are self-evident in their source code
|
||||
if (isLiteral(truffleNode, parserNode)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
// let expressions will already show the resolved value in the expression body; showing the
|
||||
// let as well simply adds noise.
|
||||
parserNode instanceof LetExpr
|
||||
// we'll already show the expression within string interpolation
|
||||
|| parserNode instanceof StringInterpolation
|
||||
|| parserNode instanceof StringChars) {
|
||||
return true;
|
||||
}
|
||||
if (isInForGeneratorOrLambdaOrPredicate(parserNode, rootExpr)) {
|
||||
return true;
|
||||
}
|
||||
if (value instanceof VmFunction) {
|
||||
return true;
|
||||
}
|
||||
// implicit behavior around `new`
|
||||
return truffleNode instanceof GetParentForTypeNode
|
||||
|| truffleNode instanceof InferParentWithinMethodNode
|
||||
|| truffleNode instanceof InferParentWithinObjectMethodNode
|
||||
|| truffleNode instanceof InferParentWithinPropertyNode;
|
||||
}
|
||||
|
||||
// tries to find the parser node for this node
|
||||
private static @Nullable org.pkl.parser.syntax.Node findParserNode(
|
||||
Node node, @Nullable org.pkl.parser.syntax.Node parserNode, int offset) {
|
||||
if (!node.getSourceSection().isAvailable()) {
|
||||
return null;
|
||||
}
|
||||
if (parserNode == null) {
|
||||
return null;
|
||||
}
|
||||
var span = parserNode.span();
|
||||
var charIndex = span.charIndex() + offset;
|
||||
var ss = node.getSourceSection();
|
||||
if (charIndex == ss.getCharIndex() && span.length() == ss.getCharLength()) {
|
||||
return parserNode;
|
||||
}
|
||||
var children = parserNode.children();
|
||||
if (children == null) {
|
||||
return null;
|
||||
}
|
||||
for (var child : children) {
|
||||
var found = findParserNode(node, child, offset);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Collection<LayerEntry> getLayerEntries(
|
||||
Map<Node, List<Object>> trackedValues, SourceSection sourceSection) {
|
||||
var exprNode = parser.parseExpressionInput(sourceSection.getCharacters().toString());
|
||||
// it's possible that two nodes can turn into identical `SourceEntry`s; ensure these entries are
|
||||
// distinct by using a set.
|
||||
var ret = new LinkedHashSet<LayerEntry>();
|
||||
for (var entry : trackedValues.entrySet()) {
|
||||
var truffleNode = entry.getKey();
|
||||
var values = entry.getValue();
|
||||
var parserNode = findParserNode(truffleNode, exprNode, sourceSection.getCharIndex());
|
||||
if (parserNode == null) {
|
||||
continue;
|
||||
}
|
||||
// this node was executed multiple times; not sure how to best show this in a power assert
|
||||
// graph
|
||||
if (values.size() > 1) {
|
||||
continue;
|
||||
}
|
||||
var value = values.get(0);
|
||||
if (shouldHide(truffleNode, parserNode, value, exprNode)) {
|
||||
continue;
|
||||
}
|
||||
ret.add(SourceEntry.create(truffleNode, parserNode, value));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean canFit(Deque<LayerEntry> layer, LayerEntry elem) {
|
||||
if (layer.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
var nextEntry = layer.getFirst();
|
||||
return elem.startColumn() + elem.length() + 1 < nextEntry.startColumn();
|
||||
}
|
||||
|
||||
private static boolean canFitMarker(Deque<LayerEntry> layer, LayerEntry elem) {
|
||||
if (layer.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
var nextEntry = layer.getFirst();
|
||||
return elem.startColumn() < nextEntry.startColumn();
|
||||
}
|
||||
|
||||
private static List<Collection<LayerEntry>> buildLayers(
|
||||
Collection<LayerEntry> layerEntries, SourceSection line) {
|
||||
var sortedSections =
|
||||
layerEntries.stream()
|
||||
.filter((it) -> it.startLine() == line.getStartLine())
|
||||
.sorted(Comparator.comparingInt((it) -> -it.startColumn()))
|
||||
.collect(Collectors.toCollection(LinkedList::new));
|
||||
if (sortedSections.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
var layers = new ArrayList<Collection<LayerEntry>>();
|
||||
|
||||
// first layer is all markers
|
||||
layers.add(firstLayerMarkers(sortedSections));
|
||||
|
||||
while (!sortedSections.isEmpty()) {
|
||||
var layer = new ArrayDeque<LayerEntry>();
|
||||
var iter = sortedSections.iterator();
|
||||
while (iter.hasNext()) {
|
||||
var next = iter.next();
|
||||
if (canFit(layer, next)) {
|
||||
layer.addFirst(next);
|
||||
iter.remove();
|
||||
} else if (canFitMarker(layer, next)) {
|
||||
layer.addFirst(next.toMarker());
|
||||
}
|
||||
}
|
||||
layers.add(layer);
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
|
||||
private static Collection<LayerEntry> firstLayerMarkers(List<LayerEntry> entries) {
|
||||
var layer = new ArrayDeque<LayerEntry>();
|
||||
LayerEntry prevEntry = null;
|
||||
for (var entry : entries) {
|
||||
if (prevEntry != null && prevEntry.startColumn() == entry.startColumn()) {
|
||||
continue;
|
||||
}
|
||||
layer.addFirst(entry.toMarker());
|
||||
prevEntry = entry;
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
private static List<SourceSection> lines(SourceSection sourceSection) {
|
||||
var startLine = sourceSection.getStartLine();
|
||||
var endLine = sourceSection.getEndLine();
|
||||
if (!sourceSection.isAvailable() || startLine == endLine) {
|
||||
return Collections.singletonList(sourceSection);
|
||||
}
|
||||
|
||||
var result = new ArrayList<SourceSection>();
|
||||
var source = sourceSection.getSource();
|
||||
var charIndex = sourceSection.getCharIndex();
|
||||
var endCharIndex = charIndex + sourceSection.getCharLength();
|
||||
|
||||
for (var lineNumber = startLine; lineNumber <= endLine; lineNumber++) {
|
||||
var lineSection = source.createSection(lineNumber);
|
||||
var lineStartChar = lineSection.getCharIndex();
|
||||
var lineEndChar = lineStartChar + lineSection.getCharLength();
|
||||
|
||||
var sectionStartChar = Math.max(charIndex, lineStartChar);
|
||||
var sectionEndChar = Math.min(endCharIndex, lineEndChar);
|
||||
|
||||
if (sectionStartChar < sectionEndChar) {
|
||||
result.add(source.createSection(sectionStartChar, sectionEndChar - sectionStartChar));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String trimLeadingWhitespace(String str, int n) {
|
||||
var i = 0;
|
||||
|
||||
while (i < str.length() && i < n && Character.isWhitespace(str.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
|
||||
return str.substring(i);
|
||||
}
|
||||
|
||||
private static boolean renderLine(
|
||||
AnsiStringBuilder out,
|
||||
String indent,
|
||||
SourceSection line,
|
||||
Collection<LayerEntry> layerEntries,
|
||||
int trimStart,
|
||||
@Nullable Consumer<AnsiStringBuilder> lineSuffix) {
|
||||
var layers = buildLayers(layerEntries, line);
|
||||
var content = trimLeadingWhitespace(line.getCharacters().toString(), trimStart);
|
||||
out.append(indent).append(content);
|
||||
if (lineSuffix != null) {
|
||||
lineSuffix.accept(out);
|
||||
}
|
||||
out.append('\n');
|
||||
if (layers.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < layers.size(); i++) {
|
||||
var layer = layers.get(i);
|
||||
out.append(indent);
|
||||
var cursor = 0;
|
||||
for (var entry : layer) {
|
||||
var currentOffset = entry.startColumn() - 1 - trimStart - cursor;
|
||||
out.append(" ".repeat(currentOffset));
|
||||
entry.appendTo(out);
|
||||
cursor += currentOffset + entry.length();
|
||||
}
|
||||
if (i < layers.size() - 1) {
|
||||
out.append('\n');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int leadingWhitespace(CharSequence src) {
|
||||
var result = 0;
|
||||
for (var i = 0; i < src.length(); i++) {
|
||||
var c = src.charAt(i);
|
||||
if (c != ' ' && c != '\t') {
|
||||
break;
|
||||
}
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private sealed interface LayerEntry permits SourceEntry, MarkerEntry {
|
||||
|
||||
/** 1-based (because {@link SourceSection}'s is too). */
|
||||
int startLine();
|
||||
|
||||
/** 1-based (because {@link SourceSection}'s is too). */
|
||||
int startColumn();
|
||||
|
||||
int length();
|
||||
|
||||
void appendTo(AnsiStringBuilder builder);
|
||||
|
||||
default MarkerEntry toMarker() {
|
||||
return new MarkerEntry(startLine(), startColumn());
|
||||
}
|
||||
}
|
||||
|
||||
private record SourceEntry(int startLine, int startColumn, String src) implements LayerEntry {
|
||||
static SourceEntry create(
|
||||
Node truffleNode, org.pkl.parser.syntax.Node parserNode, Object value) {
|
||||
var effectiveSourceSection =
|
||||
truffleNode
|
||||
.getSourceSection()
|
||||
.getSource()
|
||||
.createSection(getCharIndex(truffleNode, parserNode), 1);
|
||||
return new SourceEntry(
|
||||
effectiveSourceSection.getStartLine(),
|
||||
effectiveSourceSection.getStartColumn(),
|
||||
vmValueRenderer.render(value));
|
||||
}
|
||||
|
||||
// create a subsection that points to the operator
|
||||
//
|
||||
// 1 + 1
|
||||
// |
|
||||
//
|
||||
// or in the case of qualified access, the very next character after qualified access
|
||||
//
|
||||
// foo.bar
|
||||
// |
|
||||
private static int getCharIndex(Node truffleNode, org.pkl.parser.syntax.Node parserNode) {
|
||||
if (truffleNode instanceof WrapperNode wrapperNode) {
|
||||
truffleNode = wrapperNode.getDelegateNode();
|
||||
}
|
||||
var originalSource = truffleNode.getSourceSection();
|
||||
if (!originalSource.isAvailable()) {
|
||||
return originalSource.getCharIndex();
|
||||
}
|
||||
var exprText = originalSource.getCharacters().toString();
|
||||
var skip = 0;
|
||||
if (parserNode instanceof BinaryOperatorExpr binaryOperatorExpr) {
|
||||
skip =
|
||||
binaryOperatorExpr.getLeft().span().stopIndexExclusive()
|
||||
- parserNode.span().charIndex();
|
||||
} else if (parserNode instanceof TypeCheckExpr typeCheckExpr) {
|
||||
skip = typeCheckExpr.getExpr().span().stopIndexExclusive() - parserNode.span().charIndex();
|
||||
} else if (parserNode instanceof SubscriptExpr subscriptExpr) {
|
||||
skip = subscriptExpr.getExpr().span().stopIndexExclusive() - parserNode.span().charIndex();
|
||||
} else if (parserNode instanceof SuperAccessExpr
|
||||
|| parserNode instanceof SuperSubscriptExpr) {
|
||||
skip = "super".length();
|
||||
} else if (parserNode instanceof QualifiedAccessExpr qualifiedAccessExpr) {
|
||||
skip =
|
||||
qualifiedAccessExpr.getExpr().span().stopIndexExclusive()
|
||||
- parserNode.span().charIndex();
|
||||
}
|
||||
if (skip == 0) {
|
||||
return originalSource.getCharIndex();
|
||||
}
|
||||
// need to lex the source again because the parse tree doesn't carry the operator's position
|
||||
var lexer = new Lexer(exprText.substring(skip));
|
||||
var nextToken = lexer.next();
|
||||
while (!nextToken.isOperator()) {
|
||||
nextToken = lexer.next();
|
||||
}
|
||||
var span = lexer.span();
|
||||
if (nextToken == Token.DOT || nextToken == Token.QDOT) {
|
||||
return originalSource.getCharIndex() + skip + span.charIndex() + span.length();
|
||||
}
|
||||
return originalSource.getCharIndex() + skip + span.charIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return src.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(AnsiStringBuilder builder) {
|
||||
SyntaxHighlighter.writeTo(builder, src);
|
||||
}
|
||||
}
|
||||
|
||||
private record MarkerEntry(int startLine, int startColumn) implements LayerEntry {
|
||||
@Override
|
||||
public int length() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(AnsiStringBuilder builder) {
|
||||
builder.append(AnsiCode.FAINT, "│");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkerEntry toMarker() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.pkl.core.runtime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
@@ -31,15 +32,20 @@ public final class StackTraceRenderer {
|
||||
this.frameTransformer = frameTransformer;
|
||||
}
|
||||
|
||||
public void render(List<StackFrame> frames, @Nullable String hint, AnsiStringBuilder out) {
|
||||
public void render(
|
||||
List<StackFrame> frames,
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
AnsiStringBuilder out) {
|
||||
var compressed = compressFrames(frames);
|
||||
doRender(compressed, hint, out, "", true);
|
||||
doRender(compressed, hint, hintBuilder, out, "", true);
|
||||
}
|
||||
|
||||
// non-private for testing
|
||||
void doRender(
|
||||
List<Object /*StackFrame|StackFrameLoop*/> frames,
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
AnsiStringBuilder out,
|
||||
String leftMargin,
|
||||
boolean isFirstElement) {
|
||||
@@ -47,7 +53,7 @@ public final class StackTraceRenderer {
|
||||
if (frame instanceof StackFrameLoop loop) {
|
||||
// ensure a cycle of length 1 doesn't get rendered as a loop
|
||||
if (loop.count == 1) {
|
||||
doRender(loop.frames, null, out, leftMargin, isFirstElement);
|
||||
doRender(loop.frames, null, null, out, leftMargin, isFirstElement);
|
||||
} else {
|
||||
if (!isFirstElement) {
|
||||
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n');
|
||||
@@ -57,9 +63,9 @@ public final class StackTraceRenderer {
|
||||
.append(AnsiTheme.STACK_TRACE_LOOP_COUNT, loop.count)
|
||||
.append(" repetitions of:\n");
|
||||
var newLeftMargin = leftMargin + "│ ";
|
||||
doRender(loop.frames, null, out, newLeftMargin, isFirstElement);
|
||||
doRender(loop.frames, null, null, out, newLeftMargin, isFirstElement);
|
||||
if (isFirstElement) {
|
||||
renderHint(hint, out, newLeftMargin);
|
||||
renderHint(hint, hintBuilder, out, newLeftMargin);
|
||||
isFirstElement = false;
|
||||
}
|
||||
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin + "└─").append('\n');
|
||||
@@ -72,7 +78,7 @@ public final class StackTraceRenderer {
|
||||
}
|
||||
|
||||
if (isFirstElement) {
|
||||
renderHint(hint, out, leftMargin);
|
||||
renderHint(hint, hintBuilder, out, leftMargin);
|
||||
isFirstElement = false;
|
||||
}
|
||||
}
|
||||
@@ -84,13 +90,20 @@ public final class StackTraceRenderer {
|
||||
renderSourceLocation(transformed, out, leftMargin);
|
||||
}
|
||||
|
||||
private void renderHint(@Nullable String hint, AnsiStringBuilder out, String leftMargin) {
|
||||
if (hint == null || hint.isEmpty()) return;
|
||||
private void renderHint(
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
AnsiStringBuilder out,
|
||||
String leftMargin) {
|
||||
if (hint == null && hintBuilder == null) return;
|
||||
|
||||
out.append('\n')
|
||||
.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin)
|
||||
.append(AnsiTheme.ERROR_MESSAGE_HINT, hint)
|
||||
.append('\n');
|
||||
out.append('\n').append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin);
|
||||
if (hint != null) {
|
||||
out.append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
|
||||
} else {
|
||||
out.append(AnsiTheme.ERROR_MESSAGE_HINT, () -> hintBuilder.accept(out, true));
|
||||
}
|
||||
out.append('\n');
|
||||
}
|
||||
|
||||
private void renderSourceLine(StackFrame frame, AnsiStringBuilder out, String leftMargin) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -23,6 +24,7 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.pkl.core.BufferedLogger;
|
||||
import org.pkl.core.StackFrameTransformer;
|
||||
import org.pkl.core.TestResults;
|
||||
@@ -47,6 +49,7 @@ public final class TestRunner {
|
||||
private final StackFrameTransformer stackFrameTransformer;
|
||||
private final boolean overwrite;
|
||||
private final boolean useColor;
|
||||
private final VmValueTrackerFactory valueTrackerFactory;
|
||||
|
||||
public TestRunner(
|
||||
BufferedLogger logger,
|
||||
@@ -57,6 +60,7 @@ public final class TestRunner {
|
||||
this.stackFrameTransformer = stackFrameTransformer;
|
||||
this.overwrite = overwrite;
|
||||
this.useColor = useColor;
|
||||
this.valueTrackerFactory = VmContext.get(null).getValueTrackerFactory();
|
||||
}
|
||||
|
||||
public TestResults run(VmTyped testModule) {
|
||||
@@ -108,8 +112,16 @@ public final class TestRunner {
|
||||
try {
|
||||
var factValue = VmUtils.readMember(listing, idx);
|
||||
if (factValue == Boolean.FALSE) {
|
||||
var failure = factFailure(member.getSourceSection(), getDisplayUri(member));
|
||||
resultBuilder.addFailure(failure);
|
||||
try (var valueTracker = valueTrackerFactory.create()) {
|
||||
listing.cachedValues.clear();
|
||||
VmUtils.readMember(listing, idx);
|
||||
var failure =
|
||||
factFailure(
|
||||
member.getSourceSection(),
|
||||
getDisplayUri(member),
|
||||
valueTracker.values());
|
||||
resultBuilder.addFailure(failure);
|
||||
}
|
||||
} else {
|
||||
resultBuilder.addSuccess();
|
||||
}
|
||||
@@ -395,10 +407,18 @@ public final class TestRunner {
|
||||
moduleInfo.getModuleKey().getUri(), VmContext.get(null).getFrameTransformer());
|
||||
}
|
||||
|
||||
private Failure factFailure(SourceSection sourceSection, String location) {
|
||||
private Failure factFailure(
|
||||
SourceSection sourceSection, String location, Map<Node, List<Object>> trackedValues) {
|
||||
var sb = new AnsiStringBuilder(useColor);
|
||||
sb.append(AnsiTheme.TEST_FACT_SOURCE, sourceSection.getCharacters().toString()).append(" ");
|
||||
appendLocation(sb, location);
|
||||
PowerAssertions.render(
|
||||
sb,
|
||||
"",
|
||||
sourceSection,
|
||||
trackedValues,
|
||||
(it) -> {
|
||||
it.append(" ");
|
||||
appendLocation(it, location);
|
||||
});
|
||||
return new Failure("Fact Failure", sb.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,20 +21,23 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.pkl.core.*;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public final class VmBugException extends VmException {
|
||||
public VmBugException(
|
||||
String message,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable cause,
|
||||
boolean isExternalMessage,
|
||||
Object[] messageArguments,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> messageBuilder,
|
||||
List<ProgramValue> programValues,
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
|
||||
super(
|
||||
@@ -42,11 +45,12 @@ public final class VmBugException extends VmException {
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
hintBuilder,
|
||||
insertedStackFrames);
|
||||
}
|
||||
|
||||
@@ -59,7 +63,7 @@ public final class VmBugException extends VmException {
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public PklException toPklException(StackFrameTransformer transformer, boolean color) {
|
||||
var renderer = new VmExceptionRenderer(new StackTraceRenderer(transformer), color);
|
||||
var renderer = new VmExceptionRenderer(new StackTraceRenderer(transformer), color, true);
|
||||
var rendered = renderer.render(this);
|
||||
return new PklBugException(rendered, this);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
|
||||
import com.oracle.truffle.api.TruffleLanguage.Env;
|
||||
import com.oracle.truffle.api.instrumentation.Instrumenter;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
@@ -33,6 +35,11 @@ import org.pkl.core.util.Nullable;
|
||||
public final class VmContext {
|
||||
private static final ContextReference<VmContext> REFERENCE =
|
||||
ContextReference.create(VmLanguage.class);
|
||||
private final VmValueTrackerFactory valueTrackerFactory;
|
||||
|
||||
public VmContext(VmLanguage vmLanguage, Env env) {
|
||||
this.valueTrackerFactory = new VmValueTrackerFactory(env.lookup(Instrumenter.class));
|
||||
}
|
||||
|
||||
@LateInit private Holder holder;
|
||||
|
||||
@@ -151,4 +158,8 @@ public final class VmContext {
|
||||
public TraceMode getTraceMode() {
|
||||
return holder.traceMode;
|
||||
}
|
||||
|
||||
public VmValueTrackerFactory getValueTrackerFactory() {
|
||||
return valueTrackerFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,20 +20,23 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public class VmEvalException extends VmException {
|
||||
public VmEvalException(
|
||||
String message,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable cause,
|
||||
boolean isExternalMessage,
|
||||
Object[] messageArguments,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> messageBuilder,
|
||||
List<ProgramValue> programValues,
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
|
||||
super(
|
||||
@@ -41,11 +44,12 @@ public class VmEvalException extends VmException {
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
hintBuilder,
|
||||
insertedStackFrames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,9 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.pkl.core.*;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public abstract class VmException extends AbstractTruffleException {
|
||||
@@ -31,28 +33,31 @@ public abstract class VmException extends AbstractTruffleException {
|
||||
private final List<ProgramValue> programValues;
|
||||
private final @Nullable SourceSection sourceSection;
|
||||
private final @Nullable String memberName;
|
||||
protected @Nullable String hint;
|
||||
private final Map<CallTarget, StackFrame> insertedStackFrames;
|
||||
@Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder;
|
||||
@Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder;
|
||||
|
||||
public VmException(
|
||||
String message,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable cause,
|
||||
boolean isExternalMessage,
|
||||
Object[] messageArguments,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> messageBuilder,
|
||||
List<ProgramValue> programValues,
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
super(message, cause, UNLIMITED_STACK_TRACE, location);
|
||||
this.messageBuilder = messageBuilder;
|
||||
this.isExternalMessage = isExternalMessage;
|
||||
this.messageArguments = messageArguments;
|
||||
this.programValues = programValues;
|
||||
this.sourceSection = sourceSection;
|
||||
this.memberName = memberName;
|
||||
this.hint = hint;
|
||||
this.insertedStackFrames = insertedStackFrames;
|
||||
this.hintBuilder = hintBuilder;
|
||||
}
|
||||
|
||||
public final boolean isExternalMessage() {
|
||||
@@ -75,14 +80,6 @@ public abstract class VmException extends AbstractTruffleException {
|
||||
return memberName;
|
||||
}
|
||||
|
||||
public @Nullable String getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
public void setHint(@Nullable String hint) {
|
||||
this.hint = hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack frames to insert into the stack trace presented to the user. For each entry `(target,
|
||||
* frame)`, `frame` will be inserted below the top-most frame associated with `target`.
|
||||
@@ -91,6 +88,18 @@ public abstract class VmException extends AbstractTruffleException {
|
||||
return insertedStackFrames;
|
||||
}
|
||||
|
||||
public @Nullable BiConsumer<AnsiStringBuilder, Boolean> getMessageBuilder() {
|
||||
return messageBuilder;
|
||||
}
|
||||
|
||||
public @Nullable BiConsumer<AnsiStringBuilder, Boolean> getHintBuilder() {
|
||||
return hintBuilder;
|
||||
}
|
||||
|
||||
public void setHint(String hint) {
|
||||
this.hintBuilder = ((builder, aBoolean) -> builder.append(hint));
|
||||
}
|
||||
|
||||
public enum Kind {
|
||||
EVAL_ERROR,
|
||||
UNDEFINED_VALUE,
|
||||
@@ -110,7 +119,7 @@ public abstract class VmException extends AbstractTruffleException {
|
||||
|
||||
@TruffleBoundary
|
||||
public PklException toPklException(StackFrameTransformer transformer, boolean color) {
|
||||
var renderer = new VmExceptionRenderer(new StackTraceRenderer(transformer), color);
|
||||
var renderer = new VmExceptionRenderer(new StackTraceRenderer(transformer), color, true);
|
||||
var rendered = renderer.render(this);
|
||||
return new PklException(rendered);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,11 +19,13 @@ import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.runtime.MemberLookupSuggestions.Candidate.Kind;
|
||||
import org.pkl.core.runtime.VmException.ProgramValue;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/**
|
||||
@@ -49,11 +51,13 @@ import org.pkl.core.util.Nullable;
|
||||
* preferable but isn't currently used. One problem with special formatting is that error
|
||||
* output doesn't always go to a terminal and hence may be rendered verbatim.)
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public final class VmExceptionBuilder {
|
||||
|
||||
private @Nullable Object receiver;
|
||||
private @Nullable Map<CallTarget, StackFrame> insertedStackFrames;
|
||||
private VmException wrappedException;
|
||||
private @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder;
|
||||
|
||||
public static class MultilineValue {
|
||||
private final Iterable<?> lines;
|
||||
@@ -86,6 +90,7 @@ public final class VmExceptionBuilder {
|
||||
}
|
||||
|
||||
private @Nullable String message;
|
||||
private @Nullable BiConsumer<AnsiStringBuilder, Boolean> messageBuilder;
|
||||
private @Nullable Throwable cause;
|
||||
private VmException.Kind kind = VmException.Kind.EVAL_ERROR;
|
||||
private boolean isExternalMessage;
|
||||
@@ -94,7 +99,6 @@ public final class VmExceptionBuilder {
|
||||
private @Nullable Node location;
|
||||
private @Nullable SourceSection sourceSection;
|
||||
private @Nullable String memberName;
|
||||
private @Nullable String hint;
|
||||
|
||||
public VmExceptionBuilder typeMismatch(Object value, VmClass expectedType) {
|
||||
if (value instanceof VmNull) {
|
||||
@@ -293,6 +297,12 @@ public final class VmExceptionBuilder {
|
||||
return withMessage(message, args);
|
||||
}
|
||||
|
||||
public VmExceptionBuilder withMessageBuilder(
|
||||
BiConsumer<AnsiStringBuilder, Boolean> messageBuilder) {
|
||||
this.messageBuilder = messageBuilder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VmExceptionBuilder withProgramValue(String name, Object value) {
|
||||
programValues.add(new ProgramValue(name, value));
|
||||
return this;
|
||||
@@ -329,7 +339,15 @@ public final class VmExceptionBuilder {
|
||||
}
|
||||
|
||||
public VmExceptionBuilder withHint(@Nullable String hint) {
|
||||
this.hint = hint;
|
||||
if (hint != null) {
|
||||
this.hintBuilder = (builder, ignored) -> builder.append(hint);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public VmExceptionBuilder withHintBuilder(
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder) {
|
||||
this.hintBuilder = hintBuilder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -346,9 +364,12 @@ public final class VmExceptionBuilder {
|
||||
}
|
||||
|
||||
public VmException build() {
|
||||
if (message == null) {
|
||||
if (message == null && messageBuilder == null) {
|
||||
throw new IllegalStateException("No message set.");
|
||||
}
|
||||
if (message != null && messageBuilder != null) {
|
||||
throw new IllegalStateException("Both message and messageBuilder are set");
|
||||
}
|
||||
|
||||
var effectiveInsertedStackFrames =
|
||||
insertedStackFrames == null ? new HashMap<CallTarget, StackFrame>() : insertedStackFrames;
|
||||
@@ -359,11 +380,12 @@ public final class VmExceptionBuilder {
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
hintBuilder,
|
||||
effectiveInsertedStackFrames);
|
||||
case UNDEFINED_VALUE ->
|
||||
new VmUndefinedValueException(
|
||||
@@ -371,11 +393,12 @@ public final class VmExceptionBuilder {
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
hintBuilder,
|
||||
receiver,
|
||||
effectiveInsertedStackFrames);
|
||||
case BUG ->
|
||||
@@ -384,11 +407,12 @@ public final class VmExceptionBuilder {
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
hintBuilder,
|
||||
effectiveInsertedStackFrames);
|
||||
case WRAPPED ->
|
||||
new VmWrappedEvalException(
|
||||
@@ -396,11 +420,12 @@ public final class VmExceptionBuilder {
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
hintBuilder,
|
||||
effectiveInsertedStackFrames,
|
||||
wrappedException);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,14 +28,17 @@ import org.pkl.core.util.Nullable;
|
||||
public final class VmExceptionRenderer {
|
||||
private final @Nullable StackTraceRenderer stackTraceRenderer;
|
||||
private final boolean color;
|
||||
private final boolean powerAssertions;
|
||||
|
||||
/**
|
||||
* Constructs an error renderer with the given stack trace renderer. If stack trace renderer is
|
||||
* {@code null}, stack traces will not be included in error output.
|
||||
*/
|
||||
public VmExceptionRenderer(@Nullable StackTraceRenderer stackTraceRenderer, boolean color) {
|
||||
public VmExceptionRenderer(
|
||||
@Nullable StackTraceRenderer stackTraceRenderer, boolean color, boolean powerAssertions) {
|
||||
this.stackTraceRenderer = stackTraceRenderer;
|
||||
this.color = color;
|
||||
this.powerAssertions = powerAssertions;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
@@ -78,7 +81,8 @@ public final class VmExceptionRenderer {
|
||||
|
||||
private void renderException(VmException exception, AnsiStringBuilder out, boolean withHeader) {
|
||||
String message;
|
||||
var hint = exception.getHint();
|
||||
var hintBuilder = exception.getHintBuilder();
|
||||
@Nullable String hint = null;
|
||||
if (exception.isExternalMessage()) {
|
||||
var totalMessage =
|
||||
ErrorMessages.create(exception.getMessage(), exception.getMessageArguments());
|
||||
@@ -99,7 +103,14 @@ public final class VmExceptionRenderer {
|
||||
if (withHeader) {
|
||||
out.append(AnsiTheme.ERROR_HEADER, "–– Pkl Error ––").append('\n');
|
||||
}
|
||||
out.append(AnsiTheme.ERROR_MESSAGE, message).append('\n');
|
||||
if (exception.getMessageBuilder() != null) {
|
||||
out.append(
|
||||
AnsiTheme.ERROR_MESSAGE,
|
||||
() -> exception.getMessageBuilder().accept(out, powerAssertions));
|
||||
out.append('\n');
|
||||
} else {
|
||||
out.append(AnsiTheme.ERROR_MESSAGE, message).append('\n');
|
||||
}
|
||||
|
||||
// include cause's message unless it's the same as this exception's message
|
||||
if (exception.getCause() != null) {
|
||||
@@ -132,10 +143,13 @@ public final class VmExceptionRenderer {
|
||||
}
|
||||
|
||||
if (!frames.isEmpty()) {
|
||||
stackTraceRenderer.render(frames, hint, out.append('\n'));
|
||||
stackTraceRenderer.render(frames, hint, hintBuilder, out.append('\n'));
|
||||
} else if (hint != null) {
|
||||
// render hint if there are no stack frames
|
||||
out.append('\n').append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
|
||||
} else if (hintBuilder != null) {
|
||||
out.append('\n')
|
||||
.append(AnsiTheme.ERROR_MESSAGE_HINT, () -> hintBuilder.accept(out, powerAssertions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public final class VmFunction extends VmObjectLike {
|
||||
|
||||
private final Object thisValue;
|
||||
private final int paramCount;
|
||||
private final PklRootNode rootNode;
|
||||
@@ -97,6 +98,10 @@ public final class VmFunction extends VmObjectLike {
|
||||
return EconomicMaps.create();
|
||||
}
|
||||
|
||||
public PklRootNode getRootNode() {
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object getCachedValue(Object key) {
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.ContextThreadLocal;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.TruffleLanguage.ContextPolicy;
|
||||
import com.oracle.truffle.api.instrumentation.ProvidedTags;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.Source;
|
||||
import org.pkl.core.ast.builder.AstBuilder;
|
||||
@@ -36,6 +37,7 @@ import org.pkl.parser.syntax.Module;
|
||||
version = "0.31.0-dev",
|
||||
characterMimeTypes = VmLanguage.MIME_TYPE,
|
||||
contextPolicy = ContextPolicy.SHARED)
|
||||
@ProvidedTags(PklTags.Expression.class)
|
||||
public final class VmLanguage extends TruffleLanguage<VmContext> {
|
||||
public static final String MIME_TYPE = "application/x-pkl";
|
||||
|
||||
@@ -51,7 +53,7 @@ public final class VmLanguage extends TruffleLanguage<VmContext> {
|
||||
|
||||
@Override
|
||||
protected VmContext createContext(Env env) {
|
||||
return new VmContext();
|
||||
return new VmContext(this, env);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ public final class VmStackOverflowException extends VmException {
|
||||
e,
|
||||
true,
|
||||
new Object[0],
|
||||
null,
|
||||
List.of(),
|
||||
null,
|
||||
null,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,10 +18,13 @@ package org.pkl.core.runtime;
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.Nullable;
|
||||
import org.pkl.parser.Lexer;
|
||||
|
||||
@@ -29,38 +32,40 @@ public final class VmUndefinedValueException extends VmEvalException {
|
||||
private final @Nullable Object receiver;
|
||||
|
||||
public VmUndefinedValueException(
|
||||
String message,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable cause,
|
||||
boolean isExternalMessage,
|
||||
Object[] messageArguments,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> messageBuilder,
|
||||
List<ProgramValue> programValues,
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
@Nullable Object receiver,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
@Nullable Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
|
||||
super(
|
||||
message,
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
insertedStackFrames);
|
||||
hintBuilder,
|
||||
insertedStackFrames == null ? Collections.emptyMap() : insertedStackFrames);
|
||||
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
public VmUndefinedValueException fillInHint(Deque<Object> path, Object topLevelValue) {
|
||||
if (hint != null) return this;
|
||||
if (hintBuilder != null) return this;
|
||||
var builder = new StringBuilder();
|
||||
var memberKey = getMessageArguments()[0];
|
||||
path.push(memberKey);
|
||||
var builder = new StringBuilder();
|
||||
builder.append("The above error occurred when rendering path `");
|
||||
renderPath(builder, path);
|
||||
builder.append('`');
|
||||
@@ -72,7 +77,7 @@ public final class VmUndefinedValueException extends VmEvalException {
|
||||
.append('`');
|
||||
}
|
||||
builder.append('.');
|
||||
hint = builder.toString();
|
||||
this.hintBuilder = (b, ignored) -> b.append(builder.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||
import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
|
||||
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
|
||||
import com.oracle.truffle.api.instrumentation.Instrumenter;
|
||||
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public final class VmValueTracker implements AutoCloseable {
|
||||
|
||||
private final EventBinding<ExecutionEventNodeFactory> binding;
|
||||
|
||||
private final Map<Node, List<Object>> values = new IdentityHashMap<>();
|
||||
|
||||
public VmValueTracker(Instrumenter instrumenter) {
|
||||
binding =
|
||||
instrumenter.attachExecutionEventFactory(
|
||||
SourceSectionFilter.newBuilder().tagIs(PklTags.Expression.class).build(),
|
||||
context ->
|
||||
new ExecutionEventNode() {
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
protected void onReturnValue(VirtualFrame frame, @Nullable Object result) {
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
var node = context.getInstrumentedNode();
|
||||
var myValues = values.getOrDefault(node, new ArrayList<>());
|
||||
myValues.add(result);
|
||||
values.put(node, myValues);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Map<Node, List<Object>> values() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
binding.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.instrumentation.Instrumenter;
|
||||
|
||||
public final class VmValueTrackerFactory {
|
||||
|
||||
private final Instrumenter instrumenter;
|
||||
|
||||
public VmValueTrackerFactory(Instrumenter instrumenter) {
|
||||
this.instrumenter = instrumenter;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
public VmValueTracker create() {
|
||||
return new VmValueTracker(instrumenter);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,9 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public class VmWrappedEvalException extends VmEvalException {
|
||||
@@ -28,15 +30,16 @@ public class VmWrappedEvalException extends VmEvalException {
|
||||
private final VmException wrappedException;
|
||||
|
||||
public VmWrappedEvalException(
|
||||
String message,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable cause,
|
||||
boolean isExternalMessage,
|
||||
Object[] messageArguments,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> messageBuilder,
|
||||
List<ProgramValue> programValues,
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint,
|
||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames,
|
||||
VmException wrappedException) {
|
||||
super(
|
||||
@@ -44,11 +47,12 @@ public class VmWrappedEvalException extends VmEvalException {
|
||||
cause,
|
||||
isExternalMessage,
|
||||
messageArguments,
|
||||
messageBuilder,
|
||||
programValues,
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
hintBuilder,
|
||||
insertedStackFrames);
|
||||
this.wrappedException = wrappedException;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,4 +21,9 @@ public abstract class ExternalPropertyNode extends ExternalMemberNode {
|
||||
public interface Factory {
|
||||
ExternalPropertyNode create(ExpressionNode receiverNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstrumentable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.pkl.core.stdlib.base;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.ast.expression.binary.LessThanNode;
|
||||
import org.pkl.core.ast.expression.binary.LessThanNodeGen;
|
||||
@@ -32,7 +33,8 @@ public final class CollectionNodes {
|
||||
private CollectionNodes() {}
|
||||
|
||||
public abstract static class SortComparatorNode extends PklNode {
|
||||
public abstract boolean executeWith(Object left, Object right, @Nullable VmFunction function);
|
||||
public abstract boolean executeWith(
|
||||
VirtualFrame frame, Object left, Object right, @Nullable VmFunction function);
|
||||
}
|
||||
|
||||
public static final class CompareNode extends SortComparatorNode {
|
||||
@@ -42,9 +44,10 @@ public final class CollectionNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Override
|
||||
public boolean executeWith(Object left, Object right, @Nullable VmFunction function) {
|
||||
public boolean executeWith(
|
||||
VirtualFrame frame, Object left, Object right, @Nullable VmFunction function) {
|
||||
assert function == null;
|
||||
return lessThanNode.executeWith(left, right);
|
||||
return lessThanNode.executeWith(frame, left, right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +60,12 @@ public final class CollectionNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Override
|
||||
public boolean executeWith(Object left, Object right, @Nullable VmFunction selector) {
|
||||
public boolean executeWith(
|
||||
VirtualFrame frame, Object left, Object right, @Nullable VmFunction selector) {
|
||||
assert selector != null;
|
||||
var leftResult = applyLambdaNode.execute(selector, left);
|
||||
var rightResult = applyLambdaNode.execute(selector, right);
|
||||
return lessThanNode.executeWith(leftResult, rightResult);
|
||||
return lessThanNode.executeWith(frame, leftResult, rightResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +73,8 @@ public final class CollectionNodes {
|
||||
@Child private ApplyVmFunction2Node applyLambdaNode = ApplyVmFunction2NodeGen.create();
|
||||
|
||||
@Override
|
||||
public boolean executeWith(Object left, Object right, @Nullable VmFunction comparator) {
|
||||
public boolean executeWith(
|
||||
VirtualFrame frame, Object left, Object right, @Nullable VmFunction comparator) {
|
||||
assert comparator != null;
|
||||
var result = applyLambdaNode.execute(comparator, left, right);
|
||||
|
||||
|
||||
@@ -856,7 +856,7 @@ public final class ListNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self) {
|
||||
protected Object eval(VirtualFrame frame, VmList self) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -864,7 +864,7 @@ public final class ListNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (lessThanNode.executeWith(elem, result)) {
|
||||
if (lessThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -881,7 +881,7 @@ public final class ListNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self) {
|
||||
protected Object eval(VirtualFrame frame, VmList self) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -889,7 +889,7 @@ public final class ListNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (lessThanNode.executeWith(elem, result)) {
|
||||
if (lessThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -906,7 +906,7 @@ public final class ListNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self) {
|
||||
protected Object eval(VirtualFrame frame, VmList self) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -914,7 +914,7 @@ public final class ListNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (greaterThanNode.executeWith(elem, result)) {
|
||||
if (greaterThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -931,7 +931,7 @@ public final class ListNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self) {
|
||||
protected Object eval(VirtualFrame frame, VmList self) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -939,7 +939,7 @@ public final class ListNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (greaterThanNode.executeWith(elem, result)) {
|
||||
if (greaterThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -958,7 +958,7 @@ public final class ListNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmList self, VmFunction function) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -968,7 +968,7 @@ public final class ListNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (lessThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (lessThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -988,7 +988,7 @@ public final class ListNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmList self, VmFunction function) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -998,7 +998,7 @@ public final class ListNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (greaterThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (greaterThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -1018,7 +1018,7 @@ public final class ListNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmList self, VmFunction function) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -1028,7 +1028,7 @@ public final class ListNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (lessThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (lessThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -1048,7 +1048,7 @@ public final class ListNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmList self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmList self, VmFunction function) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -1058,7 +1058,7 @@ public final class ListNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (greaterThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (greaterThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -1161,8 +1161,8 @@ public final class ListNodes {
|
||||
@Child private CompareNode compareNode = new CompareNode();
|
||||
|
||||
@Specialization
|
||||
protected VmList eval(VmList self) {
|
||||
return VmList.create(MergeSort.sort(self.toArray(), compareNode, null));
|
||||
protected VmList eval(VirtualFrame frame, VmList self) {
|
||||
return VmList.create(MergeSort.sort(frame, self.toArray(), compareNode, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,8 +1170,8 @@ public final class ListNodes {
|
||||
@Child private CompareByNode compareByNode = new CompareByNode();
|
||||
|
||||
@Specialization
|
||||
protected VmList eval(VmList self, VmFunction selector) {
|
||||
return VmList.create(MergeSort.sort(self.toArray(), compareByNode, selector));
|
||||
protected VmList eval(VirtualFrame frame, VmList self, VmFunction selector) {
|
||||
return VmList.create(MergeSort.sort(frame, self.toArray(), compareByNode, selector));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1179,8 +1179,8 @@ public final class ListNodes {
|
||||
@Child private CompareWithNode compareWithNode = new CompareWithNode();
|
||||
|
||||
@Specialization
|
||||
protected VmList eval(VmList self, VmFunction function) {
|
||||
return VmList.create(MergeSort.sort(self.toArray(), compareWithNode, function));
|
||||
protected VmList eval(VirtualFrame frame, VmList self, VmFunction function) {
|
||||
return VmList.create(MergeSort.sort(frame, self.toArray(), compareWithNode, function));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.pkl.core.stdlib.base;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
import org.pkl.core.stdlib.base.CollectionNodes.SortComparatorNode;
|
||||
import org.pkl.core.util.Nullable;
|
||||
@@ -26,7 +27,10 @@ final class MergeSort {
|
||||
private MergeSort() {}
|
||||
|
||||
public static Object[] sort(
|
||||
Object[] array, SortComparatorNode comparator, @Nullable VmFunction function) {
|
||||
VirtualFrame frame,
|
||||
Object[] array,
|
||||
SortComparatorNode comparator,
|
||||
@Nullable VmFunction function) {
|
||||
|
||||
var length = array.length;
|
||||
var temp = new Object[length];
|
||||
@@ -35,14 +39,14 @@ final class MergeSort {
|
||||
|
||||
for (var start = 0; start < length; start += INITIAL_MERGE_SORT_STRIDE) {
|
||||
var end = Math.min(start + INITIAL_MERGE_SORT_STRIDE, length);
|
||||
insertionSort(array, start, end, comparator, function);
|
||||
insertionSort(frame, array, start, end, comparator, function);
|
||||
}
|
||||
|
||||
for (var stride = INITIAL_MERGE_SORT_STRIDE; stride < length; stride *= 2) {
|
||||
for (var start = 0; start < length - stride; start += stride + stride) {
|
||||
var end = Math.min(start + stride + stride, length);
|
||||
var mid = start + stride; // start of second half
|
||||
merge(array, temp, start, mid, end, comparator, function);
|
||||
merge(frame, array, temp, start, mid, end, comparator, function);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +54,7 @@ final class MergeSort {
|
||||
}
|
||||
|
||||
private static void merge(
|
||||
VirtualFrame frame,
|
||||
Object[] array,
|
||||
Object[] temp,
|
||||
int start,
|
||||
@@ -58,7 +63,7 @@ final class MergeSort {
|
||||
SortComparatorNode comparator,
|
||||
@Nullable VmFunction function) {
|
||||
|
||||
if (comparator.executeWith(array[mid - 1], array[mid], function)) {
|
||||
if (comparator.executeWith(frame, array[mid - 1], array[mid], function)) {
|
||||
return; // already sorted
|
||||
}
|
||||
|
||||
@@ -70,12 +75,13 @@ final class MergeSort {
|
||||
for (var k = start; k < end; k++) {
|
||||
if (i >= mid) array[k] = temp[j++];
|
||||
else if (j >= end) array[k] = temp[i++];
|
||||
else if (comparator.executeWith(temp[j], temp[i], function)) array[k] = temp[j++];
|
||||
else if (comparator.executeWith(frame, temp[j], temp[i], function)) array[k] = temp[j++];
|
||||
else array[k] = temp[i++];
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertionSort(
|
||||
VirtualFrame frame,
|
||||
Object[] array,
|
||||
int start,
|
||||
int end,
|
||||
@@ -83,7 +89,9 @@ final class MergeSort {
|
||||
@Nullable VmFunction function) {
|
||||
|
||||
for (var i = start; i < end; i++) {
|
||||
for (var j = i; j > start && comparator.executeWith(array[j], array[j - 1], function); j--) {
|
||||
for (var j = i;
|
||||
j > start && comparator.executeWith(frame, array[j], array[j - 1], function);
|
||||
j--) {
|
||||
Object swap = array[j];
|
||||
array[j] = array[j - 1];
|
||||
array[j - 1] = swap;
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.pkl.core.stdlib.base;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.LoopNode;
|
||||
import org.pkl.core.ast.expression.binary.GreaterThanNode;
|
||||
import org.pkl.core.ast.expression.binary.GreaterThanNodeGen;
|
||||
@@ -632,7 +633,7 @@ public final class SetNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -640,7 +641,7 @@ public final class SetNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (lessThanNode.executeWith(elem, result)) {
|
||||
if (lessThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -657,7 +658,7 @@ public final class SetNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -665,7 +666,7 @@ public final class SetNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (lessThanNode.executeWith(elem, result)) {
|
||||
if (lessThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -684,7 +685,7 @@ public final class SetNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self, VmFunction function) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -694,7 +695,7 @@ public final class SetNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (lessThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (lessThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -714,7 +715,7 @@ public final class SetNodes {
|
||||
LessThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self, VmFunction function) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -724,7 +725,7 @@ public final class SetNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (lessThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (lessThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -800,7 +801,7 @@ public final class SetNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -808,7 +809,7 @@ public final class SetNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (greaterThanNode.executeWith(elem, result)) {
|
||||
if (greaterThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -825,7 +826,7 @@ public final class SetNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -833,7 +834,7 @@ public final class SetNodes {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
if (greaterThanNode.executeWith(elem, result)) {
|
||||
if (greaterThanNode.executeWith(frame, elem, result)) {
|
||||
result = elem;
|
||||
}
|
||||
}
|
||||
@@ -852,7 +853,7 @@ public final class SetNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self, VmFunction function) {
|
||||
self.checkNonEmpty();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -862,7 +863,7 @@ public final class SetNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (greaterThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (greaterThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -882,7 +883,7 @@ public final class SetNodes {
|
||||
GreaterThanNodeGen.create(VmUtils.unavailableSourceSection(), null, null);
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VmSet self, VmFunction function) {
|
||||
protected Object eval(VirtualFrame frame, VmSet self, VmFunction function) {
|
||||
if (self.isEmpty()) return VmNull.withoutDefault();
|
||||
|
||||
var iterator = self.iterator();
|
||||
@@ -892,7 +893,7 @@ public final class SetNodes {
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var elemValue = applyLambdaNode.execute(function, elem);
|
||||
if (greaterThanNode.executeWith(elemValue, resultValue)) {
|
||||
if (greaterThanNode.executeWith(frame, elemValue, resultValue)) {
|
||||
result = elem;
|
||||
resultValue = elemValue;
|
||||
}
|
||||
@@ -965,8 +966,8 @@ public final class SetNodes {
|
||||
@Child private CompareNode compareNode = new CompareNode();
|
||||
|
||||
@Specialization
|
||||
protected VmList eval(VmSet self) {
|
||||
return VmList.create(MergeSort.sort(self.toArray(), compareNode, null));
|
||||
protected VmList eval(VirtualFrame frame, VmSet self) {
|
||||
return VmList.create(MergeSort.sort(frame, self.toArray(), compareNode, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,8 +975,8 @@ public final class SetNodes {
|
||||
@Child private CompareByNode compareByNode = new CompareByNode();
|
||||
|
||||
@Specialization
|
||||
protected VmList eval(VmSet self, VmFunction selector) {
|
||||
return VmList.create(MergeSort.sort(self.toArray(), compareByNode, selector));
|
||||
protected VmList eval(VirtualFrame frame, VmSet self, VmFunction selector) {
|
||||
return VmList.create(MergeSort.sort(frame, self.toArray(), compareByNode, selector));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,8 +984,8 @@ public final class SetNodes {
|
||||
@Child private CompareWithNode compareWithNode = new CompareWithNode();
|
||||
|
||||
@Specialization
|
||||
protected VmList eval(VmSet self, VmFunction function) {
|
||||
return VmList.create(MergeSort.sort(self.toArray(), compareWithNode, function));
|
||||
protected VmList eval(VirtualFrame frame, VmSet self, VmFunction function) {
|
||||
return VmList.create(MergeSort.sort(frame, self.toArray(), compareWithNode, function));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,8 +27,8 @@ import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||
import org.pkl.core.stdlib.PklName;
|
||||
|
||||
public final class TestNodes {
|
||||
private static final VmExceptionRenderer noStackTraceExceptionRenderer =
|
||||
new VmExceptionRenderer(null, false);
|
||||
private static final VmExceptionRenderer testNodeRenderer =
|
||||
new VmExceptionRenderer(null, false, false);
|
||||
|
||||
private TestNodes() {}
|
||||
|
||||
@@ -64,7 +64,7 @@ public final class TestNodes {
|
||||
|
||||
@TruffleBoundary
|
||||
private static String render(VmException e) {
|
||||
return noStackTraceExceptionRenderer
|
||||
return testNodeRenderer
|
||||
.render(e)
|
||||
.lines()
|
||||
.skip(1) // remove meaningless header line
|
||||
|
||||
@@ -108,6 +108,19 @@ public final class AnsiStringBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnsiStringBuilder append(EnumSet<AnsiCode> codes, Runnable runnable) {
|
||||
if (!usingColor) {
|
||||
runnable.run();
|
||||
return this;
|
||||
}
|
||||
var prevDeclaredCodes = declaredCodes;
|
||||
declaredCodes = EnumSet.copyOf(codes);
|
||||
declaredCodes.addAll(prevDeclaredCodes);
|
||||
runnable.run();
|
||||
declaredCodes = prevDeclaredCodes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Provides a runnable where anything appended is not affected by the existing context. */
|
||||
public AnsiStringBuilder appendSandboxed(Runnable runnable) {
|
||||
if (!usingColor) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.pkl.core.util;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import org.pkl.core.util.AnsiStringBuilder.AnsiCode;
|
||||
|
||||
public final class AnsiTheme {
|
||||
@@ -24,7 +23,7 @@ public final class AnsiTheme {
|
||||
|
||||
public static final AnsiCode ERROR_MESSAGE_HINT = AnsiCode.YELLOW;
|
||||
public static final AnsiCode ERROR_HEADER = AnsiCode.RED;
|
||||
public static final Set<AnsiCode> ERROR_MESSAGE = EnumSet.of(AnsiCode.RED, AnsiCode.BOLD);
|
||||
public static final EnumSet<AnsiCode> ERROR_MESSAGE = EnumSet.of(AnsiCode.RED, AnsiCode.BOLD);
|
||||
|
||||
public static final AnsiCode STACK_FRAME = AnsiCode.FAINT;
|
||||
public static final AnsiCode STACK_TRACE_MARGIN = AnsiCode.YELLOW;
|
||||
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints1.pkl
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints1.pkl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
foo: String(this == "foo") = "bar"
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints10.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints10.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
local list = List(1, 2, 3)
|
||||
|
||||
foo: Dynamic(this == new Dynamic { ...list })
|
||||
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints11.pkl
vendored
Normal file
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints11.pkl
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
local list = List(1)
|
||||
|
||||
foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
|
||||
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints12.pkl
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints12.pkl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
foo: String(true, this.length > 5) = "bob"
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints13.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints13.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
name: String = "Bub"
|
||||
|
||||
foo: String(this == "Hello, \(name)") = "Hello, Patty"
|
||||
9
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints14.pkl
vendored
Normal file
9
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints14.pkl
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
open class Foo {
|
||||
name: String = "Foo"
|
||||
}
|
||||
|
||||
class Bar extends Foo {
|
||||
bub: String(startsWith(super.name)) = "Bubby"
|
||||
}
|
||||
|
||||
bar: Bar
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints15.pkl
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints15.pkl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
foo: String(this == "foo") | String(this == "qux") = "bar"
|
||||
10
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints16.pkl
vendored
Normal file
10
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints16.pkl
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
class Bird {
|
||||
name: String?
|
||||
}
|
||||
|
||||
// this will create `NullPropagatingOperationNode` and `InvokeVirtualMethodNode` for
|
||||
// `name?.contains("Bob")`.
|
||||
// this tests that we don't have two duplicate values in the diagram.
|
||||
p: Bird(name?.contains("Bob") ?? false) = new {
|
||||
name = "Pigeon"
|
||||
}
|
||||
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints17.pkl
vendored
Normal file
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints17.pkl
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
local name = "Bob"
|
||||
local function greet(name: String) = "Hello, \(name)"
|
||||
|
||||
foo: Any(greet(name) == "Hello, \(name)!") = null
|
||||
7
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints2.pkl
vendored
Normal file
7
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints2.pkl
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
foo: String(startsWith(bub.name)) = "Huzzah"
|
||||
|
||||
bub: Person = new { name = "Bub" }
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
}
|
||||
10
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints3.pkl
vendored
Normal file
10
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints3.pkl
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
foo: Int(this
|
||||
+ two
|
||||
/ three
|
||||
== four) = 4
|
||||
|
||||
local two = 2
|
||||
|
||||
local three = 3
|
||||
|
||||
local four = 4
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints4.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints4.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
foo: List(fold(0, (a, b) -> a + b) == 5) = myList
|
||||
|
||||
myList = List(1, 2, 3)
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints5.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints5.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
foo: String(isCapitalized) = "hello"
|
||||
|
||||
local isCapitalized = (it: String) -> it == it.capitalize()
|
||||
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints6.pkl
vendored
Normal file
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints6.pkl
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
foo: String(isCapitalized) = "hello"
|
||||
|
||||
local isCapitalized = (it: String) ->
|
||||
let (first = it.take(1).toUpperCase())
|
||||
it == "\(first)\(it.drop(1))"
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints7.pkl
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints7.pkl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
foo: UInt8 = -5
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints8.pkl
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints8.pkl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
foo: NonNull = null
|
||||
10
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints9.pkl
vendored
Normal file
10
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints9.pkl
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// stdlib constructors are treated as literals
|
||||
foo: Int(
|
||||
IntSeq(1, 5).toList().contains(this)
|
||||
|| Map(1, 2, 3, 4).values.contains(this)
|
||||
|| List(1, 2, 3, 4).contains(this)
|
||||
|| Set(1, 2, 3, 4).contains(this)
|
||||
|| Pair(1, 2).second == this
|
||||
|| Bytes(1, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("1234").toString().length == this
|
||||
) = 10
|
||||
12
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints9a.pkl
vendored
Normal file
12
pkl-core/src/test/files/LanguageSnippetTests/input/errors/power/typeConstraints9a.pkl
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
local num = 1
|
||||
|
||||
// stdlib constructors that have a non-literal are not treated as literals
|
||||
foo: Int(
|
||||
IntSeq(num, 5).toList().contains(this)
|
||||
|| Map(num, 2, 3, 4).values.contains(this)
|
||||
|| List(num, 2, 3, 4).contains(this)
|
||||
|| Set(num, 2, 3, 4).contains(this)
|
||||
|| Pair(num, 2).second == this
|
||||
|| Bytes(num, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("\(num)234").toString().length == this
|
||||
) = 10
|
||||
@@ -1,5 +1,10 @@
|
||||
amends "pkl:Project"
|
||||
|
||||
dependencies {
|
||||
["badLocalProject"] = import("../badLocalProject/PklProject")
|
||||
["badLocalProject"] = import("PklProject")
|
||||
}
|
||||
|
||||
// hack: override the project file URI so that our tests render the same error message
|
||||
// across different machines (it will truncate at 100 chars, and otherwise would be influenced by
|
||||
// where the pkl project is placed within the file tree).
|
||||
projectFileUri = "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
Type constraint `this >= min` violated.
|
||||
Value: 3
|
||||
|
||||
this >= min
|
||||
│ │ │
|
||||
3 │ 4
|
||||
false
|
||||
|
||||
x | max: Int(this >= min)
|
||||
^^^^^^^^^^^
|
||||
at constraints5#Gauge.max (file:///$snippetsDir/input/classes/constraints5.pkl)
|
||||
|
||||
@@ -10,6 +10,10 @@ at unionTypesErrorDifferent2#X.a (file:///$snippetsDir/input/classes/unionTypesE
|
||||
Type constraint `length > 3` violated.
|
||||
Value: List(1, 2, 3)
|
||||
|
||||
length > 3
|
||||
│ │
|
||||
3 false
|
||||
|
||||
x | a = List(1, 2, 3)
|
||||
^^^^^^^^^^^^^
|
||||
at unionTypesErrorDifferent2#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorDifferent2.pkl)
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
Type constraint `!isEmpty` violated.
|
||||
Value: List()
|
||||
|
||||
!isEmpty
|
||||
││
|
||||
│true
|
||||
false
|
||||
|
||||
x | names: List<String>(!isEmpty)
|
||||
^^^^^^^^
|
||||
at wrongType6#Person.names (file:///$snippetsDir/input/classes/wrongType6.pkl)
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
Type constraint `firstOneIsSandy` violated.
|
||||
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
(it: Listing<Bird>) -> it[0].name == "Sandy"
|
||||
│ │ │ │
|
||||
│ │ │ false
|
||||
│ │ "Bob"
|
||||
│ new Bird { name = "Bob" }
|
||||
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
x | birds: Listing(firstOneIsSandy) = new {
|
||||
^^^^^^^^^^^^^^^
|
||||
at constraintDetails1#birds (file:///$snippetsDir/input/errors/constraintDetails1.pkl)
|
||||
|
||||
@@ -3,6 +3,17 @@ Type constraint `let (myself: Listing<Bird> = this)
|
||||
myself[0].name == "Sandy"` violated.
|
||||
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
let (myself: Listing<Bird> = this)
|
||||
│
|
||||
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
myself[0].name == "Sandy"
|
||||
│ │ │ │
|
||||
│ │ │ false
|
||||
│ │ "Bob"
|
||||
│ new Bird { name = "Bob" }
|
||||
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
x | let (myself: Listing<Bird> = this)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at constraintDetails2#birds (file:///$snippetsDir/input/errors/constraintDetails2.pkl)
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
Type constraint `toList().every((it: Listing<Bird>) -> it[0].name == "Bob")` violated.
|
||||
Value: new Listing { new Listing { new Bird { name = "Eagle" }; new Bird { name = ? ...
|
||||
|
||||
toList().every((it: Listing<Bird>) -> it[0].name == "Bob")
|
||||
│ │
|
||||
│ false
|
||||
List(new Listing { new Bird { name = "Eagle" }; new Bird { name = ? } })
|
||||
|
||||
x | foo: Listing(toList().every((it: Listing<Bird>) -> it[0].name == "Bob")) = new {
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at constraintDetails3#foo (file:///$snippetsDir/input/errors/constraintDetails3.pkl)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 65535)` violated.
|
||||
Value: -1
|
||||
|
||||
isBetween(0, 65535)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt16 = Int(isBetween(0, 65535))
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
at intrinsifiedTypeAlias2#res1 (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `!(this is Null)` violated.
|
||||
Value: null
|
||||
|
||||
!(this is Null)
|
||||
│
|
||||
false
|
||||
|
||||
xx | typealias NonNull = Any(!(this is Null))
|
||||
^^^^^^^^^^^^^^^
|
||||
at intrinsifiedTypeAlias3#res1 (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 65535
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at invalidBytes1#foo (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 4095
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at invalidBytes2#res (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 65535
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at invalidBytes3#bytes (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 65535
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#List.toBytes (pkl:base)
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
Type constraint `!isEmpty` violated.
|
||||
Value: ""
|
||||
|
||||
!isEmpty
|
||||
││
|
||||
│true
|
||||
false
|
||||
|
||||
x | res: Listing<String(!isEmpty)> = new Listing<String> {
|
||||
^^^^^^^^
|
||||
at listingTypeCheckError3#res (file:///$snippetsDir/input/errors/listingTypeCheckError3.pkl)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `endsWith("ga")` violated.
|
||||
Value: "hola"
|
||||
|
||||
endsWith("ga")
|
||||
│
|
||||
false
|
||||
|
||||
x | res: Listing<String(!isEmpty)> = new Listing<String(endsWith("ga"))> {
|
||||
^^^^^^^^^^^^^^
|
||||
at listingTypeCheckError4#res (file:///$snippetsDir/input/errors/listingTypeCheckError4.pkl)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `length == 5` violated.
|
||||
Value: "hi"
|
||||
|
||||
length == 5
|
||||
│ │
|
||||
2 false
|
||||
|
||||
xx | res: Mapping<Listing<String(length == 5)>, String> = bar
|
||||
^^^^^^^^^^^
|
||||
at mappingTypeCheckError6#res (file:///$snippetsDir/input/errors/mappingTypeCheckError6.pkl)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `length == 5` violated.
|
||||
Value: "hi"
|
||||
|
||||
length == 5
|
||||
│ │
|
||||
2 false
|
||||
|
||||
x | res = new Mapping<Listing<String(length == 5)>, String> {
|
||||
^^^^^^^^^^^
|
||||
at mappingTypeCheckError7#res (file:///$snippetsDir/input/errors/mappingTypeCheckError7.pkl)
|
||||
|
||||
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints1.err
vendored
Normal file
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints1.err
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == "foo"` violated.
|
||||
Value: "bar"
|
||||
|
||||
this == "foo"
|
||||
│ │
|
||||
│ false
|
||||
"bar"
|
||||
|
||||
x | foo: String(this == "foo") = "bar"
|
||||
^^^^^^^^^^^^^
|
||||
at typeConstraints1#foo (file:///$snippetsDir/input/errors/power/typeConstraints1.pkl)
|
||||
|
||||
x | foo: String(this == "foo") = "bar"
|
||||
^^^^^
|
||||
at typeConstraints1#foo (file:///$snippetsDir/input/errors/power/typeConstraints1.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
26
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints10.err
vendored
Normal file
26
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints10.err
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == new Dynamic { ...list }` violated.
|
||||
Value: new Dynamic {}
|
||||
|
||||
this == new Dynamic { ...list }
|
||||
│ │ │ │
|
||||
│ │ │ List(1, 2, 3)
|
||||
│ │ new Dynamic { 1; 2; 3 }
|
||||
│ false
|
||||
new Dynamic {}
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { ...list })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints10#foo (file:///$snippetsDir/input/errors/power/typeConstraints10.pkl)
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { ...list })
|
||||
^^^
|
||||
at typeConstraints10#foo (file:///$snippetsDir/input/errors/power/typeConstraints10.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
25
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints11.err
vendored
Normal file
25
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints11.err
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == new Dynamic { for (elem in list) { elem + 1 } }` violated.
|
||||
Value: new Dynamic {}
|
||||
|
||||
this == new Dynamic { for (elem in list) { elem + 1 } }
|
||||
│ │ │ │
|
||||
│ │ new Dynamic { 2 } List(1)
|
||||
│ false
|
||||
new Dynamic {}
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints11#foo (file:///$snippetsDir/input/errors/power/typeConstraints11.pkl)
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
|
||||
^^^
|
||||
at typeConstraints11#foo (file:///$snippetsDir/input/errors/power/typeConstraints11.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints12.err
vendored
Normal file
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints12.err
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this.length > 5` violated.
|
||||
Value: "bob"
|
||||
|
||||
this.length > 5
|
||||
│ │ │
|
||||
│ 3 false
|
||||
"bob"
|
||||
|
||||
x | foo: String(true, this.length > 5) = "bob"
|
||||
^^^^^^^^^^^^^^^
|
||||
at typeConstraints12#foo (file:///$snippetsDir/input/errors/power/typeConstraints12.pkl)
|
||||
|
||||
x | foo: String(true, this.length > 5) = "bob"
|
||||
^^^^^
|
||||
at typeConstraints12#foo (file:///$snippetsDir/input/errors/power/typeConstraints12.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
26
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints13.err
vendored
Normal file
26
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints13.err
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == "Hello, \(name)"` violated.
|
||||
Value: "Hello, Patty"
|
||||
|
||||
this == "Hello, \(name)"
|
||||
│ │ │ │
|
||||
│ │ │ "Bub"
|
||||
│ │ "Hello, Bub"
|
||||
│ false
|
||||
"Hello, Patty"
|
||||
|
||||
x | foo: String(this == "Hello, \(name)") = "Hello, Patty"
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints13#foo (file:///$snippetsDir/input/errors/power/typeConstraints13.pkl)
|
||||
|
||||
x | foo: String(this == "Hello, \(name)") = "Hello, Patty"
|
||||
^^^^^^^^^^^^^^
|
||||
at typeConstraints13#foo (file:///$snippetsDir/input/errors/power/typeConstraints13.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints14.err
vendored
Normal file
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints14.err
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `startsWith(super.name)` violated.
|
||||
Value: "Bubby"
|
||||
|
||||
startsWith(super.name)
|
||||
│ │
|
||||
false "Foo"
|
||||
|
||||
x | bub: String(startsWith(super.name)) = "Bubby"
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints14#Bar.bub (file:///$snippetsDir/input/errors/power/typeConstraints14.pkl)
|
||||
|
||||
x | bub: String(startsWith(super.name)) = "Bubby"
|
||||
^^^^^^^
|
||||
at typeConstraints14#Bar.bub (file:///$snippetsDir/input/errors/power/typeConstraints14.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
37
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints15.err
vendored
Normal file
37
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints15.err
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String(this == "foo") | String(this == "qux")`, but got a different `String`.
|
||||
Value: "bar"
|
||||
|
||||
x | foo: String(this == "foo") | String(this == "qux") = "bar"
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints15#foo (file:///$snippetsDir/input/errors/power/typeConstraints15.pkl)
|
||||
|
||||
* Value is not of type `String(this == "foo")` because:
|
||||
Type constraint `this == "foo"` violated.
|
||||
Value: "bar"
|
||||
|
||||
this == "foo"
|
||||
│ │
|
||||
│ false
|
||||
"bar"
|
||||
|
||||
* Value is not of type `String(this == "qux")` because:
|
||||
Type constraint `this == "qux"` violated.
|
||||
Value: "bar"
|
||||
|
||||
this == "qux"
|
||||
│ │
|
||||
│ false
|
||||
"bar"
|
||||
|
||||
x | foo: String(this == "foo") | String(this == "qux") = "bar"
|
||||
^^^^^
|
||||
at typeConstraints15#foo (file:///$snippetsDir/input/errors/power/typeConstraints15.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints16.err
vendored
Normal file
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints16.err
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `name?.contains("Bob") ?? false` violated.
|
||||
Value: new Bird { name = "Pigeon" }
|
||||
|
||||
name?.contains("Bob") ?? false
|
||||
│ │ │
|
||||
│ false false
|
||||
"Pigeon"
|
||||
|
||||
x | p: Bird(name?.contains("Bob") ?? false) = new {
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints16#p (file:///$snippetsDir/input/errors/power/typeConstraints16.pkl)
|
||||
|
||||
x | p: Bird(name?.contains("Bob") ?? false) = new {
|
||||
^^^^^
|
||||
at typeConstraints16#p (file:///$snippetsDir/input/errors/power/typeConstraints16.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
27
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints17.err
vendored
Normal file
27
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints17.err
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `greet(name) == "Hello, \(name)!"` violated.
|
||||
Value: null
|
||||
|
||||
greet(name) == "Hello, \(name)!"
|
||||
│ │ │ │ │
|
||||
│ │ │ │ "Bob"
|
||||
│ │ │ "Hello, Bob!"
|
||||
│ │ false
|
||||
│ "Bob"
|
||||
"Hello, Bob"
|
||||
|
||||
x | foo: Any(greet(name) == "Hello, \(name)!") = null
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints17#foo (file:///$snippetsDir/input/errors/power/typeConstraints17.pkl)
|
||||
|
||||
x | foo: Any(greet(name) == "Hello, \(name)!") = null
|
||||
^^^^
|
||||
at typeConstraints17#foo (file:///$snippetsDir/input/errors/power/typeConstraints17.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints2.err
vendored
Normal file
24
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints2.err
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `startsWith(bub.name)` violated.
|
||||
Value: "Huzzah"
|
||||
|
||||
startsWith(bub.name)
|
||||
│ │ │
|
||||
false │ "Bub"
|
||||
new Person { name = "Bub" }
|
||||
|
||||
x | foo: String(startsWith(bub.name)) = "Huzzah"
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints2#foo (file:///$snippetsDir/input/errors/power/typeConstraints2.pkl)
|
||||
|
||||
x | foo: String(startsWith(bub.name)) = "Huzzah"
|
||||
^^^^^^^^
|
||||
at typeConstraints2#foo (file:///$snippetsDir/input/errors/power/typeConstraints2.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
41
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints3.err
vendored
Normal file
41
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints3.err
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this
|
||||
+ two
|
||||
/ three
|
||||
== four` violated.
|
||||
Value: 4
|
||||
|
||||
this
|
||||
│
|
||||
4
|
||||
|
||||
+ two
|
||||
│ │
|
||||
│ 2
|
||||
4.666666666666667
|
||||
|
||||
/ three
|
||||
│ │
|
||||
│ 3
|
||||
0.6666666666666666
|
||||
|
||||
== four
|
||||
│ │
|
||||
│ 4
|
||||
false
|
||||
|
||||
x | foo: Int(this
|
||||
^^^^
|
||||
at typeConstraints3#foo (file:///$snippetsDir/input/errors/power/typeConstraints3.pkl)
|
||||
|
||||
x | == four) = 4
|
||||
^
|
||||
at typeConstraints3#foo (file:///$snippetsDir/input/errors/power/typeConstraints3.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints4.err
vendored
Normal file
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints4.err
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `fold(0, (a, b) -> a + b) == 5` violated.
|
||||
Value: List(1, 2, 3)
|
||||
|
||||
fold(0, (a, b) -> a + b) == 5
|
||||
│ │
|
||||
6 false
|
||||
|
||||
x | foo: List(fold(0, (a, b) -> a + b) == 5) = myList
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints4#foo (file:///$snippetsDir/input/errors/power/typeConstraints4.pkl)
|
||||
|
||||
x | foo: List(fold(0, (a, b) -> a + b) == 5) = myList
|
||||
^^^^^^
|
||||
at typeConstraints4#foo (file:///$snippetsDir/input/errors/power/typeConstraints4.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
26
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints5.err
vendored
Normal file
26
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints5.err
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `isCapitalized` violated.
|
||||
Value: "hello"
|
||||
|
||||
(it: String) -> it == it.capitalize()
|
||||
│ │ │ │
|
||||
│ │ │ "Hello"
|
||||
│ │ "hello"
|
||||
│ false
|
||||
"hello"
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^^^^^^^
|
||||
at typeConstraints5#foo (file:///$snippetsDir/input/errors/power/typeConstraints5.pkl)
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^
|
||||
at typeConstraints5#foo (file:///$snippetsDir/input/errors/power/typeConstraints5.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
32
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints6.err
vendored
Normal file
32
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints6.err
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `isCapitalized` violated.
|
||||
Value: "hello"
|
||||
|
||||
(it: String) ->
|
||||
let (first = it.take(1).toUpperCase())
|
||||
│ │ │
|
||||
│ "h" "H"
|
||||
"hello"
|
||||
|
||||
it == "\(first)\(it.drop(1))"
|
||||
│ │ │ │ │ │
|
||||
│ │ │ "H" │ "ello"
|
||||
│ │ "Hello" "hello"
|
||||
│ false
|
||||
"hello"
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^^^^^^^
|
||||
at typeConstraints6#foo (file:///$snippetsDir/input/errors/power/typeConstraints6.pkl)
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^
|
||||
at typeConstraints6#foo (file:///$snippetsDir/input/errors/power/typeConstraints6.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints7.err
vendored
Normal file
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints7.err
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: -5
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints7#foo (pkl:base)
|
||||
|
||||
x | foo: UInt8 = -5
|
||||
^^
|
||||
at typeConstraints7#foo (file:///$snippetsDir/input/errors/power/typeConstraints7.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints8.err
vendored
Normal file
23
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints8.err
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `!(this is Null)` violated.
|
||||
Value: null
|
||||
|
||||
!(this is Null)
|
||||
│
|
||||
false
|
||||
|
||||
xx | typealias NonNull = Any(!(this is Null))
|
||||
^^^^^^^^^^^^^^^
|
||||
at typeConstraints8#foo (pkl:base)
|
||||
|
||||
x | foo: NonNull = null
|
||||
^^^^
|
||||
at typeConstraints8#foo (file:///$snippetsDir/input/errors/power/typeConstraints8.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
59
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints9.err
vendored
Normal file
59
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints9.err
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `IntSeq(1, 5).toList().contains(this)
|
||||
|| Map(1, 2, 3, 4).values.contains(this)
|
||||
|| List(1, 2, 3, 4).contains(this)
|
||||
|| Set(1, 2, 3, 4).contains(this)
|
||||
|| Pair(1, 2).second == this
|
||||
|| Bytes(1, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("1234").toString().length == this` violated.
|
||||
Value: 10
|
||||
|
||||
IntSeq(1, 5).toList().contains(this)
|
||||
│ │ │
|
||||
│ false 10
|
||||
List(1, 2, 3, 4, 5)
|
||||
|
||||
|| Map(1, 2, 3, 4).values.contains(this)
|
||||
│ │ │ │
|
||||
false │ false 10
|
||||
List(2, 4)
|
||||
|
||||
|| List(1, 2, 3, 4).contains(this)
|
||||
│ │ │
|
||||
false false 10
|
||||
|
||||
|| Set(1, 2, 3, 4).contains(this)
|
||||
│ │ │
|
||||
false false 10
|
||||
|
||||
|| Pair(1, 2).second == this
|
||||
│ │ │ │
|
||||
false 2 │ 10
|
||||
false
|
||||
|
||||
|| Bytes(1, 2, 3, 4).toList().contains(this)
|
||||
│ │ │ │
|
||||
false │ false 10
|
||||
List(1, 2, 3, 4)
|
||||
|
||||
|| Regex("1234").toString().length == this
|
||||
│ │ │ │ │
|
||||
false │ 13 │ 10
|
||||
│ false
|
||||
"Regex(\"1234\")"
|
||||
|
||||
x | IntSeq(1, 5).toList().contains(this)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints9#foo (file:///$snippetsDir/input/errors/power/typeConstraints9.pkl)
|
||||
|
||||
xx | ) = 10
|
||||
^^
|
||||
at typeConstraints9#foo (file:///$snippetsDir/input/errors/power/typeConstraints9.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
67
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints9a.err
vendored
Normal file
67
pkl-core/src/test/files/LanguageSnippetTests/output/errors/power/typeConstraints9a.err
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `IntSeq(num, 5).toList().contains(this)
|
||||
|| Map(num, 2, 3, 4).values.contains(this)
|
||||
|| List(num, 2, 3, 4).contains(this)
|
||||
|| Set(num, 2, 3, 4).contains(this)
|
||||
|| Pair(num, 2).second == this
|
||||
|| Bytes(num, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("\(num)234").toString().length == this` violated.
|
||||
Value: 10
|
||||
|
||||
IntSeq(num, 5).toList().contains(this)
|
||||
│ │ │ │ │
|
||||
│ 1 │ false 10
|
||||
IntSeq(1, 5) List(1, 2, 3, 4, 5)
|
||||
|
||||
|| Map(num, 2, 3, 4).values.contains(this)
|
||||
│ │ │ │ │ │
|
||||
│ │ 1 │ false 10
|
||||
│ Map(1, 2, 3, 4) List(2, 4)
|
||||
false
|
||||
|
||||
|| List(num, 2, 3, 4).contains(this)
|
||||
│ │ │ │ │
|
||||
│ │ 1 false 10
|
||||
│ List(1, 2, 3, 4)
|
||||
false
|
||||
|
||||
|| Set(num, 2, 3, 4).contains(this)
|
||||
│ │ │ │ │
|
||||
│ │ 1 false 10
|
||||
│ Set(1, 2, 3, 4)
|
||||
false
|
||||
|
||||
|| Pair(num, 2).second == this
|
||||
│ │ │ │ │ │
|
||||
│ │ 1 2 │ 10
|
||||
│ Pair(1, 2) false
|
||||
false
|
||||
|
||||
|| Bytes(num, 2, 3, 4).toList().contains(this)
|
||||
│ │ │ │ │ │
|
||||
│ │ 1 │ false 10
|
||||
│ Bytes(1, 2, 3, 4) List(1, 2, 3, 4)
|
||||
false
|
||||
|
||||
|| Regex("\(num)234").toString().length == this
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ 1 │ 13 │ 10
|
||||
│ │ "1234" │ false
|
||||
│ Regex("1234") "Regex(\"1234\")"
|
||||
false
|
||||
|
||||
x | IntSeq(num, 5).toList().contains(this)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints9a#foo (file:///$snippetsDir/input/errors/power/typeConstraints9a.pkl)
|
||||
|
||||
xx | ) = 10
|
||||
^^
|
||||
at typeConstraints9a#foo (file:///$snippetsDir/input/errors/power/typeConstraints9a.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
@@ -2,6 +2,11 @@
|
||||
Type constraint `!isEmpty` violated.
|
||||
Value: ""
|
||||
|
||||
!isEmpty
|
||||
││
|
||||
│true
|
||||
false
|
||||
|
||||
x | res1: String(!isEmpty) = ""
|
||||
^^^^^^^^
|
||||
at typedModuleProperties3#res1 (file:///$snippetsDir/input/modules/typedModuleProperties3.pkl)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `*RemoteDependency | Project(isValidLoadDependency)`, but got a different `pkl.Project`.
|
||||
Value: new ModuleClass { package = null; tests {}; dependencies {}; evaluatorSetting...
|
||||
Value: new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] ...
|
||||
|
||||
xxx | dependencies: Mapping<String(!contains("/")), *RemoteDependency | Project(isValidLoadDependency)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -8,8 +8,32 @@ at pkl.Project#dependencies (pkl:Project)
|
||||
|
||||
* Value is not of type `Project(isValidLoadDependency)` because:
|
||||
Type constraint `isValidLoadDependency` violated.
|
||||
Value: new ModuleClass { package = null; tests {}; dependencies {}; evaluatorSetti...
|
||||
Value: new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"...
|
||||
|
||||
x | ["badLocalProject"] = import("../badLocalProject/PklProject")
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
(it: Project) ->
|
||||
isUriLocal(projectFileUri, it.projectFileUri)
|
||||
│ │ │ │
|
||||
true │ │ "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
|
||||
"file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
|
||||
&& it.projectFileUri.endsWith("/PklProject")
|
||||
│ │ │ │
|
||||
│ │ │ true
|
||||
│ │ "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
|
||||
true
|
||||
|
||||
&& it != module
|
||||
│ │ │
|
||||
│ │ false
|
||||
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
|
||||
false
|
||||
|
||||
&& it.package != null
|
||||
│
|
||||
false
|
||||
|
||||
x | ["badLocalProject"] = import("PklProject")
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
at PklProject#dependencies["badLocalProject"] (file:///$snippetsDir/input/projects/badProjectDeps4/PklProject)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `endsWith(lastName)` violated.
|
||||
Value: "Jimmy Bird"
|
||||
|
||||
endsWith(lastName)
|
||||
│ │
|
||||
false "Birdo"
|
||||
|
||||
x | typealias Birds = Listing<String(endsWith(lastName))>
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
at typeAliasContext#res (file:///$snippetsDir/input/types/helpers/originalTypealias.pkl)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -88,10 +88,26 @@ class EvaluateTestsTest {
|
||||
assertThat(res.failures.size).isEqualTo(2)
|
||||
|
||||
val fail1 = res.failures[0]
|
||||
assertThat(fail1.message).isEqualTo("1 == 2 (repl:text)")
|
||||
assertThat(fail1.message)
|
||||
.isEqualTo(
|
||||
"""
|
||||
1 == 2 (repl:text)
|
||||
│
|
||||
false
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
val fail2 = res.failures[1]
|
||||
assertThat(fail2.message).isEqualTo(""""foo" == "bar" (repl:text)""")
|
||||
assertThat(fail2.message)
|
||||
.isEqualTo(
|
||||
"""
|
||||
"foo" == "bar" (repl:text)
|
||||
│
|
||||
false
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -192,7 +192,7 @@ class StackTraceRendererTest {
|
||||
val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1)
|
||||
val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop)
|
||||
val formatter = AnsiStringBuilder(false)
|
||||
renderer.doRender(frames, null, formatter, "", true)
|
||||
renderer.doRender(frames, null, null, formatter, "", true)
|
||||
val renderedFrames = formatter.toString()
|
||||
assertThat(renderedFrames)
|
||||
.isEqualTo(
|
||||
|
||||
Reference in New Issue
Block a user