Reverse order of stack frames and test the result (#9954)

Fixes #9934 by reversing the order of `dropInitJava` frames.
This commit is contained in:
Jaroslav Tulach 2024-05-15 11:09:36 +02:00 committed by GitHub
parent c437721ba5
commit a53b2f0b18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 172 additions and 65 deletions

View File

@ -3,8 +3,6 @@ import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as url from 'node:url'
import BUILD_INFO from '../../../../../build.json' assert { type: 'json' }
// ===============================
// === readEnvironmentFromFile ===
// ===============================
@ -50,10 +48,17 @@ export async function readEnvironmentFromFile() {
if (!isProduction || entries.length > 0) {
Object.assign(process.env, variables)
}
const buildInfo = await (async () => {
try {
return await import('../../../../../build.json', { type: 'json' })
} catch {
return { commit: '', version: '', engineVersion: '', name: '' }
}
})()
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= BUILD_INFO.version
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= BUILD_INFO.commit
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
} catch (error) {
if (missingKeys.length !== 0) {
console.warn('Could not load `.env` file; disabling cloud backend.')

View File

@ -11,9 +11,9 @@ import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.cli.CommandLine;
@ -43,6 +43,7 @@ import org.enso.profiling.sampler.OutputStreamSampler;
import org.enso.version.VersionDescription;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.PolyglotException.StackFrame;
import org.graalvm.polyglot.SourceSection;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import scala.concurrent.ExecutionContext;
@ -1231,69 +1232,22 @@ public final class Main {
scala.Option.apply(profilingPath), scala.Option.apply(profilingTime));
}
private void printFrame(StackFrame frame, File relativeTo) {
var langId = frame.isHostFrame() ? "java" : frame.getLanguage().getId();
String fmtFrame;
if (LanguageInfo.ID.equals(langId)) {
var fName = frame.getRootName();
var src = "Internal";
var sourceLoc = frame.getSourceLocation();
if (sourceLoc != null) {
var path = sourceLoc.getSource().getPath();
var ident = sourceLoc.getSource().getName();
if (path != null) {
if (relativeTo != null) {
var absRoot = relativeTo.getAbsoluteFile();
if (path.startsWith(absRoot.getAbsolutePath())) {
var rootDir = absRoot.isDirectory() ? absRoot : absRoot.getParentFile();
ident = rootDir.toPath().relativize(new File(path).toPath()).toString();
}
}
}
var loc = sourceLoc.getStartLine() + "-" + sourceLoc.getEndLine();
var line = sourceLoc.getStartLine();
if (line == sourceLoc.getEndLine()) {
var start = sourceLoc.getStartColumn();
var end = sourceLoc.getEndColumn();
loc = line + ":" + start + "-" + end;
}
src = ident + ":" + loc;
}
fmtFrame = fName + "(" + src + ")";
} else {
fmtFrame = frame.toString();
}
println(" at <" + langId + "> " + fmtFrame);
}
private void printPolyglotException(PolyglotException exception, File relativeTo) {
var fullStackReversed = new LinkedList<StackFrame>();
for (var e : exception.getPolyglotStackTrace()) {
fullStackReversed.addFirst(e);
}
var dropInitJava =
fullStackReversed.stream()
.dropWhile(f -> !LanguageInfo.ID.equals(f.getLanguage().getId()))
.toList();
Collections.reverse(fullStackReversed);
var msg = HostEnsoUtils.findExceptionMessage(exception);
println("Execution finished with an error: " + msg);
Function<StackFrame, String> fnLangId =
(frame) -> frame.isHostFrame() ? "java" : frame.getLanguage().getId();
Function<StackFrame, String> fnRootName = StackFrame::getRootName;
Function<StackFrame, SourceSection> fnSourceSection = StackFrame::getSourceLocation;
if (exception.isSyntaxError()) {
// no stack
} else if (dropInitJava.isEmpty()) {
for (var f : exception.getPolyglotStackTrace()) {
printFrame(f, relativeTo);
}
} else {
for (var f : dropInitJava) {
printFrame(f, relativeTo);
}
}
Utils.printStackTrace(
exception.getPolyglotStackTrace(),
exception.isSyntaxError(),
msg,
relativeTo,
this::println,
fnLangId,
fnRootName,
fnSourceSection);
}
@SuppressWarnings("unchecked")

View File

@ -2,6 +2,13 @@ package org.enso.runner;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.function.Consumer;
import java.util.function.Function;
import org.enso.common.LanguageInfo;
import org.graalvm.polyglot.SourceSection;
import scala.Tuple3;
final class Utils {
@ -66,4 +73,86 @@ final class Utils {
}
return Tuple3.apply(projectMode, canonicalFile, projectRoot);
}
static <S> void printStackTrace(
Iterable<S> stack,
boolean syntaxError,
String msg,
File relativeTo,
Consumer<String> print,
Function<S, String> fnLangId,
Function<S, String> fnRootName,
Function<S, SourceSection> fnSourceSection) {
var fullStackReversed = new LinkedList<S>();
for (var e : stack) {
fullStackReversed.addFirst(e);
}
var dropInitJava =
new ArrayList<S>(
fullStackReversed.stream()
.dropWhile(f -> !LanguageInfo.ID.equals(fnLangId.apply(f)))
.toList());
Collections.reverse(dropInitJava);
print.accept("Execution finished with an error: " + msg);
Iterable<S> toPrint;
if (syntaxError) {
toPrint = null;
} else if (dropInitJava.isEmpty()) {
toPrint = stack;
} else {
toPrint = dropInitJava;
}
if (toPrint != null) {
for (var f : toPrint) {
printFrame(f, relativeTo, print, fnLangId, fnRootName, fnSourceSection);
}
}
}
private static <S> void printFrame(
S frame,
File relativeTo,
Consumer<String> print,
Function<S, String> fnLangId,
Function<S, String> fnRootName,
Function<S, SourceSection> fnSourceSection) {
var langId = fnLangId.apply(frame);
String fmtFrame;
if (LanguageInfo.ID.equals(langId)) {
var fName = fnRootName.apply(frame);
var src = "Internal";
var sourceLoc = fnSourceSection.apply(frame);
if (sourceLoc != null) {
var path = sourceLoc.getSource().getPath();
var ident = sourceLoc.getSource().getName();
if (path != null) {
if (relativeTo != null) {
var absRoot = relativeTo.getAbsoluteFile();
if (path.startsWith(absRoot.getAbsolutePath())) {
var rootDir = absRoot.isDirectory() ? absRoot : absRoot.getParentFile();
ident = rootDir.toPath().relativize(new File(path).toPath()).toString();
}
}
}
var loc = sourceLoc.getStartLine() + "-" + sourceLoc.getEndLine();
var line = sourceLoc.getStartLine();
if (line == sourceLoc.getEndLine()) {
var start = sourceLoc.getStartColumn();
var end = sourceLoc.getEndColumn();
loc = line + ":" + start + "-" + end;
}
src = ident + ":" + loc;
}
fmtFrame = fName + "(" + src + ")";
} else {
fmtFrame = frame.toString();
}
print.accept(" at <" + langId + "> " + fmtFrame);
}
}

View File

@ -7,6 +7,10 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.graalvm.polyglot.SourceSection;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@ -16,6 +20,48 @@ public class UtilsTest {
public UtilsTest() {}
@Test
public void printStackTrace1() throws Exception {
var f = folder.newFile("p.enso");
var code =
"""
from Standard.Base import all
a = b
b = c
c = Panic.throw "foo"
main = a
""";
Files.writeString(f.toPath(), code);
var lines = new ArrayList<String>();
var stack =
List.of(
new MockFrame("enso", "Panic", "throw", "Internal", -1, -1, -1),
new MockFrame("enso", "p", "c", "p.enso", 5, 5, 21),
new MockFrame("enso", "m", "main", "p.enso", 20, 4, 8));
Utils.printStackTrace(
stack,
false,
"foo",
new File("/"),
lines::add,
MockFrame::lang,
MockFrame::method,
MockFrame::section);
assertEquals(
"""
Execution finished with an error: foo
at <enso> throw(Internal)
at <enso> c(Internal)
at <enso> main(Internal)""",
lines.stream().collect(Collectors.joining("\n")));
}
@Test
public void detectParentProject() throws Exception {
var dir = folder.newFolder("dir", "prj", "src", "some", "file").getCanonicalFile();
@ -100,4 +146,17 @@ public class UtilsTest {
assertEquals("Source detected", src, found._2());
assertEquals("No project folder detected without src dir", "", found._3());
}
private static record MockFrame(
String lang,
String type,
String method,
String src,
int line,
int startColumn,
int endColumn) {
SourceSection section() {
return null;
}
}
}