WithWarnings uses EnsoHashMap to speed things up (#10555)

Majority of warnings handling is now done via newly introduced nodes. Moreover, the underlying representation of warnings storage in `WithWarnings` was changed from `Warning[]` to `EnsoHashMap`.

# Important Notes
- Remove `ArrayRope`.
This commit is contained in:
Pavel Marek 2024-08-07 17:29:57 +02:00 committed by GitHub
parent 3a4b942aae
commit c2c6712b77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 1537 additions and 1160 deletions

View File

@ -1422,6 +1422,7 @@ lazy val `interpreter-dsl-test` =
)
.dependsOn(`interpreter-dsl`)
.dependsOn(`runtime`)
.dependsOn(`test-utils`)
// ============================================================================
// === Sub-Projects ===========================================================

View File

@ -255,36 +255,6 @@ type Warning
origin : Vector Stack_Trace_Element
origin self = @Builtin_Method "Warning.origin"
## PRIVATE
ADVANCED
A list of locations where the warning was reassigned in the order of
latest-first.
Warnings are reassigned whenever they interact 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 `self` 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 Stack_Trace_Element
reassignments self =
Vector.from_polyglot_array self.get_reassignments . map r->
loc = case Polyglot.has_source_location r of
False -> Nothing
True -> Source_Location.Value (Polyglot.get_source_location r)
Stack_Trace_Element.Value (Polyglot.get_executable_name r) loc
## PRIVATE
Builtin method for getting the list of locations where the warnings was reassigned.
Should use `Warning.reassignments` instead.
get_reassignments : Array Any
get_reassignments self = @Builtin_Method "Warning.get_reassignments"
## PRIVATE

View File

@ -8,6 +8,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.RootNode;
import org.enso.interpreter.node.InlineableNode;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.test.utils.ContextUtils;
import org.junit.Test;
public class InliningBuiltinsTest {
@ -17,26 +18,33 @@ public class InliningBuiltinsTest {
*/
@Test
public void executeWithoutVirtualFrame() {
var fn = InliningBuiltinsInMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
var call = root.createInlineableNode();
var clazz = call.getClass();
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName());
assertEquals(
"org.enso.interpreter.node.InlineableNode$Root",
clazz.getEnclosingClass().getInterfaces()[0].getName());
try (var ctx = ContextUtils.createDefaultContext()) {
ContextUtils.executeInContext(
ctx,
() -> {
var fn = InliningBuiltinsInMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
var call = root.createInlineableNode();
var clazz = call.getClass();
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName());
assertEquals(
"org.enso.interpreter.node.InlineableNode$Root",
clazz.getEnclosingClass().getInterfaces()[0].getName());
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
frame,
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 5L, 7L}));
});
assertEquals(12L, res);
} else {
fail("It is inlineable: " + fn.getCallTarget().getRootNode());
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
frame,
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 5L, 7L}));
});
assertEquals(12L, res);
} else {
fail("It is inlineable: " + fn.getCallTarget().getRootNode());
}
return null;
});
}
}
@ -45,22 +53,29 @@ public class InliningBuiltinsTest {
*/
@Test
public void executeWithVirtualFrame() {
var fn = InliningBuiltinsOutMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode());
} else {
var call = DirectCallNode.create(fn.getCallTarget());
var clazz = call.getClass().getSuperclass();
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName());
try (var ctx = ContextUtils.createDefaultContext()) {
ContextUtils.executeInContext(
ctx,
() -> {
var fn = InliningBuiltinsOutMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode());
} else {
var call = DirectCallNode.create(fn.getCallTarget());
var clazz = call.getClass().getSuperclass();
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName());
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 3L, 9L}));
});
assertEquals(12L, res);
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 3L, 9L}));
});
assertEquals(12L, res);
}
return null;
});
}
}
@ -69,22 +84,29 @@ public class InliningBuiltinsTest {
*/
@Test
public void executeWhenNeedsVirtualFrame() {
var fn = InliningBuiltinsNeedsMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode());
} else {
var call = DirectCallNode.create(fn.getCallTarget());
var clazz = call.getClass().getSuperclass();
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName());
try (var ctx = ContextUtils.createDefaultContext()) {
ContextUtils.executeInContext(
ctx,
() -> {
var fn = InliningBuiltinsNeedsMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode());
} else {
var call = DirectCallNode.create(fn.getCallTarget());
var clazz = call.getClass().getSuperclass();
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName());
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 3L, 9L}));
});
assertEquals(12L, res);
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 3L, 9L}));
});
assertEquals(12L, res);
}
return null;
});
}
}
@ -93,26 +115,33 @@ public class InliningBuiltinsTest {
*/
@Test
public void executeWhenNeedNotVirtualFrame() {
var fn = InliningBuiltinsNeedNotMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
var call = root.createInlineableNode();
var clazz = call.getClass();
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName());
assertEquals(
"org.enso.interpreter.node.InlineableNode$Root",
clazz.getEnclosingClass().getInterfaces()[0].getName());
try (var ctx = ContextUtils.createDefaultContext()) {
ContextUtils.executeInContext(
ctx,
() -> {
var fn = InliningBuiltinsNeedNotMethodGen.makeFunction(null);
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
var call = root.createInlineableNode();
var clazz = call.getClass();
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName());
assertEquals(
"org.enso.interpreter.node.InlineableNode$Root",
clazz.getEnclosingClass().getInterfaces()[0].getName());
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
frame,
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 5L, 7L}));
});
assertEquals(12L, res);
} else {
fail("It is inlineable: " + fn.getCallTarget().getRootNode());
var res =
WithFrame.invoke(
(frame) -> {
return call.call(
frame,
Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 5L, 7L}));
});
assertEquals(12L, res);
} else {
fail("It is inlineable: " + fn.getCallTarget().getRootNode());
}
return null;
});
}
}

View File

@ -27,7 +27,7 @@ public final class Utils {
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.option(RuntimeOptions.LOG_LEVEL, Level.INFO.getName())
.option(RuntimeOptions.DISABLE_IR_CACHES, "true")
.option(RuntimeOptions.STRICT_ERRORS, "true")
.option("engine.CompilationFailureAction", "Print")

View File

@ -73,29 +73,34 @@ public class WarningBenchmarks {
benchmarkName = SrcUtil.findName(params);
var code =
new StringBuilder(
"""
var benchCode =
"""
from Standard.Base import all
vec_sum_bench : Vector Integer -> Integer
vec_sum_bench vec =
vec.fold 0 (x->y->x+y)
""";
create_vec size elem =
var setupCode =
new StringBuilder(
"""
from Standard.Base import all
create_vec size elem = Runtime.no_inline <|
Vector.fill size elem
elem =
42
elem_const_with_warning =
elem_const_with_warning = Runtime.no_inline <|
x = 42
Warning.attach "Foo!" x
elem_with_warning v =
elem_with_warning v = Runtime.no_inline <|
Warning.attach "Foo!" v
map_vector_with_warnings vec =
map_vector_with_warnings vec = Runtime.no_inline <|
vec.map (e-> elem_with_warning e)
""");
@ -103,31 +108,37 @@ public class WarningBenchmarks {
var randomIntVectorName = "vector_with_random_values";
var vectorWithRandomValues =
generateRandomVector(random, randomIntVectorName, INPUT_DIFF_VEC_SIZE, 3_000);
code.append(vectorWithRandomValues.repr());
setupCode.append(vectorWithRandomValues.repr());
randomVectorSum = vectorWithRandomValues.sum();
var src = SrcUtil.source(benchmarkName, code.toString());
Value module = ctx.eval(src);
vecSumBench =
Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "vec_sum_bench"));
var setupSrc = SrcUtil.source(benchmarkName + "_Setup", setupCode.toString());
Value setupModule = ctx.eval(setupSrc);
createVec =
Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create_vec"));
setupModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create_vec"));
mapVecWithWarnings =
Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "map_vector_with_warnings"));
setupModule.invokeMember(
MethodNames.Module.EVAL_EXPRESSION, "map_vector_with_warnings"));
constElem =
Objects.requireNonNull(module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "elem"));
Objects.requireNonNull(
setupModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "elem"));
constElemWithWarning =
Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "elem_const_with_warning"));
setupModule.invokeMember(
MethodNames.Module.EVAL_EXPRESSION, "elem_const_with_warning"));
noWarningsVec = createVec.execute(INPUT_VEC_SIZE, constElem);
sameWarningVec = createVec.execute(INPUT_VEC_SIZE, constElemWithWarning);
randomVec =
Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, randomIntVectorName));
setupModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, randomIntVectorName));
randomElemsWithWarningsVec = mapVecWithWarnings.execute(randomVec);
var benchSrc = SrcUtil.source(benchmarkName + "_Bench", benchCode);
Value benchModule = ctx.eval(benchSrc);
vecSumBench =
Objects.requireNonNull(
benchModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "vec_sum_bench"));
}
@TearDown

View File

@ -5,7 +5,7 @@ import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import java.nio.charset.StandardCharsets;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.warning.WithWarnings;
public final class VisualizationResult {
private VisualizationResult() {}

View File

@ -22,12 +22,7 @@ import org.enso.interpreter.runtime.`type`.{Types, TypesGen}
import org.enso.interpreter.runtime.data.atom.AtomConstructor
import org.enso.interpreter.runtime.callable.function.Function
import org.enso.interpreter.runtime.control.ThreadInterruptedException
import org.enso.interpreter.runtime.error.{
DataflowError,
PanicSentinel,
WarningsLibrary,
WithWarnings
}
import org.enso.interpreter.runtime.error.{DataflowError, PanicSentinel}
import org.enso.interpreter.service.ExecutionService.{
ExpressionCall,
ExpressionValue,
@ -41,6 +36,11 @@ import org.enso.interpreter.service.error.{
VisualizationException
}
import org.enso.common.LanguageInfo
import org.enso.interpreter.runtime.warning.{
Warning,
WarningsLibrary,
WithWarnings
}
import org.enso.polyglot.debugger.ExecutedVisualization
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.polyglot.runtime.Runtime.Api.{ContextId, ExecutionResult}
@ -419,12 +419,9 @@ object ProgramExecutionSupport {
value.getValue
)
) {
val warnings =
WarningsLibrary.getUncached.getWarnings(
value.getValue,
null,
false
)
val warnsMap =
WarningsLibrary.getUncached.getWarnings(value.getValue, false)
val warnings = Warning.fromMapToArray(warnsMap)
val warningsCount = warnings.length
val warning =
if (warningsCount > 0) {

View File

@ -6,12 +6,11 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.enso.common.LanguageInfo;
import org.enso.common.MethodNames;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.warning.WithWarnings;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
@ -33,11 +32,7 @@ public class WarningsTest {
public static void initEnsoContext() {
ctx = ContextUtils.createDefaultContext();
generator = ValuesGenerator.create(ctx, ValuesGenerator.Language.ENSO);
ensoContext =
(EnsoContext)
ctx.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject();
ensoContext = ContextUtils.leakContext(ctx);
var module =
ctx.eval(
"enso",
@ -57,19 +52,22 @@ public class WarningsTest {
@Test
public void doubleWithWarningsWrap() {
var warn1 = Warning.create(ensoContext, "w1", this);
var warn2 = Warning.create(ensoContext, "w2", this);
var value = 42L;
ContextUtils.executeInContext(
ctx,
() -> {
var warn1 = Warning.create(ensoContext, "w1", this);
var warn2 = Warning.create(ensoContext, "w2", this);
var value = 42L;
var with1 = WithWarnings.wrap(ensoContext, value, warn1);
var with2 = WithWarnings.wrap(ensoContext, with1, warn2);
var with1 = AppendWarningNode.getUncached().executeAppend(null, value, warn1);
var with2 = AppendWarningNode.getUncached().executeAppend(null, with1, warn2);
assertEquals(value, with1.getValue());
assertEquals(value, with2.getValue());
Assert.assertArrayEquals(
new Object[] {warn1}, with1.getWarningsArray(WarningsLibrary.getUncached(), false));
Assert.assertArrayEquals(
new Object[] {warn1, warn2}, with2.getWarningsArray(WarningsLibrary.getUncached(), false));
assertEquals(value, with1.getValue());
assertEquals(value, with2.getValue());
Assert.assertArrayEquals(new Object[] {warn1}, with1.getWarningsArray(false));
Assert.assertArrayEquals(new Object[] {warn1, warn2}, with2.getWarningsArray(false));
return null;
});
}
@Test
@ -77,7 +75,7 @@ public class WarningsTest {
var value = 42;
WithWarnings without;
try {
without = WithWarnings.wrap(ensoContext, 42, new Warning[0]);
without = AppendWarningNode.getUncached().executeAppend(null, 42, new Warning[0]);
} catch (AssertionError e) {
// OK
return;

View File

@ -22,10 +22,9 @@ import org.enso.interpreter.runtime.data.atom.AtomConstructor;
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.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/**
* Invokes any callable with given arguments.
@ -74,7 +73,8 @@ public abstract class IndirectInvokeCallableNode extends Node {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
@Cached IndirectInvokeCallableNode invokeCallableNode,
@CachedLibrary(limit = "3") WarningsLibrary warnings) {
@CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached AppendWarningNode appendWarningNode) {
try {
var result =
invokeCallableNode.execute(
@ -87,8 +87,8 @@ public abstract class IndirectInvokeCallableNode extends Node {
argumentsExecutionMode,
isTail);
Warning[] extracted = warnings.getWarnings(warning, null, false);
return WithWarnings.wrap(EnsoContext.get(this), result, extracted);
var extracted = warnings.getWarnings(warning, false);
return appendWarningNode.executeAppend(null, result, extracted);
} catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this);
throw ctx.raiseAssertionPanic(this, null, e);

View File

@ -1,5 +1,6 @@
package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached;
@ -19,15 +20,16 @@ import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
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.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
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.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
@GenerateUncached
@ReportPolymorphism
@ -157,9 +159,16 @@ abstract class IndirectInvokeConversionNode extends Node {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thatArgumentPosition,
@Cached IndirectInvokeConversionNode childDispatch) {
@Cached IndirectInvokeConversionNode childDispatch,
@Cached AppendWarningNode appendWarningNode,
@CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
arguments[thatArgumentPosition] = that.getValue();
ArrayRope<Warning> warnings = that.getReassignedWarningsAsRope(this, false);
EnsoHashMap warnings;
try {
warnings = warnsLib.getWarnings(that, false);
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
Object result =
childDispatch.execute(
frame,
@ -173,7 +182,7 @@ abstract class IndirectInvokeConversionNode extends Node {
argumentsExecutionMode,
isTail,
thatArgumentPosition);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings);
return appendWarningNode.executeAppend(null, result, warnings);
}
@Specialization(guards = "interop.isString(that)")

View File

@ -1,5 +1,6 @@
package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
@ -24,14 +25,15 @@ import org.enso.interpreter.runtime.EnsoContext;
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.hash.EnsoHashMap;
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.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
@GenerateUncached
@ReportPolymorphism
@ -129,9 +131,16 @@ public abstract class IndirectInvokeMethodNode extends Node {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thisArgumentPosition,
@Cached IndirectInvokeMethodNode childDispatch) {
@Cached IndirectInvokeMethodNode childDispatch,
@Cached AppendWarningNode appendWarningNode,
@CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
arguments[thisArgumentPosition] = self.getValue();
ArrayRope<Warning> warnings = self.getReassignedWarningsAsRope(this, false);
EnsoHashMap warnings;
try {
warnings = warnsLib.getWarnings(self, false);
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
Object result =
childDispatch.execute(
frame,
@ -144,7 +153,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
argumentsExecutionMode,
isTail,
thisArgumentPosition);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings);
return appendWarningNode.executeAppend(null, result, warnings);
}
@Specialization

View File

@ -19,7 +19,6 @@ import java.util.UUID;
import java.util.concurrent.locks.Lock;
import org.enso.interpreter.Constants;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.BaseNode.TailStatus;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.EnsoContext;
@ -31,14 +30,14 @@ import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
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.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/**
* This class is responsible for performing the actual invocation of a given callable with its
@ -287,12 +286,12 @@ public abstract class InvokeCallableNode extends BaseNode {
VirtualFrame callerFrame,
State state,
Object[] arguments,
@Shared("warnings") @CachedLibrary(limit = "3") WarningsLibrary warnings) {
Warning[] extracted;
@Shared("warnings") @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached AppendWarningNode appendWarningNode) {
EnsoHashMap extracted;
Object callable;
try {
extracted = warnings.getWarnings(warning, null, false);
extracted = warnings.getWarnings(warning, false);
callable = warnings.removeWarnings(warning);
} catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this);
@ -325,7 +324,7 @@ public abstract class InvokeCallableNode extends BaseNode {
if (result instanceof DataflowError) {
return result;
} else {
return WithWarnings.wrap(EnsoContext.get(this), result, extracted);
return appendWarningNode.executeAppend(null, result, extracted);
}
} catch (TailCallException e) {
throw new TailCallException(e, extracted);

View File

@ -20,17 +20,18 @@ import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
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.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
public abstract class InvokeConversionNode extends BaseNode {
private @Child InvokeFunctionNode invokeFunctionNode;
@ -198,7 +199,9 @@ public abstract class InvokeConversionNode extends BaseNode {
UnresolvedConversion conversion,
Object self,
WithWarnings that,
Object[] arguments) {
Object[] arguments,
@Cached AppendWarningNode appendWarningNode,
@CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
// Cannot use @Cached for childDispatch, because we need to call notifyInserted.
if (childDispatch == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
@ -223,12 +226,17 @@ public abstract class InvokeConversionNode extends BaseNode {
}
Object value = that.getValue();
arguments[thatArgumentPosition] = value;
ArrayRope<Warning> warnings = that.getReassignedWarningsAsRope(this, false);
EnsoHashMap warnings;
try {
warnings = warnsLib.getWarnings(that, false);
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
try {
Object result = childDispatch.execute(frame, state, conversion, self, value, arguments);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings);
return appendWarningNode.executeAppend(null, result, warnings);
} catch (TailCallException e) {
throw new TailCallException(e, warnings.toArray(Warning[]::new));
throw new TailCallException(e, warnings);
}
}

View File

@ -43,7 +43,6 @@ import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoDate;
import org.enso.interpreter.runtime.data.EnsoDateTime;
import org.enso.interpreter.runtime.data.EnsoDuration;
@ -51,15 +50,16 @@ import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.EnsoTimeOfDay;
import org.enso.interpreter.runtime.data.EnsoTimeZone;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
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.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ImportStatic({HostMethodCallNode.PolyglotCallType.class, HostMethodCallNode.class})
public abstract class InvokeMethodNode extends BaseNode {
@ -436,12 +436,13 @@ public abstract class InvokeMethodNode extends BaseNode {
UnresolvedSymbol symbol,
Object self,
Object[] arguments,
@Shared("warnings") @CachedLibrary(limit = "10") WarningsLibrary warnings) {
@Shared("warnings") @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Shared @Cached AppendWarningNode appendWarningNode) {
Object selfWithoutWarnings;
Warning[] arrOfWarnings;
EnsoHashMap warnsMap;
try {
selfWithoutWarnings = warnings.removeWarnings(self);
arrOfWarnings = warnings.getWarnings(self, this, false);
warnsMap = warnings.getWarnings(self, false);
} catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this);
throw ctx.raiseAssertionPanic(this, null, e);
@ -475,9 +476,10 @@ public abstract class InvokeMethodNode extends BaseNode {
try {
Object result = childDispatch.execute(frame, state, symbol, selfWithoutWarnings, arguments);
return WithWarnings.appendTo(EnsoContext.get(this), result, arrOfWarnings);
return appendWarningNode.executeAppend(null, result, warnsMap);
} catch (TailCallException e) {
throw new TailCallException(e, arrOfWarnings);
CompilerDirectives.transferToInterpreter();
throw new TailCallException(e, warnsMap);
}
}
@ -505,10 +507,13 @@ public abstract class InvokeMethodNode extends BaseNode {
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] profiles,
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] warningProfiles,
@Cached BranchProfile anyWarningsProfile,
@Cached HostMethodCallNode hostMethodCallNode) {
@Cached HostMethodCallNode hostMethodCallNode,
@Shared @Cached AppendWarningNode appendWarningNode,
@Cached HashMapInsertAllNode mapInsertAllNode) {
Object[] args = new Object[argExecutors.length];
boolean anyWarnings = false;
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>();
var accumulatedWarnings = EnsoHashMap.empty();
var maxWarnings = EnsoContext.get(this).getWarningsLimit();
for (int i = 0; i < argExecutors.length; i++) {
var r = argExecutors[i].executeThunk(frame, arguments[i + 1], state, TailStatus.NOT_TAIL);
if (r instanceof DataflowError) {
@ -518,7 +523,9 @@ public abstract class InvokeMethodNode extends BaseNode {
warningProfiles[i].enter();
anyWarnings = true;
try {
accumulatedWarnings = accumulatedWarnings.append(warnings.getWarnings(r, this, false));
EnsoHashMap rWarnsMap = warnings.getWarnings(r, false);
accumulatedWarnings =
mapInsertAllNode.executeInsertAll(frame, accumulatedWarnings, rWarnsMap, maxWarnings);
args[i] = warnings.removeWarnings(r);
} catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this);
@ -531,7 +538,7 @@ public abstract class InvokeMethodNode extends BaseNode {
Object res = hostMethodCallNode.execute(polyglotCallType, symbol.getName(), self, args);
if (anyWarnings) {
anyWarningsProfile.enter();
res = WithWarnings.appendTo(EnsoContext.get(this), res, accumulatedWarnings);
res = appendWarningNode.executeAppend(null, res, accumulatedWarnings);
}
return res;
}

View File

@ -5,7 +5,7 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.state.State;
/**
@ -43,5 +43,5 @@ public abstract class CallOptimiserNode extends Node {
CallerInfo callerInfo,
State state,
Object[] arguments,
Warning[] warnings);
EnsoHashMap warnings);
}

View File

@ -17,13 +17,12 @@ import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.RepeatingNode;
import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
/**
* A version of {@link CallOptimiserNode} that is fully prepared to handle tail calls. Tail calls
@ -65,7 +64,7 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo,
State state,
Object[] arguments,
Warning[] warnings,
EnsoHashMap warnings,
@Shared("loopNode") @Cached(value = "createLoopNode()") LoopNode loopNode) {
return dispatch(function, callerInfo, state, arguments, loopNode);
}
@ -76,10 +75,11 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo,
State state,
Object[] arguments,
Warning[] warnings,
@Shared("loopNode") @Cached(value = "createLoopNode()") LoopNode loopNode) {
EnsoHashMap warnings,
@Shared("loopNode") @Cached(value = "createLoopNode()") LoopNode loopNode,
@Shared @Cached AppendWarningNode appendWarningNode) {
Object result = dispatch(function, callerInfo, state, arguments, loopNode);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings);
return appendWarningNode.executeAppend(null, result, warnings);
}
private Object dispatch(
@ -104,7 +104,7 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo,
State state,
Object[] arguments,
Warning[] warnings,
EnsoHashMap warnings,
@Shared("executeCallNode") @Cached ExecuteCallNode executeCallNode) {
return loopUntilCompletion(frame, function, callerInfo, state, arguments, executeCallNode);
}
@ -117,11 +117,12 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo,
State state,
Object[] arguments,
Warning[] warnings,
@Shared("executeCallNode") @Cached ExecuteCallNode executeCallNode) {
EnsoHashMap warnings,
@Shared("executeCallNode") @Cached ExecuteCallNode executeCallNode,
@Shared @Cached AppendWarningNode appendWarningNode) {
Object result =
loopUntilCompletion(frame, function, callerInfo, state, arguments, executeCallNode);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings);
return appendWarningNode.executeAppend(null, result, warnings);
}
private Object loopUntilCompletion(

View File

@ -9,7 +9,7 @@ import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.state.State;
/**
@ -51,7 +51,7 @@ public class SimpleCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo,
State state,
Object[] arguments,
Warning[] warnings) {
EnsoHashMap warnings) {
try {
return executeCallNode.executeCall(frame, function, callerInfo, state, arguments);
} catch (TailCallException e) {

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.node.controlflow.caseexpr;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
@ -16,6 +17,8 @@ import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.*;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.type.TypesGen;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/**
* A node representing a pattern match on an arbitrary runtime value.
@ -82,12 +85,12 @@ public abstract class CaseNode extends ExpressionNode {
Object doWarning(
VirtualFrame frame,
Object object,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings) {
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached AppendWarningNode appendWarningNode) {
try {
EnsoContext ctx = EnsoContext.get(this);
Warning[] ws = warnings.getWarnings(object, this, false);
var ws = warnings.getWarnings(object, false);
Object result = doMatch(frame, warnings.removeWarnings(object), warnings);
return WithWarnings.wrap(ctx, result, ws);
return appendWarningNode.executeAppend(null, result, ws);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(this).raiseAssertionPanic(this, null, e);
}

View File

@ -13,8 +13,8 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.CountingConditionProfile;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/**
* Converts a value returned by a polyglot call back to a value that can be further used within Enso
@ -75,13 +75,14 @@ public abstract class HostValueToEnsoNode extends Node {
Object value,
@CachedLibrary(limit = "3") InteropLibrary iop,
@CachedLibrary(limit = "3") WarningsLibrary warningsLibrary,
@Cached CountingConditionProfile nullWarningProfile) {
@Cached CountingConditionProfile nullWarningProfile,
@Cached AppendWarningNode appendWarningNode) {
var ctx = EnsoContext.get(this);
var nothing = ctx.getBuiltins().nothing();
if (nothing != value && nullWarningProfile.profile(warningsLibrary.hasWarnings(value))) {
try {
var attachedWarnings = warningsLibrary.getWarnings(value, null, false);
return WithWarnings.wrap(ctx, nothing, attachedWarnings);
var attachedWarnings = warningsLibrary.getWarnings(value, false);
return appendWarningNode.executeAppend(null, nothing, attachedWarnings);
} catch (UnsupportedMessageException e) {
return nothing;
}

View File

@ -17,8 +17,8 @@ import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@BuiltinMethod(
type = "IO",

View File

@ -27,9 +27,9 @@ import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.EnsoFile;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@GenerateUncached
public abstract class EqualsComplexNode extends Node {

View File

@ -21,8 +21,8 @@ import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.polyglot.common_utils.Core_Text_Utils;
@GenerateUncached

View File

@ -45,11 +45,11 @@ import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.atom.StructsLibrary;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.polyglot.common_utils.Core_Text_Utils;
/**

View File

@ -21,8 +21,8 @@ import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNod
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@BuiltinMethod(
type = "Default_Comparator",

View File

@ -39,12 +39,13 @@ import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
/**
* Sorts a vector with elements that have only Default_Comparator, thus, only elements with a
@ -66,7 +67,7 @@ public abstract class SortVectorNode extends Node {
* @param self Vector that has elements with only Default_Comparator, that are elements with
* builtin types.
* @param ascending -1 for descending, 1 for ascending
* @param comparators Vector of comparators, with the same length of self. This is gather in the
* @param comparators Vector of comparators, with the same length of self. This is gathered in the
* Enso code, because doing that in this builtin would be difficult. If {@code onFunc}
* parameter is not {@code Nothing}, comparators are gathered from the result of {@code
* onFunc} projection.
@ -354,7 +355,8 @@ public abstract class SortVectorNode extends Node {
.map(text -> Warning.create(ctx, text, this))
.limit(MAX_SORT_WARNINGS)
.toArray(Warning[]::new);
return WithWarnings.appendTo(ctx, vector, warnArray.length < warnings.size(), warnArray);
var reachedMaxCount = warnArray.length < warnings.size();
return WithWarnings.create(vector, MAX_SORT_WARNINGS, reachedMaxCount, warnArray);
}
private Object attachDifferentComparatorsWarning(Object vector, List<Group> groups) {
@ -366,8 +368,13 @@ public abstract class SortVectorNode extends Node {
.collect(Collectors.joining(", "));
var text = Text.create("Different comparators: [" + diffCompsMsg + "]");
var ctx = EnsoContext.get(this);
var warn = Warning.create(ctx, text, this);
return WithWarnings.appendTo(ctx, vector, false, warn);
var warnsLib = WarningsLibrary.getUncached();
if (warnsLib.hasWarnings(vector) && warnsLib.isLimitReached(vector)) {
return vector;
} else {
var warn = Warning.create(ctx, text, this);
return AppendWarningNode.getUncached().executeAppend(null, vector, warn);
}
} else {
return vector;
}

View File

@ -13,7 +13,7 @@ import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
public abstract class ExpectStringNode extends Node {
private @Child InteropLibrary library = InteropLibrary.getFactory().createDispatched(10);

View File

@ -3,7 +3,7 @@ package org.enso.interpreter.runtime.control;
import com.oracle.truffle.api.nodes.ControlFlowException;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
/**
* Used to model the switch of control-flow from standard stack-based execution to looping.
@ -14,7 +14,13 @@ public class TailCallException extends ControlFlowException {
private final Function function;
private final CallerInfo callerInfo;
private final Object[] arguments;
private final Warning[] warnings;
/**
* May be null.
*
* @see org.enso.interpreter.runtime.warning.WithWarnings#warnings
*/
private final EnsoHashMap warnings;
/**
* Creates a new exception containing the necessary data to continue computation.
@ -31,7 +37,7 @@ public class TailCallException extends ControlFlowException {
}
private TailCallException(
Function function, CallerInfo callerInfo, Object[] arguments, Warning[] warnings) {
Function function, CallerInfo callerInfo, Object[] arguments, EnsoHashMap warnings) {
this.function = function;
this.callerInfo = callerInfo;
this.arguments = arguments;
@ -44,7 +50,7 @@ public class TailCallException extends ControlFlowException {
* @param origin the original tail call exception
* @param warnings warnings to be associated with the tail call exception
*/
public TailCallException(TailCallException origin, Warning[] warnings) {
public TailCallException(TailCallException origin, EnsoHashMap warnings) {
this(origin.getFunction(), origin.getCallerInfo(), origin.getArguments(), warnings);
}
@ -80,7 +86,7 @@ public class TailCallException extends ControlFlowException {
*
* @return the warnings to be appended to the result of the call, or null if empty
*/
public Warning[] getWarnings() {
public EnsoHashMap getWarnings() {
return warnings;
}
}

View File

@ -1,123 +0,0 @@
package org.enso.interpreter.runtime.data;
import com.oracle.truffle.api.CompilerDirectives;
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));
}
@CompilerDirectives.TruffleBoundary
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
+ '}';
}
}
}

View File

@ -26,9 +26,9 @@ import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.type.TypesGen;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** A runtime representation of an Atom in Enso. */
@ExportLibrary(InteropLibrary.class)

View File

@ -11,11 +11,11 @@ import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.CountingConditionProfile;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.type.TypesGen;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/**
* A node instantiating a constant {@link AtomConstructor} with values computed based on the
@ -66,10 +66,13 @@ abstract class InstantiateNode extends ExpressionNode {
@ExplodeLoop
Object doExecute(
VirtualFrame frame,
@Cached(parameters = {"constructor"}) AtomConstructorInstanceNode createInstanceNode) {
@Cached(parameters = {"constructor"}) AtomConstructorInstanceNode createInstanceNode,
@Cached AppendWarningNode appendWarningNode,
@Cached HashMapInsertAllNode mapInsertAllNode) {
Object[] argumentValues = new Object[arguments.length];
boolean anyWarnings = false;
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>();
var accumulatedWarnings = EnsoHashMap.empty();
var maxWarnings = EnsoContext.get(this).getWarningsLimit();
for (int i = 0; i < arguments.length; i++) {
CountingConditionProfile profile = profiles[i];
CountingConditionProfile warningProfile = warningProfiles[i];
@ -80,8 +83,10 @@ abstract class InstantiateNode extends ExpressionNode {
} else if (warningProfile.profile(warnings.hasWarnings(argument))) {
anyWarnings = true;
try {
var argumentWarnsMap = warnings.getWarnings(argument, false);
accumulatedWarnings =
accumulatedWarnings.append(warnings.getWarnings(argument, this, false));
mapInsertAllNode.executeInsertAll(
frame, accumulatedWarnings, argumentWarnsMap, maxWarnings);
argumentValues[i] = warnings.removeWarnings(argument);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(this).raiseAssertionPanic(this, null, e);
@ -94,8 +99,8 @@ abstract class InstantiateNode extends ExpressionNode {
}
}
if (anyWarningsProfile.profile(anyWarnings)) {
return WithWarnings.appendTo(
EnsoContext.get(this), createInstanceNode.execute(argumentValues), accumulatedWarnings);
return appendWarningNode.executeAppend(
frame, createInstanceNode.execute(argumentValues), accumulatedWarnings);
} else {
return createInstanceNode.execute(argumentValues);
}

View File

@ -66,6 +66,7 @@ public final class EnsoHashMap implements EnsoObject {
}
}
/** Slow version of {@link #getCachedVectorRepresentation(ConditionProfile)}. */
Object getCachedVectorRepresentation() {
return getCachedVectorRepresentation(ConditionProfile.getUncached());
}

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.runtime.data.hash;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import java.util.Arrays;
import java.util.Iterator;
import org.enso.interpreter.node.expression.builtin.meta.EqualsNode;
import org.enso.interpreter.node.expression.builtin.meta.HashCodeNode;
@ -48,17 +49,22 @@ final class EnsoHashMapBuilder {
}
/** Create a new builder with default size being {@code 11}. */
public static EnsoHashMapBuilder create() {
static EnsoHashMapBuilder create() {
return new EnsoHashMapBuilder(11);
}
static EnsoHashMapBuilder createWithCapacity(int capacity) {
assert capacity > 0;
return new EnsoHashMapBuilder(capacity);
}
/** Returns count of elements in the storage. */
public int generation() {
int generation() {
return generation;
}
/** Returns the actual number of visible elements in current generation. */
public int size() {
int size() {
return actualSize;
}
@ -66,7 +72,7 @@ final class EnsoHashMapBuilder {
* Provides access to all {@code StorageEntry} in this builder at given {@code atGeneration}.
* Classical usage is to {@code for (var e : this) if (e.isVisible(atGeneration) operation(e))}.
*/
public StorageEntry[] getEntries(int atGeneration, int size) {
StorageEntry[] getEntries(int atGeneration, int size) {
var arr = new StorageEntry[size];
var at = 0;
for (var i = 0; i < byHash.length; i++) {
@ -82,12 +88,57 @@ final class EnsoHashMapBuilder {
}
}
record Entry(Object key, Object value) {}
Iterator<Entry> getEntriesIterator(int atGeneration) {
return new EntriesIterator(atGeneration);
}
final class EntriesIterator implements Iterator<Entry> {
private final int atGeneration;
private int nextVisibleEntryIdx = -1;
EntriesIterator(int atGeneration) {
this.atGeneration = atGeneration;
skipToNextVisibleEntry();
}
@Override
public boolean hasNext() {
if (nextVisibleEntryIdx >= byHash.length) {
return false;
}
var entry = byHash[nextVisibleEntryIdx];
return entry != null && entry.isVisible(atGeneration);
}
@Override
public Entry next() {
var entry = byHash[nextVisibleEntryIdx];
skipToNextVisibleEntry();
return new Entry(entry.key(), entry.value());
}
private void skipToNextVisibleEntry() {
while (true) {
nextVisibleEntryIdx++;
if (nextVisibleEntryIdx == byHash.length) {
break;
}
var entry = byHash[nextVisibleEntryIdx];
if (entry != null && entry.isVisible(atGeneration)) {
break;
}
}
}
}
/**
* Prepares a builder ready for modification at given generation. It may return {@code this} if
* the {@code atGeneration == this.generation} and the {@code byHash} array is less than 75% full.
* Otherwise it may return new builder suitable for additions.
*/
public EnsoHashMapBuilder asModifiable(
EnsoHashMapBuilder asModifiable(
VirtualFrame frame, int atGeneration, HashCodeNode hashCodeNode, EqualsNode equalsNode) {
if (atGeneration != generation || generation * 4 > byHash.length * 3) {
var newSize = Math.max(actualSize * 2, byHash.length);
@ -103,7 +154,7 @@ final class EnsoHashMapBuilder {
* equal key, it marks it as removed, if it hasn't been removed yet. Once it finds an empty slot,
* it puts there a new entry with the next generation.
*/
public void put(
void put(
VirtualFrame frame,
Object key,
Object value,
@ -139,7 +190,7 @@ final class EnsoHashMapBuilder {
* Finds storage entry for given key or {@code null}. Searches only entries that are visible for
* given {@code generation}.
*/
public StorageEntry get(
StorageEntry get(
VirtualFrame frame,
Object key,
int generation,
@ -175,8 +226,7 @@ final class EnsoHashMapBuilder {
*
* @return true if the removal was successful false otherwise.
*/
public boolean remove(
VirtualFrame frame, Object key, HashCodeNode hashCodeNode, EqualsNode equalsNode) {
boolean remove(VirtualFrame frame, Object key, HashCodeNode hashCodeNode, EqualsNode equalsNode) {
assert actualSize <= generation;
var at = findWhereToStart(key, hashCodeNode);
var nextGeneration = ++generation;
@ -225,11 +275,11 @@ final class EnsoHashMapBuilder {
* are in the storage as of this moment, i.e., all the entries with their indexes lesser than
* {@code generation}.
*
* <p>Should be called where most once for a particular {@code generation}.
* <p>Should be called at most once for a particular {@code generation}.
*
* @return A new hash map snapshot.
*/
public EnsoHashMap build() {
EnsoHashMap build() {
return EnsoHashMap.createWithBuilder(this);
}

View File

@ -0,0 +1,73 @@
package org.enso.interpreter.runtime.data.hash;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.common.LanguageInfo;
import org.enso.interpreter.node.expression.builtin.meta.EqualsNode;
import org.enso.interpreter.node.expression.builtin.meta.HashCodeNode;
@GenerateUncached
@NodeInfo(
shortName = "MapInsertAll",
description = "Inserts all elements from the given container into a hash map",
language = LanguageInfo.ID)
public abstract class HashMapInsertAllNode extends Node {
public static HashMapInsertAllNode build() {
return HashMapInsertAllNodeGen.create();
}
public static HashMapInsertAllNode getUncached() {
return HashMapInsertAllNodeGen.getUncached();
}
/**
* Insert all the elements from the given container into the given map.
*
* @param self A map to insert into.
* @param container Either a map, or a list of pairs to insert into the map.
* @param maxItems Maximum number of items to insert into the map from the container.
*/
public abstract EnsoHashMap executeInsertAll(
VirtualFrame frame, EnsoHashMap self, EnsoHashMap container, int maxItems);
@Specialization
EnsoHashMap doEnsoHashMaps(
VirtualFrame frame,
EnsoHashMap self,
EnsoHashMap other,
int maxItems,
@Cached HashCodeNode hashCodeNode,
@Cached EqualsNode equalsNode) {
assert maxItems > 0;
var selfSize = self.getHashSize();
var otherSize = other.getHashSize();
if (otherSize == 0) {
return self;
}
var mapBuilder = EnsoHashMapBuilder.createWithCapacity(selfSize + otherSize);
var selfMapBuilder = self.getMapBuilder(frame, true, hashCodeNode, equalsNode);
var selfEntriesIt = selfMapBuilder.getEntriesIterator(selfMapBuilder.generation());
while (selfEntriesIt.hasNext()) {
var selfEntry = selfEntriesIt.next();
mapBuilder.put(frame, selfEntry.key(), selfEntry.value(), hashCodeNode, equalsNode);
}
var otherMapBuilder = other.getMapBuilder(frame, true, hashCodeNode, equalsNode);
var otherEntriesIt = otherMapBuilder.getEntriesIterator(otherMapBuilder.generation());
var itemsInserted = 0;
while (otherEntriesIt.hasNext()) {
if (itemsInserted >= maxItems) {
break;
}
var entry = otherEntriesIt.next();
mapBuilder.put(frame, entry.key(), entry.value(), hashCodeNode, equalsNode);
itemsInserted++;
}
return mapBuilder.build();
}
}

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.runtime.data.hash;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
@ -25,6 +26,7 @@ import org.enso.interpreter.runtime.error.PanicException;
Returns newly created hash map with the given key value mapping.
""",
autoRegister = false)
@GenerateUncached
public abstract class HashMapInsertNode extends Node {
public static HashMapInsertNode build() {
@ -41,6 +43,7 @@ public abstract class HashMapInsertNode extends Node {
Object value,
@Shared("hash") @Cached HashCodeNode hashCodeNode,
@Shared("equals") @Cached EqualsNode equalsNode) {
assert value != null;
var mapBuilder = hashMap.getMapBuilder(frame, false, hashCodeNode, equalsNode);
mapBuilder.put(frame, key, value, hashCodeNode, equalsNode);
var newMap = mapBuilder.build();
@ -61,6 +64,7 @@ public abstract class HashMapInsertNode extends Node {
@CachedLibrary(limit = "3") InteropLibrary iteratorInterop,
@Shared("hash") @Cached HashCodeNode hashCodeNode,
@Shared("equals") @Cached EqualsNode equalsNode) {
assert valueToInsert != null;
var mapBuilder = EnsoHashMapBuilder.create();
try {
Object entriesIterator = mapInterop.getHashEntriesIterator(foreignMap);

View File

@ -23,6 +23,10 @@ public abstract class HashMapSizeNode extends Node {
return HashMapSizeNodeGen.create();
}
public static HashMapSizeNode getUncached() {
return HashMapSizeNodeGen.getUncached();
}
public abstract long execute(Object self);
@Specialization(guards = "interop.hasHashEntries(hashMap)", limit = "3")

View File

@ -13,15 +13,19 @@ import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import java.util.Arrays;
import java.util.Comparator;
import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
import org.enso.interpreter.runtime.data.hash.HashMapSizeNode;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.graalvm.collections.EconomicSet;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** A primitive boxed array type for use in the runtime. */
@ExportLibrary(InteropLibrary.class)
@ -30,9 +34,13 @@ import org.graalvm.collections.EconomicSet;
@Builtin(pkg = "mutable", stdlibName = "Standard.Base.Data.Array.Array")
final class Array implements EnsoObject {
private final Object[] items;
/** If true, some elements contain warning, and thus, this Array contains warning. */
private Boolean withWarnings;
private Warning[] cachedWarningsWrapped;
private Warning[] cachedWarningsUnwrapped;
private EnsoHashMap cachedWarningsWrapped;
private EnsoHashMap cachedWarningsUnwrapped;
/**
* Creates a new array
@ -91,7 +99,12 @@ final class Array implements EnsoObject {
long index,
@CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached BranchProfile errProfile,
@Cached BranchProfile hasWarningsProfile)
@Cached BranchProfile hasWarningsProfile,
@Cached HashMapInsertNode mapInsertNode,
@Cached AppendWarningNode appendWarningNode,
@Cached BranchProfile shouldWrapProfile,
@Cached HashMapSizeNode mapSizeNode,
@Cached HashMapInsertAllNode mapInsertAllNode)
throws InvalidArrayIndexException, UnsupportedMessageException {
if (index >= items.length || index < 0) {
errProfile.enter();
@ -101,11 +114,13 @@ final class Array implements EnsoObject {
var v = items[(int) index];
if (this.hasWarnings(warnings)) {
hasWarningsProfile.enter();
Warning[] extracted = this.getWarnings(null, false, warnings);
var extractedWarnsMap =
this.getWarnings(
false, warnings, mapInsertNode, shouldWrapProfile, mapSizeNode, mapInsertAllNode);
if (warnings.hasWarnings(v)) {
v = warnings.removeWarnings(v);
}
return WithWarnings.wrap(EnsoContext.get(warnings), v, extracted);
return appendWarningNode.executeAppend(null, v, extractedWarnsMap);
}
return v;
@ -180,48 +195,81 @@ final class Array implements EnsoObject {
}
@ExportMessage
Warning[] getWarnings(
Node location,
EnsoHashMap getWarnings(
boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Shared("mapInsertNode") @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached BranchProfile shouldWrapProfile,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached HashMapInsertAllNode mapInsertAllNode)
throws UnsupportedMessageException {
Warning[] cache = shouldWrap ? cachedWarningsWrapped : cachedWarningsUnwrapped;
var cache = shouldWrap ? cachedWarningsWrapped : cachedWarningsUnwrapped;
if (cache == null) {
cache = Warning.fromSetToArray(collectAllWarnings(warnings, location, shouldWrap));
var warnLimit = EnsoContext.get(warnings).getWarningsLimit();
var allWarnsMap =
collectAllWarnings(
warnings,
mapInsertNode,
shouldWrap,
mapInsertAllNode,
warnLimit,
shouldWrapProfile,
mapSizeNode);
if (shouldWrap) {
cachedWarningsWrapped = cache;
cachedWarningsWrapped = allWarnsMap;
cache = cachedWarningsWrapped;
} else {
cachedWarningsUnwrapped = cache;
cachedWarningsUnwrapped = allWarnsMap;
cache = cachedWarningsUnwrapped;
}
}
assert cache != null;
return cache;
}
@CompilerDirectives.TruffleBoundary
private EconomicSet<Warning> collectAllWarnings(
WarningsLibrary warningsLib, Node location, boolean shouldWrap)
private EnsoHashMap collectAllWarnings(
WarningsLibrary warningsLib,
HashMapInsertNode mapInsertNode,
boolean shouldWrap,
HashMapInsertAllNode mapInsertAllNode,
int warnLimit,
BranchProfile shouldWrapProfile,
HashMapSizeNode mapSizeNode)
throws UnsupportedMessageException {
EconomicSet<Warning> setOfWarnings = EconomicSet.create(new WithWarnings.WarningEquivalence());
for (int i = 0; i < this.items.length; i++) {
final int finalIndex = i;
Object item = this.items[i];
var warnsSet = EnsoHashMap.empty();
for (int itemIdx = 0; itemIdx < this.items.length; itemIdx++) {
Object item = this.items[itemIdx];
var warnsCnt = (int) mapSizeNode.execute(warnsSet);
if (warnsCnt == warnLimit) {
break;
}
if (warningsLib.hasWarnings(item)) {
Warning[] warnings = warningsLib.getWarnings(item, location, shouldWrap);
Warning[] wrappedWarningsMaybe;
var itemWarnsMap = warningsLib.getWarnings(item, shouldWrap);
assert mapSizeNode.execute(itemWarnsMap) <= warnLimit;
if (shouldWrap) {
wrappedWarningsMaybe =
Arrays.stream(warnings)
.map(warning -> Warning.wrapMapError(warningsLib, warning, finalIndex))
.toArray(Warning[]::new);
if (!shouldWrap) {
warnsSet =
mapInsertAllNode.executeInsertAll(null, warnsSet, itemWarnsMap, warnLimit - warnsCnt);
} else {
wrappedWarningsMaybe = warnings;
shouldWrapProfile.enter();
CompilerDirectives.transferToInterpreter();
// warnings need to be sorted such that at the first index, there is the oldest warning.
// This is because we are creating new warnings by wrapping the previous one, and we need
// to
// do that in the same creation order.
var warnings = Warning.fromMapToArray(itemWarnsMap);
Arrays.sort(warnings, Comparator.comparing(Warning::getSequenceId));
for (int i = 0; i < Math.min(warnings.length, warnLimit); i++) {
var warn = warnings[i];
var wrappedWarn = Warning.wrapMapError(warningsLib, warn, itemIdx);
warnsSet =
mapInsertNode.execute(null, warnsSet, wrappedWarn.getSequenceId(), wrappedWarn);
}
}
setOfWarnings.addAll(Arrays.asList(wrappedWarningsMaybe));
}
}
return setOfWarnings;
assert mapSizeNode.execute(warnsSet) <= warnLimit;
return warnsSet;
}
@ExportMessage
@ -239,10 +287,18 @@ final class Array implements EnsoObject {
}
@ExportMessage
boolean isLimitReached(@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings) {
boolean isLimitReached(
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnsLib,
@Shared("mapInsertNode") @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached HashMapInsertAllNode mapInsertAllNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached BranchProfile shouldWrapProfile) {
try {
int limit = EnsoContext.get(warnings).getWarningsLimit();
return getWarnings(null, false, warnings).length >= limit;
int limit = EnsoContext.get(warnsLib).getWarningsLimit();
var ourWarnings =
getWarnings(
false, warnsLib, mapInsertNode, shouldWrapProfile, mapSizeNode, mapInsertAllNode);
return (int) mapSizeNode.execute(ourWarnings) >= limit;
} catch (UnsupportedMessageException e) {
return false;
}

View File

@ -12,7 +12,7 @@ import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import java.util.Arrays;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ExportLibrary(InteropLibrary.class)
final class ArrayBuilder implements EnsoObject {
@ -142,14 +142,31 @@ final class ArrayBuilder implements EnsoObject {
}
}
private static boolean checkArraySize(boolean mustBeExact, int real, int expected) {
if (real == expected) {
return true;
} else {
if (mustBeExact) {
CompilerDirectives.transferToInterpreter();
}
return false;
}
}
/** Returns the current array of the builder. */
private Object toArray() {
private Object toArray(boolean mustBeExact) {
if (objectArray != null) {
return objectArray.length == size ? objectArray : Arrays.copyOf(objectArray, size);
return checkArraySize(mustBeExact, objectArray.length, size)
? objectArray
: Arrays.copyOf(objectArray, size);
} else if (primitiveArray instanceof long[] longArray) {
return longArray.length == size ? longArray : Arrays.copyOf(longArray, size);
return checkArraySize(mustBeExact, longArray.length, size)
? longArray
: Arrays.copyOf(longArray, size);
} else if (primitiveArray instanceof double[] doubleArray) {
return doubleArray.length == size ? doubleArray : Arrays.copyOf(doubleArray, size);
return checkArraySize(mustBeExact, doubleArray.length, size)
? doubleArray
: Arrays.copyOf(doubleArray, size);
} else {
return null;
}
@ -195,7 +212,7 @@ final class ArrayBuilder implements EnsoObject {
yield get(index, iop);
}
case "getSize" -> getSize();
case "toArray" -> asVector();
case "toArray" -> asVector(false);
default -> throw UnknownIdentifierException.create(name);
};
}
@ -225,8 +242,8 @@ final class ArrayBuilder implements EnsoObject {
return "Array_Builder";
}
Object asVector() {
var res = toArray();
Object asVector(boolean mustBeExact) {
var res = toArray(mustBeExact);
if (res instanceof long[] longs) {
return Vector.fromLongArray(longs);
}

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.runtime.data.vector;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
@ -10,8 +11,10 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@GenerateUncached
public abstract class ArrayLikeAtNode extends Node {
public abstract Object executeAt(Object arrayLike, long index) throws InvalidArrayIndexException;
@ -57,10 +60,11 @@ public abstract class ArrayLikeAtNode extends Node {
long index,
@Cached.Exclusive @CachedLibrary(limit = "3") InteropLibrary interop,
@Cached.Exclusive @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached.Exclusive @Cached HostValueToEnsoNode convert)
@Cached.Exclusive @Cached HostValueToEnsoNode convert,
@Cached AppendWarningNode appendWarningNode)
throws InvalidArrayIndexException {
try {
return self.readArrayElement(index, interop, warnings, convert);
return self.readArrayElement(index, interop, warnings, convert, appendWarningNode);
} catch (UnsupportedMessageException ex) {
throw ArrayPanics.notAnArrayPanic(this, self);
}

View File

@ -11,8 +11,8 @@ import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** Publicly available operations on array-like classes. */
@Builtin(pkg = "immutable", stdlibName = "Standard.Base.Internal.Array_Like_Helpers")
@ -97,7 +97,7 @@ public final class ArrayLikeHelpers {
}
target.add(value, warnings);
}
return target.asVector();
return target.asVector(true);
}
@Builtin.Method(

View File

@ -2,6 +2,7 @@ package org.enso.interpreter.runtime.data.vector;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
@ -9,6 +10,7 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
@GenerateUncached
public abstract class ArrayLikeLengthNode extends Node {
public abstract long executeLength(Object arrayLike);

View File

@ -15,10 +15,10 @@ import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEn
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ExportLibrary(TypesLibrary.class)
@ExportLibrary(InteropLibrary.class)
@ -91,7 +91,8 @@ final class ArraySlice implements EnsoObject {
long index,
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached HostValueToEnsoNode toEnso)
@Cached HostValueToEnsoNode toEnso,
@Cached AppendWarningNode appendWarningNode)
throws InvalidArrayIndexException, UnsupportedMessageException {
if (index < 0 || index >= getArraySize(interop)) {
throw InvalidArrayIndexException.create(index);
@ -99,11 +100,11 @@ final class ArraySlice implements EnsoObject {
var v = interop.readArrayElement(storage, start + index);
if (this.hasWarnings(warnings)) {
Warning[] extracted = this.getWarnings(null, false, warnings);
var extracted = this.getWarnings(false, warnings);
if (warnings.hasWarnings(v)) {
v = warnings.removeWarnings(v);
}
return WithWarnings.wrap(EnsoContext.get(warnings), toEnso.execute(v), extracted);
return appendWarningNode.executeAppend(null, toEnso.execute(v), extracted);
}
return toEnso.execute(v);
}
@ -155,12 +156,10 @@ final class ArraySlice implements EnsoObject {
}
@ExportMessage
Warning[] getWarnings(
Node location,
boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
EnsoHashMap getWarnings(
boolean shouldWrap, @Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException {
return warnings.getWarnings(this.storage, location, shouldWrap);
return warnings.getWarnings(this.storage, shouldWrap);
}
@ExportMessage

View File

@ -15,10 +15,10 @@ import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEn
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class)
@ -178,8 +178,8 @@ abstract class Vector implements EnsoObject {
}
@ExportMessage
Warning[] getWarnings(Node location, boolean shouldWrap) throws UnsupportedMessageException {
return new Warning[0];
EnsoHashMap getWarnings(boolean shouldWrap) {
return EnsoHashMap.empty();
}
@ExportMessage
@ -249,15 +249,16 @@ abstract class Vector implements EnsoObject {
long index,
@Cached.Shared(value = "interop") @CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached HostValueToEnsoNode toEnso)
@Cached HostValueToEnsoNode toEnso,
@Cached AppendWarningNode appendWarningNode)
throws InvalidArrayIndexException, UnsupportedMessageException {
var v = interop.readArrayElement(this.storage, index);
if (warnings.hasWarnings(this.storage)) {
Warning[] extracted = warnings.getWarnings(this.storage, null, false);
var extracted = warnings.getWarnings(this.storage, false);
if (warnings.hasWarnings(v)) {
v = warnings.removeWarnings(v);
}
return WithWarnings.wrap(EnsoContext.get(interop), toEnso.execute(v), extracted);
return appendWarningNode.executeAppend(null, toEnso.execute(v), extracted);
}
return toEnso.execute(v);
}
@ -287,12 +288,11 @@ abstract class Vector implements EnsoObject {
}
@ExportMessage
Warning[] getWarnings(
Node location,
EnsoHashMap getWarnings(
boolean shouldWrap,
@Cached.Shared(value = "warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException {
return warnings.getWarnings(this.storage, location, shouldWrap);
return warnings.getWarnings(this.storage, shouldWrap);
}
@ExportMessage
@ -357,8 +357,8 @@ abstract class Vector implements EnsoObject {
}
@ExportMessage
Warning[] getWarnings(Node location, boolean shouldWrap) throws UnsupportedMessageException {
return new Warning[0];
EnsoHashMap getWarnings(boolean shouldWrap) {
return EnsoHashMap.empty();
}
@ExportMessage
@ -407,8 +407,8 @@ abstract class Vector implements EnsoObject {
}
@ExportMessage
Warning[] getWarnings(Node location, boolean shouldWrap) throws UnsupportedMessageException {
return new Warning[0];
EnsoHashMap getWarnings(boolean shouldWrap) {
return EnsoHashMap.empty();
}
@ExportMessage

View File

@ -1,258 +0,0 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
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 java.util.Arrays;
import java.util.Comparator;
import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.graalvm.collections.EconomicSet;
@Builtin(pkg = "error", stdlibName = "Standard.Base.Warning.Warning")
@ExportLibrary(TypesLibrary.class)
public final class Warning implements EnsoObject {
private final Object value;
private final Object origin;
private final ArrayRope<Reassignment> reassignments;
private final long sequenceId;
private Warning(Object value, Object origin, long sequenceId) {
this(value, origin, sequenceId, new ArrayRope<>());
}
private Warning(
Object value, Object origin, long sequenceId, ArrayRope<Reassignment> reassignments) {
this.value = value;
this.origin = origin;
this.reassignments = reassignments;
this.sequenceId = sequenceId;
}
@Builtin.Method(name = "value", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getValue() {
return value;
}
@Builtin.Method(name = "origin", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getOrigin() {
return origin;
}
@Builtin.Method(
name = "create",
description = "Creates a new instance of the primitive warning value.",
autoRegister = false)
@Builtin.Specialize
public static Warning create(EnsoContext ctx, Object payload, Object origin) {
return new Warning(payload, origin, ctx.nextSequenceId());
}
@Builtin.Method(description = "Gets the list of locations where the warnings was reassigned.")
public EnsoObject getReassignments() {
Warning.Reassignment[] reassignmentsArray = reassignments.toArray(Warning.Reassignment[]::new);
return ArrayLikeHelpers.wrapEnsoObjects(reassignmentsArray);
}
@Builtin.Method(
name = "attach_with_stacktrace",
description = "Attaches the given warning to the value.",
autoRegister = false)
@Builtin.Specialize
public static WithWarnings attach(
EnsoContext ctx, WithWarnings value, Object warning, Object origin) {
return value.append(ctx, new Warning(warning, origin, ctx.nextSequenceId()));
}
@Builtin.Method(
name = "attach_with_stacktrace",
description = "Attaches the given warning to the value.",
autoRegister = false)
@Builtin.Specialize(fallback = true)
public static WithWarnings attach(EnsoContext ctx, Object value, Object warning, Object origin) {
return WithWarnings.wrap(ctx, value, new Warning(warning, origin, ctx.nextSequenceId()));
}
@Builtin.Method(
name = "get_all_vector",
description = "Gets all the warnings associated with the value.",
autoRegister = false)
@Builtin.Specialize
@CompilerDirectives.TruffleBoundary
public static EnsoObject getAll(
WithWarnings value, boolean shouldWrap, WarningsLibrary warningsLib) {
Warning[] warnings = value.getWarningsArray(warningsLib, shouldWrap);
sortArray(warnings);
return ArrayLikeHelpers.asVectorEnsoObjects(warnings);
}
@Builtin.Method(
name = "get_all_vector",
description = "Gets all the warnings associated with the value.",
autoRegister = false)
@Builtin.Specialize(fallback = true)
public static EnsoObject getAll(Object value, boolean shouldWrap, WarningsLibrary warningsLib) {
if (warningsLib.hasWarnings(value)) {
try {
Warning[] warnings = warningsLib.getWarnings(value, null, shouldWrap);
sortArray(warnings);
return ArrayLikeHelpers.asVectorEnsoObjects(warnings);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLib).raiseAssertionPanic(warningsLib, null, e);
}
} else {
return ArrayLikeHelpers.asVectorEmpty();
}
}
@Builtin.Method(
description =
"Returns `true` if the maximal number of warnings has been reached, `false` otherwise.",
autoRegister = false)
@Builtin.Specialize
public static boolean limitReached(WithWarnings value, WarningsLibrary warnings) {
return value.isLimitReached();
}
@Builtin.Method(
description =
"Returns `true` if the maximal number of warnings has been reached, `false` otherwise.",
autoRegister = false)
@Builtin.Specialize(fallback = true)
public static boolean limitReached(Object value, WarningsLibrary warnings) {
return warnings.hasWarnings(value) ? warnings.isLimitReached(value) : false;
}
@CompilerDirectives.TruffleBoundary
private static void sortArray(Warning[] arr) {
Arrays.sort(arr, Comparator.comparing(Warning::getSequenceId).reversed());
}
/** Converts set to an array behing a truffle boundary. */
@CompilerDirectives.TruffleBoundary
public static Warning[] fromSetToArray(EconomicSet<Warning> set) {
return set.toArray(new Warning[set.size()]);
}
@Builtin.Method(
name = "set_array",
description = "Sets all the warnings associated with the value.",
autoRegister = false)
@Builtin.Specialize
@SuppressWarnings("generic-enso-builtin-type")
public static Object set(
EnsoContext ctx, WithWarnings value, Object warnings, InteropLibrary interop) {
return setGeneric(ctx, value.getValue(), interop, warnings);
}
@Builtin.Method(
name = "set_array",
description = "Sets all the warnings associated with the value.",
autoRegister = false)
@SuppressWarnings("generic-enso-builtin-type")
@Builtin.Specialize(fallback = true)
public static Object set(EnsoContext ctx, Object value, Object warnings, InteropLibrary interop) {
return setGeneric(ctx, value, interop, warnings);
}
private static Object setGeneric(
EnsoContext ctx, Object value, InteropLibrary interop, Object warnings) {
try {
var size = interop.getArraySize(warnings);
if (size == 0) {
return value;
}
Warning[] warningsCast = new Warning[(int) size];
for (int i = 0; i < warningsCast.length; i++) {
warningsCast[i] = (Warning) interop.readArrayElement(warnings, i);
}
return WithWarnings.wrap(ctx, value, warningsCast);
} catch (UnsupportedMessageException | InvalidArrayIndexException ex) {
throw EnsoContext.get(interop).raiseAssertionPanic(interop, null, ex);
}
}
@CompilerDirectives.TruffleBoundary
@Override
public String toString() {
return value.toString();
}
@ExportLibrary(InteropLibrary.class)
public static final class Reassignment implements EnsoObject {
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() throws UnsupportedMessageException {
if (location == null) {
throw UnsupportedMessageException.create();
}
return location;
}
}
public long getSequenceId() {
return sequenceId;
}
@CompilerDirectives.TruffleBoundary
public Warning reassign(Node location) {
RootNode root = location.getRootNode();
SourceSection section = location.getEncapsulatingSourceSection();
Reassignment reassignment = new Reassignment(root == null ? "" : root.getName(), section);
return new Warning(value, origin, sequenceId, reassignments.prepend(reassignment));
}
@ExportMessage
boolean hasType() {
return true;
}
@ExportMessage
Type getType(@Bind("$node") Node node) {
return EnsoContext.get(node).getBuiltins().warning();
}
public static Warning wrapMapError(WarningsLibrary warningsLib, Warning warning, long index) {
var ctx = EnsoContext.get(warningsLib);
var error = warning.getValue();
var wrappedError = ctx.getBuiltins().error().makeMapError(index, error);
var wrappedWarning = Warning.create(ctx, wrappedError, warning.getOrigin());
return wrappedWarning;
}
}

View File

@ -1,374 +0,0 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.library.Message;
import com.oracle.truffle.api.library.ReflectionLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.node.callable.InteropMethodCallNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
import org.graalvm.collections.EconomicSet;
import org.graalvm.collections.Equivalence;
/**
* Represents a typical Enso <em>value with warnings</em>. As much of care as possible is taken to
* delegate all operations to the underlaying {@code value}. Warnings are considered {@link
* InteropLibrary#isException exceptional values} - e.g. one can check for them in Java polyglot
* code as:
*
* <pre>
* Value value = ...;
* if (value.fitsInLong() && value.isException()) {
* // probably an Integer with a warning
* try {
* warningMulti.throwException();
* } catch (PolyglotException ex) {
* System.out.println("Warnings attached to " + value.asLong() + " are " + ex.getMessage());
* }
* }
* </pre>
*/
@ExportLibrary(TypesLibrary.class)
@ExportLibrary(WarningsLibrary.class)
@ExportLibrary(ReflectionLibrary.class)
@ExportLibrary(value = InteropLibrary.class, delegateTo = "value")
public final class WithWarnings implements EnsoObject {
final Object value;
private final EconomicSet<Warning> warnings;
private final boolean limitReached;
private final int maxWarnings;
/**
* Creates a new instance of value wrapped in warnings. `limitReached` parameter allows for
* indicating if some custom warnings filtering on `warnings` have already been performed.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param limitReached if `true`, indicates that `warnings` have already been limited for a
* custom-method, `false` otherwise
* @param warnings non-empty warnings to be attached to a value
*/
private WithWarnings(Object value, int maxWarnings, boolean limitReached, Warning... warnings) {
assert isAcceptableValue(value);
this.warnings = createSetFromArray(maxWarnings, warnings);
assert this.warnings.size() > 0;
this.value = value;
this.limitReached = limitReached || this.warnings.size() >= maxWarnings;
this.maxWarnings = maxWarnings;
}
private WithWarnings(Object value, int maxWarnings, Warning... warnings) {
this(value, maxWarnings, false, warnings);
}
/**
* Creates a new instance of value wrapped in warnings. `limitReached` parameter allows for
* indicating if some custom warnings filtering on `additionalWarnings` have already been
* performed.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param warnings warnings originally attached to a value
* @param limitReached if `true`, indicates that `warnings` have already been limited for a
* custom-method, `false` otherwise
* @param additionalWarnings additional warnings to be appended to the list of `warnings`
*/
private WithWarnings(
Object value,
int maxWarnings,
EconomicSet<Warning> warnings,
boolean limitReached,
Warning... additionalWarnings) {
assert isAcceptableValue(value);
this.warnings = cloneSetAndAppend(maxWarnings, warnings, additionalWarnings);
assert this.warnings.size() > 0;
this.value = value;
this.limitReached = limitReached || this.warnings.size() >= maxWarnings;
this.maxWarnings = maxWarnings;
}
private WithWarnings(
Object value, int maxWarnings, EconomicSet<Warning> warnings, Warning... additionalWarnings) {
this(value, maxWarnings, warnings, false, additionalWarnings);
}
private static boolean isAcceptableValue(Object value) {
assert value != null;
assert !(value instanceof WithWarnings) : "Trying to double wrap WithWarnings " + value;
boolean goodValue =
value instanceof TruffleObject
|| value instanceof Long
|| value instanceof Double
|| value instanceof Boolean;
assert goodValue : "Unexpected value floating around " + value + " type: " + value.getClass();
return goodValue;
}
public static WithWarnings wrap(EnsoContext ctx, Object value, Warning... warnings) {
if (value instanceof WithWarnings with) {
return with.append(ctx, warnings);
} else {
return new WithWarnings(value, ctx.getWarningsLimit(), warnings);
}
}
public Object getValue() {
return value;
}
public WithWarnings append(EnsoContext ctx, boolean limitReached, Warning... newWarnings) {
return new WithWarnings(value, ctx.getWarningsLimit(), warnings, limitReached, newWarnings);
}
public WithWarnings append(EnsoContext ctx, Warning... newWarnings) {
return new WithWarnings(value, ctx.getWarningsLimit(), warnings, newWarnings);
}
public WithWarnings append(EnsoContext ctx, ArrayRope<Warning> newWarnings) {
return new WithWarnings(
value, ctx.getWarningsLimit(), warnings, newWarnings.toArray(Warning[]::new));
}
// Ignore the warnings cache in .value and re-fetch them using the WarningsLibrary.
// This is only used for shouldWrap=true.
private Warning[] getWarningsNoCache(WarningsLibrary warningsLibrary) {
if (warningsLibrary != null && warningsLibrary.hasWarnings(value)) {
try {
return warningsLibrary.getWarnings(value, null, true);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
return Warning.fromSetToArray(warnings);
}
}
public Warning[] getWarningsArray(WarningsLibrary warningsLibrary, boolean shouldWrap) {
Warning[] allWarnings;
if (warningsLibrary != null && warningsLibrary.hasWarnings(value)) {
try {
Warning[] valueWarnings = warningsLibrary.getWarnings(value, null, shouldWrap);
EconomicSet<Warning> tmp = cloneSetAndAppend(maxWarnings, warnings, valueWarnings);
allWarnings = Warning.fromSetToArray(tmp);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
allWarnings = Warning.fromSetToArray(warnings);
}
return allWarnings;
}
/**
* @return the number of warnings.
*/
public int getWarningsCount() {
return warnings.size();
}
public ArrayRope<Warning> getReassignedWarningsAsRope(Node location, boolean shouldWrap) {
return new ArrayRope<>(getReassignedWarnings(location, shouldWrap, null));
}
public Warning[] getReassignedWarnings(
Node location, boolean shouldWrap, WarningsLibrary warningsLibrary) {
Warning[] warnings = getWarningsArray(warningsLibrary, shouldWrap);
for (int i = 0; i < warnings.length; i++) {
warnings[i] = warnings[i].reassign(location);
}
return warnings;
}
public static WithWarnings appendTo(EnsoContext ctx, Object target, ArrayRope<Warning> warnings) {
if (target instanceof WithWarnings) {
return ((WithWarnings) target).append(ctx, warnings.toArray(Warning[]::new));
} else {
return new WithWarnings(target, ctx.getWarningsLimit(), warnings.toArray(Warning[]::new));
}
}
public static WithWarnings appendTo(EnsoContext ctx, Object target, Warning... warnings) {
return appendTo(ctx, target, false, warnings);
}
public static WithWarnings appendTo(
EnsoContext ctx, Object target, boolean reachedMaxCount, Warning... warnings) {
if (target instanceof WithWarnings) {
return ((WithWarnings) target).append(ctx, reachedMaxCount, warnings);
} else {
return new WithWarnings(target, ctx.getWarningsLimit(), reachedMaxCount, warnings);
}
}
@CompilerDirectives.TruffleBoundary
private PanicException asException(Node where) {
var rawWarn = this.getWarnings(where, false, WarningsLibrary.getUncached());
var ctx = EnsoContext.get(where);
var scopeOfAny = ctx.getBuiltins().any().getDefinitionScope();
var toText = UnresolvedSymbol.build("to_text", scopeOfAny);
var node = InteropMethodCallNode.getUncached();
var state = State.create(ctx);
var text = Text.empty();
for (var w : rawWarn) {
try {
var wText = node.execute(toText, state, new Object[] {w});
if (wText instanceof Text t) {
text = text.add(t);
}
} catch (ArityException e) {
throw ctx.raiseAssertionPanic(where, null, e);
}
}
return new PanicException(text, where);
}
@ExportMessage
Object send(Message message, Object[] args, @CachedLibrary(limit = "3") ReflectionLibrary lib)
throws Exception {
return lib.send(value, message, args);
}
@ExportMessage
boolean hasWarnings() {
return true;
}
@ExportMessage
Warning[] getWarnings(
Node location,
boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warningsLibrary) {
if (location != null) {
return getReassignedWarnings(location, shouldWrap, warningsLibrary);
} else {
if (shouldWrap) {
// In the wrapping case, we don't use the local cache in .values, since
// it contains unwrapped warnings. Instead, we fetch them again.
return getWarningsNoCache(warningsLibrary);
} else {
return Warning.fromSetToArray(warnings);
}
}
}
@ExportMessage
Object removeWarnings(@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException {
if (warnings.hasWarnings(value)) {
return warnings.removeWarnings(value);
} else {
return value;
}
}
@ExportMessage
public boolean isLimitReached() {
return limitReached;
}
@ExportMessage
boolean hasType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.hasType(value);
}
@ExportMessage
Type getType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.getType(value);
}
@ExportMessage
boolean hasSpecialDispatch() {
return true;
}
@ExportMessage
boolean isException() {
return true;
}
@ExportMessage
RuntimeException throwException(@Bind("$node") Node node) throws UnsupportedMessageException {
throw asException(node);
}
public static class WarningEquivalence extends Equivalence {
@Override
public boolean equals(Object a, Object b) {
if (a instanceof Warning thisObj && b instanceof Warning thatObj) {
return thisObj.getSequenceId() == thatObj.getSequenceId();
}
return false;
}
@Override
public int hashCode(Object o) {
return (int) ((Warning) o).getSequenceId();
}
}
@CompilerDirectives.TruffleBoundary
private EconomicSet<Warning> createSetFromArray(int maxWarnings, Warning[] entries) {
EconomicSet<Warning> set = EconomicSet.create(new WarningEquivalence());
for (int i = 0; i < entries.length; i++) {
if (set.size() == maxWarnings) {
return set;
}
set.add(entries[i]);
}
return set;
}
private EconomicSet<Warning> cloneSetAndAppend(
int maxWarnings, EconomicSet<Warning> initial, Warning[] entries) {
return initial.size() == maxWarnings
? initial
: cloneSetAndAppendSlow(maxWarnings, initial, entries);
}
@CompilerDirectives.TruffleBoundary
private EconomicSet<Warning> cloneSetAndAppendSlow(
int maxWarnings, EconomicSet<Warning> initial, Warning[] entries) {
EconomicSet<Warning> set = EconomicSet.create(new WarningEquivalence());
for (Warning warning : initial) {
if (set.size() == maxWarnings) {
return set;
}
set.add(warning);
}
for (int i = 0; i < entries.length; i++) {
if (set.size() == maxWarnings) {
return set;
}
set.add(entries[i]);
}
return set;
}
@Override
public String toString() {
return "WithWarnings{"
+ value
+ " has "
+ warnings.size()
+ " warnings"
+ (limitReached ? " (warnings limit reached)}" : "}");
}
}

View File

@ -20,8 +20,8 @@ import org.enso.interpreter.runtime.data.Type;
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.WithWarnings;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.warning.WithWarnings;
@GenerateUncached
public abstract class TypeOfNode extends Node {
@ -70,8 +70,8 @@ public abstract class TypeOfNode extends Node {
}
@Specialization
Object doWarning(WithWarnings value) {
return execute(value.getValue());
Object doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) {
return withoutWarning.execute(value.getValue());
}
static boolean isWithoutType(Object value, TypesLibrary types) {

View File

@ -22,9 +22,11 @@ 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.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
import org.enso.polyglot.data.TypeGraph;
/**
@ -57,6 +59,8 @@ import org.enso.polyglot.data.TypeGraph;
PanicSentinel.class,
EnsoHashMap.class,
Warning.class,
WithWarnings.class,
WarningsLibrary.class,
EnsoFile.class,
EnsoDate.class,
EnsoDateTime.class,

View File

@ -0,0 +1,209 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
import org.enso.interpreter.runtime.data.hash.HashMapSizeNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
@GenerateUncached
public abstract class AppendWarningNode extends Node {
public static AppendWarningNode build() {
return AppendWarningNodeGen.create();
}
public static AppendWarningNode getUncached() {
return AppendWarningNodeGen.getUncached();
}
/**
* Appends a warning to the given object.
*
* @param object Object that will have the warning appended
* @param warnings Either an array-like object containing warnings to append, or a single warning.
* It is expected that all the elements in the container are of {@link Warning} class.
* @return A wrapped object with warnings
*/
public abstract WithWarnings executeAppend(VirtualFrame frame, Object object, Object warnings);
@Specialization
WithWarnings doSingleWarning(
VirtualFrame frame,
Object object,
Warning warning,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
EnsoHashMap warnsMap;
int warnsLimit;
Object value;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
warnsMap = ((WithWarnings) object).warnings;
value = ((WithWarnings) object).value;
warnsLimit = ((WithWarnings) object).maxWarnings;
} else {
warnsMap = EnsoHashMap.empty();
value = object;
warnsLimit = EnsoContext.get(this).getWarningsLimit();
}
boolean isLimitReached;
if ((int) mapSizeNode.execute(warnsMap) < warnsLimit) {
warnsMap = mapInsertNode.execute(frame, warnsMap, warning.getSequenceId(), warning);
isLimitReached = false;
} else {
isLimitReached = true;
}
return new WithWarnings(value, warnsLimit, isLimitReached, warnsMap);
}
@Specialization
WithWarnings doMultipleWarningsArray(
VirtualFrame frame,
Object object,
Warning[] warnings,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
EnsoHashMap warnsMap;
int warnsLimit;
Object value;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
warnsMap = ((WithWarnings) object).warnings;
warnsLimit = ((WithWarnings) object).maxWarnings;
value = ((WithWarnings) object).value;
} else {
warnsMap = EnsoHashMap.empty();
warnsLimit = EnsoContext.get(this).getWarningsLimit();
value = object;
}
var isLimitReached = false;
for (var warn : warnings) {
if (mapSizeNode.execute(warnsMap) < warnsLimit) {
warnsMap = mapInsertNode.execute(frame, warnsMap, warn.getSequenceId(), warn);
} else {
isLimitReached = true;
break;
}
}
return new WithWarnings(value, warnsLimit, isLimitReached, warnsMap);
}
/**
* This specialization should be the most frequent - just wrapping the given {@code object} with
* warnings hash map
*/
@Specialization(guards = {"!isWithWarns(object)"})
WithWarnings doObjectMultipleWarningsHashMap(
VirtualFrame frame,
Object object,
EnsoHashMap newWarnsMap,
@Shared @Cached HashMapSizeNode mapSizeNode) {
assert !(object instanceof WithWarnings);
int warnsLimit = EnsoContext.get(this).getWarningsLimit();
var limitReached = mapSizeNode.execute(newWarnsMap) >= warnsLimit;
return new WithWarnings(object, warnsLimit, limitReached, newWarnsMap);
}
@Specialization
WithWarnings doWithWarnMultipleWarningsHashMap(
VirtualFrame frame,
WithWarnings withWarnings,
EnsoHashMap newWarnsMap,
@Cached HashMapInsertAllNode mapInsertAllNode,
@Shared @Cached HashMapSizeNode mapSizeNode) {
if (withWarnings.isLimitReached()) {
return withWarnings;
}
var maxWarns = withWarnings.maxWarnings;
var warnsMap = withWarnings.warnings;
var curWarnsCnt = (int) mapSizeNode.execute(warnsMap);
warnsMap =
mapInsertAllNode.executeInsertAll(frame, warnsMap, newWarnsMap, maxWarns - curWarnsCnt);
var isLimitReached =
mapSizeNode.execute(withWarnings.warnings) + mapSizeNode.execute(newWarnsMap) >= maxWarns;
return new WithWarnings(withWarnings.value, withWarnings.maxWarnings, isLimitReached, warnsMap);
}
@Specialization(guards = {"interop.hasArrayElements(warnings)", "!isWarnArray(warnings)"})
WithWarnings doMultipleWarningsInterop(
VirtualFrame frame,
Object object,
Object warnings,
@CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Cached ArrayLikeAtNode atNode,
@Cached ArrayLikeLengthNode lengthNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
assert !(warnings instanceof Warning[]);
EnsoHashMap warnsMap;
int warnsLimit;
Object value;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
value = ((WithWarnings) object).value;
warnsLimit = ((WithWarnings) object).maxWarnings;
warnsMap = ((WithWarnings) object).warnings;
} else {
value = object;
warnsLimit = EnsoContext.get(this).getWarningsLimit();
warnsMap = EnsoHashMap.empty();
}
var currWarnsCnt = mapSizeNode.execute(warnsMap);
var resWarningMap =
insertToWarningMap(
frame, warnsMap, warnings, warnsLimit, lengthNode, atNode, mapInsertNode, mapSizeNode);
var newWarnsCnt = lengthNode.executeLength(warnings);
var isLimitReached = currWarnsCnt + newWarnsCnt >= warnsLimit;
return new WithWarnings(value, warnsLimit, isLimitReached, resWarningMap);
}
/** Inserts all {@code warnings} to the {@code initialWarningMap}. */
private EnsoHashMap insertToWarningMap(
VirtualFrame frame,
EnsoHashMap initialWarningMap,
Object warnings,
int warnsLimit,
ArrayLikeLengthNode lengthNode,
ArrayLikeAtNode atNode,
HashMapInsertNode mapInsertNode,
HashMapSizeNode mapSizeNode) {
EnsoHashMap resWarningMap = initialWarningMap;
for (long i = 0; i < lengthNode.executeLength(warnings); i++) {
Warning warn;
try {
warn = (Warning) atNode.executeAt(warnings, i);
} catch (InvalidArrayIndexException e) {
throw CompilerDirectives.shouldNotReachHere(e);
} catch (ClassCastException e) {
throw EnsoContext.get(this).raiseAssertionPanic(this, "Expected warning object", e);
}
if (mapSizeNode.execute(resWarningMap) >= warnsLimit) {
return resWarningMap;
}
resWarningMap = mapInsertNode.execute(frame, resWarningMap, warn.getSequenceId(), warn);
}
return resWarningMap;
}
protected static boolean isWarnArray(Object obj) {
return obj instanceof Warning[];
}
protected static boolean isWithWarns(Object obj) {
return obj instanceof WithWarnings;
}
}

View File

@ -0,0 +1,70 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import java.util.Arrays;
import java.util.Comparator;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
@BuiltinMethod(
type = "Warning",
name = "get_all_vector",
description = "Gets all the warnings associated with the value.",
autoRegister = false)
public abstract class GetAllWarningsNode extends Node {
public static GetAllWarningsNode build() {
return GetAllWarningsNodeGen.create();
}
public abstract Object execute(@AcceptsWarning Object value, boolean shouldWrap);
@Specialization
Object doWithWarn(
WithWarnings value,
boolean shouldWrap,
@Shared @CachedLibrary(limit = "3") WarningsLibrary warningsLib,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Cached HashMapInsertAllNode mapInsertAllNode) {
var warns = value.getWarningsArray(shouldWrap, warningsLib, mapInsertAllNode, interop);
sortArray(warns);
return ArrayLikeHelpers.asVectorEnsoObjects(warns);
}
@Fallback
Object doGeneric(
Object value,
boolean shouldWrap,
@Shared @CachedLibrary(limit = "3") WarningsLibrary warningsLib,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop) {
assert !(value instanceof WithWarnings);
if (warningsLib.hasWarnings(value)) {
try {
var warnsMap = warningsLib.getWarnings(value, shouldWrap);
var warnings = Warning.fromMapToArray(warnsMap, interop);
sortArray(warnings);
return ArrayLikeHelpers.asVectorEnsoObjects(warnings);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLib).raiseAssertionPanic(warningsLib, null, e);
}
} else {
return ArrayLikeHelpers.asVectorEmpty();
}
}
@TruffleBoundary
private static void sortArray(Warning[] arr) {
Arrays.sort(arr, Comparator.comparing(Warning::getSequenceId).reversed());
}
}

View File

@ -0,0 +1,32 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(
type = "Warning",
name = "limit_reached",
description =
"Returns `true` if the maximal number of warnings has been reached, `false` otherwise.",
autoRegister = false)
public abstract class LimitReachedNode extends Node {
public static LimitReachedNode build() {
return LimitReachedNodeGen.create();
}
public abstract boolean execute(@AcceptsWarning Object value);
@Specialization
boolean doWithWarns(WithWarnings withWarns) {
return withWarns.isLimitReached();
}
@Fallback
boolean doGeneric(Object value, @CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
return warnsLib.hasWarnings(value) && warnsLib.isLimitReached(value);
}
}

View File

@ -0,0 +1,118 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
@BuiltinMethod(
type = "Warning",
name = "set_array",
description = "Sets all the warnings associated with the value.",
autoRegister = false)
@GenerateUncached
public abstract class SetWarningsNode extends Node {
public static SetWarningsNode build() {
return SetWarningsNodeGen.create();
}
public abstract Object execute(VirtualFrame frame, @AcceptsWarning Object value, Object warnings);
@Specialization(guards = "isEmpty(warnings, interop)")
Object doEmpty(
VirtualFrame frame,
Object object,
Object warnings,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
return ((WithWarnings) object).value;
} else {
return object;
}
}
@Specialization(guards = "!isEmpty(warnings, interop)")
Object doSetArray(
VirtualFrame frame,
Object object,
Warning[] warnings,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
var warnMap = EnsoHashMap.empty();
for (var warn : warnings) {
warnMap = mapInsertNode.execute(frame, warnMap, warn.getSequenceId(), warn);
}
var maxWarns = EnsoContext.get(this).getWarningsLimit();
var isLimitReached = warnings.length >= maxWarns;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
return new WithWarnings(((WithWarnings) object).value, maxWarns, isLimitReached, warnMap);
} else {
return new WithWarnings(object, maxWarns, isLimitReached, warnMap);
}
}
@Specialization(guards = {"!isEmpty(warnings, interop)", "!isWarnArray(warnings)"})
Object doSetInteropArray(
VirtualFrame frame,
Object object,
Object warnings,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
assert !(warnings instanceof Warning[]);
var warnMap = EnsoHashMap.empty();
var ctx = EnsoContext.get(this);
try {
var size = interop.getArraySize(warnings);
for (long i = 0; i < interop.getArraySize(warnings); i++) {
var warn = (Warning) interop.readArrayElement(warnings, i);
warnMap = mapInsertNode.execute(frame, warnMap, warn.getSequenceId(), warn);
}
var maxWarns = ctx.getWarningsLimit();
var isLimitReached = size >= maxWarns;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
return new WithWarnings(((WithWarnings) object).value, maxWarns, isLimitReached, warnMap);
} else {
return new WithWarnings(object, maxWarns, isLimitReached, warnMap);
}
} catch (UnsupportedMessageException | InvalidArrayIndexException e) {
throw CompilerDirectives.shouldNotReachHere(e);
} catch (ClassCastException e) {
throw ctx.raiseAssertionPanic(this, "Expected Warning, got something else", e);
}
}
protected static boolean isEmpty(Object warns, InteropLibrary interop) {
if (warns instanceof Warning[] warnsArray) {
return warnsArray.length == 0;
}
if (interop.hasArrayElements(warns)) {
try {
return interop.getArraySize(warns) == 0;
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
}
return false;
}
protected static boolean isWarnArray(Object obj) {
return obj instanceof Warning[];
}
}

View File

@ -0,0 +1,134 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.StopIterationException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
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.dsl.Builtin;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@Builtin(pkg = "error", stdlibName = "Standard.Base.Warning.Warning")
@ExportLibrary(TypesLibrary.class)
public final class Warning implements EnsoObject {
private final Object value;
private final Object origin;
private final long sequenceId;
private Warning(Object value, Object origin, long sequenceId) {
this.value = value;
this.origin = origin;
this.sequenceId = sequenceId;
}
@Builtin.Method(name = "value", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getValue() {
return value;
}
@Builtin.Method(name = "origin", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getOrigin() {
return origin;
}
@Builtin.Method(
name = "create",
description = "Creates a new instance of the primitive warning value.",
autoRegister = false)
@Builtin.Specialize
public static Warning create(EnsoContext ctx, Object payload, Object origin) {
return new Warning(payload, origin, ctx.nextSequenceId());
}
@Builtin.Method(
name = "attach_with_stacktrace",
description = "Attaches the given warning to the value.",
autoRegister = false)
@Builtin.Specialize
public static WithWarnings attach(
EnsoContext ctx,
Object value,
Object warning,
Object origin,
@Cached AppendWarningNode appendWarningNode) {
var warn = new Warning(warning, origin, ctx.nextSequenceId());
return appendWarningNode.executeAppend(null, value, warn);
}
/** Slow version of {@link #fromMapToArray(EnsoHashMap, InteropLibrary)}. */
@TruffleBoundary
public static Warning[] fromMapToArray(EnsoHashMap map) {
return fromMapToArray(map, InteropLibrary.getUncached());
}
public static Warning[] fromMapToArray(EnsoHashMap map, InteropLibrary interop) {
assert interop.hasHashEntries(map);
Warning[] warns = null;
try {
long mapSize = interop.getHashSize(map);
assert mapSize < Integer.MAX_VALUE;
warns = new Warning[(int) mapSize];
var hashValuesIt = interop.getHashValuesIterator(map);
assert interop.isIterator(hashValuesIt);
int warnsIdx = 0;
while (interop.hasIteratorNextElement(hashValuesIt)) {
var value = interop.getIteratorNextElement(hashValuesIt);
warns[warnsIdx] = (Warning) value;
warnsIdx++;
}
return warns;
} catch (UnsupportedMessageException | ClassCastException | ArrayIndexOutOfBoundsException e) {
throw CompilerDirectives.shouldNotReachHere(e);
} catch (StopIterationException e) {
assert warns != null;
return warns;
}
}
public static EnsoHashMap fromArrayToMap(Warning[] warnings, HashMapInsertNode mapInsertNode) {
var map = EnsoHashMap.empty();
for (var warn : warnings) {
map = mapInsertNode.execute(null, map, warn.getSequenceId(), warn);
}
return map;
}
@CompilerDirectives.TruffleBoundary
@Override
public String toString() {
return value.toString();
}
public long getSequenceId() {
return sequenceId;
}
@ExportMessage
boolean hasType() {
return true;
}
@ExportMessage
Type getType(@Bind("$node") Node node) {
return EnsoContext.get(node).getBuiltins().warning();
}
public static Warning wrapMapError(WarningsLibrary warningsLib, Warning warning, long index) {
var ctx = EnsoContext.get(warningsLib);
var error = warning.getValue();
var wrappedError = ctx.getBuiltins().error().makeMapError(index, error);
return Warning.create(ctx, wrappedError, warning.getOrigin());
}
}

View File

@ -1,10 +1,10 @@
package org.enso.interpreter.runtime.error;
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.GenerateLibrary;
import com.oracle.truffle.api.library.Library;
import com.oracle.truffle.api.library.LibraryFactory;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
@GenerateLibrary
public abstract class WarningsLibrary extends Library {
@ -44,15 +44,14 @@ public abstract class WarningsLibrary extends Library {
/**
* Returns all unique warnings associated with the receiver.
*
* @param receiver the receiver to analyze
* @param location optional parameter specifying the node to which the warnings should be
* reassigned to
* @param receiver the receiver to get the warnings from
* @param shouldWrap if true, warnings attached to elements in array-likes are wrapped in
* Map_Error
* @return the associated warnings
* @return the associated warnings as map - keys are {@code sequenceId} and values are warnings.
* Not null
*/
@GenerateLibrary.Abstract(ifExported = {"hasWarnings"})
public Warning[] getWarnings(Object receiver, Node location, boolean shouldWrap)
public EnsoHashMap getWarnings(Object receiver, boolean shouldWrap)
throws UnsupportedMessageException {
throw UnsupportedMessageException.create();
}

View File

@ -0,0 +1,282 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.library.Message;
import com.oracle.truffle.api.library.ReflectionLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.node.callable.InteropMethodCallNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNodeGen;
import org.enso.interpreter.runtime.data.hash.HashMapSizeNode;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNodeGen;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNodeGen;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
/**
* Represents a typical Enso <em>value with warnings</em>. As much of care as possible is taken to
* delegate all operations to the underlaying {@code value}. Warnings are considered {@link
* InteropLibrary#isException exceptional values} - e.g. one can check for them in Java polyglot
* code as:
*
* <pre>
* Value value = ...;
* if (value.fitsInLong() && value.isException()) {
* // probably an Integer with a warning
* try {
* warningMulti.throwException();
* } catch (PolyglotException ex) {
* System.out.println("Warnings attached to " + value.asLong() + " are " + ex.getMessage());
* }
* }
* </pre>
*/
@ExportLibrary(TypesLibrary.class)
@ExportLibrary(WarningsLibrary.class)
@ExportLibrary(ReflectionLibrary.class)
@ExportLibrary(value = InteropLibrary.class, delegateTo = "value")
public final class WithWarnings implements EnsoObject {
final Object value;
/**
* Internal storage for warnings is a PE-friendly hash map. The key is a sequence ID (gathered
* from {@link EnsoContext#nextSequenceId()} and the value is the warning itself. Note that it is
* essential that sequenceId is the key so that the warnings are not duplicated.
*/
final EnsoHashMap warnings;
private final boolean limitReached;
final int maxWarnings;
/**
* Creates a new instance of value wrapped in warnings.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param warningsMap warnings originally attached to a value
* @param limitReached if `true`, indicates that `warnings` have already been limited for a
* custom-method, `false` otherwise
*/
WithWarnings(Object value, int maxWarnings, boolean limitReached, EnsoHashMap warningsMap) {
assert isAcceptableValue(value);
var mapSizeNode = HashMapSizeNode.getUncached();
assert mapSizeNode.execute(warningsMap) <= maxWarnings;
if (limitReached) {
assert mapSizeNode.execute(warningsMap) == maxWarnings;
}
this.value = value;
this.maxWarnings = maxWarnings;
this.limitReached = limitReached || mapSizeNode.execute(warningsMap) >= maxWarnings;
this.warnings = warningsMap;
}
/**
* Explicit creation of WithWarnings. Allows to set a specific {@code maxWarnings} count, which
* cannot be achieved by using warning-handling nodes like {@link AppendWarningNode}. If {@code
* maxWarnings} does not need to be set explicitly, use nodes to create WithWarning objects
* instead.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param limitReached if `true`, no other warnings will be attached to the {@code value}.
* @param warnings array of warnings to be attached to the {@code value}
*/
public static WithWarnings create(
Object value, int maxWarnings, boolean limitReached, Warning[] warnings) {
assert warnings.length <= maxWarnings;
if (limitReached) {
assert warnings.length == maxWarnings;
}
var warnMap = Warning.fromArrayToMap(warnings, HashMapInsertNodeGen.getUncached());
return new WithWarnings(value, maxWarnings, limitReached, warnMap);
}
private static boolean isAcceptableValue(Object value) {
assert value != null;
assert !(value instanceof WithWarnings) : "Trying to double wrap WithWarnings " + value;
boolean goodValue =
value instanceof TruffleObject
|| value instanceof Long
|| value instanceof Double
|| value instanceof Boolean;
assert goodValue : "Unexpected value floating around " + value + " type: " + value.getClass();
return goodValue;
}
public Object getValue() {
return value;
}
// Ignore the warnings cache in .value and re-fetch them using the WarningsLibrary.
// This is only used for shouldWrap=true.
private EnsoHashMap getWarningsNoCache(
WarningsLibrary warningsLibrary, ArrayLikeLengthNode lengthNode, ArrayLikeAtNode atNode) {
assert warningsLibrary != null;
if (warningsLibrary.hasWarnings(value)) {
try {
return warningsLibrary.getWarnings(value, true);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
return warnings;
}
}
/**
* Slow version of {@link #getWarningsArray(boolean, WarningsLibrary, HashMapInsertAllNode,
* InteropLibrary)} that uses uncached version of nodes and libraries parameters.
*/
public Warning[] getWarningsArray(boolean shouldWrap) {
return getWarningsArray(
shouldWrap,
WarningsLibrary.getUncached(),
HashMapInsertAllNode.getUncached(),
InteropLibrary.getUncached());
}
public Warning[] getWarningsArray(
boolean shouldWrap,
WarningsLibrary warningsLibrary,
HashMapInsertAllNode mapInsertAllNode,
InteropLibrary interop) {
Warning[] allWarnsArray;
if (warningsLibrary != null && warningsLibrary.hasWarnings(value)) {
try {
var valueWarnings = warningsLibrary.getWarnings(value, shouldWrap);
var allWarns =
mapInsertAllNode.executeInsertAll(null, warnings, valueWarnings, maxWarnings);
allWarnsArray = Warning.fromMapToArray(allWarns, interop);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
allWarnsArray = Warning.fromMapToArray(warnings, interop);
}
return allWarnsArray;
}
@CompilerDirectives.TruffleBoundary
private PanicException asException(Node where) {
var warnsMap =
this.getWarnings(
false,
WarningsLibrary.getUncached(),
ArrayLikeAtNodeGen.getUncached(),
ArrayLikeLengthNodeGen.getUncached());
var warns = Warning.fromMapToArray(warnsMap);
var ctx = EnsoContext.get(where);
var scopeOfAny = ctx.getBuiltins().any().getDefinitionScope();
var toText = UnresolvedSymbol.build("to_text", scopeOfAny);
var node = InteropMethodCallNode.getUncached();
var state = State.create(ctx);
var text = Text.empty();
for (var w : warns) {
try {
var wText = node.execute(toText, state, new Object[] {w});
if (wText instanceof Text t) {
text = text.add(t);
}
} catch (ArityException e) {
throw ctx.raiseAssertionPanic(where, null, e);
}
}
return new PanicException(text, where);
}
@ExportMessage
Object send(Message message, Object[] args, @CachedLibrary(limit = "3") ReflectionLibrary lib)
throws Exception {
return lib.send(value, message, args);
}
@ExportMessage
boolean hasWarnings() {
return true;
}
@ExportMessage
EnsoHashMap getWarnings(
boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warningsLibrary,
@Cached ArrayLikeAtNode atNode,
@Cached ArrayLikeLengthNode lengthNode) {
if (shouldWrap) {
// In the wrapping case, we don't use the local cache in .values, since
// it contains unwrapped warnings. Instead, we fetch them again.
return getWarningsNoCache(warningsLibrary, lengthNode, atNode);
} else {
return warnings;
}
}
@ExportMessage
Object removeWarnings(@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException {
if (warnings.hasWarnings(value)) {
return warnings.removeWarnings(value);
} else {
return value;
}
}
@ExportMessage
public boolean isLimitReached() {
return limitReached;
}
@ExportMessage
boolean hasType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.hasType(value);
}
@ExportMessage
Type getType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.getType(value);
}
@ExportMessage
boolean hasSpecialDispatch() {
return true;
}
@ExportMessage
boolean isException() {
return true;
}
@ExportMessage
RuntimeException throwException(@Bind("$node") Node node) {
throw asException(node);
}
@Override
public String toString() {
return "WithWarnings{"
+ value
+ " has "
+ HashMapSizeNode.getUncached().execute(warnings)
+ " warnings"
+ (limitReached ? " (warnings limit reached)}" : "}");
}
}

View File

@ -5,7 +5,11 @@ 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. */
/**
* An interface marking an argument as allowing it to accept a WithWarnings. Parameters annotated
* with this annotation will not have their warnings stripped, so if it is of type {@link
* org.enso.interpreter.runtime.warning.WithWarnings} it will not be unwrapped.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.SOURCE)
public @interface AcceptsWarning {}

View File

@ -16,7 +16,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
public @interface BuiltinMethod {
/**
* @return the language-level type of {@code self} argument.
* @return the language-level type of {@code self} argument. Not a fully-qualified name.
*/
String type();

View File

@ -130,6 +130,8 @@ public class MethodProcessor
"com.oracle.truffle.api.CompilerDirectives",
"com.oracle.truffle.api.dsl.UnsupportedSpecializationException",
"com.oracle.truffle.api.frame.VirtualFrame",
"com.oracle.truffle.api.interop.InteropLibrary",
"com.oracle.truffle.api.interop.UnsupportedMessageException",
"com.oracle.truffle.api.nodes.ControlFlowException",
"com.oracle.truffle.api.nodes.Node",
"com.oracle.truffle.api.nodes.NodeInfo",
@ -146,14 +148,18 @@ public class MethodProcessor
"org.enso.interpreter.runtime.callable.function.FunctionSchema",
"org.enso.interpreter.runtime.EnsoContext",
"org.enso.interpreter.runtime.builtin.Builtins",
"org.enso.interpreter.runtime.data.ArrayRope",
"org.enso.interpreter.runtime.data.hash.EnsoHashMap",
"org.enso.interpreter.runtime.data.hash.HashMapInsertNode",
"org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode",
"org.enso.interpreter.runtime.data.text.Text",
"org.enso.interpreter.runtime.error.DataflowError",
"org.enso.interpreter.runtime.error.PanicException",
"org.enso.interpreter.runtime.error.Warning",
"org.enso.interpreter.runtime.error.WithWarnings",
"org.enso.interpreter.runtime.state.State",
"org.enso.interpreter.runtime.type.TypesGen");
"org.enso.interpreter.runtime.type.TypesGen",
"org.enso.interpreter.runtime.warning.Warning",
"org.enso.interpreter.runtime.warning.WarningsLibrary",
"org.enso.interpreter.runtime.warning.WithWarnings",
"org.enso.interpreter.runtime.warning.AppendWarningNode");
/** List of exception types that should be caught from the builtin's execute method. */
private static final List<String> handleExceptionTypes =
@ -190,6 +196,13 @@ public class MethodProcessor
+ " extends BuiltinRootNode implements InlineableNode.Root {");
}
out.println(" private @Child " + methodDefinition.getOriginalClassName() + " bodyNode;");
out.println(
" private @Child AppendWarningNode appendWarningNode = AppendWarningNode.build();");
out.println(
" private @Child WarningsLibrary warnLib ="
+ " WarningsLibrary.getFactory().createDispatched(5);");
out.println(
" private @Child HashMapInsertAllNode mapInsertAllNode = HashMapInsertAllNode.build();");
out.println();
out.println(" private static final class Internals {");
out.println(" Internals(boolean s) {");
@ -282,27 +295,44 @@ public class MethodProcessor
+ " body = "
+ methodDefinition.getConstructorExpression()
+ ";");
out.println(
" private @Child AppendWarningNode appendWarningNode ="
+ " AppendWarningNode.build();");
out.println(
" private @Child WarningsLibrary warnLib ="
+ " WarningsLibrary.getFactory().createDispatched(5);");
out.println(
" private @Child HashMapInsertAllNode mapInsertAllNode ="
+ " HashMapInsertAllNode.build();");
out.println();
out.println(" @Override");
out.println(" public Object call(VirtualFrame frame, Object[] args) {");
out.println(" return handleExecute(frame, extra, body, args);");
out.println(
" return handleExecute(frame, extra, body, appendWarningNode, warnLib,"
+ " mapInsertAllNode, args);");
out.println(" }");
out.println(" }");
out.println();
out.println(" return new Inlineable();");
out.println(" }");
}
out.println();
out.println(" @Override");
out.println(" public Object execute(VirtualFrame frame) {");
if (methodDefinition.needsFrame()) {
out.println(" var args = frame.getArguments();");
} else {
out.println(
" return handleExecute(frame, this.internals, bodyNode, frame.getArguments());");
" return handleExecute(frame, this.internals, bodyNode, this.appendWarningNode,"
+ " this.warnLib, this.mapInsertAllNode, frame.getArguments());");
out.println(" }");
out.println(
" private static Object handleExecute(VirtualFrame frame, Internals internals, "
+ methodDefinition.getOriginalClassName()
+ " bodyNode, Object[] args) {");
+ " bodyNode, AppendWarningNode appendWarningNode, WarningsLibrary warnLib,"
+ " HashMapInsertAllNode mapInsertAllNode, Object[] args) {");
}
out.println(" var prefix = internals.staticOfInstanceMethod ? 1 : 0;");
out.println(" State state = Function.ArgumentsHelper.getState(args);");
@ -346,8 +376,8 @@ public class MethodProcessor
out.println(" internals.anyWarningsProfile.enter();");
out.println(" Object result;");
out.println(wrapInTryCatch("result = " + executeCall + ";", 6));
out.println(" EnsoContext ctx = EnsoContext.get(bodyNode);");
out.println(" return WithWarnings.appendTo(ctx, result, gatheredWarnings);");
out.println(
" return appendWarningNode.executeAppend(frame, result, gatheredWarnings);");
out.println(" } else {");
out.println(wrapInTryCatch("return " + executeCall + ";", 6));
out.println(" }");
@ -598,28 +628,27 @@ public class MethodProcessor
return false;
} else {
out.println(" boolean anyWarnings = false;");
out.println(" ArrayRope<Warning> gatheredWarnings = new ArrayRope<>();");
out.println(" int maxWarnings = EnsoContext.get(bodyNode).getWarningsLimit();");
out.println(" EnsoHashMap gatheredWarnings = EnsoHashMap.empty();");
for (var arg : argsToCheck) {
String argCode = arrayRead(argumentsArray, arg.getPosition());
out.println(
" if ("
+ arrayRead(argumentsArray, arg.getPosition())
+ " instanceof WithWarnings) {");
+ " instanceof WithWarnings withWarnings) {");
out.println(
" internals." + mkArgumentInternalVarName(arg) + WARNING_PROFILE + ".enter();");
out.println(" anyWarnings = true;");
out.println(" try {"); // begin try
out.println(" var warns = warnLib.getWarnings(withWarnings, false);");
out.println(" " + argCode + " = withWarnings.getValue();");
out.println(
" WithWarnings withWarnings = (WithWarnings) "
+ arrayRead(argumentsArray, arg.getPosition())
+ ";");
out.println(
" "
+ arrayRead(argumentsArray, arg.getPosition())
+ " = withWarnings.getValue();");
out.print(
"""
gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarningsAsRope(bodyNode, false));
""");
out.println(" }");
" gatheredWarnings = mapInsertAllNode.executeInsertAll(frame, gatheredWarnings,"
+ " warns, maxWarnings);");
out.println(" } catch (UnsupportedMessageException e) {"); // end try
out.println(" throw CompilerDirectives.shouldNotReachHere(e);");
out.println(" }"); // end catch
out.println(" }"); // end hasWarnings
}
return true;
}

View File

@ -122,7 +122,7 @@ class CachedLibraryMethodParameter extends SpecializedMethodParameter {
public static final String INTEROP_LIBRARY = "com.oracle.truffle.api.interop.InteropLibrary";
public static final String WARNINGS_LIBRARY =
"org.enso.interpreter.runtime.error.WarningsLibrary";
"org.enso.interpreter.runtime.warning.WarningsLibrary";
}
class InjectedMethodParameter extends SpecializedMethodParameter {

View File

@ -14,6 +14,7 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import org.apache.commons.lang3.StringUtils;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.Builtin;
@ -160,12 +161,16 @@ public final class SpecializedMethodsGenerator extends MethodGenerator {
paramss.forEach(
(k, v) -> {
if (v.size() != elements.size()) {
throw new RuntimeException(
"Restriction: Specialized methods have to have equal number of parameters.\n"
+ "Expected "
+ elements.size()
+ ", got "
+ v.size());
processingEnv
.getMessager()
.printMessage(
Kind.ERROR,
"Restriction: Specialized methods have to have equal number of parameters.\n"
+ "Expected "
+ elements.size()
+ ", got "
+ v.size(),
elements.get(0));
}
MethodParameter p = v.get(0);

View File

@ -46,16 +46,6 @@ get_foo x = x.foo
unwrap x = Integer.from x
reassign_test x =
consed = Wrap.Value x
reconsed = rewrap consed
i = unwrap reconsed
rereconsed = Wrap.Value i
x1 = get_foo rereconsed
prim_sum = 1 + x1
r = poly_sum prim_sum 1
r
baz value = Warning.attach value "I have warned you"
bar value = baz value
foo value = bar value
@ -165,21 +155,6 @@ add_specs suite_builder = suite_builder.group "Dataflow Warnings" group_builder-
relevant = warning_stack . drop (..Last current.length)
relevant.map .name . should_equal (['baz', 'bar', 'foo'].map ('Warnings_Spec.'+))
group_builder.specify "should attach reassignment info in the last-reassigned-first order" <|
x = Warning.attach "warn!" 1
r = reassign_test x
warn = Warning.get_all r . first
reassignments = warn.reassignments.map .name
expected_stack = []
+ [ 'Warnings_Spec.poly_sum' ]
+ [ 'Integer.+' ]
+ [ 'Warnings_Spec.get_foo' ]
+ [ 'Wrap.Value' ]
+ [ 'Integer.from' ]
+ [ 'Warnings_Spec.rewrap' ]
+ [ 'Wrap.Value' ]
reassignments.should_equal expected_stack.to_vector
group_builder.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