diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso index c16d09ac0b..24d3d78b98 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso @@ -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 diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/AssertNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/AssertNode.java deleted file mode 100644 index 64d7208eb9..0000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/AssertNode.java +++ /dev/null @@ -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); - } - } - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/IsAssertionEnabledNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/IsAssertionEnabledNode.java new file mode 100644 index 0000000000..a0ab85d1ab --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/IsAssertionEnabledNode.java @@ -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(); + } +} diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/asserts/AssertionsTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/asserts/AssertionsTest.java index 6300cc25c6..f5165a944c 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/asserts/AssertionsTest.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/asserts/AssertionsTest.java @@ -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)); } diff --git a/test/Tests/src/Runtime/Asserts_Spec.enso b/test/Tests/src/Runtime/Asserts_Spec.enso index b53ea5d600..f46ed60b9b 100644 --- a/test/Tests/src/Runtime/Asserts_Spec.enso +++ b/test/Tests/src/Runtime/Asserts_Spec.enso @@ -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