mirror of
https://github.com/apple/pkl.git
synced 2026-07-12 16:12:42 +02:00
Fix eval -x of local members (#1760)
This fixes a regression where local members cannot be seen by the expression evaluator. Because variable are now parse-time resolved, this builds a truffle node by first constructing a synthetic module of all defined local members, and calls AstBuilder on this synthetic module first.
This commit is contained in:
@@ -50,7 +50,6 @@ import org.pkl.core.util.AnsiStringBuilder;
|
|||||||
import org.pkl.core.util.EconomicMaps;
|
import org.pkl.core.util.EconomicMaps;
|
||||||
import org.pkl.core.util.IoUtils;
|
import org.pkl.core.util.IoUtils;
|
||||||
import org.pkl.core.util.MutableReference;
|
import org.pkl.core.util.MutableReference;
|
||||||
import org.pkl.core.util.Pair;
|
|
||||||
import org.pkl.core.util.SyntaxHighlighter;
|
import org.pkl.core.util.SyntaxHighlighter;
|
||||||
import org.pkl.parser.Parser;
|
import org.pkl.parser.Parser;
|
||||||
import org.pkl.parser.ParserError;
|
import org.pkl.parser.ParserError;
|
||||||
@@ -183,51 +182,6 @@ public class ReplServer implements AutoCloseable {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a fake module that declares all the local properties that exist in ReplState, so that
|
|
||||||
* AstBuilder can correctly resolve variables. Additionally, return a {@link Node} with its span
|
|
||||||
* calculated in terms of this fake module.
|
|
||||||
*
|
|
||||||
* <p>This created module is never executed.
|
|
||||||
*/
|
|
||||||
private Pair<String, Node> buildSyntheticModuleText(Node syntaxNode, String srcText) {
|
|
||||||
if (syntaxNode instanceof ModuleDecl) {
|
|
||||||
return Pair.of(srcText, syntaxNode);
|
|
||||||
}
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
var nodeText = syntaxNode.text(srcText.toCharArray());
|
|
||||||
var adjustedNode = syntaxNode;
|
|
||||||
if (syntaxNode instanceof Expr) {
|
|
||||||
sb.append(expressionPreamble).append(nodeText).append('\n');
|
|
||||||
} else {
|
|
||||||
sb.append(nodeText).append('\n');
|
|
||||||
}
|
|
||||||
var mod = parser.parseModule(sb.toString());
|
|
||||||
if (syntaxNode instanceof Expr) {
|
|
||||||
adjustedNode = mod.getProperties().get(0).getExpr();
|
|
||||||
} else if (syntaxNode instanceof ImportClause) {
|
|
||||||
adjustedNode = mod.getImports().get(0);
|
|
||||||
} else if (syntaxNode instanceof ClassProperty) {
|
|
||||||
adjustedNode = mod.getProperties().get(0);
|
|
||||||
} else if (syntaxNode instanceof Class) {
|
|
||||||
adjustedNode = mod.getClasses().get(0);
|
|
||||||
} else if (syntaxNode instanceof org.pkl.parser.syntax.TypeAlias) {
|
|
||||||
adjustedNode = mod.getTypeAliases().get(0);
|
|
||||||
} else if (syntaxNode instanceof org.pkl.parser.syntax.ClassMethod) {
|
|
||||||
adjustedNode = mod.getMethods().get(0);
|
|
||||||
}
|
|
||||||
var cursor = EconomicMaps.getEntries(replState.module.getMembers());
|
|
||||||
while (cursor.advance()) {
|
|
||||||
var key = cursor.getKey();
|
|
||||||
var value = cursor.getValue();
|
|
||||||
if (value.isLocal()) {
|
|
||||||
sb.append("local ").append(key).append(" = Undefined()\n\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert adjustedNode != null;
|
|
||||||
return Pair.of(sb.toString(), adjustedNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("StatementWithEmptyBody")
|
@SuppressWarnings("StatementWithEmptyBody")
|
||||||
private void handleNode(
|
private void handleNode(
|
||||||
ReplState replState,
|
ReplState replState,
|
||||||
@@ -237,7 +191,7 @@ public class ReplServer implements AutoCloseable {
|
|||||||
String srcText,
|
String srcText,
|
||||||
boolean evalDefinitions,
|
boolean evalDefinitions,
|
||||||
boolean forceResults) {
|
boolean forceResults) {
|
||||||
var pair = buildSyntheticModuleText(node, srcText);
|
var pair = VmUtils.buildSyntheticModuleText(node, replState.module, srcText);
|
||||||
var syntheticModuleText = pair.first;
|
var syntheticModuleText = pair.first;
|
||||||
var adjustedNode = pair.second;
|
var adjustedNode = pair.second;
|
||||||
var module = ModuleKeys.synthetic(uri, workingDir.toUri(), uri, syntheticModuleText, false);
|
var module = ModuleKeys.synthetic(uri, workingDir.toUri(), uri, syntheticModuleText, false);
|
||||||
@@ -283,8 +237,9 @@ public class ReplServer implements AutoCloseable {
|
|||||||
new ReplResponse.InternalError(new IllegalStateException("Unexpected parse result")));
|
new ReplResponse.InternalError(new IllegalStateException("Unexpected parse result")));
|
||||||
}
|
}
|
||||||
} catch (VmException e) {
|
} catch (VmException e) {
|
||||||
|
e.setForExpressionInput(true);
|
||||||
// TODO: patch stack trace for constants
|
// TODO: patch stack trace for constants
|
||||||
results.add(new EvalError(renderException(e)));
|
results.add(new EvalError(errorRenderer.render(e)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import org.pkl.core.util.SyntaxHighlighter;
|
|||||||
public final class StackTraceRenderer {
|
public final class StackTraceRenderer {
|
||||||
private final Function<StackFrame, StackFrame> frameTransformer;
|
private final Function<StackFrame, StackFrame> frameTransformer;
|
||||||
|
|
||||||
|
private static final int PREAMBLE_LENGTH = VmUtils.EXPRESSION_PREAMBLE.length();
|
||||||
|
|
||||||
public StackTraceRenderer(Function<StackFrame, StackFrame> frameTransformer) {
|
public StackTraceRenderer(Function<StackFrame, StackFrame> frameTransformer) {
|
||||||
this.frameTransformer = frameTransformer;
|
this.frameTransformer = frameTransformer;
|
||||||
}
|
}
|
||||||
@@ -36,9 +38,10 @@ public final class StackTraceRenderer {
|
|||||||
List<StackFrame> frames,
|
List<StackFrame> frames,
|
||||||
@Nullable String hint,
|
@Nullable String hint,
|
||||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||||
AnsiStringBuilder out) {
|
AnsiStringBuilder out,
|
||||||
|
boolean forExpressionInput) {
|
||||||
var compressed = compressFrames(frames);
|
var compressed = compressFrames(frames);
|
||||||
doRender(compressed, hint, hintBuilder, out, "", true);
|
doRender(compressed, hint, hintBuilder, out, "", true, forExpressionInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-private for testing
|
// non-private for testing
|
||||||
@@ -48,12 +51,13 @@ public final class StackTraceRenderer {
|
|||||||
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
|
||||||
AnsiStringBuilder out,
|
AnsiStringBuilder out,
|
||||||
String leftMargin,
|
String leftMargin,
|
||||||
boolean isFirstElement) {
|
boolean isFirstElement,
|
||||||
|
boolean forExpressionInput) {
|
||||||
for (var frame : frames) {
|
for (var frame : frames) {
|
||||||
if (frame instanceof StackFrameLoop loop) {
|
if (frame instanceof StackFrameLoop loop) {
|
||||||
// ensure a cycle of length 1 doesn't get rendered as a loop
|
// ensure a cycle of length 1 doesn't get rendered as a loop
|
||||||
if (loop.count == 1) {
|
if (loop.count == 1) {
|
||||||
doRender(loop.frames, null, null, out, leftMargin, isFirstElement);
|
doRender(loop.frames, null, null, out, leftMargin, isFirstElement, forExpressionInput);
|
||||||
} else {
|
} else {
|
||||||
if (!isFirstElement) {
|
if (!isFirstElement) {
|
||||||
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n');
|
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n');
|
||||||
@@ -63,7 +67,7 @@ public final class StackTraceRenderer {
|
|||||||
.append(AnsiTheme.STACK_TRACE_LOOP_COUNT, loop.count)
|
.append(AnsiTheme.STACK_TRACE_LOOP_COUNT, loop.count)
|
||||||
.append(" repetitions of:\n");
|
.append(" repetitions of:\n");
|
||||||
var newLeftMargin = leftMargin + "│ ";
|
var newLeftMargin = leftMargin + "│ ";
|
||||||
doRender(loop.frames, null, null, out, newLeftMargin, isFirstElement);
|
doRender(loop.frames, null, null, out, newLeftMargin, isFirstElement, forExpressionInput);
|
||||||
if (isFirstElement) {
|
if (isFirstElement) {
|
||||||
renderHint(hint, hintBuilder, out, newLeftMargin);
|
renderHint(hint, hintBuilder, out, newLeftMargin);
|
||||||
isFirstElement = false;
|
isFirstElement = false;
|
||||||
@@ -74,7 +78,7 @@ public final class StackTraceRenderer {
|
|||||||
if (!isFirstElement) {
|
if (!isFirstElement) {
|
||||||
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n');
|
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n');
|
||||||
}
|
}
|
||||||
renderFrame((StackFrame) frame, out, leftMargin);
|
renderFrame((StackFrame) frame, out, leftMargin, forExpressionInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFirstElement) {
|
if (isFirstElement) {
|
||||||
@@ -84,9 +88,10 @@ public final class StackTraceRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderFrame(StackFrame frame, AnsiStringBuilder out, String leftMargin) {
|
private void renderFrame(
|
||||||
|
StackFrame frame, AnsiStringBuilder out, String leftMargin, boolean forExpressionInput) {
|
||||||
var transformed = frameTransformer.apply(frame);
|
var transformed = frameTransformer.apply(frame);
|
||||||
renderSourceLine(transformed, out, leftMargin);
|
renderSourceLine(transformed, out, leftMargin, forExpressionInput);
|
||||||
renderSourceLocation(transformed, out, leftMargin);
|
renderSourceLocation(transformed, out, leftMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,16 +118,27 @@ public final class StackTraceRenderer {
|
|||||||
out.append('\n');
|
out.append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderSourceLine(StackFrame frame, AnsiStringBuilder out, String leftMargin) {
|
private void renderSourceLine(
|
||||||
|
StackFrame frame, AnsiStringBuilder out, String leftMargin, boolean forExpressionInput) {
|
||||||
var originalSourceLine = frame.getSourceLines().get(0);
|
var originalSourceLine = frame.getSourceLines().get(0);
|
||||||
var leadingWhitespace = VmUtils.countLeadingWhitespace(originalSourceLine);
|
var leadingWhitespace = VmUtils.countLeadingWhitespace(originalSourceLine);
|
||||||
var sourceLine = originalSourceLine.strip();
|
var sourceLine = originalSourceLine.strip();
|
||||||
|
var hasPreamble = false;
|
||||||
|
if (forExpressionInput && sourceLine.startsWith(VmUtils.EXPRESSION_PREAMBLE)) {
|
||||||
|
sourceLine = sourceLine.substring(PREAMBLE_LENGTH);
|
||||||
|
hasPreamble = true;
|
||||||
|
}
|
||||||
var startColumn = frame.getStartColumn() - leadingWhitespace;
|
var startColumn = frame.getStartColumn() - leadingWhitespace;
|
||||||
var endColumn =
|
var endColumn =
|
||||||
frame.getStartLine() == frame.getEndLine()
|
frame.getStartLine() == frame.getEndLine()
|
||||||
? frame.getEndColumn() - leadingWhitespace
|
? frame.getEndColumn() - leadingWhitespace
|
||||||
: sourceLine.length();
|
: sourceLine.length();
|
||||||
|
|
||||||
|
if (hasPreamble) {
|
||||||
|
startColumn -= PREAMBLE_LENGTH;
|
||||||
|
endColumn -= PREAMBLE_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
var prefix = frame.getStartLine() + " | ";
|
var prefix = frame.getStartLine() + " | ";
|
||||||
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin)
|
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin)
|
||||||
.append(AnsiTheme.STACK_TRACE_LINE_NUMBER, prefix);
|
.append(AnsiTheme.STACK_TRACE_LINE_NUMBER, prefix);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public abstract class VmException extends AbstractTruffleException {
|
|||||||
private final List<StackFrame> leadingStackFrames;
|
private final List<StackFrame> leadingStackFrames;
|
||||||
@Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder;
|
@Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder;
|
||||||
@Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder;
|
@Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder;
|
||||||
|
private boolean forExpressionInput;
|
||||||
|
|
||||||
public VmException(
|
public VmException(
|
||||||
@Nullable String message,
|
@Nullable String message,
|
||||||
@@ -62,6 +63,7 @@ public abstract class VmException extends AbstractTruffleException {
|
|||||||
this.insertedStackFrames = insertedStackFrames;
|
this.insertedStackFrames = insertedStackFrames;
|
||||||
this.leadingStackFrames = leadingStackFrames;
|
this.leadingStackFrames = leadingStackFrames;
|
||||||
this.hintBuilder = hintBuilder;
|
this.hintBuilder = hintBuilder;
|
||||||
|
this.forExpressionInput = forExpressionInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isExternalMessage() {
|
public final boolean isExternalMessage() {
|
||||||
@@ -112,6 +114,14 @@ public abstract class VmException extends AbstractTruffleException {
|
|||||||
this.hintBuilder = ((builder, aBoolean) -> builder.append(hint));
|
this.hintBuilder = ((builder, aBoolean) -> builder.append(hint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setForExpressionInput(boolean forExpressionInput) {
|
||||||
|
this.forExpressionInput = forExpressionInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForExpressionInput() {
|
||||||
|
return forExpressionInput;
|
||||||
|
}
|
||||||
|
|
||||||
public enum Kind {
|
public enum Kind {
|
||||||
EVAL_ERROR,
|
EVAL_ERROR,
|
||||||
UNDEFINED_VALUE,
|
UNDEFINED_VALUE,
|
||||||
|
|||||||
@@ -144,7 +144,8 @@ public final class VmExceptionRenderer {
|
|||||||
|
|
||||||
var hintBuilder = exception.getHintBuilder();
|
var hintBuilder = exception.getHintBuilder();
|
||||||
if (!frames.isEmpty()) {
|
if (!frames.isEmpty()) {
|
||||||
stackTraceRenderer.render(frames, hint, hintBuilder, out.append('\n'));
|
stackTraceRenderer.render(
|
||||||
|
frames, hint, hintBuilder, out.append('\n'), exception.isForExpressionInput());
|
||||||
} else if (hint != null) {
|
} else if (hint != null) {
|
||||||
// render hint if there are no stack frames
|
// render hint if there are no stack frames
|
||||||
out.append('\n').append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
|
out.append('\n').append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
|
||||||
|
|||||||
@@ -61,9 +61,12 @@ import org.pkl.core.module.ModuleKey;
|
|||||||
import org.pkl.core.module.ModuleKeys;
|
import org.pkl.core.module.ModuleKeys;
|
||||||
import org.pkl.core.module.ResolvedModuleKey;
|
import org.pkl.core.module.ResolvedModuleKey;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
import org.pkl.core.util.EconomicMaps;
|
||||||
|
import org.pkl.core.util.Pair;
|
||||||
import org.pkl.parser.Parser;
|
import org.pkl.parser.Parser;
|
||||||
import org.pkl.parser.ParserError;
|
import org.pkl.parser.ParserError;
|
||||||
import org.pkl.parser.syntax.Expr;
|
import org.pkl.parser.syntax.Expr;
|
||||||
|
import org.pkl.parser.syntax.ImportClause;
|
||||||
|
import org.pkl.parser.syntax.ModuleDecl;
|
||||||
|
|
||||||
public final class VmUtils {
|
public final class VmUtils {
|
||||||
/** See {@link VmUtils#shouldRunTypeCheck(VirtualFrame)}. */
|
/** See {@link VmUtils#shouldRunTypeCheck(VirtualFrame)}. */
|
||||||
@@ -92,6 +95,10 @@ public final class VmUtils {
|
|||||||
private static final DecimalFormatSymbols ROOT_DECIMAL_FORMAT_SYMBOLS =
|
private static final DecimalFormatSymbols ROOT_DECIMAL_FORMAT_SYMBOLS =
|
||||||
DecimalFormatSymbols.getInstance(Locale.ROOT);
|
DecimalFormatSymbols.getInstance(Locale.ROOT);
|
||||||
|
|
||||||
|
public static final String EXPRESSION_PREAMBLE = "`THE REPL TEXT EXPR` = ";
|
||||||
|
|
||||||
|
private static final Parser parser = new Parser();
|
||||||
|
|
||||||
private VmUtils() {}
|
private VmUtils() {}
|
||||||
|
|
||||||
static VmTyped createEmptyModule() {
|
static VmTyped createEmptyModule() {
|
||||||
@@ -930,12 +937,69 @@ public final class VmUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a fake module that declares all the local properties that exist in {@code module}, so
|
||||||
|
* that AstBuilder can correctly resolve variables.
|
||||||
|
*
|
||||||
|
* <p>Additionally, return a {@link org.pkl.parser.syntax.Node} with its span calculated in terms
|
||||||
|
* of this fake module.
|
||||||
|
*
|
||||||
|
* <p>This created module is never executed.
|
||||||
|
*/
|
||||||
|
public static Pair<String, org.pkl.parser.syntax.Node> buildSyntheticModuleText(
|
||||||
|
org.pkl.parser.syntax.Node syntaxNode, VmTyped module, String srcText) {
|
||||||
|
if (syntaxNode instanceof ModuleDecl) {
|
||||||
|
return Pair.of(srcText, syntaxNode);
|
||||||
|
}
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var nodeText = syntaxNode.text(srcText.toCharArray());
|
||||||
|
var adjustedNode = syntaxNode;
|
||||||
|
if (syntaxNode instanceof Expr) {
|
||||||
|
sb.append(EXPRESSION_PREAMBLE).append(nodeText).append('\n');
|
||||||
|
} else {
|
||||||
|
sb.append(nodeText).append('\n');
|
||||||
|
}
|
||||||
|
var mod = parser.parseModule(sb.toString());
|
||||||
|
if (syntaxNode instanceof Expr) {
|
||||||
|
adjustedNode = mod.getProperties().get(0).getExpr();
|
||||||
|
} else if (syntaxNode instanceof ImportClause) {
|
||||||
|
adjustedNode = mod.getImports().get(0);
|
||||||
|
} else if (syntaxNode instanceof org.pkl.parser.syntax.ClassProperty) {
|
||||||
|
adjustedNode = mod.getProperties().get(0);
|
||||||
|
} else if (syntaxNode instanceof org.pkl.parser.syntax.Class) {
|
||||||
|
adjustedNode = mod.getClasses().get(0);
|
||||||
|
} else if (syntaxNode instanceof org.pkl.parser.syntax.TypeAlias) {
|
||||||
|
adjustedNode = mod.getTypeAliases().get(0);
|
||||||
|
} else if (syntaxNode instanceof org.pkl.parser.syntax.ClassMethod) {
|
||||||
|
adjustedNode = mod.getMethods().get(0);
|
||||||
|
}
|
||||||
|
var cursor = EconomicMaps.getEntries(module.getMembers());
|
||||||
|
while (cursor.advance()) {
|
||||||
|
var key = cursor.getKey();
|
||||||
|
var value = cursor.getValue();
|
||||||
|
if (value.isLocal()) {
|
||||||
|
sb.append("local ").append(key).append(" = Undefined()\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert adjustedNode != null;
|
||||||
|
return Pair.of(sb.toString(), adjustedNode);
|
||||||
|
}
|
||||||
|
|
||||||
public static Object evaluateExpression(
|
public static Object evaluateExpression(
|
||||||
VmTyped module,
|
VmTyped module,
|
||||||
String expression,
|
String expression,
|
||||||
SecurityManager securityManager,
|
SecurityManager securityManager,
|
||||||
ModuleResolver moduleResolver) {
|
ModuleResolver moduleResolver) {
|
||||||
var syntheticModule = ModuleKeys.synthetic(REPL_TEXT_URI, expression);
|
org.pkl.parser.syntax.Node node;
|
||||||
|
try {
|
||||||
|
node = parser.parseExpressionInput(expression);
|
||||||
|
} catch (ParserError e) {
|
||||||
|
throw toVmException(e, expression, REPL_TEXT_URI, "");
|
||||||
|
}
|
||||||
|
var pair = buildSyntheticModuleText(node, module, expression);
|
||||||
|
var syntheticModuleText = pair.first;
|
||||||
|
var adjustedNode = pair.second;
|
||||||
|
var syntheticModule = ModuleKeys.synthetic(REPL_TEXT_URI, syntheticModuleText);
|
||||||
ResolvedModuleKey resolvedModule;
|
ResolvedModuleKey resolvedModule;
|
||||||
try {
|
try {
|
||||||
resolvedModule = syntheticModule.resolve(securityManager);
|
resolvedModule = syntheticModule.resolve(securityManager);
|
||||||
@@ -955,9 +1019,10 @@ public final class VmUtils {
|
|||||||
resolvedModule,
|
resolvedModule,
|
||||||
false);
|
false);
|
||||||
var language = VmLanguage.get(null);
|
var language = VmLanguage.get(null);
|
||||||
var parsedExpression = parseExpressionNode(expression, source);
|
|
||||||
var builder = new AstBuilder(source, language, moduleInfo, moduleResolver);
|
var builder = new AstBuilder(source, language, moduleInfo, moduleResolver);
|
||||||
var exprNode = builder.visitExpr(parsedExpression);
|
var mod = parser.parseModule(syntheticModuleText);
|
||||||
|
builder.visitModule(mod);
|
||||||
|
var exprNode = builder.visitExpr((Expr) adjustedNode);
|
||||||
var rootNode =
|
var rootNode =
|
||||||
new SimpleRootNode(
|
new SimpleRootNode(
|
||||||
language,
|
language,
|
||||||
@@ -966,7 +1031,12 @@ public final class VmUtils {
|
|||||||
"",
|
"",
|
||||||
exprNode);
|
exprNode);
|
||||||
var callNode = Truffle.getRuntime().createIndirectCallNode();
|
var callNode = Truffle.getRuntime().createIndirectCallNode();
|
||||||
|
try {
|
||||||
return callNode.call(rootNode.getCallTarget(), module, module);
|
return callNode.call(rootNode.getCallTarget(), module, module);
|
||||||
|
} catch (VmException e) {
|
||||||
|
e.setForExpressionInput(true);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int findCustomThisSlot(VirtualFrame frame) {
|
public static int findCustomThisSlot(VirtualFrame frame) {
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// make sure that we don't trim this identifier when not using the expression evaluator
|
||||||
|
// see VmUtils.buildSyntheticModuleText()
|
||||||
|
`THE REPL TEXT EXPR` = throw("uh oh")
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
uh oh
|
||||||
|
|
||||||
|
x | `THE REPL TEXT EXPR` = throw("uh oh")
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
at replTextPreamble#`THE REPL TEXT EXPR` (file:///$snippetsDir/input/errors/replTextPreamble.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)
|
||||||
@@ -134,4 +134,43 @@ class EvaluateExpressionTest {
|
|||||||
|
|
||||||
assertThat(error).hasMessageContaining("Expected value of type `String`, but got type `Int`")
|
assertThat(error).hasMessageContaining("Expected value of type `String`, but got type `Int`")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `evaluate local property`() {
|
||||||
|
val result = evaluate("local foo = 1", "foo")
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(1L)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `evaluate throwing local property trims expression preamble when rendering stack frames`() {
|
||||||
|
val error = assertThrows<PklException> { evaluate("local foo = throw(\"uh oh\")", "foo") }
|
||||||
|
|
||||||
|
assertThat(error.message)
|
||||||
|
.isEqualTo(
|
||||||
|
"""
|
||||||
|
–– Pkl Error ––
|
||||||
|
uh oh
|
||||||
|
|
||||||
|
1 | local foo = throw("uh oh")
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
at text#foo (repl:text)
|
||||||
|
|
||||||
|
1 | foo
|
||||||
|
^^^
|
||||||
|
at (repl:text)
|
||||||
|
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `evaluate import`() {
|
||||||
|
val result = evaluate("import \"pkl:base\"", "base")
|
||||||
|
|
||||||
|
assertThat(result).isInstanceOf(PModule::class.java)
|
||||||
|
result as PModule
|
||||||
|
assertThat(result.moduleName).isEqualTo("pkl.base")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class StackTraceRendererTest {
|
|||||||
val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1)
|
val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1)
|
||||||
val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop)
|
val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop)
|
||||||
val formatter = AnsiStringBuilder(false)
|
val formatter = AnsiStringBuilder(false)
|
||||||
renderer.doRender(frames, null, null, formatter, "", true)
|
renderer.doRender(frames, null, null, formatter, "", true, true)
|
||||||
val renderedFrames = formatter.toString()
|
val renderedFrames = formatter.toString()
|
||||||
assertThat(renderedFrames)
|
assertThat(renderedFrames)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
|
|||||||
Reference in New Issue
Block a user