mirror of
https://github.com/enso-org/enso.git
synced 2024-11-29 17:22:57 +03:00
Fix Runtime.assert
(#8742)
This commit is contained in:
parent
58cf4e5244
commit
6ae35abc46
@ -8,10 +8,12 @@ import project.Errors.Common.Assertion_Error
|
||||
import project.Errors.Common.Forbidden_Operation
|
||||
import project.Errors.Common.Type_Error
|
||||
import project.Function.Function
|
||||
import project.IO
|
||||
import project.Nothing.Nothing
|
||||
import project.Panic.Panic
|
||||
import project.Polyglot.Polyglot
|
||||
import project.Runtime.Source_Location.Source_Location
|
||||
import project.System
|
||||
from project.Data.Index_Sub_Range.Index_Sub_Range import First, Last
|
||||
from project.Data.Text.Extensions import all
|
||||
from project.Runtime.Context import Input, Output
|
||||
@ -58,13 +60,18 @@ gc = @Builtin_Method "Runtime.gc"
|
||||
Asserts that the given action succeeds, otherwise throws a panic.
|
||||
|
||||
Assertions are disable by default, meaning that call to this method is
|
||||
a no-op. To enable assertions, set the environment variable
|
||||
`ENSO_ENABLE_ASSERTIONS=true`
|
||||
assert : Boolean -> Text -> Nothing ! Assertion_Error
|
||||
assert ~action message="" = assert_builtin action message
|
||||
a no-op. To enable assertions, either set the environment variable
|
||||
`ENSO_ENABLE_ASSERTIONS=true` or enable JVM assertions by passing `-ea`
|
||||
cmd line option to java.
|
||||
assert : Boolean -> Text -> Nothing
|
||||
assert (~action : Boolean) (message : Text = "") =
|
||||
if assertions_enabled.not then Nothing else
|
||||
if action then Nothing else
|
||||
Panic.throw <| Assertion_Error.Error message
|
||||
|
||||
## PRIVATE
|
||||
assert_builtin ~action message = @Builtin_Method "Runtime.assert_builtin"
|
||||
Returns True if assertions are enabled.
|
||||
assertions_enabled = @Builtin_Method "Runtime.assertions_enabled"
|
||||
|
||||
## PRIVATE
|
||||
ADVANCED
|
||||
|
@ -1,98 +0,0 @@
|
||||
package org.enso.interpreter.node.expression.builtin.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Idempotent;
|
||||
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.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode;
|
||||
import org.enso.interpreter.runtime.EnsoContext;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
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.state.State;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Runtime",
|
||||
name = "assert_builtin",
|
||||
description = "Asserts that the given condition is true",
|
||||
autoRegister = false)
|
||||
public abstract class AssertNode extends Node {
|
||||
|
||||
public static AssertNode build() {
|
||||
return AssertNodeGen.create();
|
||||
}
|
||||
|
||||
public abstract Object execute(VirtualFrame frame, State state, @Suspend Object action, Text msg);
|
||||
|
||||
@Idempotent
|
||||
protected boolean isAssertionsEnabled() {
|
||||
return EnsoContext.get(this).isAssertionsEnabled();
|
||||
}
|
||||
|
||||
@Specialization(guards = "!isAssertionsEnabled()")
|
||||
Object doAssertionsDisabled(VirtualFrame frame, State state, Object action, Text msg) {
|
||||
return EnsoContext.get(this).getNothing();
|
||||
}
|
||||
|
||||
@Specialization(replaces = "doAssertionsDisabled")
|
||||
Object doAssertionsEnabled(
|
||||
VirtualFrame frame,
|
||||
State state,
|
||||
Object action,
|
||||
Text msg,
|
||||
@Cached("create()") ThunkExecutorNode thunkExecutorNode,
|
||||
@Cached BranchProfile resultIsNotAtomProfile) {
|
||||
var ctx = EnsoContext.get(this);
|
||||
var builtins = ctx.getBuiltins();
|
||||
Object actionRes =
|
||||
thunkExecutorNode.executeThunk(frame, action, state, BaseNode.TailStatus.TAIL_DIRECT);
|
||||
if (actionRes instanceof Atom resAtom) {
|
||||
var isTrue = resAtom.getConstructor() == builtins.bool().getTrue();
|
||||
if (isTrue) {
|
||||
return ctx.getNothing();
|
||||
} else {
|
||||
throw new PanicException(builtins.error().makeAssertionError(msg), this);
|
||||
}
|
||||
} else {
|
||||
resultIsNotAtomProfile.enter();
|
||||
return checkResultSlowPath(actionRes, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
private Object checkResultSlowPath(Object actionRes, Text msg) {
|
||||
var ctx = EnsoContext.get(this);
|
||||
var builtins = ctx.getBuiltins();
|
||||
try {
|
||||
if (InteropLibrary.getUncached().asBoolean(actionRes)) {
|
||||
return ctx.getNothing();
|
||||
} else {
|
||||
throw new PanicException(builtins.error().makeAssertionError(msg), this);
|
||||
}
|
||||
} catch (UnsupportedMessageException e) {
|
||||
if (actionRes instanceof DataflowError dataflowError) {
|
||||
var txt = Text.create("Result of assert action is a dataflow error: " + dataflowError);
|
||||
throw new PanicException(builtins.error().makeAssertionError(txt), this);
|
||||
} else {
|
||||
var typeError =
|
||||
builtins
|
||||
.error()
|
||||
.makeTypeError(
|
||||
builtins.bool().getType(),
|
||||
TypeOfNode.getUncached().execute(actionRes),
|
||||
"Result of assert action");
|
||||
throw new PanicException(typeError, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.enso.interpreter.node.expression.builtin.runtime;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.EnsoContext;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Runtime",
|
||||
name = "assertions_enabled",
|
||||
description = "Returns True iff assertions are enabled")
|
||||
public class IsAssertionEnabledNode extends Node {
|
||||
public static IsAssertionEnabledNode build() {
|
||||
return new IsAssertionEnabledNode();
|
||||
}
|
||||
|
||||
public boolean execute(VirtualFrame frame) {
|
||||
return EnsoContext.get(this).isAssertionsEnabled();
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AssertionsTest extends TestBase {
|
||||
|
||||
private static Context ctx;
|
||||
|
||||
private static final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
@ -63,9 +64,9 @@ public class AssertionsTest extends TestBase {
|
||||
TestBase.evalModule(
|
||||
ctx,
|
||||
"""
|
||||
from Standard.Base import False, Runtime
|
||||
main = Runtime.assert False
|
||||
""");
|
||||
from Standard.Base import False, Runtime
|
||||
main = Runtime.assert False
|
||||
""");
|
||||
fail("Should throw Assertion_Error");
|
||||
} catch (PolyglotException e) {
|
||||
assertThat(e.getGuestObject().isException(), is(true));
|
||||
@ -78,9 +79,9 @@ public class AssertionsTest extends TestBase {
|
||||
TestBase.evalModule(
|
||||
ctx,
|
||||
"""
|
||||
from Standard.Base import False, Runtime
|
||||
main = Runtime.assert False 'My fail message'
|
||||
""");
|
||||
from Standard.Base import False, Runtime
|
||||
main = Runtime.assert False 'My fail message'
|
||||
""");
|
||||
fail("Should throw Assertion_Error");
|
||||
} catch (PolyglotException e) {
|
||||
assertThat(
|
||||
@ -95,17 +96,18 @@ public class AssertionsTest extends TestBase {
|
||||
TestBase.evalModule(
|
||||
ctx,
|
||||
"""
|
||||
from Standard.Base import False, Runtime
|
||||
foo = Runtime.assert False 'My fail message'
|
||||
main = foo
|
||||
""");
|
||||
from Standard.Base import False, Runtime
|
||||
foo = Runtime.assert False 'My fail message'
|
||||
main = foo
|
||||
""");
|
||||
fail("Should throw Assertion_Error");
|
||||
} catch (PolyglotException e) {
|
||||
assertThat(e.getStackTrace().length, greaterThan(3));
|
||||
assertThat(e.getStackTrace()[0].toString(), containsString("Runtime.assert_builtin"));
|
||||
assertThat(e.getStackTrace().length, greaterThan(5));
|
||||
assertThat(e.getStackTrace()[0].toString(), containsString("Panic"));
|
||||
assertThat(e.getStackTrace()[1].toString(), containsString("Runtime.assert"));
|
||||
assertThat(e.getStackTrace()[2].toString(), containsString("foo"));
|
||||
assertThat(e.getStackTrace()[3].toString(), containsString("main"));
|
||||
// Ignore the next two frames as they are implementation details
|
||||
assertThat(e.getStackTrace()[4].toString(), containsString("foo"));
|
||||
assertThat(e.getStackTrace()[5].toString(), containsString("main"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,9 +117,9 @@ public class AssertionsTest extends TestBase {
|
||||
TestBase.evalModule(
|
||||
ctx,
|
||||
"""
|
||||
from Standard.Base import Runtime, True
|
||||
main = Runtime.assert True
|
||||
""");
|
||||
from Standard.Base import Runtime, True
|
||||
main = Runtime.assert True
|
||||
""");
|
||||
assertTrue(res.isNull());
|
||||
}
|
||||
|
||||
@ -127,9 +129,9 @@ public class AssertionsTest extends TestBase {
|
||||
TestBase.evalModule(
|
||||
ctx,
|
||||
"""
|
||||
from Standard.Base import Runtime
|
||||
main = Runtime.assert [1,2,3]
|
||||
""");
|
||||
from Standard.Base import Runtime
|
||||
main = Runtime.assert [1,2,3]
|
||||
""");
|
||||
fail("Should throw Type_Error");
|
||||
} catch (PolyglotException e) {
|
||||
assertThat(e.getMessage(), stringContainsInOrder(List.of("Type", "error")));
|
||||
@ -142,14 +144,14 @@ public class AssertionsTest extends TestBase {
|
||||
TestBase.evalModule(
|
||||
ctx,
|
||||
"""
|
||||
from Standard.Base import Runtime
|
||||
import Standard.Base.Runtime.Ref.Ref
|
||||
from Standard.Base import Runtime
|
||||
import Standard.Base.Runtime.Ref.Ref
|
||||
|
||||
main =
|
||||
ref = Ref.new 10
|
||||
Runtime.assert (ref.put 23 . is_nothing . not)
|
||||
ref.get
|
||||
""");
|
||||
main =
|
||||
ref = Ref.new 10
|
||||
Runtime.assert (ref.put 23 . is_nothing . not)
|
||||
ref.get
|
||||
""");
|
||||
assertTrue(res.isNumber());
|
||||
assertThat(res.asInt(), is(23));
|
||||
}
|
||||
|
@ -9,10 +9,8 @@ import Standard.Test.Extensions
|
||||
foreign js js_check = """
|
||||
return (4 == 2 + 2)
|
||||
|
||||
on_ci = if Environment.get "ENSO_RUNNER_CONTAINER_NAME" . is_nothing . not then Nothing else "Not in CI"
|
||||
|
||||
spec = Test.group "Asserts" pending=on_ci <|
|
||||
Test.specify "should be enabled on the CI" <|
|
||||
spec = Test.group "Asserts" <|
|
||||
Test.specify "should be enabled in tests" <|
|
||||
p = Panic.catch Assertion_Error (Runtime.assert False) err->
|
||||
err.payload
|
||||
Meta.type_of p . should_be_a Assertion_Error
|
||||
@ -26,15 +24,14 @@ spec = Test.group "Asserts" pending=on_ci <|
|
||||
4 == 2 + 2
|
||||
ret . should_be_a Nothing
|
||||
|
||||
Test.specify "should fail with type error when given dataflow error as expression" <|
|
||||
p = Panic.catch Assertion_Error (Runtime.assert (Error.throw "foo")) err->
|
||||
err.payload
|
||||
Meta.type_of p . should_be_a Assertion_Error
|
||||
p.message . should_start_with "Result of assert action is a dataflow error"
|
||||
|
||||
Test.specify "should be able to take values with warnings" <|
|
||||
foo x = Warning.attach "My warning" (x+2)
|
||||
Runtime.assert (foo 2 > 2) . should_be_a Nothing
|
||||
|
||||
Test.specify "should fail with Type_Error if action does not return Boolean" <|
|
||||
p = Panic.catch Type_Error (Runtime.assert 42) err->
|
||||
err
|
||||
Meta.type_of p.payload . should_be_a Type_Error
|
||||
|
||||
|
||||
main = Test_Suite.run_main spec
|
||||
|
Loading…
Reference in New Issue
Block a user