diff --git a/app/ide-desktop/lib/common/src/appConfig.js b/app/ide-desktop/lib/common/src/appConfig.js index 0b194464470..ca164dffdee 100644 --- a/app/ide-desktop/lib/common/src/appConfig.js +++ b/app/ide-desktop/lib/common/src/appConfig.js @@ -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.') diff --git a/engine/runner/src/main/java/org/enso/runner/Main.java b/engine/runner/src/main/java/org/enso/runner/Main.java index 115f3fb5536..c26f5d95ee2 100644 --- a/engine/runner/src/main/java/org/enso/runner/Main.java +++ b/engine/runner/src/main/java/org/enso/runner/Main.java @@ -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(); - 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 fnLangId = + (frame) -> frame.isHostFrame() ? "java" : frame.getLanguage().getId(); + Function fnRootName = StackFrame::getRootName; + Function 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") diff --git a/engine/runner/src/main/java/org/enso/runner/Utils.java b/engine/runner/src/main/java/org/enso/runner/Utils.java index 37b15613f46..c0c2e084d52 100644 --- a/engine/runner/src/main/java/org/enso/runner/Utils.java +++ b/engine/runner/src/main/java/org/enso/runner/Utils.java @@ -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 void printStackTrace( + Iterable stack, + boolean syntaxError, + String msg, + File relativeTo, + Consumer print, + Function fnLangId, + Function fnRootName, + Function fnSourceSection) { + + var fullStackReversed = new LinkedList(); + for (var e : stack) { + fullStackReversed.addFirst(e); + } + + var dropInitJava = + new ArrayList( + fullStackReversed.stream() + .dropWhile(f -> !LanguageInfo.ID.equals(fnLangId.apply(f))) + .toList()); + Collections.reverse(dropInitJava); + print.accept("Execution finished with an error: " + msg); + + Iterable 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 void printFrame( + S frame, + File relativeTo, + Consumer print, + Function fnLangId, + Function fnRootName, + Function 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); + } } diff --git a/engine/runner/src/test/java/org/enso/runner/UtilsTest.java b/engine/runner/src/test/java/org/enso/runner/UtilsTest.java index 5e70a140133..1884eb04b42 100644 --- a/engine/runner/src/test/java/org/enso/runner/UtilsTest.java +++ b/engine/runner/src/test/java/org/enso/runner/UtilsTest.java @@ -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(); + + 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 throw(Internal) + at c(Internal) + at 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; + } + } }