mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 18:15:21 +03:00
Decorate values with arbitrary warnings (#3248)
This commit is contained in:
parent
31be7c8b9a
commit
4653bfeeab
@ -105,8 +105,10 @@
|
||||
|
||||
- [Added overloaded `from` conversions.][3227]
|
||||
- [Upgraded to Graal VM 21.3.0][3258]
|
||||
- [Added the ability to decorate values with warnings.][3248]
|
||||
|
||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||
[3258]: https://github.com/enso-org/enso/pull/3258
|
||||
|
||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||
|
@ -22,8 +22,10 @@ import project.Meta
|
||||
import project.Meta.Enso_Project
|
||||
import project.Polyglot.Java
|
||||
import project.Runtime.Extensions
|
||||
import project.System.Environment
|
||||
import project.System.File
|
||||
import project.Data.Text.Regex.Mode as Regex_Mode
|
||||
import project.Warning
|
||||
|
||||
from Standard.Builtins import Nothing, Number, Integer, Any, True, False, Cons, Boolean, Arithmetic_Error
|
||||
|
||||
@ -37,8 +39,10 @@ export project.Data.Ordering.Sort_Order
|
||||
export project.Data.Vector
|
||||
export project.Math
|
||||
export project.Meta
|
||||
export project.System.Environment
|
||||
export project.System.File
|
||||
export project.Data.Text.Regex.Mode as Regex_Mode
|
||||
export project.Warning
|
||||
|
||||
from project.Data.Any.Extensions export all
|
||||
from project.Data.Array.Extensions export all
|
||||
|
@ -52,7 +52,7 @@ type Source_Location
|
||||
end = this.end_column.to_text
|
||||
row + ":" + start + "-" + end
|
||||
False ->
|
||||
start_line + '-' + end_line
|
||||
start_line.to_text + '-' + end_line.to_text
|
||||
cwd = File.current_directory
|
||||
file = this.file.absolute
|
||||
formatted_file = case file.is_child_of cwd of
|
||||
|
69
distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso
Normal file
69
distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso
Normal file
@ -0,0 +1,69 @@
|
||||
from Standard.Base import all
|
||||
|
||||
## A representation of a dataflow warning attached to a value.
|
||||
type Warning
|
||||
## PRIVATE
|
||||
|
||||
The constructor to wrap primitive warnings.
|
||||
type Warning prim_warning
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
Returns the warning value – usually its explanation or other contents.
|
||||
value : Any
|
||||
value = Prim_Warning.get_value this.prim_warning
|
||||
|
||||
## UNSTABLE
|
||||
ADVANCED
|
||||
|
||||
A stack trace for the original warning creation.
|
||||
origin : Vector.Vector Stack_Trace_Element
|
||||
origin = Prim_Warning.get_origin this.prim_warning
|
||||
|
||||
## UNSTABLE
|
||||
ADVANCED
|
||||
|
||||
A list of locations where the warning was reassigned in the order of
|
||||
latest-first.
|
||||
|
||||
Warnings are reassigned whenever they interract with specific language
|
||||
elements:
|
||||
- When pattern matching, the warnings of the scrutinee will be reassigned
|
||||
to the `case` expression result.
|
||||
- When calling a method, warnings assigned to `this` will be reassigned to
|
||||
the method return value.
|
||||
- When calling a polyglot function or method, warnings assigned to any
|
||||
arguments will be accumulated in the return value.
|
||||
- The standard library methods reassign warnings such that their dataflow
|
||||
nature is preserved.
|
||||
reassignments : Vector.Vector Stack_Trace_Element
|
||||
reassignments =
|
||||
Vector.Vector (Prim_Warning.get_reassignments this.prim_warning) . map r->
|
||||
loc = case Polyglot.has_source_location r of
|
||||
False -> Nothing
|
||||
True -> Source_Location (Polyglot.get_source_location r)
|
||||
Stack_Trace_Element (Polyglot.get_executable_name r) loc
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
Attaches a new warning to the value.
|
||||
attach : Any -> Any -> Any
|
||||
attach warning value =
|
||||
origin = Runtime.get_stack_trace
|
||||
Prim_Warning.attach value warning (origin.drop_start 1)
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
Gets all the warnings attached to the given value. Warnings are returned in the
|
||||
reverse-chronological order with respect to their attachment time.
|
||||
get_all : Any -> Vector.Vector Warning
|
||||
get_all value =
|
||||
Vector.Vector (Prim_Warning.get_all value) . map Warning
|
||||
|
||||
## UNSTABLE
|
||||
ADVANCED
|
||||
|
||||
Sets a new list of warnings for the given value. Any warnings already present
|
||||
in `value` will be lost.
|
||||
set warnings value =
|
||||
Prim_Warning.set value (warnings.map .prim_warning).to_array
|
@ -21,10 +21,14 @@ import Standard.Builtins
|
||||
2*3 . should_equal 6
|
||||
Suite.run_main : Any -> Nothing
|
||||
Suite.run_main ~specs =
|
||||
r = this.run specs
|
||||
r = this.run specs here.config_from_env
|
||||
code = if r.is_fail then 1 else 0
|
||||
System.exit code
|
||||
|
||||
config_from_env =
|
||||
only_group_regexp = Environment.get "TEST_ONLY_GROUP"
|
||||
Suite_Config only_group_regexp
|
||||
|
||||
## Creates a new test group, desribing properties of the object
|
||||
described by `this`.
|
||||
|
||||
@ -42,9 +46,9 @@ Suite.run_main ~specs =
|
||||
2+3 . should_equal 5
|
||||
Test.specify "should define multiplication" <|
|
||||
2*3 . should_equal 6
|
||||
Suite.run : Any -> Any
|
||||
Suite.run ~specs =
|
||||
r = State.run Suite (Suite Nil) <|
|
||||
Suite.run : Any -> Suite_Config -> Any
|
||||
Suite.run ~specs config =
|
||||
r = State.run Suite (Suite config Nil) <|
|
||||
specs
|
||||
State.get Suite
|
||||
r
|
||||
@ -67,18 +71,20 @@ Suite.run ~specs =
|
||||
Test.group "Number" <| Nothing
|
||||
group : Text -> Any -> (Text | Nothing) -> Nothing
|
||||
group name ~behaviors pending=Nothing =
|
||||
case pending of
|
||||
Nothing ->
|
||||
r = State.run Spec (Spec name Nil) <|
|
||||
behaviors
|
||||
State.get Spec
|
||||
r.print_report
|
||||
suite = State.get Suite
|
||||
new_suite = Suite (Cons r suite.specs)
|
||||
State.put Suite new_suite
|
||||
reason ->
|
||||
IO.print_err ("[PENDING] " + name)
|
||||
IO.print_err (" Reason: " + reason)
|
||||
config = State.get Suite . config
|
||||
if config.should_run_group name then
|
||||
case pending of
|
||||
Nothing ->
|
||||
r = State.run Spec (Spec name Nil) <|
|
||||
behaviors
|
||||
State.get Spec
|
||||
r.print_report
|
||||
suite = State.get Suite
|
||||
new_suite = Suite suite.config (Cons r suite.specs)
|
||||
State.put Suite new_suite
|
||||
reason ->
|
||||
IO.print_err ("[PENDING] " + name)
|
||||
IO.print_err (" Reason: " + reason)
|
||||
|
||||
## Specifies a single behavior, described by `this`.
|
||||
|
||||
@ -435,13 +441,24 @@ Spec.print_report =
|
||||
IO.print_err (" - [PENDING] " + behavior.name)
|
||||
IO.print_err (" Reason: " + reason)
|
||||
|
||||
## PRVATE
|
||||
type Suite_Config
|
||||
type Suite_Config only_group_regexp
|
||||
|
||||
should_run_group name =
|
||||
regexp = this.only_group_regexp
|
||||
case regexp of
|
||||
Text -> name.matches regexp . catch (_->True)
|
||||
_ -> True
|
||||
|
||||
|
||||
## PRIVATE
|
||||
|
||||
The top-level entry point for a test suite.
|
||||
|
||||
Arguments:
|
||||
- specs: The specs contained within the test suite.
|
||||
type Suite specs
|
||||
type Suite config specs
|
||||
|
||||
## PRIVATE
|
||||
|
||||
|
@ -8,6 +8,7 @@ import com.oracle.truffle.api.TruffleLogger;
|
||||
import com.oracle.truffle.api.debug.DebuggerTags;
|
||||
import com.oracle.truffle.api.instrumentation.ProvidedTags;
|
||||
import com.oracle.truffle.api.instrumentation.StandardTags;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.distribution.DistributionManager;
|
||||
import org.enso.distribution.Environment;
|
||||
@ -58,6 +59,12 @@ import org.graalvm.options.OptionDescriptors;
|
||||
})
|
||||
public final class Language extends TruffleLanguage<Context> {
|
||||
private IdExecutionInstrument idExecutionInstrument;
|
||||
private static final LanguageReference<Language> REFERENCE =
|
||||
LanguageReference.create(Language.class);
|
||||
|
||||
public static Language get(Node node) {
|
||||
return REFERENCE.get(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Enso context.
|
||||
|
@ -156,7 +156,8 @@ public abstract class IndirectInvokeCallableNode extends Node {
|
||||
schema,
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode,
|
||||
isTail);
|
||||
isTail,
|
||||
thisArgumentPosition);
|
||||
} else {
|
||||
throw new RuntimeException("Currying without `this` argument is not yet supported.");
|
||||
}
|
||||
|
@ -23,10 +23,9 @@ import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.error.*;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@ -50,7 +49,8 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail);
|
||||
BaseNode.TailStatus isTail,
|
||||
int thatArgumentPosition);
|
||||
|
||||
@Specialization(guards = "dispatch.canConvertFrom(that)")
|
||||
Stateful doConvertFrom(
|
||||
@ -64,8 +64,8 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thatArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
|
||||
@Cached ConditionProfile atomProfile,
|
||||
@Cached ConditionProfile atomConstructorProfile,
|
||||
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
|
||||
@ -74,7 +74,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
dispatch.getConversionFunction(
|
||||
that,
|
||||
InvokeConversionNode.extractConstructor(
|
||||
this, _this, ctx, atomConstructorProfile, atomProfile),
|
||||
this, _this, atomConstructorProfile, atomProfile),
|
||||
conversion);
|
||||
return indirectInvokeFunctionNode.execute(
|
||||
function,
|
||||
@ -87,7 +87,11 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
isTail);
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
Context.get(this)
|
||||
.getBuiltins()
|
||||
.error()
|
||||
.makeNoSuchConversionError(_this, that, conversion),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,8 +107,8 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thatArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
|
||||
@Cached BranchProfile profile,
|
||||
@Cached ConditionProfile atomProfile,
|
||||
@Cached ConditionProfile atomConstructorProfile,
|
||||
@ -114,7 +118,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
dispatch.getConversionFunction(
|
||||
that,
|
||||
InvokeConversionNode.extractConstructor(
|
||||
this, _this, ctx, atomConstructorProfile, atomProfile),
|
||||
this, _this, atomConstructorProfile, atomProfile),
|
||||
conversion);
|
||||
return indirectInvokeFunctionNode.execute(
|
||||
function,
|
||||
@ -142,10 +146,43 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail) {
|
||||
BaseNode.TailStatus isTail,
|
||||
int thatArgumentPosition) {
|
||||
throw that;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doWarning(
|
||||
MaterializedFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
WithWarnings that,
|
||||
Object[] arguments,
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thatArgumentPosition,
|
||||
@Cached IndirectInvokeConversionNode childDispatch) {
|
||||
arguments[thatArgumentPosition] = that.getValue();
|
||||
ArrayRope<Warning> warnings = that.getReassignedWarnings(this);
|
||||
Stateful result =
|
||||
childDispatch.execute(
|
||||
frame,
|
||||
state,
|
||||
conversion,
|
||||
_this,
|
||||
that.getValue(),
|
||||
arguments,
|
||||
schema,
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode,
|
||||
isTail,
|
||||
thatArgumentPosition);
|
||||
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
|
||||
}
|
||||
|
||||
@Specialization(guards = "interop.isString(that)")
|
||||
Stateful doConvertText(
|
||||
MaterializedFrame frame,
|
||||
@ -158,12 +195,12 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thatArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@Cached ConditionProfile atomProfile,
|
||||
@Cached ConditionProfile atomConstructorProfile,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
|
||||
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
|
||||
try {
|
||||
String str = interop.asString(that);
|
||||
@ -172,7 +209,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
textDispatch.getConversionFunction(
|
||||
txt,
|
||||
InvokeConversionNode.extractConstructor(
|
||||
this, _this, ctx, atomConstructorProfile, atomProfile),
|
||||
this, _this, atomConstructorProfile, atomProfile),
|
||||
conversion);
|
||||
arguments[0] = txt;
|
||||
return indirectInvokeFunctionNode.execute(
|
||||
@ -188,31 +225,36 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
throw new IllegalStateException("Impossible, that is guaranteed to be a string.");
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
Context.get(this)
|
||||
.getBuiltins()
|
||||
.error()
|
||||
.makeNoSuchConversionError(_this, that, conversion),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.canConvertFrom(that)",
|
||||
"!interop.isString(that)",
|
||||
"!methods.hasSpecialConversion(that)"
|
||||
})
|
||||
guards = {
|
||||
"!methods.canConvertFrom(that)",
|
||||
"!interop.isString(that)",
|
||||
"!methods.hasSpecialConversion(that)"
|
||||
})
|
||||
Stateful doFallback(
|
||||
MaterializedFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@CachedContext(Language.class) Context ctx) {
|
||||
MaterializedFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thatArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
throw new PanicException(
|
||||
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
Context.get(this).getBuiltins().error().makeNoSuchConversionError(_this, that, conversion),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,9 @@ import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.*;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
import org.enso.interpreter.runtime.number.EnsoBigInteger;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
@ -54,7 +53,8 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail);
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition);
|
||||
|
||||
@Specialization(guards = "dispatch.hasFunctionalDispatch(_this)")
|
||||
Stateful doFunctionalDispatch(
|
||||
@ -67,9 +67,9 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@Cached IndirectInvokeFunctionNode invokeFunctionNode,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
@Cached IndirectInvokeFunctionNode invokeFunctionNode) {
|
||||
try {
|
||||
Function function = dispatch.getFunctionalDispatch(_this, symbol);
|
||||
return invokeFunctionNode.execute(
|
||||
@ -83,7 +83,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
isTail);
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +98,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition,
|
||||
@Cached DataflowErrorResolverNode dataflowErrorResolverNode,
|
||||
@Cached IndirectInvokeFunctionNode invokeFunctionNode,
|
||||
@Cached ConditionProfile profile) {
|
||||
@ -117,6 +118,36 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doWarning(
|
||||
MaterializedFrame frame,
|
||||
Object state,
|
||||
UnresolvedSymbol symbol,
|
||||
WithWarnings _this,
|
||||
Object[] arguments,
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition,
|
||||
@Cached IndirectInvokeMethodNode childDispatch) {
|
||||
arguments[thisArgumentPosition] = _this.getValue();
|
||||
ArrayRope<Warning> warnings = _this.getReassignedWarnings(this);
|
||||
Stateful result =
|
||||
childDispatch.execute(
|
||||
frame,
|
||||
state,
|
||||
symbol,
|
||||
_this.getValue(),
|
||||
arguments,
|
||||
schema,
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode,
|
||||
isTail,
|
||||
thisArgumentPosition);
|
||||
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doPanicSentinel(
|
||||
MaterializedFrame frame,
|
||||
@ -127,7 +158,8 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail) {
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition) {
|
||||
throw _this;
|
||||
}
|
||||
|
||||
@ -148,6 +180,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@Bind("getPolyglotCallType(_this, symbol.getName(), interop)")
|
||||
@ -184,10 +217,10 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
|
||||
@Cached IndirectInvokeFunctionNode invokeFunctionNode) {
|
||||
try {
|
||||
String str = interop.asString(_this);
|
||||
@ -207,7 +240,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
throw new IllegalStateException("Impossible, _this is guaranteed to be a string.");
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,6 +261,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
int thisArgumentPosition,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@Bind("getPolyglotCallType(_this, symbol.getName(), interop)")
|
||||
|
@ -46,20 +46,21 @@ public abstract class InteropConversionCallNode extends Node {
|
||||
InvokeConversionNode buildInvoker(int length) {
|
||||
CallArgumentInfo[] args = buildSchema(length);
|
||||
return InvokeConversionNode.build(
|
||||
args,
|
||||
DefaultsExecutionMode.EXECUTE,
|
||||
ArgumentsExecutionMode.PRE_EXECUTED);
|
||||
args, DefaultsExecutionMode.EXECUTE, ArgumentsExecutionMode.PRE_EXECUTED, 1);
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
return Context.get(this);
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {"!context.isInlineCachingDisabled()", "arguments.length == cachedArgsLength"},
|
||||
guards = {"!getContext().isInlineCachingDisabled()", "arguments.length == cachedArgsLength"},
|
||||
limit = Constants.CacheSizes.FUNCTION_INTEROP_LIBRARY)
|
||||
@ExplodeLoop
|
||||
Object callCached(
|
||||
UnresolvedConversion conversion,
|
||||
Object state,
|
||||
Object[] arguments,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("arguments.length") int cachedArgsLength,
|
||||
@Cached("buildInvoker(cachedArgsLength)") InvokeConversionNode invokerNode,
|
||||
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode)
|
||||
@ -68,7 +69,7 @@ public abstract class InteropConversionCallNode extends Node {
|
||||
for (int i = 0; i < cachedArgsLength; i++) {
|
||||
args[i] = hostValueToEnsoNode.execute(arguments[i]);
|
||||
}
|
||||
if (cachedArgsLength < 2) throw ArityException.create(2, cachedArgsLength);
|
||||
if (cachedArgsLength < 2) throw ArityException.create(2, -1, cachedArgsLength);
|
||||
return invokerNode.execute(null, state, conversion, args[0], args[1], args).getValue();
|
||||
}
|
||||
|
||||
@ -84,7 +85,7 @@ public abstract class InteropConversionCallNode extends Node {
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
args[i] = hostValueToEnsoNode.execute(arguments[i]);
|
||||
}
|
||||
if (arguments.length < 2) throw ArityException.create(2, arguments.length);
|
||||
if (arguments.length < 2) throw ArityException.create(2, -1, arguments.length);
|
||||
return indirectInvokeConversionNode
|
||||
.execute(
|
||||
null,
|
||||
@ -96,7 +97,8 @@ public abstract class InteropConversionCallNode extends Node {
|
||||
buildSchema(arguments.length),
|
||||
DefaultsExecutionMode.EXECUTE,
|
||||
ArgumentsExecutionMode.PRE_EXECUTED,
|
||||
TailStatus.NOT_TAIL)
|
||||
TailStatus.NOT_TAIL,
|
||||
1)
|
||||
.getValue();
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ public abstract class InteropMethodCallNode extends Node {
|
||||
return InvokeMethodNode.build(
|
||||
args,
|
||||
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
|
||||
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
|
||||
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED,
|
||||
0);
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
@ -107,7 +108,8 @@ public abstract class InteropMethodCallNode extends Node {
|
||||
buildSchema(arguments.length),
|
||||
DefaultsExecutionMode.EXECUTE,
|
||||
ArgumentsExecutionMode.PRE_EXECUTED,
|
||||
TailStatus.NOT_TAIL)
|
||||
TailStatus.NOT_TAIL,
|
||||
0)
|
||||
.getValue();
|
||||
}
|
||||
}
|
||||
|
@ -112,9 +112,11 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
this.invokeFunctionNode =
|
||||
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
this.invokeMethodNode =
|
||||
InvokeMethodNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
InvokeMethodNode.build(
|
||||
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition);
|
||||
this.invokeConversionNode =
|
||||
InvokeConversionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
InvokeConversionNode.build(
|
||||
schema, defaultsExecutionMode, argumentsExecutionMode, thatArgumentPosition);
|
||||
}
|
||||
|
||||
public static Integer thisArgumentPosition(CallArgumentInfo[] schema) {
|
||||
@ -143,9 +145,7 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Creates a new instance of this node.
|
||||
*
|
||||
* @param schema a description of the arguments being applied to the callable
|
||||
@ -215,20 +215,22 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
}
|
||||
}
|
||||
Stateful selfResult = thisExecutor.executeThunk(selfArgument, state, TailStatus.NOT_TAIL);
|
||||
Stateful thatResult = thatExecutor.executeThunk(thatArgument, selfResult.getState(), TailStatus.NOT_TAIL);
|
||||
Stateful thatResult =
|
||||
thatExecutor.executeThunk(thatArgument, selfResult.getState(), TailStatus.NOT_TAIL);
|
||||
selfArgument = selfResult.getValue();
|
||||
thatArgument = thatResult.getValue();
|
||||
state = thatResult.getState();
|
||||
arguments[thisArgumentPosition] = selfArgument;
|
||||
arguments[thatArgumentPosition] = thatArgument;
|
||||
}
|
||||
return invokeConversionNode.execute(callerFrame, state, conversion, selfArgument, thatArgument, arguments);
|
||||
return invokeConversionNode.execute(
|
||||
callerFrame, state, conversion, selfArgument, thatArgument, arguments);
|
||||
} else {
|
||||
throw new RuntimeException("Conversion currying without `this` or `that` argument is not supported.");
|
||||
throw new RuntimeException(
|
||||
"Conversion currying without `this` or `that` argument is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Specialization
|
||||
public Stateful invokeDynamicSymbol(
|
||||
UnresolvedSymbol symbol, VirtualFrame callerFrame, Object state, Object[] arguments) {
|
||||
@ -261,8 +263,7 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
@Fallback
|
||||
public Stateful invokeGeneric(
|
||||
Object callable, VirtualFrame callerFrame, Object state, Object[] arguments) {
|
||||
Context ctx = lookupContextReference(Language.class).get();
|
||||
Atom error = ctx.getBuiltins().error().makeNotInvokableError(callable);
|
||||
Atom error = Context.get(this).getBuiltins().error().makeNotInvokableError(callable);
|
||||
throw new PanicException(error, this);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.interpreter.node.callable;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.dsl.*;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
@ -20,19 +21,21 @@ import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.error.*;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
public abstract class InvokeConversionNode extends BaseNode {
|
||||
private @Child InvokeFunctionNode invokeFunctionNode;
|
||||
private @Child InvokeConversionNode childDispatch;
|
||||
private final ConditionProfile atomProfile = ConditionProfile.createCountingProfile();
|
||||
private final ConditionProfile atomConstructorProfile = ConditionProfile.createCountingProfile();
|
||||
private final int thatArgumentPosition;
|
||||
|
||||
/**
|
||||
* Creates a new node for method invocation.
|
||||
@ -45,16 +48,20 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
public static InvokeConversionNode build(
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
return InvokeConversionNodeGen.create(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
int thatArgumentPosition) {
|
||||
return InvokeConversionNodeGen.create(
|
||||
schema, defaultsExecutionMode, argumentsExecutionMode, thatArgumentPosition);
|
||||
}
|
||||
|
||||
InvokeConversionNode(
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
int thatArgumentPosition) {
|
||||
this.invokeFunctionNode =
|
||||
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
this.thatArgumentPosition = thatArgumentPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,7 +81,6 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
static AtomConstructor extractConstructor(
|
||||
Node thisNode,
|
||||
Object _this,
|
||||
TruffleLanguage.ContextReference<Context> ctx,
|
||||
ConditionProfile atomConstructorProfile,
|
||||
ConditionProfile atomProfile) {
|
||||
if (atomConstructorProfile.profile(_this instanceof AtomConstructor)) {
|
||||
@ -83,12 +89,13 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
return ((Atom) _this).getConstructor();
|
||||
} else {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeInvalidConversionTargetError(_this), thisNode);
|
||||
Context.get(thisNode).getBuiltins().error().makeInvalidConversionTargetError(_this),
|
||||
thisNode);
|
||||
}
|
||||
}
|
||||
|
||||
AtomConstructor extractConstructor(Object _this, TruffleLanguage.ContextReference<Context> ctx) {
|
||||
return extractConstructor(this, _this, ctx, atomConstructorProfile, atomProfile);
|
||||
AtomConstructor extractConstructor(Object _this) {
|
||||
return extractConstructor(this, _this, atomConstructorProfile, atomProfile);
|
||||
}
|
||||
|
||||
@Specialization(guards = "dispatch.canConvertFrom(that)")
|
||||
@ -99,15 +106,18 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch) {
|
||||
try {
|
||||
Function function =
|
||||
dispatch.getConversionFunction(that, extractConstructor(_this, ctx), conversion);
|
||||
dispatch.getConversionFunction(that, extractConstructor(_this), conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
Context.get(this)
|
||||
.getBuiltins()
|
||||
.error()
|
||||
.makeNoSuchConversionError(_this, that, conversion),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,11 +130,10 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
DataflowError that,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@Cached BranchProfile profile,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
@Cached BranchProfile profile) {
|
||||
try {
|
||||
Function function =
|
||||
dispatch.getConversionFunction(that, extractConstructor(_this, ctx), conversion);
|
||||
dispatch.getConversionFunction(that, extractConstructor(_this), conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
profile.enter();
|
||||
@ -134,15 +143,51 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
|
||||
@Specialization
|
||||
Stateful doPanicSentinel(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
PanicSentinel that,
|
||||
Object[] arguments) {
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
PanicSentinel that,
|
||||
Object[] arguments) {
|
||||
throw that;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doWarning(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
WithWarnings that,
|
||||
Object[] arguments) {
|
||||
// Cannot use @Cached for childDispatch, because we need to call notifyInserted.
|
||||
if (childDispatch == null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
Lock lock = getLock();
|
||||
lock.lock();
|
||||
try {
|
||||
if (childDispatch == null) {
|
||||
childDispatch =
|
||||
insert(
|
||||
build(
|
||||
invokeFunctionNode.getSchema(),
|
||||
invokeFunctionNode.getDefaultsExecutionMode(),
|
||||
invokeFunctionNode.getArgumentsExecutionMode(),
|
||||
thatArgumentPosition));
|
||||
childDispatch.setTailStatus(getTailStatus());
|
||||
notifyInserted(childDispatch);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
arguments[thatArgumentPosition] = that.getValue();
|
||||
ArrayRope<Warning> warnings = that.getReassignedWarnings(this);
|
||||
Stateful result =
|
||||
childDispatch.execute(frame, state, conversion, _this, that.getValue(), arguments);
|
||||
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
|
||||
}
|
||||
|
||||
@Specialization(guards = "interop.isString(that)")
|
||||
Stateful doConvertText(
|
||||
VirtualFrame frame,
|
||||
@ -153,20 +198,23 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
try {
|
||||
String str = interop.asString(that);
|
||||
Text txt = Text.create(str);
|
||||
Function function =
|
||||
textDispatch.getConversionFunction(txt, extractConstructor(_this, ctx), conversion);
|
||||
textDispatch.getConversionFunction(txt, extractConstructor(_this), conversion);
|
||||
arguments[0] = txt;
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (UnsupportedMessageException e) {
|
||||
throw new IllegalStateException("Impossible, that is guaranteed to be a string.");
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
Context.get(this)
|
||||
.getBuiltins()
|
||||
.error()
|
||||
.makeNoSuchConversionError(_this, that, conversion),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,10 +232,10 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@CachedContext(Language.class) Context ctx) {
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
throw new PanicException(
|
||||
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
Context.get(this).getBuiltins().error().makeNoSuchConversionError(_this, that, conversion),
|
||||
this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.interpreter.node.callable;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.dsl.*;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
@ -12,6 +13,8 @@ import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
|
||||
@ -21,10 +24,9 @@ import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.*;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@ -32,7 +34,10 @@ import org.enso.interpreter.runtime.state.Stateful;
|
||||
public abstract class InvokeMethodNode extends BaseNode {
|
||||
private @Child InvokeFunctionNode invokeFunctionNode;
|
||||
private final ConditionProfile errorReceiverProfile = ConditionProfile.createCountingProfile();
|
||||
private final BranchProfile polyglotArgumentErrorProfile = BranchProfile.create();
|
||||
private @Child InvokeMethodNode childDispatch;
|
||||
private final int argumentCount;
|
||||
private final int thisArgumentPosition;
|
||||
|
||||
/**
|
||||
* Creates a new node for method invocation.
|
||||
@ -45,23 +50,30 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
public static InvokeMethodNode build(
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
return InvokeMethodNodeGen.create(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
int thisArgumentPosition) {
|
||||
return InvokeMethodNodeGen.create(
|
||||
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition);
|
||||
}
|
||||
|
||||
InvokeMethodNode(
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
int thisArgumentPosition) {
|
||||
this.invokeFunctionNode =
|
||||
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
this.argumentCount = schema.length;
|
||||
this.thisArgumentPosition = thisArgumentPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTailStatus(TailStatus tailStatus) {
|
||||
super.setTailStatus(tailStatus);
|
||||
this.invokeFunctionNode.setTailStatus(tailStatus);
|
||||
if (childDispatch != null) {
|
||||
childDispatch.setTailStatus(tailStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Stateful execute(
|
||||
@ -74,14 +86,13 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
UnresolvedSymbol symbol,
|
||||
Object _this,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch) {
|
||||
try {
|
||||
Function function = dispatch.getFunctionalDispatch(_this, symbol);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,6 +122,41 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
throw _this;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doWarning(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedSymbol symbol,
|
||||
WithWarnings _this,
|
||||
Object[] arguments) {
|
||||
// Cannot use @Cached for childDispatch, because we need to call notifyInserted.
|
||||
if (childDispatch == null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
Lock lock = getLock();
|
||||
lock.lock();
|
||||
try {
|
||||
if (childDispatch == null) {
|
||||
childDispatch =
|
||||
insert(
|
||||
build(
|
||||
invokeFunctionNode.getSchema(),
|
||||
invokeFunctionNode.getDefaultsExecutionMode(),
|
||||
invokeFunctionNode.getArgumentsExecutionMode(),
|
||||
thisArgumentPosition));
|
||||
childDispatch.setTailStatus(getTailStatus());
|
||||
notifyInserted(childDispatch);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
arguments[thisArgumentPosition] = _this.getValue();
|
||||
ArrayRope<Warning> warnings = _this.getReassignedWarnings(this);
|
||||
Stateful result = childDispatch.execute(frame, state, symbol, _this.getValue(), arguments);
|
||||
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
@Specialization(
|
||||
guards = {
|
||||
@ -131,19 +177,33 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
HostMethodCallNode.PolyglotCallType polyglotCallType,
|
||||
@Cached(value = "buildExecutors()") ThunkExecutorNode[] argExecutors,
|
||||
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] profiles,
|
||||
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] warningProfiles,
|
||||
@Cached BranchProfile anyWarningsProfile,
|
||||
@Cached HostMethodCallNode hostMethodCallNode) {
|
||||
Object[] args = new Object[argExecutors.length];
|
||||
boolean anyWarnings = false;
|
||||
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>();
|
||||
for (int i = 0; i < argExecutors.length; i++) {
|
||||
Stateful r = argExecutors[i].executeThunk(arguments[i + 1], state, TailStatus.NOT_TAIL);
|
||||
state = r.getState();
|
||||
args[i] = r.getValue();
|
||||
if (r.getValue() instanceof DataflowError) {
|
||||
profiles[i].enter();
|
||||
return r;
|
||||
} else if (r.getValue() instanceof WithWarnings) {
|
||||
warningProfiles[i].enter();
|
||||
anyWarnings = true;
|
||||
accumulatedWarnings =
|
||||
accumulatedWarnings.append(((WithWarnings) r.getValue()).getReassignedWarnings(this));
|
||||
args[i] = ((WithWarnings) r.getValue()).getValue();
|
||||
}
|
||||
state = r.getState();
|
||||
args[i] = r.getValue();
|
||||
}
|
||||
return new Stateful(
|
||||
state, hostMethodCallNode.execute(polyglotCallType, symbol.getName(), _this, args));
|
||||
Object res = hostMethodCallNode.execute(polyglotCallType, symbol.getName(), _this, args);
|
||||
if (anyWarnings) {
|
||||
anyWarningsProfile.enter();
|
||||
res = WithWarnings.prependTo(res, accumulatedWarnings);
|
||||
}
|
||||
return new Stateful(state, res);
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
@ -160,8 +220,7 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
try {
|
||||
String str = interop.asString(_this);
|
||||
Text txt = Text.create(str);
|
||||
@ -172,7 +231,7 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
throw new IllegalStateException("Impossible, _this is guaranteed to be a string.");
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
package org.enso.interpreter.node.callable.argument;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
import org.enso.interpreter.runtime.error.WithWarnings;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GatherWarningsNode extends BaseNode {
|
||||
private final int argsCount;
|
||||
private final @CompilerDirectives.CompilationFinal(dimensions = 1) ConditionProfile[] profiles;
|
||||
|
||||
private GatherWarningsNode(int argsCount) {
|
||||
this.argsCount = argsCount;
|
||||
this.profiles =
|
||||
Stream.generate(ConditionProfile::createCountingProfile)
|
||||
.limit(argsCount)
|
||||
.toArray(ConditionProfile[]::new);
|
||||
}
|
||||
|
||||
public static GatherWarningsNode create(int argsCount) {
|
||||
return new GatherWarningsNode(argsCount);
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
public ArrayRope<Warning> execute(Object... arguments) {
|
||||
ArrayRope<Warning> result = new ArrayRope<>();
|
||||
boolean anyFound = false;
|
||||
for (int i = 0; i < argsCount; i++) {
|
||||
if (profiles[i].profile(arguments[i] instanceof WithWarnings)) {
|
||||
anyFound = true;
|
||||
result = result.append(((WithWarnings) arguments[i]).getWarnings());
|
||||
}
|
||||
}
|
||||
if (anyFound) {
|
||||
return result;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -160,15 +160,15 @@ public abstract class InvokeFunctionNode extends BaseNode {
|
||||
public abstract Stateful execute(
|
||||
Function callable, VirtualFrame callerFrame, Object state, Object[] arguments);
|
||||
|
||||
CallArgumentInfo[] getSchema() {
|
||||
public CallArgumentInfo[] getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
InvokeCallableNode.DefaultsExecutionMode getDefaultsExecutionMode() {
|
||||
public InvokeCallableNode.DefaultsExecutionMode getDefaultsExecutionMode() {
|
||||
return this.defaultsExecutionMode;
|
||||
}
|
||||
|
||||
InvokeCallableNode.ArgumentsExecutionMode getArgumentsExecutionMode() {
|
||||
public InvokeCallableNode.ArgumentsExecutionMode getArgumentsExecutionMode() {
|
||||
return argumentsExecutionMode;
|
||||
}
|
||||
|
||||
@ -187,4 +187,5 @@ public abstract class InvokeFunctionNode extends BaseNode {
|
||||
public void setId(UUID id) {
|
||||
functionCallInstrumentationNode.setId(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,9 +12,8 @@ import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.error.*;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/**
|
||||
@ -72,6 +71,13 @@ public abstract class CaseNode extends ExpressionNode {
|
||||
throw sentinel;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Object doWarning(VirtualFrame frame, WithWarnings object) {
|
||||
ArrayRope<Warning> warnings = object.getReassignedWarnings(this);
|
||||
Object result = doMatch(frame, object.getValue());
|
||||
return WithWarnings.appendTo(result, warnings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the case expression.
|
||||
*
|
||||
@ -80,12 +86,10 @@ public abstract class CaseNode extends ExpressionNode {
|
||||
* @param ctx the language context reference
|
||||
* @return the result of executing the case expression on {@code object}
|
||||
*/
|
||||
@Specialization(guards = {"!isDataflowError(object)", "!isPanicSentinel(object)"})
|
||||
@Specialization(
|
||||
guards = {"!isDataflowError(object)", "!isPanicSentinel(object)", "!isWarning(object)"})
|
||||
@ExplodeLoop
|
||||
public Object doMatch(
|
||||
VirtualFrame frame,
|
||||
Object object,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
public Object doMatch(VirtualFrame frame, Object object) {
|
||||
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
|
||||
try {
|
||||
for (BranchNode branchNode : cases) {
|
||||
@ -93,7 +97,11 @@ public abstract class CaseNode extends ExpressionNode {
|
||||
}
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().inexhaustivePatternMatchError().newInstance(object),
|
||||
Context.get(this)
|
||||
.getBuiltins()
|
||||
.error()
|
||||
.inexhaustivePatternMatchError()
|
||||
.newInstance(object),
|
||||
this);
|
||||
} catch (BranchSelectedException e) {
|
||||
// Note [Branch Selection Control Flow]
|
||||
@ -110,6 +118,10 @@ public abstract class CaseNode extends ExpressionNode {
|
||||
return TypesGen.isPanicSentinel(sentinel);
|
||||
}
|
||||
|
||||
boolean isWarning(Object warning) {
|
||||
return warning instanceof WithWarnings;
|
||||
}
|
||||
|
||||
/* Note [Branch Selection Control Flow]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* Truffle provides no easy way to return control flow from multiple paths. This is entirely due
|
||||
|
@ -7,7 +7,11 @@ import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
import org.enso.interpreter.runtime.error.WithWarnings;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/**
|
||||
@ -19,16 +23,20 @@ public class InstantiateNode extends ExpressionNode {
|
||||
private final AtomConstructor constructor;
|
||||
private @Children ExpressionNode[] arguments;
|
||||
private @CompilationFinal(dimensions = 1) ConditionProfile[] profiles;
|
||||
private @CompilationFinal(dimensions = 1) ConditionProfile[] warningProfiles;
|
||||
private @CompilationFinal(dimensions = 1) BranchProfile[] sentinelProfiles;
|
||||
private final ConditionProfile anyWarningsProfile = ConditionProfile.createCountingProfile();
|
||||
|
||||
InstantiateNode(AtomConstructor constructor, ExpressionNode[] arguments) {
|
||||
this.constructor = constructor;
|
||||
this.arguments = arguments;
|
||||
this.profiles = new ConditionProfile[arguments.length];
|
||||
this.sentinelProfiles = new BranchProfile[arguments.length];
|
||||
this.warningProfiles = new ConditionProfile[arguments.length];
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
this.profiles[i] = ConditionProfile.createCountingProfile();
|
||||
this.sentinelProfiles[i] = BranchProfile.create();
|
||||
this.warningProfiles[i] = ConditionProfile.createCountingProfile();
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,12 +62,21 @@ public class InstantiateNode extends ExpressionNode {
|
||||
@ExplodeLoop
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
Object[] argumentValues = new Object[arguments.length];
|
||||
boolean anyWarnings = false;
|
||||
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
ConditionProfile profile = profiles[i];
|
||||
ConditionProfile warningProfile = warningProfiles[i];
|
||||
BranchProfile sentinelProfile = sentinelProfiles[i];
|
||||
Object argument = arguments[i].executeGeneric(frame);
|
||||
if (profile.profile(TypesGen.isDataflowError(argument))) {
|
||||
return argument;
|
||||
} else if (warningProfile.profile(argument instanceof WithWarnings)) {
|
||||
anyWarnings = true;
|
||||
WithWarnings originalArg = (WithWarnings) argument;
|
||||
accumulatedWarnings =
|
||||
accumulatedWarnings.append(originalArg.getReassignedWarnings(this));
|
||||
argumentValues[i] = originalArg.getValue();
|
||||
} else if (TypesGen.isPanicSentinel(argument)) {
|
||||
sentinelProfile.enter();
|
||||
throw TypesGen.asPanicSentinel(argument);
|
||||
@ -67,6 +84,10 @@ public class InstantiateNode extends ExpressionNode {
|
||||
argumentValues[i] = argument;
|
||||
}
|
||||
}
|
||||
return constructor.newInstance(argumentValues);
|
||||
if (anyWarningsProfile.profile(anyWarnings)) {
|
||||
return WithWarnings.appendTo(constructor.newInstance(argumentValues), accumulatedWarnings);
|
||||
} else {
|
||||
return constructor.newInstance(argumentValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public class InstantiateAtomNode extends RootNode {
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "constructor::" + name;
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ import com.oracle.truffle.api.dsl.ReportPolymorphism;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.dsl.AcceptsWarning;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
@ -24,7 +25,8 @@ public abstract class PutStateNode extends Node {
|
||||
return PutStateNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(@MonadicState Object state, Object _this, Object key, Object new_state);
|
||||
abstract Stateful execute(
|
||||
@MonadicState Object state, Object _this, Object key, Object new_state);
|
||||
|
||||
@Specialization(guards = "state.getKey() == key")
|
||||
Stateful doExistingSingleton(SingletonMap state, Object _this, Object key, Object new_state) {
|
||||
|
@ -0,0 +1,34 @@
|
||||
package org.enso.interpreter.node.expression.builtin.warning;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.AcceptsWarning;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
import org.enso.interpreter.runtime.error.WithWarnings;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Prim_Warning",
|
||||
name = "attach",
|
||||
description = "Attaches the given warning to the value.")
|
||||
public abstract class AttachWarningNode extends Node {
|
||||
abstract WithWarnings execute(
|
||||
Object _this, @AcceptsWarning Object value, Object warning, Object origin);
|
||||
|
||||
static AttachWarningNode build() {
|
||||
return AttachWarningNodeGen.create();
|
||||
}
|
||||
|
||||
@Specialization
|
||||
WithWarnings doWarning(Object _this, WithWarnings value, Object warning, Object origin) {
|
||||
return value.prepend(new Warning(warning, origin, Context.get(this).clockTick()));
|
||||
}
|
||||
|
||||
@Fallback
|
||||
WithWarnings doOther(Object _this, Object value, Object warning, Object origin) {
|
||||
return new WithWarnings(value, new Warning(warning, origin, Context.get(this).clockTick()));
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.enso.interpreter.node.expression.builtin.warning;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Prim_Warning",
|
||||
name = "get_origin",
|
||||
description = "Gets the payload of the warning.")
|
||||
public class GetOriginNode extends Node {
|
||||
Object execute(Object _this, Warning warning) {
|
||||
return warning.getOrigin();
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.enso.interpreter.node.expression.builtin.warning;
|
||||
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Prim_Warning",
|
||||
name = "get_reassignments",
|
||||
description = "Gets the list of locations where the warnings was reassigned.")
|
||||
public class GetReassignmentsNode extends Node {
|
||||
Array execute(Object _this, Warning warning) {
|
||||
Warning.Reassignment[] reassignments =
|
||||
warning.getReassignments().toArray(Warning.Reassignment[]::new);
|
||||
return new Array(Arrays.copyOf(reassignments, reassignments.length, Object[].class));
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.enso.interpreter.node.expression.builtin.warning;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Prim_Warning",
|
||||
name = "get_value",
|
||||
description = "Gets the payload of the warning.")
|
||||
public class GetValueNode extends Node {
|
||||
Object execute(Object _this, Warning warning) {
|
||||
return warning.getValue();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package org.enso.interpreter.node.expression.builtin.warning;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.AcceptsWarning;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
import org.enso.interpreter.runtime.error.WithWarnings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Prim_Warning",
|
||||
name = "get_all",
|
||||
description = "Gets all the warnings associated with the value.")
|
||||
public abstract class GetWarningsNode extends Node {
|
||||
abstract Array execute(Object _this, @AcceptsWarning Object value);
|
||||
|
||||
static GetWarningsNode build() {
|
||||
return GetWarningsNodeGen.create();
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Array doWarning(Object _this, WithWarnings value) {
|
||||
Warning[] warnings = value.getWarningsArray();
|
||||
Arrays.sort(warnings, Comparator.comparing(Warning::getCreationTime).reversed());
|
||||
Object[] result = new Object[warnings.length];
|
||||
System.arraycopy(warnings, 0, result, 0, warnings.length);
|
||||
return new Array(result);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
Array doOther(Object _this, Object value) {
|
||||
return new Array();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package org.enso.interpreter.node.expression.builtin.warning;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.AcceptsWarning;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
import org.enso.interpreter.runtime.error.WithWarnings;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Prim_Warning",
|
||||
name = "set",
|
||||
description = "Attaches the given warning to the value.")
|
||||
public abstract class SetNode extends Node {
|
||||
abstract Object execute(Object _this, @AcceptsWarning Object value, Array warnings);
|
||||
|
||||
static SetNode build() {
|
||||
return SetNodeGen.create();
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Object doWarning(Object _this, WithWarnings value, Array warnings) {
|
||||
if (warnings.length() == 0) {
|
||||
return value.getValue();
|
||||
}
|
||||
Warning[] warningsCast = new Warning[warnings.length()];
|
||||
System.arraycopy(warnings.getItems(), 0, warningsCast, 0, warningsCast.length);
|
||||
return new WithWarnings(value.getValue(), warningsCast);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
Object doOther(Object _this, Object value, Array warnings) {
|
||||
if (warnings.length() == 0) {
|
||||
return value;
|
||||
}
|
||||
Warning[] warningsCast = new Warning[warnings.length()];
|
||||
System.arraycopy(warnings.getItems(), 0, warningsCast, 0, warningsCast.length);
|
||||
return new WithWarnings(value, warningsCast);
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import scala.jdk.javaapi.OptionConverters;
|
||||
import java.io.*;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* The language context is the internal state of the language that is associated with each thread in
|
||||
@ -62,6 +63,7 @@ public class Context {
|
||||
private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID, Context.class);
|
||||
private final DistributionManager distributionManager;
|
||||
private final LockManager lockManager;
|
||||
private final AtomicLong clock = new AtomicLong();
|
||||
|
||||
/**
|
||||
* Creates a new Enso context.
|
||||
@ -151,6 +153,10 @@ public class Context {
|
||||
return REFERENCE.get(node);
|
||||
}
|
||||
|
||||
public static TruffleLanguage.ContextReference<Context> getReference() {
|
||||
return REFERENCE;
|
||||
}
|
||||
|
||||
/** Performs eventual cleanup before the context is disposed of. */
|
||||
public void shutdown() {
|
||||
threadManager.shutdown();
|
||||
@ -449,4 +455,8 @@ public class Context {
|
||||
public TruffleLogger getLogger(Class<?> klass) {
|
||||
return TruffleLogger.getLogger(LanguageInfo.ID, klass);
|
||||
}
|
||||
|
||||
public long clockTick() {
|
||||
return clock.getAndIncrement();
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ public class Builtins {
|
||||
bool = new Bool(language, scope);
|
||||
debug = new AtomConstructor("Debug", scope).initializeFields();
|
||||
dataflowError = new DataflowError(language, scope);
|
||||
Warning.initWarningMethods(language, scope);
|
||||
projectDescription =
|
||||
new AtomConstructor("Project_Description", scope)
|
||||
.initializeFields(
|
||||
|
@ -0,0 +1,21 @@
|
||||
package org.enso.interpreter.runtime.builtin;
|
||||
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.warning.*;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
public class Warning {
|
||||
public static void initWarningMethods(Language language, ModuleScope scope) {
|
||||
var warning = new AtomConstructor("Prim_Warning", scope).initializeFields();
|
||||
|
||||
scope.registerConstructor(warning);
|
||||
scope.registerMethod(warning, "get_all", GetWarningsMethodGen.makeFunction(language));
|
||||
scope.registerMethod(warning, "attach", AttachWarningMethodGen.makeFunction(language));
|
||||
scope.registerMethod(warning, "get_value", GetValueMethodGen.makeFunction(language));
|
||||
scope.registerMethod(warning, "get_origin", GetOriginMethodGen.makeFunction(language));
|
||||
scope.registerMethod(
|
||||
warning, "get_reassignments", GetReassignmentsMethodGen.makeFunction(language));
|
||||
scope.registerMethod(warning, "set", SetMethodGen.makeFunction(language));
|
||||
}
|
||||
}
|
@ -82,7 +82,9 @@ public final class AtomConstructor implements TruffleObject {
|
||||
argumentReaders[i] = ReadArgumentNode.build(i, args[i].getDefaultValue().orElse(null));
|
||||
}
|
||||
ExpressionNode instantiateNode = InstantiateNode.build(this, argumentReaders);
|
||||
RootNode rootNode = InstantiateAtomNode.build(null, name, instantiateNode);
|
||||
RootNode rootNode =
|
||||
InstantiateAtomNode.build(
|
||||
null, definitionScope.getModule().getName().item() + "." + name, instantiateNode);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
|
||||
return new Function(callTarget, null, new FunctionSchema(args));
|
||||
}
|
||||
@ -303,7 +305,8 @@ public final class AtomConstructor implements TruffleObject {
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("_this") AtomConstructor cachedConstructor,
|
||||
@Cached("doResolve(context, cachedConstructor, cachedTarget, cachedConversion)") Function function) {
|
||||
@Cached("doResolve(context, cachedConstructor, cachedTarget, cachedConversion)")
|
||||
Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,121 @@
|
||||
package org.enso.interpreter.runtime.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class ArrayRope<T> {
|
||||
private final ArrayRopeSegment<T> segment;
|
||||
|
||||
private ArrayRope(ArrayRopeSegment<T> segment) {
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public ArrayRope(T... elements) {
|
||||
segment = new ArraySegment<>(elements);
|
||||
}
|
||||
|
||||
public ArrayRope<T> append(ArrayRope<T> that) {
|
||||
return new ArrayRope<>(new ConcatSegment<>(this.segment, that.segment));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final ArrayRope<T> append(T... items) {
|
||||
return new ArrayRope<>(new ConcatSegment<>(this.segment, new ArraySegment<>(items)));
|
||||
}
|
||||
|
||||
public ArrayRope<T> prepend(ArrayRope<T> that) {
|
||||
return new ArrayRope<>(new ConcatSegment<>(that.segment, this.segment));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final ArrayRope<T> prepend(T... items) {
|
||||
return new ArrayRope<>(new ConcatSegment<>(new ArraySegment<>(items), this.segment));
|
||||
}
|
||||
|
||||
public T[] toArray(Function<Integer, T[]> genArray) {
|
||||
T[] res = genArray.apply(size());
|
||||
writeArray(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void writeArray(T[] arr) {
|
||||
segment.appendTo(arr, 0);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return segment.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArrayRope{" + "segment=" + segment + '}';
|
||||
}
|
||||
|
||||
private interface ArrayRopeSegment<T> {
|
||||
void appendTo(T[] builder, int start);
|
||||
|
||||
int size();
|
||||
}
|
||||
|
||||
private static final class ArraySegment<T> implements ArrayRopeSegment<T> {
|
||||
private final T[] elements;
|
||||
|
||||
public ArraySegment(T[] elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(T[] builder, int start) {
|
||||
System.arraycopy(elements, 0, builder, start, elements.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return elements.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArraySegment{" + "elements=" + Arrays.toString(elements) + '}';
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ConcatSegment<T> implements ArrayRopeSegment<T> {
|
||||
private final ArrayRopeSegment<T> left;
|
||||
private final ArrayRopeSegment<T> right;
|
||||
private int cachedSize = UNKNOWN;
|
||||
private static final int UNKNOWN = -1;
|
||||
|
||||
public ConcatSegment(ArrayRopeSegment<T> left, ArrayRopeSegment<T> right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(T[] builder, int start) {
|
||||
left.appendTo(builder, start);
|
||||
right.appendTo(builder, start + left.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
if (cachedSize == UNKNOWN) {
|
||||
cachedSize = left.size() + right.size();
|
||||
}
|
||||
return cachedSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConcatSegment{"
|
||||
+ "left="
|
||||
+ left
|
||||
+ ", right="
|
||||
+ right
|
||||
+ ", cachedSize="
|
||||
+ cachedSize
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package org.enso.interpreter.runtime.error;
|
||||
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.data.EnsoSourceSection;
|
||||
|
||||
public class Warning implements TruffleObject {
|
||||
private final Object value;
|
||||
private final Object origin;
|
||||
private final ArrayRope<Reassignment> reassignments;
|
||||
private final long creationTime;
|
||||
|
||||
public Warning(Object value, Object origin, long creationTime) {
|
||||
this(value, origin, creationTime, new ArrayRope<>());
|
||||
}
|
||||
|
||||
public Warning(
|
||||
Object value, Object origin, long creationTime, ArrayRope<Reassignment> reassignments) {
|
||||
this.value = value;
|
||||
this.origin = origin;
|
||||
this.reassignments = reassignments;
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
public static class Reassignment implements TruffleObject {
|
||||
private final String methodName;
|
||||
private final SourceSection location;
|
||||
|
||||
public Reassignment(String methodName, SourceSection location) {
|
||||
this.methodName = methodName;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasExecutableName() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
String getExecutableName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasSourceLocation() {
|
||||
return location != null;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
SourceSection getSourceLocation() {
|
||||
return location;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public long getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
public ArrayRope<Reassignment> getReassignments() {
|
||||
return reassignments;
|
||||
}
|
||||
|
||||
public Warning reassign(Node location) {
|
||||
RootNode root = location.getRootNode();
|
||||
SourceSection section = location.getEncapsulatingSourceSection();
|
||||
Reassignment reassignment = new Reassignment(root.getName(), section);
|
||||
return new Warning(value, origin, creationTime, reassignments.prepend(reassignment));
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package org.enso.interpreter.runtime.error;
|
||||
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
@ExportLibrary(MethodDispatchLibrary.class)
|
||||
public class WithWarnings implements TruffleObject {
|
||||
private final ArrayRope<Warning> warnings;
|
||||
private final Object value;
|
||||
|
||||
public WithWarnings(Object value, Warning... warnings) {
|
||||
this.warnings = new ArrayRope<>(warnings);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private WithWarnings(Object value, ArrayRope<Warning> warnings) {
|
||||
this.warnings = warnings;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public WithWarnings append(Warning... newWarnings) {
|
||||
return new WithWarnings(value, warnings.append(newWarnings));
|
||||
}
|
||||
|
||||
public WithWarnings append(ArrayRope<Warning> newWarnings) {
|
||||
return new WithWarnings(value, warnings.append(newWarnings));
|
||||
}
|
||||
|
||||
public WithWarnings prepend(Warning... newWarnings) {
|
||||
return new WithWarnings(value, warnings.prepend(newWarnings));
|
||||
}
|
||||
|
||||
public WithWarnings prepend(ArrayRope<Warning> newWarnings) {
|
||||
return new WithWarnings(value, warnings.prepend(newWarnings));
|
||||
}
|
||||
|
||||
public Warning[] getWarningsArray() {
|
||||
return warnings.toArray(Warning[]::new);
|
||||
}
|
||||
|
||||
public ArrayRope<Warning> getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
public ArrayRope<Warning> getReassignedWarnings(Node location) {
|
||||
Warning[] warnings = getWarningsArray();
|
||||
for (int i = 0; i < warnings.length; i++) {
|
||||
warnings[i] = warnings[i].reassign(location);
|
||||
}
|
||||
return new ArrayRope<>(warnings);
|
||||
}
|
||||
|
||||
public static WithWarnings appendTo(Object target, ArrayRope<Warning> warnings) {
|
||||
if (target instanceof WithWarnings) {
|
||||
return ((WithWarnings) target).append(warnings);
|
||||
} else {
|
||||
return new WithWarnings(target, warnings);
|
||||
}
|
||||
}
|
||||
|
||||
public static WithWarnings prependTo(Object target, ArrayRope<Warning> warnings) {
|
||||
if (target instanceof WithWarnings) {
|
||||
return ((WithWarnings) target).prepend(warnings);
|
||||
} else {
|
||||
return new WithWarnings(target, warnings);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasSpecialDispatch() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
import org.enso.interpreter.runtime.number.EnsoBigInteger;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
import org.enso.polyglot.data.TypeGraph;
|
||||
@ -49,7 +50,8 @@ import org.enso.polyglot.data.TypeGraph;
|
||||
ModuleScope.class,
|
||||
Ref.class,
|
||||
PanicException.class,
|
||||
PanicSentinel.class
|
||||
PanicSentinel.class,
|
||||
Warning.class
|
||||
})
|
||||
public class Types {
|
||||
|
||||
|
@ -243,6 +243,30 @@ type Error
|
||||
to_text : Text
|
||||
to_text = @Builtin_Method "Error.to_text"
|
||||
|
||||
@Builtin_Type
|
||||
type Prim_Warning
|
||||
type Prim_Warning
|
||||
|
||||
## PRIVATE
|
||||
attach : Any -> Any -> Any
|
||||
attach value warning = @Builtin_Method "Prim_Warning.attach"
|
||||
|
||||
## PRIVATE
|
||||
get_all : Any -> Array Prim_Warning
|
||||
get_all value = @Builtin_Method "Prim_Warning.get_all"
|
||||
|
||||
## PRIVATE
|
||||
get_origin : Prim_Warning -> Any
|
||||
get_origin warn = @Builtin_Method "Prim_Warning.get_origin"
|
||||
|
||||
## PRIVATE
|
||||
get_value : Prim_Warning -> Any
|
||||
get_value warn = @Builtin_Method "Prim_Warning.get_value"
|
||||
|
||||
## PRIVATE
|
||||
get_reassignments : Prim_Warning -> Any
|
||||
get_reassignments warn = @Builtin_Method "Prim_Warning.get_reassignments"
|
||||
|
||||
## The runtime representation of a syntax error.
|
||||
|
||||
Arguments:
|
||||
|
@ -0,0 +1,11 @@
|
||||
package org.enso.interpreter.dsl;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** An interface marking an argument as allowing it to accept a WithWarnings. */
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface AcceptsWarning {}
|
@ -14,6 +14,7 @@ import javax.tools.JavaFileObject;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** The processor used to generate code from the {@link BuiltinMethod} annotation. */
|
||||
@SupportedAnnotationTypes("org.enso.interpreter.dsl.BuiltinMethod")
|
||||
@ -82,7 +83,11 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
"org.enso.interpreter.runtime.callable.argument.ArgumentDefinition",
|
||||
"org.enso.interpreter.runtime.callable.function.Function",
|
||||
"org.enso.interpreter.runtime.callable.function.FunctionSchema",
|
||||
"org.enso.interpreter.runtime.Context",
|
||||
"org.enso.interpreter.runtime.data.ArrayRope",
|
||||
"org.enso.interpreter.runtime.error.PanicException",
|
||||
"org.enso.interpreter.runtime.error.Warning",
|
||||
"org.enso.interpreter.runtime.error.WithWarnings",
|
||||
"org.enso.interpreter.runtime.state.Stateful",
|
||||
"org.enso.interpreter.runtime.type.TypesGen");
|
||||
|
||||
@ -111,15 +116,21 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
|
||||
for (MethodDefinition.ArgumentDefinition arg : methodDefinition.getArguments()) {
|
||||
if (!arg.isState() && !arg.isFrame() && !arg.isCallerInfo()) {
|
||||
String condName = "arg" + arg.getPosition() + "ConditionProfile";
|
||||
String branchName = "arg" + arg.getPosition() + "BranchProfile";
|
||||
String condName = mkArgumentInternalVarName(arg) + "ConditionProfile";
|
||||
String branchName = mkArgumentInternalVarName(arg) + "BranchProfile";
|
||||
out.println(
|
||||
" private final ConditionProfile "
|
||||
+ condName
|
||||
+ " = ConditionProfile.createCountingProfile();");
|
||||
out.println(" private final BranchProfile " + branchName + " = BranchProfile.create();");
|
||||
if (!arg.isThis() && !arg.acceptsWarning()) {
|
||||
String warningName = mkArgumentInternalVarName(arg) + "WarningProfile";
|
||||
out.println(
|
||||
" private final BranchProfile " + warningName + " = BranchProfile.create();");
|
||||
}
|
||||
}
|
||||
}
|
||||
out.println(" private final BranchProfile anyWarningsProfile = BranchProfile.create();");
|
||||
|
||||
out.println(" private " + methodDefinition.getClassName() + "(Language language) {");
|
||||
out.println(" super(language);");
|
||||
@ -169,6 +180,8 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
out.println(
|
||||
" Object[] arguments = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());");
|
||||
List<String> callArgNames = new ArrayList<>();
|
||||
boolean warningsPossible =
|
||||
generateWarningsCheck(out, methodDefinition.getArguments(), "arguments");
|
||||
for (MethodDefinition.ArgumentDefinition argumentDefinition :
|
||||
methodDefinition.getArguments()) {
|
||||
if (argumentDefinition.isState()) {
|
||||
@ -178,15 +191,37 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
} else if (argumentDefinition.isCallerInfo()) {
|
||||
callArgNames.add("callerInfo");
|
||||
} else {
|
||||
callArgNames.add("arg" + argumentDefinition.getPosition());
|
||||
callArgNames.add(mkArgumentInternalVarName(argumentDefinition));
|
||||
generateArgumentRead(out, argumentDefinition, "arguments");
|
||||
}
|
||||
}
|
||||
String executeCall = "bodyNode.execute(" + String.join(", ", callArgNames) + ")";
|
||||
if (methodDefinition.modifiesState()) {
|
||||
out.println(" return " + executeCall + ";");
|
||||
if (warningsPossible) {
|
||||
out.println(" if (anyWarnings) {");
|
||||
out.println(" anyWarningsProfile.enter();");
|
||||
if (methodDefinition.modifiesState()) {
|
||||
out.println(" Stateful result = " + executeCall + ";");
|
||||
out.println(
|
||||
" Object newValue = WithWarnings.appendTo(result.getValue(), gatheredWarnings);");
|
||||
out.println(" return new Stateful(result.getState(), newValue);");
|
||||
} else {
|
||||
out.println(" Object result = " + executeCall + ";");
|
||||
out.println(
|
||||
" return new Stateful(state, WithWarnings.appendTo(result, gatheredWarnings));");
|
||||
}
|
||||
out.println(" } else {");
|
||||
if (methodDefinition.modifiesState()) {
|
||||
out.println(" return " + executeCall + ";");
|
||||
} else {
|
||||
out.println(" return new Stateful(state, " + executeCall + ");");
|
||||
}
|
||||
out.println(" }");
|
||||
} else {
|
||||
out.println(" return new Stateful(state, " + executeCall + ");");
|
||||
if (methodDefinition.modifiesState()) {
|
||||
out.println(" return " + executeCall + ";");
|
||||
} else {
|
||||
out.println(" return new Stateful(state, " + executeCall + ");");
|
||||
}
|
||||
}
|
||||
out.println(" }");
|
||||
|
||||
@ -215,10 +250,7 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
|
||||
out.println(" @Override");
|
||||
out.println(" protected RootNode cloneUninitialized() {");
|
||||
out.println(
|
||||
" return new "
|
||||
+ methodDefinition.getClassName()
|
||||
+ "(lookupLanguageReference(Language.class).get());");
|
||||
out.println(" return new " + methodDefinition.getClassName() + "(Language.get(this));");
|
||||
out.println(" }");
|
||||
|
||||
out.println();
|
||||
@ -239,8 +271,8 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
|
||||
if (!arg.acceptsError()) {
|
||||
|
||||
String varName = "arg" + arg.getPosition();
|
||||
String condProfile = "arg" + arg.getPosition() + "ConditionProfile";
|
||||
String varName = mkArgumentInternalVarName(arg);
|
||||
String condProfile = mkArgumentInternalVarName(arg) + "ConditionProfile";
|
||||
out.println(
|
||||
" if ("
|
||||
+ condProfile
|
||||
@ -252,7 +284,7 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
+ ");\n"
|
||||
+ " }");
|
||||
if (!(arg.getName().equals("this") && arg.getPosition() == 0)) {
|
||||
String branchProfile = "arg" + arg.getPosition() + "BranchProfile";
|
||||
String branchProfile = mkArgumentInternalVarName(arg) + "BranchProfile";
|
||||
out.println(
|
||||
" else if (TypesGen.isPanicSentinel("
|
||||
+ varName
|
||||
@ -270,7 +302,7 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
|
||||
private void generateUncastedArgumentRead(
|
||||
PrintWriter out, MethodDefinition.ArgumentDefinition arg, String argsArray) {
|
||||
String varName = "arg" + arg.getPosition();
|
||||
String varName = mkArgumentInternalVarName(arg);
|
||||
out.println(
|
||||
" "
|
||||
+ arg.getTypeName()
|
||||
@ -286,7 +318,7 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
private void generateUncheckedArgumentRead(
|
||||
PrintWriter out, MethodDefinition.ArgumentDefinition arg, String argsArray) {
|
||||
String castName = "TypesGen.as" + capitalize(arg.getTypeName());
|
||||
String varName = "arg" + arg.getPosition();
|
||||
String varName = mkArgumentInternalVarName(arg);
|
||||
out.println(
|
||||
" "
|
||||
+ arg.getTypeName()
|
||||
@ -304,13 +336,13 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
private void generateCheckedArgumentRead(
|
||||
PrintWriter out, MethodDefinition.ArgumentDefinition arg, String argsArray) {
|
||||
String castName = "TypesGen.expect" + capitalize(arg.getTypeName());
|
||||
String varName = "arg" + arg.getPosition();
|
||||
String varName = mkArgumentInternalVarName(arg);
|
||||
out.println(" " + arg.getTypeName() + " " + varName + ";");
|
||||
out.println(" try {");
|
||||
out.println(
|
||||
" " + varName + " = " + castName + "(" + argsArray + "[" + arg.getPosition() + "]);");
|
||||
out.println(" } catch (UnexpectedResultException e) {");
|
||||
out.println(" var builtins = lookupContextReference(Language.class).get().getBuiltins();");
|
||||
out.println(" var builtins = Context.get(this).getBuiltins();");
|
||||
out.println(
|
||||
" var expected = builtins.fromTypeSystem(TypesGen.getName(arguments["
|
||||
+ arg.getPosition()
|
||||
@ -325,6 +357,52 @@ public class MethodProcessor extends AbstractProcessor {
|
||||
out.println(" }");
|
||||
}
|
||||
|
||||
private boolean generateWarningsCheck(
|
||||
PrintWriter out, List<MethodDefinition.ArgumentDefinition> arguments, String argumentsArray) {
|
||||
List<MethodDefinition.ArgumentDefinition> argsToCheck =
|
||||
arguments.stream()
|
||||
.filter(arg -> !arg.acceptsWarning() && !arg.isThis())
|
||||
.collect(Collectors.toList());
|
||||
if (argsToCheck.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
out.println(" boolean anyWarnings = false;");
|
||||
out.println(" ArrayRope<Warning> gatheredWarnings = new ArrayRope<>();");
|
||||
for (var arg : argsToCheck) {
|
||||
out.println(
|
||||
" if ("
|
||||
+ arrayRead(argumentsArray, arg.getPosition())
|
||||
+ " instanceof WithWarnings) {");
|
||||
out.println(" " + mkArgumentInternalVarName(arg) + "WarningProfile.enter();");
|
||||
out.println(" anyWarnings = true;");
|
||||
out.println(
|
||||
" WithWarnings withWarnings = (WithWarnings) "
|
||||
+ arrayRead(argumentsArray, arg.getPosition())
|
||||
+ ";");
|
||||
out.println(
|
||||
" "
|
||||
+ arrayRead(argumentsArray, arg.getPosition())
|
||||
+ " = withWarnings.getValue();");
|
||||
out.println(
|
||||
" gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarnings(this));");
|
||||
out.println(" }");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private String warningCheck(MethodDefinition.ArgumentDefinition arg) {
|
||||
return "(" + mkArgumentInternalVarName(arg) + " instanceof WithWarnings)";
|
||||
}
|
||||
|
||||
private String mkArgumentInternalVarName(MethodDefinition.ArgumentDefinition arg) {
|
||||
return "arg" + arg.getPosition();
|
||||
}
|
||||
|
||||
private String arrayRead(String array, int index) {
|
||||
return array + "[" + index + "]";
|
||||
}
|
||||
|
||||
private String capitalize(String name) {
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
package org.enso.interpreter.dsl.model;
|
||||
|
||||
import org.enso.interpreter.dsl.AcceptsError;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.dsl.*;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.*;
|
||||
@ -185,6 +182,7 @@ public class MethodDefinition {
|
||||
private final boolean isCallerInfo;
|
||||
private final boolean isSuspended;
|
||||
private final boolean acceptsError;
|
||||
private final boolean acceptsWarning;
|
||||
private final int position;
|
||||
private final VariableElement element;
|
||||
|
||||
@ -206,6 +204,7 @@ public class MethodDefinition {
|
||||
acceptsError =
|
||||
(element.getAnnotation(AcceptsError.class) != null)
|
||||
|| type.toString().equals(DATAFLOW_ERROR);
|
||||
acceptsWarning = element.getAnnotation(AcceptsWarning.class) != null;
|
||||
isFrame = type.toString().equals(VIRTUAL_FRAME);
|
||||
isCallerInfo = type.toString().equals(CALLER_INFO);
|
||||
this.position = position;
|
||||
@ -288,5 +287,13 @@ public class MethodDefinition {
|
||||
public boolean acceptsError() {
|
||||
return acceptsError;
|
||||
}
|
||||
|
||||
public boolean acceptsWarning() {
|
||||
return acceptsWarning;
|
||||
}
|
||||
|
||||
public boolean isThis() {
|
||||
return name.equals("this") || position == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import project.Semantic.Import_Loop.Spec as Import_Loop_Spec
|
||||
import project.Semantic.Meta_Spec
|
||||
import project.Semantic.Names_Spec
|
||||
import project.Semantic.Runtime_Spec
|
||||
import project.Semantic.Warnings_Spec
|
||||
|
||||
import project.Semantic.Java_Interop_Spec
|
||||
import project.Semantic.Js_Interop_Spec
|
||||
@ -90,3 +91,4 @@ main = Test.Suite.run_main <|
|
||||
Time_Spec.spec
|
||||
Uri_Spec.spec
|
||||
Vector_Spec.spec
|
||||
Warnings_Spec.spec
|
||||
|
105
test/Tests/src/Semantic/Warnings_Spec.enso
Normal file
105
test/Tests/src/Semantic/Warnings_Spec.enso
Normal file
@ -0,0 +1,105 @@
|
||||
from Standard.Base import all
|
||||
|
||||
polyglot java import java.lang.Long
|
||||
|
||||
import Standard.Test
|
||||
|
||||
type My_Warning reason
|
||||
|
||||
type My_Type a b c
|
||||
My_Type.my_method = this.a + this.b + this.c
|
||||
|
||||
type Wrap foo
|
||||
|
||||
rewrap w = case w of
|
||||
Wrap a -> Wrap a+1
|
||||
|
||||
poly_sum x y =
|
||||
Long.sum x y
|
||||
|
||||
get_foo x = x.foo
|
||||
|
||||
unwrap x = Integer.from x
|
||||
|
||||
reassign_test x =
|
||||
consed = Wrap x
|
||||
reconsed = here.rewrap consed
|
||||
i = here.unwrap reconsed
|
||||
rereconsed = Wrap i
|
||||
x1 = here.get_foo rereconsed
|
||||
prim_sum = 1 + x1
|
||||
r = here.poly_sum prim_sum 1
|
||||
r
|
||||
|
||||
baz value = Warning.attach value "I have warned you"
|
||||
bar value = here.baz value
|
||||
foo value = here.bar value
|
||||
|
||||
Integer.from (that:Wrap) = that.foo
|
||||
|
||||
spec = Test.group "Dataflow Warnings" <|
|
||||
Test.specify "should allow to attach multiple warnings and read them back" <|
|
||||
x = 1233
|
||||
y = Warning.attach "don't do this" x
|
||||
z = Warning.attach "I'm serious" y
|
||||
Warning.get_all z . map .value . should_equal ["I'm serious", "don't do this"]
|
||||
|
||||
Test.specify "should thread warnings through constructor calls" <|
|
||||
z = Warning.attach (My_Warning "warn!!!") 3
|
||||
y = Warning.attach (My_Warning "warn!!") 2
|
||||
x = Warning.attach (My_Warning "warn!") 1
|
||||
mtp = My_Type x y z
|
||||
mtp.should_equal (My_Type 1 2 3)
|
||||
Warning.get_all mtp . map .value . should_equal [My_Warning "warn!", My_Warning "warn!!", My_Warning "warn!!!"]
|
||||
|
||||
Test.specify "should thread warnings through method calls"
|
||||
mtp = My_Type 1 2 3
|
||||
warned = Warning.attach "omgggg" mtp
|
||||
r = warned.my_method
|
||||
r.should_equal 6
|
||||
Warning.get_all r . map .value . should_equal ["omgggg"]
|
||||
|
||||
Test.specify "should thread warnings through polyglot calls" <|
|
||||
y = Warning.attach "warn!!" 2
|
||||
x = Warning.attach "warn!" 1
|
||||
r = Long.sum x y
|
||||
r.should_equal 3
|
||||
Warning.get_all r . map .value . should_equal ['warn!', 'warn!!']
|
||||
|
||||
Test.specify "should thread warnings through case expressions" <|
|
||||
z = Warning.attach (My_Warning "warn!!!") 3
|
||||
y = Warning.attach (My_Warning "warn!!") 2
|
||||
x = Warning.attach (My_Warning "warn!") 1
|
||||
mtp = My_Type x y z
|
||||
r = case mtp of
|
||||
My_Type a b c -> a + b + c
|
||||
r.should_equal 6
|
||||
Warning.get_all r . map .value . should_equal [My_Warning "warn!", My_Warning "warn!!", My_Warning "warn!!!"]
|
||||
|
||||
Test.specify "should thread warnings through conversions" <|
|
||||
z = Wrap (Warning.attach 'warn!' 1)
|
||||
i = Integer.from z
|
||||
Warning.get_all i . map .value . should_equal ['warn!']
|
||||
|
||||
Test.specify "should attach correct stacktraces" <|
|
||||
current = Runtime.get_stack_trace
|
||||
warned = here.foo "value"
|
||||
warning_stack = Warning.get_all warned . head . origin
|
||||
relevant = warning_stack . drop_end current.length
|
||||
relevant.map .name . should_equal (['baz', 'bar', 'foo'].map ('Warnings_Spec.'+))
|
||||
|
||||
Test.specify "should attach reassignment info in the last-reassigned-first order" <|
|
||||
x = Warning.attach "warn!" 1
|
||||
r = here.reassign_test x
|
||||
warn = Warning.get_all r . head
|
||||
reassignments = warn.reassignments.map .name
|
||||
reassignments.should_equal ['Warnings_Spec.poly_sum', 'Small_Integer.+', 'Warnings_Spec.get_foo', 'Warnings_Spec.Wrap', 'Warnings_Spec.unwrap', 'Warnings_Spec.rewrap', 'Warnings_Spec.Wrap']
|
||||
|
||||
Test.specify "should allow to set all warnings" <|
|
||||
warned = Warning.attach 1 <| Warning.attach 2 <| Warning.attach 3 <| Warning.attach 4 "foo"
|
||||
warnings = Warning.get_all warned
|
||||
filtered = warnings.filter x-> x.value % 2 == 0
|
||||
rewarned = Warning.set filtered warned
|
||||
rewarned.should_equal 'foo'
|
||||
Warning.get_all rewarned . map .value . should_equal [2,4]
|
||||
|
Loading…
Reference in New Issue
Block a user