Packages
bond
0.9.0
1.13.0
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.0
1.7.0
1.6.0
1.5.0
1.4.0
1.3.0
1.3.0-rc.1
1.2.1
1.2.0
1.1.0
1.0.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
0.18.0
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.1.0
Design by Contract (DbC) for Elixir
Current section
Files
Jump to
Current section
Files
lib/bond/runtime/eval.ex
defmodule Bond.Runtime.Eval do
@moduledoc internal: true
@moduledoc """
Internal helper module for runtime execution of contracts and assertions.
"""
@assertion_errors %{
precondition: Bond.PreconditionError,
postcondition: Bond.PostconditionError,
check: Bond.CheckError
}
def evaluate_preconditions(preconditions_fun) do
evaluate_assertions(preconditions_fun)
end
def evaluate_postconditions(postconditions_fun) do
evaluate_assertions(postconditions_fun)
end
def evaluate_checks(checks_fun) do
evaluate_assertions(checks_fun)
end
defp evaluate_assertions(assertions_fun) do
try do
with_recursion_check(assertions_fun)
catch
{:assertion_failure, %{kind: kind} = assertion_info} ->
exception = Map.fetch!(@assertion_errors, kind)
raise exception, assertion_info
end
end
defp with_recursion_check(assertions_fun) do
# Mutually recursive contracts lead to infinite recursion, so don't evaluate assertions for a
# function if other assertions are already being evaluated in the current process.
#
# Assertion Evaluation rule (from Object-Oriented Software Construction):
# During the process of evaluating an assertion at run-time, routine calls shall
# be executed without any evaluation of the associated assertions.
if not evaluating_assertions?() do
set_evaluating_assertions(true)
try do
assertions_fun.()
after
set_evaluating_assertions(false)
end
end
end
defp evaluating_assertions? do
Process.get(:__bond_evaluating_assertions__, false)
end
defp set_evaluating_assertions(bool) do
Process.put(:__bond_evaluating_assertions__, bool)
end
end