Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly expose stacktraces and related data to user code #3271

Merged
merged 13 commits into from
Feb 16, 2022
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
- [Implemented `Range.find`, `Table.rename_columns` and
`Table.use_first_row_as_names` operations][3249]
- [Implemented `Text.at` and `Text.is_digit` methods][3269]
- [Implemented `Runtime.get_stack_trace` together with some utilities to process
stack traces and code locations][3271]
- [Implemented `Vector.flatten`][3259]

[debug-shortcuts]:
Expand All @@ -59,6 +61,7 @@
[3249]: https://github.com/enso-org/enso/pull/3249
[3264]: https://github.com/enso-org/enso/pull/3264
[3269]: https://github.com/enso-org/enso/pull/3269
[3271]: https://github.com/enso-org/enso/pull/3271
[3259]: https://github.com/enso-org/enso/pull/3259

#### Enso Compiler
Expand Down
98 changes: 50 additions & 48 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
@@ -1,55 +1,57 @@
import Standard.Base.Data.Any.Extensions
import Standard.Base.Data.Array.Extensions
import Standard.Base.Data.Interval
import Standard.Base.Data.Json
import Standard.Base.Data.List
import Standard.Base.Data.Locale
import Standard.Base.Data.Map
import Standard.Base.Data.Maybe
import Standard.Base.Data.Noise
import Standard.Base.Data.Number.Extensions
import Standard.Base.Data.Ordering
import Standard.Base.Data.Ordering.Sort_Order
import Standard.Base.Data.Pair
import Standard.Base.Data.Range
import Standard.Base.Data.Text.Extensions
import Standard.Base.Data.Vector
import Standard.Base.Error.Common
import Standard.Base.Error.Extensions
import Standard.Base.Math
import Standard.Base.Meta
import Standard.Base.Meta.Enso_Project
import Standard.Base.Polyglot.Java
import Standard.Base.System.File
import Standard.Base.Data.Text.Regex.Mode as Regex_Mode
import project.Data.Any.Extensions
import project.Data.Array.Extensions
import project.Data.Interval
import project.Data.Json
import project.Data.List
import project.Data.Locale
import project.Data.Map
import project.Data.Maybe
import project.Data.Noise
import project.Data.Number.Extensions
import project.Data.Ordering
import project.Data.Ordering.Sort_Order
import project.Data.Pair
import project.Data.Range
import project.Data.Text.Extensions
import project.Data.Vector
import project.Error.Common
import project.Error.Extensions
import project.Math
import project.Meta
import project.Meta.Enso_Project
import project.Polyglot.Java
import project.Runtime.Extensions
import project.System.File
import project.Data.Text.Regex.Mode as Regex_Mode

from Standard.Builtins import Nothing, Number, Integer, Any, True, False, Cons, Boolean, Arithmetic_Error

export Standard.Base.Data.Interval
export Standard.Base.Data.Json
export Standard.Base.Data.Locale
export Standard.Base.Data.Map
export Standard.Base.Data.Maybe
export Standard.Base.Data.Ordering
export Standard.Base.Data.Ordering.Sort_Order
export Standard.Base.Data.Vector
export Standard.Base.Math
export Standard.Base.Meta
export Standard.Base.System.File
export Standard.Base.Data.Text.Regex.Mode as Regex_Mode
export project.Data.Interval
export project.Data.Json
export project.Data.Locale
export project.Data.Map
export project.Data.Maybe
export project.Data.Ordering
export project.Data.Ordering.Sort_Order
export project.Data.Vector
export project.Math
export project.Meta
export project.System.File
export project.Data.Text.Regex.Mode as Regex_Mode

from Standard.Base.Data.Any.Extensions export all
from Standard.Base.Data.Array.Extensions export all
from Standard.Base.Data.List export Nil, Cons
from Standard.Base.Data.Number.Extensions export all hiding Math, String, Double
from Standard.Base.Data.Noise export all hiding Noise
from Standard.Base.Data.Pair export Pair
from Standard.Base.Data.Range export Range
from Standard.Base.Data.Text.Extensions export Text, Split_Kind, Line_Ending_Style
from Standard.Base.Error.Common export all
from Standard.Base.Error.Extensions export all
from Standard.Base.Meta.Enso_Project export all
from Standard.Base.Polyglot.Java export all
from project.Data.Any.Extensions export all
from project.Data.Array.Extensions export all
from project.Data.List export Nil, Cons
from project.Data.Number.Extensions export all hiding Math, String, Double
from project.Data.Noise export all hiding Noise
from project.Data.Pair export Pair
from project.Data.Range export Range
from project.Data.Text.Extensions export Text, Split_Kind, Line_Ending_Style
from project.Error.Common export all
from project.Error.Extensions export all
from project.Meta.Enso_Project export all
from project.Polyglot.Java export all
from project.Runtime.Extensions export all
4e6 marked this conversation as resolved.
Show resolved Hide resolved

from Standard.Builtins export all hiding Meta, Less, Equal, Greater, Ordering

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Standard.Builtins

Enso_Project.root
Builtins.Project_Description.root : File.File
Builtins.Project_Description.root = File.File this.prim_root_file
Builtins.Project_Description.root = File.new this.prim_root_file.getPath

## Returns the root data directory of the project.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from Standard.Base import all

## ADVANCED
UNSTABLE
Represents a source location in Enso code. Contains information about the
source file and code position within it.
type Source_Location
## PRIVATE
type Source_Location prim_location

## UNSTABLE
Pretty prints the location.
to_text : Text
to_text =
'(Source_Location ' + this.formatted_coordinates + ')'

## UNSTABLE

Returns the 1-based line index of the start of this code range.
start_line : Integer
start_line = this.prim_location.getStartLine

## UNSTABLE

Returns the 1-based line index of the end of this code range.
end_line : Integer
end_line = this.prim_location.getEndLine

## UNSTABLE

Returns the 1-based column index of the start of this code range.
start_column : Integer
start_column = this.prim_location.getStartColumn

## UNSTABLE

Returns the 1-based column index of the end of this code range.
end_column : Integer
end_column = this.prim_location.getEndColumn

## UNSTABLE

Returns a pretty-printed location (file and line info).
formatted_coordinates : Text
formatted_coordinates =
start_line = this.start_line
end_line = this.end_line
indices = case start_line == end_line of
True ->
row = start_line.to_text
start = this.start_column.to_text
end = this.end_column.to_text
row + ":" + start + "-" + end
False ->
start_line + '-' + end_line
cwd = File.current_directory
file = this.file.absolute
formatted_file = case file.is_child_of cwd of
True -> cwd.relativize file . path
_ -> file.path
formatted_file + ":" + indices

## UNSTABLE

Return the source file corresponding to this location.
file : File.File
file = File.new this.prim_location.getSource.getPath

## ADVANCED
UNSTABLE

Represents a single stack frame in an Enso stack trace.
type Stack_Trace_Element
## PRIVATE
type Stack_Trace_Element name source_location

## ADVANCED
UNSTABLE

Returns the execution stack trace of its call site. The ordering of the
resulting vector is such that the top stack frame is the first element.
Runtime.get_stack_trace : Vector.Vector Stack_Trace_Element
Runtime.get_stack_trace =
prim_stack = this.primitive_get_stack_trace
stack_with_prims = Vector.Vector prim_stack
stack = stack_with_prims.map el->
loc = case Polyglot.has_source_location el of
True -> Source_Location (Polyglot.get_source_location el)
False -> Nothing
name = Polyglot.get_executable_name el
Stack_Trace_Element name loc
# drop this frame and the one from `Runtime.primitive_get_stack_trace`
stack.drop_start 2
10 changes: 10 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,16 @@ type File
matcher.matches (Path.of pathStr)
filtered

## UNSTABLE

Checks if `this` is a child path of `other`.
is_child_of other = this.prim_file.startsWith other.prim_file

## UNSTABLE

Transforms `child` to a relative path with respect to `this`.
relativize child = File (this.prim_file.relativize child.prim_file)

## An output stream, allowing for interactive writing of contents into an
open file.
type Output_Stream
Expand Down
16 changes: 16 additions & 0 deletions docs/syntax/imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ code from modules.

<!-- MarkdownTOC levels="2,3" autolink="true" -->

- [Qualified Names](#qualified-names)
- [Import Syntax](#import-syntax)
- [Qualified Imports](#qualified-imports)
- [Unqualified Imports](#unqualified-imports)
Expand All @@ -24,6 +25,21 @@ code from modules.

<!-- /MarkdownTOC -->

## Qualified Names

Both imports and exports require the use of qualified module names. A qualified
name consists of the library namespace (usually organization under which its
published) and the library name, followed by module names mirroring the source
tree of the library. For example the file `src/Stuff/Things/Util.enso` inside
the library `My_Lib` published by the user `wdanilo` would have the following
qualified name: `wdanilo.My_Lib.Stuff.Things.Util`. To facilitate library
renaming (or deciding on the publishing organization later in the development
cycle, or working on a project that won't be published) it is possible to use
the keyword `project` instead of namespace and project name, to import a file in
the same project. Therefore, the file `src/Varia/Tools/Manager.enso` in `My_Lib`
published (or not) by `wdanilo` may use `project.Stuff.Things.Util` to refer to
the previously mentioned file.

## Import Syntax

There are two main ways of importing a module into the current scope.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.enso.interpreter.node.expression.builtin.interop.generic;

import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;

@BuiltinMethod(
type = "Polyglot",
name = "get_executable_name",
description = "Returns the executable name of a polyglot object.")
public class GetExecutableNameNode extends Node {
private @Child InteropLibrary functionsLibrary =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private @Child InteropLibrary stringsLibrary =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private final BranchProfile err = BranchProfile.create();

Text execute(Object _this, Object function) {
try {
return Text.create(stringsLibrary.asString(functionsLibrary.getExecutableName(function)));
} catch (UnsupportedMessageException e) {
err.enter();
Builtins builtins = Context.get(this).getBuiltins();
throw new PanicException(
builtins.error().makeTypeError(builtins.function(), function, "function"), this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.enso.interpreter.node.expression.builtin.interop.generic;

import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.data.EnsoSourceSection;
import org.enso.interpreter.runtime.error.PanicException;

@BuiltinMethod(
type = "Polyglot",
name = "get_source_location",
description = "Returns the source location of a polyglot object.")
public class GetSourceLocationNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private final BranchProfile err = BranchProfile.create();

Object execute(Object _this, Object value) {
try {
return Context.get(this)
.getEnvironment()
.asGuestValue(new EnsoSourceSection(library.getSourceLocation(value)));
} catch (UnsupportedMessageException e) {
err.enter();
Builtins builtins = Context.get(this).getBuiltins();
throw new PanicException(
builtins.error().makeTypeError(builtins.function(), value, "function"), this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.enso.interpreter.node.expression.builtin.interop.generic;

import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.dsl.BuiltinMethod;

@BuiltinMethod(
type = "Polyglot",
name = "has_source_location",
description = "Checks if an object has a source location.")
public class HasSourceLocationNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private final BranchProfile err = BranchProfile.create();

boolean execute(Object _this, Object value) {
return library.hasSourceLocation(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.enso.interpreter.node.expression.builtin.runtime;

import com.oracle.truffle.api.TruffleStackTrace;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.PanicException;

@BuiltinMethod(
type = "Runtime",
name = "primitive_get_stack_trace",
description = "Gets the current execution stacktrace.")
public class GetStackTraceNode extends Node {
Array execute(Object _this) {
var exception = new PanicException(null, this);
TruffleStackTrace.fillIn(exception);
var elements = TruffleStackTrace.getStackTrace(exception);
var ret = new Array(elements.size());
for (int i = 0; i < elements.size(); i++) {
var element = elements.get(i);
ret.getItems()[i] = element.getGuestObject();
}
return ret;
}
}
Loading