diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3394befbc709bec2d77dbc1603508d8c0e8f786e..87d65d5591a26341fecf9f5c8e2e77512a365b82 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -64,6 +64,13 @@ genassigns: tags: - nix +frama-clang: + stage: tests + script: + - nix/frama-ci.sh build -A frama-clang.tests + tags: + - nix + counter-examples: stage: tests script: diff --git a/Changelog b/Changelog index 3cd809c4cb31f6030fbea1adc5a62e71fcb9bc55..ca81d8698f74c64ed4d224ea0a0cdaeb0dd06da9 100644 --- a/Changelog +++ b/Changelog @@ -17,6 +17,10 @@ Open Source Release <next-release> ################################## +- Eva [2020-07-27] Improved automatic loop unroll (-eva-auto-loop-unroll + option) on loops with several exit conditions, conditions using + equality operators, temporary variables introduced by the Frama-C + normalization or goto statements. - Eva [2020-05-29] New builtins for trigonometric functions acos, asin and atan (and their single-precision version acosf, asinf, atanf). - Kernel [2020-05-28] Support for C11's _Thread_local storage specifier diff --git a/Makefile b/Makefile index e9664546176d1f99637970d4274ede5bfbcf060d..432c0e8363f6b0598675ff68943e6266bc8ea1eb 100644 --- a/Makefile +++ b/Makefile @@ -254,22 +254,24 @@ DISTRIB_FILES:=\ Changelog config.h.in \ VERSION VERSION_CODENAME $(wildcard licenses/*) \ $(LIBC_FILES) \ + share/analysis-scripts/analysis.mk \ share/analysis-scripts/benchmark_database.py \ share/analysis-scripts/cmd-dep.sh \ share/analysis-scripts/concat-csv.sh \ share/analysis-scripts/clone.sh \ share/analysis-scripts/creduce.sh \ - $(wildcard share/analysis-scripts/examples/*) \ + share/analysis-scripts/epilogue.mk \ share/analysis-scripts/fc_stubs.c \ share/analysis-scripts/find_fun.py \ share/analysis-scripts/flamegraph.pl \ - share/analysis-scripts/frama-c.mk \ share/analysis-scripts/frama_c_results.py \ + share/analysis-scripts/function_finder.py \ share/analysis-scripts/git_utils.py \ share/analysis-scripts/list_files.py \ share/analysis-scripts/make_template.py \ share/analysis-scripts/make_wrapper.py \ share/analysis-scripts/parse-coverage.sh \ + share/analysis-scripts/prologue.mk \ share/analysis-scripts/README.md \ share/analysis-scripts/results_display.py \ share/analysis-scripts/summary.py \ @@ -829,7 +831,7 @@ PLUGIN_NAME:=Eva PLUGIN_DIR:=src/plugins/value PLUGIN_EXTRA_DIRS:=engine values domains api domains/cvalue domains/apron \ domains/gauges domains/equality legacy partitioning utils gui_files \ - values/numerors domains/numerors + api values/numerors domains/numerors PLUGIN_TESTS_DIRS+=value/traces # Files for the binding to Apron domains. Only available if Apron is available. @@ -912,6 +914,7 @@ PLUGIN_CMO:= partitioning/split_strategy domains/domain_mode value_parameters \ engine/compute_functions engine/analysis register \ api/general_requests \ utils/unit_tests \ + api/values_request \ $(APRON_CMO) $(NUMERORS_CMO) PLUGIN_CMI:= values/abstract_value values/abstract_location \ domains/abstract_domain domains/simpler_domains @@ -1574,10 +1577,6 @@ STDLIB_FILES:=\ weak \ ephemeron -ifeq ($(HAS_OCAML407),no) - STDLIB_FILES+=pervasives -endif - STDLIB_FILES:=$(patsubst %,$(OCAMLLIB)/%.mli,$(STDLIB_FILES)) .PHONY: doc-kernel @@ -1937,29 +1936,30 @@ install:: install-lib-$(OCAMLBEST) share/configure.ac share/autocomplete_frama-c share/_frama-c \ $(FRAMAC_DATADIR) $(MKDIR) $(FRAMAC_DATADIR)/analysis-scripts - $(CP) share/analysis-scripts/benchmark_database.py \ + $(CP) \ + share/analysis-scripts/analysis.mk \ + share/analysis-scripts/benchmark_database.py \ share/analysis-scripts/cmd-dep.sh \ share/analysis-scripts/concat-csv.sh \ share/analysis-scripts/clone.sh \ share/analysis-scripts/creduce.sh \ + share/analysis-scripts/epilogue.mk \ share/analysis-scripts/fc_stubs.c \ share/analysis-scripts/find_fun.py \ share/analysis-scripts/flamegraph.pl \ - share/analysis-scripts/frama-c.mk \ share/analysis-scripts/frama_c_results.py \ + share/analysis-scripts/function_finder.py \ share/analysis-scripts/git_utils.py \ share/analysis-scripts/list_files.py \ share/analysis-scripts/make_template.py \ share/analysis-scripts/make_wrapper.py \ share/analysis-scripts/parse-coverage.sh \ + share/analysis-scripts/prologue.mk \ share/analysis-scripts/README.md \ share/analysis-scripts/results_display.py \ share/analysis-scripts/summary.py \ share/analysis-scripts/template.mk \ $(FRAMAC_DATADIR)/analysis-scripts - $(MKDIR) $(FRAMAC_DATADIR)/analysis-scripts/examples - $(CP) share/analysis-scripts/examples/* \ - $(FRAMAC_DATADIR)/analysis-scripts/examples $(MKDIR) $(FRAMAC_DATADIR)/compliance $(CP) share/compliance/c11_functions.json \ share/compliance/glibc_functions.json \ diff --git a/Makefile.generating b/Makefile.generating index 2e1c0c6c5dfc5b3bdb9ca7fc003397c3255386d3..e55a39ff38ce2e325850aa68aff2c11074ca1a24 100644 --- a/Makefile.generating +++ b/Makefile.generating @@ -126,56 +126,12 @@ endif GENERATED+= src/libraries/utils/json.ml src/libraries/stdlib/transitioning.ml -ifeq ($(HAS_OCAML408),yes) - DYNLINK_INIT=fun () -> () - FORMAT_STAG=stag - FORMAT_STRING_OF_STAG=match s with \ - Format.String_tag str -> str \ - | _ -> raise (Invalid_argument "unsupported tag extension") - FORMAT_STAG_OF_STRING=Format.String_tag s - FORMAT_PP_OPT=Format.pp_print_option - HAS_OCAML407_OR_408=yes -else - DYNLINK_INIT=Dynlink.init - FORMAT_STAG=tag - FORMAT_STRING_OF_STAG=s - FORMAT_STAG_OF_STRING=s - ifeq ($(HAS_OCAML407),yes) - HAS_OCAML407_OR_408=yes - else - HAS_OCAML407_OR_408=no - endif - FORMAT_PP_OPT=fun ?(none=(fun _ () -> ())) pp fmt o -> \ - match o with \ - | None -> none fmt () \ - | Some v -> pp fmt v -endif - -ifeq ($(HAS_OCAML407_OR_408),yes) - FLOAT_MAX_FLOAT=Float.max_float -else - FLOAT_MAX_FLOAT=Pervasives.max_float -endif - src/libraries/stdlib/transitioning.ml: \ src/libraries/stdlib/transitioning.ml.in \ Makefile.generating share/Makefile.config $(PRINT_MAKING) $@ rm -f $@ - sed \ - -e 's/@SPLIT_ON_CHAR@/$(SPLIT_ON_CHAR)/g' \ - -e 's/@STACK_FOLD@/$(STACK_FOLD)/g' \ - -e 's/@NTH_OPT@/$(NTH_OPT)/g' \ - -e 's/@FIND_OPT@/$(FIND_OPT)/g' \ - -e 's/@ASSOC_OPT@/$(ASSOC_OPT)/g' \ - -e 's/@ASSQ_OPT@/$(ASSQ_OPT)/g' \ - -e 's/@DYNLINK_INIT@/$(DYNLINK_INIT)/g' \ - -e 's/@FLOAT_MAX_FLOAT@/$(FLOAT_MAX_FLOAT)/g' \ - -e 's/@FORMAT_STAG@/$(FORMAT_STAG)/g' \ - -e 's/@FORMAT_STRING_OF_STAG@/$(FORMAT_STRING_OF_STAG)/g' \ - -e 's/@FORMAT_STAG_OF_STRING@/$(FORMAT_STAG_OF_STRING)/g' \ - -e 's/@FORMAT_PP_OPT@/$(FORMAT_PP_OPT)/g' \ - $< > $@ + cat $< > $@ $(CHMOD_RO) $@ ################## diff --git a/bin/frama-c-script b/bin/frama-c-script index a3dfda783a26d79051a3392bfefc1a52cf7ee433..0683f0371d7a4b63a0caddd0335db2f9d71349c9 100755 --- a/bin/frama-c-script +++ b/bin/frama-c-script @@ -35,27 +35,27 @@ usage() { echo " Display this help message and exit." echo "" echo " - make-template [dir]" - echo " Interactively prepares a template for running analysis scripts," - echo " writing it to [dir/GNUmakefile]. [dir] is [.] if omitted." + echo " Interactively prepares a template for analyses," + echo " writing it to dir/GNUmakefile [default: .frama-c]." echo "" - echo " - make-path" + echo " - make-path [dir]" echo " [for Frama-C developers and advanced users without Frama-C in the path]" - echo " Creates a frama-c-path.mk file in the current working directory." + echo " Creates a path.mk file in dir [default: .frama-c]." echo "" echo " - list-files [path/to/compile_commands.json]" echo " Lists all sources in the given compile_commands.json" - echo " (defaults to './compile_commands.json' if omitted)." + echo " [default: ./compile_commands.json]." echo " Also lists files defining a 'main' function" echo " (heuristics-based; neither correct nor complete)." echo "" - echo " - flamegraph <flamegraph.txt> [dir]" - echo " Generates flamegraph.svg and flamegraph.html in [dir]" - echo " (or in the FRAMAC_SESSION directory by default)." + echo " - flamegraph flamegraph.txt [dir]" + echo " Generates flamegraph.svg and flamegraph.html in dir" + echo " [default: FRAMAC_SESSION]." echo " Also opens it in a browser, unless variable NOGUI is set." echo "" - echo " - find-fun <function-name> [dirs]" - echo " Lists files in [dirs] declaring or defining <function-name>" - echo " (defaults to PWD + /usr/include)." + echo " - find-fun function-name [dir...]" + echo " Lists files in dir... declaring or defining function-name" + echo " [default: PWD /usr/include]." echo " Heuristics-based: neither correct nor complete." echo "" echo " - summary [options]" @@ -63,20 +63,20 @@ usage() { echo " in the current PWD." echo " Use $0 summary --help for more informations." echo "" - echo " - configure <machdep>" + echo " - configure machdep" echo " Runs an existing configure script to only consider files" echo " in Frama-C's libc; this will hopefully disable non-essential" echo " and non-POSIX external libraries." - echo " <machdep> is necessary to define a required preprocessor symbol" - echo " (run 'frama-c -machdep' help to get the list of machdeps)." + echo " (run 'frama-c -machdep help' to get the list of machdeps)." echo "" - echo " - make-wrapper <target> <args>" - echo " Runs 'make <target> <args>', parsing the output to suggest" + echo " - make-wrapper target arg..." + echo " Runs 'make target arg...', parsing the output to suggest" echo " useful commands in case of failure." echo "" echo " - normalize-jcdb [path/to/compile_commands.json]" echo " Applies some transformations to an existing compile_commands.json" - echo " (such as relativizing paths) to improve portability" + echo " (such as relativizing paths) to improve portability." + echo " [default: ./compile_commands.json]" exit $1 } @@ -85,7 +85,7 @@ if [ $# -lt 1 ]; then fi DIR="$( cd "$( dirname "$0" )" && pwd )" -FRAMAC_SHARE=$("${DIR}/frama-c-config" -print-share-path) +FRAMAC_SHARE=$("${DIR}/frama-c-config" -share) if [ -z ${FRAMAC_SESSION+x} ]; then FRAMAC_SESSION="./.frama-c"; fi @@ -121,17 +121,31 @@ open_file() { } make_path() { - cat <<EOF > frama-c-path.mk + dir=".frama-c" + if [ "$#" -gt 0 ]; then + dir="$1" + fi + if [ ! -d "$dir" ]; then + read -p "Directory '$dir' does not exist. Create it? [y/N] " yn + case $yn in + [Yy]) + mkdir -p "$dir" + ;; + *) + echo "Exiting without creating." + exit 0;; + esac + fi + cat <<EOF > "${dir}/path.mk" FRAMAC_DIR=${DIR} ifeq (\$(wildcard \$(FRAMAC_DIR)),) # Frama-C not installed locally; using the version in the PATH else FRAMAC=\$(FRAMAC_DIR)/frama-c FRAMAC_GUI=\$(FRAMAC_DIR)/frama-c-gui -FRAMAC_CONFIG=\$(FRAMAC_DIR)/frama-c-config endif EOF - echo "Wrote to: frama-c-path.mk" + echo "Wrote to: ${dir}/path.mk" } flamegraph() { @@ -232,11 +246,12 @@ case "$command" in ;; "make-template") shift; - ${FRAMAC_SHARE}/analysis-scripts/make_template.py "$0" "$@"; + export FRAMAC="${DIR}/frama-c" + ${FRAMAC_SHARE}/analysis-scripts/make_template.py "$@"; ;; "make-path") shift; - make_path; + make_path "$@"; ;; "list-files") shift; diff --git a/configure.in b/configure.in index 364940906cb9c955115ee9d81cca18b36168df30..3f792af4a5f4938a604d93d181bcb4807ebd9999 100644 --- a/configure.in +++ b/configure.in @@ -108,8 +108,8 @@ AC_MSG_CHECKING(version of OCaml) OCAMLVERSION=`$OCAMLC -v | sed -n -e 's|.*version *\(.*\)$|\1|p' ` AC_MSG_RESULT($OCAMLVERSION) case $OCAMLVERSION in - 0.*|1.*|2.*|3.*|4.00.*|4.01.*|4.02.*|4.03.*|4.04.*) - AC_MSG_ERROR(Incompatible OCaml version; use 4.05+.);; + 0.*|1.*|2.*|3.*|4.00.*|4.01.*|4.02.*|4.03.*|4.04.*|4.05.*|4.06.*|4.07.*|4.08.0) + AC_MSG_ERROR(Incompatible OCaml version; use 4.08.1+.);; *) OCAML_ANNOT_OPTION="-bin-annot";; esac @@ -118,24 +118,24 @@ AC_SUBST(OCAMLMAJORNB) AC_SUBST(OCAMLMINORNB) AC_SUBST(OCAMLPATCHNB) -AC_SUBST(HAS_OCAML407) -AC_SUBST(HAS_OCAML408) +AC_SUBST(HAS_OCAML409) +AC_SUBST(HAS_OCAML410) OCAMLMAJORNB=$(echo $OCAMLVERSION | cut -f 1 -d .) OCAMLMINORNB=$(echo $OCAMLVERSION | cut -f 2 -d .) OCAMLPATCHNB=$(echo $OCAMLVERSION | cut -f 3 -d .) if test $OCAMLMAJORNB -gt 4; then - HAS_OCAML407=yes; - HAS_OCAML408=yes; + HAS_OCAML409=yes; + HAS_OCAML410=yes; else - HAS_OCAML407=no; - HAS_OCAML408=no; - if test $OCAMLMINORNB -ge 7; then - HAS_OCAML407=yes; + HAS_OCAML409=no; + HAS_OCAML410=no; + if test $OCAMLMINORNB -ge 9; then + HAS_OCAML409=yes; fi; - if test $OCAMLMINORNB -ge 8; then - HAS_OCAML408=yes; + if test $OCAMLMINORNB -ge 10; then + HAS_OCAML410=yes; fi; fi; # MAJORNB -gt 4 @@ -300,10 +300,14 @@ AC_MSG_CHECKING(for zarith) ZARITH=$($OCAMLFIND query zarith -format %v) if test -z "$ZARITH" ; then - AC_MSG_ERROR(Cannot find zarith via ocamlfind.) -else - AC_MSG_RESULT(found $ZARITH) + AC_MSG_ERROR(Cannot find zarith via ocamlfind (requires zarith 1.5 or higher).) fi +case ZARITH in + 1.[[01234]].*) + AC_MSG_ERROR(found $ZARITH: requires 1.5 or higher.);; + *) + AC_MSG_RESULT(found $ZARITH);; +esac # yojson ######## @@ -839,7 +843,7 @@ AC_ARG_ENABLE(external, ]) AC_FOREACH([__plugin],m4_esyscmd([ls src/plugins]), - [ m4_if(m4_regexp(KNOWN_SRC_DIRS,`\<__plugin\>'),[-1], + [ m4_if(m4_bregexp(KNOWN_SRC_DIRS,`\<__plugin\>'),[-1], [ m4_define([plugin_dir],[src/plugins/__plugin]) m4_syscmd(test -r plugin_dir/configure.in) diff --git a/doc/code/.gitignore b/doc/code/.gitignore index 05325c534d0a551dca0cd670d1af3259695c1272..8fe76cbf5c38b74c255641dfcec33d154b838dbd 100644 --- a/doc/code/.gitignore +++ b/doc/code/.gitignore @@ -6,6 +6,7 @@ /callgraph_gui/ /constant_propagation/ /counter-examples/ +/dive/ /dynamic_plugins/ /e-acsl/ /from/ diff --git a/doc/developer/advance.tex b/doc/developer/advance.tex index 2489c968a1927c694a9065eb189fe51d7caf914d..1be1e161e81026b4b4d255c1605fdd11f083defa 100644 --- a/doc/developer/advance.tex +++ b/doc/developer/advance.tex @@ -991,7 +991,8 @@ test & \textit{None} \\ & \texttt{TIMEOUT}\nscodeidxdef{Test!Directive}{TIMEOUT} -& kill the test after the given duration and report a failure +& kill the test after the given duration (in seconds of CPU user time) +and report a failure & \textit{None} \\ & \texttt{NOFRAMAC}\nscodeidxdef{Test!Directive}{NOFRAMAC} @@ -1235,8 +1236,8 @@ and user-friendly way. As a general rule, you should \emph{never} write to standard output and error channels through \ocaml standard libraries. For instance, -you should never use \texttt{Pervasives.stdout} and -\texttt{Pervasives.stderr} channels, nor \texttt{Format.printf}-like +you should never use \texttt{Stdlib.stdout} and +\texttt{Stdlib.stderr} channels, nor \texttt{Format.printf}-like routines. Instead, you should use \texttt{Format.fprintf} to implement @@ -1615,7 +1616,7 @@ allows you to create such channels for your own purposes. Basically, \emph{channels} ensure that no message emission interfere with each others during echo on standard output. Hence the forbidden -direct access to \lstinline{Pervasives.stdout}. However, +direct access to \lstinline{Stdlib.stdout}. However, \lstinline{Log} interface allows you to create such channels on your own, in addition to the one automatically created for your plug-in. @@ -1665,7 +1666,7 @@ another channel: \end{description} It is also possible to have a momentary direct access to -\lstinline{Pervasives.stdout}, or whatever its redirection is: +\lstinline{Stdlib.stdout}, or whatever its redirection is: \begin{description} %%item \logroutine{print\_on\_output}{ "..."}% @@ -1843,7 +1844,7 @@ module AB = (Structural_desr.Sum [| [| Structural_descr.p_int |] |]) (* equality, compare and hash are the standard OCaml ones *) let equal (x:t) y = x = y - let compare (x:t) y = Pervasives.compare x y + let compare (x:t) y = Stdlib.compare x y let hash (x:t) = Hashtbl.hash x (* the type ab is a standard functional type, thus copying and rehashing are simply identity. Rehashing is used when marshaling. *) @@ -1967,7 +1968,7 @@ module Poly_ab = | A _, B _ | B _, A _ -> false let mk_compare f x y = match x, y with | A x, A y -> f x y - | B x, B y -> Pervasives.compare x y + | B x, B y -> Stdlib.compare x y | A _, B _ -> 1 | B _, A _ -> -1 let mk_hash f = function A x -> f x | B x -> 257 * x diff --git a/headers/header_spec.txt b/headers/header_spec.txt index 5d791ad40ee2ce6301155c602be4b5fb34761a11..a201e0f32e7a5d6c5491bb2f1e97396c84fe0add 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -111,26 +111,24 @@ ptests/.gitignore: .ignore ptests/.merlin: .ignore ptests/ptests.ml: CEA_LGPL share/_frama-c: CEA_LGPL +share/analysis-scripts/analysis.mk: CEA_LGPL share/analysis-scripts/benchmark_database.py: .ignore share/analysis-scripts/clone.sh: .ignore share/analysis-scripts/creduce.sh: CEA_LGPL +share/analysis-scripts/epilogue.mk: CEA_LGPL share/analysis-scripts/fc_stubs.c: .ignore -share/analysis-scripts/frama-c.mk: CEA_LGPL share/analysis-scripts/frama_c_results.py: .ignore share/analysis-scripts/cmd-dep.sh: .ignore share/analysis-scripts/concat-csv.sh: .ignore -share/analysis-scripts/examples/example.c: .ignore -share/analysis-scripts/examples/example.mk: .ignore -share/analysis-scripts/examples/example-multi.mk: .ignore -share/analysis-scripts/examples/example-slevel.mk: .ignore -share/analysis-scripts/examples/Makefile: .ignore share/analysis-scripts/find_fun.py: .ignore share/analysis-scripts/flamegraph.pl: CDDL +share/analysis-scripts/function_finder.py: .ignore share/analysis-scripts/git_utils.py: .ignore share/analysis-scripts/list_files.py: .ignore share/analysis-scripts/make_template.py: .ignore share/analysis-scripts/make_wrapper.py: .ignore share/analysis-scripts/parse-coverage.sh: .ignore +share/analysis-scripts/prologue.mk: CEA_LGPL share/analysis-scripts/README.md: .ignore share/analysis-scripts/results_display.py: .ignore share/analysis-scripts/summary.py: .ignore @@ -768,14 +766,18 @@ src/plugins/dive/build.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/callstack.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/callstack.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/configure.ac: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/context.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/context.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/dive_graph.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/dive_graph.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/dive_types.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/Dive.mli: CEA_LGPL_OR_PROPRIETARY -src/plugins/dive/graph_types.mli: CEA_LGPL_OR_PROPRIETARY -src/plugins/dive/imprecision_graph.ml: CEA_LGPL_OR_PROPRIETARY -src/plugins/dive/imprecision_graph.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/main.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/Makefile.in: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/node_kind.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/node_kind.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/node_range.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/node_range.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/self.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/self.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/dive/server_interface.ml: CEA_LGPL_OR_PROPRIETARY @@ -1210,6 +1212,8 @@ src/plugins/value/alarmset.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/alarmset.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/api/general_requests.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/api/general_requests.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/value/api/values_request.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/value/api/values_request.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/abstract_domain.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/printer_domain.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/printer_domain.mli: CEA_LGPL_OR_PROPRIETARY diff --git a/ivette/.eslintrc.js b/ivette/.eslintrc.js index 7c42af4153fd5aab1724dafbb91c50ee1613214c..256118d6c6b93af0c0ea15b5f60c2792e6ee93f7 100644 --- a/ivette/.eslintrc.js +++ b/ivette/.eslintrc.js @@ -20,7 +20,10 @@ module.exports = { "import/core-modules": [ 'electron', 'react-hot-loader' ] }, rules: { + // Do not enforce a displayName "react/display-name": "off", + // Do not enforce component methods order + "react/sort-comp": "off", // Be more strict on usage of useMemo and useRef "react-hooks/exhaustive-deps": "error", // Allow type any, even if it should be avoided @@ -99,6 +102,10 @@ module.exports = { "default-case": "off", "consistent-return": "off", // Allow modify properties of object passed in parameter - "no-param-reassign": [ "error", { "props": false } ], + "no-param-reassign": "error", //[ "error", { "props": false } ], + // Disallow the use of var in favor of let and const + "no-var": "error", + // Do not favor default import + "import/prefer-default-export": "off", } }; diff --git a/ivette/Makefile b/ivette/Makefile index 920c510f4b0abd5385077a1891359fbd33d05c64..2aef147770780018c94873fcbc5532c58ad44a44 100644 --- a/ivette/Makefile +++ b/ivette/Makefile @@ -8,28 +8,24 @@ DOME_API=./src/frama-c COPYRIGHT=CEA LIST / LSL # -------------------------------------------------------------------------- -.PHONY: all app dev doc serve dist typecheck lint tsc +.PHONY: all app dev doc serve dist lint fixlint -all: typecheck lint app +all: lint app app: dome-app dev: dome-dev dist: dome-dist -typecheck: dome-pkg dome-templ - @echo "[Ivette] running ts typechecker" - yarn run typecheck - lint: dome-pkg dome-templ - @echo "[Ivette] running ts linter" + @echo "[Ivette] running typechecker & linter" + yarn run typecheck yarn run lint -fixlint: dome-pkg dome-templ - @echo "[Ivette] running ts linter (with cache and fix)" +tsc: dome-pkg dome-templ + @echo "[Ivette] running typechecker & linter (with cache & fix mode)" + yarn run typecheck yarn run lint --fix --cache --cache-location .eslint-cache -tsc: typecheck fixlint - # -------------------------------------------------------------------------- # --- Frama-C API # -------------------------------------------------------------------------- diff --git a/ivette/api/kernel/ast/index.ts b/ivette/api/kernel/ast/index.ts index 623884b14adfa9242c3387102ec72042534bad37..64b119c98b4d1b4315d7031fdbd8e1a6d8eee987 100644 --- a/ivette/api/kernel/ast/index.ts +++ b/ivette/api/kernel/ast/index.ts @@ -43,10 +43,6 @@ export const compute: Server.ExecRequest<null,null>= compute_internal; /** Marker kind */ export enum markerKind { - /** Variable */ - variable = 'variable', - /** Function */ - function = 'function', /** Expression */ expression = 'expression', /** Lvalue */ @@ -83,12 +79,44 @@ const markerKindTags_internal: Server.GetRequest<null,tag[]> = { /** Registered tags for the above type. */ export const markerKindTags: Server.GetRequest<null,tag[]>= markerKindTags_internal; +/** Marker variable */ +export enum markerVar { + /** None */ + none = 'none', + /** Variable */ + variable = 'variable', + /** Function */ + function = 'function', +} + +/** Loose decoder for `markerVar` */ +export const jMarkerVar: Json.Loose<markerVar> = Json.jEnum(markerVar); + +/** Safe decoder for `markerVar` */ +export const jMarkerVarSafe: Json.Safe<markerVar> = + Json.jFail(Json.jEnum(markerVar),'kernel.ast.markerVar expected'); + +/** Natural order for `markerVar` */ +export const byMarkerVar: Compare.Order<markerVar> = + Compare.byEnum(markerVar); + +const markerVarTags_internal: Server.GetRequest<null,tag[]> = { + kind: Server.RqKind.GET, + name: 'kernel.ast.markerVarTags', + input: Json.jNull, + output: Json.jList(jTag), +}; +/** Registered tags for the above type. */ +export const markerVarTags: Server.GetRequest<null,tag[]>= markerVarTags_internal; + /** Data for array rows [`markerInfo`](#markerinfo) */ export interface markerInfoData { /** Entry identifier. */ key: Json.key<'#markerInfo'>; /** Marker kind */ kind: markerKind; + /** Marker variable */ + var: markerVar; /** Marker short name */ name: string; /** Marker declaration or description */ @@ -101,6 +129,7 @@ export const jMarkerInfoData: Json.Loose<markerInfoData> = key: Json.jFail(Json.jKey<'#markerInfo'>('#markerInfo'), '#markerInfo expected'), kind: jMarkerKindSafe, + var: jMarkerVarSafe, name: Json.jFail(Json.jString,'String expected'), descr: Json.jFail(Json.jString,'String expected'), }); @@ -112,10 +141,11 @@ export const jMarkerInfoDataSafe: Json.Safe<markerInfoData> = /** Natural order for `markerInfoData` */ export const byMarkerInfoData: Compare.Order<markerInfoData> = Compare.byFields - <{ key: Json.key<'#markerInfo'>, kind: markerKind, name: string, - descr: string }>({ + <{ key: Json.key<'#markerInfo'>, kind: markerKind, var: markerVar, + name: string, descr: string }>({ key: Compare.string, kind: byMarkerKind, + var: byMarkerVar, name: Compare.alpha, descr: Compare.string, }); @@ -197,6 +227,33 @@ export const jMarkerSafe: Json.Safe<marker> = /** Natural order for `marker` */ export const byMarker: Compare.Order<marker> = Compare.structural; +/** Location: function and marker */ +export interface location { + /** Function */ + function: Json.key<'#fct'>; + /** Marker */ + marker: marker; +} + +/** Loose decoder for `location` */ +export const jLocation: Json.Loose<location> = + Json.jObject({ + function: Json.jFail(Json.jKey<'#fct'>('#fct'),'#fct expected'), + marker: jMarkerSafe, + }); + +/** Safe decoder for `location` */ +export const jLocationSafe: Json.Safe<location> = + Json.jFail(jLocation,'Location expected'); + +/** Natural order for `location` */ +export const byLocation: Compare.Order<location> = + Compare.byFields + <{ function: Json.key<'#fct'>, marker: marker }>({ + function: Compare.string, + marker: byMarker, + }); + const getFunctions_internal: Server.GetRequest<null,Json.key<'#fct'>[]> = { kind: Server.RqKind.GET, name: 'kernel.ast.getFunctions', diff --git a/ivette/api/plugins/dive/index.ts b/ivette/api/plugins/dive/index.ts index e1bc260040c20ab9541d1464d712df6ce5af8c94..b1e2c48ddcccd6edb1f670193decea3cc8039f21 100644 --- a/ivette/api/plugins/dive/index.ts +++ b/ivette/api/plugins/dive/index.ts @@ -15,42 +15,296 @@ import * as Server from 'frama-c/server'; //@ts-ignore import * as State from 'frama-c/states'; +//@ts-ignore +import { byLocation } from 'api/kernel/ast'; +//@ts-ignore +import { byMarker } from 'api/kernel/ast'; +//@ts-ignore +import { jLocation } from 'api/kernel/ast'; +//@ts-ignore +import { jLocationSafe } from 'api/kernel/ast'; +//@ts-ignore +import { jMarker } from 'api/kernel/ast'; +//@ts-ignore +import { jMarkerSafe } from 'api/kernel/ast'; +//@ts-ignore +import { location } from 'api/kernel/ast'; +//@ts-ignore +import { marker } from 'api/kernel/ast'; -/** The name of variable of the program */ -export interface variableName { - /** owner function for a local variable */ - funName?: string; - /** variable name */ - varName: string; +/** Parametrization of the exploration range. */ +export interface range { + /** range for the write dependencies */ + backward?: number; + /** range for the read dependencies */ + forward?: number; } -/** Loose decoder for `variableName` */ -export const jVariableName: Json.Loose<variableName> = +/** Loose decoder for `range` */ +export const jRange: Json.Loose<range> = + Json.jObject({ backward: Json.jNumber, forward: Json.jNumber,}); + +/** Safe decoder for `range` */ +export const jRangeSafe: Json.Safe<range> = + Json.jFail(jRange,'Range expected'); + +/** Natural order for `range` */ +export const byRange: Compare.Order<range> = + Compare.byFields + <{ backward?: number, forward?: number }>({ + backward: Compare.defined(Compare.number), + forward: Compare.defined(Compare.number), + }); + +/** Global parametrization of the exploration. */ +export interface explorationWindow { + /** how far dive will explore from root nodes ; must be a finite range */ + perception: range; + /** range beyond which the nodes must be hidden */ + horizon: range; +} + +/** Loose decoder for `explorationWindow` */ +export const jExplorationWindow: Json.Loose<explorationWindow> = + Json.jObject({ perception: jRangeSafe, horizon: jRangeSafe,}); + +/** Safe decoder for `explorationWindow` */ +export const jExplorationWindowSafe: Json.Safe<explorationWindow> = + Json.jFail(jExplorationWindow,'ExplorationWindow expected'); + +/** Natural order for `explorationWindow` */ +export const byExplorationWindow: Compare.Order<explorationWindow> = + Compare.byFields + <{ perception: range, horizon: range }>({ + perception: byRange, + horizon: byRange, + }); + +/** A node identifier in the graph */ +export type nodeId = number; + +/** Loose decoder for `nodeId` */ +export const jNodeId: Json.Loose<nodeId> = Json.jNumber; + +/** Safe decoder for `nodeId` */ +export const jNodeIdSafe: Json.Safe<nodeId> = + Json.jFail(Json.jNumber,'Number expected'); + +/** Natural order for `nodeId` */ +export const byNodeId: Compare.Order<nodeId> = Compare.number; + +/** A callsite */ +export type callsite = { fun: string, instr: number | string }; + +/** Loose decoder for `callsite` */ +export const jCallsite: Json.Loose<callsite> = + Json.jObject({ + fun: Json.jFail(Json.jString,'String expected'), + instr: Json.jFail( + Json.jUnion<number | string>( Json.jNumber, Json.jString,), + 'Union expected'), + }); + +/** Safe decoder for `callsite` */ +export const jCallsiteSafe: Json.Safe<callsite> = + Json.jFail(jCallsite,'Callsite expected'); + +/** Natural order for `callsite` */ +export const byCallsite: Compare.Order<callsite> = + Compare.byFields + <{ fun: string, instr: number | string }>({ + fun: Compare.string, + instr: Compare.structural, + }); + +/** The callstack context for a node */ +export type callstack = callsite[]; + +/** Safe decoder for `callstack` */ +export const jCallstackSafe: Json.Safe<callstack> = + Json.jArray(jCallsiteSafe); + +/** Loose decoder for `callstack` */ +export const jCallstack: Json.Loose<callstack> = Json.jTry(jCallstackSafe); + +/** Natural order for `callstack` */ +export const byCallstack: Compare.Order<callstack> = + Compare.array(byCallsite); + +/** The description of a node locality */ +export type nodeLocality = { file: string, callstack?: callstack }; + +/** Loose decoder for `nodeLocality` */ +export const jNodeLocality: Json.Loose<nodeLocality> = Json.jObject({ - funName: Json.jString, - varName: Json.jFail(Json.jString,'String expected'), + file: Json.jFail(Json.jString,'String expected'), + callstack: jCallstack, }); -/** Safe decoder for `variableName` */ -export const jVariableNameSafe: Json.Safe<variableName> = - Json.jFail(jVariableName,'VariableName expected'); +/** Safe decoder for `nodeLocality` */ +export const jNodeLocalitySafe: Json.Safe<nodeLocality> = + Json.jFail(jNodeLocality,'NodeLocality expected'); -/** Natural order for `variableName` */ -export const byVariableName: Compare.Order<variableName> = +/** Natural order for `nodeLocality` */ +export const byNodeLocality: Compare.Order<nodeLocality> = Compare.byFields - <{ funName?: string, varName: string }>({ - funName: Compare.defined(Compare.alpha), - varName: Compare.alpha, + <{ file: string, callstack?: callstack }>({ + file: Compare.string, + callstack: Compare.defined(byCallstack), }); -const graph_internal: Server.GetRequest<null,Json.json> = { +/** A graph node */ +export type node = + { id: nodeId, label: string, kind: string, locality: nodeLocality, + is_root: boolean, backward_explored: string, forward_explored: string, + writes: location[], values?: string, range: number | string, + type?: string }; + +/** Loose decoder for `node` */ +export const jNode: Json.Loose<node> = + Json.jObject({ + id: jNodeIdSafe, + label: Json.jFail(Json.jString,'String expected'), + kind: Json.jFail(Json.jString,'String expected'), + locality: jNodeLocalitySafe, + is_root: Json.jFail(Json.jBoolean,'Boolean expected'), + backward_explored: Json.jFail(Json.jString,'String expected'), + forward_explored: Json.jFail(Json.jString,'String expected'), + writes: Json.jArray(jLocationSafe), + values: Json.jString, + range: Json.jFail( + Json.jUnion<number | string>( Json.jNumber, Json.jString,), + 'Union expected'), + type: Json.jString, + }); + +/** Safe decoder for `node` */ +export const jNodeSafe: Json.Safe<node> = Json.jFail(jNode,'Node expected'); + +/** Natural order for `node` */ +export const byNode: Compare.Order<node> = + Compare.byFields + <{ id: nodeId, label: string, kind: string, locality: nodeLocality, + is_root: boolean, backward_explored: string, forward_explored: string, + writes: location[], values?: string, range: number | string, + type?: string }>({ + id: byNodeId, + label: Compare.string, + kind: Compare.string, + locality: byNodeLocality, + is_root: Compare.boolean, + backward_explored: Compare.string, + forward_explored: Compare.string, + writes: Compare.array(byLocation), + values: Compare.defined(Compare.string), + range: Compare.structural, + type: Compare.defined(Compare.string), + }); + +/** The dependency between two nodes */ +export type dependency = + { id: number, src: nodeId, dst: nodeId, kind: string, origins: location[] }; + +/** Loose decoder for `dependency` */ +export const jDependency: Json.Loose<dependency> = + Json.jObject({ + id: Json.jFail(Json.jNumber,'Number expected'), + src: jNodeIdSafe, + dst: jNodeIdSafe, + kind: Json.jFail(Json.jString,'String expected'), + origins: Json.jArray(jLocationSafe), + }); + +/** Safe decoder for `dependency` */ +export const jDependencySafe: Json.Safe<dependency> = + Json.jFail(jDependency,'Dependency expected'); + +/** Natural order for `dependency` */ +export const byDependency: Compare.Order<dependency> = + Compare.byFields + <{ id: number, src: nodeId, dst: nodeId, kind: string, + origins: location[] }>({ + id: Compare.number, + src: byNodeId, + dst: byNodeId, + kind: Compare.string, + origins: Compare.array(byLocation), + }); + +/** The whole graph being built */ +export type graphData = { nodes: node[], deps: dependency[] }; + +/** Loose decoder for `graphData` */ +export const jGraphData: Json.Loose<graphData> = + Json.jObject({ + nodes: Json.jArray(jNodeSafe), + deps: Json.jArray(jDependencySafe), + }); + +/** Safe decoder for `graphData` */ +export const jGraphDataSafe: Json.Safe<graphData> = + Json.jFail(jGraphData,'GraphData expected'); + +/** Natural order for `graphData` */ +export const byGraphData: Compare.Order<graphData> = + Compare.byFields + <{ nodes: node[], deps: dependency[] }>({ + nodes: Compare.array(byNode), + deps: Compare.array(byDependency), + }); + +/** Graph differences from the last action. */ +export type diffData = + { root?: nodeId, add: { nodes: node[], deps: dependency[] }, sub: nodeId[] + }; + +/** Loose decoder for `diffData` */ +export const jDiffData: Json.Loose<diffData> = + Json.jObject({ + root: jNodeId, + add: Json.jFail( + Json.jObject({ + nodes: Json.jArray(jNodeSafe), + deps: Json.jArray(jDependencySafe), + }),'Record expected'), + sub: Json.jArray(jNodeIdSafe), + }); + +/** Safe decoder for `diffData` */ +export const jDiffDataSafe: Json.Safe<diffData> = + Json.jFail(jDiffData,'DiffData expected'); + +/** Natural order for `diffData` */ +export const byDiffData: Compare.Order<diffData> = + Compare.byFields + <{ root?: nodeId, add: { nodes: node[], deps: dependency[] }, + sub: nodeId[] }>({ + root: Compare.defined(byNodeId), + add: Compare.byFields + <{ nodes: node[], deps: dependency[] }>({ + nodes: Compare.array(byNode), + deps: Compare.array(byDependency), + }), + sub: Compare.array(byNodeId), + }); + +const window_internal: Server.SetRequest<explorationWindow,null> = { + kind: Server.RqKind.SET, + name: 'plugins.dive.window', + input: jExplorationWindow, + output: Json.jNull, +}; +/** Set the exploration window */ +export const window: Server.SetRequest<explorationWindow,null>= window_internal; + +const graph_internal: Server.GetRequest<null,graphData> = { kind: Server.RqKind.GET, name: 'plugins.dive.graph', input: Json.jNull, - output: Json.jAny, + output: jGraphData, }; /** Retrieve the whole graph */ -export const graph: Server.GetRequest<null,Json.json>= graph_internal; +export const graph: Server.GetRequest<null,graphData>= graph_internal; const clear_internal: Server.ExecRequest<null,null> = { kind: Server.RqKind.EXEC, @@ -61,58 +315,40 @@ const clear_internal: Server.ExecRequest<null,null> = { /** Erase the graph and start over with an empty one */ export const clear: Server.ExecRequest<null,null>= clear_internal; -const addVar_internal: Server.ExecRequest<variableName,Json.json> = { +const add_internal: Server.ExecRequest<marker,diffData> = { kind: Server.RqKind.EXEC, - name: 'plugins.dive.addVar', - input: jVariableName, - output: Json.jAny, + name: 'plugins.dive.add', + input: jMarker, + output: jDiffData, }; -/** Add a variable to the graph */ -export const addVar: Server.ExecRequest<variableName,Json.json>= addVar_internal; +/** Add a node to the graph */ +export const add: Server.ExecRequest<marker,diffData>= add_internal; -const addFunctionAlarms_internal: Server.ExecRequest< - Json.key<'#fct'>, - Json.json - > = { - kind: Server.RqKind.EXEC, - name: 'plugins.dive.addFunctionAlarms', - input: Json.jKey<'#fct'>('#fct'), - output: Json.jAny, -}; -/** Add all alarms of the given function */ -export const addFunctionAlarms: Server.ExecRequest< - Json.key<'#fct'>, - Json.json - >= addFunctionAlarms_internal; - -const explore_internal: Server.ExecRequest< - Json.index<'#dive-node'>, - Json.json - > = { +const explore_internal: Server.ExecRequest<nodeId,diffData> = { kind: Server.RqKind.EXEC, name: 'plugins.dive.explore', - input: Json.jIndex<'#dive-node'>('#dive-node'), - output: Json.jAny, + input: jNodeId, + output: jDiffData, }; /** Explore the graph starting from an existing vertex */ -export const explore: Server.ExecRequest<Json.index<'#dive-node'>,Json.json>= explore_internal; +export const explore: Server.ExecRequest<nodeId,diffData>= explore_internal; -const show_internal: Server.ExecRequest<Json.index<'#dive-node'>,Json.json> = { +const show_internal: Server.ExecRequest<nodeId,diffData> = { kind: Server.RqKind.EXEC, name: 'plugins.dive.show', - input: Json.jIndex<'#dive-node'>('#dive-node'), - output: Json.jAny, + input: jNodeId, + output: jDiffData, }; /** Show the dependencies of an existing vertex */ -export const show: Server.ExecRequest<Json.index<'#dive-node'>,Json.json>= show_internal; +export const show: Server.ExecRequest<nodeId,diffData>= show_internal; -const hide_internal: Server.ExecRequest<Json.index<'#dive-node'>,Json.json> = { +const hide_internal: Server.ExecRequest<nodeId,diffData> = { kind: Server.RqKind.EXEC, name: 'plugins.dive.hide', - input: Json.jIndex<'#dive-node'>('#dive-node'), - output: Json.jAny, + input: jNodeId, + output: jDiffData, }; /** Hide the dependencies of an existing vertex */ -export const hide: Server.ExecRequest<Json.index<'#dive-node'>,Json.json>= hide_internal; +export const hide: Server.ExecRequest<nodeId,diffData>= hide_internal; /* ------------------------------------- */ diff --git a/ivette/api/plugins/eva/general/index.ts b/ivette/api/plugins/eva/general/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..519329eeb53dfa7fda48ac9c286506bd2da3440c --- /dev/null +++ b/ivette/api/plugins/eva/general/index.ts @@ -0,0 +1,83 @@ +/* --- Generated Frama-C Server API --- */ + +/** + Eva General Services + @packageDocumentation + @module api/plugins/eva/general +*/ + +//@ts-ignore +import * as Json from 'dome/data/json'; +//@ts-ignore +import * as Compare from 'dome/data/compare'; +//@ts-ignore +import * as Server from 'frama-c/server'; +//@ts-ignore +import * as State from 'frama-c/states'; + +//@ts-ignore +import { byMarker } from 'api/kernel/ast'; +//@ts-ignore +import { jMarker } from 'api/kernel/ast'; +//@ts-ignore +import { jMarkerSafe } from 'api/kernel/ast'; +//@ts-ignore +import { marker } from 'api/kernel/ast'; + +const getCallers_internal: Server.GetRequest< + Json.key<'#fct'>, + [ Json.key<'#fct'>, Json.key<'#stmt'> ][] + > = { + kind: Server.RqKind.GET, + name: 'plugins.eva.general.getCallers', + input: Json.jKey<'#fct'>('#fct'), + output: Json.jList( + Json.jTry( + Json.jPair( + Json.jFail(Json.jKey<'#fct'>('#fct'),'#fct expected'), + Json.jFail(Json.jKey<'#stmt'>('#stmt'),'#stmt expected'), + ))), +}; +/** Get the list of call site of a function */ +export const getCallers: Server.GetRequest< + Json.key<'#fct'>, + [ Json.key<'#fct'>, Json.key<'#stmt'> ][] + >= getCallers_internal; + +/** Unreachable and non terminating statements. */ +export interface deadCode { + /** List of unreachable statements. */ + unreachable: marker[]; + /** List of reachable but non terminating statements. */ + nonTerminating: marker[]; +} + +/** Loose decoder for `deadCode` */ +export const jDeadCode: Json.Loose<deadCode> = + Json.jObject({ + unreachable: Json.jList(jMarker), + nonTerminating: Json.jList(jMarker), + }); + +/** Safe decoder for `deadCode` */ +export const jDeadCodeSafe: Json.Safe<deadCode> = + Json.jFail(jDeadCode,'DeadCode expected'); + +/** Natural order for `deadCode` */ +export const byDeadCode: Compare.Order<deadCode> = + Compare.byFields + <{ unreachable: marker[], nonTerminating: marker[] }>({ + unreachable: Compare.array(byMarker), + nonTerminating: Compare.array(byMarker), + }); + +const getDeadCode_internal: Server.GetRequest<Json.key<'#fct'>,deadCode> = { + kind: Server.RqKind.GET, + name: 'plugins.eva.general.getDeadCode', + input: Json.jKey<'#fct'>('#fct'), + output: jDeadCode, +}; +/** Get the lists of unreachable and of non terminating statements in a function */ +export const getDeadCode: Server.GetRequest<Json.key<'#fct'>,deadCode>= getDeadCode_internal; + +/* ------------------------------------- */ diff --git a/ivette/api/plugins/eva/index.ts b/ivette/api/plugins/eva/index.ts deleted file mode 100644 index eff005e5d8e2a1e1e73c6c460d4558e01b3913ff..0000000000000000000000000000000000000000 --- a/ivette/api/plugins/eva/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* --- Generated Frama-C Server API --- */ - -/** - Eva General Services - @packageDocumentation - @module api/plugins/eva -*/ - -//@ts-ignore -import * as Json from 'dome/data/json'; -//@ts-ignore -import * as Compare from 'dome/data/compare'; -//@ts-ignore -import * as Server from 'frama-c/server'; -//@ts-ignore -import * as State from 'frama-c/states'; - - -const getCallers_internal: Server.GetRequest< - Json.key<'#fct'>, - [ Json.key<'#fct'>, Json.key<'#stmt'> ][] - > = { - kind: Server.RqKind.GET, - name: 'plugins.eva.getCallers', - input: Json.jKey<'#fct'>('#fct'), - output: Json.jList( - Json.jTry( - Json.jPair( - Json.jFail(Json.jKey<'#fct'>('#fct'),'#fct expected'), - Json.jFail(Json.jKey<'#stmt'>('#stmt'),'#stmt expected'), - ))), -}; -/** Get the list of call site of a function */ -export const getCallers: Server.GetRequest< - Json.key<'#fct'>, - [ Json.key<'#fct'>, Json.key<'#stmt'> ][] - >= getCallers_internal; - -/* ------------------------------------- */ diff --git a/ivette/api/plugins/eva/values/index.ts b/ivette/api/plugins/eva/values/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..22375d253f9b2f0bac5f1242c8cfe80693973c0e --- /dev/null +++ b/ivette/api/plugins/eva/values/index.ts @@ -0,0 +1,154 @@ +/* --- Generated Frama-C Server API --- */ + +/** + Eva Values + @packageDocumentation + @module api/plugins/eva/values +*/ + +//@ts-ignore +import * as Json from 'dome/data/json'; +//@ts-ignore +import * as Compare from 'dome/data/compare'; +//@ts-ignore +import * as Server from 'frama-c/server'; +//@ts-ignore +import * as State from 'frama-c/states'; + +//@ts-ignore +import { byMarker } from 'api/kernel/ast'; +//@ts-ignore +import { jMarker } from 'api/kernel/ast'; +//@ts-ignore +import { jMarkerSafe } from 'api/kernel/ast'; +//@ts-ignore +import { marker } from 'api/kernel/ast'; + +/** CallStack */ +export interface callstack { + /** Callstack id */ + id: number; + /** Short name for the callstack */ + short: string; + /** Full name for the callstack */ + full: string; +} + +/** Loose decoder for `callstack` */ +export const jCallstack: Json.Loose<callstack> = + Json.jObject({ + id: Json.jFail(Json.jNumber,'Number expected'), + short: Json.jFail(Json.jString,'String expected'), + full: Json.jFail(Json.jString,'String expected'), + }); + +/** Safe decoder for `callstack` */ +export const jCallstackSafe: Json.Safe<callstack> = + Json.jFail(jCallstack,'Callstack expected'); + +/** Natural order for `callstack` */ +export const byCallstack: Compare.Order<callstack> = + Compare.byFields + <{ id: number, short: string, full: string }>({ + id: Compare.number, + short: Compare.string, + full: Compare.string, + }); + +/** Data for array rows [`values`](#values) */ +export interface valuesData { + /** Entry identifier. */ + key: Json.key<'#values'>; + /** CallStack */ + callstack: callstack; + /** Value inferred just before the selected point */ + value_before: string; + /** Did the evaluation led to an alarm? */ + alarm: boolean; + /** Value inferred just after the selected point */ + value_after?: string; +} + +/** Loose decoder for `valuesData` */ +export const jValuesData: Json.Loose<valuesData> = + Json.jObject({ + key: Json.jFail(Json.jKey<'#values'>('#values'),'#values expected'), + callstack: jCallstackSafe, + value_before: Json.jFail(Json.jString,'String expected'), + alarm: Json.jFail(Json.jBoolean,'Boolean expected'), + value_after: Json.jString, + }); + +/** Safe decoder for `valuesData` */ +export const jValuesDataSafe: Json.Safe<valuesData> = + Json.jFail(jValuesData,'ValuesData expected'); + +/** Natural order for `valuesData` */ +export const byValuesData: Compare.Order<valuesData> = + Compare.byFields + <{ key: Json.key<'#values'>, callstack: callstack, value_before: string, + alarm: boolean, value_after?: string }>({ + key: Compare.string, + callstack: byCallstack, + value_before: Compare.string, + alarm: Compare.boolean, + value_after: Compare.defined(Compare.string), + }); + +/** Signal for array [`values`](#values) */ +export const signalValues: Server.Signal = { + name: 'plugins.eva.values.signalValues', +}; + +const reloadValues_internal: Server.GetRequest<null,null> = { + kind: Server.RqKind.GET, + name: 'plugins.eva.values.reloadValues', + input: Json.jNull, + output: Json.jNull, +}; +/** Force full reload for array [`values`](#values) */ +export const reloadValues: Server.GetRequest<null,null>= reloadValues_internal; + +const fetchValues_internal: Server.GetRequest< + number, + { pending: number, updated: valuesData[], removed: Json.key<'#values'>[], + reload: boolean } + > = { + kind: Server.RqKind.GET, + name: 'plugins.eva.values.fetchValues', + input: Json.jNumber, + output: Json.jObject({ + pending: Json.jFail(Json.jNumber,'Number expected'), + updated: Json.jList(jValuesData), + removed: Json.jList(Json.jKey<'#values'>('#values')), + reload: Json.jFail(Json.jBoolean,'Boolean expected'), + }), +}; +/** Data fetcher for array [`values`](#values) */ +export const fetchValues: Server.GetRequest< + number, + { pending: number, updated: valuesData[], removed: Json.key<'#values'>[], + reload: boolean } + >= fetchValues_internal; + +const values_internal: State.Array<Json.key<'#values'>,valuesData> = { + name: 'plugins.eva.values.values', + getkey: ((d:valuesData) => d.key), + signal: signalValues, + fetch: fetchValues, + reload: reloadValues, + order: byValuesData, +}; +/** Abstract values inferred by the Eva analysis */ +export const values: State.Array<Json.key<'#values'>,valuesData> = values_internal; + +const getValues_internal: Server.GetRequest<marker,null> = { + kind: Server.RqKind.GET, + name: 'plugins.eva.values.getValues', + input: jMarker, + output: Json.jNull, +}; +/** Get the abstract values computed for an expression or lvalue */ +export const getValues: Server.GetRequest<marker,null>= getValues_internal; + +/* ------------------------------------- */ diff --git a/ivette/api/server_tsc.ml b/ivette/api/server_tsc.ml index 54f247e27aab1ccd98ff40c8b80536b1f41a2bab..4e11f8b611762b57c27393e014da65f20edd760a 100644 --- a/ivette/api/server_tsc.ml +++ b/ivette/api/server_tsc.ml @@ -167,7 +167,7 @@ let rec makeDecoder ~safe ?self ~names fmt js = | Jboolean -> jsafe ~safe "Boolean" jprim fmt "jBoolean" | Jnumber -> jsafe ~safe "Number" jprim fmt "jNumber" | Jstring | Jalpha -> jsafe ~safe "String" jprim fmt "jString" - | Jtag a -> Format.fprintf fmt "jTag(\"%s\")" a + | Jtag a -> Format.fprintf fmt "Json.jTag(\"%s\")" a | Jkey kd -> jsafe ~safe ("#" ^ kd) jkey fmt kd | Jindex kd -> jsafe ~safe ("#" ^ kd) jindex fmt kd | Jdata id -> jcall names fmt (Pkg.Derived.decode ~safe id) @@ -188,6 +188,10 @@ let rec makeDecoder ~safe ?self ~names fmt js = | Jrecord jfs -> jsafe ~safe "Record" (jrecord ~makeSafe) fmt jfs | Jtuple jts -> jtry ~safe (jtuple ~makeSafe) fmt jts +let makeLooseNeedSafe = function + | Pkg.Jtuple _ | Pkg.Jarray _ -> true + | _ -> false + let makeRootDecoder ~safe ~self ~names fmt js = let open Pkg in match js with @@ -249,7 +253,7 @@ let makeOrder ~self ~names fmt js = | Jdict js -> let jtype fmt js = makeJtype ~names fmt js in Format.fprintf fmt - "@[<hov 2>Compare.dictionary<@,%a>(@,%a)@]" + "@[<hov 2>Compare.dictionary<@,Json.dict<%a>>(@,%a)@]" jtype js pp js | Jany | Junion _ | Jtag _ -> Format.fprintf fmt "Compare.structural" @@ -414,12 +418,13 @@ let makeDeclaration fmt names d = type ranking = { mutable rank : int ; mutable mark : int Pkg.IdMap.t ; + index : Pkg.declInfo Pkg.IdMap.t ; } let depends d = match d.Pkg.d_kind with - | D_loose(id,(Jtuple _ | Jarray _)) -> [Pkg.Derived.safe id] - | D_safe(id,_) -> [Pkg.Derived.loose id] + | D_loose(id,t) when makeLooseNeedSafe t -> [Pkg.Derived.safe id] + | D_safe(id,t) when not (makeLooseNeedSafe t) -> [Pkg.Derived.loose id] | D_array _ -> let id = d.d_ident in let data = Pkg.Derived.data id in @@ -439,13 +444,20 @@ let next m id = m.mark <- Pkg.IdMap.add id r m.mark ; m.rank <- succ r -let mark m d = +let rec mark m d = let id = d.Pkg.d_ident in if not (Pkg.IdMap.mem id m.mark) then - ( List.iter (next m) (depends d) ; next m id ) + ( List.iter (mark_id m) (depends d) ; next m id ) + +and mark_id m id = + try mark m (Pkg.IdMap.find id m.index) + with Not_found -> () let ranking ds = - let m = { rank = 0 ; mark = Pkg.IdMap.empty } in + let index = List.fold_left + (fun m d -> Pkg.IdMap.add d.Pkg.d_ident d m) + Pkg.IdMap.empty ds in + let m = { rank = 0 ; mark = Pkg.IdMap.empty ; index } in List.iter (mark m) ds ; let rk = m.mark in let getRank a = try Pkg.IdMap.find a.Pkg.d_ident rk with Not_found -> 0 in diff --git a/ivette/package.json b/ivette/package.json index 1cd2ee014010573adf99e1d26a021d63f22fedcd..d67c43963a5db2cbc2e26a6878330fc96deb54f7 100644 --- a/ivette/package.json +++ b/ivette/package.json @@ -21,7 +21,8 @@ "@babel/preset-react": "^7.9.4", "@babel/preset-typescript": "^7.9.0", "@hot-loader/react-dom": "^16.13.0", - "@types/codemirror": "^0.0.89", + "@types/codemirror": "^0.0.97", + "@types/cytoscape": "^3.14.5", "@types/lodash": "^4.14.149", "@types/node": "12.12.21", "@types/react": "^16.9.17", @@ -33,7 +34,7 @@ "css-loader": "^3.4.0", "electron": "^7", "electron-builder": "^22.4.1", - "electron-devtools-installer": "^2.2.4", + "electron-devtools-installer": "^3.0.0", "electron-webpack": "^2.8.2", "eslint": "^6.8.0", "eslint-config-airbnb": "^18.1.0", @@ -53,15 +54,25 @@ }, "dependencies": { "@babel/runtime": "^7.9.2", + "@fortawesome/fontawesome-free": "^5.13.1", "codemirror": "^5.52.2", + "cytoscape": "^3.15.1", + "cytoscape-cola": "^2.3.1", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-cxtmenu": "^3.1.2", + "cytoscape-dagre": "^2.2.2", + "cytoscape-klay": "^3.1.3", + "cytoscape-popper": "^1.0.7", "immutable": "^4.0.0-rc.12", "lodash": "^4.17.15", "react": "^16.8", + "react-cytoscapejs": "^1.2.1", "react-dom": "^16.13.1", "react-draggable": "^4.2.0", "react-fast-compare": "^3.2.0", "react-virtualized": "^9.21.2", "source-map-support": "^0.5.16", + "tippy.js": "5.2.1", "zeromq": "^6.0.0-beta.5" } } diff --git a/ivette/src/dome/src/main/dome.js b/ivette/src/dome/src/main/dome.js index 8fd188b09b66272a81b7684b26a925f5e844ae22..8b7d3c81195fc04ece1783978380f1034a04f2df 100644 --- a/ivette/src/dome/src/main/dome.js +++ b/ivette/src/dome/src/main/dome.js @@ -392,7 +392,10 @@ function createPrimaryWindow() // React Developper Tools if (DEVEL) - installExtension(REACT_DEVELOPER_TOOLS,true); + installExtension(REACT_DEVELOPER_TOOLS,true) + .catch((err) => { + console.error('[Dome] Enable to install React dev-tools',err); + }); const cwd = process.cwd(); const wdir = cwd === '/' ? app.getPath('home') : cwd ; const argv = stripElectronArgv(process.argv); diff --git a/ivette/src/dome/src/misc/devtools.js b/ivette/src/dome/src/misc/devtools.js index 222b66d9d3d159ba7703dbf7d822519abc0b5ab8..4c921aa6c9ed956aaa131813074cb4303c6af844 100644 --- a/ivette/src/dome/src/misc/devtools.js +++ b/ivette/src/dome/src/misc/devtools.js @@ -8,5 +8,5 @@ export const REACT_DEVELOPER_TOOLS = undefined ; // Shall not be used in non-development mode export default function installExtension(_id) { - return Promise.reject(); + return Promise.resolve(); } diff --git a/ivette/src/dome/src/misc/utils.ts b/ivette/src/dome/src/misc/utils.ts index b39a84b6d38a5f07d9015126c9f09b2dd759d29f..9dbd931ae2cc4673f797078f4654ca50d9ad2201 100644 --- a/ivette/src/dome/src/misc/utils.ts +++ b/ivette/src/dome/src/misc/utils.ts @@ -2,12 +2,41 @@ // --- Utilities // -------------------------------------------------------------------------- +/** + @packageDocumentation + @module dome/misc/utils + */ + import type { CSSProperties } from 'react'; -export type ClassSpec = - undefined | boolean | null | string | - { [cname: string]: boolean | null | undefined }; +type falsy = undefined | boolean | null | ''; + +export type ClassSpec = string | falsy | { [cname: string]: true | falsy }; + +/** + Utility function to merge various HTML class properties + into a `className` property. + Class specifications can be made of: + - a string, interpreted as a CSS class specification + - an object, with keys corresponding to CSS class associated + to true of falsy value. + - any falsy value, which is discarded + + Example of usage: + + * ```ts + * const className = classes( + * 'my-base-class', + * condition && 'my-class-when-condition', + * { + * 'my-class-1': cond-1, + * 'my-class-2': cond-2, + * 'my-class-3': cond-3, + * } + * ); + * ``` + */ export function classes( ...args: ClassSpec[] ): string { @@ -24,8 +53,25 @@ export function classes( return buffer.join(' '); } -export type StyleSpec = - undefined | boolean | null | CSSProperties; +export type StyleSpec = falsy | CSSProperties; + +/** + Utility function to merge various CSS style properties + into a single CSS style object. + + Each style specification can be made of a CSS object or (discarded) + falsy values. + Example of usage: + + * ```ts + * const sty = styles( + * { ... }, + * cond-1 && { ... }, + * cond-2 && { ... }, + * ); + * ``` + +*/ export function styles( ...args: StyleSpec[] diff --git a/ivette/src/dome/src/renderer/data/json.ts b/ivette/src/dome/src/renderer/data/json.ts index f6ce22153b9d82e50ce33869e4e741573f6549dc..56ef9117e0fb57fc7d9724f842985e5412a63ba6 100644 --- a/ivette/src/dome/src/renderer/data/json.ts +++ b/ivette/src/dome/src/renderer/data/json.ts @@ -193,7 +193,7 @@ export function jCatch<A>(fn: Loose<A>, fallBack: A): Safe<A> { try { return fn(js) ?? fallBack; } catch (err) { - if (DEVEL) console.error('[Dome.json]', err); + if (DEVEL) console.warn('[Dome.json]', err); return fallBack; } }; @@ -207,7 +207,8 @@ export function jTry<A>(fn: Loose<A>, defaultValue?: A): Loose<A> { return (js: json) => { try { return fn(js) ?? defaultValue; - } catch (_err) { + } catch (err) { + if (DEVEL) console.warn('[Dome.json]', err); return defaultValue; } }; @@ -448,7 +449,13 @@ export function jIndex<K>(kd: K): Loose<index<K>> { return (js: json) => (typeof js === 'number' ? forge(kd, js) : undefined); } +/** Dictionaries. */ export type dict<A> = { [key: string]: A }; + +/** + Decode a JSON dictionary, discarding all inconsistent entries. + If the JSON contains no valid entry, still returns `{}`. +*/ export function jDict<A>(fn: Loose<A>): Safe<dict<A>> { return (js: json) => { const buffer: dict<A> = {}; diff --git a/ivette/src/dome/src/renderer/data/settings.ts b/ivette/src/dome/src/renderer/data/settings.ts index 51ef3fcd73e7d3c48ea19f45cf4e2fca3213aa9d..114d14c5a6089c4b78e7513b36a47e9eccbae55e 100644 --- a/ivette/src/dome/src/renderer/data/settings.ts +++ b/ivette/src/dome/src/renderer/data/settings.ts @@ -327,6 +327,15 @@ export function useWindowSettingsData<A>( }, WindowDriver, key); } +/** Call the callback function on window settings events. */ +export function useWindowSettingsEvent(callback: () => void) { + React.useEffect(() => { + const { evt } = WindowDriver; + SysEmitter.on(evt, callback); + return () => { SysEmitter.off(evt, callback); }; + }); +} + // -------------------------------------------------------------------------- // --- Global Settings // -------------------------------------------------------------------------- @@ -346,6 +355,15 @@ export function useGlobalSettings<A>(S: GlobalSettings<A>) { return useSettings(S, GlobalDriver, S.name); } +/** Call the callback function on global settings events. */ +export function useGlobalSettingsEvent(callback: () => void) { + React.useEffect(() => { + const { evt } = GlobalDriver; + SysEmitter.on(evt, callback); + return () => { SysEmitter.off(evt, callback); }; + }); +} + // -------------------------------------------------------------------------- // --- Settings Synchronization // -------------------------------------------------------------------------- diff --git a/ivette/src/dome/src/renderer/dome.ts b/ivette/src/dome/src/renderer/dome.ts index aac7877c772d10758b62330df706dd11d6b9bfb0..d5565c1e57fdfd8d72cb11f23d80f298c2047905 100644 --- a/ivette/src/dome/src/renderer/dome.ts +++ b/ivette/src/dome/src/renderer/dome.ts @@ -509,7 +509,7 @@ const CLOCKEVENT = (period: number) => `dome.clock.${period}`; const TIC_CLOCK = (clk: Clock) => () => { if (0 < clk.pending) { - clk.time += clk.period; + clk.time += clk.period; // eslint-disable-line no-param-reassign System.emitter.emit(clk.event, clk.time); } else { if (clk.timer) clearInterval(clk.timer); diff --git a/ivette/src/dome/src/renderer/layout/boxes.tsx b/ivette/src/dome/src/renderer/layout/boxes.tsx index d95a01e940833421a464cd9844ebd4b14ffd900a..3e87958bbbd4a386a19d02db37e9a47390400e44 100644 --- a/ivette/src/dome/src/renderer/layout/boxes.tsx +++ b/ivette/src/dome/src/renderer/layout/boxes.tsx @@ -180,13 +180,13 @@ export const Folder = (props: FolderProps) => { indent = 18, label, title, children, } = props; - const [unfold, onClick] = Dome.useBoolSettings(settings, defaultUnfold); - const foldUnfold = React.useCallback(() => onClick(), [onClick]); + const [unfold, onClick]: [boolean, () => void] = + Dome.useBoolSettings(settings, defaultUnfold); const icon = unfold ? 'TRIANGLE.DOWN' : 'TRIANGLE.RIGHT'; const display = unfold ? 'none' : 'block'; return ( <Vpack> - <Hpack onClick={foldUnfold}> + <Hpack onClick={onClick}> <Title icon={icon} label={label} title={title} /> </Hpack> <Vpack style={{ display, marginLeft: indent }}> diff --git a/ivette/src/dome/src/renderer/table/arrays.ts b/ivette/src/dome/src/renderer/table/arrays.ts index d7dfddcc938bea4ee826b225051acdefba4a3fc9..5abe5992daa086172bd98a05b63c30ec72c72908 100644 --- a/ivette/src/dome/src/renderer/table/arrays.ts +++ b/ivette/src/dome/src/renderer/table/arrays.ts @@ -108,7 +108,8 @@ export class ArrayModel<Key, Row> return current; } - // Lazily compute table + // Lazily compute table ; modifies packed entries in place + /* eslint-disable no-param-reassign */ protected rebuild(): PACK<Key, Row>[] { const current = this.table; let filtered = 0; @@ -127,11 +128,12 @@ export class ArrayModel<Key, Row> } catch (err) { console.warn('[Dome] error when rebuilding table:', err); } - table.forEach((pack, index) => { pack.index = index; }); + table.forEach((packed, index) => { packed.index = index; }); this.table = table; this.filtered = filtered; return table; } + /* eslint-enable no-param-reassign */ // -------------------------------------------------------------------------- // --- Proxy @@ -431,7 +433,7 @@ export class ArrayModel<Key, Row> // -------------------------------------------------------------------------- /** - @template Row - object data that also contains their « key » + @template Row - object data that also contains their « key ». */ export class CompactModel<Key, Row> extends ArrayModel<Key, Row> { diff --git a/ivette/src/dome/src/renderer/table/views.tsx b/ivette/src/dome/src/renderer/table/views.tsx index 1adb34a0f4873bfa36115555c614ca388c52d0e9..c8ff5652821670e6a43bb3b1b6cd4b3248c166e2 100644 --- a/ivette/src/dome/src/renderer/table/views.tsx +++ b/ivette/src/dome/src/renderer/table/views.tsx @@ -61,6 +61,14 @@ export type RenderByFields<Row> = { export type index = number | number[]; /** + Column Properties. + + __Warning:__ callback properties, namely `getter`, `render` + and `onContextMenu`, shall be as stable as possible to prevent + the table from constantly re-rendering. + Use constant callbacks whenever possible, or memoize them with + `React.useCallback()` hook. + @template Row - table row data of some table entries @template Cell - type of cell data to render in this column */ @@ -106,14 +114,17 @@ export interface ColumnProps<Row, Cell> { visible?: boolean | 'never' | 'always'; /** Data getter for this column. + Shall be constant or protected by `React.useCallback`. */ getter?: (row: Row, dataKey: string) => Cell | undefined; /** Override table by-fields cell renderers. + Shall be constant or protected by `React.useCallback`. */ render?: Renderer<Cell>; /** Override table right-click callback. + Shall be constant or protected by `React.useCallback`. */ onContextMenu?: (row: Row, index: number, dataKey: string) => void; } @@ -310,7 +321,6 @@ class TableState<Key, Row> { this.onRowRightClick = this.onRowRightClick.bind(this); this.onKeyDown = this.onKeyDown.bind(this); this.onSorting = this.onSorting.bind(this); - this.reloadSettings = this.reloadSettings.bind(this); this.rebuild = debounce(this.rebuild.bind(this), 5); this.rowGetter = makeRowGetter(); } @@ -407,27 +417,24 @@ class TableState<Key, Row> { this.forceUpdate(); } - reloadSettings() { - const { settings, resize, visible } = this; - resize.clear(); - visible.clear(); - const theSettings: undefined | TableSettings = - Settings.getWindowSettings(settings, jTableSettings, undefined); - if (theSettings) { - forEach(theSettings.resize, (cw, cid) => { - this.resize.set(cid, cw); - }); - forEach(theSettings.visible, (cv, cid) => { - this.visible.set(cid, cv); - }); - this.forceUpdate(); - } - } - importSettings(settings?: string) { if (settings !== this.settings) { this.settings = settings; - this.reloadSettings(); + const { resize } = this; + const { visible } = this; + resize.clear(); + visible.clear(); + const theSettings: undefined | TableSettings = + Settings.getWindowSettings(settings, jTableSettings, undefined); + if (theSettings) { + forEach(theSettings.resize, (cw, cid) => { + if (typeof cw === 'number') this.resize.set(cid, cw); + }); + forEach(theSettings.visible, (cv, cid) => { + if (typeof cv === 'boolean') this.visible.set(cid, cv); + }); + this.forceUpdate(); + } } } @@ -991,6 +998,7 @@ function makeResizers( const CSS_HEADER_HEIGHT = 22; const CSS_ROW_HEIGHT = 20; +// Modifies state in place function makeTable<Key, Row>( props: TableProps<Key, Row>, state: TableState<Key, Row>, @@ -1008,11 +1016,13 @@ function makeTable<Key, Row>( const columns = makeColumns(state, cprops); const resizers = makeResizers(state, cprops); + /* eslint-disable no-param-reassign */ state.rowCount = rowCount; if (state.width !== width) { state.width = width; setImmediate(state.forceUpdate); } + /* eslint-enable no-param-reassign */ return ( <div onKeyDown={state.onKeyDown}> @@ -1078,7 +1088,7 @@ function makeTable<Key, Row>( @template Key - unique identifiers of table entries. @template Row - data associated to each key in the table entries. -*/ + */ export function Table<Key, Row>(props: TableProps<Key, Row>) { @@ -1094,7 +1104,7 @@ export function Table<Key, Row>(props: TableProps<Key, Row>) { state.onContextMenu = props.onContextMenu; return state.unwind; }); - Dome.useEvent(Dome.windowSettings, state.reloadSettings); + Settings.useGlobalSettingsEvent(state.importSettings); return ( <div className="dome-xTable"> <React.Fragment key="columns"> diff --git a/ivette/src/dome/src/renderer/text/buffers.js b/ivette/src/dome/src/renderer/text/buffers.js deleted file mode 100644 index b25bf354c55eb01028dd728e69b8af29b4a27459..0000000000000000000000000000000000000000 --- a/ivette/src/dome/src/renderer/text/buffers.js +++ /dev/null @@ -1,573 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Text Documents -// -------------------------------------------------------------------------- - -/** - @packageDocumentation - @module dome/text/buffers -*/ - -import Emitter from 'events' ; -import CodeMirror from 'codemirror/lib/codemirror.js' ; - -const combineClass = (a,b) => a ? (b ? (a + " " + b) : a) : b ; - -// -------------------------------------------------------------------------- -// --- Marker Proxy -// -------------------------------------------------------------------------- - -class Proxy { - - clear() { this.marker && this.marker.clear(); } - changed() { this.marker && this.marker.changed(); } - find() { return this.marker && this.marker.find(); } - _link(marker) { this.marker = marker ; } - -} - -// -------------------------------------------------------------------------- -// --- Buffer -// -------------------------------------------------------------------------- - -/** - @summary Rich Text Content and State. - @description - - A buffer encapsulate a CodeMirror document instance inside an standard - Node event emitter. CodeMirror signals are automatically linked back to - the event emitter (on a lazy basis). - - The `maxlines` will control the maximum number of lines kept in the buffer. - By default, it is set to `10000`, but you can use `null`, `0` or any negative - value to disable this behavior. See also `this.setMaxlines()` method. - - Additionnaly, a Buffer maintains an _edited_ state internally that can - be manually updated, and that is automatically set to `true` when the - underlying CodeMirror document is changed. It is associated to an `'edited'` - event of the buffer, and can be used to manage the « Saved » / « Not-Saved » - state of an editor, for instance. - - Typically, a sequence of changed events would fire a unique `'edited'` buffer - event, untill `setEdited(false)` is invoked. The `clear()` method also resets - the _edited_ state to `false`, but sill emit an `'edited'` event if the - buffer was not empty. - - Buffers can also be updated programmatically by various methods. In addition to - specified CodeMirror modes, you can also attach text markers programmatically with - a push/pop API. - - Text markers can be associated with an identifier, that can be used for - dynamic highlighting, called Decorations. Decorations are class names that - add styling attributes upon the current mode. Typically, consider only using - background colors, underlines and/or strike-through for decorations, since - font styles and colors are likely to be managed by CodeMirror modes, unless - you know exactly what your are doing. - - The `setDecorator` method can be used to set or update - the highlighting function that computes the decoration of a text marker - from its identifier. When the decorations may have change, you shall either - set again the highlighting function with a call to `setDecorator()` or call - the `updateDecorations()` method. This also triggers the `'decorated'` event. - - */ - -export class RichTextBuffer extends Emitter { - - /** - @param {object} [props] - Constructor properties (see below) - @param {string | object} [props.mode] - CodeMirror [mode](https://codemirror.net/mode/index.html) specification - @param {number} [props.maxlines] - Maximum number of lines in the buffer - */ - constructor(props = {}) { - super(); - const { mode , maxlines } = props ; - this._doc = new CodeMirror.Doc('',mode); - this._operations = 0 ; - this._editors = [] ; - this._stacked = [] ; - this._edited = false ; - this._focused = false ; - this._markid = 0 ; - this._markers = {} ; - this._decorator = undefined ; - this.setMaxlines(maxlines); - this.setEdited = this.setEdited.bind(this); - this.setFocused = this.setFocused.bind(this); - this.clear = this.clear.bind(this); - this.append = this.append.bind(this); - this.setValue = this.setValue.bind(this); - this.getValue = this.getValue.bind(this); - this.log = this.log.bind(this); - this._doc.on('change', ( _target , { origin } ) => { - if (origin !== 'buffer') this.setEdited(true); - this.emit('change'); - }); - } - - /** - @summary CodeMirror document instance. - @return {CodeMirror.Doc} internal [document](https://codemirror.net/doc/manual.html#api_doc) - @description - Returns the `CodeMirror.Doc` instance holding the buffer contents. - This can be used for further customization. - <p> - This document will never be bound to a `CodeMirror` instance. Instead, `Text` views - will use _linked_ documents to the buffer one. This allows for several views to - share the same document (and history). - */ - getDoc() { return this._doc; } - - // -------------------------------------------------------------------------- - // --- Buffer Manipulation - // -------------------------------------------------------------------------- - - /** Clear buffer contents and resets _edited_ state. */ - clear() { this.setValue(''); } - - /** - @summary Writes in the buffer. - @param {any} [value] - content to append in the buffer - @description - Appends textual contents to the end of the buffer. - All parameters are converted to string and joined with spaces. - The generated change event has origin `'buffer'` and it does not - modifies the _edited_ internal state. - */ - append(...value) { - if (value.length > 0) { - const doc = this._doc ; - const from = doc.posFromIndex(Infinity); - const text = value.join(' '); - doc.replaceRange(text,from,undefined,'buffer'); - this.shrink(); - } - } - - /** - @summary Starts a new line in the buffer. - @description - If the current buffer content does not finish at the beginning of a fresh line, - inserts a newline character. - */ - flushline() { - const doc = this._doc ; - const from = doc.posFromIndex(Infinity); - if (from.ch > 0) doc.replaceRange('\n',from,undefined,'flush'); - } - - /** - @summary Appends with newline and auto-scrolling. - @param {any} [value] - content to append in the buffer - @description - This is a short-cut to `flushline()` followed by `append(...value,'\n')` and `scroll()`. - */ - log(...value) { - this.flushline(); - this.append(...value,'\n'); - this.scroll(); - } - - /** - @summary Replace textual content with the given value. - @param {string} [txt] - new text content - @description - Also remove all markers. - */ - setValue(txt='') { - this._doc.setValue(txt); - this._edited = false; - this._stacked = [] ; - this._focused = false ; - this._markid = 0 ; - this._markers = {} ; - } - - /** - @summary Return textual content. - @return {string} - */ - getValue() { return this._doc.getValue(); } - - /** - @summary Opens a text marker. - @param {object} options - CodeMirror - [text marker](https://codemirror.net/doc/manual.html#api_marker) options - @return {CodeMirror.TextMarker} the associated - [text marker](https://codemirror.net/doc/manual.html#api_marker) (proxy) - @description -Opens a text marker at the current (end) position in the buffer. - -The text marker is stacked and would be actually created on the -matching `closeTextMarker()` call. It will be applied to the full range of text -inserted between its associated `openTextMarker()` and `closeTextMarker()` calls. - -The returned text marker is actually a _proxy_ to the text marker that -will be eventually created by `closeTextMarker()`. Its methods (`find`, `clear` and `changed`) -are automatically forwarded to the actual `CodeMirror.TextMarker` instance, once created. -Hence, you can safely invoke these methods on either the _proxy_ or the _final_ -text marker at your convenience. - -Additionnaly to standard `CodeMirror.TextMarker` options, you may also the -following Dome specific ones: -- `id: string` assigns an identifier to the marker (expected to be unique) ; -- `hover: boolean` makes the text-marker highlighted on mouse-hover ; this is compatible with - _nested_ markers, which is not possible with CSS `:hover` pseudo selectors ; defaults to `true` for - identified text markers and `false` otherwize. -*/ - - openTextMarker( { id , hover, className, ...options } ) { - const from = this._doc.posFromIndex(Infinity); - const proxy = new Proxy(); - this._stacked.push( { from , id , hover, className, options , proxy } ); - return proxy ; - } - - /** - @summary Close last opened marker. - @return {CodeMirror.TextMarker} the (actual) - [text marker](https://codemirror.net/doc/manual.html#api_marker) - @description - Closes the lastly opened text marker with `openTextMarker()`. - The method returns the _actual_ - text marker ; the _proxy_ text marker provided by the corresponding - call to `openTextMarker()` is automatically bound to the actual one. - */ - closeTextMarker() { - const tag = this._stacked.pop(); - if (tag) { - const { id, hover, from } = tag ; - const to = this._doc.posFromIndex(Infinity); - var classNameWithId ; - var tagid ; - if ( id || hover ) { - const mhover = hover !== undefined ? hover : (id !== undefined) ; - const cid = id ? 'dome-xMark-' + id : ('dome-xHover-' + (++this._markid)) ; - const p = this._doc.indexFromPos(from); - const q = this._doc.indexFromPos(to); - const m = this._markers[cid]; - if (m) console.warn('[Dome.text.buffers] duplicate marker',id); - tagid = this._markers[cid] = { id, hover:mhover, classNameId:cid, length: q-p } ; - classNameWithId = id ? "dome-xMarked " + cid : cid ; - } - const className = combineClass( tag.className, classNameWithId ); - const options = Object.assign( { shared:true, className } , tag.options ); - const marker = this._doc.markText( from , to , options ); - tag.proxy._link(marker); - if (tagid) tagid.marker = marker ; - this.shrink(); - return marker ; - } else - return undefined ; - } - - /** - @description Lookup a text marker - @param {string} id - requested identifier - @return {CodeMirror.TextMarker} the identified text marker, or `undefined` if not found. - */ - findTextMarker(id) { - if (id) { - const m = this._markers['dome-xMark-' + id] ; - if (m) return m.marker ; - } - return undefined; - } - - /** - @summary Lookup a hover class. - @param {string} className - a class name, possibly identifying a hover element - @return {hover} the associated hovered element `{id, classNameId, length}`, or - `undefined` if the provided `className` does not identify any such element. - */ - findHover(name) { return this._markers[name]; } - - // -------------------------------------------------------------------------- - // --- Highlighter - // -------------------------------------------------------------------------- - - /** - @summary Define highlighter. - @param {function} highlighter - the function that computes - the decoration class of a marker id. - */ - setDecorator(f) { this._decorator = f ; this.emit('decorated'); } - - /** - @summary Current highlighter. - @return {function} the current highlighting function - */ - getDecorator() { return this._decorator ; } - - /** - @summary Rehighlight document. - @description - Emits the `'decorated'` event to make editors - updating the decorations of identified text markers. - */ - updateDecorations() { this.emit('decorated'); } - - // -------------------------------------------------------------------------- - // --- Edited State - // -------------------------------------------------------------------------- - - /** - @summary Set edited state. - @param {boolean} [state] - the new edited state (defaults to `true`). - @description - -Set the _edited_ internal state. The method is automatically invoked by editor -views when the user actually edit the document. The state is _not_ modified -when modifying the document from buffer's own methods, _eg._ `append()` and -`clear()`. - -The method fires the `'edited'` event on modifications. This method is bound to -`this`, hence `this.setEdited` can be used as a valid callback function. - */ - setEdited(state = true) { - if ( state !== this._edited ) { - this._edited = state ; - this.emit('edited',state); - } - } - - /** - @summary Set focused state. - @param {boolean} [focus] - the new focused state (defaults to `true`). - @description - -Set the _focused_ internal state. The method is automatically invoked by editor -views when they gain or lose focus or when the user actually interact with the -view, eg. mouse-scrolling, edition, cursor move, etc. The escape key `ESC` -explicitly relax the _focused_ state, although the editor view might actually -keep the _focus_. - -When a buffer is _focused_, shrinking and auto-scrolling are temporarily deactivated -to avoid confusing user's experience. - -The method fires `'focused'` events on modifications. This method is bound to -`this`, hence `this.setEdited` can be used as a valid callback function. - */ - setFocused(state = true) { - if ( state !== this._focused ) { - this._focused = state ; - this.emit('focused',state); - this.shrink(); - } - } - - /** Returns the current _edited_ state. */ - isEdited() { return this._edited; } - - /** Returns the current _focused_ state. */ - isFocused() { return this._focused; } - - // -------------------------------------------------------------------------- - // --- Document Scrolling - // -------------------------------------------------------------------------- - - /** - @summary Set (or unset) the maximum number of lines. - @param {number} [maxlines] - maximum number of lines - @description - -By default, the maximum number of lines is set to `10,000` to avoid -unwanted memory leaks. Setting `null`, `0` of any negative value cancel -the management of maximum number of lines. - -Although CodeMirror is powerfull enough to manage huge buffers, -you should turn this limit _off_ with care. - */ - setMaxlines(maxlines=10000) { - this._maxlines = maxlines > 0 ? 1 + maxlines : 0 ; - this.shrink(); - } - - /** - @summary Remove head lines to fits into maximum lines. - @description - -Shrinking is activated when `maxlines` property is set to a strictly -positive number. When the number of lines in the buffer is larger than -the max, buffer is trimmed by removing extra _heading_ lines. - -When the buffer if _focused_ or when there are still opened text marks pending, -shrinking is automatically postponed until focus is lost and all pending marks -have been closed. - */ - shrink() - { - if (!this._focused && this._maxlines > 0 && this._stacked.length == 0) { - const lines = this._doc.lineCount(); - if (lines > this._maxlines) { - const p = this._doc.firstLine(); - const q = p + lines - this._maxlines ; - this._doc.replaceRange('',{line:p,ch:0},{line:q,ch:0},'buffer'); - } - } - } - - /** - @summary Requires all connected views to scroll to the specified position in the buffer. - @param {...any} [args] - the position or range to be made visible - @description -Typical usage: - - `scroll()` to the end of buffer - - `scroll(id)` to identified marked `id`; - - `scroll(p)` a position: line number or `{line,ch?}` CodeMirror position; - - `scroll(p,q)` a range of two positions (like above); - - `scroll({from,to})` an object range of two positions (like above). - -When the buffer is _focused_, programmatic auto-scrolling with `scroll()` -is blocked. - */ - scroll(a,b) { - switch(typeof(a)) { - case 'undefined': - if (this._focused) return; - this.emit('scroll',{line:this._doc.lastLine(),ch:0}); - break; - case 'string': - const tm = this.findTextMarker(a); - const rg = tm && tm.find(); - if (rg) this.emit('scroll',rg); - break; - default: - const isLineCh = (a) => ( - a && Number.isInteger(a.line) - && (a.ch === undefined || Number.isInteger(a.ch)) - ); - const getPos = (a) => ( - Number.isInteger(a) ? { line: a, ch:0 } : - isLineCh(a) ? a : console.warn('[Dome.text.buffers] can not scroll to',a) - ); - var from,to ; - if (a && a.from && a.to) { - from = getPos(a.from); - to = getPos(a.to); - } else { - from = getPos(a); - to = b && getPos(b); - } - if (from && to) this.emit('scroll',{from,to}); - else if (from) this.emit('scroll',from); - } - } - - // -------------------------------------------------------------------------- - // --- Document Linking - // -------------------------------------------------------------------------- - - /** - @summary Bind this buffer to a CodeMirror instance. - @param {CodeMirror} cm - code mirror instance to link this document in. - @description - Uses CodeMirror linked documents to allow several CodeMirror instances to be linked - to the same buffer. - */ - link(cm) { - const newDoc = this._doc.linkedDoc( { sharedHist: true } ); - cm.swapDoc( newDoc ); - this._editors.push(cm); - if (this._operations > 0) cm.startOperation(); - } - - /** - @summary Release a linked CodeMirror document. - @param {CodeMirror} cm - the code mirror instance to unlink - @param {Document} previous document of the instance. - @description - Unlinks a CodeMirror document previously linked by `link(cm)`. - */ - unlink(cm) { - const oldDoc = cm.getDoc(); - this._doc.unlinkDoc( oldDoc ); - this._editors = this._editors.filter((cm0) => cm0 !== cm); - if (this._operations > 0) cm.endOperation(); - } - - /** - @summary Iterates over each linked CodeMirror instances - @description - The operation `fn` is performed on each code mirror - instance currently linked to this buffer. - */ - forEach(fn) { - this._editors.forEach(fn); - }; - - // -------------------------------------------------------------------------- - // --- Stacked Operations - // -------------------------------------------------------------------------- - - /** - @summary Batch heavy operations on editors - @param {function} job - a function performing the operations (can return a promise) - @return {promise} the promised job - @description - Uses code mirror `cm.startOperation()` and `cm.sendOperation()` on all - linked editors to batch the updating operations performed on the - buffer. The batched updates can run asynchronously. - */ - operation(job) { - - // Protect each start/end call against error - const forEachEditor = (fn) => { - this._editors.forEach((cm) => { - try { fn(cm); } catch(e) { console.error('[Dome.text.buffers]',e); } - }); - }; - - // Invariant: this._operations is the number of batched job still running - // Invariant: when pending job are running, all linked this._editors are started - // Second invariant is also maintained by link and unlink methods - - const startOperation = () => { - this._operations ++; - if (this._operations == 1) - forEachEditor((cm) => cm.startOperation()); - }; - - const endOperation = () => { - this._operations --; - if (this._operations == 0) { - forEachEditor((cm) => cm.endOperation()); - } - }; - - return Promise.resolve() - .then(startOperation) - .then(job) - .finally(endOperation); - } - - // -------------------------------------------------------------------------- - // --- Print Utilities - // -------------------------------------------------------------------------- - - /** - @summary Print text containing tags into buffer (bound to `this`). - @param {any} text - Text to print. - @param {object} options - CodeMirror - [text marker](https://codemirror.net/doc/manual.html#api_marker) options. - */ - printTextWithTags(text, options = {}) { - if (Array.isArray(text)) { - const tag = text.shift(); - if (tag !== '') { - const markerOptions = { id: tag, ...options }; - this.openTextMarker(markerOptions); - } - text.forEach((txt) => this.printTextWithTags(txt, options)); - if (tag !== '') { - this.closeTextMarker(); - } - } else if (typeof text === 'string') { - this.append(text); - } else if (text !== null && text !== undefined) { - console.error('[Dome.buffers] unexpected text',text); - } - } - -} - -// -------------------------------------------------------------------------- diff --git a/ivette/src/dome/src/renderer/text/buffers.ts b/ivette/src/dome/src/renderer/text/buffers.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0a587f849fa9616c6add252a9533f632576fb8a --- /dev/null +++ b/ivette/src/dome/src/renderer/text/buffers.ts @@ -0,0 +1,649 @@ +// -------------------------------------------------------------------------- +// --- Text Documents +// -------------------------------------------------------------------------- + +/** + @packageDocumentation + @module dome/text/buffers +*/ + +import Emitter from 'events'; +import CodeMirror from 'codemirror/lib/codemirror'; + +export type Range = { from: CodeMirror.Position; to: CodeMirror.Position }; + +export interface Decorator { + /** @return a className to apply on markers with the identifier. */ + (id: string): string | undefined; +} + +export interface TextMarkerProxy { + clear(): void; + changed(): void; + find(): Range | undefined; +} + +/** + Text Marker options. + + Inherits CodeMirror + [TextMerkerOptions](https://codemirror.net/doc/manual.html#api_marker). + */ +export interface MarkerProps extends CodeMirror.TextMarkerOptions { + id?: string; + hover?: boolean; + className?: string; +} + +export interface CSSMarker { + /** Hover class `'dome-xHover-nnn'` */ + classNameId: string; + /** Marker id */ + id: string | undefined; + /** Hovered marker */ + hover: boolean; + /** Size in character */ + length: number; +} + +// -------------------------------------------------------------------------- +// --- Batched Update +// -------------------------------------------------------------------------- + +const BATCH_OPS = 500; +const BATCH_DELAY = 5; +const BATCH_RMAX = 1000; // max tag range for sorting +const BATCH_MARGINS = 20; // visible lines above the viewport + +interface MarkerOptions { + id?: string; + hover?: boolean; + className?: string; + options: CodeMirror.TextMarkerOptions; +} + +interface StackedMarker extends MarkerOptions { + startIndex: number; +} + +interface BufferedMarker extends MarkerOptions { + startIndex: number; + stopIndex: number; +} + +type BufferedTag = BufferedMarker | undefined; + +function rankTag(lmin: number, lmax: number, tg: BufferedTag) { + if (tg && (lmin <= tg.stopIndex || tg.startIndex <= lmax)) { + const size = tg.stopIndex - tg.startIndex; + return size < BATCH_RMAX ? size : BATCH_RMAX; + } + return BATCH_RMAX; +} + +function byVisibleTag(lmin: number, lmax: number) { + return (a: BufferedTag, b: BufferedTag) => ( + rankTag(lmin, lmax, a) - rankTag(lmin, lmax, b) + ); +} + +// -------------------------------------------------------------------------- +// --- Buffer +// -------------------------------------------------------------------------- + +export interface RichTextBufferProps { + + /** + * CodeMirror [mode](https://codemirror.net/mode/index.html) specification. + */ + mode?: any; + + /** Maximum number of lines in the buffer. */ + maxlines?: number; + +} + +/** + Rich Text Content and State. + + A buffer encapsulate a CodeMirror document instance inside an standard + Node event emitter. CodeMirror signals are automatically linked back to + the event emitter (on a lazy basis). + + All buffer modifications are stacked and executed asynchronously inside + CodeMirror batched operations to increase performance. + + The `maxlines` will control the maximum number of lines kept in the buffer. + By default, it is set to `10000`, but you can use `null`, `0` or any negative + value to disable this behavior. See also `this.setMaxlines()` method. + + Additionnaly, a Buffer maintains an _edited_ state internally that can + be manually updated, and that is automatically set to `true` when the + underlying CodeMirror document is changed. It is associated to an `'edited'` + event of the buffer, and can be used to manage the « Saved » / « Not-Saved » + state of an editor, for instance. + + Typically, a sequence of changed events would fire a unique `'edited'` buffer + event, untill `setEdited(false)` is invoked. The `clear()` method also resets + the _edited_ state to `false`, but sill emit an `'edited'` event if the + buffer was not empty. + + Buffers can also be updated programmatically by various methods. In addition + to specified CodeMirror modes, you can also attach text markers + programmatically with a push/pop API. + + Text markers can be associated with an identifier, that can be used for + dynamic highlighting, called Decorations. Decorations are class names that + add styling attributes upon the current mode. Typically, consider only using + background colors, underlines and/or strike-through for decorations, since + font styles and colors are likely to be managed by CodeMirror modes, unless + you know exactly what your are doing. + + The `setDecorator` method can be used to set or update + the highlighting function that computes the decoration of a text marker + from its identifier. When the decorations may have change, you shall either + set again the highlighting function with a call to `setDecorator()` or call + the `updateDecorations()` method. This also triggers the `'decorated'` event. + + */ + +export class RichTextBuffer extends Emitter { + + private document: CodeMirror.Doc; + private maxlines = 10000; + private editors: CodeMirror.Editor[] = []; + private cacheIndex = 0; // document index, negative if not computed + private bufferedText: string; // buffered text to append + private bufferedTags: BufferedTag[]; + private stacked: StackedMarker[] = []; + private batched = false; + + // Indexed by CSS property dome-xHover-nnnn + private cssmarkers = new Map<string, CSSMarker>(); + + // Indexed by marker user identifier + private textmarkers = new Map<string, CodeMirror.TextMarker[]>(); + + private decorator?: Decorator; + private edited = false; + private focused = false; + private markid = 0; + + constructor(props: RichTextBufferProps = {}) { + super(); + const { mode, maxlines } = props; + this.document = new CodeMirror.Doc('', mode); + this.cacheIndex = -1; + this.bufferedTags = []; + this.bufferedText = ''; + this.setMaxlines(maxlines); + this.setEdited = this.setEdited.bind(this); + this.setFocused = this.setFocused.bind(this); + this.clear = this.clear.bind(this); + this.append = this.append.bind(this); + this.setValue = this.setValue.bind(this); + this.getValue = this.getValue.bind(this); + this.updateDecorations = this.updateDecorations.bind(this); + this.onChange = this.onChange.bind(this); + this.doBatch = this.doBatch.bind(this); + this.log = this.log.bind(this); + } + + /** + Internal CodeMirror + [document](https://codemirror.net/doc/manual.html#api_doc) instance. + */ + getDoc(): CodeMirror.Doc { return this.document; } + + // -------------------------------------------------------------------------- + // --- Buffer Manipulation + // -------------------------------------------------------------------------- + + /** Clear buffer contents and resets _edited_ state. */ + clear() { this.setValue(''); } + + /** + Writes in the buffer. All parameters are converted to string and joined + with spaces. The generated change event has origin `'buffer'` and it does + not modifies the _edited_ internal state. + */ + append(...values: any[]) { + if (values.length > 0) { + const text = values.join(' '); + this.bufferedText += text; + this.armBatch(); + } + } + + /** + Starts a new line in the buffer. If the current buffer content does not + finish at the beginning of a fresh line, inserts a newline character. + */ + flushline() { + const doc = this.document; + const buf = this.bufferedText; + if (buf === '') { + const start = doc.posFromIndex(Infinity); + if (start.ch > 0) doc.replaceRange('\n', start, undefined, 'buffer'); + this.cacheIndex = -1; + } else if (buf[buf.length - 1] !== '\n') this.bufferedText += '\n'; + } + + /** + Appends with newline and auto-scrolling. This is a short-cut to + `flushline()` followed by `append(...value,'\n')` and `scroll()`. + */ + log(...values: any[]) { + this.flushline(); + this.append(...values, '\n'); + this.scroll(); + } + + /** + Replace all textual content with the given string. + Also remove all markers. + */ + setValue(txt = '') { + this.document.setValue(txt); + this.cssmarkers.clear(); + this.textmarkers.clear(); + this.stacked = []; + this.bufferedTags = []; + this.bufferedText = ''; + this.cacheIndex = 0; + this.edited = false; + this.focused = false; + this.markid = 0; + } + + /** Return the textual contents of the buffer. */ + getValue() { return this.document.getValue(); } + + // -------------------------------------------------------------------------- + // --- Text Markers + // -------------------------------------------------------------------------- + + /** + Opens a text marker. + + Opens a text marker at the current (end) position in the buffer. + + The text marker is stacked and would be actually created on the matching + [[closeTextMarker]] call. It will be applied to the full range of text + inserted between its associated [[openTextMarker]] and [[closeTextMarker]] + calls. + + The returned text marker is actually a _proxy_ to the text marker that will + be eventually created by [[closeTextMarker]]. Its methods are automatically + forwarded to the actual `CodeMirror.TextMarker` instance, once created. + Hence, you can safely invoke these methods on either the _proxy_ or the + _final_ text marker at your convenience. + */ + openTextMarker(props: MarkerProps) { + const { id, hover, className, ...options } = props; + const startIndex = this.getLastIndex() + this.bufferedText.length; + this.stacked.push({ startIndex, id, hover, className, options }); + } + + /** + Closes the last opened marker. + + Returns the (actual) [text marker] + (https://codemirror.net/doc/manual.html#api_marker) ; the proxy + returned by the corresponding call to [[openTextMarker]] is automatically + bound to the actual one. + + */ + closeTextMarker() { + const tag = this.stacked.pop(); + if (tag) { + const stopIndex = this.getLastIndex() + this.bufferedText.length; + this.bufferedTags.push({ stopIndex, ...tag }); + this.armBatch(); + } + } + + /** Lookup for the text markers associated with a marker identifier. + Remove the marked tags from the buffered tag array. */ + findTextMarker(id: string): CodeMirror.TextMarker[] { + this.doFlushText(); + this.bufferedTags.forEach((tg, idx) => { + if (tg?.id === id) { + this.doMark(tg); + this.bufferedTags[idx] = undefined; + } + }); + return this.textmarkers.get(id) ?? []; + } + + /** Lopokup for a hover class. */ + findHover(className: string): CSSMarker | undefined { + return this.cssmarkers.get(className); + } + + // -------------------------------------------------------------------------- + // --- Highlighter + // -------------------------------------------------------------------------- + + /** + Define highlighter. + + @param fn - highlighter, `fn(id)` shall return a className to apply + on text markers with the provided identifier. + */ + setDecorator(fn: Decorator) { + this.decorator = fn; + this.emit('decorated'); + } + + /** + Current highlighter. + */ + getDecorator() { return this.decorator; } + + /** + Rehighlight document. + + Emits the `'decorated'` event to make editors + updating the decorations of identified text markers. + + This can be used when decoration shall be re-computed, + even if the decoration function was not modified. + + The method is bound to `this`. + */ + updateDecorations() { this.emit('decorated'); } + + // -------------------------------------------------------------------------- + // --- Edited State + // -------------------------------------------------------------------------- + + /** + Set edited state. + + The method is automatically invoked by editor views when the user actually + edit the document. The state is _not_ modified when modifying the document + from buffer's own methods, _eg._ `append()` and `clear()`. + + The method fires the `'edited'` event on modifications. This method is + bound to `this`, hence `this.setEdited` can be used as a valid callback + function. + + @param state - the new edited state (defaults to `true`). + */ + setEdited(state = true) { + if (state !== this.edited) { + this.edited = state; + this.emit('edited', state); + } + } + + /** + Set focused state. + + The method is automatically invoked by editor views when they gain or lose + focus or when the user actually interact with the view, + eg. mouse-scrolling, edition, cursor move, etc. The escape key `ESC` + explicitly relax the _focused_ state, although the editor view might + actually keep the _focus_. + + When a buffer is _focused_, shrinking and auto-scrolling are temporarily + deactivated to avoid confusing user's experience. + + The method fires `'focused'` events on modifications. This method is bound + to `this`, hence `this.setEdited` can be used as a valid callback function. + + @param focus - the new focused state (defaults to `true`). + */ + setFocused(state = true) { + if (state !== this.focused) { + this.focused = state; + this.emit('focused', state); + this.doShrink(); + } + } + + /** Returns the current _edited_ state. */ + isEdited() { return this.edited; } + + /** Returns the current _focused_ state. */ + isFocused() { return this.focused; } + + // -------------------------------------------------------------------------- + // --- Document Scrolling + // -------------------------------------------------------------------------- + + /** + Set (or unset) the maximum number of lines. + + By default, the maximum number of lines is set to `10,000` to avoid + unwanted memory leaks. Setting `null`, `0` of any negative value cancel the + management of maximum number of lines. + + Although CodeMirror is powerfull enough to manage huge buffers, you should + turn this limit _off_ with care. + */ + setMaxlines(maxlines = 10000) { + this.maxlines = maxlines > 0 ? 1 + maxlines : 0; + this.doShrink(); + } + + /** + Requires all connected views to scroll to the + specified line or identified marker. + + @param position - + defaults to the end of buffer (when not focused). + */ + scroll(position?: string | number): void { + if (position === undefined) { + if (!this.focused) { + this.doFlushText(); + this.emit('scroll', this.document.lastLine()); + } + } else if (typeof position === 'number') { + this.doFlushText(); + this.emit('scroll', position); + } else if (typeof position === 'string') { + let line = Infinity; + this.findTextMarker(position).forEach((tm) => { + const rg = tm.find(); + const ln = rg.from.line; + if (ln < line) line = ln; + }); + if (line !== Infinity) + this.emit('scroll', line); + } + } + + // -------------------------------------------------------------------------- + // --- Document Linking + // -------------------------------------------------------------------------- + + private onChange( + _editor: CodeMirror.Editor, + change: CodeMirror.EditorChangeLinkedList, + ) { + if (change.origin !== 'buffer') { + this.setEdited(true); + this.cacheIndex = -1; + } + this.emit('change'); + } + + /** + Binds this buffer to a CodeMirror instance. + + Uses CodeMirror linked documents to allow several CodeMirror instances to + be linked to the same buffer. Can be released with [[unlink]]. + + @param cm - code mirror instance to link this document in. + */ + link(cm: CodeMirror.Editor) { + const newDoc = this.document.linkedDoc( + { sharedHist: true, mode: undefined }, + ); + cm.swapDoc(newDoc); + cm.on('change', this.onChange); + this.editors.push(cm); + } + + /** + Release a linked CodeMirror document previously linked with [[link]]. + @param cm - the code mirror instance to unlink + */ + unlink(cm: CodeMirror.Editor) { + const oldDoc = cm.getDoc(); + this.document.unlinkDoc(oldDoc); + this.editors = this.editors.filter((cm0) => { + if (cm === cm0) { + cm.off('change', this.onChange); + return false; + } + return true; + }); + } + + /** + Iterates over each linked CodeMirror instances + + The operation `fn` is performed on each code mirror + instance currently linked to this buffer. + + Exceptions raised by `fn` are catched and dumped in the console. + */ + forEach(fn: (editor: CodeMirror.Editor) => void) { + this.editors.forEach((cm) => { + try { fn(cm); } catch (e) { console.error('[Dome.text]', e); } + }); + } + + // -------------------------------------------------------------------------- + // --- Update Operations + // -------------------------------------------------------------------------- + + /* Append Operation */ + private doFlushText() { + const text = this.bufferedText; + if (text.length > 0) { + this.bufferedText = ''; + const doc = this.document; + const start = doc.posFromIndex(Infinity); + doc.replaceRange(text, start, undefined, 'buffer'); + this.cacheIndex = -1; + this.doShrink(); + } + } + + /* Shrink Operation */ + private doShrink() { + const lines = this.document.lineCount(); + if (lines > this.maxlines) { + const p = this.document.firstLine(); + const q = p + lines - this.maxlines; + this.document.replaceRange( + '', + { line: p, ch: 0 }, + { line: q, ch: 0 }, + 'buffer', + ); + this.cacheIndex = -1; + } + } + + /* Close Operation */ + private doMark(tag: BufferedMarker) { + const { id, hover, className, startIndex, stopIndex } = tag; + let markerId; + if (id || hover) { + markerId = `dome-xHover-${this.markid++}`; + const cmark = { + id, + classNameId: markerId, + hover: hover ?? (id !== undefined), + length: stopIndex - startIndex, + }; + this.cssmarkers.set(markerId, cmark); + } + const fullClassName = [ + 'dome-xMarked', + id && (`dome-xMark-${id}`), + markerId, + className, + ].filter((s) => !!s).join(' '); + const options = { + shared: true, + className: fullClassName, + ...tag.options, + }; + const doc = this.document; + const start = doc.posFromIndex(startIndex); + const stop = doc.posFromIndex(stopIndex); + const marker = doc.markText(start, stop, options); + if (id) { + const markers = this.textmarkers; + const ms = markers.get(id); + if (ms === undefined) + markers.set(id, [marker]); + else + ms.push(marker); + } + } + + private doFilterTags() { + const tgs = this.bufferedTags; + if (tgs.length > 0 && this.editors.length > 0) { + let lmin = Infinity; + let lmax = 0; + this.forEach((cm) => { + const { from: fr, to } = cm.getViewport(); + if (to > lmax) lmax = to; + if (fr < lmin) lmin = fr; + }); + if (lmin <= lmax) tgs.sort(byVisibleTag(lmin, lmax + BATCH_MARGINS)); + } + return tgs.splice(0, BATCH_OPS); + } + + private getLastIndex() { + if (this.cacheIndex < 0) { + const doc = this.document; + const line = doc.lastLine() + 1; + this.cacheIndex = doc.indexFromPos({ line, ch: 0 }); + } + return this.cacheIndex; + } + + // -------------------------------------------------------------------------- + // --- Batched Operations + // -------------------------------------------------------------------------- + + private armBatch() { + if (!this.batched) { + this.batched = true; + setTimeout(this.doBatch, BATCH_DELAY); + } + } + + /* Batch Operation */ + private doBatch() { + this.batched = false; + if (!this.bufferedText.length && !this.bufferedTags.length) return; + try { + // Start Operations + this.forEach((cm) => cm.startOperation()); + // Flush Text + this.doFlushText(); + // Mark Tags + this.doFilterTags().forEach((tg) => { if (tg) this.doMark(tg); }); + // End Operations + this.forEach((cm) => cm.endOperation()); + } finally { + if (this.bufferedTags.length > 0) + this.armBatch(); + } + } + +} + +// -------------------------------------------------------------------------- diff --git a/ivette/src/dome/src/renderer/text/editors.js b/ivette/src/dome/src/renderer/text/editors.js deleted file mode 100644 index a2efb0ea1c654779e3330937071fac753ad591a2..0000000000000000000000000000000000000000 --- a/ivette/src/dome/src/renderer/text/editors.js +++ /dev/null @@ -1,416 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Text Documents -// -------------------------------------------------------------------------- - -/** - @packageDocumentation - @module dome/text/editors -*/ - -import _ from 'lodash' ; -import React from 'react' ; -import * as Dome from 'dome' ; -import { Vfill } from 'dome/layout/boxes' ; -import CodeMirror from 'codemirror/lib/codemirror.js' ; - -import './style.css' ; -import 'codemirror/lib/codemirror.css' ; - -const CSS_HOVERED = 'dome-xText-hover' ; -const CSS_SELECTED = 'dome-xText-select' ; - -const getConfig = ({ - buffer, - selection, - onSelection, - onContextMenu, - fontSize, - ...config -}) => config; - -// -------------------------------------------------------------------------- -// --- Code Mirror Instance Wrapper -// -------------------------------------------------------------------------- - -class CodeMirrorWrapper extends React.Component { - - constructor(props) { - super(props); - this.mountPoint = this.mountPoint.bind(this); - this.refresh = this.refresh.bind(this); - this.autoRefresh = this.autoRefresh.bind(this); - this.onBlur = this.onBlur.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onScroll = this.onScroll.bind(this); - this.onClick = this.onClick.bind(this); - this.onContextMenu = this.onContextMenu.bind(this); - this.onMouseMove = this.onMouseMove.bind(this); - this.handleKey = this.handleKey.bind(this); - this.handleUpdate = this.handleUpdate.bind(this); - this.handleHover = _.throttle(this.handleHover,50); - this.handleScrollTo = this.handleScrollTo.bind(this); - this.rootElement = undefined ; - this.decorations = {} ; - this.hover = undefined ; - } - - // -------------------------------------------------------------------------- - // --- Mounting - // -------------------------------------------------------------------------- - - mountPoint(elt) { - this.rootElement = elt ; - if (elt) { - // Mounting... - const { buffer } = this.props; - const config = getConfig(this.props); - const cm = this.codeMirror = new CodeMirror(elt, { value: '' }); - if (buffer) buffer.link(cm); - // Passing all options to constructor does not work (Cf. CodeMirror's BTS) - for (var opt in config) cm.setOption( opt , config[opt] ); - cm.on('update',this.handleUpdate); - cm.on('keyHandled',this.handleKey); - buffer.on('decorated',this.handleUpdate); - buffer.on('scroll',this.handleScrollTo); - Dome.on('dome.update',this.refresh); - this.refreshPolling = setInterval( this.autoRefresh, 250 ); - this.handleUpdate(); - } else { - // Unmounting... - const polling = this.refreshPolling ; - if (polling) { - clearInterval( polling ); - this.refreshPolling = undefined ; - } - const cm = this.codeMirror ; - Dome.off('dome.update',this.refresh); - const { buffer } = this.props ; - if (cm && buffer) { - buffer.unlink(cm); - buffer.off('decorated',this.handleUpdate); - buffer.off('scroll',this.handleScrollTo); - } - this.codeMirror = undefined ; - } - } - - // -------------------------------------------------------------------------- - // --- Auto Refresh - // -------------------------------------------------------------------------- - - refresh() { - const elt = this.rootElement ; - const cm = this.codeMirror ; - if (cm && elt) { - this.currentWidth = elt.offsetWidth ; - this.currentHeight = elt.offsetHeight ; - this.currentParent = elt.offsetParent ; - cm.refresh(); - } - } - - // Polled every 250ms - autoRefresh() { - const elt = this.rootElement ; - const cm = this.codeMirror ; - if (cm && elt) { - const eltW = elt.offsetWidth ; - const eltH = elt.offsetHeight ; - const eltP = elt.offsetParent ; - if (eltW !== this.currentWidth || - eltH !== this.currentHeight || - eltP !== this.currentParent) - { - this.currentWidth = eltW ; - this.currentHeight = eltH ; - this.currentParent = eltP ; - cm.refresh(); - } - } - } - - // -------------------------------------------------------------------------- - // --- Hover - // -------------------------------------------------------------------------- - - _findHover(elt) { - const buffer = this.props.buffer ; - var best = undefined ; - elt.classList.forEach((name) => { - const hover = buffer.findHover(name); - if (hover && (!best || hover.length < best.length )) best = hover ; - }); - return best; - } - - _findDecoration(classes,buffer,decorator) - { - var best_hover = undefined ; - var best_decor = undefined ; - var best_decoration = undefined ; - classes.forEach((name) => { - - const hover = buffer.findHover(name); - var decoration = hover && hover.id && decorator(hover.id); - - if (hover && (!best_hover || hover.length < best_hover.length)) - best_hover = hover ; - - if (decoration && (!best_decor || hover.length < best_decor.length)) { - best_decor = hover ; - best_decoration = decoration ; - } - - }); - return best_hover && { classNameId: best_hover.classNameId , decoration: best_decoration } ; - } - - _markElementsWith(classNameId,className) { - const root = this.rootElement ; - const tohover = root && root.getElementsByClassName(classNameId); - const n = tohover ? tohover.length : 0 ; - for (var k = 0; k < n; k++) tohover[k].classList.add(className); - } - - _unmarkElementsWith(className) { - const root = this.rootElement ; - const hovered = root && root.getElementsByClassName(className); - const n = hovered ? hovered.length : 0 ; - if (n==0) return ; - const elts = new Array(n); ; - for (var k = 0; k < n; k++) elts[k] = hovered[k]; - elts.forEach((elt) => elt.classList.remove(className)); - } - - handleHover(target) { - // Throttled (see constructor) - const older = this.hover ; - const hover = this._findHover(target); - if (older !== hover) { - if (older) this._unmarkElementsWith( CSS_HOVERED ); - if (hover && hover.hover) this._markElementsWith( hover.classNameId, CSS_HOVERED ); - this.hover = hover ; - } - } - - handleUpdate() { - const root = this.rootElement ; - const marked = root && root.getElementsByClassName('dome-xMarked'); - const n = marked ? marked.length : 0 ; - if (n==0) return; - const hover = this.hover ; - const hovered = hover && hover.hover && hover.classNameId ; - const selection = this.props.selection ; - const selected = selection && ('dome-xMark-' + selection) ; - const buffer = this.props.buffer; - const decorator = buffer.getDecorator(); - if ( !hovered && !selection && !decorator ) return; - const actual = {} ; - for (var k = 0; k < n; k++) { - const elt = marked[k]; - const classes = elt.classList ; - if (hovered && classes.contains(hovered)) classes.add( CSS_HOVERED ); - if (selected && classes.contains(selected)) classes.add( CSS_SELECTED ); - if (decorator) { - const hover = this._findDecoration(classes,buffer,decorator,hovered,selected); - if (hover) { - const prev = this.decorations[ hover.classNameId ]; - const curr = hover.decoration ; - if (prev !== curr && prev) classes.remove(prev); - if (curr) { classes.add(curr); actual[ hover.classNameId ] = curr ; } - } - } - } - this.decorations = actual ; - } - - onMouseMove(evt) { - // Not throttled (can not leak Synthetic Events) - this.handleHover( evt.target ); - } - - onMouseClick(evt,callback) { - // No need for throttling - const target = evt.target ; - if ( target && callback ) { - const hover = this._findHover(target); - if ( hover && hover.id ) callback( hover.id ); - } - this.props.buffer.setFocused(true); - } - - onClick(evt) { this.onMouseClick(evt,this.props.onSelection); } - onContextMenu(evt) { this.onMouseClick(evt,this.props.onContextMenu); } - - // -------------------------------------------------------------------------- - // --- Scrolling - // -------------------------------------------------------------------------- - - handleScrollTo(position) { - try { - const cm = this.codeMirror; - cm && cm.scrollIntoView(position); - } catch(_error) { } // Out of range - } - - // -------------------------------------------------------------------------- - // --- Focus - // -------------------------------------------------------------------------- - - handleKey(cm,key,_evt) { - switch(key) { - case 'Esc': - this.props.buffer.setFocused(false); - break; - default: - this.props.buffer.setFocused(true); - break; - } - } - - onFocus() { this.props.buffer.setFocused(true); } - onBlur() { this.props.buffer.setFocused(false); } - onScroll() { - const cm = this.codeMirror; - if (cm) { - const rect = cm.getScrollInfo(); - const delta = rect.height-rect.top-rect.clientHeight; - const buffer = this.props.buffer; - buffer.setFocused(delta > 0); - } - } - - // -------------------------------------------------------------------------- - // --- Rendering - // -------------------------------------------------------------------------- - - shouldComponentUpdate(newProps) { - const cm = this.codeMirror ; - if (cm) { - // Swap documents if necessary - const { - buffer:oldBuffer, - selection:oldSelect, - fontSize:oldFont - } = this.props; - const { - buffer:newBuffer, - selection:newSelect, - fontSize:newFont - } = newProps ; - if (oldBuffer !== newBuffer) { - if (oldBuffer) oldBuffer.unlink(cm); - if (newBuffer) newBuffer.link(cm); - else cm.clear(); - } - const oldConfig = getConfig(this.props); - const newConfig = getConfig(newProps); - // Incremental update options - var opt ; - for ( opt in oldConfig ) if (!(opt in newConfig)) { - const defValue = CodeMirror.defaults[opt]; - if (defValue) - cm.setOption( opt , defValue ); - } - for ( opt in newConfig ) { - const oldValue = oldConfig[opt]; - const newValue = newConfig[opt]; - if (newValue !== oldValue) { - cm.setOption( opt , newValue ); - } - } - // Update selection - if ( oldSelect !== newSelect ) { - if (oldSelect) this._unmarkElementsWith( CSS_SELECTED ); - if (newSelect) this._markElementsWith( 'dome-xMark-' + newSelect, CSS_SELECTED ); - } - // Refresh on new font - if ( oldFont !== newFont ) setImmediate(this.refresh); - } - // Keep mounted node unchanged - return false; - } - - render() { - return ( - <div className={'dome-xText'} - ref={this.mountPoint} - onClick={this.onClick} - onContextMenu={this.onContextMenu} - onBlur={this.onBlur} - onFocus={this.onFocus} - onScroll={this.onScroll} - onMouseMove={this.onMouseMove} - />); - } - -} - -// -------------------------------------------------------------------------- -// --- Text View -// -------------------------------------------------------------------------- - -/** - @summary Rich Text Editor. - @property {Buffer} buffer - associated Buffer holding the text content - @property {string} className - additional class name(s) - @property {object} style - additional CSS style - @property {number} fontSize - editor font-size - @property {string} selection - currently selected markder identifier - @property {function} onSelection - callback used when an identified marker is clicked - @property {function} onContextMenu - selection callback on right-click - @property {object} [...options] - additional CodeMirror - [configuration](https://codemirror.net/doc/manual.html#config) properties - @description - - A component rendering the content of a text buffer, that shall be instances - of the `Buffer` base class. - - The view is based on a [CodeMirror](https://codemirror.net) component linked with - the internal Code Mirror Document from the associated buffer. - - Multiple views might share the same buffer as source content. The buffer will be - kept in sync with all its linked views. - - The Text component never update its mounted NODE element, however, all property - modifications (including buffer) are propagated to the internal CodeMirror instance. - Undefined properties are set (or reset) to the CodeMirror defaults. - - ##### Themes - - The CodeMirror `theme` option allow you to style your document, - especially when using modes. - Themes are only accessible if you load the associated CSS style sheet. - For instance, to use the `'ambiance'` theme provided with CodeMirror, you shall - import `'codemirror/theme/ambiance.css'` somewhere in your application. - - ##### Modes & Adds-On - - You can install modes and adds-on provided by the CodeMirror distribution by - simply importing (once, before being used) the associated modules in your - application. For instance, to use the `'javascript'` mode option, you shall - import `'codemirror/mode/javascript/javascript.js'` file in your application. - - ##### Further Customization - - You can register your own extensions directly into the global `CodeMirror` - class instance. However, the correct instance must be retrieved by using - `import CodeMirror from 'codemirror/lib/codemirror.js'` ; using `from - 'codemirror'` returns a different instance of `CodeMirror` class and will - not work. - - */ -export function Text(props) { - let { className, style, fontSize, ...cmprops } = props ; - if (fontSize < 4) fontSize = 4; - if (fontSize > 48) fontSize = 48; - const theStyle = Object.assign( {} , style ); - theStyle.fontSize = fontSize ; - return ( - <Vfill className={className} style={theStyle}> - <CodeMirrorWrapper fontSize={fontSize} {...cmprops}/> - </Vfill> - ); -} - -// -------------------------------------------------------------------------- diff --git a/ivette/src/dome/src/renderer/text/editors.tsx b/ivette/src/dome/src/renderer/text/editors.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a1592f1e971258232eb176afff4ba55d6b33a040 --- /dev/null +++ b/ivette/src/dome/src/renderer/text/editors.tsx @@ -0,0 +1,513 @@ +// -------------------------------------------------------------------------- +// --- Text Documents +// -------------------------------------------------------------------------- + +/** + @packageDocumentation + @module dome/text/editors +*/ + +import _ from 'lodash'; +import React from 'react'; +import * as Dome from 'dome'; +import { Vfill } from 'dome/layout/boxes'; +import CodeMirror, { EditorConfiguration } from 'codemirror/lib/codemirror'; +import { RichTextBuffer, CSSMarker, Decorator } from './buffers'; + +import './style.css'; +import 'codemirror/lib/codemirror.css'; + +const CSS_HOVERED = 'dome-xText-hover'; +const CSS_SELECTED = 'dome-xText-select'; + +/* --------------------------------------------------------------------------*/ +/* --- View Properties --- */ +/* --------------------------------------------------------------------------*/ + +export interface MarkerCallback { + (id: string): void; +} + +/** + Text Editor configuration. + + Inherits CodeMirror + [EditorConfiguration](https://codemirror.net/doc/manual.html#config) + options. + */ + +export interface TextProps extends CodeMirror.EditorConfiguration { + + /** The buffer to view. */ + buffer: RichTextBuffer | undefined; + + /** The currently selected marker identifier. */ + selection?: string; + + /** Callback on identified marker selection. */ + onSelection?: MarkerCallback; + + /** Callback on identified marker right-click. */ + onContextMenu?: MarkerCallback; + + /** Font Size. */ + fontSize?: number; + + /** Additional className for the text container. */ + className?: string; + + /** Additional style. */ + style?: React.CSSProperties; + +} + +/* --------------------------------------------------------------------------*/ +/* --- CodeMirror Configuration Utils ---*/ +/* --------------------------------------------------------------------------*/ + +function getConfig(props: TextProps): CodeMirror.EditorConfiguration { + const { + buffer, + selection, + onSelection, + onContextMenu, + fontSize, + ...config + } = props; + return config; +} + +type MouseEvt = React.MouseEvent<HTMLDivElement, MouseEvent>; +type CMoption = keyof EditorConfiguration; + +function forEachOption( + config: EditorConfiguration, + fn: (opt: CMoption) => void, +) { + (Object.keys(config) as (keyof EditorConfiguration)[]).forEach(fn); +} + +// -------------------------------------------------------------------------- +// --- Code Mirror Instance Wrapper +// -------------------------------------------------------------------------- + +class CodeMirrorWrapper extends React.Component<TextProps> { + + private currentParent?: Element | null; + private currentHeight?: number; + private currentWidth?: number; + private rootElement: HTMLDivElement | null = null; + private codeMirror?: CodeMirror.Editor; + private refreshPolling?: NodeJS.Timeout; + + // Currently hovered 'dome-xHover-nnn' + private marker?: CSSMarker; + + // Maps hovered 'dome-xMark-id' to their decorations + private decorations = new Map<string, string>(); + + constructor(props: TextProps) { + super(props); + this.mountPoint = this.mountPoint.bind(this); + this.refresh = this.refresh.bind(this); + this.autoRefresh = this.autoRefresh.bind(this); + this.onBlur = this.onBlur.bind(this); + this.onFocus = this.onFocus.bind(this); + this.onScroll = this.onScroll.bind(this); + this.onClick = this.onClick.bind(this); + this.onContextMenu = this.onContextMenu.bind(this); + this.onMouseMove = this.onMouseMove.bind(this); + this.handleKey = this.handleKey.bind(this); + this.handleUpdate = this.handleUpdate.bind(this); + this.handleHover = _.throttle(this.handleHover, 50); + this.handleScrollTo = this.handleScrollTo.bind(this); + } + + // -------------------------------------------------------------------------- + // --- Mounting + // -------------------------------------------------------------------------- + + mountPoint(elt: HTMLDivElement | null) { + this.rootElement = elt; + if (elt !== null) { + // Mounting... + const { buffer } = this.props; + const config = getConfig(this.props); + this.codeMirror = CodeMirror(elt, { value: '' }); + if (buffer) { + buffer.link(this.codeMirror); + buffer.on('decorated', this.handleUpdate); + buffer.on('scroll', this.handleScrollTo); + } + // Passing all options to constructor does not work (Cf. CodeMirror's BTS) + forEachOption( + config, (opt) => this.codeMirror?.setOption(opt, config[opt]), + ); + // Binding events to view + this.codeMirror.on('update', this.handleUpdate); + this.codeMirror.on('keyHandled', this.handleKey); + Dome.update.on(this.refresh); + // Auto refresh + this.refreshPolling = setInterval(this.autoRefresh, 250); + this.handleUpdate(); + } else { + // Unmounting... + if (this.refreshPolling) { + clearInterval(this.refreshPolling); + this.refreshPolling = undefined; + } + Dome.update.off(this.refresh); + const { buffer } = this.props; + if (this.codeMirror && buffer) { + buffer.unlink(this.codeMirror); + buffer.off('decorated', this.handleUpdate); + buffer.off('scroll', this.handleScrollTo); + } + this.codeMirror = undefined; + this.marker = undefined; + this.decorations.clear(); + } + } + + // -------------------------------------------------------------------------- + // --- Auto Refresh + // -------------------------------------------------------------------------- + + refresh() { + const elt = this.rootElement; + const cm = this.codeMirror; + if (cm && elt) { + this.currentWidth = elt.offsetWidth; + this.currentHeight = elt.offsetHeight; + this.currentParent = elt.offsetParent; + cm.refresh(); + } + } + + // Polled every 250ms + autoRefresh() { + const elt = this.rootElement; + const cm = this.codeMirror; + if (cm && elt) { + const eltW = elt.offsetWidth; + const eltH = elt.offsetHeight; + const eltP = elt.offsetParent; + if (eltW !== this.currentWidth || + eltH !== this.currentHeight || + eltP !== this.currentParent) { + this.currentWidth = eltW; + this.currentHeight = eltH; + this.currentParent = eltP; + cm.refresh(); + } + } + } + + // -------------------------------------------------------------------------- + // --- Hover + // -------------------------------------------------------------------------- + + _findMarker(elt: Element): CSSMarker | undefined { + const { buffer } = this.props; + if (buffer) { + let best: CSSMarker | undefined; + elt.classList.forEach((name) => { + const marker = buffer.findHover(name); + if (marker && (!best || marker.length < best.length)) best = marker; + }); + return best; + } + return undefined; + } + + // eslint-disable-next-line class-methods-use-this + _findDecoration( + classes: DOMTokenList, + buffer: RichTextBuffer, + decorator: Decorator, + ) { + let bestMarker: CSSMarker | undefined; + let bestDecorated: CSSMarker | undefined; + let bestDecoration: string | undefined; + classes.forEach((name) => { + + const marker = buffer.findHover(name); + const id = marker && marker.id; + const decoration = id && decorator(id); + + if (marker && (!bestMarker || marker.length < bestMarker.length)) { + bestMarker = marker; + } + + if (marker && decoration && + (!bestDecorated || marker.length < bestDecorated.length)) { + bestDecorated = marker; + bestDecoration = decoration; + } + + }); + return bestMarker ? { + classNameId: bestMarker.classNameId, + decoration: bestDecoration, + } : undefined; + } + + _markElementsWith(classNameId: string, className: string) { + const root = this.rootElement; + const toMark = root && root.getElementsByClassName(classNameId); + if (toMark) { + const n = toMark.length; + if (n === 0) return; + for (let k = 0; k < n; k++) toMark[k].classList.add(className); + } + } + + _unmarkElementsWith(className: string) { + const root = this.rootElement; + const toUnmark = root && root.getElementsByClassName(className); + if (toUnmark) { + const n = toUnmark.length; + if (n === 0) return; + const elts: Element[] = new Array(n); + for (let k = 0; k < n; k++) elts[k] = toUnmark[k]; + elts.forEach((elt) => elt.classList.remove(className)); + } + } + + handleHover(target: Element) { + // Throttled (see constructor) + const oldMarker = this.marker; + const newMarker = this._findMarker(target); + if (oldMarker !== newMarker) { + if (oldMarker) this._unmarkElementsWith(CSS_HOVERED); + if (newMarker && newMarker.hover) + this._markElementsWith(newMarker.classNameId, CSS_HOVERED); + this.marker = newMarker; + } + } + + handleUpdate() { + const root = this.rootElement; + const marked = root && root.getElementsByClassName('dome-xMarked'); + if (!marked) return; + const n = marked.length; + if (n === 0) return; + const { marker } = this; + const hovered = (marker && marker.hover) ? marker.classNameId : undefined; + const { selection } = this.props; + const selected = selection && (`dome-xMark-${selection}`); + const { buffer } = this.props; + const decorator = buffer?.getDecorator(); + if (!hovered && !selection && !decorator) return; + const newDecorations = new Map<string, string>(); + for (let k = 0; k < n; k++) { + const elt = marked[k]; + const classes = elt.classList; + if (hovered && classes.contains(hovered)) classes.add(CSS_HOVERED); + if (selected && classes.contains(selected)) classes.add(CSS_SELECTED); + if (buffer && decorator) { + const deco = this._findDecoration(classes, buffer, decorator); + if (deco) { + const prev = this.decorations.get(deco.classNameId); + const curr = deco.decoration; + if (prev && prev !== curr) classes.remove(prev); + if (curr) { + classes.add(curr); + newDecorations.set(deco.classNameId, curr); + } + } + } + } + this.decorations = newDecorations; + } + + onMouseMove(evt: MouseEvt) { + // Not throttled (can not leak Synthetic Events) + const tgt = evt.target; + if (tgt instanceof Element) this.handleHover(tgt); + } + + onMouseClick(evt: MouseEvt, callback: MarkerCallback | undefined) { + // No need for throttling + const { target } = evt; + if (target instanceof Element && callback) { + const marker = this._findMarker(target); + if (marker && marker.id) callback(marker.id); + } + this.props.buffer?.setFocused(true); + } + + onClick(evt: MouseEvt) { + this.onMouseClick(evt, this.props.onSelection); + } + + onContextMenu(evt: MouseEvt) { + this.onMouseClick(evt, this.props.onContextMenu); + } + + // -------------------------------------------------------------------------- + // --- Scrolling + // -------------------------------------------------------------------------- + + handleScrollTo(line: number) { + try { + const cm = this.codeMirror; + return cm && cm.scrollIntoView({ line, ch: 0 }); + } catch (_error) { + console.warn(`[Dome] Unable to scroll to line ${line}: out of range.`); + } + } + + // -------------------------------------------------------------------------- + // --- Focus + // -------------------------------------------------------------------------- + + handleKey(_cm: CodeMirror.Editor, key: string, _evt: KeyboardEvent) { + switch (key) { + case 'Esc': + this.props.buffer?.setFocused(false); + break; + default: + this.props.buffer?.setFocused(true); + break; + } + } + + onFocus() { this.props.buffer?.setFocused(true); } + onBlur() { this.props.buffer?.setFocused(false); } + onScroll() { + const cm = this.codeMirror; + const { buffer } = this.props; + if (cm && buffer) { + const rect = cm.getScrollInfo(); + const delta = rect.height - rect.top - rect.clientHeight; + buffer.setFocused(delta > 0); + } + } + + // -------------------------------------------------------------------------- + // --- Rendering + // -------------------------------------------------------------------------- + + shouldComponentUpdate(newProps: TextProps) { + const cm = this.codeMirror; + if (cm) { + // Swap documents if necessary + const { + buffer: oldBuffer, + selection: oldSelect, + fontSize: oldFont, + } = this.props; + const { + buffer: newBuffer, + selection: newSelect, + fontSize: newFont, + } = newProps; + if (oldBuffer !== newBuffer) { + if (oldBuffer) oldBuffer.unlink(cm); + if (newBuffer) newBuffer.link(cm); + else cm.setValue(''); + } + const oldConfig = getConfig(this.props); + const newConfig = getConfig(newProps); + // Incremental update options + forEachOption(oldConfig, (opt) => { + if (!(opt in newConfig)) { + const defValue = CodeMirror.defaults[opt]; + if (defValue) + cm.setOption(opt, defValue); + } + }); + forEachOption(newConfig, (opt) => { + const oldValue = oldConfig[opt]; + const newValue = newConfig[opt]; + if (newValue !== oldValue) { + cm.setOption(opt, newValue); + } + }); + // Update selection + if (oldSelect !== newSelect) { + const selected = `dome-xMark-${newSelect}`; + if (oldSelect) this._unmarkElementsWith(CSS_SELECTED); + if (newSelect) this._markElementsWith(selected, CSS_SELECTED); + } + // Refresh on new font + if (oldFont !== newFont) setImmediate(this.refresh); + } + // Keep mounted node unchanged + return false; + } + + render() { + return ( + <div + className="dome-xText" + ref={this.mountPoint} + onClick={this.onClick} + onContextMenu={this.onContextMenu} + onBlur={this.onBlur} + onFocus={this.onFocus} + onScroll={this.onScroll} + onMouseMove={this.onMouseMove} + /> + ); + } + +} + +// -------------------------------------------------------------------------- +// --- Text View +// -------------------------------------------------------------------------- + +/** + #### Text Editor. + + A component rendering the content of a text buffer, that shall be instances + of the `Buffer` base class. + + The view is based on a [CodeMirror](https://codemirror.net) component linked + with the internal Code Mirror Document from the associated buffer. + + Multiple views might share the same buffer as source content. The buffer will + be kept in sync with all its linked views. + + The Text component never update its mounted NODE element, however, all + property modifications (including buffer) are propagated to the internal + CodeMirror instance. Undefined properties are set (or reset) to the + CodeMirror defaults. + + #### Themes + + The CodeMirror `theme` option allow you to style your document, + especially when using modes. + Themes are only accessible if you load the associated CSS style sheet. + For instance, to use the `'ambiance'` theme provided with CodeMirror, you + shall import `'codemirror/theme/ambiance.css'` somewhere in your application. + + #### Modes & Adds-On + + You can install modes and adds-on provided by the CodeMirror distribution by + simply importing (once, before being used) the associated modules in your + application. For instance, to use the `'javascript'` mode option, you shall + import `'codemirror/mode/javascript/javascript.js'` file in your application. + + #### Further Customization + + You can register your own extensions directly into the global `CodeMirror` + class instance. However, the correct instance must be retrieved by using + `import CodeMirror from 'codemirror/lib/codemirror.js'` ; using `from + 'codemirror'` returns a different instance of `CodeMirror` class and will + not work. + */ +export function Text(props: TextProps) { + let { className, style, fontSize, ...cmprops } = props; + if (fontSize !== undefined && fontSize < 4) fontSize = 4; + if (fontSize !== undefined && fontSize > 48) fontSize = 48; + return ( + <Vfill className={className} style={{ ...style, fontSize }}> + <CodeMirrorWrapper fontSize={fontSize} {...cmprops} /> + </Vfill> + ); +} + +// -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/dive/Dive.tsx b/ivette/src/frama-c/dive/Dive.tsx new file mode 100644 index 0000000000000000000000000000000000000000..547aaff243abf47997c79e712891a09ec1c04e37 --- /dev/null +++ b/ivette/src/frama-c/dive/Dive.tsx @@ -0,0 +1,593 @@ +import React, { useState, useEffect } from 'react'; +import _ from 'lodash'; +import { renderToString } from 'react-dom/server'; +import * as Dome from 'dome'; +import * as Server from 'frama-c/server'; +import * as States from 'frama-c/states'; + +import * as API from 'api/plugins/dive'; + +import Cytoscape from 'cytoscape'; +import CytoscapeComponent from 'react-cytoscapejs'; +import './cytoscape_libs'; + +import tippy, * as Tippy from 'tippy.js'; +import 'tippy.js/dist/tippy.css'; +import 'tippy.js/themes/light-border.css'; +import 'tippy.js/animations/shift-away.css'; +import './tippy.css'; + +import { IconButton } from 'dome/controls/buttons'; +import { Space } from 'dome/frame/toolbars'; +import { Component, TitleBar } from 'frama-c/LabViews'; + +import '@fortawesome/fontawesome-free/js/all'; + +import style from './style.json'; +import layouts from './layouts.json'; + +interface Cxtcommand { + content: string; + select: () => void; + enabled: boolean; +} + +interface CytoscapeExtended extends Cytoscape.Core { + cxtmenu(options: any): void; +} + +function callstackToString(callstack: API.callstack): string { + return callstack.map((cs) => `${cs.fun}:${cs.instr}`).join('/'); +} + +function buildCxtMenu( + commands: Cxtcommand[], + content?: JSX.Element, + action?: () => void, +) { + commands.push({ + content: content ? renderToString(content) : '', + select: action || (() => { }), + enabled: !!action, + }); +} + +/* The Dive class handles the selection of nodes according to user actions. + To prevent cytoscape to automatically select (and unselect) nodes wrongly, + we make some nodes unselectable. We then use the functions below to make + the nodes selectable before (un)selecting them. */ + +function select(node: Cytoscape.NodeSingular): void { + node.selectify(); + node.select(); +} + +function unselect(node: Cytoscape.NodeSingular): void { + node.selectify(); + node.unselect(); +} + +export type mode = 'explore' | 'overview'; + +class Dive { + headless: boolean; + cy: Cytoscape.Core; + mode: mode = 'explore'; + _layout = ''; + layoutOptions: Cytoscape.LayoutOptions | undefined; + currentSelection: string | null = null; + onSelect: ((_: States.Location[]) => void) | null = null; + selectedLocation: (States.Location | undefined) = undefined; + + constructor(cy: Cytoscape.Core | null = null) { + this.cy = cy || Cytoscape(); + this.headless = this.cy.container() === null; + this.cy.elements().remove(); + this.cy.off('click'); // Remove previous listeners + this.cy.on('click', 'node', (event) => this.clickNode(event.target)); + + this.layout = 'cose-bilkent'; + + if (!this.headless) { + this.cy.scratch('cxtmenu')?.destroy?.(); // Remove previous menu + this.cy.scratch('cxtmenu', (this.cy as CytoscapeExtended).cxtmenu({ + selector: 'node', + commands: (node: Cytoscape.NodeSingular) => this.onCxtMenu(node), + })); + } + + this.refresh(); + } + + onCxtMenu(node: Cytoscape.NodeSingular) { + const data = node.data(); + const commands = [] as Cxtcommand[]; + buildCxtMenu(commands, + <><div className="fas fa-binoculars fa-2x" />Explore</>, + () => { this.explore(node); }); + if (data.kind === 'composite') + buildCxtMenu(commands, + <><div className="fa fa-expand-arrows-alt fa-2x" />Unfold</>); + else + buildCxtMenu(commands); + if (data.backward_explored === 'no') + buildCxtMenu(commands, + <div><div className="fa fa-eye fa-2x" />Show</div>, + () => this.show(node)); + else + buildCxtMenu(commands, + <><div className="fa fa-eye-slash fa-2x" />Hide</>, + () => this.hide(node)); + return commands; + } + + remove(node: Cytoscape.NodeSingular) { + const parent = node.parent(); + node.remove(); + this.cy.$id(`${node.id()}-more`).remove(); + if (parent.nonempty() && parent.children().empty()) + this.remove(parent as Cytoscape.NodeSingular); // Recursively remove parents + } + + referenceFile(fileName: string): Cytoscape.NodeSingular { + const id = `file_${fileName}`; + const node = this.cy.$id(id); + if (node.nonempty()) { + return node; + } + + return this.cy.add({ data: { id, label: fileName }, classes: 'file' }); + } + + referenceCallstack(callstack: API.callstack): Cytoscape.NodeSingular | null { + const name = callstackToString(callstack); + const elt = callstack.shift(); + + if (!elt) + return null; + + const id = `callstack_${name}`; + const node = this.cy.$id(id); + if (node.nonempty()) { + return node; + } + + const parentNode = this.referenceCallstack(callstack); + const parent = parentNode?.id(); + const label = elt.fun; + return this.cy.add({ data: { id, label, parent }, classes: 'function' }); + } + + createTips(node: Cytoscape.NodeSingular): Tippy.Instance[] { + const container = this.cy.container(); + if (!container) + return []; + + const common = { + interactive: true, + multiple: true, + animation: 'shift-away', + duration: 500, + trigger: 'manual', + appendTo: document.body, + lazy: false, + onCreate: (instance: Tippy.Instance) => { + const { popperInstance } = instance; + if (popperInstance) + popperInstance.reference = (node as any).popperRef(); + }, + }; + + const tips = []; + + if (node.data().values) { + tips.push(tippy(container, { + ...common, + content: node.data().values, + placement: 'top', + distance: 10, + arrow: true, + })); + } + + if (node.data().type) { + tips.push(tippy(container, { + ...common, + content: node.data().type, + placement: 'bottom', + distance: 20, + theme: 'light-border', + arrow: false, + })); + } + + return tips; + } + + addTips(node: Cytoscape.NodeSingular): void { + let timeout: NodeJS.Timeout; + let tips: Tippy.Instance[]; + + // Create tips lazily + node.on('mouseover', () => { + if (tips === undefined) + tips = this.createTips(node); + clearTimeout(timeout); + timeout = setTimeout(() => tips?.forEach((tip) => { tip.show(); }), 200); + }); + + node.on('mouseout', () => { + clearTimeout(timeout); + timeout = setTimeout(() => tips?.forEach((tip) => { tip.hide(); }), 1000); + }); + } + + /* eslint-disable no-restricted-syntax */ + receiveGraph(data: any): Cytoscape.CollectionReturnValue { + let newNodes = this.cy.collection(); + + for (const node of data.nodes) { + if (typeof node.range === 'number') + node.stops = `0% ${node.range}% ${node.range}% 100%`; + + let ele = this.cy.$id(node.id); + if (ele.nonempty()) { + ele.removeData(); + ele.data(node); + ele.neighborhood('edge').remove(); + } + else { + let parent = null; + if (node.locality.callstack) + parent = this.referenceCallstack(node.locality.callstack)?.id(); + else + parent = this.referenceFile(node.locality.file).id(); + + ele = this.cy.add({ + group: 'nodes', + data: { ...node, parent }, + classes: 'new', + }); + this.addTips(ele); + newNodes = ele.union(newNodes); + } + + // Add a node for the user to ask for more dependencies + const idmore = `${node.id}-more`; + this.cy.$id(idmore).remove(); + if (node.backward_explored === 'partial') { + const elemore = this.cy.add({ + group: 'nodes', + data: { id: idmore, parent: ele.data('parent') }, + classes: 'new more', + }); + newNodes = elemore.union(newNodes); + this.cy.add({ + group: 'edges', + data: { source: idmore, target: node.id }, + classes: 'new', + }); + } + } + + for (const dep of data.deps) { + const src = this.cy.$id(dep.src); + const dst = this.cy.$id(dep.dst); + const isRoot = dst?.data('is_root'); + this.cy.add({ + data: { ...dep, source: dep.src, target: dep.dst, is_root: isRoot }, + group: 'edges', + classes: src?.hasClass('new') || dst?.hasClass('new') ? 'new' : '', + }); + } + + return newNodes; + } + + receiveData(data: any): Cytoscape.NodeSingular { + this.cy.startBatch(); + + for (const id of data.sub) + this.remove(this.cy.$id(id)); + + const newNodes = this.receiveGraph(data.add); + + this.cy.endBatch(); + + this.recomputeLayout(newNodes); + + return this.cy.$id(data.root); + } + + get layout(): string { + return this._layout; + } + + set layout(layout: string) { + let extendedOptions = {}; + if (layout in layouts) + extendedOptions = (layouts as { [key: string]: object })[layout]; + this._layout = layout; + this.layoutOptions = { + name: layout, + fit: true, + animate: true, + randomize: false, /* Keep previous positions if layout supports it */ + ...extendedOptions, + }; + + this.recomputeLayout(); + } + + recomputeLayout(newNodes?: Cytoscape.Collection): void { + if (this.layoutOptions && this.cy.container() && + (newNodes === undefined || !newNodes.empty())) { + this.cy.layout({ + animationEasing: 'ease-in-out-quad', + /* Do not move new nodes */ + animateFilter: (node: Cytoscape.Singular) => newNodes === undefined || + !newNodes.contains(node), + stop: () => { + this.cy.$('.new').addClass('old').removeClass('new'); + }, + ...this.layoutOptions, + } as unknown as Cytoscape.LayoutOptions).run(); + } + } + + async exec<In, Out>( + request: Server.ExecRequest<In, Out>, + param: In, + ) { + try { + if (Server.isRunning()) { + await this.setMode(); + const data = await Server.send(request, param); + if (data) + return this.receiveData(data); + } + } + catch (err) { + console.error(err); + } + + return null; + } + + async refresh() { + try { + if (Server.isRunning()) { + const data = await Server.send(API.graph, {}); + this.cy.startBatch(); + const newNodes = this.receiveGraph(data); + this.cy.endBatch(); + this.recomputeLayout(newNodes); + } + } + catch (err) { + console.error(err); + } + } + + static async setWindow(window: any): Promise<void> { + if (Server.isRunning()) + await Server.send(API.window, window); + } + + async setMode(): Promise<void> { + switch (this.mode) { + case 'explore': + await Dive.setWindow({ + perception: { backward: 3, forward: 1 }, + horizon: { backward: 3, forward: 1 }, + }); + break; + case 'overview': + await Dive.setWindow({ + perception: { backward: 4, forward: 1 }, + horizon: { backward: null, forward: null }, + }); + break; + default: /* This is useless and impossible if the program is correctly + typed, but the linter wants it */ + } + } + + clear(): void { + this.cy.elements().remove(); + this.exec(API.clear, null); + } + + async add(marker: string) { + const node = await this.exec(API.add, marker); + if (node) + this.updateNodeSelection(node); + } + + async explore(node: Cytoscape.NodeSingular) { + const id = parseInt(node.id(), 10); + if (id) + await this.exec(API.explore, id); + } + + show(node: Cytoscape.NodeSingular): void { + const id = parseInt(node.id(), 10); + if (id) + this.exec(API.show, id); + } + + hide(node: Cytoscape.NodeSingular): void { + const id = parseInt(node.id(), 10); + if (id) + this.exec(API.hide, id); + } + + async clickNode(node: Cytoscape.NodeSingular) { + this.updateNodeSelection(node); + await this.explore(node); + + const writes = node.data()?.writes; + if (writes && writes.length) + this.onSelect?.(writes); + + /* Cytoscape automatically selects the node clicked, and unselects all other + nodes and edges. As we want some incoming edges to remain selected, we + make the node unselectable, preventing cytoscape to select it. */ + node.unselectify(); + } + + selectLocation(location: States.Location | undefined, doExplore: boolean) { + if (!location) { + // Reset whole graph if no location is selected. + this.clear(); + } else if (location !== this.selectedLocation) { + this.selectedLocation = location; + const selectNode = this.cy.$('node:selected'); + const writes = selectNode?.data()?.writes; + if (doExplore && location.marker && !_.some(writes, location)) { + this.add(location.marker); + } + else { + this.updateNodeSelection(selectNode); + } + } + } + + updateNodeSelection(node: Cytoscape.NodeSingular): void { + const hasOrigin = (ele: Cytoscape.NodeSingular) => ( + _.some(ele.data().origins, this.selectedLocation) + ); + this.cy.$(':selected').forEach(unselect); + select(node); + const edges = node.incomers('edge'); + edges.unselect(); + const relevantEdges = edges.filter(hasOrigin); + if (relevantEdges.empty()) + edges.select(); + else + relevantEdges.select(); + } +} + +const GraphView = () => { + + // Hooks + const [dive, setDive] = useState(() => new Dive()); + const [selection, updateSelection] = States.useSelection(); + const [lock, flipLock] = + Dome.useBoolSettings('dive.lock'); + const [selectionMode, setSelectionMode] = + Dome.useStringSettings('dive.selectionMode', 'follow'); + + function setCy(cy: Cytoscape.Core) { + if (cy !== dive.cy) + setDive(new Dive(cy)); + } + + useEffect(() => { + setDive(new Dive(dive.cy)); + }, [Dive]); // eslint-disable-line react-hooks/exhaustive-deps + + // Follow mode + useEffect(() => { + dive.mode = selectionMode === 'follow' ? 'explore' : 'overview'; + }, [dive, selectionMode]); + + useEffect(() => { + /* When clicking on a node, select its writes locations as a multiple + selection. If these locations were already selected, select the next + location in the multiple selection. */ + dive.onSelect = (locations) => { + if (_.isEqual(locations, selection?.multiple?.allSelections)) + updateSelection('MULTIPLE_CYCLE'); + else + updateSelection({ locations, index: 0 }); + }; + + // Updates the graph according to the selected marker. + dive.selectLocation(selection?.current, !lock); + }, [dive, lock, selection, updateSelection]); + + // Layout selection + const selectLayout = (layout?: string) => { + if (layout) { + dive.layout = layout; + } + }; + const layoutsNames = ['cose-bilkent', 'dagre', 'cola', 'klay']; + const layoutItem = (id: string) => ( + { id, label: id, checked: (id === dive.layout) } + ); + const layoutMenu = () => { + Dome.popupMenu(layoutsNames.map(layoutItem), selectLayout); + }; + + // Selection mode + const selectMode = (id?: string) => id && setSelectionMode(id); + const modes = [ + { id: 'follow', label: 'Follow selection' }, + { id: 'add', label: 'Add selection to the graph' }, + ]; + const checkMode = + (item: { id: string; label: string }) => ( + { checked: item.id === selectionMode, ...item } + ); + const modeMenu = () => { + Dome.popupMenu(modes.map(checkMode), selectMode); + }; + + // Component + return ( + <> + <TitleBar> + <IconButton + icon="LOCK" + onClick={flipLock} + kind={lock ? 'negative' : 'positive'} + title={lock ? + 'Unlock the graph: update the graph with the selection' : + 'Lock the graph: do not update the graph with the selection'} + /> + <IconButton + icon="SETTINGS" + onClick={modeMenu} + title="Choose the selection mode" + /> + <IconButton + icon="DISPLAY" + onClick={layoutMenu} + title="Choose the graph layout" + /> + <Space /> + <IconButton + icon="TRASH" + onClick={() => dive.clear()} + title="Clear the graph" + /> + </TitleBar> + <CytoscapeComponent + stylesheet={style} + cy={setCy} + style={{ width: '100%', height: '100%' }} + /> + </> + ); + +}; + +// -------------------------------------------------------------------------- +// --- Export Component +// -------------------------------------------------------------------------- + +export default () => ( + <Component + id="dive.graph" + label="Data-flow graph" + title={'Data dependency graph according to an Eva analysis.\nNodes color ' + + 'represents the precision of the values inferred by Eva.'} + > + <GraphView /> + </Component> +); + +// -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/dive/cytoscape_libs.js b/ivette/src/frama-c/dive/cytoscape_libs.js new file mode 100644 index 0000000000000000000000000000000000000000..0bd1aced540e77450152e649c1475d3326898915 --- /dev/null +++ b/ivette/src/frama-c/dive/cytoscape_libs.js @@ -0,0 +1,19 @@ +/* Currently Cytoscape.use emits an error when a library is already loaded. +This prevents Hot Module Reloading for modules where Cytescope.use is used. +Grouping all Cytoscape plugins registrations here solves the problem. */ + +import Cytoscape from 'cytoscape' ; + +import CytoscapeMenu from 'cytoscape-cxtmenu'; +import CytoscapePopper from 'cytoscape-popper'; +import CytoscapeLayoutDagre from 'cytoscape-dagre'; +import CytoscapeLayoutCola from 'cytoscape-cola'; +import CytoscapeLayoutCoseBilkent from 'cytoscape-cose-bilkent'; +import CytoscapeLayoutKlay from 'cytoscape-klay'; + +Cytoscape.use(CytoscapePopper); +Cytoscape.use(CytoscapeMenu); +Cytoscape.use(CytoscapeLayoutDagre); +Cytoscape.use(CytoscapeLayoutCola); +Cytoscape.use(CytoscapeLayoutCoseBilkent); +Cytoscape.use(CytoscapeLayoutKlay); diff --git a/ivette/src/frama-c/dive/layouts.json b/ivette/src/frama-c/dive/layouts.json new file mode 100644 index 0000000000000000000000000000000000000000..d8dfc4cf585979c43c8a627b86e31229f03a8d06 --- /dev/null +++ b/ivette/src/frama-c/dive/layouts.json @@ -0,0 +1,6 @@ +{ + "klay": {}, + "cose-bilkent": {}, + "dagre": {}, + "cola": {} +} diff --git a/ivette/src/frama-c/dive/react-cytoscapejs.d.ts b/ivette/src/frama-c/dive/react-cytoscapejs.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e8a271f130a09d3780fe99ff3adf309d6968892b --- /dev/null +++ b/ivette/src/frama-c/dive/react-cytoscapejs.d.ts @@ -0,0 +1 @@ +declare module 'react-cytoscapejs'; diff --git a/ivette/src/frama-c/dive/style.json b/ivette/src/frama-c/dive/style.json new file mode 100644 index 0000000000000000000000000000000000000000..38374dab223ddb7e212d2de4fc0751cd3a7835b1 --- /dev/null +++ b/ivette/src/frama-c/dive/style.json @@ -0,0 +1,173 @@ +[ + { + "selector": "node", + "style": { + "shape": "rectangle", + "background-color": "#666", + "label": "data(id)", + "color": "white", + "text-outline-width": 2, + "text-outline-color": "#666", + "text-valign" : "center", + "width": "label", + "padding" : "6px", + "border-width": 1, + "text-wrap" : "wrap" + } + }, + { + "selector": "node:selected", + "style": { + "overlay-color": "#8bf", + "overlay-padding": "10px", + "overlay-opacity": 0.4 + } + }, + { + "selector": "node[label]", + "style": { + "label": "data(label)" + + } + }, + { + "selector": "node[?is_root]", + "style": { + "border-width": "2px" + } + }, + + { + "selector": "node.more", + "style": { + "background-opacity": 0, + "label": "...", + "padding" : "0", + "border-width": 0 + } + }, + { + "selector": "edge", + "style": { + "width": 2, + "line-color": "#888", + "curve-style": "bezier", + "target-arrow-shape": "vee", + "target-arrow-color": "#888", + "arrow-scale": 2.0 + } + }, + { + "selector": "edge:selected", + "style": { + "color": "#48f", + "overlay-color": "#8bf", + "overlay-padding": "10px", + "overlay-opacity": 0.4 + } + }, + { + "selector": "edge[?is_root]", + "style": { + "width": 4 + } + }, + { + "selector": ".function", + "style": { + "shape": "rectangle", + "background-color": "#bbb", + "background-opacity" : 0.4, + "text-valign" : "top", + "text-halign" : "center", + "padding" : "10px", + "border-width": 1 + } + }, + { + "selector": ".file", + "style": { + "shape": "rectangle", + "background-color": "#ddd", + "background-opacity" : 0.4, + "text-valign" : "top", + "text-halign" : "center", + "padding" : "10px", + "border-width": 1 + } + }, + { + "selector": "node[backward_explored='no']", + "style": { + "opacity" : 0.3 + } + }, + { + "selector": ".new", + "style": { + "opacity": 0 + } + }, + { + "selector": ".old", + "style": { + "transition-property": "opacity", + "transition-duration": "0.5s" + } + }, + { + "selector": "node[stops]", + "style": { + "border-color": "#8e6", + "background-gradient-stop-colors": "#5d3 #5d3 #9f7 #9f7", + "background-width-relative-to": "include-padding", + "background-fill": "linear-gradient", + "background-gradient-direction": "to-top", + "background-gradient-stop-positions": "data(stops)" + } + }, + { + "selector": "node[range='singleton']", + "style": { + "border-color": "#8af", + "background-color": "#acf" + } + }, + { + "selector": "node[range='wide']", + "style": { + "border-color": "#f88", + "background-color": "#e44" + } + }, + { + "selector": "node[kind='alarm']", + "style": { + "shape": "octagon", + "background-color": "#f00", + "border-width": 0 + } + }, + { + "selector": "node[kind='scattered']", + "style": { + "shape": "rhomboid" + } + }, + { + "selector": "node[kind='composite']", + "style": { + "ghost": "yes", + "ghost-offset-x": "6px", + "ghost-offset-y": "6px", + "ghost-opacity": "0.7" + } + }, + { + "selector": "edge[kind='callee']", + "style": { + "line-color": "#8f8", + "target-arrow-color": "#8f8" + } + } +] diff --git a/ivette/src/frama-c/dive/tippy.css b/ivette/src/frama-c/dive/tippy.css new file mode 100644 index 0000000000000000000000000000000000000000..99b199075fc92cf207205701d932c90f65145814 --- /dev/null +++ b/ivette/src/frama-c/dive/tippy.css @@ -0,0 +1,4 @@ +.tippy-tooltip +{ + box-shadow: 1px 1px 4px black !important; +} diff --git a/ivette/src/frama-c/server.ts b/ivette/src/frama-c/server.ts index 33591e19bacfb0e8656c34cc45cede650c7d1525..f4cdf1d6396c7ee80c810382135a514ca3e92efa 100644 --- a/ivette/src/frama-c/server.ts +++ b/ivette/src/frama-c/server.ts @@ -463,7 +463,7 @@ async function _launch() { const logger = (text: string | string[]) => { buffer.append(text); if (text.indexOf('\n') >= 0) { - buffer.scroll(undefined, undefined); + buffer.scroll(); } }; process?.stdout?.on('data', logger); diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts index dc04e0662472f25acd4f6dd08d75945af67c8c06..4f3a38d0453b0a8b6793478ca5d421d2d02d5c1a 100644 --- a/ivette/src/frama-c/states.ts +++ b/ivette/src/frama-c/states.ts @@ -501,10 +501,12 @@ export interface NthSelect { * - [[NthSelect]]. * - `MULTIPLE_PREV` jumps to previous location of the multiple selections. * - `MULTIPLE_NEXT` jumps to next location of the multiple selections. + * - `MULTIPLE_CYCLE` cycles between the multiple selections. + * - `MULTIPLE_CLEAR` clears the multiple selection. */ export type MultipleSelectActions = MultipleSelect | NthSelect - | 'MULTIPLE_PREV' | 'MULTIPLE_NEXT' | 'MULTIPLE_CLEAR'; + | 'MULTIPLE_PREV' | 'MULTIPLE_NEXT' | 'MULTIPLE_CYCLE' | 'MULTIPLE_CLEAR'; export interface Selection { /** Current selection. May be one in [[history]] or [[multiple]]. */ @@ -595,15 +597,21 @@ function fromHistory(s: Selection, action: HistorySelectActions): Selection { */ function fromMultipleSelections( s: Selection, - action: 'MULTIPLE_PREV' | 'MULTIPLE_NEXT' | 'MULTIPLE_CLEAR', + action: 'MULTIPLE_PREV' | 'MULTIPLE_NEXT' | 'MULTIPLE_CYCLE' + | 'MULTIPLE_CLEAR', ): Selection { switch (action) { case 'MULTIPLE_PREV': - case 'MULTIPLE_NEXT': { + case 'MULTIPLE_NEXT': + case 'MULTIPLE_CYCLE': { + const idx = + action === 'MULTIPLE_PREV' ? + s.multiple.index - 1 : + s.multiple.index + 1; const index = - action === 'MULTIPLE_NEXT' ? - s.multiple.index + 1 : - s.multiple.index - 1; + action === 'MULTIPLE_CYCLE' && idx >= s.multiple.allSelections.length ? + 0 : + idx; if (0 <= index && index < s.multiple.allSelections.length) { const multiple = { ...s.multiple, index }; return selectLocation( @@ -660,6 +668,7 @@ function reducer(s: Selection, action: SelectionActions): Selection { return fromHistory(s, action); case 'MULTIPLE_PREV': case 'MULTIPLE_NEXT': + case 'MULTIPLE_CYCLE': case 'MULTIPLE_CLEAR': return fromMultipleSelections(s, action); default: diff --git a/ivette/src/frama-c/utils.ts b/ivette/src/frama-c/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b1a81919b4873eb92ce67f0d9b8490b60059970 --- /dev/null +++ b/ivette/src/frama-c/utils.ts @@ -0,0 +1,52 @@ +// -------------------------------------------------------------------------- +// --- Frama-C Utilities +// -------------------------------------------------------------------------- + +/** + * @packageDocumentation + * @module frama-c/utils +*/ + +import * as Dome from 'dome'; +import * as DomeBuffers from 'dome/text/buffers'; +import * as KernelData from 'api/kernel/data'; + +const D = new Dome.Debug('Utils'); + +// -------------------------------------------------------------------------- +// --- Print Utilities +// -------------------------------------------------------------------------- + +/** + * Print text containing tags into buffer. + * @param buffer Rich text buffer to print into. + * @param contents Actual text containing tags. + * @param options Specify particular marker options. + */ +export function printTextWithTags( + buffer: DomeBuffers.RichTextBuffer, + contents: KernelData.text, + options?: DomeBuffers.MarkerProps, +) { + if (Array.isArray(contents)) { + let marker = false; + const tag = contents.shift(); + if (tag) { + if (Array.isArray(tag)) { + contents.unshift(tag); + } else { + buffer.openTextMarker({ id: tag, ...options ?? {} }); + marker = true; + } + } + contents.forEach((txt) => printTextWithTags(buffer, txt, options)); + if (marker) { + marker = false; + buffer.closeTextMarker(); + } + } else if (typeof contents === 'string') { + buffer.append(contents); + } else { + D.error('Unexpected text', contents); + } +} diff --git a/ivette/src/renderer/ASTinfo.tsx b/ivette/src/renderer/ASTinfo.tsx index 8fbe76d591654ebd57f77fb6be7d1b0f9d3039a0..9e624f8182b12b1915b699b3955a06c225a71a0f 100644 --- a/ivette/src/renderer/ASTinfo.tsx +++ b/ivette/src/renderer/ASTinfo.tsx @@ -4,6 +4,7 @@ import React from 'react'; import * as States from 'frama-c/states'; +import * as Utils from 'frama-c/utils'; import { Vfill } from 'dome/layout/boxes'; import { RichTextBuffer } from 'dome/text/buffers'; @@ -25,7 +26,9 @@ const ASTinfo = () => { React.useEffect(() => { buffer.clear(); - buffer.printTextWithTags(data, { css: 'color: blue' }); + if (data) { + Utils.printTextWithTags(buffer, data, { css: 'color: blue' }); + } }, [buffer, data]); // Callbacks diff --git a/ivette/src/renderer/ASTview.tsx b/ivette/src/renderer/ASTview.tsx index e59de0ab3db5092ae3c0fa574da4d27da02886ea..b97767d733af474ca2d590b5ac717deea952ec7d 100644 --- a/ivette/src/renderer/ASTview.tsx +++ b/ivette/src/renderer/ASTview.tsx @@ -6,17 +6,17 @@ import React from 'react'; import _ from 'lodash'; import * as Server from 'frama-c/server'; import * as States from 'frama-c/states'; +import * as Utils from 'frama-c/utils'; import * as Dome from 'dome'; +import * as Json from 'dome/data/json'; import * as Settings from 'dome/data/settings'; -import { key } from 'dome/data/json'; import { RichTextBuffer } from 'dome/text/buffers'; import { Text } from 'dome/text/editors'; import { IconButton } from 'dome/controls/buttons'; import { Component, TitleBar } from 'frama-c/LabViews'; - import { printFunction, markerInfo } from 'api/kernel/ast'; -import { getCallers } from 'api/plugins/eva'; +import { getCallers, getDeadCode } from 'api/plugins/eva/general'; import 'codemirror/mode/clike/clike'; import 'codemirror/theme/ambiance.css'; @@ -50,15 +50,13 @@ async function loadAST( (async () => { try { const data = await Server.send(printFunction, theFunction); - buffer.operation(() => { - buffer.clear(); - if (!data) { - buffer.log('// No code for function', theFunction); - } - buffer.printTextWithTags(data); - if (theMarker) - buffer.scroll(theMarker, undefined); - }); + buffer.clear(); + if (!data) { + buffer.log('// No code for function', theFunction); + } + Utils.printTextWithTags(buffer, data); + if (theMarker) + buffer.scroll(theMarker); } catch (err) { D.error( `Fail to retrieve the AST of function '${theFunction}' ` + @@ -100,6 +98,8 @@ const ASTview = () => { const theFunction = selection?.current?.function; const theMarker = selection?.current?.marker; + const deadCode = States.useRequest(getDeadCode, theFunction); + // Hook: async loading React.useEffect(() => { if (printed.current !== theFunction) { @@ -112,42 +112,37 @@ const ASTview = () => { const decorator = (marker: string) => { if (multipleSelections?.some((location) => location?.marker === marker)) return 'highlighted-marker'; + if (deadCode?.unreachable?.some((m) => m === marker)) + return 'dead-code'; + if (deadCode?.nonTerminating?.some((m) => m === marker)) + return 'non-terminating'; return undefined; }; buffer.setDecorator(decorator); - }, [buffer, multipleSelections]); + }, [buffer, multipleSelections, deadCode]); // Hook: marker scrolling React.useEffect(() => { - if (theMarker) buffer.scroll(theMarker, undefined); + if (theMarker) buffer.scroll(theMarker); }, [buffer, theMarker]); // Callbacks const zoomIn = () => fontSize < 48 && setFontSize(fontSize + 2); const zoomOut = () => fontSize > 4 && setFontSize(fontSize - 2); - function onTextSelection(id: key<'#markerIndo'>) { + function onTextSelection(id: string) { if (selection.current) { const location = { ...selection.current, marker: id }; updateSelection({ location }); } } - async function onContextMenu(id: key<'#markerInfo'>) { + async function onContextMenu(id: string) { const items = []; - const selectedMarkerInfo = markersInfo.getData(id); - switch (selectedMarkerInfo?.kind) { - case 'function': { - items.push({ - label: `Go to definition of ${selectedMarkerInfo.name}`, - onClick: () => { - const location = { function: selectedMarkerInfo.name }; - updateSelection({ location }); - }, - }); - break; - } - case 'declaration': { + const markerId = (id as Json.key<'#markerInfo'>); + const selectedMarkerInfo = markersInfo.getData(markerId); + if (selectedMarkerInfo?.var === 'function') { + if (selectedMarkerInfo.kind === 'declaration') { if (selectedMarkerInfo?.name) { const locations = await functionCallers(selectedMarkerInfo.name); const locationsByFunction = _.groupBy(locations, (e) => e.function); @@ -165,10 +160,15 @@ const ASTview = () => { }); }); } - break; + } else { + items.push({ + label: `Go to definition of ${selectedMarkerInfo.name}`, + onClick: () => { + const location = { function: selectedMarkerInfo.name }; + updateSelection({ location }); + }, + }); } - default: - break; } if (items.length > 0) Dome.popupMenu(items); diff --git a/ivette/src/renderer/Application.tsx b/ivette/src/renderer/Application.tsx index 2f6bb96b07b6aca5ae8210b237c7c0a6667870a2..a19fe434c0922ac7bfd189890e8f04d2741c5741 100644 --- a/ivette/src/renderer/Application.tsx +++ b/ivette/src/renderer/Application.tsx @@ -13,7 +13,8 @@ import * as Sidebar from 'dome/frame/sidebars'; import './style.css'; import { LabView, View, Group } from 'frama-c/LabViews'; -import { GridItem } from 'dome/layout/grids'; +import Dive from 'frama-c/dive/Dive'; +import { GridHbox, GridItem } from 'dome/layout/grids'; import * as Controller from './Controller'; import ASTview from './ASTview'; @@ -21,6 +22,7 @@ import ASTinfo from './ASTinfo'; import Globals from './Globals'; import Properties from './Properties'; import Locations from './Locations'; +import Values from './Values'; // -------------------------------------------------------------------------- // --- Selection Controls @@ -55,8 +57,10 @@ const HistorySelectionControls = () => { // -------------------------------------------------------------------------- export default (() => { - const [sidebar, flipSidebar] = Dome.useBoolSettings('frama-c.sidebar.unfold'); - const [viewbar, flipViewbar] = Dome.useBoolSettings('frama-c.viewbar.unfold'); + const [sidebar, flipSidebar] = + Dome.useBoolSettings('frama-c.sidebar.unfold', true); + const [viewbar, flipViewbar] = + Dome.useBoolSettings('frama-c.viewbar.unfold', true); return ( <Vfill> @@ -86,15 +90,35 @@ export default (() => { customize={viewbar} settings="frama-c.labview" > - <View id="dashboard" label="Dashboard" defaultView> + <View id="console" label="Console" defaultView> <GridItem id="frama-c.console" /> </View> + <View id="values" label="Values"> + <GridHbox> + <GridItem id="frama-c.astview" /> + <GridItem id="frama-c.values" /> + </GridHbox> + <GridItem id="frama-c.properties" /> + </View> + <View id="dive" label="Dive"> + <GridHbox> + <GridItem id="frama-c.astview" /> + <GridItem id="dive.graph" /> + <GridItem id="frama-c.locations" /> + </GridHbox> + <GridHbox> + <GridItem id="frama-c.properties" /> + <GridItem id="frama-c.console" /> + </GridHbox> + </View> <Group id="frama-c" label="Frama-C" title="Frama-C Kernel Components"> <Controller.Console /> <Properties /> <ASTview /> <ASTinfo /> <Locations /> + <Dive /> + <Values /> </Group> </LabView> </Splitter> diff --git a/ivette/src/renderer/Values.tsx b/ivette/src/renderer/Values.tsx new file mode 100644 index 0000000000000000000000000000000000000000..652f48fc1072c0a0ea7186293fdcaec0504e00c5 --- /dev/null +++ b/ivette/src/renderer/Values.tsx @@ -0,0 +1,141 @@ +// -------------------------------------------------------------------------- +// --- Eva Values +// -------------------------------------------------------------------------- + +import React from 'react'; +import * as States from 'frama-c/states'; +import * as Json from 'dome/data/json'; +import * as Eva from 'api/plugins/eva/values'; +import * as Ast from 'api/kernel/ast'; +import * as Compare from 'dome/data/compare'; + +import { Table, Column } from 'dome/table/views'; +import { ArrayModel } from 'dome/table/arrays'; +import { Component } from 'frama-c/LabViews'; +import { Icon } from 'dome/controls/icons'; +import { Label } from 'dome/controls/labels'; + +// -------------------------------------------------------------------------- +// --- Columns +// -------------------------------------------------------------------------- + +const CallstackRenderer = ( + (cs: Eva.callstack) => <Label label={cs.short} title={cs.full} /> +); + +const ColumnCallstack = () => Column({ + id: 'callstack', + label: 'Callstack', + title: 'Context of the evaluation', + align: 'left', + width: 150, + render: CallstackRenderer, +}); + +const AlarmRenderer = ( + (alarm: boolean) => <>{alarm && <Icon id="ATTENTION" />}</> +); + +const ColumnAlarm = (props: { visible: boolean }) => Column({ + id: 'alarm', + label: 'Alarm', + title: 'Did the evaluation emit an alarm?', + align: 'center', + width: 26, + fixed: true, + icon: 'WARNING', + visible: props.visible, + render: AlarmRenderer, +}); + +const byValues: Compare.ByFields<Eva.valuesData> = + { callstack: Compare.defined(Compare.byFields({ full: Compare.string })) }; + +class ValuesModel extends ArrayModel<Json.key<'#values'>, Eva.valuesData> { + constructor() { + super(); + this.setOrderingByFields(byValues); + } +} + +// -------------------------------------------------------------------------- +// --- Values Panel +// -------------------------------------------------------------------------- + +const Values = () => { + + const model = React.useMemo(() => new ValuesModel(), []); + const evaValues = States.useSyncArray(Eva.values).getArray(); + const selectMarker = States.useSelection()[0]?.current?.marker; + const markerInfo = States.useSyncArray(Ast.markerInfo).getArray(); + const [name, setName] = React.useState<string | undefined>(undefined); + + States.useRequest(Eva.getValues, selectMarker); + + React.useEffect(() => { + model.removeAllData(); + if (selectMarker && evaValues) { + const selectMarkerInfo = markerInfo.find((e) => e.key === selectMarker); + if (selectMarkerInfo && selectMarkerInfo.var !== 'function') { + switch (selectMarkerInfo.kind) { + case 'expression': + case 'lvalue': + evaValues.forEach((i) => model.setData(i.key, i)); + setName(selectMarkerInfo.descr); + break; + case 'declaration': + evaValues.forEach((i) => model.setData(i.key, i)); + setName(selectMarkerInfo.name); + break; + default: + setName(undefined); + } + } + } else { + setName(undefined); + } + model.reload(); + }, [evaValues, selectMarker, markerInfo, model]); + + // Component + return ( + <> + <Table model={model}> + <ColumnCallstack /> + <Column + id="value_before" + visible={!!name} + label={name && `${name} (before)`} + title="Values inferred by Eva just before the selected point" + disableSort + width={300} + /> + <ColumnAlarm visible={!!name} /> + <Column + id="value_after" + visible={!!name} + label={name && `${name} (after)`} + title="Values inferred by Eva just after the selected point" + disableSort + width={300} + /> + </Table> + </> + ); +}; + +// -------------------------------------------------------------------------- +// --- Export Component +// -------------------------------------------------------------------------- + +export default () => ( + <Component + id="frama-c.values" + label="Eva Values" + title="Values inferred by the Eva analysis" + > + <Values /> + </Component> +); + +// -------------------------------------------------------------------------- diff --git a/ivette/src/renderer/style.css b/ivette/src/renderer/style.css index 3d19a26bea7129623fdb9f80e4a788abf142f352..09df391d8df39183598a6fa9d2636fc49ce2372b 100644 --- a/ivette/src/renderer/style.css +++ b/ivette/src/renderer/style.css @@ -27,4 +27,13 @@ background-color: #FFFF66; } +.dead-code { + background-color: #BBB; + border-bottom: solid 0.1em #BBB; +} + +.non-terminating { + border-bottom: solid 0.2em #BBB; +} + /* -------------------------------------------------------------------------- */ diff --git a/ivette/tests/eva-2.i b/ivette/tests/eva-2.i new file mode 100644 index 0000000000000000000000000000000000000000..8b0998c24954c340558942ef8f4bf7e1c66205da --- /dev/null +++ b/ivette/tests/eva-2.i @@ -0,0 +1,20 @@ +volatile int nondet; + +void fun () { + int x = nondet; + x = -x; +} + +void yet_another () { + int x = nondet; + fun (); + int y = 100 / x; +} + +void main () { + fun (); + /*@ assert boh: 1 == 1; */ + yet_another (); + int i = 0; + /*@ assert user: i == 0; */ +} diff --git a/ivette/tsconfig.json b/ivette/tsconfig.json index 662e4cd16a733b51a4544a219c7f8dd005d7b761..0cbb1f1af838676ae822f3048a4df3ca1086e745 100644 --- a/ivette/tsconfig.json +++ b/ivette/tsconfig.json @@ -48,7 +48,8 @@ "dome": [ "src/dome/src/renderer/dome.js" ], "dome/system": [ "src/dome/src/misc/system.js" ], "dome/misc/*": [ "src/dome/src/misc/*"], - "dome/*": [ "src/dome/src/renderer/*" ] + "dome/*": [ "src/dome/src/renderer/*" ], + "codemirror/lib/codemirror": ["node_modules/@types/codemirror/index.d.ts"], }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ @@ -100,6 +101,7 @@ "doc/pages", "src/frama-c", "api", "src/dome/src/renderer", + "src/dome/src/misc", ] } } diff --git a/ivette/yarn.lock b/ivette/yarn.lock index 326cb44b75057094cf39c1457749096978a8f9f6..1402c44ae3bc9d7200f381f5fd57207ac495b66b 100644 --- a/ivette/yarn.lock +++ b/ivette/yarn.lock @@ -956,6 +956,11 @@ global-agent "^2.0.2" global-tunnel-ng "^2.7.1" +"@fortawesome/fontawesome-free@^5.13.1": + version "5.13.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.1.tgz#c53b4066edae16cd1fd669f687baf031b45fb9d6" + integrity sha512-D819f34FLHeBN/4xvw0HR0u7U2G7RqjPSggXqf7LktsxWQ48VAfGwvMrhcVuaZV2fF069c/619RdgCCms0DHhw== + "@hot-loader/react-dom@^16.13.0": version "16.13.0" resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.13.0.tgz#de245b42358110baf80aaf47a0592153d4047997" @@ -988,10 +993,10 @@ resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== -"@types/codemirror@^0.0.89": - version "0.0.89" - resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.89.tgz#c3627a8a85a2b3a61110f05ab1949f92451e8662" - integrity sha512-kLw8yUzz1dTAHQh+WYA/2Ka7YZUf5h3oHzgwP48xhbfmqqxrfp9lWuP9Ro26SUDHTthVl5afAa+DabsL1KD5oQ== +"@types/codemirror@^0.0.97": + version "0.0.97" + resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.97.tgz#6f2d8266b7f1b34aacfe8c77221fafe324c3d081" + integrity sha512-n5d7o9nWhC49DjfhsxANP7naWSeTzrjXASkUDQh7626sM4zK9XP2EVcHp1IcCf/IPV6c7ORzDUDF3Bkt231VKg== dependencies: "@types/tern" "*" @@ -1000,6 +1005,11 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/cytoscape@^3.14.5": + version "3.14.5" + resolved "https://registry.yarnpkg.com/@types/cytoscape/-/cytoscape-3.14.5.tgz#a8ab5d82250be9bce207e269f3b6dab4666c4a2c" + integrity sha512-FhmhUAabEQZ8wTKkvuwPlF3nxvwUrTwb1Oie5M7v9wdcHQu5vSDXh/+4eWRBLjcONpFXGhH44L9oXNqyI2R4yQ== + "@types/debug@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" @@ -2722,6 +2732,13 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cose-base@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a" + integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== + dependencies: + layout-base "^1.0.0" + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -2958,6 +2975,97 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +cytoscape-cola@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/cytoscape-cola/-/cytoscape-cola-2.3.1.tgz#e281737b6a973cec53e2423129d14cf189490818" + integrity sha512-CJ+AbNxqsEPEr+bsUfMcgg3T2E9NkwWVx1u7a6wixdg3uD6aDQI5vX5I0+A19QIrpqNWrm5O8w3oXf/ZcJwwmA== + dependencies: + webcola "^3.4.0" + +cytoscape-cose-bilkent@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz#762fa121df9930ffeb51a495d87917c570ac209b" + integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== + dependencies: + cose-base "^1.0.0" + +cytoscape-cxtmenu@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/cytoscape-cxtmenu/-/cytoscape-cxtmenu-3.1.2.tgz#a183629fc53b58cdf82db601c2eed9eca41eefae" + integrity sha512-ikPWyNRZ6IfIykXU9J6tXkwu2Gk3nYK5yNmNJI/wtLFCuELdcCqpEXobgGLDT7yEGO+jI1upxmb4Lex/DPZlgA== + +cytoscape-dagre@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/cytoscape-dagre/-/cytoscape-dagre-2.2.2.tgz#5f32a85c0ba835f167efee531df9e89ac58ff411" + integrity sha512-zsg36qNwua/L2stJSWkcbSDcvW3E6VZf6KRe6aLnQJxuXuz89tMqI5EVYVKEcNBgzTEzFMFv0PE3T0nD4m6VDw== + dependencies: + dagre "^0.8.2" + +cytoscape-klay@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/cytoscape-klay/-/cytoscape-klay-3.1.3.tgz#d12faa6a39a709c4ff66d4020e1341ca26d51f50" + integrity sha512-Bq6WQRum5nojcOH1QGGihPoI5QRNJwxBlLAjpTYjNallZg3oH9/4o0a8jdUG9LIqSc1IDKFSHP2qylm7RURxxw== + dependencies: + klayjs "^0.4.1" + +cytoscape-popper@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/cytoscape-popper/-/cytoscape-popper-1.0.7.tgz#8154ff507d0cc1a17952f00643e71fc1f8ea9fae" + integrity sha512-b/vfoBL2u9GU+J7eceGT9cZ4hctn/ZzV7PXq4w6fk+45qQuOHtlhpP3WxGKpBXNqENhk7Dk0BwlIYROjYQmidQ== + dependencies: + popper.js "^1.0.0" + +cytoscape@^3.15.1, cytoscape@^3.2.19: + version "3.15.1" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.15.1.tgz#bb5724306c61dfb1500d20ce11cd90ef768fc32b" + integrity sha512-xU/0KYoCErDXseKiPYrJJRSwwxbaDSo2rNpKCd7uNPw7ZDrMahCo+fE0agVvw0ce+6gAg5bDnC1pT8AgmaqqtA== + dependencies: + heap "^0.2.6" + lodash.debounce "^4.0.8" + +d3-dispatch@1, d3-dispatch@^1.0.3: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58" + integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA== + +d3-drag@^1.0.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70" + integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w== + dependencies: + d3-dispatch "1" + d3-selection "1" + +d3-path@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-selection@1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.1.tgz#98eedbbe085fbda5bafa2f9e3f3a2f4d7d622a98" + integrity sha512-BTIbRjv/m5rcVTfBs4AMBLKs4x8XaaLkwm28KWu9S2vKNqXkXt2AH2Qf0sdPZHjFxcWg/YL53zcqAz+3g4/7PA== + +d3-shape@^1.3.5: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +d3-timer@^1.0.5: + version "1.0.10" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5" + integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw== + +dagre@^0.8.2: + version "0.8.5" + resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee" + integrity sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw== + dependencies: + graphlib "^2.1.8" + lodash "^4.17.15" + damerau-levenshtein@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" @@ -3337,6 +3445,15 @@ electron-devtools-installer@^2.2.4: rimraf "^2.5.2" semver "^5.3.0" +electron-devtools-installer@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-3.1.1.tgz#7b56c8c86475c5e4e10de6917d150c53c9ceb55e" + integrity sha512-g2D4J6APbpsiIcnLkFMyKZ6bOpEJ0Ltcc2m66F7oKUymyGAt628OWeU9nRZoh1cNmUs/a6Cls2UfOmsZtE496Q== + dependencies: + rimraf "^3.0.2" + semver "^7.2.1" + unzip-crx-3 "^0.2.0" + electron-publish@22.4.1: version "22.4.1" resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.4.1.tgz#a7fcf166786f7d5957f19a70ee8389f219769ba5" @@ -4454,6 +4571,13 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== +graphlib@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" + integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== + dependencies: + lodash "^4.17.15" + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -4562,6 +4686,11 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +heap@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" + integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= + highlight.js@^10.0.0: version "10.1.1" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.1.1.tgz#691a2148a8d922bf12e52a294566a0d993b94c57" @@ -4813,6 +4942,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + immutable@^4.0.0-rc.12: version "4.0.0-rc.12" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217" @@ -5393,6 +5527,16 @@ jsx-ast-utils@^2.4.1: array-includes "^3.1.1" object.assign "^4.1.0" +jszip@^3.1.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6" + integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -5436,6 +5580,11 @@ klaw@^3.0.0: dependencies: graceful-fs "^4.1.9" +klayjs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/klayjs/-/klayjs-0.4.1.tgz#5bf9fadc7a3e44b94082bba501e7d803076dcfc2" + integrity sha1-W/n63Ho+RLlAgrulAefYAwdtz8I= + language-subtag-registry@~0.3.2: version "0.3.20" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.20.tgz#a00a37121894f224f763268e431c55556b0c0755" @@ -5455,6 +5604,11 @@ latest-version@^5.0.0: dependencies: package-json "^6.3.0" +layout-base@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-1.0.2.tgz#1291e296883c322a9dd4c5dd82063721b53e26e2" + integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== + lazy-val@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65" @@ -5487,6 +5641,13 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + linkify-it@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" @@ -5559,6 +5720,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -6411,7 +6577,7 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pako@~1.0.5: +pako@~1.0.2, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -6615,6 +6781,11 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" +popper.js@^1.0.0, popper.js@^1.16.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" + integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== + portfinder@^1.0.25: version "1.0.25" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" @@ -7161,6 +7332,14 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-cytoscapejs@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/react-cytoscapejs/-/react-cytoscapejs-1.2.1.tgz#ccce46acabf4f0c41dce9070743854f92b0dc050" + integrity sha512-8exVCetpzyGCAKuRjXPWGjFCnb22boZ3SXUPpPB/+wQI8Q8BwkT1URN3A7J1Czvj1qAbShh5QQ514mBUp7i7kw== + dependencies: + cytoscape "^3.2.19" + prop-types "^15.6.2" + react-dom@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" @@ -7576,6 +7755,13 @@ rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: dependencies: glob "^7.1.3" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -7723,7 +7909,7 @@ semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.1: +semver@^7.1.1, semver@^7.2.1: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== @@ -7821,6 +8007,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-immediate-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -8490,6 +8681,13 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tippy.js@5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-5.2.1.tgz#e08d7332c103a15e427124d710d881fca82365d6" + integrity sha512-66UT6JRVn3dXNCORE+0UvUK3JZqV/VhLlU6HTDm3FmrweUUFUxUGvT8tUQ7ycMp+uhuLAwQw6dBabyC+iKf/MA== + dependencies: + popper.js "^1.16.0" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -8761,6 +8959,15 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +unzip-crx-3@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz#d5324147b104a8aed9ae8639c95521f6f7cda292" + integrity sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ== + dependencies: + jszip "^3.1.0" + mkdirp "^0.5.1" + yaku "^0.16.6" + upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -8948,6 +9155,16 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +webcola@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/webcola/-/webcola-3.4.0.tgz#490d26ae98e5b5109478b94a846a62ff6831a99d" + integrity sha512-4BiLXjXw3SJHo3Xd+rF+7fyClT6n7I+AR6TkBqyQ4kTsePSAMDLRCXY1f3B/kXJeP9tYn4G1TblxTO+jAt0gaw== + dependencies: + d3-dispatch "^1.0.3" + d3-drag "^1.0.4" + d3-shape "^1.3.5" + d3-timer "^1.0.5" + webpack-cli@^3.3.11: version "3.3.11" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.11.tgz#3bf21889bf597b5d82c38f215135a411edfdc631" @@ -9209,6 +9426,11 @@ xtend@^4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +yaku@^0.16.6: + version "0.16.7" + resolved "https://registry.yarnpkg.com/yaku/-/yaku-0.16.7.tgz#1d195c78aa9b5bf8479c895b9504fd4f0847984e" + integrity sha1-HRlceKqbW/hHnIlblQT9TwhHmE4= + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" diff --git a/nix/default.nix b/nix/default.nix index b7804254b581310c919738b827b0dd22a9aa0ba7..fdce398cfe5fadaca673a976eb8bb381dec43ffa 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -218,6 +218,7 @@ rec { ]; counter_examples_src = plugins.counter-examples.src; genassigns_src = plugins.genassigns.src; + frama_clang_src = plugins.frama-clang.src; pathcrawler_src = plugins.pathcrawler.src; mthread_src = plugins.mthread.src; caveat_importer_src = plugins.caveat-importer.src; @@ -233,6 +234,8 @@ rec { chmod -R u+w -- "$sourceRoot/src/plugins/counter-examples" cp -r --preserve=mode "$genassigns_src" "$sourceRoot/src/plugins/genassigns" chmod -R u+w -- "$sourceRoot/src/plugins/genassigns" + cp -r --preserve=mode "$frama_clang_src" "$sourceRoot/src/plugins/frama-clang" + chmod -R u+w -- "$sourceRoot/src/plugins/frama-clang" cp -r --preserve=mode "$pathcrawler_src" "$sourceRoot/src/plugins/pathcrawler" chmod -R u+w -- "$sourceRoot/src/plugins/pathcrawler" cp -r --preserve=mode "$mthread_src" "$sourceRoot/src/plugins/mthread" diff --git a/nix/frama-ci.nix b/nix/frama-ci.nix index 067bb0c00611b8250fb16c3f0a34a70a23ed2c86..e093574991eec97d4c896c33edf17c28d543b5cf 100644 --- a/nix/frama-ci.nix +++ b/nix/frama-ci.nix @@ -5,7 +5,7 @@ let src = builtins.fetchGit { "url" = "https://bobot:${password}@git.frama-c.com/frama-c/Frama-CI.git"; "name" = "Frama-CI"; - "rev" = "0dad15724fe9fa0782e8797f996d9a996a68ae65"; + "rev" = "abf07b7c0f53b33b32c8b170580e14480fd3aba6"; "ref" = "master"; }; in diff --git a/ptests/ptests.ml b/ptests/ptests.ml index 36fb036537e47b8837d792debbd2cbf28806d5d0..78469525850ec15315fe997160a282d72e422b8d 100644 --- a/ptests/ptests.ml +++ b/ptests/ptests.ml @@ -1037,7 +1037,7 @@ let basic_command_string = end in if command.timeout = "" then raw_command - else "timeout " ^ command.timeout ^ " " ^ raw_command + else "ulimit -t " ^ command.timeout ^ " && " ^ raw_command (* Searches for executable [s] in the directories contained in the PATH environment variable. Returns [None] if not found, or @@ -1128,7 +1128,7 @@ let command_string command = | "" -> command_string | s -> Printf.sprintf - "%s; if test $? -eq 124; then \ + "%s; if test $? -gt 127; then \ echo 'TIMEOUT (%s); ABORTING EXECUTION' > %s; \ fi" command_string s (Filename.sanitize stderr) diff --git a/share/Makefile.config.in b/share/Makefile.config.in index 653d91fad5e4696a2cb7398993aad983048684d5..0739e3cef9cf47ea569ff43de2d365dc91910a61 100644 --- a/share/Makefile.config.in +++ b/share/Makefile.config.in @@ -85,8 +85,8 @@ OCAMLMAJORNB ?=@OCAMLMAJORNB@ OCAMLMINORNB ?=@OCAMLMINORNB@ OCAMLPATCHNB ?=@OCAMLPATCHNB@ -HAS_OCAML407 ?=@HAS_OCAML407@ -HAS_OCAML408 ?=@HAS_OCAML408@ +HAS_OCAML409 ?=@HAS_OCAML409@ +HAS_OCAML410 ?=@HAS_OCAML410@ PLATFORM ?=@PLATFORM@ OCAMLWIN32 ?=@OCAMLWIN32@ diff --git a/share/analysis-scripts/.gitignore b/share/analysis-scripts/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..bee8a64b79a99590d5303307144172cfe824fbf7 --- /dev/null +++ b/share/analysis-scripts/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/share/analysis-scripts/README.md b/share/analysis-scripts/README.md index 3e95700bb8e313539399c114698008e7adca0f5d..a92f12365c513addf3d1164b633d221e947f15aa 100644 --- a/share/analysis-scripts/README.md +++ b/share/analysis-scripts/README.md @@ -1,206 +1,4 @@ -This directory contains a set of a Makefile and several bash scripts which -can be used to simplify non-trivial analyses with Frama-C and some of its -plugins, in particular Eva. -This Makefile can be included in your own Makefile for the following advantages. +# Analysis scripts -1. It ensures that no unnecessary work is done. If you change the Makefile, - targets that have their command line affected will be rebuilt, but any - target for which the command line doesn't change won't be rebuilt. -2. It provides commonly used default parameters for the analysis. Note that - you can still append new parameters or completely redefine them. -3. It splits between parsing and analysis, storing outputs in separate - repositories: <target>.parse for parsing-related outputs, and - <target>.eva for Eva-related outputs. -4. It produces several additional outputs after parsing and after an Eva - analysis: - * `<target>.parse/parse.log`, or `<target>.eva/eva.log`: - contain the entire output of the parsing/analysis command, - * `warnings.log`: only the warnings emitted by Frama-C/Eva, - * `alarms.csv`: list of emitted alarms in csv form, - * `metrics.log`: various metrics about the analysis, - * `stats.txt`: stats about the analysis, such as user time, - memory consumption, the date of the analysis, coverage of the analysis, - number of warnings and alarms, and the command line arguments. -5. It keeps copies of all previous analyses you have done in timestamped - directories. - - -Getting started -=============== - -There is a ready-to-use Makefile skeleton at the end of this section. If you -want explanations about this Makefile, read this entire section. - -Other usage examples are available in Frama-C's Github open-source-case-studies -repository: https://github.com/Frama-C/open-source-case-studies - -(If you have access to Frama-C's development repositories, you can also use -the examples in `analysis-scripts/examples`.) - -Including analysis-scripts -------------------- - -This folder contains several shell scripts and, most importantly, -the `frama-c.mk` file. This file is intended to be included at the top of your -`GNUmakefile`: - -```` -include $(shell frama-c -print-share-path)/analysis-scripts/frama-c.mk -```` - -The file is named `GNUmakefile` instead of `Makefile` for pragmatic reasons: -in GNU Make, the file `GNUmakefile`, if it exists, takes precedence over a -`Makefile`, which avoid having to rename existing Makefiles and having to -manually specify the Makefile to use when running make (e.g. via `-f`). -The analysis-scripts Makefile relies on GNU-specific features anyway. - -By default, the scripts use the frama-c binaries located in your `$PATH` -environment variable. You may want to specify different binaries, but, if you -want to version your analysis, this path will depend on the computer it is run -on. So, we recommend you use an unversioned file `frama-c-path.mk`. Add this -file to your `.gitignore` and define the `FRAMAC` and `FRAMAC_GUI` -variables there. For instance: - -```` -FRAMAC_DIR=frama-c/bin -FRAMAC=$(FRAMAC_DIR)/frama-c -FRAMAC_GUI=$(FRAMAC_DIR)/frama-c-gui -```` - -And include this file before `frama-c.mk` in your Makefile. As this file -is computer dependent and unversioned, it will not always be present. Prefix -the include command with a minus sign `-` to tell `make` to ignore missing -files: - -```` --include frama-c-path.mk -```` - -Then, to handle both cases when Frama-C is in the path, and when it is not, -use the following conditional definition of `FRAMAC` followed by the -inclusion of `frama-c.mk`: - -``` -FRAMAC ?= frama-c -include $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/frama-c.mk -``` - - -Defining analysis global parameters ------------------------------------ - -Once `frama-c.mk` is included, you may change default values of variables. -Most usual variables you may want to change are `CPPFLAGS`, `FCFLAGS` -and `EVAFLAGS`. For example: - -```` -CPPFLAGS = -D__I586__ -FCFLAGS += -verbose 0 -EVAFLAGS += -plevel 100 -```` - -Some arguments are passed to Frama-C from the environment. This is the -case of the `FRAMA_C_MEMORY_FOOTPRINT` variable. You can set it in your -Makefile with the following line: - -```` -export FRAMA_C_MEMORY_FOOTPRINT = 8 -```` - -The two steps of the analysis ------------------------------ - -Parsing might be long on some analyses. The analysis scripts save the result -of the parsing phase so that it is not redone when modifying only analysis -parameters but not parsing parameters. - -The parsing result is saved in a `<target>.parse` directory while the result -of the analysis is saved in a `<target>.eva` directory. -The second automatically depends on the first. -Thus, each time you require that make build the `.eva` target, -it will build the `.parse` one first. - -```` -all: example.eva -```` - - -Defining analysis sources -------------------------- - -To define the set of sources to analyze, you must define them as dependencies -of your `.parse` target. - -```` -example.parse: file1.c file2.c file3.c ... -```` - -As they are dependencies, parsing will be remade if the sources change. - - -Defining project-specific parameters ------------------------------------- - -You can describe several analyses with the same Makefile. We call these -analyses "projects". Projects are not likely to share the exact same -parameters. Thus, it is useful to define these parameters project wise. -`make` allows this by putting the variable definition after the target. For -instance: - -```` -example.parse: CPPFLAGS += -D__FRAMAC__ -example.eva: FCFLAGS += -main my_main -example.eva: EVAFLAGS += -slevel 500 -```` - - -Full example ------------- - -### `GNUmakefile` - -```` -# optional include, in case frama-c-path.mk does not exist (frama-c in the PATH) --include frama-c-path.mk -# frama-c-config is used to find the analysis scripts and frama-c.mk -include $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/frama-c.mk - -# Global parameters -CPPFLAGS = -D__I586__ -FCFLAGS += -verbose 0 -EVAFLAGS += -plevel 100 - -export FRAMA_C_MEMORY_FOOTPRINT = 8 - -# Default targets -all: example.eva - -# Input files -example.parse: example.c - -# Project-specific parameters -example.parse: CPPFLAGS += -D__FRAMAC__ -example.eva: FCFLAGS += -main my_main -example.eva: EVAFLAGS += -slevel 500 -```` - -### `frama-c-path.mk` - -```` -FRAMAC_DIR=frama-c/bin -FRAMAC=$(FRAMAC_DIR)/frama-c -FRAMAC_GUI=$(FRAMAC_DIR)/frama-c-gui -```` - -### `.gitignore` - -```` -*.parse* -*.eva* -*.crash -command -parse.log -eva.log -stats.txt -frama-c-path.mk -```` +Documentation related to the contents of this directory is available in the +Frama-C User Manual, chapter "Analysis scripts". diff --git a/share/analysis-scripts/frama-c.mk b/share/analysis-scripts/analysis.mk similarity index 89% rename from share/analysis-scripts/frama-c.mk rename to share/analysis-scripts/analysis.mk index b427e63aef0dcf4fe43462cc1ce061c469d05b1a..e732240db26edd846b33d9d6d610b25454044b7b 100644 --- a/share/analysis-scripts/frama-c.mk +++ b/share/analysis-scripts/analysis.mk @@ -20,23 +20,19 @@ # # ########################################################################## -# This file is intended to be included by a classic Makefile when doing -# non-trivial analyses with Frama-C and its Eva plugin. For instance, you -# can start your Makefile with the following line: -# -# include path/to/frama-c.mk +# Makefile for Frama-C/Eva case studies. +# This file is included by epilogue.mk, when using template.mk. +# See the Frama-C User Manual for more details. # # This Makefile uses the following variables. # -# FRAMAC the frama-c binary -# FRAMAC_GUI the frama-c gui binary +# FRAMAC frama-c binary +# FRAMAC_GUI frama-c gui binary # CPPFLAGS preprocessing flags +# MACHDEP machdep # FCFLAGS general flags to use with frama-c # FCGUIFLAGS flags to use with frama-c-gui # EVAFLAGS flags to use with the Eva plugin -# SLEVEL the part of the frama-c command line concerning slevel -# (you can use EVAFLAGS for this, if you don't intend -# to use slevel-tweaker.sh) # EVABUILTINS Eva builtins to be set (via -eva-builtin) # EVAUSESPECS Eva functions to be overridden by specs (-eva-use-spec) # @@ -52,15 +48,14 @@ # With command line arguments: # make FRAMAC=~/bin/frama-c # -# In your Makefile, when you want to change a parameter for all analyses : +# In your Makefile, when you want to change a parameter for all analyses: # FCFLAGS += -verbose 2 # # In your Makefile, for a single target : # target.eva: FCFLAGS += -main my_main # -# In order to define an analysis target named target, you must in addition -# give the list of source files containing the code to be analyzed by adding -# them as dependencies of target.parse, a in +# For each analysis target, you must give the list of sources to be analyzed +# by adding them as prerequisites of target.parse, as in: # # target.parse: file1.c file2.c file3.c... # @@ -71,6 +66,8 @@ ifneq (4.0,$(firstword $(sort $(MAKE_VERSION) 4.0))) endif # Test if on a Mac (and therefore sed has fewer options) +# Also test if /usr/bin/time is available, otherwise use the shell builtin +# (which has less options) UNAME := $(shell uname -s) ifeq ($(UNAME),Darwin) SED_UNBUFFERED:=sed @@ -116,14 +113,12 @@ fc_list = $(subst $(space),$(comma),$(strip $1)) FRAMAC ?= frama-c FRAMAC_SCRIPT = $(FRAMAC)-script FRAMAC_GUI ?= frama-c-gui -SLEVEL ?= EVAFLAGS ?= \ -eva-no-print -eva-no-show-progress -eva-msg-key=-initial-state \ -eva-print-callstacks -eva-warn-key alarm=inactive \ -no-deps-print -no-calldeps-print \ -eva-warn-key garbled-mix \ -memexec-all -calldeps -permissive -from-verbose 0 \ - $(SLEVEL) \ $(if $(EVABUILTINS), -eva-builtin=$(call fc_list,$(EVABUILTINS)),) \ $(if $(EVAUSESPECS), -eva-use-spec $(call fc_list,$(EVAUSESPECS)),) FCFLAGS ?= @@ -146,7 +141,6 @@ clean-backups: # --- Generic rules --- -TIMESTAMP := $(shell date +"%Y-%m-%d_%H-%M-%S") HR_TIMESTAMP := $(shell date +"%H:%M:%S %d/%m/%Y")# Human readable DIR := $(dir $(lastword $(MAKEFILE_LIST))) SHELL := /bin/bash @@ -161,7 +155,7 @@ SHELL := /bin/bash @# %.parse: SOURCES = $(filter-out %/command,$^) -%.parse: PARSE = $(FRAMAC) $(FCFLAGS) -cpp-extra-args="$(CPPFLAGS)" $(SOURCES) +%.parse: PARSE = $(FRAMAC) $(FCFLAGS) $(if $(value MACHDEP),-machdep $(MACHDEP),) -cpp-extra-args="$(CPPFLAGS)" $(SOURCES) %.parse: $$(if $$^,,.IMPOSSIBLE) $$(shell $(DIR)cmd-dep.sh $$@/command $$(PARSE)) @$(call display_command,$(PARSE)) mkdir -p $@ @@ -186,7 +180,6 @@ SHELL := /bin/bash mv $@/{running,command} touch $@ # Update timestamp and prevents remake if nothing changes -%.slevel.eva: SLEVEL = -slevel $(word 2,$(subst ., ,$*)) %.eva: EVA = $(FRAMAC) $(FCFLAGS) -eva $(EVAFLAGS) %.eva: PARSE_RESULT = $(word 1,$(subst ., ,$*)).parse %.eva: $$(PARSE_RESULT) $$(shell $(DIR)cmd-dep.sh $$@/command $$(EVA)) $(if $(BENCHMARK),.FORCE,) @@ -226,7 +219,6 @@ SHELL := /bin/bash fi mv $@/{running,command} touch $@ # Update timestamp and prevents remake if nothing changes - cp -r $@ $*_$(TIMESTAMP).eva %.gui: % $(FRAMAC_GUI) $(FCGUIFLAGS) -load $^/framac.sav & diff --git a/share/analysis-scripts/cmd-dep.sh b/share/analysis-scripts/cmd-dep.sh index 61fdaff4b3400bf2afeac287d5cf36b3d0177acf..7dc15d918d3a566489d08ced974d6aa645662ec0 100755 --- a/share/analysis-scripts/cmd-dep.sh +++ b/share/analysis-scripts/cmd-dep.sh @@ -18,7 +18,7 @@ STRING=$* if [ ! -e $FILE ] || - ! (diff --brief --ignore-space-change $FILE - <<< "$STRING") + ! (diff --brief --ignore-space-change $FILE - >/dev/null <<< "$STRING") then mkdir -p $(dirname "$FILE") echo $STRING > "$FILE" diff --git a/share/analysis-scripts/epilogue.mk b/share/analysis-scripts/epilogue.mk new file mode 100644 index 0000000000000000000000000000000000000000..ca49ed6bc7b9fdf07d6871a42123c6b277941b1f --- /dev/null +++ b/share/analysis-scripts/epilogue.mk @@ -0,0 +1,40 @@ +########################################################################## +# # +# This file is part of Frama-C. # +# # +# Copyright (C) 2007-2020 # +# CEA (Commissariat à l'énergie atomique et aux énergies # +# alternatives) # +# # +# you can redistribute it and/or modify it under the terms of the GNU # +# Lesser General Public License as published by the Free Software # +# Foundation, version 2.1. # +# # +# It is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Lesser General Public License for more details. # +# # +# See the GNU Lesser General Public License version 2.1 # +# for more details (enclosed in the file licenses/LGPLv2.1). # +# # +########################################################################## + +# Makefile template epilogue for analyses with Frama-C/Eva. +# For details and usage information, see the Frama-C User Manual. + +# Some targets provided for convenience +# Note: they all depend on TARGETS having been properly set by the user +eva: $(TARGETS) +parse: $(TARGETS:%.eva=%.parse) +# Opening one GUI for each target is cumbersome; we open only the first target +gui: $(firstword $(TARGETS)).gui + +# Default target +all: eva +ifeq ($(TARGETS),) + @echo "error: TARGETS is empty" +endif + +display-targets: + @echo "$(addprefix .frama-c/,$(TARGETS))" diff --git a/share/analysis-scripts/examples/Makefile b/share/analysis-scripts/examples/Makefile deleted file mode 100644 index 5aca1b8993332028c06307a15a9a5ff74e4eef2e..0000000000000000000000000000000000000000 --- a/share/analysis-scripts/examples/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -TARGETS=example example-multi example-slevel - -.PHONY: all update-submodules clean $(TARGETS) - -all: $(TARGETS) - -update-submodules: - git submodule update --init --recursive --remote - -clean: - @for f in $(TARGETS); \ - do \ - $(MAKE) --no-print-directory --file $$f.mk clean; \ - done - -$(TARGETS): %: %.mk - @$(MAKE) --no-print-directory --file $< - diff --git a/share/analysis-scripts/examples/example-multi.mk b/share/analysis-scripts/examples/example-multi.mk deleted file mode 100644 index fa25003a7127116c5195395fea4e4c1abe1e8880..0000000000000000000000000000000000000000 --- a/share/analysis-scripts/examples/example-multi.mk +++ /dev/null @@ -1,23 +0,0 @@ --include frama-c-path.mk -FRAMAC ?= frama-c --include $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/frama-c.mk - -# Global parameters -CPPFLAGS = -D__I586__ -FCFLAGS += -verbose 0 -EVAFLAGS += -plevel 100 -EVABUILTINS += memset:Frama_C_memset memcpy:Frama_C_memcpy - -export FRAMA_C_MEMORY_FOOTPRINT = 8 - -# Default targets -all: example1.val example2.val - -# Input files -example1.parse example2.parse: example.c - -# Project specific parameters -example1.parse: CPPFLAGS += -D__FRAMAC__ -example1.val: FCFLAGS += -main my_main -example2.val: EVAFLAGS += -slevel 500 -example2.val: FCFLAGS += -main main diff --git a/share/analysis-scripts/examples/example-slevel.mk b/share/analysis-scripts/examples/example-slevel.mk deleted file mode 100644 index d623a913abbe3b396cce5f4e2c7d02e115c96536..0000000000000000000000000000000000000000 --- a/share/analysis-scripts/examples/example-slevel.mk +++ /dev/null @@ -1,37 +0,0 @@ -# This example is the same as example-multi.mk but pay attention to the -# following changes : -# 1. slevel is set inside SLEVEL variable instead of EVAFLAGS to allow -# overriding when testing specific slevels -# 2. A percent (%) is used in example1.% and example2.% so that -# options are used also for instance for example1.5000.val which -# is the same target as example1.val but with 5000 slevel. -# 3. The all rule invoke the script - --include frama-c-path.mk -FRAMAC ?= frama-c --include $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/frama-c.mk - -# Global parameters -CPPFLAGS = -D__I586__ -FCFLAGS += -verbose 0 -EVAFLAGS += -plevel 100 -EVABUILTINS += memset:Frama_C_memset memcpy:Frama_C_memcpy - -export FRAMA_C_MEMORY_FOOTPRINT = 8 - -# Default targets -all: - $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/slevel-tweaker.sh -f example-slevel.mk example1 example2 - -# Clean -clean:: - $(RM) slevel-tweaker.log - -# Input files -example1.parse example2.parse: example.c - -# Project specific parameters -example1.parse: CPPFLAGS += -D__FRAMAC__ -example1.%: FCFLAGS += -main my_main -example2.%: SLEVEL += -slevel 500 -example2.%: FCFLAGS += -main main diff --git a/share/analysis-scripts/examples/example.c b/share/analysis-scripts/examples/example.c deleted file mode 100644 index ed7983016f1d3b02737c6f9a909fbd7a8ce29867..0000000000000000000000000000000000000000 --- a/share/analysis-scripts/examples/example.c +++ /dev/null @@ -1,25 +0,0 @@ -#include <string.h> - -char s[10], t[10]; - -int f() -{ - memset(s, 0, 10); - memcpy(t, s, 10); - return 42; -} - -void main(void) -{ - f(); -} - - -#ifdef __FRAMAC__ - -int my_main(void) -{ - return f(); -} - -#endif diff --git a/share/analysis-scripts/examples/example.mk b/share/analysis-scripts/examples/example.mk deleted file mode 100644 index 4a6130de5b3ea37c40629d1ea16052ffde4ee613..0000000000000000000000000000000000000000 --- a/share/analysis-scripts/examples/example.mk +++ /dev/null @@ -1,26 +0,0 @@ -# frama-c-path.mk contains variables which are specific to each -# user and should not be versioned, such as the path to the -# frama-c binaries (e.g. FRAMAC and FRAMAC_GUI). -# It is an optional include, unnecessary if frama-c is in the PATH --include frama-c-path.mk -# FRAMAC is defined in frama-c-path.mk when it is included, so the -# line below will be safely ignored if this is the case -FRAMAC ?= frama-c -# frama-c.mk should be included at the top of your Makefile, right below -# the inclusion of frama-c-path.mk --include $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/frama-c.mk - -# Define global parameters -CPPFLAGS += -D__I586__ -D__FRAMAC__ -FCFLAGS += -verbose 0 -main my_main -EVAFLAGS += -plevel 611 -EVABUILTINS += memset:Frama_C_memset memcpy:Frama_C_memcpy - -# Export environment variable for Frama-C -export FRAMA_C_MEMORY_FOOTPRINT = 8 - -# Default target -all: example.val - -# List input files -example.parse: example.c diff --git a/share/analysis-scripts/find_fun.py b/share/analysis-scripts/find_fun.py index 62692663c2d69264531afde5d048fb4974d3278b..5cb0b65863adcf8606a34d59d9156200b73a8f39 100755 --- a/share/analysis-scripts/find_fun.py +++ b/share/analysis-scripts/find_fun.py @@ -29,6 +29,7 @@ import sys import os import re import glob +import function_finder MIN_PYTHON = (3, 5) # for glob(recursive) if sys.version_info < MIN_PYTHON: @@ -67,36 +68,16 @@ for d in dirs: files += glob.glob(d + "/**/*.[ich]", recursive=True) print("Looking for '%s' inside %d file(s)..." % (fname, len(files))) -#print("\n".join(files)) - -# To minimize the amount of false positives, we try to match the following: -# - the line must begin with a C identifier (declarations and definitions in C -# rarely start with spaces in the line), or with the function name itself -# (supposing the return type is in the previous line) -# - any number of identifiers are allowed (to allow for 'struct', 'volatile', -# 'extern', etc) -# - asterisks are allowed both before and after identifiers, except for the -# first one (to allow for 'char *', 'struct **ptr', etc) -# - identifiers are allowed after the parentheses, to allow for some macros/ -# modifiers possible_declarators = [] possible_definers = [] -c_identifier = "[a-zA-Z_][a-zA-Z0-9_]*" -c_id_maybe_pointer = c_identifier + "\**" -type_prefix = c_id_maybe_pointer + "(?:\s+\**" + c_id_maybe_pointer + ")*\s+\**" -parentheses_suffix = "\s*\([^)]*\)" -re_fun = re.compile("^(?:" + type_prefix + "\s*)?" + fname + parentheses_suffix - + "\s*(?:" + c_identifier + ")?\s*(;|{)", flags=re.MULTILINE) +re_fun = function_finder.prepare(fname) for f in files: - with open(f, encoding="ascii", errors='ignore') as content_file: - content = content_file.read() - has_decl_or_def = re_fun.search(content) - if has_decl_or_def is not None: - is_decl = has_decl_or_def.group(1) == ";" - if is_decl: + found = function_finder.find(re_fun, f) + if found: + if found == 1: possible_declarators.append(f) - else: + else: possible_definers.append(f) if possible_declarators == [] and possible_definers == []: diff --git a/share/analysis-scripts/function_finder.py b/share/analysis-scripts/function_finder.py new file mode 100644 index 0000000000000000000000000000000000000000..ee43f29adfb7ac6b75544622045a75cbceb5b003 --- /dev/null +++ b/share/analysis-scripts/function_finder.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +########################################################################## +# # +# This file is part of Frama-C. # +# # +# Copyright (C) 2007-2020 # +# CEA (Commissariat à l'énergie atomique et aux énergies # +# alternatives) # +# # +# you can redistribute it and/or modify it under the terms of the GNU # +# Lesser General Public License as published by the Free Software # +# Foundation, version 2.1. # +# # +# It is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Lesser General Public License for more details. # +# # +# See the GNU Lesser General Public License version 2.1 # +# for more details (enclosed in the file licenses/LGPLv2.1). # +# # +########################################################################## + +# Exports find_function_in_file, to be used by other scripts + +import re + +# To minimize the amount of false positives, we try to match the following: +# - the line must begin with a C identifier (declarations and definitions in C +# rarely start with spaces in the line), or with the function name itself +# (supposing the return type is in the previous line) +# - any number of identifiers are allowed (to allow for 'struct', 'volatile', +# 'extern', etc) +# - asterisks are allowed both before and after identifiers, except for the +# first one (to allow for 'char *', 'struct **ptr', etc) +# - identifiers are allowed after the parentheses, to allow for some macros/ +# modifiers + +# Precomputes the regex for 'fname' +def prepare(fname): + c_identifier = "[a-zA-Z_][a-zA-Z0-9_]*" + c_id_maybe_pointer = c_identifier + "\**" + type_prefix = c_id_maybe_pointer + "(?:\s+\**" + c_id_maybe_pointer + ")*\s+\**" + parentheses_suffix = "\s*\([^)]*\)" + re_fun = re.compile("^(?:" + type_prefix + "\s*)?" + fname + parentheses_suffix + + "\s*(?:" + c_identifier + ")?\s*(;|{)", flags=re.MULTILINE) + return re_fun + +# Returns 0 if not found, 1 if declaration, 2 if definition +def find(prepared_re, f): + with open(f, encoding="ascii", errors='ignore') as content_file: + content = content_file.read() + has_decl_or_def = prepared_re.search(content) + if has_decl_or_def is None: + return 0 + else: + is_decl = has_decl_or_def.group(1) == ";" + return 1 if is_decl else 2 diff --git a/share/analysis-scripts/make_template.py b/share/analysis-scripts/make_template.py index bf862b7b51cc03b574ae4b84fb7e50628a1616f6..504043a6ebf62a733e1b9290293b866f0a15463a 100755 --- a/share/analysis-scripts/make_template.py +++ b/share/analysis-scripts/make_template.py @@ -28,38 +28,58 @@ import sys import os import re +import shutil +import shlex +import glob from subprocess import Popen, PIPE from pathlib import Path +import function_finder MIN_PYTHON = (3, 6) # for glob(recursive) and automatic Path conversions if sys.version_info < MIN_PYTHON: sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON) -if len(sys.argv) > 3: - print("usage: %s path-to-frama-c-script [dir]" % sys.argv[0]) - print(" creates a GNUmakefile for running Frama-C on a set of files,") - print(" interactively filling a template.") +if len(sys.argv) > 2: + print(f"usage: {sys.argv[0]} [dir]") + print(" creates a Frama-C makefile in [dir] (default: .frama-c)") sys.exit(1) -if not os.path.isfile(sys.argv[1]): - print("error: path to frama-c-script is not a file: " + sys.argv[1]) - sys.exit(1) +framac_in_path = True +framac = shutil.which("frama-c") +if not framac: + framac_in_path = False + if os.environ.get("FRAMAC"): + framac = os.environ["FRAMAC"] + else: + sys.exit("error: frama-c must be in the PATH, "\ + "or in environment variable FRAMAC") jcdb = Path("compile_commands.json") +dir = Path(sys.argv[1] if len(sys.argv) == 2 else ".frama-c") +fc_stubs_c = dir / "fc_stubs.c" +gnumakefile = dir / "GNUmakefile" + +print(f"Preparing template: {gnumakefile}") + +# relative prefix, due to GNUmakefile possibly being in a sub-directory of PWD +relprefix = os.path.relpath(os.getcwd(), dir) + if "PTESTS_TESTING" in os.environ: print("Running ptests: setting up mock files...") jcdb.touch() + Path(dir).mkdir(parents=True, exist_ok=True) + fc_stubs_c.touch() + gnumakefile.touch() -bindir = Path(os.path.dirname(os.path.abspath(sys.argv[1]))) +bindir = Path(os.path.dirname(os.path.abspath(framac))) frama_c_config = bindir / "frama-c-config" -process = Popen([frama_c_config, "-print-share-path"], stdout=PIPE) +process = Popen([frama_c_config, "-share"], stdout=PIPE) (output, err) = process.communicate() output = output.decode('utf-8') exit_code = process.wait() if exit_code != 0: - print("error running frama-c-config") - sys.exit(1) + sys.exit("error running frama-c-config") sharedir = Path(output) def get_known_machdeps(): @@ -68,41 +88,83 @@ def get_known_machdeps(): output = output.decode('utf-8') exit_code = process.wait() if exit_code != 0: - print("error getting machdeps: " + output) - sys.exit(1) + sys.exit("error getting machdeps: " + output) match = re.match("\[kernel\] supported machines are (.*) \(default is (.*)\).", output, re.DOTALL) if not match: - print("error getting known machdeps: " + output) - sys.exit(1) + sys.exit("error getting known machdeps: " + output) machdeps = match.group(1).split() default_machdep = match.group(2) return (default_machdep, machdeps) -dir = Path(sys.argv[2] if len(sys.argv) == 3 else ".") -gnumakefile = dir / "GNUmakefile" - def check_path_exists(path): if os.path.exists(path): - yn = input("warning: {} already exists. Overwrite? [y/N] ".format(path)) + yn = input(f"warning: {path} already exists. Overwrite? [y/N] ") if yn == "" or not (yn[0] == "Y" or yn[0] == "y"): print("Exiting without overwriting.") sys.exit(0) + pathdir = os.path.dirname(path) + if not os.path.exists(pathdir): + yn = input(f"warning: directory '{pathdir}' does not exit. Create it? [y/N] ") + if yn == "" or not (yn[0] == "Y" or yn[0] == "y"): + print("Exiting without creating.") + sys.exit(0) + Path(pathdir).mkdir(parents=True, exist_ok=False) check_path_exists(gnumakefile) main = input("Main target name: ") if not re.match("^[a-zA-Z_0-9]+$", main): - print("error: invalid main target name") - sys.exit(1) + sys.exit("error: invalid main target name (can only contain letters, digits, dash or underscore)") + +main_fun_finder_re = function_finder.prepare("main") + +# returns 0 if none, 1 if declaration, 2 if definition +def defines_or_declares_main(f): + return function_finder.find(main_fun_finder_re, f) + +def expand_and_normalize_sources(expression, relprefix): + subexps = shlex.split(expression) + sources_lists = [glob.glob(exp, recursive=True) for exp in subexps] + sources = sorted(set([item for sublist in sources_lists for item in sublist])) + return sources -sources = input("Source files separated by spaces (default if empty: *.c): ") -if not sources: - sources="*.c" +def rel_prefix(f): + return f"{f}" if os.path.isabs(f) else f"{relprefix}/{f}" + +def sources_list_for_makefile(sources): + return "\n".join([f" {rel_prefix(source)} \\" for source in sources]) + +def main_suffix(f): + main = defines_or_declares_main(f) + if main == 2: + return "\t# defines 'main'" + elif main == 1: + return "\t# declares 'main'" + else: + return "" + +while True: + sources = input("Source files (default: **/*.c): ") + if not sources: + sources="**/*.c" + source_list = expand_and_normalize_sources(sources, relprefix) + if not source_list: + print(f"error: no sources were matched for '{sources}'.") + else: + print(f"The following sources were matched (relative to {dir}):") + print("\n".join([" " + rel_prefix(source) + main_suffix(source) for source in source_list])) + print() + definitions_of_main = len([source for source in source_list if defines_or_declares_main(source)]) + if definitions_of_main > 1: + print("warning: 'main' seems to be defined multiple times.") + yn = input("Is this ok? [Y/n] ") + if yn == "" or not (yn[0] == "N" or yn[0] == "n"): + break json_compilation_database = None if jcdb.is_file(): yn = input("compile_commands.json exists, add option -json-compilation-database? [Y/n] ") if yn == "" or not (yn[0] == "N" or yn[0] == "n"): - json_compilation_database = "." + json_compilation_database = f"{relprefix}/compile_commands.json" else: print("Option not added; you can later add it to FCFLAGS.") @@ -110,20 +172,19 @@ add_main_stub = False yn = input("Add stub for function main (only needed if it uses command-line arguments)? [y/N] ") if yn != "" and (yn[0] == "Y" or yn[0] == "y"): add_main_stub = True - sources = "fc_stubs.c " + sources print("Please define the architectural model (machdep) of the target machine.") (default_machdep, machdeps) = get_known_machdeps() print("Known machdeps: " + " ".join(machdeps)) machdep_chosen = False while not machdep_chosen: - machdep = input("Please enter the machdep [" + default_machdep + "]: ") + machdep = input(f"Please enter the machdep [{default_machdep}]: ") if not machdep: machdep = default_machdep machdep_chosen = True else: if not (machdep in machdeps): - yn = input("'{}' is not a standard machdep. Proceed anyway? [y/N]".format(machdep)) + yn = input(f"'{machdep}' is not a standard machdep. Proceed anyway? [y/N]") if yn != "" and (yn[0] == "Y" or yn[0] == "y"): machdep_chosen = True else: @@ -135,17 +196,21 @@ def insert_line_after(lines, line_pattern, newline): if re_line.search(lines[i]): lines.insert(i+1, newline) return lines - print("error: no lines found matching pattern: " + line_pattern) - sys.exit(1) + sys.exit(f"error: no lines found matching pattern: {line_pattern}") -def replace_line(lines, line_pattern, value): +def replace_line(lines, line_pattern, value, all_occurrences=False): + replaced = False re_line = re.compile(line_pattern) for i in range(0, len(lines)): if re_line.search(lines[i]): lines[i] = value - return lines - print("error: no lines found matching pattern: " + line_pattern) - sys.exit(1) + replaced = True + if not all_occurrences: + return lines + if replaced: + return lines + else: + sys.exit(f"error: no lines found matching pattern: {line_pattern}") def remove_lines_between(lines, start_pattern, end_pattern): re_start = re.compile(start_pattern) @@ -159,34 +224,39 @@ def remove_lines_between(lines, start_pattern, end_pattern): last_to_remove = i break if first_to_remove == -1: - print("error: could not find start pattern: " + start_pattern) - sys.exit(1) + sys.exit("error: could not find start pattern: " + start_pattern) elif last_to_remove == -1: - print("error: could not find end pattern: " + end_pattern) - sys.exit(1) + sys.exit("error: could not find end pattern: " + end_pattern) return (lines[:first_to_remove-1] if first_to_remove > 0 else []) + (lines[last_to_remove+1:] if last_to_remove < len(lines)-1 else []) with open(sharedir / "analysis-scripts" / "template.mk") as f: lines = list(f) - lines = replace_line(lines, "^MAIN_TARGET :=", "MAIN_TARGET := {}\n".format(main)) - lines = remove_lines_between(lines, "Remove these lines.*main target", "^endif") - lines = replace_line(lines, "^\$\(MAIN_TARGET\).parse:", "$(MAIN_TARGET).parse: {}\n".format(sources)) - if json_compilation_database: - lines = insert_line_after(lines, "^FCFLAGS", " -json-compilation-database {} \\\n".format(json_compilation_database)) - lines = insert_line_after(lines, "^FCFLAGS", " -machdep {} \\\n".format(machdep)) + lines = replace_line(lines, "^MACHDEP = .*", f"MACHDEP = {machdep}\n") if add_main_stub: - check_path_exists("fc_stubs.c") - from shutil import copyfile - copyfile(sharedir / "analysis-scripts" / "fc_stubs.c", "fc_stubs.c") + lines = insert_line_after(lines, "^main.parse: \\\\", f" fc_stubs.c \\\n") + check_path_exists(fc_stubs_c) + shutil.copyfile(sharedir / "analysis-scripts" / "fc_stubs.c", fc_stubs_c) lines = insert_line_after(lines, "^FCFLAGS", " -main eva_main \\\n") - print("Created stub for main function: fc_stubs.c") + print(f"Created stub for main function: {dir / 'fc_stubs.c'}") + lines = replace_line(lines, "^main.parse: \\\\", f"{main}.parse: \\\n") + lines = replace_line(lines, "^TARGETS = main.eva", f"TARGETS = {main}.eva\n") + lines = replace_line(lines, "^ main.c \\\\", sources_list_for_makefile(source_list) + "\n") + if json_compilation_database: + lines = insert_line_after(lines, "^FCFLAGS", f" -json-compilation-database {json_compilation_database} \\\n") + if relprefix != "..": + lines = replace_line(lines, "^ -add-symbolic-path=.:.. \\\\", f" -add-symbolic-path=.:{relprefix} \\\n", all_occurrences=True) gnumakefile.write_text("".join(lines)) -print("Template created: " + gnumakefile.name) +print(f"Template created: {gnumakefile}") + +if not "PTESTS_TESTING" in os.environ and not framac_in_path: + print(f"Frama-C not in path, adding path.mk to {dir}") + frama_c_script = bindir / "frama-c-script" + os.system(f"{frama_c_script} make-path {dir}") if "PTESTS_TESTING" in os.environ: print("Running ptests: cleaning up after tests...") jcdb.unlink() - if add_main_stub: - Path("fc_stubs.c").unlink() + fc_stubs_c.unlink() + # gnumakefile is not erased because we want it as an oracle diff --git a/share/analysis-scripts/prologue.mk b/share/analysis-scripts/prologue.mk new file mode 100644 index 0000000000000000000000000000000000000000..f1cca3d38888a266e2f049084e8aa4a227af99fa --- /dev/null +++ b/share/analysis-scripts/prologue.mk @@ -0,0 +1,43 @@ +########################################################################## +# # +# This file is part of Frama-C. # +# # +# Copyright (C) 2007-2020 # +# CEA (Commissariat à l'énergie atomique et aux énergies # +# alternatives) # +# # +# you can redistribute it and/or modify it under the terms of the GNU # +# Lesser General Public License as published by the Free Software # +# Foundation, version 2.1. # +# # +# It is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Lesser General Public License for more details. # +# # +# See the GNU Lesser General Public License version 2.1 # +# for more details (enclosed in the file licenses/LGPLv2.1). # +# # +########################################################################## + +# Makefile template prologue for Frama-C/Eva case studies. +# For details and usage information, see the Frama-C User Manual. + +# Note: this variable must be defined before including any files +makefile_dir := $(dir $(lastword $(MAKEFILE_LIST))) + +## Useful definitions (to be overridden later if needed) + +# Improves analysis time, at the cost of extra memory usage +export FRAMA_C_MEMORY_FOOTPRINT = 8 + +# FRAMAC is defined in path.mk when it is included, so the +# line below will be safely ignored if this is the case. +# Otherwise, the user may supply it to indicate which Frama-C binary to use. +FRAMAC ?= frama-c + +# analysis.mk contains the main rules and targets +include $(makefile_dir)/analysis.mk + +# Default target +all: eva diff --git a/share/analysis-scripts/slevel-tweaker.sh b/share/analysis-scripts/slevel-tweaker.sh deleted file mode 100755 index f67979a1ebe319e6aed9eb2a7037e74dc115e955..0000000000000000000000000000000000000000 --- a/share/analysis-scripts/slevel-tweaker.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/bash -u - -declare -A alarms -declare -A utimes - - -function print_results() -{ - local s - local t - - if [ -z "$quiet" ] - then - echo -e '\e\0143' - printf "%24s" 'file / slevel' - for s in $slevels - do - printf "%8s" $s - done - printf "\n" - for t in $targets - do - printf "\n" - printf "%18s%6s" $t '#alrm' - for s in $slevels - do - printf "%8s" ${alarms["$t,$s"]-} - done - printf "\n" - printf "%18s%6s" '' 'time' - for s in $slevels - do - printf "%8s" ${utimes["$t,$s"]-} - done - printf "\n" - done - printf "\n" - fi -} - -function poll_results() -{ - for s in $slevels - do - for t in $targets - do - base=$t.$s.slevel.eva - if [ -f $base/stats.txt ] - then - read alarms["$t,$s"] utimes["$t,$s"] <<< $( - source $base/stats.txt - echo ${alarms:-x} ${user_time:- } - ) - fi - done - done -} - - -# Parse command Line - -slevels="0 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000" -run="make --jobs 9" -targets="" -quiet="" - -while [[ $# > 0 ]] -do - case $1 in - -f|--file|--makefile) - run="$run $1 $2" - shift - ;; - - -B|--always-make) - run="$run $1" - ;; - - -q|--quiet) - quiet="yes" - ;; - - *) - targets="$targets $1" - ;; - esac - shift -done - - -# List make targets - -for s in $slevels -do - for t in $targets - do - run="$run $t.$s.slevel.eva" - done -done - - -# Run and display - -{ - $run > /dev/null & - pid=$! - - print_results - - while ps -p $pid >/dev/null - do - sleep 1 - poll_results - print_results - done -} 2> slevel-tweaker.log - -cat slevel-tweaker.log >&2 diff --git a/share/analysis-scripts/summary.py b/share/analysis-scripts/summary.py index a23c34e73ac9d5960d75d0140ee09f355e6f3d8d..6c0e9790e457e66562005c1ee7e75fc8ddb43b41 100755 --- a/share/analysis-scripts/summary.py +++ b/share/analysis-scripts/summary.py @@ -38,21 +38,36 @@ import benchmark_database class OperationException(Exception): pass -def build_env(framac): +def build_make_environment(framac): if framac is None: - return { **os.environ } + env = { **os.environ } + args = [] else: - bindir = framac + '/build/bin' - return { **os.environ, 'PATH' : bindir + ':' + os.environ['PATH'] } - -def list_targets(): - env = build_env(framac) + env = { **os.environ, 'PATH' : f"{framac}/bin:{os.environ['PATH']}" } + args = [ + f"FRAMAC_DIR={framac}/bin", + f"FRAMAC={framac}/bin/frama-c" + ] + return env, args + +def list_targets(dir): + if not os.path.isdir(dir): + raise OperationException(f"target is not a directory: {dir}") + + env, args = build_make_environment(framac) res = subprocess.run( - ["make", "--quiet", "display-targets"], + ["make", "--directory", dir, "--quiet", "display-targets"] + args, env=env, stdout=subprocess.PIPE, encoding='ascii') - return res.stdout.split() + targets = res.stdout.split() + res = [] + for target in targets: + if target.endswith(".eva") or target.endswith(".parse"): + res += [f"{dir}/{target}"] + else: + res += list_targets(target) + return res def clone_frama_c(clonedir, hash): print("Cloning Frama-C", hash, "...") @@ -63,21 +78,16 @@ def clone_frama_c(clonedir, hash): if res.returncode != 0: raise OperationException("Cannot clone repository. Try to manually" "remove the broken clone in " + clonedir) - return res.stdout.strip() + return res.stdout.strip() + '/build' def run_make(framac, benchmark_tag=None): args = ['make', '--keep-going', 'all'] - env = build_env(framac) - if not framac is None: - bindir = framac + '/build/bin' - args += [ - 'FRAMAC_DIR=' + bindir, - 'FRAMAC=' + bindir + '/frama-c'] + env, var_args = build_make_environment(framac) if benchmark_tag is None: - args += ['-j', '8'] + args += ['-j', str(os.cpu_count ())] else: args += ['BENCHMARK=' + benchmark_tag] - return subprocess.Popen(args, env=env, + return subprocess.Popen(args + var_args, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, preexec_fn=os.setsid) @@ -95,8 +105,10 @@ def terminate_process(process): return errors def smart_rename(target): + target = re.sub('^\./', '', target) target = re.sub('main\.eva$', '', target) target = re.sub('\.eva$', '', target) + target = re.sub('\.frama-c/', '', target) target = re.sub('qds/frama-c', 'qds', target) return target @@ -118,7 +130,7 @@ def poll_results(targets, benchmark_tag): def run_analyses(display, database, framac, benchmark_tag): results = [] - targets = list_targets() + targets = list_targets(".") process = run_make(framac, benchmark_tag) errors = b"" next_poll = time.time() @@ -188,7 +200,7 @@ try: gitdir = clonedir + "/frama-c.git" framac = clone_frama_c(clonedir, args.rev) else: - framac = args.repository_path + framac = os.path.abspath(args.repository_path) gitdir = framac if args.benchmark: diff --git a/share/analysis-scripts/summary.sh b/share/analysis-scripts/summary.sh deleted file mode 100755 index 26c4678d98df70eda29f8729f28f2b22cc31b7e5..0000000000000000000000000000000000000000 --- a/share/analysis-scripts/summary.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/bin/bash -u - -declare -A stats - -function pretty_size() -{ - ([[ $# -lt 1 ]] || ! [[ $1 =~ ^[0-9]+$ ]]) && return - KB=$1 - [ $KB -lt 4096 ] && echo ${KB} kiB && return - MB=$(((KB+512)/1024)) - [ $MB -lt 4096 ] && echo ${MB} MiB && return - GB=$(((MB+512)/1024)) - echo $GB GiB -} - -function pretty_coverage() -{ - if [[ $# -gt 1 ]] && [ -n '$1' -a $2 -ne 0 ] - then - echo $(bc <<<"scale=1; 100 * $2 / $1")% - fi -} - -function print_results() -{ - local t - local format - - format="%20s %10s %10s %10s %10s %10s\n" - - if [ -z "$quiet" ] - then - echo -e '\e\0143' - printf "$format" 'target' 'coverage' 'alarms' 'warnings' 'time' 'memory' - printf "%s\n" " ----------------------------------------------------------------------------" - for t in $targets - do - printf "$format" $t \ - "${stats["$t,coverage"]-}" \ - "${stats["$t,alarms"]-}" \ - "${stats["$t,warnings"]-}" \ - "${stats["$t,user_time"]-}" \ - "${stats["$t,memory"]-}" - done - printf "%s\n" " ----------------------------------------------------------------------------" - printf "$format" 'total' '' "${stats["total_alarms"]-}" "${stats["total_warnings"]-}" "${stats["total_user_time"]-}" '' - printf "\n" - fi -} - -function print_csv() -{ - local t - local format - - format="%s\t%s\t%s\t%s\t%s\t%s\n" - - printf "$format" 'target' 'coverage' 'alarms' 'warnings' 'time' 'memory' - for t in $targets - do - printf "$format" $t \ - "${stats["$t,coverage"]-}" \ - "${stats["$t,alarms"]-}" \ - "${stats["$t,warnings"]-}" \ - "${stats["$t,user_time"]-}" \ - "${stats["$t,memory"]-}" - done -} - -function poll_results() -{ - stats["total_alarms"]=0 - stats["total_warnings"]=0 - stats["total_user_time"]=0 - - for t in $targets - do - if [ -f "$t/stats.txt" ] - then - read stats["$t,syn_reach"] stats["$t,sem_reach"] \ - stats["$t,alarms"] stats["$t,warnings"] \ - stats["$t,user_time"] stats["$t,mem_bytes"] <<< $( - source $t/stats.txt - echo ${syn_reach_stmt:-0} ${sem_reach_stmt:-0} \ - ${alarms:-x} ${warnings:-x} \ - ${user_time:-x} ${memory:-'x'} - ) - stats["$t,coverage"]=$(pretty_coverage ${stats["$t,syn_reach"]} ${stats["$t,sem_reach"]}) - stats["$t,memory"]=$(pretty_size ${stats["$t,mem_bytes"]}) - stats["total_alarms"]=$(bc <<<"${stats["total_alarms"]} + ${stats["$t,alarms"]-0}") - stats["total_warnings"]=$(bc <<<"${stats["total_warnings"]} + ${stats["$t,warnings"]-0}") - stats["total_user_time"]=$(bc <<<"${stats["total_user_time"]} + ${stats["$t,user_time"]-0}") - fi - done -} - - -# Parse command Line - -run="make" -targets="" -quiet="" - -while [[ $# > 0 ]] -do - case $1 in - -f|--file|--makefile) - run="$run $1 $2" - shift - ;; - - -B|--always-make) - run="$run $1" - ;; - - -q|--quiet) - quiet="yes" - ;; - - *) - targets="$targets $1" - ;; - esac - shift -done - - -# List make targets - -for t in $targets -do - run="$run $t" -done - - -# Run and display - -{ - $run > /dev/null & - pid=$! - - poll_results - print_results - - while ps -p $pid >/dev/null - do - sleep 1 - poll_results - print_results - done -} 2> summary.log - -cat summary.log >&2 -rm -f summary.log -print_csv > summary.csv diff --git a/share/analysis-scripts/template.mk b/share/analysis-scripts/template.mk index 7cf5bcb29e186a6a7b80a0a261094e0915c77bb4..114d2cd6f80d6b624ece1a7f0478979eeac81036 100644 --- a/share/analysis-scripts/template.mk +++ b/share/analysis-scripts/template.mk @@ -1,70 +1,43 @@ -# TEMPLATE FOR MAKEFILE TO USE IN FRAMA-C/EVA CASE STUDIES - -# DO NOT EDIT THE LINES BETWEEN THE '#'S - -############################################################################### -# Improves analysis time, at the cost of extra memory usage -export FRAMA_C_MEMORY_FOOTPRINT = 8 -# -# frama-c-path.mk contains variables which are specific to each -# user and should not be versioned, such as the path to the -# frama-c binaries (e.g. FRAMAC and FRAMAC_GUI). -# It is an optional include, unnecessary if frama-c is in the PATH --include frama-c-path.mk -# -# FRAMAC is defined in frama-c-path.mk when it is included, so the -# line below will be safely ignored if this is the case -FRAMAC ?= frama-c -# -# frama-c.mk contains the main rules and targets --include $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/frama-c.mk -# +# Makefile template for Frama-C/Eva case studies. +# For details and usage information, see the Frama-C User Manual. + +### Prologue. Do not modify this block. ####################################### +-include path.mk # path.mk contains variables specific to each user + # (e.g. FRAMAC, FRAMAC_GUI) and should not be versioned. It is + # an optional include, unnecessary if frama-c is in the PATH. +FRAMAC ?= frama-c # FRAMAC is defined in path.mk when it is included, but the + # user can override it in the command-line. +include $(shell $(FRAMAC)-config -scripts)/prologue.mk ############################################################################### -# EDIT VARIABLES AND TARGETS BELOW AS NEEDED -# The flags below are only suggestions to use with Eva, and can be removed +# Edit below as needed. Suggested flags are optional. -# (Optional) preprocessing flags, usually handled by -json-compilation-database -CPPFLAGS += +MACHDEP = x86_32 -# (Optional) Frama-C general flags (parsing and kernel) +## Preprocessing flags (for -cpp-extra-args) +CPPFLAGS += \ + +## General flags FCFLAGS += \ + -add-symbolic-path=.:.. \ -kernel-warn-key annot:missing-spec=abort \ -kernel-warn-key typing:implicit-function-declaration=abort \ -# (Optional) Eva-specific flags +## Eva-specific flags EVAFLAGS += \ -eva-warn-key builtins:missing-spec=abort \ -# (MANDATORY) Name of the main target -MAIN_TARGET := - -# Remove these lines after defining the main target -ifeq ($(MAIN_TARGET),) -$(error MAIN_TARGET not defined in $(firstword $(MAKEFILE_LIST))) -endif +## GUI-only flags +FCGUIFLAGS += \ + -add-symbolic-path=.:.. \ -# Add other targets if needed -TARGETS = $(MAIN_TARGET).eva +## Analysis targets (suffixed with .eva) +TARGETS = main.eva -# Default target -all: $(TARGETS) +### Each target <t>.eva needs a rule <t>.parse with source files as prerequisites +main.parse: \ + main.c \ -# (MANDATORY) List of source files used by MAIN_TARGET. -# If there is a JSON compilation database, -# 'frama-c-script list-files' can help obtain it -$(MAIN_TARGET).parse: - - -# The following targets are optional and provided for convenience only -parse: $(TARGETS:%.eva=%.parse) -loop: $(TARGETS:%.eva=%.parse.loop) $(TARGETS:%=%.loop) -gui: $(MAIN_TARGET).eva.gui - -# Run 'make <TARGET>.eva.loop' to obtain a .loop file, fine-tune it by hand, -# then rename it to <TARGET>.slevel to prevent it from being overwritten. -# If such file exists, use it to define per-function slevel values. -ifneq (,$(wildcard $(MAIN_TARGET).slevel)) -$(MAIN_TARGET).eva: \ - EVAFLAGS += $(shell cat $(MAIN_TARGET).slevel | tr -d '\n\\') -endif +### Epilogue. Do not modify this block. ####################################### +include $(shell $(FRAMAC)-config -scripts)/epilogue.mk +############################################################################### diff --git a/src/kernel_services/abstract_interp/abstract_interp.ml b/src/kernel_services/abstract_interp/abstract_interp.ml index ae489b0b24b78e15ecff98fa17b1f8384b375205..89202b4021e323eb84e96c3951b1246e64149594 100644 --- a/src/kernel_services/abstract_interp/abstract_interp.ml +++ b/src/kernel_services/abstract_interp/abstract_interp.ml @@ -432,7 +432,7 @@ module Bool = struct type t = Top | True | False | Bottom let hash (b : t) = Hashtbl.hash b let equal (b1 : t) (b2 : t) = b1 = b2 - let compare (b1 : t) (b2 : t) = Transitioning.Stdlib.compare b1 b2 + let compare (b1 : t) (b2 : t) = Stdlib.compare b1 b2 let pretty fmt = function | Top -> Format.fprintf fmt "Top" | True -> Format.fprintf fmt "True" diff --git a/src/kernel_services/abstract_interp/float_interval.ml b/src/kernel_services/abstract_interp/float_interval.ml index fd063ecc15feda204b32f728b7d5d8c40ce53c11..195d7f285c35cccc2eaf3bd81012fe37ef1ba1f4 100644 --- a/src/kernel_services/abstract_interp/float_interval.ml +++ b/src/kernel_services/abstract_interp/float_interval.ml @@ -172,7 +172,7 @@ module Make (F: Float_sig.S) = struct let compare x y = match x, y with | FRange.Itv (b1, e1, n1), FRange.Itv (b2, e2, n2) -> - let c = Transitioning.Stdlib.compare n1 n2 in + let c = Stdlib.compare n1 n2 in if c <> 0 then c else let r = F.compare b1 b2 in if r <> 0 then r else F.compare e1 e2 diff --git a/src/kernel_services/abstract_interp/offsetmap.ml b/src/kernel_services/abstract_interp/offsetmap.ml index 2d1fce1097ab3aadbeddbc72ec6c83e549ba32a2..e3c5c99c8db5ef823e98341d23bceef3eec40262 100644 --- a/src/kernel_services/abstract_interp/offsetmap.ml +++ b/src/kernel_services/abstract_interp/offsetmap.ml @@ -306,8 +306,8 @@ module Make (V : module type of Offsetmap_lattice_with_isotropy) = struct if hashed_node == tentative_new_node then begin if current_counter = max_int - then Kernel.fatal "Offsetmap(%s): internal maximum exeeded" V.name; - counter := Transitioning.Stdlib.succ current_counter; + then Kernel.fatal "Offsetmap(%s): internal maximum exceeded" V.name; + counter := Stdlib.succ current_counter; end; hashed_node diff --git a/src/kernel_services/ast_data/alarms.ml b/src/kernel_services/ast_data/alarms.ml index a0845e815040b754587d15eca7cf150ba0911b25..5635f1b14691576d2a1c76134895676b7c3dc6c3 100644 --- a/src/kernel_services/ast_data/alarms.ml +++ b/src/kernel_services/ast_data/alarms.ml @@ -134,7 +134,7 @@ module D = let n = Exp.compare e1 e2 in if n = 0 then Extlib.compare_basic fk1 fk2 else n | Memory_access(lv1, access_kind1), Memory_access(lv2, access_kind2) -> - let n = Transitioning.Stdlib.compare access_kind1 access_kind2 in + let n = Stdlib.compare access_kind1 access_kind2 in if n = 0 then Lval.compare lv1 lv2 else n | Index_out_of_bound(e11, e12), Index_out_of_bound(e21, e22) -> let n = Exp.compare e11 e21 in @@ -147,11 +147,11 @@ module D = let n = Extlib.opt_compare Exp.compare e11 e21 in if n = 0 then Exp.compare e12 e22 else n | Overflow(s1, e1, n1, b1), Overflow(s2, e2, n2, b2) -> - let n = Transitioning.Stdlib.compare s1 s2 in + let n = Stdlib.compare s1 s2 in if n = 0 then let n = Exp.compare e1 e2 in if n = 0 then - let n = Transitioning.Stdlib.compare b1 b2 in + let n = Stdlib.compare b1 b2 in if n = 0 then Integer.compare n1 n2 else n else n @@ -160,7 +160,7 @@ module D = | Float_to_int(e1, n1, b1), Float_to_int(e2, n2, b2) -> let n = Exp.compare e1 e2 in if n = 0 then - let n = Transitioning.Stdlib.compare b1 b2 in + let n = Stdlib.compare b1 b2 in if n = 0 then Integer.compare n1 n2 else n else n diff --git a/src/kernel_services/ast_data/annotations.ml b/src/kernel_services/ast_data/annotations.ml index 7f6f2802239ca20044ef97d8dbd18fd2146d74a1..01dde03fd642e3351dd11f0ddb2a4888dde30020 100644 --- a/src/kernel_services/ast_data/annotations.ml +++ b/src/kernel_services/ast_data/annotations.ml @@ -285,7 +285,7 @@ let merge_funspec s1 s2 = (** {2 Getting annotations} *) (**************************************************************************) -module Behavior_set_map = Transitioning.Stdlib.Map.Make(Datatype.String.Set) +module Behavior_set_map = Stdlib.Map.Make(Datatype.String.Set) let is_same_behavior_set l1 l2 = Datatype.String.Set.(equal (of_list l1) (of_list l2)) diff --git a/src/kernel_services/ast_data/property.ml b/src/kernel_services/ast_data/property.ml index bfa51a66126795e29082521bb02dfbfa96d3fee6..7edb1e01b553e34a9c6e32619d61abfcd8ea779d 100644 --- a/src/kernel_services/ast_data/property.ml +++ b/src/kernel_services/ast_data/property.ml @@ -658,7 +658,7 @@ include Datatype.Make_with_collections let n = Extlib.opt_compare Kf.compare kf1 kf2 in if n = 0 then let n = Kinstr.compare ki1 ki2 in - if n = 0 then Transitioning.Stdlib.compare ba1 ba2 else n + if n = 0 then Stdlib.compare ba1 ba2 else n else n | IPAxiom {il_name=s1}, IPAxiom {il_name=s2} diff --git a/src/kernel_services/ast_data/property_status.ml b/src/kernel_services/ast_data/property_status.ml index d4209faf3a99aca4c09714fa442d0128d45c42eb..441c1594ac728b5cbb08d757ac1ee7eaea8413fa 100644 --- a/src/kernel_services/ast_data/property_status.ml +++ b/src/kernel_services/ast_data/property_status.ml @@ -47,7 +47,7 @@ module Emitted_status = | True -> "VALID" | False_if_reachable | False_and_reachable -> "**NOT** VALID" | Dont_know -> "unknown") - let compare (s1:t) s2 = Transitioning.Stdlib.compare s1 s2 + let compare (s1:t) s2 = Stdlib.compare s1 s2 let equal (s1:t) s2 = s1 = s2 let hash (s:t) = Caml_hashtbl.hash s end) diff --git a/src/kernel_services/ast_printing/cabs_debug.ml b/src/kernel_services/ast_printing/cabs_debug.ml index 90a28b89a7125de34b7b761d84e88fc50bf62e6e..9ec29a131e400c08717e0b17458a5f4b5373de2f 100644 --- a/src/kernel_services/ast_printing/cabs_debug.ml +++ b/src/kernel_services/ast_printing/cabs_debug.ml @@ -251,11 +251,11 @@ and pp_raw_stmt fmt = function pp_block bl1 pp_block bl2 pp_cabsloc loc | THROW(e,loc) -> fprintf fmt "@[<hov 2>THROW %a, loc(%a)@]" - (Transitioning.Format.pp_print_option pp_exp) e pp_cabsloc loc + (Format.pp_print_option pp_exp) e pp_cabsloc loc | TRY_CATCH(s,l,loc) -> let print_one_catch fmt (v,s) = fprintf fmt "@[<v 2>@[CATCH %a {@]@;%a@]@;}" - (Transitioning.Format.pp_print_option pp_single_name) v + (Format.pp_print_option pp_single_name) v pp_stmt s in fprintf fmt "@[<v 2>@[TRY %a (loc %a) {@]@;%a@]@;}" diff --git a/src/kernel_services/ast_printing/description.ml b/src/kernel_services/ast_printing/description.ml index 47add398d3dbb5684c85af5f42351c50b8090032..6822800afecaf8e3d5e546787baa5fa0545a42bb 100644 --- a/src/kernel_services/ast_printing/description.ml +++ b/src/kernel_services/ast_printing/description.ml @@ -424,7 +424,7 @@ type order = | A of Datatype.String.Set.t let cmp_order a b = match a , b with - | I a , I b -> Transitioning.Stdlib.compare a b + | I a , I b -> Stdlib.compare a b | I _ , _ -> (-1) | _ , I _ -> 1 | S a , S b -> String.compare a b diff --git a/src/kernel_services/ast_queries/cil_datatype.ml b/src/kernel_services/ast_queries/cil_datatype.ml index 36c2d644f4678075e8515401b601ed91b3ef308c..e3437fa40dd4b9f93bed8c05a3826b3266070abe 100644 --- a/src/kernel_services/ast_queries/cil_datatype.ml +++ b/src/kernel_services/ast_queries/cil_datatype.ml @@ -1639,7 +1639,7 @@ and compare_toffset off1 off2 = and compare_logic_label l1 l2 = match l1, l2 with | StmtLabel s1 , StmtLabel s2 -> Stmt.compare !s1 !s2 | FormalLabel s1, FormalLabel s2 -> String.compare s1 s2 - | BuiltinLabel l1, BuiltinLabel l2 -> Transitioning.Stdlib.compare l1 l2 + | BuiltinLabel l1, BuiltinLabel l2 -> Stdlib.compare l1 l2 | (StmtLabel _ | FormalLabel _), (FormalLabel _ | BuiltinLabel _) -> -1 | (BuiltinLabel _ | FormalLabel _), (StmtLabel _ | FormalLabel _) -> 1 diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml index 6deba7d75d1e529660d1516ff905088ba8af6f8d..319f65e11a927a36afa0fcaff587ae6524aa7a0e 100644 --- a/src/kernel_services/ast_queries/file.ml +++ b/src/kernel_services/ast_queries/file.ml @@ -303,7 +303,7 @@ module DatatypeMachdep = Datatype.Make_with_collections(struct let reprs = [Machdeps.x86_32] let name = "File.Machdep" type t = Cil_types.mach - let compare : t -> t -> int = Transitioning.Stdlib.compare + let compare : t -> t -> int = Stdlib.compare let equal : t -> t -> bool = (=) let hash : t -> int = Hashtbl.hash let copy = Datatype.identity diff --git a/src/kernel_services/ast_queries/logic_typing.ml b/src/kernel_services/ast_queries/logic_typing.ml index d0298228e25e382a6a140398d4a14ec0fd524f7b..8bd52d2425db3ee2d4b36909b04efa6b388ae7d4 100644 --- a/src/kernel_services/ast_queries/logic_typing.ml +++ b/src/kernel_services/ast_queries/logic_typing.ml @@ -478,7 +478,7 @@ module Type_namespace = let reprs = [Typedef] let name = "Logic_typing.type_namespace" type t = type_namespace - let compare : t -> t -> int = Transitioning.Stdlib.compare + let compare : t -> t -> int = Stdlib.compare let equal : t -> t -> bool = (=) let hash : t -> int = Hashtbl.hash end) @@ -3614,7 +3614,7 @@ struct struct type t = string list let compare s1 s2 = - Transitioning.Stdlib.(compare (List.sort compare s1) (List.sort compare s2)) + Stdlib.(compare (List.sort compare s1) (List.sort compare s2)) end) let type_spec old_behaviors loc is_stmt_contract result env s = diff --git a/src/kernel_services/ast_queries/logic_utils.ml b/src/kernel_services/ast_queries/logic_utils.ml index bf0b1e828dbf5c66679c255ebaf1c2e306b066c2..bd2af8507ff33b2cc6e28ab4b1557d552988b316 100644 --- a/src/kernel_services/ast_queries/logic_utils.ml +++ b/src/kernel_services/ast_queries/logic_utils.ml @@ -1657,12 +1657,12 @@ let rec compare_term t1 t2 = | TAlignOfE _, _ -> 1 | _, TAlignOfE _ -> -1 | TUnOp (o1,t1), TUnOp(o2,t2) -> - let res = Transitioning.Stdlib.compare o1 o2 in + let res = Stdlib.compare o1 o2 in if res = 0 then compare_term t1 t2 else res | TUnOp _, _ -> 1 | _, TUnOp _ -> -1 | TBinOp(o1,l1,r1), TBinOp(o2,l2,r2) -> - let res = Transitioning.Stdlib.compare o1 o2 in + let res = Stdlib.compare o1 o2 in if res = 0 then let res = compare_term l1 l2 in if res = 0 then compare_term r1 r2 else res @@ -1852,7 +1852,7 @@ and compare_predicate_node p1 p2 = | Papp _, _ -> 1 | _, Papp _ -> -1 | Prel(r1,lt1,rt1), Prel(r2,lt2,rt2) -> - let res = Transitioning.Stdlib.compare r1 r2 in + let res = Stdlib.compare r1 r2 in if res = 0 then let res = compare_term lt1 lt2 in if res = 0 then compare_term rt1 rt2 else res diff --git a/src/kernel_services/cmdline_parameters/parameter_builder.ml b/src/kernel_services/cmdline_parameters/parameter_builder.ml index 3d6cefa192d4fa5f37a279ff9591af2a2ee5f606..533be6968e7c37f0b8e0eb30919a465623537770 100644 --- a/src/kernel_services/cmdline_parameters/parameter_builder.ml +++ b/src/kernel_services/cmdline_parameters/parameter_builder.ml @@ -338,8 +338,6 @@ struct (** {3 String} *) (* ************************************************************************ *) - module Pervasives_string = String - module String (X: sig include Parameter_sig.Input_with_arg val default: string end) = struct @@ -711,7 +709,7 @@ struct (* return the list of tokens, in reverse order *) let parse s = - let len = Pervasives_string.length s in + let len = Stdlib.String.length s in let rec aux acc pos i s = if i = len then acc else @@ -730,7 +728,7 @@ struct read_char_in_word (fun acc -> add_char c (add_char '\\' acc)) (Word false) in - match Pervasives_string.get s i, pos with + match Stdlib.String.get s i, pos with | '+', Start when use_category -> aux (add_action Add acc) (Word true) next s | '-', Start when use_category -> @@ -1552,7 +1550,7 @@ struct in let r = Str.regexp "[^:]:[^:]" in let split_delim d = - (Pervasives_string.sub d 0 1, Pervasives_string.sub d 2 1) + (Stdlib.String.sub d 0 1, Stdlib.String.sub d 2 1) in let remove_none_and_rev l = List.fold_left diff --git a/src/kernel_services/plugin_entry_points/dynamic.ml b/src/kernel_services/plugin_entry_points/dynamic.ml index 160d39bcf4743e2bb176574380d78f7539e9ac63..8d52b20163f8efcb9f77d1deb3fe64a445a5d7e4 100644 --- a/src/kernel_services/plugin_entry_points/dynamic.ml +++ b/src/kernel_services/plugin_entry_points/dynamic.ml @@ -46,7 +46,6 @@ let dynlib_init () = if not !dynlib_init then begin dynlib_init := true ; - Transitioning.Dynlink.init () ; Dynlink.allow_unsafe_modules true ; end diff --git a/src/kernel_services/plugin_entry_points/plugin.ml b/src/kernel_services/plugin_entry_points/plugin.ml index 40e1c3b169904a21ea2f915e39222f25c8ce7b56..06ec07489dc851480fc9b9964637474f4ba26b4c 100644 --- a/src/kernel_services/plugin_entry_points/plugin.ml +++ b/src/kernel_services/plugin_entry_points/plugin.ml @@ -679,7 +679,7 @@ struct (* the level of verbose is at least the level of debug *) if n > Verbose.get () then Verbose.set n; if n = 0 then decr positive_debug_ref - else if old = 0 then Transitioning.Stdlib.incr positive_debug_ref); + else if old = 0 then Stdlib.incr positive_debug_ref); if is_kernel () then begin Cmdline.kernel_debug_atleast_ref := (fun n -> get () >= n); match !Cmdline.Kernel_debug_level.value_if_set with diff --git a/src/libraries/datatype/datatype.ml b/src/libraries/datatype/datatype.ml index 3afa8e4aa0410776bfad147cfd9f04dfefd0c37c..15f719230756dc77631aa9760feed0ebaac405e7 100644 --- a/src/libraries/datatype/datatype.ml +++ b/src/libraries/datatype/datatype.ml @@ -1676,13 +1676,13 @@ module With_collections(X: S)(Info: Functor_info) = struct module Set = Set - (Transitioning.Stdlib.Set.Make(D)) + (Stdlib.Set.Make(D)) (D) (struct let module_name = Info.module_name ^ ".Set" end) module Map = Map - (Transitioning.Stdlib.Map.Make(D)) + (Stdlib.Map.Make(D)) (D) (struct let module_name = Info.module_name ^ ".Map" end) @@ -1781,7 +1781,7 @@ module Bool = let name = "bool" let reprs = [ true ] let copy = identity - let compare : bool -> bool -> int = Transitioning.Stdlib.compare + let compare : bool -> bool -> int = Stdlib.compare let equal : bool -> bool -> bool = (=) let pretty fmt b = Format.fprintf fmt "%B" b let varname _ = "b" @@ -1795,12 +1795,12 @@ module Int = struct let name = "int" let reprs = [ 2 ] let copy = identity - let compare : int -> int -> int = Transitioning.Stdlib.compare + let compare : int -> int -> int = Stdlib.compare let equal : int -> int -> bool = (=) let pretty fmt n = Format.fprintf fmt "%d" n let varname _ = "n" end) - let compare : int -> int -> int = Transitioning.Stdlib.compare + let compare : int -> int -> int = Stdlib.compare end let int = Int.ty @@ -1853,7 +1853,7 @@ module Float = let name = "float" let reprs = [ 0.1 ] let copy = identity - let compare : float -> float -> int = Transitioning.Stdlib.compare + let compare : float -> float -> int = Stdlib.compare let equal : float -> float -> bool = (=) let pretty fmt f = Format.fprintf fmt "%f" f let varname _ = "f" diff --git a/src/libraries/datatype/datatype.mli b/src/libraries/datatype/datatype.mli index 070d355011242fc21581b923dcbf2c73b521422b..f4717ef4eb03594985ed4672be70167f169b777b 100644 --- a/src/libraries/datatype/datatype.mli +++ b/src/libraries/datatype/datatype.mli @@ -66,10 +66,10 @@ module type S_no_copy = sig (** List of representants of the descriptor. *) val equal: t -> t -> bool - (** Equality: same spec than [Pervasives.(=)]. *) + (** Equality: same spec than [Stdlib.(=)]. *) val compare: t -> t -> int - (** Comparison: same spec than [Pervasives.compare]. *) + (** Comparison: same spec than [Stdlib.compare]. *) val hash: t -> int (** Hash function: same spec than [Hashtbl.hash]. *) diff --git a/src/libraries/stdlib/FCHashtbl.ml b/src/libraries/stdlib/FCHashtbl.ml index c5e703592cdab256f351fe8b8208888e0a1c3240..c8a339057523a41e0b6d4e9661ee819cf6257e05 100644 --- a/src/libraries/stdlib/FCHashtbl.ml +++ b/src/libraries/stdlib/FCHashtbl.ml @@ -50,7 +50,7 @@ module Make(H: Hashtbl.HashedType) : S with type key = H.t = struct include Hashtbl.Make(H) - let fold_sorted ?(cmp=Transitioning.Stdlib.compare) f h acc = + let fold_sorted ?(cmp=Stdlib.compare) f h acc = let module Aux = struct type t = key let compare = cmp end in let module M = Map.Make(Aux) in let add k v m = diff --git a/src/libraries/stdlib/FCHashtbl.mli b/src/libraries/stdlib/FCHashtbl.mli index e8f59e718087316aafe7b27e737ecb990f1b2904..998aab3dd926769674233ce33bf7ba5d049e2cdf 100644 --- a/src/libraries/stdlib/FCHashtbl.mli +++ b/src/libraries/stdlib/FCHashtbl.mli @@ -33,7 +33,7 @@ module type S = sig val iter_sorted: ?cmp:(key -> key -> int) -> (key -> 'a -> unit) -> 'a t -> unit (** Iter on the hashtbl, but respecting the order on keys induced - by [cmp]. Use [Pervasives.compare] if [cmp] not given. + by [cmp]. Use [Stdlib.compare] if [cmp] not given. If the table contains several bindings for the same key, they are passed to [f] in reverse order of introduction, that is, @@ -42,7 +42,7 @@ module type S = sig val fold_sorted: ?cmp:(key -> key -> int) -> (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b (** Fold on the hashtbl, but respecting the order on keys induced - by [cmp]. Use [Pervasives.compare] if [cmp] not given. + by [cmp]. Use [Stdlib.compare] if [cmp] not given. If the table contains several bindings for the same key, they are passed to [f] in reverse order of introduction, that is, diff --git a/src/libraries/stdlib/extlib.ml b/src/libraries/stdlib/extlib.ml index 0714b72b4c9816a4c8a2f4453104fb99bc2fbdab..04d1facd71e8c7df8b9a21aef7b9b054ff26f70b 100644 --- a/src/libraries/stdlib/extlib.ml +++ b/src/libraries/stdlib/extlib.ml @@ -512,6 +512,10 @@ let html_escape s = ) s ; Buffer.contents buf +let format_string_of_stag = function + | Format.String_tag tag -> tag + | _ -> raise (Invalid_argument "unsupported tag extension") + (* ************************************************************************* *) (** Comparison functions *) (* ************************************************************************* *) diff --git a/src/libraries/stdlib/extlib.mli b/src/libraries/stdlib/extlib.mli index 50d3745f5506e63bd47700cd3e88dacfe35122c5..7a1204e843cf57bea0b27b12844c5a73774360b1 100644 --- a/src/libraries/stdlib/extlib.mli +++ b/src/libraries/stdlib/extlib.mli @@ -320,6 +320,13 @@ val strip_underscore: string -> string val html_escape: string -> string +(** [format_string_of_stag stag] returns the string corresponding to [stag], + or raises an exception if the tag extension is unsupported. + + @since Frama-C+dev + *) +val format_string_of_stag: Format.stag -> string + (* ************************************************************************* *) (** {2 Performance} *) (* ************************************************************************* *) @@ -351,9 +358,9 @@ val mkdir : ?parents:bool -> string -> Unix.file_perm -> unit @since 19.0-Potassium *) val safe_at_exit : (unit -> unit) -> unit - (** Register function to call with [Pervasives.at_exit], but only + (** Register function to call with [Stdlib.at_exit], but only for non-child process (fork). The order of execution is preserved - {i wrt} ordinary calls to [Pervasives.at_exit]. *) + {i wrt} ordinary calls to [Stdlib.at_exit]. *) val cleanup_at_exit: string -> unit (** [cleanup_at_exit file] indicates that [file] must be removed when the @@ -386,7 +393,7 @@ val safe_remove_dir: string -> unit (** Comparison functions *) (* ************************************************************************* *) -(** Use this function instead of [Pervasives.compare], as this makes +(** Use this function instead of [Stdlib.compare], as this makes it easier to find incorrect uses of the latter *) external compare_basic: 'a -> 'a -> int = "%compare" diff --git a/src/libraries/stdlib/transitioning.ml.in b/src/libraries/stdlib/transitioning.ml.in index 6d95b7a1466454402704bd22f262d9bb770d5096..182bc40a8e1af00f6d1329952d449d0f2e6808e1 100644 --- a/src/libraries/stdlib/transitioning.ml.in +++ b/src/libraries/stdlib/transitioning.ml.in @@ -19,112 +19,3 @@ (* for more details (enclosed in the file licenses/LGPLv2.1). *) (* *) (**************************************************************************) - -module Stdlib = struct - (* Pervasives/Stdlib functions *) - let compare = compare - let succ = succ - let incr = incr - let min = min - let max = max - let min_int = min_int - let max_int = max_int - let flush = flush - module Set = Set - module Map = Map -end - -[@@@ warning "-3"] - -module Dynlink = struct - let init = @DYNLINK_INIT@ -end - -module Float = struct - let max_float = @FLOAT_MAX_FLOAT@ -end - -module Format = struct - type stag = Format.@FORMAT_STAG@ - let string_of_stag s = @FORMAT_STRING_OF_STAG@ - let stag_of_string s = @FORMAT_STAG_OF_STRING@ - type formatter_stag_functions = { - mark_open_stag : stag -> string; - mark_close_stag : stag -> string; - print_open_stag : stag -> unit; - print_close_stag : stag -> unit; - } - let pp_set_formatter_stag_functions fmt set_formatter_stag_functions = - Format.pp_set_formatter_@FORMAT_STAG@_functions fmt - { - Format.mark_open_@FORMAT_STAG@ = - set_formatter_stag_functions.mark_open_stag; - Format.mark_close_@FORMAT_STAG@ = - set_formatter_stag_functions.mark_close_stag; - Format.print_open_@FORMAT_STAG@ = - set_formatter_stag_functions.print_open_stag; - Format.print_close_@FORMAT_STAG@ = - set_formatter_stag_functions.print_close_stag; - } - let pp_get_formatter_stag_functions fmt () = - let st = Format.pp_get_formatter_@FORMAT_STAG@_functions fmt () in - { - mark_open_stag = st.Format.mark_open_@FORMAT_STAG@; - mark_close_stag = st.Format.mark_close_@FORMAT_STAG@; - print_open_stag = st.Format.print_open_@FORMAT_STAG@; - print_close_stag = st.Format.print_close_@FORMAT_STAG@; - } - let pp_open_stag fmt s = - Format.pp_open_@FORMAT_STAG@ fmt s - let pp_close_stag fmt () = - Format.pp_close_@FORMAT_STAG@ fmt () - - let pp_print_option = @FORMAT_PP_OPT@ -end - -module Q = struct - - let round_to_float x exact = - let m = Z.to_int64 x in - (* Unless the fractional part is exactly 0, round m to an odd integer *) - let m = if exact then m else Int64.logor m 1L in - (* Then convert m to float, with the current rounding mode. *) - Int64.to_float m - - - let to_float x = - match Q.classify x with - | Q.ZERO -> 0.0 - | Q.INF -> infinity - | Q.MINF -> neg_infinity - | Q.UNDEF -> nan - | Q.NZERO -> - let p = x.Q.num and q = x.Q.den in - let np = Z.numbits p and nq = Z.numbits q in - if np <= 53 && nq <= 53 then - (* p and q convert to floats exactly; use FP division to get the - correctly-rounded result. *) - Int64.to_float (Z.to_int64 p) /. Int64.to_float (Z.to_int64 q) - else begin - (* |p| is in [2^(np-1), 2^np) - q is in [2^(nq-1), 2^nq) - hence |p/q| is in (2^(np-nq-1), 2^(np-nq+1)). - We define n such that |p/q*2^n| is in [2^54, 2^56). - >= 2^54 so that the round to odd technique applies. - < 2^56 so that the integral part is representable as an int64. *) - let n = 55 - (np - nq) in - (* Scaling p/q by 2^n *) - let (p', q') = - if n >= 0 - then (Z.shift_left p n, q) - else (p, Z.shift_left q (-n)) in - (* Euclidean division of p' by q' *) - let (quo, rem) = Z.ediv_rem p' q' in - (* quo is the integral part of p/q*2^n - rem/q' is the fractional part. *) - (* Round quo to float *) - let f = round_to_float quo (Z.sign rem = 0) in - (* Apply exponent *) - ldexp f (-n) - end -end diff --git a/src/libraries/stdlib/transitioning.mli b/src/libraries/stdlib/transitioning.mli index b07a1d6bd0bb4138a1840dcd60541f3d82d5c720..d882bcef8ac05f6e0783ad0bf76ed0dbfbebd9f5 100644 --- a/src/libraries/stdlib/transitioning.mli +++ b/src/libraries/stdlib/transitioning.mli @@ -32,55 +32,3 @@ *) (** {1 OCaml} *) - -(** 4.08 *) -module Stdlib: sig - val compare: 'a -> 'a -> int - val succ: int -> int - val incr: int ref -> unit - val min: 'a -> 'a -> 'a - val max: 'a -> 'a -> 'a - val min_int: int - val max_int: int - val flush: out_channel -> unit - module Set: module type of Set - module Map: module type of Map -end - -(** 4.08 *) -module Dynlink: sig - val init: unit -> unit -end - -(** 4.07 *) -module Float: sig - val max_float: float -end - -(** 4.08 *) -module Format: sig - type stag - val string_of_stag: stag -> string - val stag_of_string: string -> stag - type formatter_stag_functions = { - mark_open_stag : stag -> string; - mark_close_stag : stag -> string; - print_open_stag : stag -> unit; - print_close_stag : stag -> unit; - } - val pp_set_formatter_stag_functions: - Format.formatter -> formatter_stag_functions -> unit - val pp_get_formatter_stag_functions: - Format.formatter -> unit -> formatter_stag_functions - val pp_open_stag : Format.formatter -> stag -> unit - val pp_close_stag : Format.formatter -> unit -> unit - val pp_print_option: ?none:(Format.formatter -> unit -> unit) -> - (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a option -> unit -end - -(** {1 Zarith} *) - -(** Function [Q.to_float] was introduced in Zarith 1.5 *) -module Q: sig - val to_float : Q.t -> float -end diff --git a/src/libraries/utils/bitvector.ml b/src/libraries/utils/bitvector.ml index e927e6488f05243dcba174f6830a5d9abca64c6b..7aa34b0fa72987a7fbcf37a7695aded378d31f17 100644 --- a/src/libraries/utils/bitvector.ml +++ b/src/libraries/utils/bitvector.ml @@ -223,7 +223,7 @@ let bitwise_op4 size op4 a b c d = let equal = (=);; (* String equality. *) -let compare = Transitioning.Stdlib.compare +let compare = Stdlib.compare let hash = Hashtbl.hash let concat bv1 size1 bv2 size2 = diff --git a/src/libraries/utils/dotgraph.ml b/src/libraries/utils/dotgraph.ml index f162ef75c89ce637b4c50ced8f208c3fba98a07a..3072dbf8b956913d6596465858dba437d97924f6 100644 --- a/src/libraries/utils/dotgraph.ml +++ b/src/libraries/utils/dotgraph.ml @@ -128,7 +128,7 @@ let close dot = begin Format.fprintf dot.fmt "}@." ; dot.fmt <- Format.err_formatter ; - Transitioning.Stdlib.flush out ; close_out out ; + Stdlib.flush out ; close_out out ; dot.out <- None ; end diff --git a/src/libraries/utils/json.mli b/src/libraries/utils/json.mli index 40e348054821262bed3b760b550ff50221ed2655..f374ebf853bda2feb075aa70ca7fc4a1b70922d7 100644 --- a/src/libraries/utils/json.mli +++ b/src/libraries/utils/json.mli @@ -44,8 +44,8 @@ type json = | `String of string ] type t = json -val equal : t -> t -> bool (** Pervasives *) -val compare : t -> t -> int (** Pervasives *) +val equal : t -> t -> bool (** Stdlib *) +val compare : t -> t -> int (** Stdlib *) val pp : Format.formatter -> t -> unit val pp_dump : Format.formatter -> t -> unit (** without formatting *) diff --git a/src/libraries/utils/json.mll b/src/libraries/utils/json.mll index 491569669a1b011898697c82f0f9b60e4049742a..f17c82ac25de8d3bc772b2ea4eb1141432190980 100644 --- a/src/libraries/utils/json.mll +++ b/src/libraries/utils/json.mll @@ -37,7 +37,7 @@ type json = type t = json let equal = (=) -let compare = Transitioning.Stdlib.compare +let compare = Stdlib.compare type token = EOF | TRUE | FALSE | NULL | KEY of char | STR of string | INT of string | DEC of string diff --git a/src/libraries/utils/pretty_utils.ml b/src/libraries/utils/pretty_utils.ml index 645b73304e1850265f169ed52e65908914918ae8..2d192599aaa33325aae95373c5c550bd34c703fd 100644 --- a/src/libraries/utils/pretty_utils.ml +++ b/src/libraries/utils/pretty_utils.ml @@ -184,7 +184,7 @@ type marger = int ref let marger () = ref 0 let add_margin marger ?(margin=0) ?(min=0) ?(max=80) text = let size = String.length text + margin in - let n = Transitioning.Stdlib.min max (Transitioning.Stdlib.max min size) in + let n = Stdlib.min max (Stdlib.max min size) in if n > !marger then marger := n type align = [ `Center | `Left | `Right ] diff --git a/src/libraries/utils/rgmap.ml b/src/libraries/utils/rgmap.ml index e6d0b182f88970205e3ccdb12d0f411b4395c83e..4888dec36402ea59e368f93c678b15bdc2d865bd 100644 --- a/src/libraries/utils/rgmap.ml +++ b/src/libraries/utils/rgmap.ml @@ -42,7 +42,7 @@ type 'a entry = int * int * 'a module Wmap = Map.Make (struct type t = int - let compare (a:t) (b:t) = Transitioning.Stdlib.compare a b + let compare (a:t) (b:t) = Stdlib.compare a b end) module Rmap = Map.Make diff --git a/src/libraries/utils/rich_text.ml b/src/libraries/utils/rich_text.ml index 2a2a6e8d88b89ba8414bcd335a563c8aeacd5b16..006995446ac7f96006debcb8247caac138c9fdf9 100644 --- a/src/libraries/utils/rich_text.ml +++ b/src/libraries/utils/rich_text.ml @@ -27,7 +27,7 @@ type tag = { p : int ; (* first position *) q : int ; (* last position (excluded) *) - tag : Transitioning.Format.stag ; + tag : Format.stag ; children : tag list ; } @@ -50,8 +50,8 @@ let tags_at (_,tags) k = lookup [] k tags type env = { text : string ; output : (string -> int -> int -> unit) option ; - open_tag : (Transitioning.Format.stag -> int -> int -> unit) option ; - close_tag : (Transitioning.Format.stag -> int -> int -> unit) option ; + open_tag : (Format.stag -> int -> int -> unit) option ; + close_tag : (Format.stag -> int -> int -> unit) option ; } let signal f tag p q = @@ -86,8 +86,8 @@ let rec output_vbox fmt text k n = end let output_fmt fmt text k n = Format.pp_print_string fmt (String.sub text k n) -let open_tag fmt tag _k _n = Transitioning.Format.pp_open_stag fmt tag -let close_tag fmt _tag _k _n = Transitioning.Format.pp_close_stag fmt () +let open_tag fmt tag _k _n = Format.pp_open_stag fmt tag +let close_tag fmt _tag _k _n = Format.pp_close_stag fmt () let pretty ?vbox fmt message = let open_tag = open_tag fmt in @@ -208,8 +208,8 @@ let create ?indent ?margin () = Format.pp_set_max_indent fmt (max 0 (min k (m-10))) end ; let open Format in - Transitioning.Format.pp_set_formatter_stag_functions fmt { - Transitioning.Format.print_open_stag = push_tag buffer ; + Format.pp_set_formatter_stag_functions fmt { + Format.print_open_stag = push_tag buffer ; print_close_stag = pop_tag buffer ; mark_open_stag = no_mark ; mark_close_stag = no_mark ; diff --git a/src/libraries/utils/rich_text.mli b/src/libraries/utils/rich_text.mli index f28e688c30324270d72b1f94007a776c04e4920c..1326ed8ca1a9ba0a4772257c23a53f51fda61b3e 100644 --- a/src/libraries/utils/rich_text.mli +++ b/src/libraries/utils/rich_text.mli @@ -31,14 +31,14 @@ val char_at : message -> int -> char val string : message -> string val substring : message -> int -> int -> string -val tags_at : message -> int -> (Transitioning.Format.stag * int * int) list +val tags_at : message -> int -> (Format.stag * int * int) list (** Returns the list of tags at the given position. Inner tags come first, outer tags last. *) val visit : ?output:(string -> int -> int -> unit) -> - ?open_tag:(Transitioning.Format.stag -> int -> int -> unit) -> - ?close_tag:(Transitioning.Format.stag -> int -> int -> unit) -> + ?open_tag:(Format.stag -> int -> int -> unit) -> + ?close_tag:(Format.stag -> int -> int -> unit) -> message -> unit (** Visit the message, with depth-first recursion on tags. All methods are called with text or tag, position and length. *) diff --git a/src/libraries/utils/utf8_logic.ml b/src/libraries/utils/utf8_logic.ml index 1c2f3a6ddf068d4d24cec065a1d52cbad7f28301..92f594e89998bdcfad518673c529275de25a26e6 100644 --- a/src/libraries/utils/utf8_logic.ml +++ b/src/libraries/utils/utf8_logic.ml @@ -78,6 +78,9 @@ let integer = from_unichar 0x2124 let real = from_unichar 0x211D let pi = from_unichar 0x3C0 + +let infinity = from_unichar 0x221E + (* Local Variables: compile-command: "make -C ../../.." diff --git a/src/libraries/utils/utf8_logic.mli b/src/libraries/utils/utf8_logic.mli index 449e20476bd3f55a53b09dd0cf143395d1ce5fad..7b6ea13a0da7362607108c23d890ca96d5f34184 100644 --- a/src/libraries/utils/utf8_logic.mli +++ b/src/libraries/utils/utf8_logic.mli @@ -49,6 +49,7 @@ val boolean: string val integer: string val real: string val pi: string +val infinity: string (* Local Variables: diff --git a/src/plugins/dive/Makefile.in b/src/plugins/dive/Makefile.in index 1609a95d3178f2a00f289b029af915220d16f2e6..10125171ad2534f8eeca352f7b38a593576dae06 100644 --- a/src/plugins/dive/Makefile.in +++ b/src/plugins/dive/Makefile.in @@ -39,9 +39,9 @@ endif PLUGIN_DIR ?=. PLUGIN_ENABLE:=@ENABLE_DIVE@ PLUGIN_NAME := Dive -PLUGIN_CMO := self callstack node_kind imprecision_graph build main \ - server_interface -PLUGIN_CMI:= graph_types +PLUGIN_CMO := self callstack node_kind node_range dive_graph context build \ + main server_interface +PLUGIN_CMI := dive_types PLUGIN_DEPENDENCIES:= Eva Studia Server PLUGIN_HAS_MLI:= yes PLUGIN_TESTS_DIRS:=dive diff --git a/src/plugins/dive/build.ml b/src/plugins/dive/build.ml index 236582ae1ad1e06d21eac2f065347021e7c06d7e..13f3bcbc5251638fb3fe846bf0642ca4825ce013 100644 --- a/src/plugins/dive/build.ml +++ b/src/plugins/dive/build.ml @@ -21,116 +21,127 @@ (**************************************************************************) open Cil_types -open Graph_types +open Dive_types + +module Graph = Dive_graph let dkey = Self.register_category "build" -exception Too_many_deps +(* --- Utility function --- *) -(* --- Precision evaluation --- *) +(* Breaks a list at n-th element into two sublists *) +let rec list_break n l = + if n <= 0 then ([], l) + else match l with + | [] -> ([], []) + | a :: l -> + let l1, l2 = list_break (n - 1) l in + (a :: l1, l2) -let _fval_contains_maximal_bounds fkind fval = - let top = Fval.top_finite (Fval.kind fkind) in - Fval.has_greater_min_bound top fval >= 0 || - Fval.has_smaller_max_bound top fval >= 0 - -let fkind_limits = - let max_single = float_of_string "0x1.fffffep+127" - and max_double = float_of_string "0x1.fffffffffffffp+1023" in - let single_limits = { min = -. max_single ; max = max_single } - and double_limits = { min = -. max_double ; max = max_double } in - function - | FFloat -> single_limits - | FDouble -> double_limits - | FLongDouble -> assert false - -let float_grade_limits = - let single = float_of_string "0x1p+120" - and double = float_of_string "0x1p+960" - and long_double = float_of_string "0x1p+15360" - in function - | FFloat -> single - | FDouble -> double - | FLongDouble -> long_double - -let is_large_float_range fkind (min,max) = - let limit = float_grade_limits fkind in - if (min < 0.0) = (max < 0.0) then (* if bounds have same sign *) - max -. min >= limit - else - min <= -.limit || max >= limit -let float_grade fkind (min,max) = - if min = max then - Singleton - else if is_large_float_range fkind (min,max) then - Wide - else - Normal +(* --- Lval enumeration --- *) -let ikind_limits ikind = - let open Cil in - let bits = bitsSizeOfInt ikind in - if isSigned ikind then - { min=min_signed_number bits; max=max_signed_number bits } - else - { min=Integer.zero; max=max_unsigned_number bits } +module IterLval = +struct + let visitor f = + object + inherit Visitor.frama_c_inplace + method! vlval lval = f lval; SkipChildren + end -let int_grade_limits ikind = - let bits = Cil.bitsSizeOfInt ikind in - Integer.(pred (two_power_of_int (bits - bits / 8))) + let from_exp f exp = + ignore (Visitor.visitFramacExpr (visitor f) exp) -let is_large_int_range ikind (l,u) = - let limit = int_grade_limits ikind in - if Integer.(lt l zero) = Integer.(lt u zero) then (* if bounds have same sign *) - Integer.(ge (sub u l) limit) - else - Integer.(le l (neg limit)) || Integer.(ge u limit) + let from_init f vi init = + ignore (Visitor.visitFramacInit (visitor f) vi NoOffset init) -let int_grade ikind (min,max) = - if min = max then - Singleton - else if is_large_int_range ikind (min,max) then - Wide - else - Normal + let from_alarm f = function + | Alarms.Division_by_zero e | Index_out_of_bound (e, _) | Invalid_shift (e,_) + | Overflow (_,e,_,_) | Float_to_int (e,_,_) | Is_nan_or_infinite (e,_) + | Is_nan (e,_) | Function_pointer (e,_) | Invalid_pointer e -> + from_exp f e + | Pointer_comparison (opt_e1,e2) -> + Extlib.may (from_exp f) opt_e1; + from_exp f e2 + | Differing_blocks (e1,e2) -> + from_exp f e1; from_exp f e2 + | Memory_access _ | Not_separated _ | Overlap _ + | Uninitialized _ | Dangling _ | Uninitialized_union _ -> () + | Invalid_bool lv -> f lv +end + + +(* --- Evaluation from analysis results --- *) + +module Eval = +struct + let to_kf_list kinstr expr = + let _,set = !Db.Value.expr_to_kernel_function kinstr ~deps:None expr in + Kernel_function.Hptset.fold (fun kf acc -> kf :: acc) set [] + + let to_cvalue kinstr lval = + let state = Db.Value.get_state kinstr in + let _,cvalue = !Db.Value.eval_lval None state lval in + cvalue + + let to_location kinstr lval = + let state = Db.Value.get_state kinstr in + !Db.Value.lval_to_loc_state state lval + + let to_zone kinstr lval = + !Db.Value.lval_to_zone kinstr lval + + let to_callstacks stmt = + let states = Db.Value.get_stmt_state_callstack ~after:false stmt in + match states with + | None -> assert false + | Some table -> + let module Table = Value_types.Callstack.Hashtbl in + Table.fold (fun cs _ acc -> cs :: acc) table [] + + let studia_is_direct (_,{Studia.Writes.direct}) = direct + + let writes kinstr lval = + let zone = to_zone kinstr lval in + Self.debug ~dkey "computing writes for %a" Cil_printer.pp_lval lval; + let result = Studia.Writes.compute zone in + let writes = Extlib.filter_map studia_is_direct fst result in + Self.debug ~dkey "%d found" (List.length writes); + writes + + let reads kinstr lval = + let zone = to_zone kinstr lval in + Self.debug ~dkey "computing reads for %a" Cil_printer.pp_lval lval; + let result = Studia.Reads.compute zone in + let reads = Extlib.filter_map studia_is_direct fst result in + Self.debug ~dkey "%d found" (List.length reads); + reads + + let does_cil_read_zone iter stmt cil zone = + let intersects lval = + let zone' = to_zone (Kstmt stmt) lval in + Locations.Zone.intersects zone' zone + in + try iter (fun lval -> if intersects lval then raise Exit) cil; false + with Exit -> true + + let does_exp_read_zone stmt exp zone = + does_cil_read_zone IterLval.from_exp stmt exp zone + + let does_init_read_zone stmt vi init zone = + does_cil_read_zone + (fun f init -> IterLval.from_init f vi init) + stmt init zone +end +(* --- Precision evaluation --- *) + let update_node_values node kinstr lval = - let typ = Cil.typeOfLval lval in - let state = Db.Value.get_state kinstr in - let _,cvalue = !Db.Value.eval_lval None state lval in - try - let ival = Cvalue.V.project_ival cvalue in - match typ with - | TInt (ikind,_) -> - let size = Integer.of_int (Cil.bitsSizeOfInt ikind) - and signed = Cil.isSigned ikind in - let ival = Ival.reinterpret_as_int ~size ~signed ival in - let min, max = match Ival.min_and_max ival with - | Some min, Some max -> min, max - | _, _ -> assert false (* ival have been reinterpreted *) - in - Imprecision_graph.update_node_int_values node { - values_interval = {min;max}; - values_limits = ikind_limits ikind; - values_grade = int_grade ikind (min,max) - } - - | TFloat (fkind,_) -> - begin match Ival.min_and_max_float ival with - | None, _can_be_nan -> () - | Some (min, max), _can_be_nan -> - let min = Fval.F.to_float min and max = Fval.F.to_float max in - Imprecision_graph.update_node_float_values node { - values_interval = {min;max}; - values_limits = fkind_limits fkind; - values_grade = float_grade fkind (min,max); - } - end - | _ -> () - with Cvalue.V.Not_based_on_null -> () + let typ = Cil.typeOfLval lval + and cvalue = Eval.to_cvalue kinstr lval in + Graph.update_node_values node cvalue typ (* --- Locations handling --- *) @@ -145,55 +156,59 @@ let is_foldable_type typ = | TBuiltin_va_list _ -> false | TNamed _ -> assert false (* the type have been unrolled *) -exception NoMatchingOffset - -let cell_to_scalar typ vi offset = - (* TODO: exceptions must be shown to the user somehow *) - try - let matching = Bit_utils.MatchType typ in - let offset', _ = Bit_utils.find_offset vi.vtype ~offset matching in - Scalar (vi, typ, offset') - with Bit_utils.NoMatchingOffset -> raise NoMatchingOffset -exception NotACell +exception Too_many_deps of node_kind list +exception Unknown_location let enumerate_cells ~is_folded_base ~limit lval kinstr = - (* TODO: non-variable bases must be shown to the user somehow *) - (* TODO: exceptions must be shown to the user somehow *) (* If possible, refine the lval to a non-symbolic one *) - let location = !Db.Value.lval_to_loc kinstr lval - and typ = Cil.typeOfLval lval in + let typ = Cil.typeOfLval lval in + let location = Eval.to_location kinstr lval in let open Locations in let add (acc,count) node_kind = - if count > limit then - raise Too_many_deps; + if count >= limit then + raise (Too_many_deps acc); (node_kind :: acc, count+1) in let add_base base ival (acc,count) = match base with - | Base.Var (vi,_) -> + | Base.Var (vi,_) | Allocated (vi,_,_) -> begin if is_foldable_type vi.vtype && is_folded_base vi then add (acc,count) (Composite (vi)) else - let add_cells offset (acc,count) = - add (acc,count) (cell_to_scalar typ vi offset) + let add_cell offset (acc,count) = + let matching = Bit_utils.MatchType typ in + let offset', _ = Bit_utils.find_offset vi.vtype ~offset matching in + let node_kind = Scalar (vi, typ, offset') in + add (acc,count) node_kind in try - Ival.fold_int add_cells ival (acc,count) - with Abstract_interp.Error_Top -> raise NotACell + Ival.fold_int add_cell ival (acc,count) + with Abstract_interp.Error_Top | Bit_utils.NoMatchingOffset -> + (* fallback to composite node *) + add (acc,count) (Composite (vi)) end - | _ -> raise NotACell + | CLogic_Var _ -> add (acc,count) (Error "logic variables not supported") + | Null -> add (acc,count) AbsoluteMemory + | String (i,cs) -> add (acc,count) (String (i, cs)) in try fst (Location_Bits.fold_i add_base location.loc ([],0)) - with Abstract_interp.Error_Top | NoMatchingOffset -> raise NotACell + with Abstract_interp.Error_Top -> raise Unknown_location let build_node_kind ~is_folded_base lval kinstr = - match enumerate_cells ~is_folded_base ~limit:1 lval kinstr with - | [node_kind] -> node_kind - | _ -> Scattered (lval, kinstr) - | exception NotACell -> Scattered (lval, kinstr) + match lval with + | Var vi, offset -> + (* Build a scalar node even if kinstr is dead *) + Scalar (vi, Cil.typeOfLval lval, offset) + | Mem _, _ -> + match enumerate_cells ~is_folded_base ~limit:1 lval kinstr with + | [node_kind] -> node_kind + | [] (* happens if kinstr is dead code *) -> Scattered (lval, kinstr) + | _ -> assert false + | exception (Too_many_deps _) -> Scattered (lval, kinstr) + | exception Unknown_location -> Unknown (lval, kinstr) let default_node_locality callstack = match callstack with @@ -220,98 +235,49 @@ let build_node_locality callstack node_kind = | None -> { loc_file = get_loc_filename vi.vdecl ; loc_callstack = [] } - -(* --- Context object --- *) - -module NodeRef = Datatype.Pair_with_collections - (Node_kind) (Callstack) - (struct let module_name = "Build.NodeRef" end) - -module Graph = Imprecision_graph -module Index = Datatype.Int.Hashtbl -module NodeTable = FCHashtbl.Make (NodeRef) -module BaseSet = Cil_datatype.Varinfo.Set -module FunctionMap = Kernel_function.Map - -type t = { - mutable graph: Graph.t; - mutable vertex_table: node Index.t; - mutable node_table: node NodeTable.t; - mutable unfolded_bases: BaseSet.t; - mutable hidden_bases: BaseSet.t; - mutable focus: bool FunctionMap.t; - mutable roots: node list; - mutable graph_diff: graph_diff; -} - -let is_folded context vi = - not (BaseSet.mem vi context.unfolded_bases) - -let is_hidden context node_kind = - match Node_kind.get_base node_kind with - | Some vi when BaseSet.mem vi context.hidden_bases -> true - | _ -> false - -let find_node context node_key = - Index.find context.vertex_table node_key - -let update_node context node = - if - not (List.exists (Graph.Node.equal node) context.graph_diff.added_nodes) +let find_compatible_callstacks stmt callstack = + let kf = Kernel_function.find_englobing_kf stmt in + if callstack <> [] && Kernel_function.equal kf (Callstack.top_kf callstack) then - context.graph_diff <- { - context.graph_diff with - added_nodes = node :: context.graph_diff.added_nodes - } - -let add_node context ~node_kind ~node_locality = - let node_ref = (node_kind, node_locality.loc_callstack) in - let add_new _ = - let node = Graph.create_node context.graph ~node_kind ~node_locality in - node.node_hidden <- is_hidden context node.node_kind; - Index.add context.vertex_table node.node_key node; - update_node context node; - node - in - NodeTable.memo context.node_table node_ref add_new - -let remove_node context node = - let node_ref = (node.node_kind, node.node_locality.loc_callstack) in - Graph.remove_node context.graph node; - Index.remove context.vertex_table node.node_key; - NodeTable.remove context.node_table node_ref; - context.graph_diff <- { - context.graph_diff with - removed_nodes = node :: context.graph_diff.removed_nodes - } + (* slight improvement which only work when there is no recursion + and which is only usefull because you currently can't have + all callstacks due to memexec -> in this particular case + we are sure not to miss the only admissible callstack *) + [callstack] + else + (* Keep only callstacks which are a compatible with the current one *) + let callstacks = Eval.to_callstacks stmt in + (* TODO: missing callstacks filtered by memexec *) + Callstack.filter_truncate callstacks callstack (* --- Graph building --- *) let add_or_update_node context callstack node_kind = let node_locality = build_node_locality callstack node_kind in - add_node context ~node_kind ~node_locality + Context.add_node context ~node_kind ~node_locality let build_node context callstack lval kinstr = - let is_folded_base = is_folded context in + let is_folded_base = Context.is_folded context in let node_kind = build_node_kind ~is_folded_base lval kinstr in add_or_update_node context callstack node_kind -let build_all_scattered_node context callstack kinstr lval = - let is_folded_base = is_folded context in - try - let cells = enumerate_cells ~is_folded_base ~limit:20 lval kinstr in - let add node_kind = - let node = add_or_update_node context callstack node_kind in - let new_lval = Extlib.the (Node_kind.to_lval node_kind) in - update_node_values node kinstr new_lval; - node - in - List.map add cells - with NotACell -> - Self.warning "Unable to enumerate cells for %a" - Cil_printer.pp_lval lval; - [] +let build_all_scattered_node ~limit context callstack kinstr lval = + let is_folded_base = Context.is_folded context in + let cells, complete = + try + enumerate_cells ~is_folded_base ~limit lval kinstr, true + with Too_many_deps cells -> cells, false + in + let add node_kind = + let node = add_or_update_node context callstack node_kind in + begin match Node_kind.to_lval node_kind with + | Some lval' -> update_node_values node kinstr lval'; + | _ -> () + end; + node + in + List.map add cells, complete let build_var context callstack varinfo = let lval = Var varinfo, NoOffset in @@ -325,76 +291,76 @@ let build_lval context callstack kinstr lval = let build_alarm context callstack stmt alarm = let node_kind = Alarm (stmt,alarm) in let node_locality = build_node_locality callstack node_kind in - add_node context ~node_kind ~node_locality - -let build_node_deps context node = - let rec build_lval_write_deps callstack kinstr lval = - let zone = !Db.Value.lval_to_zone kinstr lval in - build_write_deps callstack zone - - and build_write_deps callstack zone = - Self.debug ~dkey "computing deps for %a" Node_kind.pretty node.node_kind; - let writes = Studia.Writes.compute zone - and add_deps (stmt,effects) = - match stmt.skind with - | Instr _ when not effects.Studia.Writes.direct -> () - | Instr instr -> - let callstacks = - if callstack <> [] && - Kernel_function.(equal - (find_englobing_kf stmt) - (Callstack.top_kf callstack)) - then - (* slight improvement which only work when there is no recursion - and which is only usefull because you currently can't have - all callstacks due to memexec -> in this particular case - we are sure not to miss the only admissible callstack *) - [callstack] - else - (* Keep only callstacks which are a compatible with the current one *) - let states = Db.Value.get_stmt_state_callstack ~after:false stmt in - let callstacks = match states with - | None -> assert false - | Some table -> - let module Table = Value_types.Callstack.Hashtbl in - Table.fold (fun cs _ acc -> cs :: acc) table [] - in - (* TODO: missing callstacks filtered by memexec *) - Callstack.filter_truncate callstacks callstack - in - (* Create a dependency for each of them *) + Context.add_node context ~node_kind ~node_locality + + +(* --- Writes --- *) + +let build_node_writes context node = + let graph = Context.get_graph context + and max_dep_fetch_count = Context.get_max_dep_fetch_count context in + + let rec build_write_deps callstack kinstr lval = + let writes = match node.node_writes_computation with + | Done -> [] + | Partial writes -> writes + | NotDone -> + let writes = Eval.writes kinstr lval in + node.node_writes_stmts <- writes @ build_arg_deps callstack; + writes + and add_deps = function + | { skind=Instr instr } as stmt -> + let callstacks = find_compatible_callstacks stmt callstack in List.iter (fun cs -> build_instr_deps cs stmt instr) callstacks - | _ -> assert false + | _ -> assert false (* Studia invariant *) in - let count = List.length writes in - Self.debug ~dkey "%d found" count; - if count > 20 then - raise Too_many_deps - else - List.iter add_deps writes - - and build_arg_deps callstack vi = - assert vi.vformal; - let kf = Extlib.the (Kernel_function.find_defining_kf vi) in - let pos = Kernel_function.get_formal_position vi kf in - let callsites = - match Callstack.pop callstack with - | Some (kf',stmt,callstack) -> - assert (Kernel_function.equal kf' kf); - [(stmt,callstack)] - | None -> - let callsites = Kernel_function.find_syntactic_callsites kf in - List.map (fun (kf,stmt) -> (stmt,Callstack.init kf)) callsites - and add_deps (stmt,callstack) = - match stmt.skind with - | Instr (Call (_,_,args,_)) - | Instr (Local_init (_, ConsInit (_, args, _), _)) -> - let exp = List.nth args pos in - build_exp_deps callstack stmt Data exp - | _ -> - assert false (* Callsites can only be Call or ConsInit *) - in - List.iter add_deps callsites + let sub,rest = list_break max_dep_fetch_count writes in + List.iter add_deps sub; + if rest = [] then Done else Partial rest + + and build_alarm_deps callstack stmt alarm = + IterLval.from_alarm (build_lval_deps callstack stmt Data) alarm + + and build_instr_deps callstack stmt = function + | Set (_, exp, _) -> + build_exp_deps callstack stmt Data exp + | Call (_, callee, args, _) -> + build_call_deps callstack stmt callee args + | Local_init (dest, ConsInit (f, args, k), loc) -> + let as_func _dest callee args _loc = + build_call_deps callstack stmt callee args + in + Cil.treat_constructor_as_func as_func dest f args k loc + | Local_init (vi, AssignInit init, _) -> + IterLval.from_init (build_lval_deps callstack stmt Data) vi init + | Asm _ | Skip _ | Code_annot _ -> () (* Cases not returned by Studia *) + + and build_arg_deps callstack = + match Node_kind.get_base node.node_kind with + (* TODO refine formal dependency computation for non-scalar formals *) + | Some vi when vi.vformal -> + let kf = Extlib.the (Kernel_function.find_defining_kf vi) in + let pos = Kernel_function.get_formal_position vi kf in + let callsites = + match Callstack.pop callstack with + | Some (kf',stmt,callstack) -> + assert (Kernel_function.equal kf' kf); + [(stmt,callstack)] + | None -> + let callsites = Kernel_function.find_syntactic_callsites kf in + List.map (fun (kf,stmt) -> (stmt,Callstack.init kf)) callsites + and add_deps (stmt,callstack) = + match stmt.skind with + | Instr (Call (_,_,args,_)) + | Instr (Local_init (_, ConsInit (_, args, _), _)) -> + let exp = List.nth args pos in + build_exp_deps callstack stmt Data exp + | _ -> + assert false (* Callsites can only be Call or ConsInit *) + in + List.iter add_deps callsites; + List.map fst callsites + | _ -> [] and build_return_deps callstack stmt args kf = match Kernel_function.find_return kf with @@ -416,230 +382,312 @@ let build_node_deps context node = Self.warning "Cannot compute all callee dependencies for %a" Cil_printer.pp_stmt stmt; end; - let kinstr = Kstmt stmt in - let _,set = !Db.Value.expr_to_kernel_function kinstr ~deps:None callee in - Kernel_function.Hptset.iter (build_return_deps callstack stmt args) set - - and build_alarm_deps callstack stmt alarm = - let for_exp e = - build_exp_deps callstack stmt Data e - in - let open Alarms in - match alarm with - | Division_by_zero e | Index_out_of_bound (e, _) | Invalid_shift (e,_) - | Overflow (_,e,_,_) | Float_to_int (e,_,_) | Is_nan_or_infinite (e,_) - | Is_nan (e,_) | Function_pointer (e,_) | Invalid_pointer e -> for_exp e - | Pointer_comparison (opt_e1,e2) -> Extlib.may for_exp opt_e1; for_exp e2 - | Differing_blocks (e1,e2) -> for_exp e1; for_exp e2 - | Memory_access _ | Not_separated _ | Overlap _ - | Uninitialized _ | Dangling _ | Uninitialized_union _ -> () - (* TODO: adress depencies inside lval *) - | Invalid_bool lv -> build_lval_deps callstack stmt Data lv - - and build_instr_deps callstack stmt = function - | Set (_, exp, _) -> - build_exp_deps callstack stmt Data exp - | Call (_, callee, args, _) -> build_call_deps callstack stmt callee args - | Local_init (dest, ConsInit (f, args, k), loc) -> - let as_func _dest callee args _loc = - build_call_deps callstack stmt callee args - in - Cil.treat_constructor_as_func as_func dest f args k loc - | Local_init (_, AssignInit init, _) -> - build_init_deps callstack stmt init - | Asm _ | Skip _ | Code_annot _ -> () (* Cases not returned by Studia *) - - and build_init_deps callstack stmt = function - | SingleInit exp -> - build_exp_deps callstack stmt Data exp - | CompoundInit (_typ, initl) -> - List.iter (fun (_off,init) -> build_init_deps callstack stmt init) initl + let l = Eval.to_kf_list (Kstmt stmt) callee in + List.iter (build_return_deps callstack stmt args) l and build_exp_deps callstack stmt kind exp = - match exp.enode with - | Const _ - | SizeOf _ | SizeOfE _ | SizeOfStr _ - | AlignOf _ | AlignOfE _ - | AddrOf _ | StartOf _ -> () - | Lval lval -> - build_lval_deps callstack stmt kind lval - | UnOp (_,e,_) | CastE (_,e) | Info (e,_) -> - build_exp_deps callstack stmt kind e - | BinOp (_,e1,e2,_) -> - build_exp_deps callstack stmt kind e1; - build_exp_deps callstack stmt kind e2 + IterLval.from_exp (build_lval_deps callstack stmt kind) exp and build_lval_deps callstack stmt kind lval = - let dst = build_lval context callstack (Kstmt stmt) lval in - Graph.create_dependency ~allow_folding:true context.graph dst kind node + let kinstr = Kstmt stmt in + let dst = build_lval context callstack kinstr lval in + Graph.create_dependency graph kinstr dst kind node and build_scattered_deps callstack kinstr lval = - let nodes = build_all_scattered_node context callstack kinstr lval in + let succ_count = List.length (Graph.pred graph node) in + let limit = succ_count + max_dep_fetch_count in + let nodes, complete = + build_all_scattered_node ~limit context callstack kinstr lval + in let kind = Composition in let add_dep dst = - Graph.create_dependency ~allow_folding:true context.graph dst kind node + Graph.create_dependency graph kinstr dst kind node in - List.iter add_dep nodes + List.iter add_dep nodes; + if complete then Done else Partial [] in - update_node context node; let callstack = node.node_locality.loc_callstack in - begin match node.node_kind with + let writes_computation = + match node.node_kind with | Scalar (vi,_typ,offset) -> - let lval = (Cil_types.Var vi, offset) in - build_lval_write_deps callstack Kglobal lval + build_write_deps callstack Kglobal (Cil_types.Var vi, offset) | Composite (vi) -> - let lval = (Cil_types.Var vi, Cil_types.NoOffset) in - build_lval_write_deps callstack Kglobal lval + build_write_deps callstack Kglobal (Cil_types.Var vi, Cil_types.NoOffset) | Scattered (lval,kinstr) -> build_scattered_deps callstack kinstr lval | Alarm (stmt,alarm) -> - build_alarm_deps callstack stmt alarm - end; - begin match Node_kind.get_base node.node_kind with - (* TODO refine formal dependency computation for non-scalar formals *) - | Some vi when vi.vformal -> build_arg_deps callstack vi + build_alarm_deps callstack stmt alarm; + Done + | Unknown _ | AbsoluteMemory | String _ | Error _ -> + Done + in + node.node_writes_computation <- writes_computation; + let compare_stmt = Cil_datatype.Stmt_Id.compare in + node.node_writes_stmts <- List.sort compare_stmt node.node_writes_stmts; + Context.update_diff context node + + +(* --- Reads --- *) + +let build_node_reads context node = + let graph = Context.get_graph context + and max_dep_fetch_count = Context.get_max_dep_fetch_count context in + + let rec build_reads_deps callstack kinstr lval = + let reads = match node.node_reads_computation with + | Done -> [] + | Partial reads -> reads + | NotDone -> Eval.reads kinstr lval + and add_deps stmt = + let zone = Some (Eval.to_zone kinstr lval) in + let callstacks = find_compatible_callstacks stmt callstack in + List.iter (fun cs -> build_stmt_deps cs zone stmt) callstacks + in + let sub,rest = list_break max_dep_fetch_count reads in + List.iter add_deps sub; + if rest = [] then Done else Partial rest + + and exp_contains_read zone stmt exp = + match zone with + | None -> true + | Some zone' -> + Eval.does_exp_read_zone stmt exp zone' + + and init_contains_read zone stmt vi init = + match zone with + | None -> true + | Some zone' -> + Eval.does_init_read_zone stmt vi init zone' + + and build_kinstr_deps callstack zone = function + | Kglobal -> () (* Do nothing *) + | Kstmt stmt -> build_stmt_deps callstack zone stmt + + and build_stmt_deps callstack zone stmt = + match stmt.skind with + | Instr instr -> build_instr_deps callstack zone stmt instr + | Return (Some exp,_) -> + if exp_contains_read zone stmt exp then + build_return_deps callstack stmt | _ -> () - end - - -(* --- Graph initialization --- *) - -let create () = - !Db.Value.compute (); - { - graph = Graph.create (); - vertex_table = Index.create 13; - node_table = NodeTable.create 13; - unfolded_bases = BaseSet.empty; - hidden_bases = BaseSet.empty; - focus = FunctionMap.empty; - roots = []; - graph_diff = { added_nodes=[] ; removed_nodes=[] }; - } -let clear context = - context.graph <- Graph.create (); - context.vertex_table <- Index.create 13; - context.node_table <- NodeTable.create 13; - context.focus <- FunctionMap.empty; - context.roots <- []; - context.graph_diff <- { added_nodes=[] ; removed_nodes=[] } + and build_instr_deps callstack zone stmt = function + | Set (lval, exp, _) -> + if exp_contains_read zone stmt exp then + build_lval_deps callstack stmt lval + | Local_init (dest, ConsInit (f, args, k), loc) -> + let as_func _dest callee args _loc = + build_call_deps callstack zone stmt callee args + in + Cil.treat_constructor_as_func as_func dest f args k loc + | Local_init (vi, AssignInit init, _) -> + if init_contains_read zone stmt vi init then + build_var_deps callstack stmt vi + | Call (_, callee, args, _) -> + build_call_deps callstack zone stmt callee args + | _ -> () + and build_return_deps callstack stmt = + let kf = Kernel_function.find_englobing_kf stmt in + let callsites = + match Callstack.pop callstack with + | Some (kf',stmt,callstack) -> + assert (Kernel_function.equal kf' kf); + [(stmt,callstack)] + | None -> + let callsites = Kernel_function.find_syntactic_callsites kf in + List.map (fun (kf,stmt) -> (stmt,Callstack.init kf)) callsites + and add_deps (stmt,callstack) = + match stmt.skind with + | Instr (Call (None,_,_,_)) -> () + | Instr (Call (Some lval,_,_,_)) -> + build_lval_deps callstack stmt lval + | Instr (Local_init (vi,_,_)) -> + build_var_deps callstack stmt vi + | _ -> + assert false (* Callsites can only be Call or ConsInit *) + in + List.iter add_deps callsites; -(* --- Accessors --- *) + and build_call_deps callstack zone stmt callee args = + let l = Eval.to_kf_list (Kstmt stmt) callee in + List.iter (build_args_deps callstack zone stmt args) l -let get_graph context = - context.graph + and build_args_deps callstack zone stmt args callee_kf = + let callstack = Callstack.push (callee_kf,stmt) callstack in + let formals = Kernel_function.get_formals callee_kf in + List.iter2 (build_arg_dep callstack stmt zone) args formals -let get_roots context = - context.roots + and build_arg_dep callstack stmt zone arg formal = + if exp_contains_read zone stmt arg then + build_var_deps callstack stmt formal + and build_lval_deps callstack stmt lval = + let kinstr = Kstmt stmt in + let src = build_lval context callstack kinstr lval in + Graph.create_dependency graph kinstr node Data src -(* --- Mutators --- *) + and build_var_deps callstack stmt vi = + build_lval_deps callstack stmt (Cil.var vi) -let unfold_base context vi = - context.unfolded_bases <- BaseSet.add vi context.unfolded_bases + in + let callstack = node.node_locality.loc_callstack in + let reads_computation = + match node.node_kind with + | Scalar (vi,_typ,offset) -> + build_reads_deps callstack Kglobal (Cil_types.Var vi, offset) + | Composite (vi) -> + build_reads_deps callstack Kglobal (Cil_types.Var vi, Cil_types.NoOffset) + | Scattered (_lval,kinstr) -> + build_kinstr_deps callstack None kinstr; + Done + | Alarm _ | Unknown _ | AbsoluteMemory | String _ | Error _ -> + Done + in + node.node_reads_computation <- reads_computation; + Context.update_diff context node -let fold_base context vi = - context.unfolded_bases <- BaseSet.remove vi context.unfolded_bases -let hide_base context vi = - context.hidden_bases <- BaseSet.add vi context.hidden_bases +(* --- Exploration --- *) -let unhide_base context vi = - context.hidden_bases <- BaseSet.add vi context.hidden_bases +let should_explore node root = + match node.node_kind with + | Scattered _ -> Graph.Node.equal node root + | _ -> not node.node_hidden -let explore ~depth context root = - let should_auto_explore node = - let is_root = Graph.Node.equal node root (* the root is always explored *) - and is_intersting_kind = match node.node_kind with - | Scalar _ | Composite _ | Alarm _ -> true - | Scattered _ -> false - in - is_root || (not node.node_hidden && is_intersting_kind) - in - (* Breadth first search *) +let bfs ~depth ~iter_succ f root = + let module NodeSet = Graph.Node.Set in let queue : (node * int) Queue.t = Queue.create () in + let marks = ref NodeSet.empty in Queue.add (root,0) queue; while not (Queue.is_empty queue) do let (n,d) = Queue.take queue in - if d < depth then begin - if not (n.node_deps_computed) && should_auto_explore n then - begin - begin try - build_node_deps context n; - with Too_many_deps -> - (* TODO: give a mean to explore more dependencies *) - Self.warning "Too many dependencies for %a ; throwing them out" - Node_kind.pretty n.node_kind; - end; - n.node_deps_computed <- true; - end; - Graph.iter_pred (fun n' -> Queue.add (n',d+1) queue) context.graph n - end; + if not (NodeSet.mem n !marks) && d < depth then begin + marks := NodeSet.add n !marks; + f n; + iter_succ (fun n' -> Queue.add (n',d+1) queue) n + end done -let complete_in_depth ~depth context root = - context.roots <- root :: context.roots; - explore ~depth context root +let explore_backward ~depth context root = + let iter_succ f n = Graph.iter_pred f (Context.get_graph context) n + and explore_node n = + if n.node_writes_computation <> Done && should_explore n root then + build_node_writes context n + in + bfs ~depth ~iter_succ explore_node root + +let explore_forward ~depth context root = + let iter_succ f n = Graph.iter_succ f (Context.get_graph context) n + and explore_node n = + if n.node_reads_computation <> Done && should_explore n root then + build_node_reads context n; + in + bfs ~depth ~iter_succ explore_node root -let add_var ?(depth=1) context varinfo = + +(* --- Adding new roots --- *) + +let complete context root = + Context.add_root context root; + root + +let add_var context varinfo = let callstack = [] in let node = build_var context callstack varinfo in - complete_in_depth ~depth context node + complete context node -let add_lval ?(depth=1) context kinstr lval = +let add_lval context kinstr lval = let callstack = match kinstr with | Kglobal -> [] | Kstmt stmt -> Callstack.init (Kernel_function.find_englobing_kf stmt) in let node = build_lval context callstack kinstr lval in - complete_in_depth ~depth context node + complete context node -let add_alarm ?(depth=1) context stmt alarm = +let add_alarm context stmt alarm = let callstack = Callstack.init (Kernel_function.find_englobing_kf stmt) in let node = build_alarm context callstack stmt alarm in - complete_in_depth ~depth context node - -let add_function_alarms ?(depth=1) context kf = - let add_one_alarm _emitter kf' stmt ~rank:_ alarm _code_annot = - if Kernel_function.equal kf' kf then - add_alarm ~depth context stmt alarm + complete context node + +let add_annotation context stmt annot = + (* Only do something for alarms notations *) + Extlib.opt_map (add_alarm context stmt) (Alarms.find annot) + +let add_instr context stmt = function + | Set (lval, _, _) + | Call (Some lval, _, _, _) -> Some (add_lval context (Kstmt stmt) lval) + | Local_init (vi, _, _) -> Some (add_var context vi) + | Code_annot (annot, _) -> add_annotation context stmt annot + | _ -> None (* Do nothing for any other instruction *) + +let add_stmt context stmt = + match stmt.skind with + | Instr instr -> add_instr context stmt instr + | _ -> None (* Do nothing for any other statements *) + +let add_property context = function + | Property.IPCodeAnnot { ica_stmt ; ica_ca } -> + add_annotation context ica_stmt ica_ca + | _ -> None (* Do nothing fo any other property *) + +let add_localizable context = function + | Printer_tag.PLval (_kf, kinstr, lval) -> Some (add_lval context kinstr lval) + | PVDecl (_kf, _kinstr, varinfo) -> Some (add_var context varinfo) + | PIP (prop) -> add_property context prop + | PStmt (_kf, stmt) | PStmtStart (_kf, stmt) -> add_stmt context stmt + | _ -> None (* Do nothing for any other localizable *) + + +(* --- Visibility handling --- *) + +let remove_dependencies context node = + (* Remove incomming edges *) + Graph.remove_dependencies (Context.get_graph context) node; + (* Dependencies are not there anymore *) + node.node_writes_computation <- NotDone; + node.node_writes_stmts <- []; + (* Notify node update *) + Context.update_diff context node + +let remove_disconnected context = + let roots = Context.get_roots context in + let l = Graph.find_independant_nodes (Context.get_graph context) roots in + List.iter (Context.remove_node context) l + +let reduce_to_horizon context range new_root = + (* Reduce to one root *) + Context.set_unique_root context new_root ; + (* List visible nodes *) + let graph = Context.get_graph context + and roots = Context.get_roots context + and backward_bfs = Graph.bfs ~iter_succ:Graph.iter_pred ?limit:range.backward + and forward_bfs = Graph.bfs ~iter_succ:Graph.iter_succ ?limit:range.forward in + let bacward_nodes = backward_bfs graph roots + and forward_nodes = forward_bfs graph roots in + (* Table of visible nodes *) + let module Table = Hashtbl.Make (Graph.Node) in + let visible = Table.create 13 in + let is_visible = Table.mem visible in + List.iter (fun n -> Table.add visible n true) (bacward_nodes @ forward_nodes); + (* Find nodes to hide / remove *) + let update node = + if not (is_visible node) then + if List.exists is_visible (Graph.succ graph node) then + remove_dependencies context node + else + Context.remove_node context node in - Alarms.iter add_one_alarm + Graph.iter_vertex update graph -let explore_from_node ~depth context node = - explore ~depth context node - -let show ?(depth=1) context node = - node.node_hidden <- false; - explore ~depth context node +let show _context node = + node.node_hidden <- false let hide context node = - if not node.node_hidden then - begin - let g = get_graph context in - (* Set the node as hidden *) - node.node_hidden <- true; - (* Remove incomming edges *) - let incomming_edges = Graph.pred_e g node in - List.iter (Graph.remove_dependency g) incomming_edges; - (* Remove disconnected vertices *) - let disconnected_nodes = Graph.find_independant_nodes g context.roots in - List.iter (remove_node context) disconnected_nodes; - (* Dependencies are not there anymore *) - node.node_deps_computed <- false; - (* Notify node update *) - update_node context node - end - -let take_last_differences context = - let pp_node fmt n = Format.pp_print_int fmt n.node_key in - let pp_node_list = Pretty_utils.pp_list ~sep:",@, " pp_node in - let diff = context.graph_diff in - Self.debug ~dkey "added: %a,@, subbed: %a" - pp_node_list diff.added_nodes - pp_node_list diff.removed_nodes; - context.graph_diff <- { added_nodes=[] ; removed_nodes=[] }; - diff + if not node.node_hidden then begin + node.node_hidden <- true; + Context.remove_root context node; + remove_dependencies context node; + remove_disconnected context + end diff --git a/src/plugins/dive/build.mli b/src/plugins/dive/build.mli index 990e3e6f89cf5916b7abd11f2acbb360a16cc448..913ee78fd5ab97210b6f76543df0a55c79c7ca27 100644 --- a/src/plugins/dive/build.mli +++ b/src/plugins/dive/build.mli @@ -20,29 +20,22 @@ (* *) (**************************************************************************) -type t +open Cil_types +open Dive_types +open Context -val create : unit -> t -val clear : t -> unit (* reset to almost an empty context, - but keeps folded and hidden bases *) +val add_lval : t -> kinstr -> lval -> node +val add_var : t -> varinfo -> node +val add_alarm : t -> stmt -> Alarms.alarm -> node +val add_annotation : t -> stmt -> code_annotation -> node option +val add_stmt : t -> stmt -> node option +val add_property : t -> Property.t -> node option +val add_localizable : t -> Printer_tag.localizable -> node option -val get_graph : t -> Imprecision_graph.t -val get_roots : t -> Graph_types.node list +val explore_forward : depth:int -> t -> node -> unit +val explore_backward : depth:int -> t -> node -> unit -val unfold_base : t -> Cil_types.varinfo -> unit -val fold_base : t -> Cil_types.varinfo -> unit -val hide_base : t -> Cil_types.varinfo -> unit -val unhide_base : t -> Cil_types.varinfo -> unit +val show : t -> node -> unit +val hide : t -> node -> unit -val find_node : t -> int -> Graph_types.node - -val add_lval : ?depth:int -> t -> Cil_types.kinstr -> Cil_types.lval -> unit -val add_var : ?depth:int -> t -> Cil_types.varinfo -> unit -val add_alarm : ?depth:int -> t -> Cil_types.stmt -> Alarms.alarm -> unit -val add_function_alarms : ?depth:int -> t -> Cil_types.kernel_function -> unit -val explore_from_node : depth:int -> t -> Graph_types.node -> unit - -val show : ?depth:int -> t -> Graph_types.node -> unit -val hide : t -> Graph_types.node -> unit - -val take_last_differences : t -> Graph_types.graph_diff +val reduce_to_horizon : t -> int option range -> node -> unit diff --git a/src/plugins/dive/context.ml b/src/plugins/dive/context.ml new file mode 100644 index 0000000000000000000000000000000000000000..df14fe7c723a4a332ce12d415422d2e28430a069 --- /dev/null +++ b/src/plugins/dive/context.ml @@ -0,0 +1,205 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Dive_types + +module Graph = Dive_graph + +let dkey = Self.register_category "context" + + +module NodeRef = Datatype.Pair_with_collections + (Node_kind) (Callstack) + (struct let module_name = "Build.NodeRef" end) + +module Index = Datatype.Int.Hashtbl +module NodeTable = FCHashtbl.Make (NodeRef) +module NodeSet = Graph.Node.Set +module BaseSet = Cil_datatype.Varinfo.Set +module FunctionMap = Kernel_function.Map + +type t = { + mutable graph: Graph.t; + mutable vertex_table: node Index.t; (* node_key -> node *) + mutable node_table: node NodeTable.t; (* node_kind * callstack -> node *) + mutable unfolded_bases: BaseSet.t; + mutable hidden_bases: BaseSet.t; + mutable focus: bool FunctionMap.t; + mutable max_dep_fetch_count: int; + mutable roots: NodeSet.t; + mutable graph_diff: graph_diff; +} + + +(* --- initialization --- *) + +let create () = + !Db.Value.compute (); + { + graph = Graph.create (); + vertex_table = Index.create 13; + node_table = NodeTable.create 13; + unfolded_bases = BaseSet.empty; + hidden_bases = BaseSet.empty; + focus = FunctionMap.empty; + max_dep_fetch_count = 10; + roots = NodeSet.empty; + graph_diff = { last_root = None ; added_nodes=[] ; removed_nodes=[] }; + } + +let clear context = + context.graph <- Graph.create (); + context.vertex_table <- Index.create 13; + context.node_table <- NodeTable.create 13; + context.focus <- FunctionMap.empty; + context.max_dep_fetch_count <- 10; + context.roots <- NodeSet.empty; + context.graph_diff <- { last_root = None ; added_nodes=[] ; removed_nodes=[] } + + +(* --- Accessors --- *) + +let get_graph context = + context.graph + +let find_node context node_key = + Index.find context.vertex_table node_key + +let get_max_dep_fetch_count context = + context.max_dep_fetch_count + + +(* --- State --- *) + +let is_node_updated context node = + let is_node n = Graph.Node.equal node n in + List.exists is_node context.graph_diff.removed_nodes || + List.exists is_node context.graph_diff.added_nodes + +let update_diff context node = + if not (is_node_updated context node) then + context.graph_diff <- { + context.graph_diff with + added_nodes = node :: context.graph_diff.added_nodes; + } + +let take_last_diff context = + let pp_node fmt n = Format.pp_print_int fmt n.node_key in + let pp_node_list = Pretty_utils.pp_list ~sep:",@, " pp_node in + let diff = context.graph_diff in + Self.debug ~dkey "root: %a,@, added: %a,@, subbed: %a" + (Pretty_utils.pp_opt pp_node) diff.last_root + pp_node_list diff.added_nodes + pp_node_list diff.removed_nodes; + context.graph_diff <- { + last_root = None ; + added_nodes=[] ; + removed_nodes=[] + }; + diff + + +(* --- Roots --- *) + +let get_roots context = + NodeSet.elements context.roots + +let update_roots context new_roots = + let old_roots = context.roots in + context.roots <- new_roots; + let unset n = + if not (NodeSet.mem n new_roots) then begin + n.node_is_root <- false; + update_diff context n + end + and set n = + if not (NodeSet.mem n old_roots) then begin + n.node_is_root <- true; + update_diff context n + end + in + NodeSet.iter unset old_roots; + NodeSet.iter set new_roots + +let set_unique_root context root = + update_roots context (NodeSet.singleton root) + +let add_root context root = + update_roots context (NodeSet.add root context.roots) + +let remove_root context root = + update_roots context (NodeSet.remove root context.roots) + + +(* --- Folding --- *) + +let is_folded context vi = + not (BaseSet.mem vi context.unfolded_bases) + +let fold context vi = + context.unfolded_bases <- BaseSet.remove vi context.unfolded_bases + +let unfold context vi = + context.unfolded_bases <- BaseSet.add vi context.unfolded_bases + + +(* --- Base hiding --- *) + +let is_hidden context node_kind = + match Node_kind.get_base node_kind with + | Some vi when BaseSet.mem vi context.hidden_bases -> true + | _ -> false + +let show context vi = + context.hidden_bases <- BaseSet.add vi context.hidden_bases + +let hide context vi = + context.hidden_bases <- BaseSet.add vi context.hidden_bases + + +(* --- Building --- *) + +let add_node context ~node_kind ~node_locality = + let node_ref = (node_kind, node_locality.loc_callstack) in + let add_new _ = + let node = Graph.create_node context.graph ~node_kind ~node_locality in + node.node_hidden <- is_hidden context node.node_kind; + Index.add context.vertex_table node.node_key node; + update_diff context node; + node + in + NodeTable.memo context.node_table node_ref add_new + +let remove_node context node = + let node_ref = (node.node_kind, node.node_locality.loc_callstack) in + let graph = context.graph in + Graph.iter_succ (fun n -> n.node_writes_computation <- NotDone) graph node; + Graph.iter_pred (fun n -> n.node_reads_computation <- NotDone) graph node; + Graph.remove_node context.graph node; + Index.remove context.vertex_table node.node_key; + NodeTable.remove context.node_table node_ref; + let is_not_node n = not (Graph.Node.equal node n) in + context.graph_diff <- { + context.graph_diff with + added_nodes = List.filter is_not_node context.graph_diff.added_nodes; + removed_nodes = node :: context.graph_diff.removed_nodes; + } diff --git a/src/plugins/dive/context.mli b/src/plugins/dive/context.mli new file mode 100644 index 0000000000000000000000000000000000000000..953fcf72a9a4f34486609518ad755f4f08a3dfa7 --- /dev/null +++ b/src/plugins/dive/context.mli @@ -0,0 +1,61 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Dive_types + +(* +module NodeRef : Datatype.S with type t = node_kind * callstack +module Index : Datatype.Hashtbl with type key = int +module NodeTable : Datatype.Hashtbl with type key = NodeRef.t +module NodeSet : Datatype.Set with type elt = node +module BaseSet : Datatype.Set with type elt = Cil_types.varinfo +module FunctionMap : Datatype.Map with type key = Cil_types.kernel_function +*) + +type t + +val create : unit -> t +val clear : t -> unit (* reset to almost an empty context, + but keeps folded and hidden bases *) + +val get_graph : t -> Dive_graph.t +val find_node : t -> int -> node +val get_max_dep_fetch_count : t -> int + +val get_roots : t -> node list +val set_unique_root : t -> node -> unit +val add_root : t -> node -> unit +val remove_root : t -> node -> unit + +val is_folded : t -> Cil_types.varinfo -> bool +val unfold : t -> Cil_types.varinfo -> unit +val fold : t -> Cil_types.varinfo -> unit + +val is_hidden : t -> node_kind -> bool +val hide : t -> Cil_types.varinfo -> unit +val show : t -> Cil_types.varinfo -> unit + +val add_node : t -> node_kind:node_kind -> node_locality:node_locality -> node +val remove_node : t -> node -> unit + +val update_diff : t -> node -> unit +val take_last_diff : t -> graph_diff diff --git a/src/plugins/dive/imprecision_graph.ml b/src/plugins/dive/dive_graph.ml similarity index 64% rename from src/plugins/dive/imprecision_graph.ml rename to src/plugins/dive/dive_graph.ml index 2ebd12cf182b051b49c551f7a1dd71d7b3b7c7b4..32aff4d9971cfeeb831a7c85e699e3d29e6c7723 100644 --- a/src/plugins/dive/imprecision_graph.ml +++ b/src/plugins/dive/dive_graph.ml @@ -20,15 +20,39 @@ (* *) (**************************************************************************) -open Graph_types - -module Node = -struct - type t = node - let compare v1 v2 = v1.node_key - v2.node_key - let hash v = v.node_key - let equal v1 v2 = v1.node_key = v2.node_key -end +open Dive_types + +let fresh_key = + let next_key = ref 0 in + fun () -> incr next_key; !next_key + +let new_node + ?(node_kind=Error "no kind") + ?(node_locality={loc_file=""; loc_callstack=[]}) + () = { + node_key = fresh_key (); + node_kind; + node_locality; + node_is_root = false; + node_hidden = false; + node_values = None; + node_range = Empty; + node_writes_computation = NotDone; + node_reads_computation = NotDone; + node_writes_stmts = []; +} + +module Node = Datatype.Make_with_collections + (struct + type t = node + include Datatype.Serializable_undefined + let name = "Dive.Node" + let reprs = [ new_node () ] + let compare n1 n2 = Datatype.Int.compare n1.node_key n2.node_key + let hash n = n.node_key + let equal n1 n2 = n1.node_key = n2.node_key + let pretty fmt n = Format.pp_print_int fmt n.node_key + end) module Dependency = struct @@ -39,7 +63,7 @@ struct let default = { dependency_key = -1; dependency_kind = Data; - dependency_multiple = false; + dependency_origins = [] } end @@ -47,105 +71,76 @@ module G = Graph.Imperative.Digraph.ConcreteBidirectionalLabeled (Node) (Dependency) include G -let vertices g = - fold_vertex (fun n acc -> n ::acc) g [] - -let edges g = - fold_edges_e (fun d acc -> d ::acc) g [] - -let next_key = ref 0 let create_node ~node_kind ~node_locality g = - let node = { - node_key = !next_key; - node_kind; - node_locality; - node_hidden = false; - node_int_values = None; - node_float_values = None; - node_deps_computed = false; - } - in - incr next_key; + let node = new_node ~node_kind ~node_locality () in add_vertex g node; node let remove_node = remove_vertex -let union_int_interval i1 i2 = - { min = Integer.min i1.min i2.min ; max = Integer.max i1.max i2.max } - -let union_float_interval i1 i2 = - { min = min i1.min i2.min ; max = max i1.max i2.max } - -let worst_precision_grade q1 q2 = - match q1, q2 with - | Wide, _ | _, Wide -> Wide - | Normal, _ | _, Normal -> Normal - | Singleton, Singleton -> Singleton - -let merge_int_values p1 p2 = - (* TODO: prevent assertion failure *) - assert (Integer.equal p1.values_limits.min p2.values_limits.min); - assert (Integer.equal p1.values_limits.max p2.values_limits.max); - { - values_interval = union_int_interval p1.values_interval p2.values_interval; - values_limits = p1.values_limits; - values_grade = worst_precision_grade p1.values_grade p2.values_grade; - } - -let merge_float_values p1 p2 = - (* TODO: prevent assertion failure *) - assert (p1.values_limits = p2.values_limits); - { - values_interval = union_float_interval p1.values_interval p2.values_interval; - values_limits = p1.values_limits; - values_grade = worst_precision_grade p1.values_grade p2.values_grade; - } - -let update_node_int_values node new_values = - node.node_int_values <- - Some (Extlib.opt_fold merge_int_values node.node_int_values new_values) - -let update_node_float_values node new_values = - node.node_float_values <- - Some (Extlib.opt_fold merge_float_values node.node_float_values new_values) - - -let create_dependency ~allow_folding g v1 dependency_kind v2 = +let create_dependency g kinstr v1 dependency_kind v2 = let same_kind (_,e,_) = e.dependency_kind = dependency_kind in let matching_edge = try - if allow_folding then - Some (List.find same_kind (G.find_all_edges g v1 v2)) - else - None + Some (List.find same_kind (G.find_all_edges g v1 v2)) with Not_found -> None in - match matching_edge with - | Some (_,e,_) -> - e.dependency_multiple <- true - | None -> - let e = { - dependency_key = !next_key; - dependency_kind; - dependency_multiple = false; - } + let e = match matching_edge with + | Some (_,e,_) -> e + | None -> + let e = { + dependency_key = fresh_key (); + dependency_kind; + dependency_origins = [] + } + in + add_edge_e g (v1,e,v2); + e + in + (* Add origins *) + match kinstr with + | Cil_types.Kglobal -> () + | Kstmt stmt -> + let add_uniq l x = + List.sort_uniq Cil_datatype.Stmt.compare (x :: l) in - incr next_key; - add_edge_e g (v1,e,v2) + e.dependency_origins <- add_uniq e.dependency_origins stmt + let remove_dependency g edge = remove_edge_e g edge +let remove_dependencies g node = + iter_pred_e (remove_dependency g) g node + +let vertices g = + fold_vertex (fun n acc -> n ::acc) g [] + +let edges g = + fold_edges_e (fun d acc -> d ::acc) g [] + + +let update_node_values node new_values typ = + node.node_values <- + Some (Extlib.opt_fold Cvalue.V.join node.node_values new_values); + node.node_range <- + Node_range.(upper_bound node.node_range (evaluate new_values typ)) let find_independant_nodes g roots = let module Dfs = Graph.Traverse.Dfs (struct include G - let iter_succ = G.iter_pred - let fold_succ = G.fold_pred + (* Consider the graph as unoriented *) + let iter_succ f g n = + iter_pred f g n; + iter_succ f g n + + let fold_succ f g n acc = + let acc = fold_pred f g n acc in + let acc = fold_succ f g n acc in + acc end) in let module Table = Hashtbl.Make (Node) in @@ -154,6 +149,24 @@ let find_independant_nodes g roots = fold_vertex (fun n acc -> if Table.mem table n then acc else n :: acc) g [] +let bfs ?(iter_succ=iter_succ) ?(limit=max_int) g roots = + let module Table = Hashtbl.Make (Node) in + let explored : int Table.t = Table.create 13 + and queue : (node * int) Queue.t = Queue.create () in + (* Add roots to queue *) + List.iter (fun root -> Queue.add (root,0) queue) roots; + (* Iterate over the queue *) + while not (Queue.is_empty queue) do + let (n,d) = Queue.take queue in + if d <= limit && not (Table.mem explored n) then begin + Table.add explored n d; + iter_succ (fun n' -> Queue.add (n',d+1) queue) g n + end + done; + (* Convert the result to list *) + Table.fold (fun n _ l -> n :: l) explored [] + + let ouptput_to_dot out_channel g = let open Graph.Graphviz.DotAttributes in (* let g = add_dummy_nodes g in *) @@ -196,13 +209,6 @@ let ouptput_to_dot out_channel g = let default_vertex_attributes _g = [] let vertex_name v = "cp" ^ (string_of_int v.node_key) let vertex_attributes v = - let grade = match v.node_int_values, v.node_float_values with - | Some v1, Some v2 -> - Some (worst_precision_grade v1.values_grade v2.values_grade) - | Some v, _ -> Some v.values_grade - | _, Some v -> Some v.values_grade - | None, None -> None - in let l = ref [] in let text = Pretty_utils.to_string Node_kind.pretty v.node_kind in if text <> "" then @@ -211,21 +217,26 @@ let ouptput_to_dot out_channel g = | Scalar _ -> [`Shape `Box] | Composite _ -> [ `Shape `Box3d ] | Scattered _ -> [ `Shape `Parallelogram ] + | Unknown _ -> [`Shape `Diamond ; `Color 0xff0000] | Alarm _ -> [ `Shape `Doubleoctagon ; `Style `Bold ; `Color 0xff0000 ; `Style `Filled ; `Fillcolor 0xff0000 ] - and values = match grade with - | None -> [] - | Some Singleton -> + | AbsoluteMemory | String _ -> [`Shape `Box3d] + | Error _ -> [`Color 0xff0000] + and range = match v.node_range with + | Empty -> [] + | Singleton -> [`Color 0x88aaff ; `Style `Filled ; `Fillcolor 0xaaccff ] - | Some Normal -> + | Normal _ -> [ `Color 0x004400 ; `Style `Filled ; `Fillcolor 0xeeffee ] - | Some Wide -> + | Wide -> [ `Color 0xff0000 ; `Style `Filled ; `Fillcolor 0xffbbbb ] in - l := values @ kind @ !l; - if not v.node_deps_computed then + l := range @ kind @ !l; + if v.node_writes_computation <> Done then l := [ `Style `Dotted ] @ !l; + if v.node_is_root then + l := [ `Style `Bold ] @ !l; !l let get_subgraph v = let {loc_file ; loc_callstack} = v.node_locality in @@ -237,9 +248,9 @@ let ouptput_to_dot out_channel g = let kind_attribute = match e.dependency_kind with | Callee -> [`Color 0x00ff00 ] | _ -> [] - and folding_attribute = match e.dependency_multiple with - | true -> [ `Style `Bold ] - | false -> [] + and folding_attribute = match e.dependency_origins with + | [] | [_] -> [] + | _ -> [ `Style `Bold ] in kind_attribute @ folding_attribute end) in @@ -247,6 +258,10 @@ let ouptput_to_dot out_channel g = module JsonPrinter = struct + let output_stmt stmt = + let kf = Kernel_function.find_englobing_kf stmt in + Server.Kernel_ast.KfMarker.to_json (kf, PStmtStart (kf, stmt)) + let output_kinstr = function | Cil_types.Kglobal -> `String "global" | Cil_types.Kstmt stmt -> `Int stmt.Cil_types.sid @@ -265,7 +280,11 @@ struct | Scalar _ -> "scalar" | Composite _ -> "composite" | Scattered _ -> "scattered" + | Unknown _ -> "unknown" | Alarm _ -> "alarm" + | AbsoluteMemory -> "absolute" + | String _ -> "string" + | Error _ -> "error" in `String s @@ -277,13 +296,12 @@ struct in `Assoc fields - let output_node_precision_grade grade = - let s = match grade with - | Singleton -> "singleton" - | Normal -> "normal" - | Wide -> "wide" - in - `String s + let output_range range = + match range with + | Empty -> `String "empty" + | Singleton -> `String "singleton" + | Normal range_grade -> `Int range_grade + | Wide -> `String "wide" let output_dep_kind kind = let s = match kind with @@ -295,32 +313,16 @@ struct in `String s - let output_int_interval interval = - (* TODO: handle overflow *) - `Assoc [ - ("min", `Int (Integer.to_int interval.min)) ; - ("max", `Int (Integer.to_int interval.max)) ; - ] + let output_node_values values = + match values with + | None -> `Null + | Some cvalue when Cvalue.V.is_bottom cvalue -> `Null + | Some cvalue -> `String (Pretty_utils.to_string Cvalue.V.pretty cvalue) - let output_float_interval interval = - `Assoc [ - ("min", `Float interval.min) ; - ("max", `Float interval.max) ; - ] - - let output_node_int_values values = - `Assoc [ - ("computed", output_int_interval values.values_interval) ; - ("limits", output_int_interval values.values_limits) ; - ("grade", output_node_precision_grade values.values_grade) ; - ] - - let output_node_float_values values = - `Assoc [ - ("computed", output_float_interval values.values_interval) ; - ("limits", output_float_interval values.values_limits) ; - ("grade", output_node_precision_grade values.values_grade) ; - ] + let output_computation = function + | Done -> `String "yes" + | Partial _ -> `String "partial" + | NotDone -> `String "no" let output_node node = let label = Pretty_utils.to_string Node_kind.pretty node.node_kind in @@ -329,18 +331,13 @@ struct ("label", `String label) ; ("kind", output_node_kind node.node_kind) ; ("locality", output_node_locality node.node_locality) ; - ("explored", `Bool node.node_deps_computed) ; + ("is_root", `Bool node.node_is_root) ; + ("backward_explored", output_computation node.node_writes_computation) ; + ("forward_explored", output_computation node.node_reads_computation) ; + ("writes", `List (List.map output_stmt node.node_writes_stmts)) ; + ("values", output_node_values node.node_values) ; + ("range", output_range node.node_range) ; ] @ - begin match node.node_int_values with - | None -> [] - | Some node_values -> - [("int_values", output_node_int_values node_values)] - end @ - begin match node.node_float_values with - | None -> [] - | Some node_values -> - [("float_values", output_node_float_values node_values)] - end @ begin match Node_kind.to_lval node.node_kind with | None -> [] | Some lval -> @@ -355,7 +352,7 @@ struct ("src", `Int n1.node_key) ; ("dst", `Int n2.node_key) ; ("kind", output_dep_kind dep.dependency_kind) ; - ("multiple", `Bool dep.dependency_multiple) + ("origins", `List (List.map output_stmt dep.dependency_origins)) ; ] let output_graph g = @@ -365,7 +362,10 @@ struct ] let output_diff g diff = - let added_nodes = List.map output_node diff.added_nodes + let root = match diff.last_root with + | None -> `Null + | Some root -> `Int root.node_key + and added_nodes = List.map output_node diff.added_nodes and added_deps = let module Set = Set.Make (struct type t = edge @@ -383,6 +383,7 @@ struct List.map (fun node -> `Int node.node_key) diff.removed_nodes in `Assoc [ + ("root", root) ; ("add", `Assoc [ ("nodes", `List added_nodes) ; ("deps", `List added_deps) diff --git a/src/plugins/dive/imprecision_graph.mli b/src/plugins/dive/dive_graph.mli similarity index 85% rename from src/plugins/dive/imprecision_graph.mli rename to src/plugins/dive/dive_graph.mli index dae5176aff12bbd6e7ead1c57dfd62394775f788..1f4f6d43cb695ba2823f203ac7ee9c771149ebf6 100644 --- a/src/plugins/dive/imprecision_graph.mli +++ b/src/plugins/dive/dive_graph.mli @@ -20,13 +20,13 @@ (* *) (**************************************************************************) -open Graph_types +open Dive_types include Graph.Sig.G with type V.t = node and type E.t = node * dependency * node -module Node : Graph.Sig.COMPARABLE with type t = node +module Node : Datatype.S_with_collections with type t = node module Dependency : Graph.Sig.COMPARABLE with type t = dependency @@ -38,15 +38,17 @@ val create_node : val remove_node : t -> node -> unit -val update_node_int_values : node -> Integer.t node_values -> unit -val update_node_float_values : node -> float node_values -> unit +val update_node_values : node -> Cvalue.V.t -> Cil_types.typ -> unit -val create_dependency : allow_folding:bool -> t -> node -> dependency_kind -> - node -> unit +val create_dependency : t -> Cil_types.kinstr -> + node -> dependency_kind -> node -> unit val remove_dependency : t -> node * dependency * node -> unit +val remove_dependencies : t -> node -> unit val find_independant_nodes : t -> node list -> node list +val bfs : ?iter_succ:((node -> unit) -> t -> node -> unit) -> ?limit:int -> + t -> node list -> node list val ouptput_to_dot : out_channel -> t -> unit val ouptput_to_json : out_channel -> t -> unit diff --git a/src/plugins/dive/graph_types.mli b/src/plugins/dive/dive_types.mli similarity index 67% rename from src/plugins/dive/graph_types.mli rename to src/plugins/dive/dive_types.mli index f1920b35026e847eaa92a1921861890d3087a590..599612a864747312c8fc8d75d26383ebcd99d4c4 100644 --- a/src/plugins/dive/graph_types.mli +++ b/src/plugins/dive/dive_types.mli @@ -24,31 +24,38 @@ type node_kind = | Scalar of Cil_types.varinfo * Cil_types.typ * Cil_types.offset | Composite of Cil_types.varinfo | Scattered of Cil_types.lval * Cil_types.kinstr + | Unknown of Cil_types.lval * Cil_types.kinstr | Alarm of Cil_types.stmt * Alarms.alarm + | AbsoluteMemory + | String of int * Base.cstring + | Error of string + +type callstack = Callstack.t type node_locality = { loc_file : string; - loc_callstack : Callstack.t; + loc_callstack : callstack; } -type 'a interval = {min: 'a; max: 'a} - -type precision_grade = Singleton | Normal | Wide +type node_range = + | Empty (* No values assigned to the node *) + | Singleton (* A unique value ever assigned *) + | Normal of int (* From 0 = almost singleton to 100 = almost all possible values *) + | Wide (* Too many values for the type to be reasonable *) -type 'a node_values = { - values_interval : 'a interval; - values_limits : 'a interval; - values_grade : precision_grade; -} +type 'a computation = NotDone | Partial of 'a | Done type node = { node_key : int; node_kind : node_kind; node_locality : node_locality; + mutable node_is_root : bool; mutable node_hidden : bool; - mutable node_int_values : (Integer.t node_values) option; - mutable node_float_values : (float node_values) option; - mutable node_deps_computed : bool; + mutable node_values : Cvalue.V.t option; + mutable node_range : node_range; + mutable node_writes_computation : (Cil_types.stmt list) computation; + mutable node_reads_computation : (Cil_types.stmt list) computation; + mutable node_writes_stmts : Cil_types.stmt list; } type dependency_kind = Callee | Data | Address | Control | Composition @@ -56,10 +63,21 @@ type dependency_kind = Callee | Data | Address | Control | Composition type dependency = { dependency_key : int; dependency_kind : dependency_kind; - mutable dependency_multiple : bool; + mutable dependency_origins : Cil_types.stmt list; } type graph_diff = { + last_root: node option; added_nodes: node list; removed_nodes: node list; } + +type 'a range = { + backward: 'a; + forward: 'a; +} + +type window = { + perception: int option range; (* depth of exploration *) + horizon: int option range; (* hide beyond horizon ; None for infinite *) +} diff --git a/src/plugins/dive/main.ml b/src/plugins/dive/main.ml index 7638783ac208063427b5dcbdfed54ac28913dcb4..5f728b624ad4dc7de4be2031e01ca37bad432053 100644 --- a/src/plugins/dive/main.ml +++ b/src/plugins/dive/main.ml @@ -24,29 +24,35 @@ type format = Dot | Json let output format context basename = let filename, output_function = match format with - | Dot -> basename ^ ".dot", Imprecision_graph.ouptput_to_dot - | Json -> basename ^ ".json", Imprecision_graph.ouptput_to_json + | Dot -> basename ^ ".dot", Dive_graph.ouptput_to_dot + | Json -> basename ^ ".json", Dive_graph.ouptput_to_json in Self.result "output to %s" filename; let out_channel = open_out filename in - output_function out_channel (Build.get_graph context); + output_function out_channel (Context.get_graph context); close_out out_channel let main () = if not (Self.FromBases.is_empty () && Self.FromFunctionAlarms.is_empty ()) then begin (* Create the initial graph *) - let context = Build.create () in + let context = Context.create () in (* Handle parameters *) - Self.UnfoldedBases.iter (Build.unfold_base context); - Self.HiddenBases.iter (Build.hide_base context); + Self.UnfoldedBases.iter (Context.unfold context); + Self.HiddenBases.iter (Context.hide context); let depth = Self.DepthLimit.get () in (* Add targeted vars to it *) - Self.FromBases.iter (Build.add_var ~depth context); + let add_var vi = + let node = Build.add_var context vi in + Build.explore_backward ~depth context node + in + Self.FromBases.iter add_var; (* Add alarms *) let add_alarm _emitter kf stmt ~rank:_ alarm _code_annot = - if Self.FromFunctionAlarms.mem kf then - Build.add_alarm ~depth context stmt alarm + if Self.FromFunctionAlarms.mem kf then begin + let node = Build.add_alarm context stmt alarm in + Build.explore_backward ~depth context node + end in if not (Self.FromFunctionAlarms.is_empty ()) then Alarms.iter add_alarm; diff --git a/src/plugins/dive/node_kind.ml b/src/plugins/dive/node_kind.ml index 5c6b5979cf3e4eba1ddb71edcf4f073fe240ea62..b52dd75817443a29118981186e3396a1b9db6304 100644 --- a/src/plugins/dive/node_kind.ml +++ b/src/plugins/dive/node_kind.ml @@ -20,7 +20,7 @@ (* *) (**************************************************************************) -open Graph_types +open Dive_types module DatatypeInput = struct @@ -55,8 +55,23 @@ struct LvalStructEq.compare lv1 lv2 <?> (Kinstr.compare, ki1, ki2) | Scattered _, _ -> 1 | _, Scattered _ -> -1 + | Unknown (lv1,ki1), Unknown (lv2,ki2) -> + LvalStructEq.compare lv1 lv2 <?> (Kinstr.compare, ki1, ki2) + | Unknown _, _ -> 1 + | _, Unknown _ -> -1 | Alarm (stmt1, alarm1), Alarm (stmt2, alarm2) -> Stmt.compare stmt1 stmt2 <?> (Alarms.compare, alarm1, alarm2) + | Alarm _, _ -> 1 + | _, Alarm _ -> -1 + | AbsoluteMemory, AbsoluteMemory -> 0 + | AbsoluteMemory, _ -> 1 + | _, AbsoluteMemory -> -1 + | String (i1,_), String (i2,_) -> + Datatype.Int.compare i1 i2 + | String _, _ -> 1 + | _, String _ -> -1 + | Error s1, Error s2 -> + Datatype.String.compare s1 s2 let equal k1 k2 = match k1, k2 with @@ -65,8 +80,13 @@ struct | Composite vi1, Composite vi2 -> Varinfo.equal vi1 vi2 | Scattered (lv1, ki1), Scattered (lv2, ki2) -> LvalStructEq.equal lv1 lv2 && Kinstr.equal ki1 ki2 + | Unknown (lv1, ki1), Unknown (lv2, ki2) -> + LvalStructEq.equal lv1 lv2 && Kinstr.equal ki1 ki2 | Alarm (stmt1, alarm1), Alarm (stmt2, alarm2) -> Stmt.equal stmt1 stmt2 && Alarms.equal alarm1 alarm2 + | AbsoluteMemory, AbsoluteMemory -> true + | String (i,_), String (j,_) -> Datatype.Int.equal i j + | Error s1, Error s2 -> Datatype.String.equal s1 s2 | _ -> false let hash k = @@ -76,8 +96,13 @@ struct | Composite vi -> Hashtbl.hash (2, Varinfo.hash vi) | Scattered (lv, ki) -> Hashtbl.hash (3, LvalStructEq.hash lv, Kinstr.hash ki) + | Unknown (lv, ki) -> + Hashtbl.hash (4, LvalStructEq.hash lv, Kinstr.hash ki) | Alarm (stmt, alarm) -> - Hashtbl.hash (4, Stmt.hash stmt, Alarms.hash alarm) + Hashtbl.hash (5, Stmt.hash stmt, Alarms.hash alarm) + | AbsoluteMemory -> 6 + | String (i, _) -> Hashtbl.hash (7, i) + | Error s -> Hashtbl.hash (8, s) end include Datatype.Make (DatatypeInput) @@ -85,16 +110,26 @@ include Datatype.Make (DatatypeInput) let get_base = function | Scalar (vi,_,_) | Composite (vi) -> Some vi - | Scattered _ | Alarm _ -> None + | Scattered _ | Unknown _ | Alarm _ | AbsoluteMemory | String _ | Error _ -> + None let to_lval = function | Scalar (vi,_typ,offset) -> Some (Cil_types.Var vi, offset) | Composite (vi) -> Some (Cil_types.Var vi, Cil_types.NoOffset) | Scattered (lval,_) -> Some lval - | Alarm (_,_) -> None + | Unknown (lval,_) -> Some lval + | Alarm (_,_) | AbsoluteMemory | String _ | Error _ -> None let pretty fmt = function - | (Scalar _ | Composite _ | Scattered _) as kind -> + | (Scalar _ | Composite _ | Scattered _ | Unknown _) as kind -> Cil_printer.pp_lval fmt (Extlib.the (to_lval kind)) | Alarm (_stmt,alarm) -> Cil_printer.pp_predicate fmt (Alarms.create_predicate alarm) + | AbsoluteMemory -> + Format.fprintf fmt "%s" (Kernel.AbsoluteValidRange.get ()) + | String (_, CSString s) -> + Format.fprintf fmt "%S" s + | String (_, CSWstring s) -> + Format.fprintf fmt "L\"%s\"" (Escape.escape_wstring s) + | Error s -> + Format.fprintf fmt "%s" s diff --git a/src/plugins/dive/node_kind.mli b/src/plugins/dive/node_kind.mli index a65055305c6980a5f09e07fe79305cbded635561..395fb9bf5ad345c08c8f8a86b3aa65041c6a2d4b 100644 --- a/src/plugins/dive/node_kind.mli +++ b/src/plugins/dive/node_kind.mli @@ -20,7 +20,7 @@ (* *) (**************************************************************************) -include Datatype.S with type t = Graph_types.node_kind +include Datatype.S with type t = Dive_types.node_kind val get_base : t -> Cil_types.varinfo option val to_lval : t -> Cil_types.lval option diff --git a/src/plugins/dive/node_range.ml b/src/plugins/dive/node_range.ml new file mode 100644 index 0000000000000000000000000000000000000000..7d5f23709b552ee499b9ba43f95a5d7b2fd713d1 --- /dev/null +++ b/src/plugins/dive/node_range.ml @@ -0,0 +1,109 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Dive_types + +type t = node_range + +let fkind_limits = + let max_single = float_of_string "0x1.fffffep+127" + and max_double = float_of_string "0x1.fffffffffffffp+1023" in + function + | Cil_types.FFloat -> max_single + | FDouble | FLongDouble -> max_double + +let ikind_limits ikind = + let open Cil in + let bits = bitsSizeOfInt ikind in + if isSigned ikind then + (min_signed_number bits, max_signed_number bits) + else + (Integer.zero, max_unsigned_number bits) + +let logscale x limit = + Float.(to_int (min 100. (100. *. log (max x 1.) /. log limit))) + +let log2scale x limit = + assert (x > 0.); + assert (limit > 0.); + Float.(logscale (log x) (log limit)) + +let interval_range l u limit = + let range = + if (l < 0.0) = (u < 0.0) then (* if bounds have same sign *) + u -. l + else + Float.(max (abs u) (abs l)) + in + let s = log2scale range limit in + if s > 98 then Wide else Normal s + +let integer_range ikind l u = + let _, limit = ikind_limits ikind + and l = Integer.to_float l + and u = Integer.to_float u in + interval_range l u (Integer.to_float limit) + +let float_range fkind l u = + let limit = fkind_limits fkind + and l = Fval.F.to_float l + and u = Fval.F.to_float u in + interval_range l u limit + +let default_range n = + Normal (logscale (Integer.to_float n) 100.) + +let evaluate cvalue typ = + let cardinal = Cvalue.V.cardinal cvalue in + match typ, cardinal with + | _, Some card when Integer.is_zero card -> Empty + | _, Some card when Integer.is_one card -> Singleton + | Cil_types.TInt (ikind,_), _ -> + begin match Ival.min_and_max (Cvalue.V.project_ival cvalue) with + | Some l, Some u -> integer_range ikind l u + | _, _ -> Wide + | exception Cvalue.V.Not_based_on_null -> Wide + | exception Abstract_interp.Error_Bottom -> Empty + end + | Cil_types.TFloat (fkind,_), _ -> + begin match Ival.min_and_max_float (Cvalue.V.project_ival cvalue) with + | Some (l, u), _can_be_nan -> float_range fkind l u + | _, _ -> Wide + | exception Cvalue.V.Not_based_on_null -> Wide + | exception Abstract_interp.Error_Bottom -> Empty + end + | _, Some cardinal -> + default_range cardinal + | _, None -> + try + let size = Integer.of_int (Cil.bitsSizeOf typ) in + if Integer.(le size sixteen) + then default_range (Integer.two_power size) + else Wide + with Cil.SizeOfError _ -> Empty + +let upper_bound r1 r2 = + match r1, r2 with + | Empty, r | r, Empty -> r + | Singleton, r | r, Singleton -> r + | Normal i1, Normal i2 -> Normal (max i1 i2) + | Wide, _ | _, Wide -> Wide diff --git a/src/plugins/dive/node_range.mli b/src/plugins/dive/node_range.mli new file mode 100644 index 0000000000000000000000000000000000000000..1f8f3f24b859b1fb14de5ba8ab2a962f8b2b4251 --- /dev/null +++ b/src/plugins/dive/node_range.mli @@ -0,0 +1,26 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +type t = Dive_types.node_range + +val evaluate : Cvalue.V.t -> Cil_types.typ -> t +val upper_bound : t -> t -> t diff --git a/src/plugins/dive/self.ml b/src/plugins/dive/self.ml index 75bc7d1cce238aad412ea6d2bb83842f8b65b297..e83c8f924e328e0ab973b79770def00fff504f1a 100644 --- a/src/plugins/dive/self.ml +++ b/src/plugins/dive/self.ml @@ -48,7 +48,7 @@ module DepthLimit = Int let option_name = "-dive-depth-limit" let help = "Build dependencies up to a depth of N." let arg_name = "N" - let default = 2 + let default = 3 end) module FromFunctionAlarms = Kernel_function_set diff --git a/src/plugins/dive/server_interface.ml b/src/plugins/dive/server_interface.ml index 65179a2d79131c57c561a5d7e9ca3e00228c85f7..b428aa3c26a5505067728761cf476339de4d3d6e 100644 --- a/src/plugins/dive/server_interface.ml +++ b/src/plugins/dive/server_interface.ml @@ -21,179 +21,316 @@ (**************************************************************************) open Server +open Data +open Dive_types -(* TODO: state *) -let get_graph = - let graph = ref None in +let package = Package.package ~plugin:"dive" ~title:"Dive Services" () + + +(* -------------------------------------------------------------------------- *) +(* --- State handling --- *) +(* -------------------------------------------------------------------------- *) + +(* TODO: project state *) +let get_context = + let context = ref None in fun () -> - match !graph with - | Some g -> g + match !context with + | Some c -> c | None -> - let g = Build.create () in - graph := Some g; - g + if Db.Value.is_computed () then + let c = Context.create () in + context := Some c; + c + else + begin + Self.error ~once:true + "A prior Eva analysis is required to build the graphs."; + Server.Data.failure "Eva analysis not computed" + end -let package = Package.package ~plugin:"dive" ~title:"Dive Services" () -module Graph = -struct - type t = Imprecision_graph.t - let jtype = Data.Jany.jtype - let to_json = Imprecision_graph.to_json -end +let global_window = ref { + perception = { backward = Some 2 ; forward = Some 1 }; + horizon = { backward = None ; forward = None }; + } -module GraphDiff = -struct - type t = Imprecision_graph.t * Graph_types.graph_diff - let jtype = Data.Jany.jtype - let to_json = fun (g,d) -> Imprecision_graph.diff_to_json g d -end -module Variable = +(* -------------------------------------------------------------------------- *) +(* --- Data types --- *) +(* -------------------------------------------------------------------------- *) + +module Range : Data.S with type t = int option range = struct - let name = "variableName" - let descr = Markdown.plain "The name of variable of the program" + type t = int option range + let name = "range" + let descr = Markdown.plain "Parametrization of the exploration range." + let sign : t Record.signature = Record.signature () - let signature = Data.Record.signature () + module Fields = + struct + let backward = Record.field sign + ~name:"backward" + ~descr:(Markdown.plain "range for the write dependencies") + (module Joption (Jint)) - let fun_field = Data.Record.option signature - ~name:"funName" - ~descr:(Markdown.plain "owner function for a local variable") - (module Data.Jalpha) + let forward = Record.field sign + ~name:"forward" + ~descr:(Markdown.plain "range for the read dependencies") + (module Joption (Jint)) + end - let var_field = Data.Record.field signature - ~name:"varName" - ~descr:(Markdown.plain "variable name") - (module Data.Jalpha) + module Record = (val Record.publish ~package ~name ~descr sign) - type t = Cil_types.varinfo + let jtype = Record.jtype - let data = Data.Record.publish ~package ~name ~descr signature - module R = (val data : Data.Record.S with type r = t) + let to_json r = + Record.default |> + Record.set Fields.backward r.backward |> + Record.set Fields.forward r.forward |> + Record.to_json - let jtype = R.jtype + let of_json js = + let r = Record.of_json js in + { + backward = Record.get Fields.backward r; + forward = Record.get Fields.forward r; + } +end - let to_json v = - let varname = v.Cil_types.vname in - let fields = R.default |> R.set var_field varname in - let fields = match Kernel_function.find_defining_kf v with - | Some kf -> fields |> R.set fun_field (Some (Kernel_function.get_name kf)) - | None -> fields - in - R.to_json fields - let of_json json = - let open Yojson.Basic.Util in - let funname = - try Some (json |> member "fun" |> to_string) - with Not_found -> None - and varname = json |> member "var" |> to_string in - match funname with - | Some name -> - let kf = - try - Globals.Functions.find_by_name name - with Not_found -> - Data.failure "no function '%s'" name - in - let vi = - try Globals.Vars.find_from_astinfo varname (Cil_types.VLocal kf) - with Not_found -> - try Globals.Vars.find_from_astinfo varname (Cil_types.VFormal kf) - with Not_found -> - Data.failure "no variable '%s' in function '%s'" - varname name - in - vi - | None -> - match - Globals.Syntactic_search.find_in_scope varname Cil_types.Program - with - | Some vi -> vi - | None -> - Data.failure "no global variable '%s'" varname +module Window : Data.S with type t = window = +struct + type t = window + let name = "explorationWindow" + let descr = Markdown.plain "Global parametrization of the exploration." + let sign : t Record.signature = Record.signature () + + module Fields = + struct + let perception = Record.field sign + ~name:"perception" + ~descr:(Markdown.plain "how far dive will explore from root nodes ; \ + must be a finite range") + (module Range) + + let horizon = Record.field sign + ~name:"horizon" + ~descr:(Markdown.plain "range beyond which the nodes must be hidden") + (module Range) + end + + module Record = (val Record.publish ~package ~name ~descr sign) + + let jtype = Record.jtype + + let to_json w = + Record.default |> + Record.set Fields.perception w.perception |> + Record.set Fields.horizon w.horizon |> + Record.to_json + + let of_json js = + let r = Record.of_json js in + { + perception = Record.get Fields.perception r; + horizon = Record.get Fields.horizon r; + } end -module Node : Data.S with type t = Graph_types.node = + +module NodeId = struct - type t = Graph_types.node + type t = node + let name = "nodeId" + let descr = Markdown.plain "A node identifier in the graph" - let jtype = Package.Jindex "dive-node" + let jtype = Data.declare ~package ~name ~descr Data.Jint.jtype - let to_json node = - `Int node.Graph_types.node_key + let _to_json node = + `Int node.node_key let of_json json = - let open Yojson.Basic.Util in - let node_key = to_int json in + let node_key = Data.Jint.of_json json in try - Build.find_node (get_graph ()) node_key + Context.find_node (get_context ()) node_key with Not_found -> Data.failure "no node '%d' in the current graph" node_key end +module Callsite = +struct + let name = "callsite" + let descr = Markdown.plain "A callsite" + let jtype = Data.declare ~package ~name ~descr (Jrecord [ + "fun", Jstring; + "instr", Junion [ Jnumber ; Jstring ]; + ]) +end + +module Callstack = +struct + let name = "callstack" + let descr = Markdown.plain "The callstack context for a node" + let jtype = Data.declare ~package ~name ~descr (Jarray Callsite.jtype) +end + +module NodeLocality = +struct + let name = "nodeLocality" + let descr = Markdown.plain "The description of a node locality" + let jtype = Data.declare ~package ~name ~descr (Jrecord [ + "file", Jstring; + "callstack", Joption (Callstack.jtype) + ]) +end + +module Node = +struct + let name = "node" + let descr = Markdown.plain "A graph node" + let jtype = Data.declare ~package ~name ~descr (Jrecord [ + "id", NodeId.jtype; + "label", Jstring; + "kind", Jstring; + "locality", NodeLocality.jtype; + "is_root", Jboolean; + "backward_explored", Jstring; + "forward_explored", Jstring; + "writes", Jarray Kernel_ast.KfMarker.jtype; + "values", Joption Jstring; + "range", Junion [ Jnumber ; Jstring ]; + "type", Joption Jstring + ]) +end + +module Dependency = +struct + let name = "dependency" + let descr = Markdown.plain "The dependency between two nodes" + let jtype = Data.declare ~package ~name ~descr (Jrecord [ + "id", Jnumber ; + "src", NodeId.jtype ; + "dst", NodeId.jtype ; + "kind", Jstring ; + "origins", Jarray Kernel_ast.KfMarker.jtype + ]) +end + +module Graph = +struct + type t = Dive_graph.t + let name = "graphData" + let descr = Markdown.plain "The whole graph being built" + let jtype = Data.declare ~package ~name ~descr (Jrecord [ + "nodes", Jarray Node.jtype; + "deps", Jarray Dependency.jtype + ]) + + let to_json = Dive_graph.to_json +end + + +module GraphDiff = +struct + type t = Dive_graph.t * graph_diff + let name = "diffData" + let descr = Markdown.plain "Graph differences from the last action." + let jtype = Data.declare ~package ~name ~descr (Jrecord [ + "root", Joption NodeId.jtype; + "add", Jrecord [ + "nodes", Jarray Node.jtype; + "deps", Jarray Dependency.jtype + ]; + "sub", Jarray NodeId.jtype + ]) + + let to_json = fun (g,d) -> Dive_graph.diff_to_json g d +end + + +(* -------------------------------------------------------------------------- *) +(* --- Actions --- *) +(* -------------------------------------------------------------------------- *) + +let result context last_root = + let diff = Context.take_last_diff context in + Context.get_graph context, { diff with last_root } + +let finalize' context node_opt = + begin match node_opt with + | None -> () + | Some node -> + let may_explore f = + Extlib.may (fun depth -> f ~depth context node) + in + may_explore Build.explore_backward !global_window.perception.backward; + may_explore Build.explore_forward !global_window.perception.forward; + let horizon = !global_window.horizon in + if Extlib.has_some horizon.forward || + Extlib.has_some horizon.backward + then + Build.reduce_to_horizon context horizon node + end; + result context node_opt + +let finalize context node = + finalize' context (Some node) + +let () = Request.register ~package + ~kind:`SET ~name:"window" + ~descr:(Markdown.plain "Set the exploration window") + ~input:(module Window) ~output:(module Data.Junit) + (fun window -> global_window := window) + let () = Request.register ~package ~kind:`GET ~name:"graph" ~descr:(Markdown.plain "Retrieve the whole graph") ~input:(module Data.Junit) ~output:(module Graph) - (fun () -> Build.get_graph (get_graph ())) + (fun () -> Context.get_graph (get_context ())) let () = Request.register ~package ~kind:`EXEC ~name:"clear" ~descr:(Markdown.plain "Erase the graph and start over with an empty one") ~input:(module Data.Junit) ~output:(module Data.Junit) - (fun () -> Build.clear (get_graph ())) - -let () = Request.register ~package - ~kind:`EXEC ~name:"addVar" - ~descr:(Markdown.plain "Add a variable to the graph") - ~input:(module Variable) ~output:(module GraphDiff) - begin fun var -> - let depth = Self.DepthLimit.get () in - let g = get_graph () in - Build.add_var ~depth g var; - Build.get_graph g, Build.take_last_differences g - end + (fun () -> Context.clear (get_context ())) let () = Request.register ~package - ~kind:`EXEC ~name:"addFunctionAlarms" - ~descr:(Markdown.plain "Add all alarms of the given function") - ~input:(module Kernel_ast.Kf) ~output:(module GraphDiff) - begin fun kf -> - let depth = Self.DepthLimit.get () in - let g = get_graph () in - Build.add_function_alarms ~depth g kf; - Build.get_graph g, Build.take_last_differences g + ~kind:`EXEC ~name:"add" + ~descr:(Markdown.plain "Add a node to the graph") + ~input:(module Kernel_ast.Marker) ~output:(module GraphDiff) + begin fun loc -> + let context = get_context () in + finalize' context (Build.add_localizable context loc) end let () = Request.register ~package ~kind:`EXEC ~name:"explore" ~descr:(Markdown.plain "Explore the graph starting from an existing vertex") - ~input:(module Node) ~output:(module GraphDiff) + ~input:(module NodeId) ~output:(module GraphDiff) begin fun node -> - let depth = Self.DepthLimit.get () in - let g = get_graph () in - Build.explore_from_node ~depth g node; - Build.get_graph g, Build.take_last_differences g + let context = get_context () in + Build.show context node; + finalize context node end let () = Request.register ~package ~kind:`EXEC ~name:"show" ~descr:(Markdown.plain "Show the dependencies of an existing vertex") - ~input:(module Node) ~output:(module GraphDiff) + ~input:(module NodeId) ~output:(module GraphDiff) begin fun node -> - let depth = Self.DepthLimit.get () in - let g = get_graph () in - Build.show ~depth g node; - Build.get_graph g, Build.take_last_differences g + let context = get_context () in + Build.show context node; + Build.explore_backward ~depth:1 context node; + finalize' context None end let () = Request.register ~package ~kind:`EXEC ~name:"hide" ~descr:(Markdown.plain "Hide the dependencies of an existing vertex") - ~input:(module Node) ~output:(module GraphDiff) + ~input:(module NodeId) ~output:(module GraphDiff) begin fun node -> - let g = get_graph () in - Build.hide g node; - Build.get_graph g, Build.take_last_differences g + let context = get_context () in + Build.hide context node; + finalize' context None end diff --git a/src/plugins/dive/server_interface.mli b/src/plugins/dive/server_interface.mli index 29e37b35e739a47e62bc92e2a91173a62fcc6855..182bc40a8e1af00f6d1329952d449d0f2e6808e1 100644 --- a/src/plugins/dive/server_interface.mli +++ b/src/plugins/dive/server_interface.mli @@ -19,5 +19,3 @@ (* for more details (enclosed in the file licenses/LGPLv2.1). *) (* *) (**************************************************************************) - -module Variable : Server.Data.S with type t = Cil_types.varinfo diff --git a/src/plugins/dive/tests/dive/callstack_global.i b/src/plugins/dive/tests/dive/callstack_global.i new file mode 100644 index 0000000000000000000000000000000000000000..22ef499b33e039a853afd5084f826209478a9d34 --- /dev/null +++ b/src/plugins/dive/tests/dive/callstack_global.i @@ -0,0 +1,17 @@ +/* run.config +STDOPT: +"-dive-from-variables main::r -dive-depth-limit 10" +*/ + +int g; + +float f(float x) { + g = g + x; + return g; +} + +void main(float a) +{ + int x1 = 3; + int x2 = 39; + int r = f(x1) + f(x2); +} diff --git a/src/plugins/dive/tests/dive/const.i b/src/plugins/dive/tests/dive/const.i index 7001746bea33cc57f5d2f0d0a678118be3606e08..622693ee684b1587bd5092c255096d84527beee3 100644 --- a/src/plugins/dive/tests/dive/const.i +++ b/src/plugins/dive/tests/dive/const.i @@ -1,5 +1,5 @@ /* run.config -STDOPT: +"-dive-from-variables main::res -dive-depth-limit 4" +STDOPT: +"-dive-from-variables main::res -dive-depth-limit 5" */ int f(int x, int y) diff --git a/src/plugins/dive/tests/dive/exceptional.i b/src/plugins/dive/tests/dive/exceptional.i new file mode 100644 index 0000000000000000000000000000000000000000..c65084f9539f908baf2ac70c605ddfe4efa437d1 --- /dev/null +++ b/src/plugins/dive/tests/dive/exceptional.i @@ -0,0 +1,24 @@ +/* run.config +STDOPT: +"-absolute-valid-range 0x10000000-0x1fffffff -dive-from-variables main::__retres -dive-depth-limit 5" +*/ + +int* gm(int *p) { return (int *) ((unsigned int) p * 2 / 2); } + + +int main(void) +{ + // String + char *s = "lorem ipsum"; + char c = s[4]; + + // Unknown + int i; + int *p = gm(&i); + int x = *p; + + // Absolute + int *r = 0x10000000; + int a = *r; + + return a + x + c; +} diff --git a/src/plugins/dive/tests/dive/manydeps.i b/src/plugins/dive/tests/dive/manydeps.i new file mode 100644 index 0000000000000000000000000000000000000000..490bd245b01308bb5541fd21021e61eed13c5e92 --- /dev/null +++ b/src/plugins/dive/tests/dive/manydeps.i @@ -0,0 +1,43 @@ +/* run.config +STDOPT: +"-dive-from-variables many_values::__retres,many_writes::x" +*/ + +int t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14; +int *pt[14] = { + &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9, &t10, &t11, &t12, &t13, &t14 +}; + +int many_values(int x) +{ + if (x >= 0 && x < 14) + return *pt[x]; + else + return 0; +} + +int many_writes() +{ + int x = 0; + x += t1; + x += t2; + x += t3; + x += t4; + x += t5; + x += t6; + x += t7; + x += t8; + x += t9; + x += t1; + x += t10; + x += t11; + x += t12; + x += t13; + x += t14; + return x; +} + +void main(int x) +{ + many_values(x); + many_writes(); +} diff --git a/src/plugins/dive/tests/dive/oracle/assigned_param.dot b/src/plugins/dive/tests/dive/oracle/assigned_param.dot index ad2fec86899271793323922c055d7bda9074c910..198c8cfc1b73565cfe4d1c33cf09e0f2dfc30938 100644 --- a/src/plugins/dive/tests/dive/oracle/assigned_param.dot +++ b/src/plugins/dive/tests/dive/oracle/assigned_param.dot @@ -1,22 +1,22 @@ digraph G { - cp0 [label=<w>, shape=box, ]; - cp1 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; - cp3 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp2 [label=<w>, shape=box, style="bold", ]; + cp3 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp7 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; + cp9 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; - subgraph cluster_cs_1 { label=<main>; cp7;cp5;cp0; - subgraph cluster_cs_2 { label=<f>; cp3;cp1; + subgraph cluster_cs_1 { label=<main>; cp9;cp5;cp2; + subgraph cluster_cs_2 { label=<f>; cp7;cp3; }; }; - cp1 -> cp0; - cp3 -> cp1; - cp5 -> cp1; + cp3 -> cp2; + cp5 -> cp3; cp7 -> cp3; + cp9 -> cp7; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/callstack_global.dot b/src/plugins/dive/tests/dive/oracle/callstack_global.dot new file mode 100644 index 0000000000000000000000000000000000000000..82c7a0b0fc6e13df782c630cc9d6619869705d5d --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/callstack_global.dot @@ -0,0 +1,42 @@ +digraph G { + cp2 [label=<r>, shape=box, style="bold", ]; + cp3 [label=<tmp>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp5 [label=<tmp_0>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp7 [label=<__retres>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp9 [label=<__retres>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp11 [label=<g>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp15 [label=<x>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp17 [label=<x2>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp19 [label=<x1>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + + subgraph cluster_cs_1 { label=<main>; cp19;cp17;cp5;cp3;cp2; + subgraph cluster_cs_2 { label=<f>; cp7; + }; + subgraph cluster_cs_3 { label=<f>; cp9; + }; + }; + subgraph cluster_cs_4 { label=<f>; cp15; + }; + subgraph cluster_file_1 { label=<tests/dive/callstack_global.i>; cp11; + }; + + cp3 -> cp2; + cp5 -> cp2; + cp7 -> cp3; + cp9 -> cp5; + cp11 -> cp7; + cp11 -> cp9; + cp11 -> cp11; + cp15 -> cp11; + cp17 -> cp15; + cp19 -> cp15; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/callstack_global.res.oracle b/src/plugins/dive/tests/dive/oracle/callstack_global.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..303837be7b7a5c6bebd4ed9418bbe54f5c46fe67 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/callstack_global.res.oracle @@ -0,0 +1,17 @@ +[kernel] Parsing tests/dive/callstack_global.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 2 functions analyzed (out of 2): 100% coverage. + In these functions, 10 statements reached (out of 10): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 0 alarms generated by the analysis. + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/callstack_global.dot diff --git a/src/plugins/dive/tests/dive/oracle/callstack_strategy.dot b/src/plugins/dive/tests/dive/oracle/callstack_strategy.dot index 2df030b641903b7f93211d1f082a334ffa6e3182..0ef329150cf7afe934680a761cb96bb07b9429ea 100644 --- a/src/plugins/dive/tests/dive/oracle/callstack_strategy.dot +++ b/src/plugins/dive/tests/dive/oracle/callstack_strategy.dot @@ -1,44 +1,44 @@ digraph G { - cp0 [label=<r>, shape=box, ]; - cp1 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp2 [label=<r>, shape=box, style="bold", ]; + cp3 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp3 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp5 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp7 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp7 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp9 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp9 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; - cp11 [label=<a>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp11 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp13 [label=<a>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp13 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp15 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp16 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp18 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - subgraph cluster_cs_1 { label=<g>; cp3;cp1;cp0; - subgraph cluster_cs_4 { label=<f>; cp9; + subgraph cluster_cs_1 { label=<g>; cp5;cp3;cp2; + subgraph cluster_cs_4 { label=<f>; cp11; }; }; - subgraph cluster_cs_2 { label=<i>; cp5; + subgraph cluster_cs_2 { label=<i>; cp7; }; - subgraph cluster_cs_3 { label=<h>; cp16;cp7; - subgraph cluster_cs_6 { label=<f>; cp13; + subgraph cluster_cs_3 { label=<h>; cp18;cp9; + subgraph cluster_cs_6 { label=<f>; cp15; }; }; - subgraph cluster_cs_5 { label=<main>; cp11; + subgraph cluster_cs_5 { label=<main>; cp13; }; - cp1 -> cp0; - cp1 -> cp9; - cp3 -> cp0; - cp5 -> cp1; - cp7 -> cp1; + cp3 -> cp2; + cp3 -> cp11; + cp5 -> cp2; + cp7 -> cp3; cp9 -> cp3; cp11 -> cp5; - cp11 -> cp16; cp13 -> cp7; - cp16 -> cp13; + cp13 -> cp18; + cp15 -> cp9; + cp18 -> cp15; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/const.dot b/src/plugins/dive/tests/dive/oracle/const.dot index db4416d80c8653914d98d9052e3e73972659c567..6ea580760fe17d262fd3be8c9f9c039e10fe7b26 100644 --- a/src/plugins/dive/tests/dive/oracle/const.dot +++ b/src/plugins/dive/tests/dive/oracle/const.dot @@ -1,29 +1,29 @@ digraph G { - cp0 [label=<res>, shape=box, ]; - cp1 [label=<__retres>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp2 [label=<res>, shape=box, style="bold", ]; + cp3 [label=<__retres>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp3 [label=<c>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp5 [label=<c>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp5 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp7 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp7 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp9 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp9 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; - cp11 [label=<i>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled,dotted", ]; + cp11 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp13 [label=<i>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; - subgraph cluster_cs_1 { label=<main>; cp11;cp0; - subgraph cluster_cs_2 { label=<f>; cp9;cp7;cp5;cp3;cp1; + subgraph cluster_cs_1 { label=<main>; cp13;cp2; + subgraph cluster_cs_2 { label=<f>; cp11;cp9;cp7;cp5;cp3; }; }; - cp1 -> cp0; - cp3 -> cp1; - cp5 -> cp1; + cp3 -> cp2; + cp5 -> cp3; cp7 -> cp3; cp9 -> cp5; cp11 -> cp7; - cp11 -> cp9; + cp13 -> cp9; + cp13 -> cp11; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/exceptional.dot b/src/plugins/dive/tests/dive/oracle/exceptional.dot new file mode 100644 index 0000000000000000000000000000000000000000..a512210f1c4dadb0f008955da870206c6de2c373 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/exceptional.dot @@ -0,0 +1,26 @@ +digraph G { + cp2 [label=<__retres>, shape=box, style="bold", ]; + cp3 [label=<a>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<c>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp9 [label=<0x10000000-0x1fffffff>, shape=box3d, fillcolor="#FFBBBB", + color="#FF0000", style="filled", ]; + cp11 [label=<*p>, shape=parallelogram, fillcolor="#FFBBBB", + color="#FF0000", style="filled,dotted", ]; + cp13 [label=<"lorem ipsum">, shape=box3d, fillcolor="#AACCFF", + color="#88AAFF", style="filled", ]; + + subgraph cluster_cs_1 { label=<main>; cp13;cp11;cp9;cp7;cp5;cp3;cp2; + }; + + cp3 -> cp2; + cp5 -> cp2; + cp7 -> cp2; + cp9 -> cp3; + cp11 -> cp5; + cp13 -> cp7; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/exceptional.res.oracle b/src/plugins/dive/tests/dive/oracle/exceptional.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..36f7c9648b859d37d957e47a97582ffb50813080 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/exceptional.res.oracle @@ -0,0 +1,39 @@ +[kernel] Parsing tests/dive/exceptional.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva] tests/dive/exceptional.i:5: + Assigning imprecise value to __retres. + The imprecision originates from Arithmetic {tests/dive/exceptional.i:5} +[eva] tests/dive/exceptional.i:5: + Assigning imprecise value to \result<gm>. + The imprecision originates from Arithmetic {tests/dive/exceptional.i:5} +[eva] tests/dive/exceptional.i:16: + Assigning imprecise value to p. + The imprecision originates from Arithmetic {tests/dive/exceptional.i:5} +[eva:alarm] tests/dive/exceptional.i:17: Warning: + accessing uninitialized left-value. assert \initialized(p); +[eva:alarm] tests/dive/exceptional.i:17: Warning: + out of bounds read. assert \valid_read(p); +[eva:alarm] tests/dive/exceptional.i:23: Warning: + signed overflow. assert -2147483648 ≤ a + x; +[eva:alarm] tests/dive/exceptional.i:23: Warning: + signed overflow. assert a + x ≤ 2147483647; +[eva:alarm] tests/dive/exceptional.i:23: Warning: + signed overflow. assert (int)(a + x) + (int)c ≤ 2147483647; +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 2 functions analyzed (out of 2): 100% coverage. + In these functions, 10 statements reached (out of 10): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 5 alarms generated by the analysis: + 1 invalid memory access + 3 integer overflows + 1 access to uninitialized left-values + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/exceptional.dot diff --git a/src/plugins/dive/tests/dive/oracle/global.dot b/src/plugins/dive/tests/dive/oracle/global.dot index b64b502f0e52418d362f35612dfdadb6b03786fe..262b981a163556c90c69875c2752a5674fe1ec59 100644 --- a/src/plugins/dive/tests/dive/oracle/global.dot +++ b/src/plugins/dive/tests/dive/oracle/global.dot @@ -1,19 +1,20 @@ digraph G { - cp0 [label=<z>, shape=box, ]; - cp1 [label=<g>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp2 [label=<z>, shape=box, style="bold", ]; + cp3 [label=<g>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp3 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp5 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled,dotted", ]; - subgraph cluster_cs_1 { label=<main>; cp5;cp3;cp0; + subgraph cluster_cs_1 { label=<main>; cp7;cp5;cp2; }; - subgraph cluster_file_1 { label=<tests/dive/global.i>; cp1; + subgraph cluster_file_1 { label=<tests/dive/global.i>; cp3; }; - cp1 -> cp0; - cp3 -> cp0; - cp5 -> cp1; + cp3 -> cp2; + cp5 -> cp2; + cp5 -> cp7; + cp7 -> cp3; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/manydeps.dot b/src/plugins/dive/tests/dive/oracle/manydeps.dot new file mode 100644 index 0000000000000000000000000000000000000000..88e1b23c12dd0692aca2aca454387139eeec13d9 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/manydeps.dot @@ -0,0 +1,48 @@ +digraph G { + cp2 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled,dotted,bold", ]; + cp4 [label=<t14>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp6 [label=<t13>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp8 [label=<t12>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp10 [label=<t11>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp12 [label=<t10>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp14 [label=<t1>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp16 [label=<t9>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp18 [label=<t8>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp20 [label=<t7>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp22 [label=<t6>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp24 [label=<__retres>, shape=box, style="bold", ]; + cp25 [label=<*(pt[x])>, shape=parallelogram, fillcolor="#AACCFF", + color="#88AAFF", style="filled,dotted", ]; + + subgraph cluster_cs_1 { label=<many_writes>; cp2; + }; + subgraph cluster_cs_2 { label=<many_values>; cp25;cp24; + }; + subgraph cluster_file_1 { label=<tests/dive/manydeps.i>; cp22;cp20;cp18;cp16;cp14;cp12;cp10;cp8;cp6;cp4; + }; + + cp2 -> cp2 [style="bold", ]; + cp4 -> cp2; + cp6 -> cp2; + cp8 -> cp2; + cp10 -> cp2; + cp12 -> cp2; + cp14 -> cp2; + cp16 -> cp2; + cp18 -> cp2; + cp20 -> cp2; + cp22 -> cp2; + cp25 -> cp24; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/manydeps.res.oracle b/src/plugins/dive/tests/dive/oracle/manydeps.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..8606c7e2651599878fb1055674e83471d58708e3 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/manydeps.res.oracle @@ -0,0 +1,17 @@ +[kernel] Parsing tests/dive/manydeps.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 3 functions analyzed (out of 3): 100% coverage. + In these functions, 32 statements reached (out of 32): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 0 alarms generated by the analysis. + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/manydeps.dot diff --git a/src/plugins/dive/tests/dive/oracle/per_callstack.dot b/src/plugins/dive/tests/dive/oracle/per_callstack.dot index f2c61f4f447e4496dd5764f14775ac53956e6af3..22fa39f4a0bafda9b87dbf7e3f399ab452d241f3 100644 --- a/src/plugins/dive/tests/dive/oracle/per_callstack.dot +++ b/src/plugins/dive/tests/dive/oracle/per_callstack.dot @@ -1,43 +1,42 @@ digraph G { - cp0 [label=<w>, shape=box, ]; - cp1 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp2 [label=<w>, shape=box, style="bold", ]; + cp3 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp3 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp5 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp5 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; - cp7 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp7 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp9 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp11 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp13 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp13 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp15 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp17 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp19 [label=<a>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp19 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp21 [label=<a>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp21 [label=<b>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp23 [label=<b>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp23 [label=<c>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp25 [label=<c>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - subgraph cluster_cs_1 { label=<main>; cp23;cp21;cp19;cp5;cp3;cp1;cp0; - subgraph cluster_cs_2 { label=<f>; cp13;cp7; + subgraph cluster_cs_1 { label=<main>; cp25;cp23;cp21;cp7;cp5;cp3;cp2; + subgraph cluster_cs_2 { label=<f>; cp15;cp9; }; - subgraph cluster_cs_3 { label=<f>; cp15;cp9; + subgraph cluster_cs_3 { label=<f>; cp17;cp11; }; - subgraph cluster_cs_4 { label=<f>; cp17;cp11; + subgraph cluster_cs_4 { label=<f>; cp19;cp13; }; }; - cp1 -> cp0; - cp3 -> cp0; - cp5 -> cp0; - cp7 -> cp1; + cp3 -> cp2; + cp5 -> cp2; + cp7 -> cp2; cp9 -> cp3; cp11 -> cp5; cp13 -> cp7; @@ -46,5 +45,6 @@ digraph G { cp19 -> cp13; cp21 -> cp15; cp23 -> cp17; + cp25 -> cp19; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/pointed_param.dot b/src/plugins/dive/tests/dive/oracle/pointed_param.dot index 29d345ca69b9a03cab6c7453244add21d00feec4..f2713a435f62e45b60876cd9ad143d53675ead1b 100644 --- a/src/plugins/dive/tests/dive/oracle/pointed_param.dot +++ b/src/plugins/dive/tests/dive/oracle/pointed_param.dot @@ -1,29 +1,32 @@ digraph G { - cp0 [label=<y>, shape=box, ]; - cp1 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; + cp2 [label=<y>, shape=box, style="bold", ]; cp3 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp5 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp7 [label=<__retres>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp7 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp9 [label=<__retres>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp9 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled,dotted", ]; + cp11 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp13 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled,dotted", ]; - subgraph cluster_cs_1 { label=<main>; cp1;cp0; - subgraph cluster_cs_2 { label=<f>; cp9;cp3; - subgraph cluster_cs_3 { label=<g>; cp5; - subgraph cluster_cs_4 { label=<h>; cp7; + subgraph cluster_cs_1 { label=<main>; cp13;cp3;cp2; + subgraph cluster_cs_2 { label=<f>; cp11;cp5; + subgraph cluster_cs_3 { label=<g>; cp7; + subgraph cluster_cs_4 { label=<h>; cp9; }; }; }; }; - cp1 -> cp0; - cp3 -> cp1; + cp3 -> cp2; cp5 -> cp3; cp7 -> cp5; cp9 -> cp7; + cp11 -> cp9; + cp13 -> cp11; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/pointers_to_local.dot b/src/plugins/dive/tests/dive/oracle/pointers_to_local.dot index 714f41eccb2a776fad23082a7d007832a0700647..243f81deb0cb5a035debce97194e9b1a338f41d3 100644 --- a/src/plugins/dive/tests/dive/oracle/pointers_to_local.dot +++ b/src/plugins/dive/tests/dive/oracle/pointers_to_local.dot @@ -1,20 +1,20 @@ digraph G { - cp0 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + cp2 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled,bold", ]; + cp3 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - cp1 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", - style="filled", ]; - cp4 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + cp6 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - subgraph cluster_cs_1 { label=<main>; cp0; - subgraph cluster_cs_4 { label=<f2>; cp4; + subgraph cluster_cs_1 { label=<main>; cp2; + subgraph cluster_cs_4 { label=<f2>; cp6; }; }; - cp0 -> cp0; - cp0 -> cp1; - cp0 -> cp4; - cp1 -> cp0; - cp4 -> cp0; + cp2 -> cp2; + cp2 -> cp3; + cp2 -> cp6; + cp3 -> cp2; + cp6 -> cp2; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/ranges.dot b/src/plugins/dive/tests/dive/oracle/ranges.dot new file mode 100644 index 0000000000000000000000000000000000000000..c6e74f511d0dcd51b0654f7e0c8fb395464ba343 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/ranges.dot @@ -0,0 +1,62 @@ +digraph G { + cp2 [label=<res>, shape=box, style="bold", ]; + cp3 [label=<i>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<f>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<i0>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp9 [label=<i1>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp11 [label=<i2>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp13 [label=<i3>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp15 [label=<i4>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp17 [label=<i5>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp19 [label=<i6>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp21 [label=<i7>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp23 [label=<f0>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp25 [label=<f1>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp27 [label=<f2>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp29 [label=<f3>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp31 [label=<f4>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp33 [label=<f5>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp35 [label=<f6>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled", ]; + cp37 [label=<f7>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + + subgraph cluster_cs_1 { label=<main>; cp37;cp35;cp33;cp31;cp29;cp27;cp25;cp23;cp21;cp19;cp17;cp15;cp13;cp11;cp9;cp7;cp5;cp3;cp2; + }; + + cp3 -> cp2; + cp5 -> cp2; + cp7 -> cp3; + cp9 -> cp3; + cp11 -> cp3; + cp13 -> cp3; + cp15 -> cp3; + cp17 -> cp3; + cp19 -> cp3; + cp21 -> cp3; + cp23 -> cp5; + cp25 -> cp5; + cp27 -> cp5; + cp29 -> cp5; + cp31 -> cp5; + cp33 -> cp5; + cp35 -> cp5; + cp37 -> cp5; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/ranges.res.oracle b/src/plugins/dive/tests/dive/oracle/ranges.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..8e49661f1ecdb7d9f620d782685b2b5d9e5fe073 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/ranges.res.oracle @@ -0,0 +1,25 @@ +[kernel] Parsing tests/dive/ranges.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva] using specification for function Frama_C_interval +[eva:alarm] tests/dive/ranges.i:25: Warning: + signed overflow. + assert + (int)((int)((int)((int)((int)((int)(i0 + i1) + i2) + i3) + i4) + i5) + i6) + + i7 ≤ 2147483647; +[eva] using specification for function Frama_C_float_interval +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 1 function analyzed (out of 1): 100% coverage. + In this function, 20 statements reached (out of 20): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 1 alarm generated by the analysis: + 1 integer overflow + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/ranges.dot diff --git a/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot b/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot index 1009dfdd36fcdc4c5b0c148f4f40caca4cfde142..a76ec08dcb670c953f4f2e4e2903ed78d0a5f724 100644 --- a/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot +++ b/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot @@ -1,30 +1,30 @@ digraph G { - cp0 [label=<x>, shape=box, ]; - cp1 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; + cp2 [label=<x>, shape=box, style="bold", ]; cp3 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; cp7 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; + cp9 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; - subgraph cluster_cs_1 { label=<g>; cp0; + subgraph cluster_cs_1 { label=<g>; cp2; }; - subgraph cluster_cs_2 { label=<f3>; cp1; + subgraph cluster_cs_2 { label=<f3>; cp3; }; - subgraph cluster_cs_3 { label=<f2>; cp3; + subgraph cluster_cs_3 { label=<f2>; cp5; }; - subgraph cluster_cs_4 { label=<f1>; cp5; + subgraph cluster_cs_4 { label=<f1>; cp7; }; - subgraph cluster_cs_5 { label=<main>; cp7; + subgraph cluster_cs_5 { label=<main>; cp9; }; - cp1 -> cp0; - cp3 -> cp0; - cp5 -> cp0; - cp7 -> cp1; - cp7 -> cp3; - cp7 -> cp5; + cp3 -> cp2; + cp5 -> cp2; + cp7 -> cp2; + cp9 -> cp3; + cp9 -> cp5; + cp9 -> cp7; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/various.dot b/src/plugins/dive/tests/dive/oracle/various.dot index 14afa37a197041f2c10b38f50cf371264a6fbdd0..201055d7fc3af13c2e9d636048862cdf0ff83857 100644 --- a/src/plugins/dive/tests/dive/oracle/various.dot +++ b/src/plugins/dive/tests/dive/oracle/various.dot @@ -1,65 +1,68 @@ digraph G { - cp0 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; - cp1 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; + cp2 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled,bold", ]; cp3 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - cp7 [label=<g>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + cp5 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp9 [label=<z>, shape=box, ]; - cp10 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp8 [label=<g>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp11 [label=<z>, shape=box, style="bold", ]; + cp12 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp14 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp12 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp16 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp14 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp18 [label=<tmp_0>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp16 [label=<tmp_0>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp21 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp18 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp23 [label=<pf>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - cp21 [label=<pf>, shape=box, ]; - cp23 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp25 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp26 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp28 [label=<f>, shape=box, ]; + cp31 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp30 [label=<is_nan_or_infinite: \is_finite((float)\mul_double((double)y, (double)2.0))>, + cp34 [label=<is_nan_or_infinite: \is_finite((float)\mul_double((double)y, (double)2.0))>, fillcolor="#FF0000", color="#FF0000", shape=doubleoctagon, - style="filled,bold", ]; - cp32 [label=<is_nan_or_infinite: \is_finite(\add_float(y, w))>, + style="filled,bold,bold", ]; + cp36 [label=<is_nan_or_infinite: \is_finite(\add_float(y, w))>, fillcolor="#FF0000", color="#FF0000", shape=doubleoctagon, - style="filled,bold", ]; + style="filled,bold,bold", ]; - subgraph cluster_cs_1 { label=<f>; cp30;cp1;cp0; + subgraph cluster_cs_1 { label=<f>; cp34;cp5;cp2; }; - subgraph cluster_cs_2 { label=<main>; cp32;cp21;cp16;cp12;cp10;cp9;cp3; - subgraph cluster_cs_3 { label=<f>; cp18;cp14; + subgraph cluster_cs_2 { label=<main>; cp36;cp23;cp18;cp14;cp12;cp11;cp3; + subgraph cluster_cs_3 { label=<f>; cp21;cp16; }; - subgraph cluster_cs_4 { label=<f>; cp26;cp23; + subgraph cluster_cs_4 { label=<f>; cp31;cp25; }; }; - subgraph cluster_file_1 { label=<tests/dive/various.i>; cp7; + subgraph cluster_file_1 { label=<tests/dive/various.i>; cp28;cp8; }; - cp0 -> cp1; - cp1 -> cp0; - cp1 -> cp30; - cp3 -> cp0; + cp2 -> cp5; + cp3 -> cp2; cp3 -> cp3; - cp3 -> cp14; - cp3 -> cp23; - cp7 -> cp3; - cp10 -> cp9; - cp10 -> cp32; - cp12 -> cp9; - cp12 -> cp32; - cp14 -> cp10; - cp14 -> cp18; + cp3 -> cp16; + cp3 -> cp25; + cp5 -> cp2; + cp5 -> cp34; + cp8 -> cp3; + cp12 -> cp11; + cp12 -> cp36; + cp14 -> cp11; + cp14 -> cp36; cp16 -> cp12; + cp16 -> cp21; cp18 -> cp14; - cp21 -> cp16 [color="#00FF00", ]; - cp23 -> cp16; - cp23 -> cp26; - cp26 -> cp23; + cp21 -> cp16; + cp23 -> cp18 [color="#00FF00", ]; + cp25 -> cp18; + cp25 -> cp31; + cp28 -> cp23; + cp31 -> cp25; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/pointed_param.i b/src/plugins/dive/tests/dive/pointed_param.i index e6bd4f47db024a2e99baa09be7a14905108f0fe2..197ef953ebf268f3e89ab473fe4ee3616f25f0cc 100644 --- a/src/plugins/dive/tests/dive/pointed_param.i +++ b/src/plugins/dive/tests/dive/pointed_param.i @@ -1,5 +1,5 @@ /* run.config -STDOPT: +"-dive-from-variables main::y -dive-depth-limit 5" +STDOPT: +"-dive-from-variables main::y -dive-depth-limit 6" */ float h(float *p) diff --git a/src/plugins/dive/tests/dive/ranges.i b/src/plugins/dive/tests/dive/ranges.i new file mode 100644 index 0000000000000000000000000000000000000000..221deca7bc7e517558e369c4f582ccb69a75c927 --- /dev/null +++ b/src/plugins/dive/tests/dive/ranges.i @@ -0,0 +1,39 @@ +/* run.config +STDOPT: +"-dive-from-variables main::res -dive-depth-limit 4 -kernel-warn-key parser:decimal-float=inactive" +*/ + +/*@ assigns \result \from min, max; + ensures result_bounded: min <= \result <= max ; + */ +extern int Frama_C_interval(int min, int max); + +/*@ assigns \result \from min, max; + ensures result_bounded: \is_finite(\result) && min <= \result <= max; + */ +extern float Frama_C_float_interval(float min, float max); + +float main() +{ + int i0 = 2; + int i1 = Frama_C_interval(0, 3); + int i2 = Frama_C_interval(0, 12); + int i3 = Frama_C_interval(0, 127); + int i4 = Frama_C_interval(0, 42000); + int i5 = Frama_C_interval(0, 1350000); + int i6 = Frama_C_interval(0, 910000000); + int i7 = Frama_C_interval(0, 2000000000); + int i = i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7; + + float f0 = 0.; + float f1 = Frama_C_float_interval(0.f, 0.001f); + float f2 = Frama_C_float_interval(0.f, 3.14f); + float f3 = Frama_C_float_interval(0.f, 1020.f); + float f4 = Frama_C_float_interval(0.f, 1e7f); + float f5 = Frama_C_float_interval(0.f, 1e20f); + float f6 = Frama_C_float_interval(0.f, 1e36f); + float f7 = Frama_C_float_interval(0.f, 1e37f); + float f = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7; + + float res = i + f; + return res; +} diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in index f0822e881e7c06c5c1ba628e4ecadb3ba71ac3eb..e7380d160ca17283c96d9b3684fdb3a4a7bc0d2c 100644 --- a/src/plugins/e-acsl/Makefile.in +++ b/src/plugins/e-acsl/Makefile.in @@ -106,6 +106,7 @@ PLUGIN_CMO:= src/local_config \ PLUGIN_HAS_MLI:=yes PLUGIN_DISTRIBUTED:=yes +PLUGIN_DEPENDENCIES:= RteGen # We "save" this variable so that it can be used once PLUGIN_DIR has been reset EACSL_PLUGIN_DIR:=$(PLUGIN_DIR) diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog index dc29fbc04f569994f198c1b2dae4f6eb4906b07c..d32d106def951ea26c47935ac6322519fd02208d 100644 --- a/src/plugins/e-acsl/doc/Changelog +++ b/src/plugins/e-acsl/doc/Changelog @@ -25,13 +25,16 @@ Plugin E-ACSL <next-release> ############################ +- E-ACSL [2020-07-28] Add support of bitwise operators. + (frama-c/e-acsl#33) -* E-ACSL [2020-07-20] Fix unstable order of generated globals. (frama-c/e-acsl#124) -* E-ACSL [2020-07-10] Fix translation of trange (incorrect length). -* E-ACSL [2020-07-09] Decrease the number of allocated blocks when one block is freed. -- E-ACSL [2020-06-19] Add support of bitwise operators for C integers. - (frama-c/e-acsl#33) +-* E-ACSL [2020-07-10] Fix translation of trange (incorrect length). +-* E-ACSL [2020-07-09] Decrease the number of allocated blocks when one + block is freed. - E-ACSL [2020-06-19] Add support to create GMP rational from GMP integer. (frama-c/e-acsl#120) -* E-ACSL [2020-06-18] Fix support of VLA memory tracking. diff --git a/src/plugins/e-acsl/doc/refman/changes_modern.tex b/src/plugins/e-acsl/doc/refman/changes_modern.tex index e532cf02b0f6ebd3b2e3aa950c40bfea5881f82e..4266ddd7e32525ee9990447e923a4d91831c2c42 100644 --- a/src/plugins/e-acsl/doc/refman/changes_modern.tex +++ b/src/plugins/e-acsl/doc/refman/changes_modern.tex @@ -126,6 +126,12 @@ in \lstinline|\\at|} \subsection*{Version \eacslpluginversion} +\begin{itemize} +\item \changeinsection{expressions}{support of bitwise operations} +\end{itemize} + +\subsection*{Version Scandium-21} + \begin{itemize} \item \changeinsection{reals}{support of rational numbers and operations} \item \changeinsection{fn-behavior}{remove abrupt clauses from the list of diff --git a/src/plugins/e-acsl/doc/refman/intro_modern.tex b/src/plugins/e-acsl/doc/refman/intro_modern.tex index 8be890311bc013277597ff2790eae605cd7ba441..869c0d0e427e8504a1638ab7d97df20c439366a4 100644 --- a/src/plugins/e-acsl/doc/refman/intro_modern.tex +++ b/src/plugins/e-acsl/doc/refman/intro_modern.tex @@ -48,7 +48,6 @@ currently implemented into the \framac's \eacsl plug-in. \hline terms & \lstinline|\\true| and \lstinline|\\false| \\ - & bitwise operators \\ & let binding \\ & t-sets \\ \hline diff --git a/src/plugins/e-acsl/doc/refman/term.tex b/src/plugins/e-acsl/doc/refman/term.tex index 6af096790d609911bff64586e68fff1e7e26180b..0582703adc5a5512f462b839fd56885c2dc1a268 100644 --- a/src/plugins/e-acsl/doc/refman/term.tex +++ b/src/plugins/e-acsl/doc/refman/term.tex @@ -4,11 +4,11 @@ | real ; real constants | string ; string constants | character ; character constants - \ - bin-op ::= "+" | "-" | "*" | [ "/" ] | [ "%" ] | { "<<" } | { ">>" }; + \ + bin-op ::= "+" | "-" | "*" | [ "/" ] | [ "%" ] | "<<" | ">>"; | "==" | "!=" | "<=" | ">=" | ">" | "<" ; | [ "&&" ] | [ "||" ] | [ "^^" ] ; boolean operations - | { "&" } | { "|" } | { "-->" } | { "<-->" } | "^" ; bitwise operations + | "&" | "|" | "-->" | "<-->" | "^" ; bitwise operations \ unary-op ::= "+" | "-" ; unary plus and minus | "!" ; boolean negation diff --git a/src/plugins/e-acsl/share/e-acsl/e_acsl_gmp_api.h b/src/plugins/e-acsl/share/e-acsl/e_acsl_gmp_api.h index 39946e50b149fcb452fdd858f0dfba24b28ac6c7..38c3716db07567bfd3555ea9b4bff940d2fa23b0 100644 --- a/src/plugins/e-acsl/share/e-acsl/e_acsl_gmp_api.h +++ b/src/plugins/e-acsl/share/e-acsl/e_acsl_gmp_api.h @@ -36,10 +36,11 @@ #include "stdlib.h" #include "e_acsl_alias.h" -#define mpz_struct export_alias(mpz_struct) -#define mpz_t export_alias(mpz_t) -#define mpq_struct export_alias(mpq_struct) -#define mpq_t export_alias(mpq_t) +#define mpz_struct export_alias(mpz_struct) +#define mpz_t export_alias(mpz_t) +#define mpq_struct export_alias(mpq_struct) +#define mpq_t export_alias(mpq_t) +#define mp_bitcnt_t export_alias(mp_bitcnt_t) struct mpz_struct { int _mp_alloc; @@ -58,6 +59,14 @@ struct mpq_struct { typedef struct mpq_struct mpq_struct; typedef mpq_struct (__attribute__((__FC_BUILTIN__)) mpq_t)[1]; +/** + * Counts of bits of a multi-precision number are represented in the C type + * mp_bitcnt_t. Currently this is always an unsigned long, but on some systems + * it will be an unsigned long long in the future. + * @see https://gmplib.org/manual/Nomenclature-and-Types#Nomenclature-and-Types + */ +typedef unsigned long int mp_bitcnt_t; + /****************/ /* Initializers */ /****************/ @@ -262,6 +271,12 @@ extern void __gmpq_sub(mpq_t q1, const mpq_t q2, const mpq_t q3) extern void __gmpz_mul(mpz_t z1, const mpz_t z2, const mpz_t z3) __attribute__((FC_BUILTIN)); +/*@ requires \valid(z1); + @ requires \valid_read(z2); + @ assigns *z1 \from *z2, n; */ +extern void __gmpz_mul_2exp(mpz_t z1, const mpz_t z2, mp_bitcnt_t n) + __attribute__((FC_BUILTIN)); + /*@ requires \valid(q1); @ requires \valid_read(q2); @ requires \valid_read(q3); @@ -283,6 +298,12 @@ extern void __gmpz_tdiv_q(mpz_t z1, const mpz_t z2, const mpz_t z3) extern void __gmpz_tdiv_r(mpz_t z1, const mpz_t z2, const mpz_t z3) __attribute__((FC_BUILTIN)); +/*@ requires \valid(z1); + @ requires \valid_read(z2); + @ assigns *z1 \from *z2, n; */ +extern void __gmpz_tdiv_q_2exp(mpz_t z1, const mpz_t z2, mp_bitcnt_t n) + __attribute__((FC_BUILTIN)); + /*@ requires \valid(q1); @ requires \valid_read(q2); @ requires \valid_read(q3); @@ -294,6 +315,27 @@ extern void __gmpq_div(mpq_t q1, const mpq_t q2, const mpq_t q3) /* Bitwise operators */ /*********************/ +/*@ requires \valid(z1); + @ requires \valid_read(z2); + @ requires \valid_read(z3); + @ assigns *z1 \from *z2, *z3; */ +extern void __gmpz_and(mpz_t z1, const mpz_t z2, const mpz_t z3) + __attribute__((FC_BUILTIN)); + +/*@ requires \valid(z1); + @ requires \valid_read(z2); + @ requires \valid_read(z3); + @ assigns *z1 \from *z2, *z3; */ +extern void __gmpz_ior(mpz_t z1, const mpz_t z2, const mpz_t z3) + __attribute__((FC_BUILTIN)); + +/*@ requires \valid(z1); + @ requires \valid_read(z2); + @ requires \valid_read(z3); + @ assigns *z1 \from *z2, *z3; */ +extern void __gmpz_xor(mpz_t z1, const mpz_t z2, const mpz_t z3) + __attribute__((FC_BUILTIN)); + /*@ requires \valid(z1); @ requires \valid_read(z2); @ assigns *z1 \from *z2; @@ -305,6 +347,18 @@ extern int __gmpz_com(mpz_t z1, const mpz_t z2) /* Coercions to C types */ /************************/ +/** Return non-zero iff the value of z fits in an unsigned long */ +/*@ requires \valid_read(z); + @ assigns \result \from *z; */ +extern int __gmpz_fits_ulong_p(const mpz_t z) + __attribute__((FC_BUILTIN)); + +/** Return non-zero iff the value of z fits in a signed long */ +/*@ requires \valid_read(z); + @ assigns \result \from *z; */ +extern int __gmpz_fits_slong_p(const mpz_t z) + __attribute__((FC_BUILTIN)); + /*@ requires \valid_read(z); @ assigns \result \from *z; */ extern long __gmpz_get_si(const mpz_t z) diff --git a/src/plugins/e-acsl/src/analyses/interval.ml b/src/plugins/e-acsl/src/analyses/interval.ml index 91043aca76ad4b42c112c4f1abe3f10a7c4d0f46..ab451611348a7ad2d828eb43eeac5f8de28ae100 100644 --- a/src/plugins/e-acsl/src/analyses/interval.ml +++ b/src/plugins/e-acsl/src/analyses/interval.ml @@ -53,8 +53,8 @@ module D = Ival.compare i1 i2 | Float (k1, f1), Float (k2, f2) -> (* faster to compare a kind than a float *) - let n = Transitioning.Stdlib.compare k1 k2 in - if n = 0 then Transitioning.Stdlib.compare f1 f2 else n + let n = Stdlib.compare k1 k2 in + if n = 0 then Stdlib.compare f1 f2 else n | Ival _, (Float _ | Rational | Real | Nan) | Float _, (Rational | Real | Nan) | Rational, (Real | Nan) @@ -92,7 +92,7 @@ module D = let is_included i1 i2 = match i1, i2 with | Ival i1, Ival i2 -> Ival.is_included i1 i2 | Float(k1, f1), Float(k2, f2) -> - Transitioning.Stdlib.compare k1 k2 <= 0 + Stdlib.compare k1 k2 <= 0 && (match f1, f2 with | None, None | Some _, None -> true | None, Some _ -> false @@ -126,7 +126,7 @@ let lift_binop ~safe_float f i1 i2 = match i1, i2 with | Ival i1, Ival i2 -> Ival (f i1 i2) | Float(k1, _), Float(k2, _) when safe_float -> - let k = if Transitioning.Stdlib.compare k1 k2 >= 0 then k1 else k2 in + let k = if Stdlib.compare k1 k2 >= 0 then k1 else k2 in Float(k, None (* lost value, if any before *)) | Ival iv, Float(k, _) | Float(k, _), Ival iv -> @@ -147,8 +147,8 @@ let lift_binop ~safe_float f i1 i2 = match i1, i2 with Floating_point.most_negative_single_precision_float, Floating_point.max_single_precision_float | FDouble -> - -. Transitioning.Float.max_float, - Transitioning.Float.max_float + -. Float.max_float, + Float.max_float | FLongDouble -> raise Exit in diff --git a/src/plugins/e-acsl/src/analyses/typing.ml b/src/plugins/e-acsl/src/analyses/typing.ml index ae46e46f67bec0a1f3d2e307daf1d7528d9f48df..a2612ee906f2595622dd37a63cc835b93b44fd9f 100644 --- a/src/plugins/e-acsl/src/analyses/typing.ml +++ b/src/plugins/e-acsl/src/analyses/typing.ml @@ -67,7 +67,7 @@ module D = if i1 = i2 then 0 else if Cil.intTypeIncluded i1 i2 then -1 else 1 | C_float f1, C_float f2 -> - Transitioning.Stdlib.compare f1 f2 + Stdlib.compare f1 f2 | (C_integer _ | C_float _ | Gmpz | Rational | Real), Nan | (C_integer _ | C_float _ | Gmpz | Rational ), Real | (C_integer _ | C_float _ | Gmpz), Rational diff --git a/src/plugins/e-acsl/src/code_generator/constructor.ml b/src/plugins/e-acsl/src/code_generator/constructor.ml index 9c7e60f4979d262a98e8ea79da9a41a0aa9d699d..eb010f92eb65a2cf9cacdd39a3e5ea53a47dd8d4 100644 --- a/src/plugins/e-acsl/src/code_generator/constructor.ml +++ b/src/plugins/e-acsl/src/code_generator/constructor.ml @@ -158,14 +158,9 @@ let mk_mark_readonly vi = let loc = vi.vdecl in mk_rtl_call ~loc "mark_readonly" [ Cil.evar ~loc vi ] -let mk_runtime_check_with_msg ?(reverse=false) ~loc msg kind kf e = +let mk_runtime_check_with_msg ~loc msg kind kf e = let file = (fst loc).Filepath.pos_path in let line = (fst loc).Filepath.pos_lnum in - let e = - if reverse - then e - else Cil.new_exp ~loc:e.eloc (UnOp(LNot, e, Cil.intType)) - in mk_rtl_call ~loc "assert" [ e; @@ -175,13 +170,13 @@ let mk_runtime_check_with_msg ?(reverse=false) ~loc msg kind kf e = Cil.mkString ~loc (Filepath.Normalized.to_pretty_string file); Cil.integer loc line ] -let mk_runtime_check ?(reverse=false) kind kf e p = +let mk_runtime_check kind kf e p = let loc = p.pred_loc in let msg = Kernel.Unicode.without_unicode (Format.asprintf "%a@?" Printer.pp_predicate) p in - mk_runtime_check_with_msg ~reverse ~loc msg kind kf e + mk_runtime_check_with_msg ~loc msg kind kf e (* Local Variables: diff --git a/src/plugins/e-acsl/src/code_generator/constructor.mli b/src/plugins/e-acsl/src/code_generator/constructor.mli index c309ad478045a7e41d6582ae886931bce1b0fdcd..676bfe14af6286cc19179f2d0c8c0aa1a6badc4e 100644 --- a/src/plugins/e-acsl/src/code_generator/constructor.mli +++ b/src/plugins/e-acsl/src/code_generator/constructor.mli @@ -80,16 +80,14 @@ type annotation_kind = | RTE val mk_runtime_check: - ?reverse:bool -> annotation_kind -> kernel_function -> exp -> predicate -> - stmt + annotation_kind -> kernel_function -> exp -> predicate -> stmt (** [mk_runtime_check kind kf e p] generates a runtime check for predicate [p] by building a call to [__e_acsl_assert]. [e] (or [!e] if [reverse] is set to [true]) is the C translation of [p], [kf] is the current kernel_function and [kind] is the annotation kind of [p]. *) val mk_runtime_check_with_msg: - ?reverse:bool -> loc:location -> string -> annotation_kind -> - kernel_function -> exp -> stmt + loc:location -> string -> annotation_kind -> kernel_function -> exp -> stmt (** [mk_runtime_check_with_msg kind kf e msg] generates a runtime check for [e] (or [!e] if [reverse] is [true]) by building a call to [__e_acsl_assert]. [msg] is the message printed if the runtime check fails. [loc] is the diff --git a/src/plugins/e-acsl/src/code_generator/loops.ml b/src/plugins/e-acsl/src/code_generator/loops.ml index fe84f48fc299b5204eeb39a5439468e6b6110a5c..d76bac69fc4f89b825141d83ccc88461fb9f0289 100644 --- a/src/plugins/e-acsl/src/code_generator/loops.ml +++ b/src/plugins/e-acsl/src/code_generator/loops.ml @@ -253,7 +253,7 @@ let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars = | Some p -> let e, env = !named_predicate_ref kf (Env.push env) p in let stmt, env = - Constructor.mk_runtime_check ~reverse:true Constructor.RTE kf e p, env + Constructor.mk_runtime_check Constructor.RTE kf e p, env in let b, env = Env.pop_and_get env stmt ~global_clear:false Env.After in let guard_for_small_type = Cil.mkStmt ~valid_sid:true (Block b) in diff --git a/src/plugins/e-acsl/src/code_generator/mmodel_translate.ml b/src/plugins/e-acsl/src/code_generator/mmodel_translate.ml index 79332b5913879d1d17cb36cc878279dde66f5669..41a1245eed32cade67ec277a16e931e21de66282 100644 --- a/src/plugins/e-acsl/src/code_generator/mmodel_translate.ml +++ b/src/plugins/e-acsl/src/code_generator/mmodel_translate.ml @@ -159,7 +159,7 @@ let gmp_to_sizet ~loc kf env size p = None sizet (fun vi _ -> - [ Constructor.mk_runtime_check ~reverse:true Constructor.RTE kf guard p; + [ Constructor.mk_runtime_check Constructor.RTE kf guard p; Constructor.mk_lib_call ~loc ~result:(Cil.var vi) "__gmpz_get_ui" diff --git a/src/plugins/e-acsl/src/code_generator/translate.ml b/src/plugins/e-acsl/src/code_generator/translate.ml index 33ca70c7a8d81640b6ba20b622b9daa6b8fc6a06..4e310e1b4adfbc577e2aaa98178f4be15ed50693 100644 --- a/src/plugins/e-acsl/src/code_generator/translate.ml +++ b/src/plugins/e-acsl/src/code_generator/translate.ml @@ -57,8 +57,13 @@ let name_of_mpz_arith_bop = function | Mult -> "__gmpz_mul" | Div -> "__gmpz_tdiv_q" | Mod -> "__gmpz_tdiv_r" - | Lt | Gt | Le | Ge | Eq | Ne | BAnd | BXor | BOr | LAnd | LOr - | Shiftlt | Shiftrt | PlusPI | IndexPI | MinusPI | MinusPP -> assert false + | BAnd -> "__gmpz_and" + | BOr -> "__gmpz_ior" + | BXor -> "__gmpz_xor" + | Shiftlt -> "__gmpz_mul_2exp" + | Shiftrt -> "__gmpz_tdiv_q_2exp" + | Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr | PlusPI | IndexPI | MinusPI + | MinusPP -> assert false (* Type of a string that represents a number. Used when a string is required to encode a constant number because it is not @@ -359,8 +364,9 @@ and context_insensitive_term_to_exp kf env t = let guard, env = let name = Misc.name_of_binop bop ^ "_guard" in comparison_to_exp - ~loc kf env Typing.gmpz ~e1:e2 ~name Eq t2 zero t + ~loc kf env Typing.gmpz ~e1:e2 ~name Ne t2 zero t in + let p = Logic_const.prel ~loc (Rneq, t2, zero) in let mk_stmts _v e = assert (Gmp_types.Z.is_t ty); let cond = @@ -368,9 +374,9 @@ and context_insensitive_term_to_exp kf env t = (Env.annotation_kind env) kf guard - (Logic_const.prel ~loc (Req, t2, zero)) + p in - Env.add_assert kf cond (Logic_const.prel (Rneq, t2, zero)); + Env.add_assert kf cond p; let instr = Constructor.mk_lib_call ~loc name [ e; e1; e2 ] in [ cond; instr ] in @@ -396,7 +402,141 @@ and context_insensitive_term_to_exp kf env t = let e1, env = term_to_exp kf env t1 in let e2, env = term_to_exp kf env t2 in if Gmp_types.Z.is_t ty then - not_yet env "left/right shift on arbitrary precision" + (* If the given term is an lvalue variable or a cast from an lvalue + variable, retrieve the name of this variable. Otherwise return + default *) + let rec term_to_name t = + match t.term_node with + | TConst _ -> "cst_" + | TLval (TVar { lv_name }, _) -> lv_name ^ "_" + | TCastE (_, t) -> term_to_name t + | TLogic_coerce (_, t) -> term_to_name t + | _ -> "" + in + let ctx = Typing.get_number_ty t in + let bop_name = Misc.name_of_binop bop in + let e1_name = term_to_name t1 in + let e2_name = term_to_name t2 in + let zero = Logic_const.tinteger 0 in + Typing.type_term ~use_gmp_opt:true ~ctx zero; + + (* Check that e2 is representable in mp_bitcnt_t *) + let coerce_guard, env = + let name = e2_name ^ bop_name ^ "_guard" in + let _vi, e, env = + Env.new_var + ~loc + ~scope:Varname.Block + ~name + env + kf + None + Cil.intType + (fun vi _e -> + let result = Cil.var vi in + let fname = "__gmpz_fits_ulong_p" in + [ Constructor.mk_lib_call ~loc ~result fname [ e2 ] ]) + in + e, env + in + (* Coerce e2 to mp_bitcnt_t *) + let mk_coerce_stmts vi _e = + let coerce_guard_cond = + let max_bitcnt = + Cil.max_unsigned_number (Cil.bitsSizeOf (Gmp_types.bitcnt_t ())) + in + let max_bitcnt_term = Cil.lconstant ~loc max_bitcnt in + let pred = + Logic_const.pand + ~loc + (Logic_const.prel ~loc (Rle, zero, t2), + Logic_const.prel ~loc (Rle, t2, max_bitcnt_term)) + in + let pname = bop_name ^ "_rhs_fits_in_mp_bitcnt_t" in + let pred = { pred with pred_name = pname :: pred.pred_name } in + let cond = Constructor.mk_runtime_check + Constructor.RTE + kf + coerce_guard + pred + in + Env.add_assert kf cond pred; + cond + in + let result = Cil.var vi in + let name = "__gmpz_get_ui" in + let instr = Constructor.mk_lib_call ~loc ~result name [ e2 ] in + [ coerce_guard_cond; instr ] + in + let name = e2_name ^ bop_name ^ "_coerced" in + let _e2_as_bitcnt_vi, e2_as_bitcnt_e, env = + Env.new_var + ~loc + ~scope:Varname.Block + ~name + env + kf + None + (Gmp_types.bitcnt_t ()) + mk_coerce_stmts + in + + (* Create the shift instruction *) + let mk_shift_instr result_e = + let name = name_of_mpz_arith_bop bop in + Constructor.mk_lib_call ~loc name [ result_e; e1; e2_as_bitcnt_e ] + in + + (* Put t in an option to use with comparison_to_exp and + Env.new_var_and_mpz_init *) + let t = Some t in + + (* TODO: let RTE generate the undef behaviors assertions *) + + (* Boolean to choose whether the guard [e1 >= 0] should be added *) + let should_guard_e1 = + match bop with + | Shiftlt -> Kernel.LeftShiftNegative.get () + | Shiftrt -> Kernel.RightShiftNegative.get () + | _ -> assert false + in + + (* Create the statements to initialize [e1 shift e2] *) + let e1_guard_opt, env = + if should_guard_e1 then + (* Future RTE: + if (warn left shift negative and left shift) + or (warn right shift negative and right shift) + then check e1 >= 0 *) + let e1_guard, env = + let name = e1_name ^ bop_name ^ "_guard" in + comparison_to_exp + ~loc kf env Typing.gmpz ~e1 ~name Ge t1 zero t + in + let e1_guard_cond = + let pred = Logic_const.prel ~loc (Rge, t1, zero) in + let cond = Constructor.mk_runtime_check + Constructor.RTE + kf + e1_guard + pred + in + Env.add_assert kf cond pred; + cond + in + Some e1_guard_cond, env + else + None, env + in + let mk_stmts _ e = + let shift_instr = mk_shift_instr e in + match e1_guard_opt with + | None -> [ shift_instr ] + | Some e1_guard -> [ e1_guard; shift_instr ] + in + let name = bop_name in + let _, e, env = Env.new_var_and_mpz_init ~loc ~name env kf t mk_stmts in + e, env, C_number, "" else begin assert (Logic_typing.is_integral_type t.term_type); Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, C_number, "" @@ -426,7 +566,15 @@ and context_insensitive_term_to_exp kf env t = let e1, env = term_to_exp kf env t1 in let e2, env = term_to_exp kf env t2 in if Gmp_types.Z.is_t ty then - not_yet env "bitwise operator on arbitrary precision" + let mk_stmts _v e = + let name = name_of_mpz_arith_bop bop in + let instr = Constructor.mk_lib_call ~loc name [ e; e1; e2 ] in + [ instr ] + in + let name = Misc.name_of_binop bop in + let t = Some t in + let _, e, env = Env.new_var_and_mpz_init ~loc ~name env kf t mk_stmts in + e, env, C_number, "" else begin assert (Logic_typing.is_integral_type t.term_type); Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, C_number, "" @@ -909,7 +1057,6 @@ and translate_named_predicate kf env p = env kf (Constructor.mk_runtime_check - ~reverse:true (Env.annotation_kind env) kf e diff --git a/src/plugins/e-acsl/src/libraries/gmp_types.ml b/src/plugins/e-acsl/src/libraries/gmp_types.ml index 12ce9dcdd6d4510cd607ec45ff3b06f6dd36ac46..6b89bba519386a97b974f290eca81a5e3ba0933b 100644 --- a/src/plugins/e-acsl/src/libraries/gmp_types.ml +++ b/src/plugins/e-acsl/src/libraries/gmp_types.ml @@ -80,6 +80,11 @@ end module Z = Make(struct end) module Q = Make(struct end) +let bitcnt_type_info_ref = mk_dummy_type_info_ref () + +let set_bitcnt_t tinfo = bitcnt_type_info_ref := tinfo +let bitcnt_t () = TNamed(!bitcnt_type_info_ref, []) + (**************************************************************************) (******************* Initialization of mpz and mpq types ******************) (**************************************************************************) @@ -89,14 +94,18 @@ let init () = let set_mp_t = object (self) inherit Cil.nopCilVisitor - (* exit after having initialized the 4 values (for Z.t and Q.t) *) + (* exit after having initialized the 5 values: + - mp_bitcnt_t + - 2 for Z.t + - 2 for Q.t *) + val nb_to_visit = 5 val mutable visited = 0 method private set f info = f info; - if visited = 3 then + visited <- visited + 1; + if visited = nb_to_visit then raise Exit else begin - visited <- visited + 1; Cil.SkipChildren end @@ -106,6 +115,7 @@ let init () = else if name = "__e_acsl_mpz_struct" then self#set Z.set_t_struct info else if name = "__e_acsl_mpq_t" then self#set Q.set_t info else if name = "__e_acsl_mpq_struct" then self#set Q.set_t_struct info + else if name = "__e_acsl_mp_bitcnt_t" then self#set set_bitcnt_t info else Cil.SkipChildren | _ -> Cil.SkipChildren diff --git a/src/plugins/e-acsl/src/libraries/gmp_types.mli b/src/plugins/e-acsl/src/libraries/gmp_types.mli index c556b9120d917a83a631ebc812c8723976f74a7d..3c9db48c536ba039e7e9bba155e2824170828161 100644 --- a/src/plugins/e-acsl/src/libraries/gmp_types.mli +++ b/src/plugins/e-acsl/src/libraries/gmp_types.mli @@ -54,6 +54,10 @@ module Z: S (** Representation of the rational type at runtime *) module Q: S +val bitcnt_t: unit -> typ +(** @return the C Type representing the count of bits of a multi-precision + number at runtime *) + (* Local Variables: compile-command: "make -C ../../../../.." diff --git a/src/plugins/e-acsl/tests/arith/bitwise.c b/src/plugins/e-acsl/tests/arith/bitwise.c index a47aba968c95d5816444f76c1500b325194a644c..7dfa73a7c444bbd24f5911e3abde2010c53ed6db 100644 --- a/src/plugins/e-acsl/tests/arith/bitwise.c +++ b/src/plugins/e-acsl/tests/arith/bitwise.c @@ -1,6 +1,7 @@ /* run.config_ci, run.config_dev COMMENT: Support of bitwise operations STDOPT: #"-warn-right-shift-negative -warn-left-shift-negative" + MACRO: ROOT_EACSL_GCC_FC_EXTRA_EXT -warn-right-shift-negative -warn-left-shift-negative */ #include <limits.h> @@ -42,8 +43,6 @@ void g_signed(int a, int b) { /*@ assert ((ULLONG_MAX + 1) << 1) != 0; */ /*@ assert ((ULLONG_MAX + 1) >> 1) != 0; */ - /*@ assert ((LLONG_MIN - 1) << 1) != 0; */ - /*@ assert ((LLONG_MIN - 1) >> 1) != 0; */ /*@ assert (1 << 65) != 0; */ /*@ assert ((ULLONG_MAX + 1) | (LLONG_MIN - 1)) != 0; */ /*@ assert ((ULLONG_MAX + 1) & (LLONG_MIN - 1)) != diff --git a/src/plugins/e-acsl/tests/arith/oracle_ci/bitwise.res.oracle b/src/plugins/e-acsl/tests/arith/oracle_ci/bitwise.res.oracle index d3656a3e7da8fdc8710c080e25eee4cc81f9efe2..80897296cd13ef45a7198ce08d4fc3841450606d 100644 --- a/src/plugins/e-acsl/tests/arith/oracle_ci/bitwise.res.oracle +++ b/src/plugins/e-acsl/tests/arith/oracle_ci/bitwise.res.oracle @@ -1,66 +1,38 @@ [e-acsl] beginning translation. -[e-acsl] tests/arith/bitwise.c:39: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:41: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:43: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:44: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:45: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:46: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:47: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:48: Warning: - E-ACSL construct `bitwise operator on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:49: Warning: - E-ACSL construct `bitwise operator on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:56: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:58: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:60: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:61: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:62: Warning: - E-ACSL construct `left/right shift on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:63: Warning: - E-ACSL construct `bitwise operator on arbitrary precision' - is not yet supported. - Ignoring annotation. -[e-acsl] tests/arith/bitwise.c:64: Warning: - E-ACSL construct `bitwise operator on arbitrary precision' - is not yet supported. - Ignoring annotation. [e-acsl] translation done in project "e-acsl". +[eva:alarm] tests/arith/bitwise.c:40: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:40: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:42: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:42: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:44: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:45: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:46: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:47: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:48: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:55: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:55: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:57: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:57: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:59: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:60: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:61: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:62: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva:alarm] tests/arith/bitwise.c:63: Warning: + function __e_acsl_assert: precondition got status unknown. diff --git a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_arith.c b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_arith.c index 6677db53488571ae0cb179d7c6504944131f9cd8..249a2e3366637540cf22f0fbbc67c8468d49a72d 100644 --- a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_arith.c +++ b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_arith.c @@ -39,8 +39,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__2)); __gmpz_init(__gen_e_acsl_div); /*@ assert E_ACSL: 0xffffffffffffffffffffff ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_div_guard == 0),"Assertion","main", - "0xffffffffffffffffffffff == 0","tests/arith/arith.i",18); + __e_acsl_assert(__gen_e_acsl_div_guard != 0,"Assertion","main", + "0xffffffffffffffffffffff != 0","tests/arith/arith.i",18); __gmpz_tdiv_q(__gen_e_acsl_div, (__e_acsl_mpz_struct const *)(__gen_e_acsl_), (__e_acsl_mpz_struct const *)(__gen_e_acsl_)); @@ -108,8 +108,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__6)); __gmpz_init(__gen_e_acsl_div_2); /*@ assert E_ACSL: y - 123456789123456789 ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_div_guard_2 == 0),"Assertion","main", - "y - 123456789123456789 == 0","tests/arith/arith.i",34); + __e_acsl_assert(__gen_e_acsl_div_guard_2 != 0,"Assertion","main", + "y - 123456789123456789 != 0","tests/arith/arith.i",34); __gmpz_tdiv_q(__gen_e_acsl_div_2, (__e_acsl_mpz_struct const *)(__gen_e_acsl_add), (__e_acsl_mpz_struct const *)(__gen_e_acsl__5)); diff --git a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_bitwise.c b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_bitwise.c index 7e230971466517b86b4844888116d3dd4359a0ba..e96f4973e6c3c735ac875d125d378d469f0fd181 100644 --- a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_bitwise.c +++ b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_bitwise.c @@ -5,27 +5,27 @@ void f_signed(int a, int b) { int c = a << 2; __e_acsl_assert(0 <= a,"RTE","f_signed","shift: 0 <= a", - "tests/arith/bitwise.c",11); + "tests/arith/bitwise.c",12); __e_acsl_assert((long)c == a << 2L,"Assertion","f_signed","c == a << 2", - "tests/arith/bitwise.c",11); + "tests/arith/bitwise.c",12); /*@ assert c ≡ a << 2; */ ; int d = b >> 2; __e_acsl_assert(0 <= b,"RTE","f_signed","shift: 0 <= b", - "tests/arith/bitwise.c",13); + "tests/arith/bitwise.c",14); __e_acsl_assert(d == b >> 2,"Assertion","f_signed","d == b >> 2", - "tests/arith/bitwise.c",13); + "tests/arith/bitwise.c",14); /*@ assert d ≡ b >> 2; */ ; int e = a | b; __e_acsl_assert((long)e == (a | (long)b),"Assertion","f_signed", - "e == (a | b)","tests/arith/bitwise.c",15); + "e == (a | b)","tests/arith/bitwise.c",16); /*@ assert e ≡ (a | b); */ ; int f = a & b; __e_acsl_assert((long)f == (a & (long)b),"Assertion","f_signed", - "f == (a & b)","tests/arith/bitwise.c",17); + "f == (a & b)","tests/arith/bitwise.c",18); /*@ assert f ≡ (a & b); */ ; int g = a ^ b; __e_acsl_assert((long)g == (a ^ (long)b),"Assertion","f_signed", - "g == (a ^ b)","tests/arith/bitwise.c",19); + "g == (a ^ b)","tests/arith/bitwise.c",20); /*@ assert g ≡ (a ^ b); */ ; return; } @@ -34,23 +34,23 @@ void f_unsigned(unsigned int a, unsigned int b) { unsigned int c = a << 2u; __e_acsl_assert((unsigned long)c == a << 2UL,"Assertion","f_unsigned", - "c == a << 2","tests/arith/bitwise.c",25); + "c == a << 2","tests/arith/bitwise.c",26); /*@ assert c ≡ a << 2; */ ; unsigned int d = b >> 2u; __e_acsl_assert(d == b >> 2U,"Assertion","f_unsigned","d == b >> 2", - "tests/arith/bitwise.c",27); + "tests/arith/bitwise.c",28); /*@ assert d ≡ b >> 2; */ ; unsigned int e = a | b; __e_acsl_assert(e == (a | b),"Assertion","f_unsigned","e == (a | b)", - "tests/arith/bitwise.c",29); + "tests/arith/bitwise.c",30); /*@ assert e ≡ (a | b); */ ; unsigned int f = a & b; __e_acsl_assert(f == (a & b),"Assertion","f_unsigned","f == (a & b)", - "tests/arith/bitwise.c",31); + "tests/arith/bitwise.c",32); /*@ assert f ≡ (a & b); */ ; unsigned int g = a ^ b; __e_acsl_assert(g == (a ^ b),"Assertion","f_unsigned","g == (a ^ b)", - "tests/arith/bitwise.c",33); + "tests/arith/bitwise.c",34); /*@ assert g ≡ (a ^ b); */ ; return; } @@ -58,19 +58,312 @@ void f_unsigned(unsigned int a, unsigned int b) void g_signed(int a, int b) { int c = a << b; + { + __e_acsl_mpz_t __gen_e_acsl_c; + __e_acsl_mpz_t __gen_e_acsl_a; + __e_acsl_mpz_t __gen_e_acsl_b; + int __gen_e_acsl_b_shiftl_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_b_shiftl_coerced; + __e_acsl_mpz_t __gen_e_acsl_; + int __gen_e_acsl_a_shiftl_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftl; + int __gen_e_acsl_eq; + __gmpz_init_set_si(__gen_e_acsl_c,(long)c); + __gmpz_init_set_si(__gen_e_acsl_a,(long)a); + __gmpz_init_set_si(__gen_e_acsl_b,(long)b); + __gen_e_acsl_b_shiftl_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl_b)); + /*@ assert + E_ACSL: shiftl_rhs_fits_in_mp_bitcnt_t: + 0 ≤ b ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_b_shiftl_guard,"RTE","g_signed", + "shiftl_rhs_fits_in_mp_bitcnt_t: 0 <= b <= 18446744073709551615", + "tests/arith/bitwise.c",40); + __gen_e_acsl_b_shiftl_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl_b)); + __gmpz_init_set_si(__gen_e_acsl_,0L); + __gen_e_acsl_a_shiftl_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_a), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_)); + __gmpz_init(__gen_e_acsl_shiftl); + /*@ assert E_ACSL: a ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_a_shiftl_guard >= 0,"RTE","g_signed", + "a >= 0","tests/arith/bitwise.c",40); + __gmpz_mul_2exp(__gen_e_acsl_shiftl, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_a), + __gen_e_acsl_b_shiftl_coerced); + __gen_e_acsl_eq = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_c), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftl)); + __e_acsl_assert(__gen_e_acsl_eq == 0,"Assertion","g_signed", + "c == a << b","tests/arith/bitwise.c",40); + __gmpz_clear(__gen_e_acsl_c); + __gmpz_clear(__gen_e_acsl_a); + __gmpz_clear(__gen_e_acsl_b); + __gmpz_clear(__gen_e_acsl_); + __gmpz_clear(__gen_e_acsl_shiftl); + } /*@ assert c ≡ a << b; */ ; int d = a >> b; + { + __e_acsl_mpz_t __gen_e_acsl_d; + __e_acsl_mpz_t __gen_e_acsl_a_2; + __e_acsl_mpz_t __gen_e_acsl_b_2; + int __gen_e_acsl_b_shiftr_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_b_shiftr_coerced; + __e_acsl_mpz_t __gen_e_acsl__2; + int __gen_e_acsl_a_shiftr_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftr; + int __gen_e_acsl_eq_2; + __gmpz_init_set_si(__gen_e_acsl_d,(long)d); + __gmpz_init_set_si(__gen_e_acsl_a_2,(long)a); + __gmpz_init_set_si(__gen_e_acsl_b_2,(long)b); + __gen_e_acsl_b_shiftr_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl_b_2)); + /*@ assert + E_ACSL: shiftr_rhs_fits_in_mp_bitcnt_t: + 0 ≤ b ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_b_shiftr_guard,"RTE","g_signed", + "shiftr_rhs_fits_in_mp_bitcnt_t: 0 <= b <= 18446744073709551615", + "tests/arith/bitwise.c",42); + __gen_e_acsl_b_shiftr_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl_b_2)); + __gmpz_init_set_si(__gen_e_acsl__2,0L); + __gen_e_acsl_a_shiftr_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_a_2), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__2)); + __gmpz_init(__gen_e_acsl_shiftr); + /*@ assert E_ACSL: a ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_a_shiftr_guard >= 0,"RTE","g_signed", + "a >= 0","tests/arith/bitwise.c",42); + __gmpz_tdiv_q_2exp(__gen_e_acsl_shiftr, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_a_2), + __gen_e_acsl_b_shiftr_coerced); + __gen_e_acsl_eq_2 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_d), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftr)); + __e_acsl_assert(__gen_e_acsl_eq_2 == 0,"Assertion","g_signed", + "d == a >> b","tests/arith/bitwise.c",42); + __gmpz_clear(__gen_e_acsl_d); + __gmpz_clear(__gen_e_acsl_a_2); + __gmpz_clear(__gen_e_acsl_b_2); + __gmpz_clear(__gen_e_acsl__2); + __gmpz_clear(__gen_e_acsl_shiftr); + } /*@ assert d ≡ a >> b; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__3; + __e_acsl_mpz_t __gen_e_acsl__4; + __e_acsl_mpz_t __gen_e_acsl_add; + int __gen_e_acsl_cst_shiftl_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_cst_shiftl_coerced; + __e_acsl_mpz_t __gen_e_acsl__5; + int __gen_e_acsl_shiftl_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftl_2; + int __gen_e_acsl_ne; + __gmpz_init_set_ui(__gen_e_acsl__3,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__4,1L); + __gmpz_init(__gen_e_acsl_add); + __gmpz_add(__gen_e_acsl_add, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__3), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__4)); + __gen_e_acsl_cst_shiftl_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl__4)); + /*@ assert + E_ACSL: shiftl_rhs_fits_in_mp_bitcnt_t: + 0 ≤ 1 ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_cst_shiftl_guard,"RTE","g_signed", + "shiftl_rhs_fits_in_mp_bitcnt_t: 0 <= 1 <= 18446744073709551615", + "tests/arith/bitwise.c",44); + __gen_e_acsl_cst_shiftl_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl__4)); + __gmpz_init_set_si(__gen_e_acsl__5,0L); + __gen_e_acsl_shiftl_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_add), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__5)); + __gmpz_init(__gen_e_acsl_shiftl_2); + /*@ assert E_ACSL: 18446744073709551615ULL + 1 ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_shiftl_guard >= 0,"RTE","g_signed", + "18446744073709551615ULL + 1 >= 0", + "tests/arith/bitwise.c",44); + __gmpz_mul_2exp(__gen_e_acsl_shiftl_2, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add), + __gen_e_acsl_cst_shiftl_coerced); + __gen_e_acsl_ne = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftl_2), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__5)); + __e_acsl_assert(__gen_e_acsl_ne != 0,"Assertion","g_signed", + "(18446744073709551615ULL + 1) << 1 != 0", + "tests/arith/bitwise.c",44); + __gmpz_clear(__gen_e_acsl__3); + __gmpz_clear(__gen_e_acsl__4); + __gmpz_clear(__gen_e_acsl_add); + __gmpz_clear(__gen_e_acsl__5); + __gmpz_clear(__gen_e_acsl_shiftl_2); + } /*@ assert (18446744073709551615ULL + 1) << 1 ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__6; + __e_acsl_mpz_t __gen_e_acsl__7; + __e_acsl_mpz_t __gen_e_acsl_add_2; + int __gen_e_acsl_cst_shiftr_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_cst_shiftr_coerced; + __e_acsl_mpz_t __gen_e_acsl__8; + int __gen_e_acsl_shiftr_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftr_2; + unsigned long __gen_e_acsl__9; + __gmpz_init_set_ui(__gen_e_acsl__6,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__7,1L); + __gmpz_init(__gen_e_acsl_add_2); + __gmpz_add(__gen_e_acsl_add_2, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__6), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__7)); + __gen_e_acsl_cst_shiftr_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl__7)); + /*@ assert + E_ACSL: shiftr_rhs_fits_in_mp_bitcnt_t: + 0 ≤ 1 ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_cst_shiftr_guard,"RTE","g_signed", + "shiftr_rhs_fits_in_mp_bitcnt_t: 0 <= 1 <= 18446744073709551615", + "tests/arith/bitwise.c",45); + __gen_e_acsl_cst_shiftr_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl__7)); + __gmpz_init_set_si(__gen_e_acsl__8,0L); + __gen_e_acsl_shiftr_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_add_2), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__8)); + __gmpz_init(__gen_e_acsl_shiftr_2); + /*@ assert E_ACSL: 18446744073709551615ULL + 1 ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_shiftr_guard >= 0,"RTE","g_signed", + "18446744073709551615ULL + 1 >= 0", + "tests/arith/bitwise.c",45); + __gmpz_tdiv_q_2exp(__gen_e_acsl_shiftr_2, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_2), + __gen_e_acsl_cst_shiftr_coerced); + __gen_e_acsl__9 = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftr_2)); + __e_acsl_assert(__gen_e_acsl__9 != 0UL,"Assertion","g_signed", + "(18446744073709551615ULL + 1) >> 1 != 0", + "tests/arith/bitwise.c",45); + __gmpz_clear(__gen_e_acsl__6); + __gmpz_clear(__gen_e_acsl__7); + __gmpz_clear(__gen_e_acsl_add_2); + __gmpz_clear(__gen_e_acsl__8); + __gmpz_clear(__gen_e_acsl_shiftr_2); + } /*@ assert (18446744073709551615ULL + 1) >> 1 ≢ 0; */ ; - /*@ assert ((-9223372036854775807LL - 1LL) - 1) << 1 ≢ 0; */ ; - /*@ assert ((-9223372036854775807LL - 1LL) - 1) >> 1 ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__10; + __e_acsl_mpz_t __gen_e_acsl__11; + int __gen_e_acsl_cst_shiftl_guard_2; + __e_acsl_mp_bitcnt_t __gen_e_acsl_cst_shiftl_coerced_2; + __e_acsl_mpz_t __gen_e_acsl__12; + int __gen_e_acsl_cst_shiftl_guard_3; + __e_acsl_mpz_t __gen_e_acsl_shiftl_3; + int __gen_e_acsl_ne_2; + __gmpz_init_set_si(__gen_e_acsl__10,1L); + __gmpz_init_set_si(__gen_e_acsl__11,65L); + __gen_e_acsl_cst_shiftl_guard_2 = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl__11)); + /*@ assert + E_ACSL: shiftl_rhs_fits_in_mp_bitcnt_t: + 0 ≤ 65 ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_cst_shiftl_guard_2,"RTE","g_signed", + "shiftl_rhs_fits_in_mp_bitcnt_t: 0 <= 65 <= 18446744073709551615", + "tests/arith/bitwise.c",46); + __gen_e_acsl_cst_shiftl_coerced_2 = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl__11)); + __gmpz_init_set_si(__gen_e_acsl__12,0L); + __gen_e_acsl_cst_shiftl_guard_3 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl__10), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__12)); + __gmpz_init(__gen_e_acsl_shiftl_3); + /*@ assert E_ACSL: 1 ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_cst_shiftl_guard_3 >= 0,"RTE","g_signed", + "1 >= 0","tests/arith/bitwise.c",46); + __gmpz_mul_2exp(__gen_e_acsl_shiftl_3, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__10), + __gen_e_acsl_cst_shiftl_coerced_2); + __gen_e_acsl_ne_2 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftl_3), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__12)); + __e_acsl_assert(__gen_e_acsl_ne_2 != 0,"Assertion","g_signed", + "1 << 65 != 0","tests/arith/bitwise.c",46); + __gmpz_clear(__gen_e_acsl__10); + __gmpz_clear(__gen_e_acsl__11); + __gmpz_clear(__gen_e_acsl__12); + __gmpz_clear(__gen_e_acsl_shiftl_3); + } /*@ assert 1 << 65 ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__13; + __e_acsl_mpz_t __gen_e_acsl__14; + __e_acsl_mpz_t __gen_e_acsl_add_3; + __e_acsl_mpz_t __gen_e_acsl__15; + __e_acsl_mpz_t __gen_e_acsl_sub; + __e_acsl_mpz_t __gen_e_acsl_bor; + __e_acsl_mpz_t __gen_e_acsl__16; + int __gen_e_acsl_ne_3; + __gmpz_init_set_ui(__gen_e_acsl__13,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__14,1L); + __gmpz_init(__gen_e_acsl_add_3); + __gmpz_add(__gen_e_acsl_add_3, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__13), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__14)); + __gmpz_init_set_si(__gen_e_acsl__15,-9223372036854775807L - 1L); + __gmpz_init(__gen_e_acsl_sub); + __gmpz_sub(__gen_e_acsl_sub, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__15), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__14)); + __gmpz_init(__gen_e_acsl_bor); + __gmpz_ior(__gen_e_acsl_bor, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_3), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_sub)); + __gmpz_init_set_si(__gen_e_acsl__16,0L); + __gen_e_acsl_ne_3 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_bor), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__16)); + __e_acsl_assert(__gen_e_acsl_ne_3 != 0,"Assertion","g_signed", + "((18446744073709551615ULL + 1) | ((-9223372036854775807LL - 1LL) - 1)) != 0", + "tests/arith/bitwise.c",47); + __gmpz_clear(__gen_e_acsl__13); + __gmpz_clear(__gen_e_acsl__14); + __gmpz_clear(__gen_e_acsl_add_3); + __gmpz_clear(__gen_e_acsl__15); + __gmpz_clear(__gen_e_acsl_sub); + __gmpz_clear(__gen_e_acsl_bor); + __gmpz_clear(__gen_e_acsl__16); + } /*@ assert ((18446744073709551615ULL + 1) | ((-9223372036854775807LL - 1LL) - 1)) ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__17; + __e_acsl_mpz_t __gen_e_acsl__18; + __e_acsl_mpz_t __gen_e_acsl_add_4; + __e_acsl_mpz_t __gen_e_acsl__19; + __e_acsl_mpz_t __gen_e_acsl_sub_2; + __e_acsl_mpz_t __gen_e_acsl_band; + __e_acsl_mpz_t __gen_e_acsl_bxor; + int __gen_e_acsl_ne_4; + __gmpz_init_set_ui(__gen_e_acsl__17,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__18,1L); + __gmpz_init(__gen_e_acsl_add_4); + __gmpz_add(__gen_e_acsl_add_4, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__17), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__18)); + __gmpz_init_set_si(__gen_e_acsl__19,-9223372036854775807L - 1L); + __gmpz_init(__gen_e_acsl_sub_2); + __gmpz_sub(__gen_e_acsl_sub_2, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__19), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__18)); + __gmpz_init(__gen_e_acsl_band); + __gmpz_and(__gen_e_acsl_band, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_4), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_sub_2)); + __gmpz_init(__gen_e_acsl_bxor); + __gmpz_xor(__gen_e_acsl_bxor, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_4), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_sub_2)); + __gen_e_acsl_ne_4 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_band), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_bxor)); + __e_acsl_assert(__gen_e_acsl_ne_4 != 0,"Assertion","g_signed", + "((18446744073709551615ULL + 1) & ((-9223372036854775807LL - 1LL) - 1)) !=\n((18446744073709551615ULL + 1) ^ ((-9223372036854775807LL - 1LL) - 1))", + "tests/arith/bitwise.c",48); + __gmpz_clear(__gen_e_acsl__17); + __gmpz_clear(__gen_e_acsl__18); + __gmpz_clear(__gen_e_acsl_add_4); + __gmpz_clear(__gen_e_acsl__19); + __gmpz_clear(__gen_e_acsl_sub_2); + __gmpz_clear(__gen_e_acsl_band); + __gmpz_clear(__gen_e_acsl_bxor); + } /*@ assert ((18446744073709551615ULL + 1) & ((-9223372036854775807LL - 1LL) - 1)) ≢ @@ -82,13 +375,290 @@ void g_signed(int a, int b) void g_unsigned(unsigned int a, unsigned int b) { unsigned int c = a << b; + { + __e_acsl_mpz_t __gen_e_acsl_c; + __e_acsl_mpz_t __gen_e_acsl_a; + __e_acsl_mpz_t __gen_e_acsl_b; + int __gen_e_acsl_b_shiftl_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_b_shiftl_coerced; + __e_acsl_mpz_t __gen_e_acsl_; + int __gen_e_acsl_a_shiftl_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftl; + int __gen_e_acsl_eq; + __gmpz_init_set_ui(__gen_e_acsl_c,(unsigned long)c); + __gmpz_init_set_ui(__gen_e_acsl_a,(unsigned long)a); + __gmpz_init_set_ui(__gen_e_acsl_b,(unsigned long)b); + __gen_e_acsl_b_shiftl_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl_b)); + /*@ assert + E_ACSL: shiftl_rhs_fits_in_mp_bitcnt_t: + 0 ≤ b ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_b_shiftl_guard,"RTE","g_unsigned", + "shiftl_rhs_fits_in_mp_bitcnt_t: 0 <= b <= 18446744073709551615", + "tests/arith/bitwise.c",55); + __gen_e_acsl_b_shiftl_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl_b)); + __gmpz_init_set_si(__gen_e_acsl_,0L); + __gen_e_acsl_a_shiftl_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_a), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_)); + __gmpz_init(__gen_e_acsl_shiftl); + /*@ assert E_ACSL: a ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_a_shiftl_guard >= 0,"RTE","g_unsigned", + "a >= 0","tests/arith/bitwise.c",55); + __gmpz_mul_2exp(__gen_e_acsl_shiftl, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_a), + __gen_e_acsl_b_shiftl_coerced); + __gen_e_acsl_eq = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_c), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftl)); + __e_acsl_assert(__gen_e_acsl_eq == 0,"Assertion","g_unsigned", + "c == a << b","tests/arith/bitwise.c",55); + __gmpz_clear(__gen_e_acsl_c); + __gmpz_clear(__gen_e_acsl_a); + __gmpz_clear(__gen_e_acsl_b); + __gmpz_clear(__gen_e_acsl_); + __gmpz_clear(__gen_e_acsl_shiftl); + } /*@ assert c ≡ a << b; */ ; unsigned int d = a >> b; + { + __e_acsl_mpz_t __gen_e_acsl_d; + __e_acsl_mpz_t __gen_e_acsl_a_2; + __e_acsl_mpz_t __gen_e_acsl_b_2; + int __gen_e_acsl_b_shiftr_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_b_shiftr_coerced; + __e_acsl_mpz_t __gen_e_acsl__2; + int __gen_e_acsl_a_shiftr_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftr; + int __gen_e_acsl_eq_2; + __gmpz_init_set_ui(__gen_e_acsl_d,(unsigned long)d); + __gmpz_init_set_ui(__gen_e_acsl_a_2,(unsigned long)a); + __gmpz_init_set_ui(__gen_e_acsl_b_2,(unsigned long)b); + __gen_e_acsl_b_shiftr_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl_b_2)); + /*@ assert + E_ACSL: shiftr_rhs_fits_in_mp_bitcnt_t: + 0 ≤ b ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_b_shiftr_guard,"RTE","g_unsigned", + "shiftr_rhs_fits_in_mp_bitcnt_t: 0 <= b <= 18446744073709551615", + "tests/arith/bitwise.c",57); + __gen_e_acsl_b_shiftr_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl_b_2)); + __gmpz_init_set_si(__gen_e_acsl__2,0L); + __gen_e_acsl_a_shiftr_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_a_2), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__2)); + __gmpz_init(__gen_e_acsl_shiftr); + /*@ assert E_ACSL: a ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_a_shiftr_guard >= 0,"RTE","g_unsigned", + "a >= 0","tests/arith/bitwise.c",57); + __gmpz_tdiv_q_2exp(__gen_e_acsl_shiftr, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_a_2), + __gen_e_acsl_b_shiftr_coerced); + __gen_e_acsl_eq_2 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_d), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftr)); + __e_acsl_assert(__gen_e_acsl_eq_2 == 0,"Assertion","g_unsigned", + "d == a >> b","tests/arith/bitwise.c",57); + __gmpz_clear(__gen_e_acsl_d); + __gmpz_clear(__gen_e_acsl_a_2); + __gmpz_clear(__gen_e_acsl_b_2); + __gmpz_clear(__gen_e_acsl__2); + __gmpz_clear(__gen_e_acsl_shiftr); + } /*@ assert d ≡ a >> b; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__3; + __e_acsl_mpz_t __gen_e_acsl__4; + __e_acsl_mpz_t __gen_e_acsl_add; + int __gen_e_acsl_cst_shiftl_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_cst_shiftl_coerced; + __e_acsl_mpz_t __gen_e_acsl__5; + int __gen_e_acsl_shiftl_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftl_2; + int __gen_e_acsl_ne; + __gmpz_init_set_ui(__gen_e_acsl__3,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__4,1L); + __gmpz_init(__gen_e_acsl_add); + __gmpz_add(__gen_e_acsl_add, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__3), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__4)); + __gen_e_acsl_cst_shiftl_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl__4)); + /*@ assert + E_ACSL: shiftl_rhs_fits_in_mp_bitcnt_t: + 0 ≤ 1u ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_cst_shiftl_guard,"RTE","g_unsigned", + "shiftl_rhs_fits_in_mp_bitcnt_t: 0 <= 1u <= 18446744073709551615", + "tests/arith/bitwise.c",59); + __gen_e_acsl_cst_shiftl_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl__4)); + __gmpz_init_set_si(__gen_e_acsl__5,0L); + __gen_e_acsl_shiftl_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_add), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__5)); + __gmpz_init(__gen_e_acsl_shiftl_2); + /*@ assert E_ACSL: 18446744073709551615ULL + 1u ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_shiftl_guard >= 0,"RTE","g_unsigned", + "18446744073709551615ULL + 1u >= 0", + "tests/arith/bitwise.c",59); + __gmpz_mul_2exp(__gen_e_acsl_shiftl_2, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add), + __gen_e_acsl_cst_shiftl_coerced); + __gen_e_acsl_ne = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftl_2), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__5)); + __e_acsl_assert(__gen_e_acsl_ne != 0,"Assertion","g_unsigned", + "(18446744073709551615ULL + 1u) << 1u != 0", + "tests/arith/bitwise.c",59); + __gmpz_clear(__gen_e_acsl__3); + __gmpz_clear(__gen_e_acsl__4); + __gmpz_clear(__gen_e_acsl_add); + __gmpz_clear(__gen_e_acsl__5); + __gmpz_clear(__gen_e_acsl_shiftl_2); + } /*@ assert (18446744073709551615ULL + 1u) << 1u ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__6; + __e_acsl_mpz_t __gen_e_acsl__7; + __e_acsl_mpz_t __gen_e_acsl_add_2; + int __gen_e_acsl_cst_shiftr_guard; + __e_acsl_mp_bitcnt_t __gen_e_acsl_cst_shiftr_coerced; + __e_acsl_mpz_t __gen_e_acsl__8; + int __gen_e_acsl_shiftr_guard; + __e_acsl_mpz_t __gen_e_acsl_shiftr_2; + unsigned long __gen_e_acsl__9; + __gmpz_init_set_ui(__gen_e_acsl__6,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__7,1L); + __gmpz_init(__gen_e_acsl_add_2); + __gmpz_add(__gen_e_acsl_add_2, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__6), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__7)); + __gen_e_acsl_cst_shiftr_guard = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl__7)); + /*@ assert + E_ACSL: shiftr_rhs_fits_in_mp_bitcnt_t: + 0 ≤ 1u ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_cst_shiftr_guard,"RTE","g_unsigned", + "shiftr_rhs_fits_in_mp_bitcnt_t: 0 <= 1u <= 18446744073709551615", + "tests/arith/bitwise.c",60); + __gen_e_acsl_cst_shiftr_coerced = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl__7)); + __gmpz_init_set_si(__gen_e_acsl__8,0L); + __gen_e_acsl_shiftr_guard = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_add_2), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__8)); + __gmpz_init(__gen_e_acsl_shiftr_2); + /*@ assert E_ACSL: 18446744073709551615ULL + 1u ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_shiftr_guard >= 0,"RTE","g_unsigned", + "18446744073709551615ULL + 1u >= 0", + "tests/arith/bitwise.c",60); + __gmpz_tdiv_q_2exp(__gen_e_acsl_shiftr_2, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_2), + __gen_e_acsl_cst_shiftr_coerced); + __gen_e_acsl__9 = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftr_2)); + __e_acsl_assert(__gen_e_acsl__9 != 0UL,"Assertion","g_unsigned", + "(18446744073709551615ULL + 1u) >> 1u != 0", + "tests/arith/bitwise.c",60); + __gmpz_clear(__gen_e_acsl__6); + __gmpz_clear(__gen_e_acsl__7); + __gmpz_clear(__gen_e_acsl_add_2); + __gmpz_clear(__gen_e_acsl__8); + __gmpz_clear(__gen_e_acsl_shiftr_2); + } /*@ assert (18446744073709551615ULL + 1u) >> 1u ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__10; + __e_acsl_mpz_t __gen_e_acsl__11; + int __gen_e_acsl_cst_shiftl_guard_2; + __e_acsl_mp_bitcnt_t __gen_e_acsl_cst_shiftl_coerced_2; + __e_acsl_mpz_t __gen_e_acsl__12; + int __gen_e_acsl_cst_shiftl_guard_3; + __e_acsl_mpz_t __gen_e_acsl_shiftl_3; + int __gen_e_acsl_ne_2; + __gmpz_init_set_si(__gen_e_acsl__10,1L); + __gmpz_init_set_si(__gen_e_acsl__11,65L); + __gen_e_acsl_cst_shiftl_guard_2 = __gmpz_fits_ulong_p((__e_acsl_mpz_struct const *)(__gen_e_acsl__11)); + /*@ assert + E_ACSL: shiftl_rhs_fits_in_mp_bitcnt_t: + 0 ≤ 65u ≤ 18446744073709551615; + */ + __e_acsl_assert(__gen_e_acsl_cst_shiftl_guard_2,"RTE","g_unsigned", + "shiftl_rhs_fits_in_mp_bitcnt_t: 0 <= 65u <= 18446744073709551615", + "tests/arith/bitwise.c",61); + __gen_e_acsl_cst_shiftl_coerced_2 = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl__11)); + __gmpz_init_set_si(__gen_e_acsl__12,0L); + __gen_e_acsl_cst_shiftl_guard_3 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl__10), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__12)); + __gmpz_init(__gen_e_acsl_shiftl_3); + /*@ assert E_ACSL: 1u ≥ 0; */ + __e_acsl_assert(__gen_e_acsl_cst_shiftl_guard_3 >= 0,"RTE","g_unsigned", + "1u >= 0","tests/arith/bitwise.c",61); + __gmpz_mul_2exp(__gen_e_acsl_shiftl_3, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__10), + __gen_e_acsl_cst_shiftl_coerced_2); + __gen_e_acsl_ne_2 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_shiftl_3), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__12)); + __e_acsl_assert(__gen_e_acsl_ne_2 != 0,"Assertion","g_unsigned", + "1u << 65u != 0","tests/arith/bitwise.c",61); + __gmpz_clear(__gen_e_acsl__10); + __gmpz_clear(__gen_e_acsl__11); + __gmpz_clear(__gen_e_acsl__12); + __gmpz_clear(__gen_e_acsl_shiftl_3); + } /*@ assert 1u << 65u ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__13; + __e_acsl_mpz_t __gen_e_acsl__14; + __e_acsl_mpz_t __gen_e_acsl_add_3; + __e_acsl_mpz_t __gen_e_acsl_bor; + __e_acsl_mpz_t __gen_e_acsl__15; + int __gen_e_acsl_ne_3; + __gmpz_init_set_ui(__gen_e_acsl__13,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__14,1L); + __gmpz_init(__gen_e_acsl_add_3); + __gmpz_add(__gen_e_acsl_add_3, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__13), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__14)); + __gmpz_init(__gen_e_acsl_bor); + __gmpz_ior(__gen_e_acsl_bor, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_3), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__14)); + __gmpz_init_set_si(__gen_e_acsl__15,0L); + __gen_e_acsl_ne_3 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_bor), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__15)); + __e_acsl_assert(__gen_e_acsl_ne_3 != 0,"Assertion","g_unsigned", + "((18446744073709551615ULL + 1u) | 1u) != 0", + "tests/arith/bitwise.c",62); + __gmpz_clear(__gen_e_acsl__13); + __gmpz_clear(__gen_e_acsl__14); + __gmpz_clear(__gen_e_acsl_add_3); + __gmpz_clear(__gen_e_acsl_bor); + __gmpz_clear(__gen_e_acsl__15); + } /*@ assert ((18446744073709551615ULL + 1u) | 1u) ≢ 0; */ ; + { + __e_acsl_mpz_t __gen_e_acsl__16; + __e_acsl_mpz_t __gen_e_acsl__17; + __e_acsl_mpz_t __gen_e_acsl_add_4; + __e_acsl_mpz_t __gen_e_acsl_band; + __e_acsl_mpz_t __gen_e_acsl_bxor; + int __gen_e_acsl_ne_4; + __gmpz_init_set_ui(__gen_e_acsl__16,18446744073709551615UL); + __gmpz_init_set_si(__gen_e_acsl__17,1L); + __gmpz_init(__gen_e_acsl_add_4); + __gmpz_add(__gen_e_acsl_add_4, + (__e_acsl_mpz_struct const *)(__gen_e_acsl__16), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__17)); + __gmpz_init(__gen_e_acsl_band); + __gmpz_and(__gen_e_acsl_band, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_4), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__17)); + __gmpz_init(__gen_e_acsl_bxor); + __gmpz_xor(__gen_e_acsl_bxor, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_4), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__17)); + __gen_e_acsl_ne_4 = __gmpz_cmp((__e_acsl_mpz_struct const *)(__gen_e_acsl_band), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_bxor)); + __e_acsl_assert(__gen_e_acsl_ne_4 != 0,"Assertion","g_unsigned", + "((18446744073709551615ULL + 1u) & 1u) !=\n((18446744073709551615ULL + 1u) ^ 1u)", + "tests/arith/bitwise.c",63); + __gmpz_clear(__gen_e_acsl__16); + __gmpz_clear(__gen_e_acsl__17); + __gmpz_clear(__gen_e_acsl_add_4); + __gmpz_clear(__gen_e_acsl_band); + __gmpz_clear(__gen_e_acsl_bxor); + } /*@ assert ((18446744073709551615ULL + 1u) & 1u) ≢ diff --git a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_longlong.c b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_longlong.c index 7ee8de540851b4cfc13f744c22e4e0271e5e0561..0720b6503aa754f974b9df3eb49a3e708e503dd3 100644 --- a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_longlong.c +++ b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_longlong.c @@ -54,8 +54,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__3)); __gmpz_init(__gen_e_acsl_mod); /*@ assert E_ACSL: 2 ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_mod_guard == 0),"Assertion","main", - "2 == 0","tests/arith/longlong.i",17); + __e_acsl_assert(__gen_e_acsl_mod_guard != 0,"Assertion","main","2 != 0", + "tests/arith/longlong.i",17); __gmpz_tdiv_r(__gen_e_acsl_mod, (__e_acsl_mpz_struct const *)(__gen_e_acsl_add), (__e_acsl_mpz_struct const *)(__gen_e_acsl_)); diff --git a/src/plugins/e-acsl/tests/gmp-only/oracle_ci/gen_arith.c b/src/plugins/e-acsl/tests/gmp-only/oracle_ci/gen_arith.c index 9899c99482fd3a9ee9baba785b2fe903ce547253..9db467e05fce5f01652cb191a4ef603018c77832 100644 --- a/src/plugins/e-acsl/tests/gmp-only/oracle_ci/gen_arith.c +++ b/src/plugins/e-acsl/tests/gmp-only/oracle_ci/gen_arith.c @@ -160,8 +160,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__11)); __gmpz_init(__gen_e_acsl_div); /*@ assert E_ACSL: 3 ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_div_guard == 0),"Assertion","main", - "3 == 0","tests/gmp-only/arith.i",17); + __e_acsl_assert(__gen_e_acsl_div_guard != 0,"Assertion","main","3 != 0", + "tests/gmp-only/arith.i",17); __gmpz_tdiv_q(__gen_e_acsl_div, (__e_acsl_mpz_struct const *)(__gen_e_acsl_x_6), (__e_acsl_mpz_struct const *)(__gen_e_acsl__10)); @@ -194,8 +194,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__14)); __gmpz_init(__gen_e_acsl_div_2); /*@ assert E_ACSL: 0xffffffffffffffffffffff ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_div_guard_2 == 0),"Assertion","main", - "0xffffffffffffffffffffff == 0","tests/gmp-only/arith.i", + __e_acsl_assert(__gen_e_acsl_div_guard_2 != 0,"Assertion","main", + "0xffffffffffffffffffffff != 0","tests/gmp-only/arith.i", 18); __gmpz_tdiv_q(__gen_e_acsl_div_2, (__e_acsl_mpz_struct const *)(__gen_e_acsl__13), @@ -228,8 +228,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__17)); __gmpz_init(__gen_e_acsl_mod); /*@ assert E_ACSL: 2 ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_mod_guard == 0),"Assertion","main", - "2 == 0","tests/gmp-only/arith.i",19); + __e_acsl_assert(__gen_e_acsl_mod_guard != 0,"Assertion","main","2 != 0", + "tests/gmp-only/arith.i",19); __gmpz_tdiv_r(__gen_e_acsl_mod, (__e_acsl_mpz_struct const *)(__gen_e_acsl_x_7), (__e_acsl_mpz_struct const *)(__gen_e_acsl__16)); @@ -273,8 +273,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__21)); __gmpz_init(__gen_e_acsl_mod_2); /*@ assert E_ACSL: -2 ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_mod_guard_2 == 0),"Assertion","main", - "-2 == 0","tests/gmp-only/arith.i",20); + __e_acsl_assert(__gen_e_acsl_mod_guard_2 != 0,"Assertion","main", + "-2 != 0","tests/gmp-only/arith.i",20); __gmpz_tdiv_r(__gen_e_acsl_mod_2, (__e_acsl_mpz_struct const *)(__gen_e_acsl_neg_8), (__e_acsl_mpz_struct const *)(__gen_e_acsl_neg_9)); @@ -315,8 +315,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__25)); __gmpz_init(__gen_e_acsl_mod_3); /*@ assert E_ACSL: -2 ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_mod_guard_3 == 0),"Assertion","main", - "-2 == 0","tests/gmp-only/arith.i",21); + __e_acsl_assert(__gen_e_acsl_mod_guard_3 != 0,"Assertion","main", + "-2 != 0","tests/gmp-only/arith.i",21); __gmpz_tdiv_r(__gen_e_acsl_mod_3, (__e_acsl_mpz_struct const *)(__gen_e_acsl__23), (__e_acsl_mpz_struct const *)(__gen_e_acsl_neg_11)); @@ -574,8 +574,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__55)); __gmpz_init(__gen_e_acsl_div_3); /*@ assert E_ACSL: y ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_div_guard_3 == 0),"Assertion","main", - "y == 0","tests/gmp-only/arith.i",31); + __e_acsl_assert(__gen_e_acsl_div_guard_3 != 0,"Assertion","main", + "y != 0","tests/gmp-only/arith.i",31); __gmpz_tdiv_q(__gen_e_acsl_div_3, (__e_acsl_mpz_struct const *)(__gen_e_acsl__54), (__e_acsl_mpz_struct const *)(__gen_e_acsl_y_2)); @@ -620,8 +620,8 @@ int main(void) (__e_acsl_mpz_struct const *)(__gen_e_acsl__59)); __gmpz_init(__gen_e_acsl_div_4); /*@ assert E_ACSL: y - 123456789123456789 ≢ 0; */ - __e_acsl_assert(! (__gen_e_acsl_div_guard_4 == 0),"Assertion","main", - "y - 123456789123456789 == 0","tests/gmp-only/arith.i", + __e_acsl_assert(__gen_e_acsl_div_guard_4 != 0,"Assertion","main", + "y - 123456789123456789 != 0","tests/gmp-only/arith.i", 34); __gmpz_tdiv_q(__gen_e_acsl_div_4, (__e_acsl_mpz_struct const *)(__gen_e_acsl_add_5), diff --git a/src/plugins/e-acsl/tests/memory/local_init.c b/src/plugins/e-acsl/tests/memory/local_init.c index 3f088213229d0076f623744b7ba65444efd8ce00..483a08acdeca626a3024b416c9922f1356da660c 100644 --- a/src/plugins/e-acsl/tests/memory/local_init.c +++ b/src/plugins/e-acsl/tests/memory/local_init.c @@ -1,7 +1,7 @@ /* run.config_ci COMMENT: test of a local initializer which contains an annotation LOG: gen_@PTEST_NAME@.c - STDOPT: #"-lib-entry -eva -e-acsl-prepare -e-acsl-share ./share/e-acsl -then -no-lib-entry" + STDOPT: #"@MACHDEP@ @EACSL_PREPARE@ -lib-entry -eva -then -no-lib-entry" */ int X = 0; diff --git a/src/plugins/e-acsl/tests/special/e-acsl-valid.c b/src/plugins/e-acsl/tests/special/e-acsl-valid.c index 437c1230d3af2162a07800f1fcfd67dea8072677..8cdb54fbcec93c33303e8fdebfa537406b57fb98 100644 --- a/src/plugins/e-acsl/tests/special/e-acsl-valid.c +++ b/src/plugins/e-acsl/tests/special/e-acsl-valid.c @@ -1,6 +1,6 @@ /* run.config_ci, run.config_dev COMMENT: test option -e-acsl-no-valid - STDOPT: #"-e-acsl-prepare -e-acsl-share ./share/e-acsl -eva -eva-verbose 0 -then -e-acsl-no-valid" + STDOPT: #"@MACHDEP@ @EACSL_PREPARE@ -eva -eva-verbose 0 -then -no-eva -e-acsl-no-valid" MACRO: ROOT_EACSL_GCC_FC_EXTRA_EXT -eva -eva-verbose 0 MACRO: ROOT_EACSL_GCC_OPTS_EXT --then --e-acsl-extra -e-acsl-no-valid */ diff --git a/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle b/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle index 8a26335fee12d916f5bdb57b0a8a437933335926..504ec1ce5e1e7ec93e53ba27fd59cb2f6fd01097 100644 --- a/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle +++ b/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle @@ -1,5 +1,3 @@ -[eva:alarm] tests/special/e-acsl-valid.c:37: Warning: - function f: precondition \valid(y) got status unknown. [eva:alarm] tests/special/e-acsl-valid.c:37: Warning: function f: precondition \valid(y) got status unknown. [e-acsl] beginning translation. diff --git a/src/plugins/e-acsl/tests/test_config_ci.in b/src/plugins/e-acsl/tests/test_config_ci.in index 7bf6bf598f0dca59d1ae6b98e7ade86a58bcceef..0a68607fc713abce8bf5ac09479dd477ff07e1dd 100644 --- a/src/plugins/e-acsl/tests/test_config_ci.in +++ b/src/plugins/e-acsl/tests/test_config_ci.in @@ -1,5 +1,7 @@ MACRO: DEST @PTEST_RESULT@/gen_@PTEST_NAME@ -MACRO: GLOBAL -machdep gcc_x86_64 -variadic-no-translation -verbose 0 +MACRO: MACHDEP -machdep gcc_x86_64 +MACRO: EACSL_PREPARE -e-acsl-prepare -e-acsl-share ./share/e-acsl +MACRO: GLOBAL @MACHDEP@ -variadic-no-translation -verbose 0 MACRO: EACSL -e-acsl -e-acsl-share ./share/e-acsl -e-acsl-verbose 1 MACRO: EVA -eva -eva-no-alloc-returns-null -eva-no-results -eva-no-print -eva-warn-key libc:unsupported-spec=inactive MACRO: EVENTUALLY -print -ocode @DEST@.c -load-script ./tests/print.cmxs diff --git a/src/plugins/gui/gtk_helper.mli b/src/plugins/gui/gtk_helper.mli index 2d2aad9b3d4c1af83cb72d104299de79909de1c8..18b41177870c626626a67afe3571c8922cafc36b 100644 --- a/src/plugins/gui/gtk_helper.mli +++ b/src/plugins/gui/gtk_helper.mli @@ -151,7 +151,7 @@ module Configuration: sig val config_values : key:string -> default:'a -> values:('a * string) list -> 'a #selector -> unit (** The [values] field is used as a dictionary of available values. - They are compared with [Pervasives.(=)]. *) + They are compared with [Stdlib.(=)]. *) end diff --git a/src/plugins/gui/pretty_source.ml b/src/plugins/gui/pretty_source.ml index 6cd8a1f06f72d9e737ecc105de7f43e6f1cf138a..fae7a1f6c75b9dad6da3f7d676e01af913aca608 100644 --- a/src/plugins/gui/pretty_source.ml +++ b/src/plugins/gui/pretty_source.ml @@ -164,8 +164,8 @@ struct if (pe1 = pe2) then 0 else (* most englobing comes first *) - Transitioning.Stdlib.compare pe2 pe1 - else Transitioning.Stdlib.compare pb1 pb2 + Stdlib.compare pe2 pe1 + else Stdlib.compare pb1 pb2 ) arr ; arr @@ -275,14 +275,14 @@ let localizable_from_locs state ~file ~line = let buffer_formatter state source = let starts = Stack.create () in let emit_open_tag s = - let s = Transitioning.Format.string_of_stag s in + let s = Extlib.format_string_of_stag s in (* Ignore tags that are not ours *) if Extlib.string_prefix "guitag:" s then Stack.push (source#end_iter#offset, Tag.get s) starts ; "" in let emit_close_tag s = - let s = Transitioning.Format.string_of_stag s in + let s = Extlib.format_string_of_stag s in (try if Extlib.string_prefix "guitag:" s then let (p,sid) = Stack.pop starts in @@ -295,7 +295,7 @@ let buffer_formatter state source = Format.pp_set_tags gtk_fmt true; Format.pp_set_print_tags gtk_fmt false; Format.pp_set_mark_tags gtk_fmt true; - let open Transitioning.Format in + let open Format in pp_set_formatter_stag_functions gtk_fmt {(pp_get_formatter_stag_functions gtk_fmt ()) with diff --git a/src/plugins/gui/warning_manager.ml b/src/plugins/gui/warning_manager.ml index 7083cff3e897b5287d409eb001a400ed31f74ab1..e76ee827ccfe9ce32cc7e9c077d086f4fab67af1 100644 --- a/src/plugins/gui/warning_manager.ml +++ b/src/plugins/gui/warning_manager.ml @@ -34,7 +34,7 @@ type t = module Data = Indexer.Make( struct type t = int*row - let compare (x,_) (y,_) = Transitioning.Stdlib.compare x y + let compare (x,_) (y,_) = Stdlib.compare x y end) let make ~packing ~callback = diff --git a/src/plugins/gui/wtext.ml b/src/plugins/gui/wtext.ml index fff094c660d5ad8c1ff7a4edbcc78551e4ca8919..7bfe557c97514b4af670d6cb0b61f7de097c69dc 100644 --- a/src/plugins/gui/wtext.ml +++ b/src/plugins/gui/wtext.ml @@ -244,7 +244,7 @@ class text ?(autoscroll=false) ?(width=80) ?(indent=60) () = end method private open_tag name = - let name = Transitioning.Format.string_of_stag name in + let name = Extlib.format_string_of_stag name in self#flush () ; style <- self#tag name :: style ; "" method private close_tag _name = @@ -255,7 +255,7 @@ class text ?(autoscroll=false) ?(width=80) ?(indent=60) () = | (TAG _ | PLAIN) :: sty -> style <- sty ; "" method fmt = match fmtref with Some fmt -> fmt | None -> - let open Transitioning.Format in + let open Format in let output_string s a b = if b > 0 then Buffer.add_substring text s a b in let fmt = Format.make_formatter output_string self#flush in let tagger = pp_get_formatter_stag_functions fmt () in @@ -308,10 +308,9 @@ class text ?(autoscroll=false) ?(width=80) ?(indent=60) () = begin let sid = hid <- succ hid ; Printf.sprintf ">%X" hid in Hashtbl.add marks sid (fun p q -> Hashtbl.remove marks sid ; f p q) ; - Transitioning.Format.pp_open_stag fmt - (Transitioning.Format.stag_of_string sid) ; + Format.pp_open_stag fmt (Format.String_tag sid) ; let () = pp fmt in - Transitioning.Format.pp_close_stag fmt () ; + Format.pp_close_stag fmt () ; end (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/metrics/metrics_acsl.ml b/src/plugins/metrics/metrics_acsl.ml index e089ea8ce2d788f66dbfbcc360a2dbad9c4b3629..6e8cc4b68f7771060214537bbe16865d7ea0904f 100644 --- a/src/plugins/metrics/metrics_acsl.ml +++ b/src/plugins/metrics/metrics_acsl.ml @@ -284,7 +284,7 @@ let dump_acsl_stats fmt = let dump_acsl_stats_html fmt = - Transitioning.Format.pp_set_formatter_stag_functions fmt Metrics_base.html_stag_functions; + Format.pp_set_formatter_stag_functions fmt Metrics_base.html_stag_functions; Format.fprintf fmt "@[<v 0> <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\ \"http://www.w3.org/TR/html4/strict.dtd\">@ \ diff --git a/src/plugins/metrics/metrics_base.ml b/src/plugins/metrics/metrics_base.ml index 9a6e2e50dc1d5b6d3376006fabd61d7bfff8465f..c4c0934db605687aeb685d16abaebc37a0b4e5d6 100644 --- a/src/plugins/metrics/metrics_base.ml +++ b/src/plugins/metrics/metrics_base.ml @@ -26,10 +26,10 @@ open Cil_types (* vname, vaddrof *) (* Formatting html with Format.formatters *) let html_stag_functions = let mark_open_stag t = - let t = Transitioning.Format.string_of_stag t in + let t = Extlib.format_string_of_stag t in Format.sprintf "<%s>" t and mark_close_stag t = - let t = Transitioning.Format.string_of_stag t in + let t = Extlib.format_string_of_stag t in try let index = String.index t ' ' in Format.sprintf "</%s>" (String.sub t 0 index) @@ -38,7 +38,7 @@ let html_stag_functions = and print_open_stag _ = () and print_close_stag _ = () in - { Transitioning.Format.mark_open_stag; mark_close_stag; + { Format.mark_open_stag; mark_close_stag; print_open_stag; print_close_stag; } ;; @@ -265,7 +265,7 @@ let get_file_type filename = module VarinfoByName = struct type t = Cil_types.varinfo - let compare v1 v2 = Transitioning.Stdlib.compare v1.vname v2.vname + let compare v1 v2 = Stdlib.compare v1.vname v2.vname end (** Map and sets of varinfos sorted by name (and not by ids) *) diff --git a/src/plugins/metrics/metrics_base.mli b/src/plugins/metrics/metrics_base.mli index e9194fe7aea4922f4e4c761969516a9780223c32..bd8b85522c181da835fdfc7c4e1fc0d628978db6 100644 --- a/src/plugins/metrics/metrics_base.mli +++ b/src/plugins/metrics/metrics_base.mli @@ -21,7 +21,7 @@ (**************************************************************************) (** Tag functions handling html tags for Format *) -val html_stag_functions : Transitioning.Format.formatter_stag_functions;; +val html_stag_functions : Format.formatter_stag_functions;; (** mk_hdr [level] [ppf] [hdr_strg] produces a title from [hdr_strg] with an underline of the same length. diff --git a/src/plugins/metrics/metrics_cilast.ml b/src/plugins/metrics/metrics_cilast.ml index c6a607be5d638141e0d2269cd2a6ce2a5d1a66d8..e719e52e683d298406b5e70bed2f14c561ea5d3a 100644 --- a/src/plugins/metrics/metrics_cilast.ml +++ b/src/plugins/metrics/metrics_cilast.ml @@ -135,7 +135,7 @@ class slocVisitor ~libc : sloc_visitor = object(self) Format.fprintf fmt "%a" self#pp_file_metrics filename) metrics_map method print_stats fmt = - Transitioning.Format.pp_set_formatter_stag_functions fmt Metrics_base.html_stag_functions; + Format.pp_set_formatter_stag_functions fmt Metrics_base.html_stag_functions; Format.pp_set_tags fmt true; let pr_hdr fmt hdr_name = Format.fprintf fmt "@{<th>%s@}" hdr_name in @@ -547,7 +547,7 @@ let pretty_used_files used_files = let dump_html fmt cil_visitor = (* Activate tagging for html *) - Transitioning.Format.pp_set_formatter_stag_functions fmt html_stag_functions; + Format.pp_set_formatter_stag_functions fmt html_stag_functions; Format.pp_set_tags fmt true; let pr_row s fmt n = diff --git a/src/plugins/obfuscator/obfuscator_kind.ml b/src/plugins/obfuscator/obfuscator_kind.ml index ad342ba999a102b65a55f7eb7cd8f5706c437723..4567b97fe13a42312c094644f9f538fe97d21590 100644 --- a/src/plugins/obfuscator/obfuscator_kind.ml +++ b/src/plugins/obfuscator/obfuscator_kind.ml @@ -78,7 +78,7 @@ include Datatype.Make_with_collections let reprs = [ Global_var ] let hash (k:k) = Hashtbl.hash k let equal (k1:k) k2 = k1 = k2 - let compare (k1:k) k2 = Transitioning.Stdlib.compare k1 k2 + let compare (k1:k) k2 = Stdlib.compare k1 k2 let varname _ = "k" let internal_pretty_code = Datatype.undefined let copy = Datatype.identity diff --git a/src/plugins/pdg/build.ml b/src/plugins/pdg/build.ml index c2834f47ab666958476dc3921c257fa3669b26e7..e761247ebc70745eeaa92b6a2cd2237e9d70eb28 100644 --- a/src/plugins/pdg/build.ml +++ b/src/plugins/pdg/build.ml @@ -45,7 +45,7 @@ exception Err_Bot of string (** set of nodes of the graph *) module BoolNodeSet = - Transitioning.Stdlib.Set.Make(Datatype.Pair(Datatype.Bool)(PdgTypes.Node)) + Stdlib.Set.Make(Datatype.Pair(Datatype.Bool)(PdgTypes.Node)) let pretty_node ?(key=false) fmt n = PdgTypes.Node.pretty fmt n; diff --git a/src/plugins/qed/export.ml b/src/plugins/qed/export.ml index b77cd13a68f251ff1d244b35f1562a6a833d2c7e..f0ab696d75cb71ce45d2fe696ab6a7dd8af8d10d 100644 --- a/src/plugins/qed/export.ml +++ b/src/plugins/qed/export.ml @@ -828,7 +828,7 @@ struct (fun (s1,e1) (s2,e2) -> match s1,s2 with | true,true | false,false -> - Transitioning.Stdlib.compare (T.weigth e1) (T.weigth e2) + Stdlib.compare (T.weigth e1) (T.weigth e2) | true,false -> (-1) | false,true -> 1 ) sxs in diff --git a/src/plugins/qed/intmap.ml b/src/plugins/qed/intmap.ml index 63cd9aefc1c8f7490df1c40774facc0069e63efa..8aa8b46723aa628f4be0f2511b590dd4d0c850ca 100644 --- a/src/plugins/qed/intmap.ml +++ b/src/plugins/qed/intmap.ml @@ -211,12 +211,12 @@ let rec compare cmp s t = | Empty , _ -> (-1) | _ , Empty -> 1 | Lf(i,x) , Lf(j,y) -> - let ck = Transitioning.Stdlib.compare i j in + let ck = Stdlib.compare i j in if ck = 0 then cmp x y else ck | Lf _ , _ -> (-1) | _ , Lf _ -> 1 | Br(p,s0,s1) , Br(q,t0,t1) -> - let cp = Transitioning.Stdlib.compare p q in + let cp = Stdlib.compare p q in if cp <> 0 then cp else let c0 = compare cmp s0 t0 in if c0 <> 0 then c0 else diff --git a/src/plugins/qed/kind.ml b/src/plugins/qed/kind.ml index eef9ea65a4d01cbbcd6040f2ce711fede63473b1..c2c5e20d2b2d716dc86383c45cb3d3c9dd5d6f67 100644 --- a/src/plugins/qed/kind.ml +++ b/src/plugins/qed/kind.ml @@ -177,7 +177,7 @@ let rec compare_tau cfield cadt t1 t2 = | Prop , Prop -> 0 | Prop , _ -> (-1) | _ , Prop -> 1 - | Tvar k , Tvar k' -> Transitioning.Stdlib.compare k k' + | Tvar k , Tvar k' -> Stdlib.compare k k' | Tvar _ , _ -> (-1) | _ , Tvar _ -> 1 | Array(ta,tb) , Array(ta',tb') -> diff --git a/src/plugins/qed/pool.ml b/src/plugins/qed/pool.ml index 61234f5c5812c90d6dd3ecf8629086ed2e03e506..7671548f9cf3af414fbc47324688c9e2df9451d2 100644 --- a/src/plugins/qed/pool.ml +++ b/src/plugins/qed/pool.ml @@ -77,9 +77,9 @@ struct let compare x y = let cmp = String.compare x.vbase y.vbase in if cmp <> 0 then cmp else - let cmp = Transitioning.Stdlib.compare x.vrank y.vrank in + let cmp = Stdlib.compare x.vrank y.vrank in if cmp <> 0 then cmp else - Transitioning.Stdlib.compare x.vid y.vid + Stdlib.compare x.vid y.vid (* POOL *) diff --git a/src/plugins/qed/term.ml b/src/plugins/qed/term.ml index 3b42acac46a11ff8e899c1b71599b840f11e6fe2..c533be908fb938cb2cc419a53579da64bb6b1d13 100644 --- a/src/plugins/qed/term.ml +++ b/src/plugins/qed/term.ml @@ -464,7 +464,7 @@ struct | Constructor -> 1 | Operator _ -> 0 - let cmp_size a b = Transitioning.Stdlib.compare a.size b.size + let cmp_size a b = Stdlib.compare a.size b.size let rank_bind = function Forall -> 0 | Exists -> 1 | Lambda -> 2 let cmp_bind p q = rank_bind p - rank_bind q let cmp_field phi (f,x) (g,y) = @@ -1413,7 +1413,7 @@ struct | _ -> (k,t) :: acc (* sorts monoms by terms *) - let compare_monoms (_,t1) (_,t2) = Transitioning.Stdlib.compare t1.id t2.id + let compare_monoms (_,t1) (_,t2) = Stdlib.compare t1.id t2.id (* factorized monoms *) let fold_monom ts k t = diff --git a/src/plugins/report/classify.ml b/src/plugins/report/classify.ml index ba15f820bfa17c098dc679eefd7155835e427a06..08e198b764dd8a9392443d780850e9d7b04ef02d 100644 --- a/src/plugins/report/classify.ml +++ b/src/plugins/report/classify.ml @@ -241,7 +241,7 @@ let json_of_event e = module EVENTS = Set.Make (struct type t = event - let compare = Transitioning.Stdlib.compare + let compare = Stdlib.compare end) let events_queue = Queue.create () diff --git a/src/plugins/report/csv.ml b/src/plugins/report/csv.ml index 680de39c07f068e09c6a73a3e061feee0d4d919a..7d50b76be8d88a410e194c2d9a81f5c7152cdf45 100644 --- a/src/plugins/report/csv.ml +++ b/src/plugins/report/csv.ml @@ -66,7 +66,7 @@ let lines () = emitted on statements copied through loop unrolling. This is the desired semantics for now. However, since we compare entire locations, textually identical lines that refer to different expressions are kept separate *) - Extlib.sort_unique Transitioning.Stdlib.compare l + Extlib.sort_unique Stdlib.compare l let output file = let ch = open_out file in diff --git a/src/plugins/server/jbuffer.ml b/src/plugins/server/jbuffer.ml index 64a536cf7662ea50fe0aa0c18f206ce3cf0a6014..f7ca9ebbfe05418e06440810bca2103881afd3b3 100644 --- a/src/plugins/server/jbuffer.ml +++ b/src/plugins/server/jbuffer.ml @@ -41,7 +41,7 @@ let flush buffer () = Buffer.clear t let push_tag buffer stag = - let tag = Transitioning.Format.string_of_stag stag in + let tag = Extlib.format_string_of_stag stag in flush buffer () ; buffer.stack <- ( tag , buffer.rjson ) :: buffer.stack ; buffer.rjson <- [] @@ -84,7 +84,7 @@ let create ?indent ?margin () = Format.pp_set_max_indent fmt (max 0 (min k (m-10))) end ; begin - let open Transitioning.Format in + let open Format in pp_set_formatter_stag_functions fmt { print_open_stag = no_mark ; print_close_stag = no_mark ; diff --git a/src/plugins/server/jbuffer.mli b/src/plugins/server/jbuffer.mli index 9db8cfab7eaed970a0cba77b1af9e913d1f4c161..2a825a04f0f8f0fbc42efba13991bb55d88c6ac6 100644 --- a/src/plugins/server/jbuffer.mli +++ b/src/plugins/server/jbuffer.mli @@ -48,8 +48,8 @@ val bprintf : buffer -> ('a,Format.formatter,unit) format -> 'a val append : buffer -> string -> int -> int -> unit val flush : buffer -> unit -> unit -val push_tag : buffer -> Transitioning.Format.stag -> unit -val pop_tag : buffer -> Transitioning.Format.stag -> unit +val push_tag : buffer -> Format.stag -> unit +val pop_tag : buffer -> Format.stag -> unit (** Flushes the buffer and returns its JSON enoding. This pops all pending tags. *) diff --git a/src/plugins/server/kernel_ast.ml b/src/plugins/server/kernel_ast.ml index 3868f9d799dc6a5b0e15bb248aae35e4ec5705da..832c1adce2b70ba182a4549234391d18512c4f28 100644 --- a/src/plugins/server/kernel_ast.ml +++ b/src/plugins/server/kernel_ast.ml @@ -46,11 +46,12 @@ module MarkerKind = struct let kinds = Enum.dictionary () - let kind name = Enum.tag ~name - ~descr:(Md.plain (String.capitalize_ascii name)) kinds + let kind name = + Enum.tag + ~name + ~descr:(Md.plain (String.capitalize_ascii name)) + kinds - let var = kind "variable" - let fct = kind "function" let expr = kind "expression" let lval = kind "lvalue" let decl = kind "declaration" @@ -59,24 +60,65 @@ module MarkerKind = struct let term = kind "term" let prop = kind "property" - let () = Enum.set_lookup kinds - begin - let open Printer_tag in - function - | PStmt _ -> stmt - | PStmtStart _ -> stmt - | PVDecl _ -> decl - | PLval (_, _, (Var vi, NoOffset)) -> - if Cil.isFunctionType vi.vtype then fct else var - | PLval _ -> lval - | PExp _ -> expr - | PTermLval _ -> term - | PGlobal _ -> glob - | PIP _ -> prop - end - - let data = Request.dictionary ~package - ~name:"markerKind" ~descr:(Md.plain "Marker kind") kinds + let () = + Enum.set_lookup + kinds + (fun localizable -> + let open Printer_tag in + match localizable with + | PStmt _ -> stmt + | PStmtStart _ -> stmt + | PVDecl _ -> decl + | PLval _ -> lval + | PExp _ -> expr + | PTermLval _ -> term + | PGlobal _ -> glob + | PIP _ -> prop) + + let data = + Request.dictionary + ~package + ~name:"markerKind" + ~descr:(Md.plain "Marker kind") + kinds + + include (val data : S with type t = Printer_tag.localizable) +end + +module MarkerVar = struct + + let vars = Enum.dictionary () + + let kind name = + Enum.tag + ~name + ~descr:(Md.plain (String.capitalize_ascii name)) + vars + + let none = kind "none" + let var = kind "variable" + let fct = kind "function" + + let () = + Enum.set_lookup + vars + (fun localizable -> + let open Printer_tag in + match localizable with + | PLval (_, _, (Var vi, NoOffset)) + | PVDecl (_, _, vi) + | PGlobal (GVar (vi, _, _) | GVarDecl (vi, _)) -> + if Cil.isFunctionType vi.vtype then fct else var + | PGlobal (GFun _ | GFunDecl _) -> fct + | PLval _ | PStmt _ | PStmtStart _ + | PExp _ | PTermLval _ | PGlobal _ | PIP _ -> none) + + let data = + Request.dictionary + ~package + ~name:"markerVar" + ~descr:(Md.plain "Marker variable") + vars include (val data : S with type t = Printer_tag.localizable) end @@ -122,8 +164,18 @@ struct let model = States.model () in let () = States.column - ~name:"kind" ~descr:(Md.plain "Marker kind") - ~data:(module MarkerKind) ~get:fst + ~name:"kind" + ~descr:(Md.plain "Marker kind") + ~data:(module MarkerKind) + ~get:fst + model + in + let () = + States.column + ~name:"var" + ~descr:(Md.plain "Marker variable") + ~data:(module MarkerVar) + ~get:fst model in let () = @@ -240,6 +292,32 @@ struct with Not_found -> Data.failure "Undefined function '%s'" key end +module KfMarker = struct + type record + let record : record Record.signature = Record.signature () + let fct = Record.field record ~name:"function" + ~descr:(Md.plain "Function") (module Kf) + let marker = Record.field record ~name:"marker" + ~descr:(Md.plain "Marker") (module Marker) + + let data = + Record.publish ~package ~name:"location" + ~descr:(Md.plain "Location: function and marker") record + module R : Record.S with type r = record = (val data) + type t = kernel_function * Printer_tag.localizable + let jtype = R.jtype + + let to_json (kf, loc) = + R.default |> + R.set fct kf |> + R.set marker loc |> + R.to_json + + let of_json json = + let r = R.of_json json in + R.get fct r, R.get marker r +end + (* -------------------------------------------------------------------------- *) (* --- Functions --- *) (* -------------------------------------------------------------------------- *) @@ -258,7 +336,14 @@ let () = Request.register ~package ~kind:`GET ~name:"printFunction" ~descr:(Md.plain "Print the AST of a function") ~input:(module Kf) ~output:(module Jtext) - (fun kf -> Jbuffer.to_json Printer.pp_global (Kernel_function.get_global kf)) + begin fun kf -> + let libc = Kernel.PrintLibc.get () in + if not libc then Kernel.PrintLibc.set true ; + let global = Kernel_function.get_global kf in + let ast = Jbuffer.to_json Printer.pp_global global in + if not libc then Kernel.PrintLibc.set false ; + ast + end module Functions = struct @@ -300,10 +385,10 @@ module Info = struct open Printer_tag let print_function fmt name = - let stag = Transitioning.Format.stag_of_string name in - Transitioning.Format.pp_open_stag fmt stag; + let stag = Format.String_tag name in + Format.pp_open_stag fmt stag; Format.pp_print_string fmt name; - Transitioning.Format.pp_close_stag fmt () + Format.pp_close_stag fmt () let print_kf fmt kf = print_function fmt (Kernel_function.get_name kf) @@ -314,7 +399,7 @@ module Info = struct let pp_kf fmt kf = Format.fprintf fmt " of function %a" print_kf kf in Format.fprintf fmt "It is a %s variable%a.@." (if vi.vglob then "global" else if vi.vformal then "formal" else "local") - (Transitioning.Format.pp_print_option pp_kf) kf; + (Format.pp_print_option pp_kf) kf; if vi.vtemp then Format.fprintf fmt "This is a temporary variable%s.@." (match vi.vdescr with None -> "" | Some descr -> " for " ^ descr); diff --git a/src/plugins/server/kernel_ast.mli b/src/plugins/server/kernel_ast.mli index c0d3d0ff73090d3c5c7d839b3494ba4fe4dbe259..6832e10f4ed79ccfd685b8bd8f966de80c09bd63 100644 --- a/src/plugins/server/kernel_ast.mli +++ b/src/plugins/server/kernel_ast.mli @@ -47,6 +47,8 @@ sig val lookup : string -> t (** Get back the localizable, if any. *) end +module KfMarker : Data.S with type t = kernel_function * Printer_tag.localizable + (* -------------------------------------------------------------------------- *) (** Ast Printer *) (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/server/package.ml b/src/plugins/server/package.ml index 39ac4af5b98a0ecf6a4b71ba01dedd1b1da56599..309cae632565703288b5cc95ca1a5793215cfba5 100644 --- a/src/plugins/server/package.ml +++ b/src/plugins/server/package.ml @@ -46,7 +46,7 @@ let pp_ident fmt { plugin ; package ; name } = (* --- Name Resolution --- *) (* -------------------------------------------------------------------------- *) -module Std = Transitioning.Stdlib +module Std = Stdlib module Id = struct type t = ident let compare = Std.compare end module IdMap = Map.Make(Id) module IdSet = Set.Make(Id) diff --git a/src/plugins/server/server_batch.ml b/src/plugins/server/server_batch.ml index 2df2d4d2567d1649b23219aa05712a9886951398..4fc37a40aa6850f9a8dfcbf01313317f67356436 100644 --- a/src/plugins/server/server_batch.ml +++ b/src/plugins/server/server_batch.ml @@ -102,7 +102,13 @@ let execute () = List.iter begin fun file -> Senv.feedback "Script %S" file ; - let response = execute_batch (Js.from_file file) in + let response = + try + execute_batch (Js.from_file file) + with Yojson.Json_error msg -> + Senv.error "[batch] error in JSON file:@\n%s@." msg; + `Null + in let output = Filename.remove_extension file ^ ".out.json" in let output = match BatchOutputDir.get () with | "" -> output diff --git a/src/plugins/server/server_doc.ml b/src/plugins/server/server_doc.ml index 522cc6584cfffdbadf3508ce01515780ef9ab1d5..9b7365eb82294c163c339f797c5139ba077d938e 100644 --- a/src/plugins/server/server_doc.ml +++ b/src/plugins/server/server_doc.ml @@ -237,7 +237,7 @@ let table_of_contents () = module Cmap = Map.Make (struct type t = string list - let compare = Transitioning.Stdlib.compare + let compare = Stdlib.compare end) let index_entry (title,href) = diff --git a/src/plugins/studia/reads.ml b/src/plugins/studia/reads.ml index 5f7d21197ceaaaa6154ec7dea556cd8636eed2fe..6a17e2cafe4fb2d7bdba0f57e0f92b81b1588a00 100644 --- a/src/plugins/studia/reads.ml +++ b/src/plugins/studia/reads.ml @@ -62,7 +62,7 @@ class find_read zlval = object if Zone.intersects inputs zlval then if !Db.Value.use_spec_instead_of_definition kf then (* Direst, as there is no body for this funtion. *) - { effects with direct = true } + { effects with direct = true } else { effects with indirect = true } (* Indirect effect *) else @@ -78,7 +78,7 @@ class find_read zlval = object match stmt.skind with | Instr (Call (lvopt, f, args, loc)) -> aux_call lvopt f args loc; - Cil.SkipChildren + Cil.SkipChildren | Instr (Local_init(v, ConsInit(f, args, k), l)) -> Cil.treat_constructor_as_func aux_call v f args k l; Cil.SkipChildren diff --git a/src/plugins/value/alarmset.ml b/src/plugins/value/alarmset.ml index 21475b79a7a7d523304ec22c16b5d8ed8b76e2d5..b0bd8fb82ac6e22c451e767cf9b015e9dbb8e2b6 100644 --- a/src/plugins/value/alarmset.ml +++ b/src/plugins/value/alarmset.ml @@ -48,7 +48,7 @@ module Status = struct let reprs = [ True; False; False; Unknown ] let mem_project = Datatype.never_any_project let pretty = pretty_status - let compare (s1:t) (s2:t) = Transitioning.Stdlib.compare s1 s2 + let compare (s1:t) (s2:t) = Stdlib.compare s1 s2 let equal (s1:t) (s2:t) = s1 = s2 let hash (s:t) = Hashtbl.hash s end) diff --git a/src/plugins/value/api/general_requests.ml b/src/plugins/value/api/general_requests.ml index 5c30a704fbcb1fc5e59c9b7bbcf611adc6091460..abf821d1d69063f79af1cc14354030b3920ae856 100644 --- a/src/plugins/value/api/general_requests.ml +++ b/src/plugins/value/api/general_requests.ml @@ -21,9 +21,22 @@ (**************************************************************************) open Server +open Cil_types -let package = Package.package ~plugin:"eva" - ~title:"Eva General Services" ~readme:"eva.md" () +let package = + Package.package + ~plugin:"eva" + ~name:"general" + ~title:"Eva General Services" + ~readme:"eva.md" + () + +let is_computed kf = + Db.Value.is_computed () && + match kf with + | { fundec = Definition (fundec, _) } -> + Mark_noresults.should_memorize_function fundec + | { fundec = Declaration _ } -> false module CallSite = Data.Jpair (Kernel_ast.Kf) (Kernel_ast.Stmt) @@ -37,4 +50,67 @@ let () = Request.register ~package ~input:(module Kernel_ast.Kf) ~output:(module Data.Jlist (CallSite)) callers + +(* ----- Dead code: unreachable and non-terminating statements -------------- *) + +type dead_code = + { kf: Kernel_function.t; + unreachable : stmt list; + non_terminating : stmt list; } + +module DeadCode = struct + open Server.Data + + type record + let record : record Record.signature = Record.signature () + + let unreachable = Record.field record ~name:"unreachable" + ~descr:(Markdown.plain "List of unreachable statements.") + (module Data.Jlist (Kernel_ast.Marker)) + let non_terminating = Record.field record ~name:"nonTerminating" + ~descr:(Markdown.plain "List of reachable but non terminating statements.") + (module Data.Jlist (Kernel_ast.Marker)) + + let data = Record.publish record ~package ~name:"deadCode" + ~descr:(Markdown.plain "Unreachable and non terminating statements.") + + module R : Record.S with type r = record = (val data) + type t = dead_code + let jtype = R.jtype + + let to_json (dead_code) = + let make_unreachable stmt = Printer_tag.PStmt (dead_code.kf, stmt) + and make_non_term stmt = Printer_tag.PStmtStart (dead_code.kf, stmt) in + R.default |> + R.set unreachable (List.map make_unreachable dead_code.unreachable) |> + R.set non_terminating (List.map make_non_term dead_code.non_terminating) |> + R.to_json +end + +let dead_code kf = + let empty = { kf; unreachable = []; non_terminating = [] } in + if is_computed kf then + let module Results = (val Analysis.current_analyzer ()) in + let is_unreachable ~after stmt = + Results.get_stmt_state ~after stmt = `Bottom + in + let classify acc stmt = + if is_unreachable ~after:false stmt + then { acc with unreachable = stmt :: acc.unreachable } + else if is_unreachable ~after:true stmt + then { acc with non_terminating = stmt :: acc.non_terminating } + else acc + in + let fundec = Kernel_function.get_definition kf in + List.fold_left classify empty fundec.sallstmts + else empty + +let () = Request.register ~package + ~kind:`GET ~name:"getDeadCode" + ~descr:(Markdown.plain "Get the lists of unreachable and of non terminating \ + statements in a function") + ~input:(module Kernel_ast.Kf) + ~output:(module DeadCode) + dead_code + (**************************************************************************) diff --git a/src/plugins/value/api/values_request.ml b/src/plugins/value/api/values_request.ml new file mode 100644 index 0000000000000000000000000000000000000000..85d2df413cded868f8088cdb2d72602a095ca4ac --- /dev/null +++ b/src/plugins/value/api/values_request.ml @@ -0,0 +1,366 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Server +open Data +open Cil_types +module Md = Markdown + +let package = + Package.package + ~plugin:"eva" + ~name:"values" + ~title:"Eva Values" + ~readme:"eva.md" + () + +type value = + { value: string; + alarm: bool; } + +type evaluation = + | Unreachable + | Evaluation of value + +type after = + | Unchanged + | Reduced of evaluation + +type before_after = + { before: evaluation; + after_instr: after option; + after_then: after option; + after_else: after option; } + +type values = + { values: before_after; + callstack: (Value_util.callstack * before_after) list option; } + +let get_value = function + | Unreachable -> "Unreachable" + | Evaluation { value } -> value + +let get_alarm = function + | Unreachable -> false + | Evaluation { alarm } -> alarm + +let get_after_value = + Extlib.opt_map + (function Unchanged -> "unchanged" | Reduced eval -> get_value eval) + +module CallStackId = + Data.Index + (Value_types.Callstack.Map) + (struct + let name = "eva-callstack-id" + end) + +(* This pretty-printer drops the toplevel kf, which is always the function + in which we are pretty-printing the expression/term *) +let pretty_callstack fmt cs = + match cs with + | [_, Kglobal] -> () + | (_kf_cur, Kstmt callsite) :: q -> begin + let rec aux callsite = function + | (kf, callsite') :: q -> begin + Format.fprintf fmt "%a (%a)" + Kernel_function.pretty kf + Cil_datatype.Location.pretty (Cil_datatype.Stmt.loc callsite); + match callsite' with + | Kglobal -> () + | Kstmt callsite' -> + Format.fprintf fmt " â†@ "; + aux callsite' q + end + | _ -> assert false + in + Format.fprintf fmt "@[<hv>%a" Value_types.Callstack.pretty_hash cs; + aux callsite q; + Format.fprintf fmt "@]" + end + | _ -> assert false + +(* This pretty-printer prints only the lists of the functions, not + the locations. *) +let pretty_callstack_short fmt cs = + match cs with + | [_, Kglobal] -> () + | (_kf_cur, Kstmt _callsite) :: q -> + Format.fprintf fmt "%a" Value_types.Callstack.pretty_hash cs; + Pretty_utils.pp_flowlist ~left:"@[" ~sep:" â†@ " ~right:"@]" + (fun fmt (kf, _) -> Kernel_function.pretty fmt kf) fmt q + | _ -> assert false + +module CallStack = struct + type record + + let record: record Record.signature = Record.signature () + + let id = Record.field record ~name:"id" + ~descr:(Md.plain "Callstack id") (module Jint) + let short = Record.field record ~name:"short" + ~descr:(Md.plain "Short name for the callstack") (module Jstring) + let full = Record.field record ~name:"full" + ~descr:(Md.plain "Full name for the callstack") (module Jstring) + + module R = + (val + (Record.publish + ~package + ~name:"callstack" + ~descr:(Md.plain "CallStack") + record) : Record.S with type r = record) + + type t = Value_types.callstack option + + let jtype = R.jtype + + let pp_callstack ~short = function + | None -> if short then "all" else "" + | Some callstack -> + let pp_text = + if short + then Pretty_utils.to_string ~margin:50 pretty_callstack_short + else Pretty_utils.to_string pretty_callstack + in + (pp_text callstack) + + let id_callstack = function + | None -> -1 + | Some callstack -> CallStackId.get callstack + + let to_json callstack = + R.default |> + R.set id (id_callstack callstack) |> + R.set short (pp_callstack ~short:true callstack) |> + R.set full (pp_callstack ~short:false callstack) |> + R.to_json + + let key = function + | None -> "all" + | Some callstack -> string_of_int (CallStackId.get callstack) +end + + +let consolidated = ref None +let table = Hashtbl.create 100 + +let iter f = + if Hashtbl.length table > 1 + then Extlib.may (fun values -> f (None, values)) !consolidated; + Hashtbl.iter (fun key data -> f (Some key, data)) table + +let array = + let model = States.model () in + let () = + States.column + ~name:"callstack" + ~descr:(Md.plain "CallStack") + ~data:(module CallStack) + ~get:fst + model + in + let () = + States.column + ~name:"value_before" + ~descr:(Md.plain "Value inferred just before the selected point") + ~data:(module Jstring) + ~get:(fun (_, e) -> get_value e.before) + model + in + let () = + States.column + ~name:"alarm" + ~descr:(Md.plain "Did the evaluation led to an alarm?") + ~data:(module Jbool) + ~get:(fun (_, e) -> get_alarm e.before) + model + in + let () = + States.column + ~name:"value_after" + ~descr:(Md.plain "Value inferred just after the selected point") + ~data:(module Joption(Jstring)) + ~get:(fun (_, e) -> get_after_value e.after_instr) + model + in + States.register_array + ~package + ~name:"values" + ~descr:(Md.plain "Abstract values inferred by the Eva analysis") + ~key:(fun (cs, _) -> CallStack.key cs) + ~iter + model + +let update_values values = + Hashtbl.clear table; + consolidated := Some values.values; + let () = + match values.callstack with + | None -> () + | Some by_callstack -> + List.iter + (fun (callstack, before_after) -> + Hashtbl.add table callstack before_after) + by_callstack + in + States.reload array + +module type S = sig + val evaluate: kinstr -> exp -> values + val lvaluate: kinstr -> lval -> values +end + +module Make (Eva: Analysis.S) : S = struct + + let make_before eval before = + let before = + match before with + | `Bottom -> Unreachable + | `Value state -> Evaluation (eval state) + in + { before; after_instr = None; after_then = None; after_else = None; } + + let make_callstack stmt eval = + let before = Eva.get_stmt_state_by_callstack ~after:false stmt in + match before with + | (`Bottom | `Top) -> [] + | `Value before -> + let aux callstack before acc = + let before_after = make_before eval (`Value before) in + (callstack, before_after) :: acc + in + Value_types.Callstack.Hashtbl.fold aux before [] + + let make_before_after eval ~before ~after = + match before with + | `Bottom -> + { before = Unreachable; + after_instr = None; + after_then = None; + after_else = None; } + | `Value before -> + let before = eval before in + let after_instr = + match after with + | `Bottom -> Some (Reduced Unreachable) + | `Value after -> + let after = eval after in + if String.equal before.value after.value + then Some Unchanged + else Some (Reduced (Evaluation after)) + in + { before = Evaluation before; + after_instr; after_then = None; after_else = None; } + + let make_instr_callstack stmt eval = + let before = Eva.get_stmt_state_by_callstack ~after:false stmt in + let after = Eva.get_stmt_state_by_callstack ~after:true stmt in + match before, after with + | (`Bottom | `Top), _ + | _, (`Bottom | `Top) -> [] + | `Value before, `Value after -> + let aux callstack before acc = + let before = `Value before in + let after = + try `Value (Value_types.Callstack.Hashtbl.find after callstack) + with Not_found -> `Bottom + in + let before_after = make_before_after eval ~before ~after in + (callstack, before_after) :: acc + in + Value_types.Callstack.Hashtbl.fold aux before [] + + let make eval kinstr = + let before = Eva.get_kinstr_state ~after:false kinstr in + let values, callstack = + match kinstr with + | Cil_types.Kglobal -> + make_before eval before, None + | Cil_types.Kstmt stmt -> + match stmt.skind with + | Instr _ -> + let after = Eva.get_kinstr_state ~after:true kinstr in + let values = make_before_after eval ~before ~after in + let callstack = make_instr_callstack stmt eval in + values, Some callstack + | _ -> + make_before eval before, Some (make_callstack stmt eval) + in + { values; callstack; } + + + let evaluate kinstr expr = + let eval state = + let value, alarms = Eva.eval_expr state expr in + let alarm = not (Alarmset.is_empty alarms) in + let str = Format.asprintf "%a" (Bottom.pretty Eva.Val.pretty) value in + { value = str; alarm } + in + make eval kinstr + + let lvaluate kinstr lval = + let eval state = + let value, alarms = Eva.copy_lvalue state lval in + let alarm = not (Alarmset.is_empty alarms) in + let flagged_value = match value with + | `Bottom -> Eval.Flagged_Value.bottom + | `Value v -> v + in + let pretty = Eval.Flagged_Value.pretty Eva.Val.pretty in + let str = Format.asprintf "%a" pretty flagged_value in + { value = str; alarm } + in + make eval kinstr +end + + +let ref_request = + let module Analyzer = (val Analysis.current_analyzer ()) in + ref (module Make (Analyzer) : S) + +let hook (module Analyzer: Analysis.S) = + ref_request := (module Make (Analyzer) : S) + +let () = Analysis.register_hook hook + + +let update tag = + let module Request = (val !ref_request) in + match tag with + | Printer_tag.PExp (_kf, kinstr, expr) -> + update_values (Request.evaluate kinstr expr) + | Printer_tag.PLval (_kf, kinstr, lval) -> + update_values (Request.lvaluate kinstr lval) + | PVDecl (_kf, kinstr, varinfo) -> + update_values (Request.lvaluate kinstr (Var varinfo, NoOffset)) + | _ -> () + +let () = + Server.Request.register + ~package + ~kind:`GET + ~name:"getValues" + ~descr:(Md.plain "Get the abstract values computed for an expression or lvalue") + ~input:(module Kernel_ast.Marker) + ~output:(module Junit) + update diff --git a/src/plugins/value/api/values_request.mli b/src/plugins/value/api/values_request.mli new file mode 100644 index 0000000000000000000000000000000000000000..182bc40a8e1af00f6d1329952d449d0f2e6808e1 --- /dev/null +++ b/src/plugins/value/api/values_request.mli @@ -0,0 +1,21 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) diff --git a/src/plugins/value/domains/domain_mode.ml b/src/plugins/value/domains/domain_mode.ml index 298f27fc57d885a4b0c438fe24e4388ad983f435..2d0788ba113800847d38ef598287acdadce0e407 100644 --- a/src/plugins/value/domains/domain_mode.ml +++ b/src/plugins/value/domains/domain_mode.ml @@ -39,7 +39,7 @@ module Mode = struct type t = mode let name = "Domain_mode.Mode" let reprs = [ default ] - let compare = Transitioning.Stdlib.compare + let compare = Stdlib.compare let equal = Datatype.from_compare let hash = Hashtbl.hash end) diff --git a/src/plugins/value/domains/gauges/gauges_domain.ml b/src/plugins/value/domains/gauges/gauges_domain.ml index 4c8dec87e1ecf236a920ef4eb38a1134f3a6ceb7..2f2bcab9c4bce90fd2be0cea767426d5261b29f7 100644 --- a/src/plugins/value/domains/gauges/gauges_domain.ml +++ b/src/plugins/value/domains/gauges/gauges_domain.ml @@ -502,7 +502,7 @@ module G = struct let compare ii1 ii2 = match ii1, ii2 with | PreciseIteration i1, PreciseIteration i2 -> - Transitioning.Stdlib.compare i1 i2 + Stdlib.compare i1 i2 | MultipleIterations i1, MultipleIterations i2 -> MultipleIterations.compare i1 i2 | PreciseIteration _, MultipleIterations _ -> -1 diff --git a/src/plugins/value/domains/traces_domain.ml b/src/plugins/value/domains/traces_domain.ml index c7cb5f5219d7ce702517e27cfac0d85b2b665b05..32548a62121bc92a17f752c05235b6fdfe1e6960 100644 --- a/src/plugins/value/domains/traces_domain.ml +++ b/src/plugins/value/domains/traces_domain.ml @@ -135,7 +135,7 @@ end = struct | Assume (_, e1, b1), Assume (_, e2, b2) -> let c = ExpStructEq.compare e1 e2 in if c <> 0 then c else - Transitioning.Stdlib.compare b1 b2 + Stdlib.compare b1 b2 | EnterScope (_, vs1), EnterScope (_, vs2) -> Extlib.list_compare Varinfo.compare vs1 vs2 | LeaveScope (_, vs1), LeaveScope (_, vs2) -> diff --git a/src/plugins/value/gui_files/gui_callstacks_manager.ml b/src/plugins/value/gui_files/gui_callstacks_manager.ml index 8b9f681f75c233319637e4bc5f6dbd8b2b1b9cc1..bbb192034aa53b3582b4ecdc77789c03864cd1e8 100644 --- a/src/plugins/value/gui_files/gui_callstacks_manager.ml +++ b/src/plugins/value/gui_files/gui_callstacks_manager.ml @@ -261,7 +261,7 @@ module Make (Input: Input) = struct module Data = Indexer.Make( struct type t = int * value row - let compare (x,_) (y,_) = Transitioning.Stdlib.compare x y + let compare (x,_) (y,_) = Stdlib.compare x y end) (* This function creates a single GTree that displays per-callstack diff --git a/src/plugins/value/gui_files/gui_red.ml b/src/plugins/value/gui_files/gui_red.ml index 8671ed982494460661e53c88939c4ae85d6b9e5e..1e6c0ffb11d8e7f62fa1bb8831407e67b413feed 100644 --- a/src/plugins/value/gui_files/gui_red.ml +++ b/src/plugins/value/gui_files/gui_red.ml @@ -108,7 +108,7 @@ type t = module Data = Indexer.Make( struct type t = int*row - let compare (x,_) (y,_) = Transitioning.Stdlib.compare x y + let compare (x,_) (y,_) = Stdlib.compare x y end) let append t message = t.append message diff --git a/src/plugins/value/partitioning/auto_loop_unroll.ml b/src/plugins/value/partitioning/auto_loop_unroll.ml index e6cd5f666aa72ff46198acbe3f1dbdc2f8b06d4c..ac50025173cccc8707fe4d6e5385fd79365d821c 100644 --- a/src/plugins/value/partitioning/auto_loop_unroll.ml +++ b/src/plugins/value/partitioning/auto_loop_unroll.ml @@ -25,8 +25,8 @@ The limit is defined by the option -eva-auto-loop-unroll. *) (* Gist of the heuristic: - - find a loop exit condition, in the form of a statement "if(cond) break;". - such that exactly one lvalue [lval] in the condition [cond] is modified + - collect loop exit conditions [cond] from statements "if (cond) break;" + - find a loop exit condition in which only one lvalue [lval] is modified within the loop; all other lvalues must be constant in the loop. - find a value [v_exit] such that [lval] ∈ [v_exit] ⇒ [cond] holds. - evaluate [lval] to its initial value [v_init] in the loop entry state. @@ -34,7 +34,7 @@ iteration of the loop. If [v_init] + k × [v_delta] ⊂ [v_exit], then the number of iterations - is bounded by the limit [k]. + is bounded by [k]. The heuristic is syntactic and limited to the current function: it does not handle assignment through pointers or function calls. @@ -42,35 +42,50 @@ whose address is never taken (they cannot be modified through pointers). If the loop contains a function call, the condition [cond] should not contain global variables (as they may be modified in the function called). - A first analyze of the loop gathers all such variables modified within the + A first analysis of the loop gathers all such variables modified within the loop; all others are constant, and can be evaluated in the loop entry state. When computing the increment [v_delta] of a lvalue [v] in the loop, the - heuristic searches assignments "v = v ± i;". Any other assignment of [v] - cancels the heuristic. *) + heuristic searches assignments "v = v ± i;". Otherwise, if [v] is only + modified within the loop through an assignement "v = v2", the heuristic + computes the initial value and delta increment of variable [v2] instead. + This is required to deal with temporary variables introduced by the Frama-C + normalization. + Any other assignment of [v] cancels the heuristic. *) open Cil_types -(* Is a statement a loop exit condition? If so, returns the condition and - whether the condition must hold to exit the loop. Otherwise, returns None. *) -let is_conditional_break stmt = - match stmt.skind with - | If (cond, {bstmts=[{skind=Break _}]}, _, _) -> Some (cond, true) - | If (cond, _, {bstmts=[{skind=Break _}]}, _) -> Some (cond, false) - | _ -> None +let is_true = function + | `True | `TrueReduced _ -> true + | _ -> false -(* Returns a loop exit condition, as the conditional expression and whether - the condition must be zero or non-zero to exit the loop. *) +(* Does a block exits a loop? *) +let is_break block = + match block.bstmts with + | [{skind = Break _}] -> true + | _ -> false + +(* Returns a list of loop exit conditions. Each condition is expressed as an + expression and whether it must be zero or non-zero to exit the loop. *) let find_loop_exit_condition loop = let rec aux = function - | [] -> None + | [] -> [] | stmt :: tl -> - match is_conditional_break stmt with - | Some _ as x -> x - | None -> aux tl + match stmt.skind with + | If (cond, b1, b2, _) when is_break b1 || is_break b2 -> + if is_break b1 + then (cond, true) :: aux (b2.bstmts @ tl) + else (cond, false) :: aux (b1.bstmts @ tl) + | Block b -> aux (b.bstmts @ tl) + | _ -> aux tl in aux loop.bstmts +let is_frama_c_builtin exp = + match exp.enode with + | Lval (Var vi, NoOffset) -> Ast_info.is_frama_c_builtin vi.vname + | _ -> false + (* Effects of a loop: - set of varinfos that are directly modified within the loop. Pointer accesses are ignored. @@ -105,7 +120,7 @@ let loop_effect_visitor = object (self) in let () = match instr with | Asm _ -> assembly <- true - | Call _ -> call <- true + | Call (_, exp, _, _) when not (is_frama_c_builtin exp) -> call <- true | _ -> () in Cil.SkipChildren @@ -143,7 +158,7 @@ let classify loop_effect lval = else (* If the address of the variable is taken, it could be modified within the loop. We suppose here that this is not the case, but this could - lead to some loop unrolling. *) + lead to some untimely loop unrolling. *) Constant | Mem _, _ -> Unsuitable (* Pointers are not supported by the heuristic. *) and is_const_offset = function @@ -177,50 +192,48 @@ let find_lonely_candidate loop_effect expr = in aux None lvalues -(* Returns true if the instruction assigns [lval]. *) +(* Returns true if the instruction does not modify [lval]. *) let is_safe_instruction lval = function | Set (lv, _, _) | Call (Some lv, _, _, _) -> not (Cil_datatype.LvalStructEq.equal lval lv) | Call (None, _, _, _) | Local_init _ | Skip _ | Code_annot _ -> true | Asm _ -> false -(* Returns true if the statement may assign [lval] during an iteration of the - loop [loop]. [lval] is a candidate for the automatic loop unroll heuristic, - and thus is modified within the loop. *) -let is_safe lval ~loop stmt = - (* The current block being checked for a goto statement. *) - let current_block = ref None in - let rec is_safe_stmt stmt = +(* Returns true if the statement [stmt] of function [kf] does not modify [lval]. + [lval] is a candidate for the automatic loop unrolling of [loop] [loop]. *) +let is_constant kf ~loop lval stmt = + let rec is_safe_stmt ~goto stmt = match stmt.skind with | Instr instr -> is_safe_instruction lval instr | Return _ | Break _ | Continue _ -> true - | If (_, b_then, b_else, _) -> is_safe_block b_then && is_safe_block b_else + | If (_, b_then, b_else, _) -> + is_safe_block ~goto b_then && is_safe_block ~goto b_else | Block b | Switch (_, b, _, _) - | Loop (_, b, _, _, _) -> is_safe_block b + | Loop (_, b, _, _, _) -> is_safe_block ~goto b | UnspecifiedSequence list -> - List.for_all (fun (stmt, _, _, _, _) -> is_safe_stmt stmt) list - | Goto (dest, _) -> begin - let dest_blocks = Kernel_function.find_all_enclosing_blocks !dest in - (* If the goto leaves the loop, then it is safe. *) - if List.mem loop dest_blocks then true else - (* If the goto moves into the block currently being checked, then it - is safe if the block is safe (which we are currently checking). *) - match !current_block with - | Some current_block when List.mem current_block dest_blocks -> true - | _ -> - (* Otherwise, we need to check that the whole block englobing - both the source and the destination of the goto is safe. *) - let block = Kernel_function.common_block !dest stmt in - current_block := Some block; - (* If this block is the loop itself, then it is not safe, as [lval] - is modified within the loop. *) - not (block = loop) && is_safe_block block - end + List.for_all (fun (stmt, _, _, _, _) -> is_safe_stmt ~goto stmt) list + | Goto (dest, _) -> + (* If [goto] holds, we are already checking a block for a [goto]. Do not + process goto statements again here. *) + goto || + let first_stmt = List.hd loop.bstmts in + (* If the loop cannot be reached from [dest], then it is safe. *) + not (Stmts_graph.stmt_can_reach kf !dest first_stmt) || + (* Otherwise, if the goto leaves the loop, then it forms another loop + that contains the current loop, which we don't want to unroll. *) + let dest_blocks = Kernel_function.find_all_enclosing_blocks !dest in + List.exists (Cil_datatype.Block.equal loop) dest_blocks && + (* Otherwise, the goto stays within the loop: check that the block + englobing both the source and the destination is safe. *) + let block = Kernel_function.common_block !dest stmt in + (* If this block is the loop itself, then it is not safe, + as [lval] is modified within the loop. *) + not (block == loop) && is_safe_block ~goto:true block | _ -> false (* A block is safe if all its statements are safe. *) - and is_safe_block block = List.for_all is_safe_stmt block.bstmts in - is_safe_stmt stmt + and is_safe_block ~goto b = List.for_all (is_safe_stmt ~goto) b.bstmts in + is_safe_stmt ~goto:false stmt module Make (Abstract: Abstractions.Eva) = struct @@ -262,9 +275,23 @@ module Make (Abstract: Abstractions.Eva) = struct (* If the new value of [expr] is top, no reduction has been performed. *) if Val.(equal top value) then None else Some (value, record) in - (* Different strategies whether cvalue is present. *) - match Val.get Main_values.CVal.key with - | Some get_cvalue -> + (* Assumes that [condition] is [positive]. Returns an over-approximation + of the values for which [condition] is [positive]. *) + reduce positive >>= fun (value, record) -> + (* Evaluates [condition] with the hypothesis [expr] ∈ [value], to check + whether [expr] ∈ [value] ⇒ [condition] = [positive]. *) + let valuation = Valuation.add valuation expr record in + fst (Eval.evaluate ~valuation ~reduction:false state condition) + >> fun (_valuation, v) -> + let satisfied = + if positive + then not Val.(is_included zero v) + else Val.(equal zero v) + in + (* If the condition is satisfied, returns [value]. Otherwise, uses a + specific feature to cvalue (if possible) to compute a better value. *) + if satisfied then Some value else + Val.get Main_values.CVal.key >>= fun get_cvalue -> (* Assumes that [condition] is NOT [positive]. *) reduce (not positive) >>= fun (value, _record) -> (* [value] is an over-approximation of the values of [expr] for which @@ -273,21 +300,6 @@ module Make (Abstract: Abstractions.Eva) = struct let cvalue = get_cvalue value in cvalue_complement (Cil.typeOf expr) cvalue >>= fun cvalue -> Some (Val.set Main_values.CVal.key cvalue Val.top) - | None -> - (* Assumes that [condition] is [positive]. Returns an over-approximation - of the values for which [condition] is [positive]. *) - reduce positive >>= fun (value, record) -> - (* Evaluates [condition] with the hypothesis [expr] ∈ [value], to check - whether [expr] ∈ [value] ⇒ [condition] = [positive]. *) - let valuation = Valuation.add valuation expr record in - fst (Eval.evaluate ~valuation ~reduction:false state condition) - >> fun (_valuation, v) -> - let satisfied = - if positive - then not Val.(is_included zero v) - else Val.(equal zero v) - in - if satisfied then Some value else None (* Same as [reduce_to_expr] above, but builds the proper valuation from the [state]. [state] is the entry state of the loop, and [expr] is the only @@ -379,7 +391,7 @@ module Make (Abstract: Abstractions.Eva) = struct should be a direct access to a variable whose address is not taken, and which should not be global if the loop contains function calls. Returns None if no increment can be computed. *) - let compute_delta lval loop = + let compute_delta kf lval loop = let rec delta_stmt acc stmt = match stmt.skind with | Instr instr -> delta_instruction lval acc instr @@ -392,9 +404,11 @@ module Make (Abstract: Abstractions.Eva) = struct | If (_e, b1, b2, _loc) -> join_delta (delta_block acc b1) (delta_block acc b2) | Block b -> delta_block acc b + | UnspecifiedSequence list -> + List.fold_left (fun acc (s, _, _, _, _) -> delta_stmt acc s) acc list | _ -> (* For other statements, we only check that they do not modify [lval]. *) - if is_safe lval ~loop stmt then acc else raise NoIncrement + if is_constant kf ~loop lval stmt then acc else raise NoIncrement and delta_block acc block = List.fold_left delta_stmt acc block.bstmts in @@ -404,6 +418,57 @@ module Make (Abstract: Abstractions.Eva) = struct final_delta delta >> fun d -> Some d with NoIncrement -> None + (* If in the block [loop], [lval] is assigned once to the value of another + lvalue, returns this new lvalue. Otherwise, returns [lval]. *) + let cross_equality lval loop = + (* If no such single equality can be found, return [lval] unchanged. *) + let exception No_equality in + let rec find_lval acc expr = + if acc <> None then raise No_equality else + match expr.enode with + | Lval lval -> Some lval + | Info (e, _) -> find_lval acc e + | _ -> raise No_equality + in + let cross_instr acc = function + | Set (lv, expr, _loc) when Cil_datatype.LvalStructEq.equal lv lval -> + find_lval acc expr + | Local_init (varinfo, AssignInit (SingleInit expr), _loc) + when Cil_datatype.LvalStructEq.equal (Cil.var varinfo) lval -> + find_lval acc expr + | Call (Some lv, _, _, _) when Cil_datatype.LvalStructEq.equal lval lv -> + raise No_equality + | _ -> acc + in + let rec cross_stmt acc stmt = + match stmt.skind with + | Instr instr -> cross_instr acc instr + | Block block -> cross_block acc block + | UnspecifiedSequence list -> + List.fold_left (fun acc (s, _, _, _, _) -> cross_stmt acc s) acc list + | If (_, {bstmts=[{skind=Break _}]}, block, _) + | If (_, block, {bstmts=[{skind=Break _}]}, _) -> cross_block acc block + | _ -> acc + and cross_block acc block = + List.fold_left cross_stmt acc block.bstmts + in + match cross_block None loop with + | None -> lval + | Some lval -> lval + | exception No_equality -> lval + + (* If [lval] is a varinfo out-of-scope at statement [stmt] of function [kf], + introduces it to the [state]. *) + let enter_scope state kf stmt lval = + match lval with + | Var vi, _ when not (vi.vglob || vi.vformal + || Kernel_function.var_is_in_scope stmt vi) -> + let kind = Abstract_domain.Local kf in + let state = Abstract.Dom.enter_scope kind [vi] state in + let location = Abstract.Loc.eval_varinfo vi in + Abstract.Dom.logic_assign None location state + | _ -> state + (* Evaluates the lvalue [lval] in the state [state]. Returns None if the value may be undeterminate. *) let evaluate_lvalue state lval = @@ -412,30 +477,55 @@ module Make (Abstract: Abstractions.Eva) = struct then None else flagged_value.v >> fun v -> Some v + let (>>:) v f = match v with Some v -> f v | None -> false + (* Is the number of iterations of a loop bounded by [limit]? [state] is the loop entry state, and [loop_block] the block of the loop. *) - let is_bounded_loop state limit loop_block = + let is_bounded_loop kf stmt state limit loop_block = (* Computes the effect of the loop. Stops if it contains assembly code. *) - loop_effect_visitor#compute_effect loop_block >>= fun loop_effect -> - (* Finds the first loop exit condition, or stops. *) - find_loop_exit_condition loop_block >>= fun (condition, positive) -> - (* Finds the unique integer lvalue modified within the loop in [condition]. - Stops if it does not exist is not a good candidate for the heuristic. *) - find_lonely_candidate loop_effect condition >>= fun lval -> - (* Reduce [condition] to a sufficient hypothesis over the [lval] value: - if [lval] ∈ [v_exit] then [condition = positive]. *) - reduce_to_lval_from_state state lval condition positive >>= fun v_exit -> - (* Evaluates the initial value [v_init] of [lval] in the loop entry state. *) - evaluate_lvalue state lval >>= fun v_init -> - (* Computes an over-approximation [v_delta] of the increment of [lval] - in one iteration of the loop. *) - compute_delta lval loop_block >>= fun v_delta -> - let typ = Cil.typeOfLval lval in - let limit = Val.inject_int typ (Integer.of_int limit) in - (* Checks whether [v_init] + [limit] × [v_delta] ⊂ [v_exit]. *) - let binop op v1 v2 = Bottom.non_bottom (Val.forward_binop typ op v1 v2) in - let value = binop PlusA v_init (binop Mult limit v_delta) in - Some (Val.is_included value v_exit) + loop_effect_visitor#compute_effect loop_block >>: fun loop_effect -> + (* Finds loop exit conditions. *) + let exit_conditions = find_loop_exit_condition loop_block in + (* Does the condition [condition = positive] limits the number of iterations + of the loop by [limit]? *) + let is_bounded_by_condition (condition, positive) = + (* Finds the unique integer lvalue modified within the loop in [condition]. + Stops if it does not exist is not a good candidate for the heuristic. *) + find_lonely_candidate loop_effect condition >>: fun lval -> + (* If [lval] is not in scope at [stmt], introduces it into [state] so that + [lval] can be properly evaluated in [state]. *) + let state = enter_scope state kf stmt lval in + (* Reduce [condition] to a sufficient hypothesis over the [lval] value: + if [lval] ∈ [v_exit] then [condition = positive]. *) + reduce_to_lval_from_state state lval condition positive >>: fun v_exit -> + (* If [lval] is only assigned to the value of another lvalue, uses it + instead. This is especially useful to deal with temporary variables + introduced by the Frama-C normalization. *) + let lval = cross_equality lval loop_block in + (* Evaluates the initial value [v_init] of [lval] in the loop entry state. *) + evaluate_lvalue state lval >>: fun v_init -> + (* Computes an over-approximation [v_delta] of the increment of [lval] + in one iteration of the loop. *) + compute_delta kf lval loop_block >>: fun v_delta -> + let typ = Cil.typeOfLval lval in + let binop op v1 v2 = Bottom.non_bottom (Val.forward_binop typ op v1 v2) in + (* Possible iterations numbers to exit the loop. *) + let iter_nb = binop Div (binop MinusA v_exit v_init) v_delta in + let bound = Abstract_value.Int (Integer.of_int limit) in + (* Use the iteration number if it is always smaller than the [limit]. + Otherwise use [limit]. *) + let limit = + if is_true (Val.assume_bounded Alarms.Upper_bound bound iter_nb) + then iter_nb + else Val.inject_int typ (Integer.of_int limit) + in + (* Checks whether [v_init] + [limit] × [v_delta] ⊂ [v_exit]. *) + let value = binop PlusA v_init (binop Mult limit v_delta) in + Val.is_included value v_exit + in + (* Tests whether at least one of the exit conditions limits the number of + iteration by [limit]. *) + List.exists is_bounded_by_condition exit_conditions (* Computes an automatic loop unrolling for statement [stmt] in state [state], with a maximum limit. Returns None for no automatic loop unrolling. *) @@ -445,8 +535,9 @@ module Make (Abstract: Abstractions.Eva) = struct let loop_stmt = Kernel_function.find_enclosing_loop kf stmt in match loop_stmt.skind with | Loop (_code_annot, block, _loc, _, _) -> - is_bounded_loop state max_unroll block >>= fun bounded -> - if bounded then Some max_unroll else None + if is_bounded_loop kf stmt state max_unroll block + then Some max_unroll + else None | _ -> None with Not_found -> None end diff --git a/src/plugins/value/utils/structure.ml b/src/plugins/value/utils/structure.ml index 9722347d7d85b1bbd54e823b3f1a5ec5876c4c27..197bad845fccfff841887077319631212f0f0c40 100644 --- a/src/plugins/value/utils/structure.ml +++ b/src/plugins/value/utils/structure.ml @@ -52,7 +52,7 @@ module Make () = struct then Some ((Obj.magic (Eq : (a,a) eq)) : (a,b) eq) else None - let compare x y = Transitioning.Stdlib.compare x.tag y.tag + let compare x y = Stdlib.compare x.tag y.tag let hash x = x.tag let tag x = x.tag diff --git a/src/plugins/value/utils/value_perf.ml b/src/plugins/value/utils/value_perf.ml index bb7ebcf7d266a91dcd7029d0e68bdd9cb61263e7..6dea7f2ed2919b1dce27acfa2f1bc8aaa2d10de9 100644 --- a/src/plugins/value/utils/value_perf.ml +++ b/src/plugins/value/utils/value_perf.ml @@ -88,7 +88,7 @@ module Call_info = struct (* Sorts call_infos by decreasing execution time. *) let cmp current_time ci1 ci2 = - - (Transitioning.Stdlib.compare (total_duration current_time ci1) (total_duration current_time ci2)) + - (Stdlib.compare (total_duration current_time ci1) (total_duration current_time ci2)) ;; (* From an iteration, filter and sort by call_info, and returns the diff --git a/src/plugins/value/values/numerors/numerors_interval.ml b/src/plugins/value/values/numerors/numerors_interval.ml index 4a65ac8f170652a0550f919a21e9ca8bce854744..7f1e0f63bf46db8409b8cec9ab3b9d09e114a71b 100644 --- a/src/plugins/value/values/numerors/numerors_interval.ml +++ b/src/plugins/value/values/numerors/numerors_interval.ml @@ -52,7 +52,7 @@ let prec = function NaN p -> p | I (x, _, _) -> F.prec x let get_max_exponent = function | NaN _ -> Value_parameters.fatal "Numerors: can't return the exponent of a NaN" - | I (x, y, _) -> Transitioning.Stdlib.max (F.exponent x) (F.exponent y) + | I (x, y, _) -> Stdlib.max (F.exponent x) (F.exponent y) let get_exponents = function | NaN _ -> Value_parameters.fatal "Numerors: can't return the exponent of a NaN" @@ -171,7 +171,7 @@ let compare a b = (a, b) >>+ fun _ -> match a, b with | NaN _, NaN _ -> 0 | NaN _, _ -> 1 | _, NaN _ -> -1 | I (x, y, n), I (x', y', n') -> - let c = Transitioning.Stdlib.compare n n' in + let c = Stdlib.compare n n' in if c = 0 then let c = F.compare x x' in if c = 0 then F.compare y y' diff --git a/src/plugins/value/values/numerors/numerors_utils.ml b/src/plugins/value/values/numerors/numerors_utils.ml index 85e6f155b27b6036d1b0671105eca7f02b74f7d2..41d0d26e91ec68440dff2d5433b6afa7217a0efe 100644 --- a/src/plugins/value/values/numerors/numerors_utils.ml +++ b/src/plugins/value/values/numerors/numerors_utils.ml @@ -51,15 +51,15 @@ module Precisions = struct (* Defined by the IEEE-754 standard *) let exponent = function | Simple -> 8 | Double -> 11 - | Long_Double -> 15 | Real -> Transitioning.Stdlib.max_int + | Long_Double -> 15 | Real -> Stdlib.max_int (* Computed as - ((2 - 2^(e-1)) - (m - 1)) where e is the number of bits of the exponent and m is the number of bits of the significand *) let denormalized = function | Simple -> -149 | Double -> -1074 - | Long_Double -> -16494 | Real -> Transitioning.Stdlib.min_int + | Long_Double -> -16494 | Real -> Stdlib.min_int - let compare a b = Transitioning.Stdlib.compare (get a) (get b) + let compare a b = Stdlib.compare (get a) (get b) let eq a b = compare a b = 0 let max a b = if compare a b <= 0 then b else a diff --git a/src/plugins/value/values/sign_value.ml b/src/plugins/value/values/sign_value.ml index 93051f60d086a6613c33d8327a049d52343fb9f8..1a119b7433829124d0aab9784e0611a2baf90ee4 100644 --- a/src/plugins/value/values/sign_value.ml +++ b/src/plugins/value/values/sign_value.ml @@ -54,7 +54,7 @@ let empty = { pos = false; zero = false; neg = false } include Datatype.Make(struct type t = signs include Datatype.Serializable_undefined - let compare = Transitioning.Stdlib.compare + let compare = Stdlib.compare let equal = Datatype.from_compare let hash = Hashtbl.hash let reprs = [top] diff --git a/src/plugins/wp/CfgCompiler.ml b/src/plugins/wp/CfgCompiler.ml index e0f6bda712ed89f21b706b64a417f588963560a2..62b804223326d9a21858e983f7004efe3817d050 100644 --- a/src/plugins/wp/CfgCompiler.ml +++ b/src/plugins/wp/CfgCompiler.ml @@ -437,7 +437,7 @@ struct let pp fmt = function | Node i -> Node.pp fmt i | Assume (i,_) -> Format.fprintf fmt "ass%i" i | Check (i,_) -> Format.fprintf fmt "chk%i" i let equal x y = (tag x) = (tag y) - let compare x y = Transitioning.Stdlib.compare (tag x) (tag y) + let compare x y = Stdlib.compare (tag x) (tag y) let hash x = tag x end in let module G = Graph.Imperative.Digraph.ConcreteBidirectionalLabeled (V)(E) in diff --git a/src/plugins/wp/Cfloat.ml b/src/plugins/wp/Cfloat.ml index 64e1cfd7065d4bd0a9de71a2afe0eb99abc6de41..04ccb8f815113a5a3d9fb346bcac84af4a2f55b3 100644 --- a/src/plugins/wp/Cfloat.ml +++ b/src/plugins/wp/Cfloat.ml @@ -145,7 +145,7 @@ let fmake ulp value = match ulp with | Float32 -> F.e_fun ~result:t32 fq32 [F.e_float (rfloat value)] | Float64 -> F.e_fun ~result:t64 fq64 [F.e_float value] -let qmake ulp q = fmake ulp (Transitioning.Q.to_float q) +let qmake ulp q = fmake ulp (Q.to_float q) let re_mantissa = "\\([-+]?[0-9]*\\)" let re_comma = "\\(.\\(\\(0*[1-9]\\)*\\)0*\\)?" let re_exponent = "\\([eE]\\([-+]?[0-9]*\\)\\)?" @@ -219,8 +219,8 @@ let force_float r = let float_lit ulp (q : Q.t) = let v = match ulp with - | Float32 -> rfloat @@ Transitioning.Q.to_float q - | Float64 -> Transitioning.Q.to_float q in + | Float32 -> rfloat @@ Q.to_float q + | Float64 -> Q.to_float q in let reparse ulp r = match ulp with | Float32 -> rfloat @@ float_of_string r @@ -314,7 +314,7 @@ module Compute = WpContext.StaticGenerator (struct type t = model * c_float * op - let compare = Transitioning.Stdlib.compare + let compare = Stdlib.compare let pretty fmt (m, ft, op) = Format.fprintf fmt "%s_%a_%s" (model_name m) pp_suffix ft (op_name op) diff --git a/src/plugins/wp/Cmath.ml b/src/plugins/wp/Cmath.ml index 5f00300cf14dfba7ae6422c690767924f6cb049a..96b326d43d9dffbadd0e8b9d0631ebae354f2c03 100644 --- a/src/plugins/wp/Cmath.ml +++ b/src/plugins/wp/Cmath.ml @@ -71,7 +71,7 @@ let builtin_truncate f e = begin try (* Waiting for Z-Arith to have truncation to big-int *) - let truncated = int_of_float (Transitioning.Q.to_float r) in + let truncated = int_of_float (Q.to_float r) in let reversed = Q.of_int truncated in let base = F.e_int truncated in if Q.equal r reversed then base else diff --git a/src/plugins/wp/Conditions.ml b/src/plugins/wp/Conditions.ml index 9acc9575ac31a2b3502930433420575c0d39643e..d6e531f56be84457ef273df9c4db17a9d8391ccd 100644 --- a/src/plugins/wp/Conditions.ml +++ b/src/plugins/wp/Conditions.ml @@ -270,7 +270,7 @@ struct | _ -> 2 in let r = rank s1.condition - rank s2.condition in - if r = 0 then Transitioning.Stdlib.compare k2 k1 else r + if r = 0 then Stdlib.compare k2 k1 else r end) type t = Vars.t * SEQ.t diff --git a/src/plugins/wp/Cstring.ml b/src/plugins/wp/Cstring.ml index 7705498957b67213b63c2dc7142c5bd396f6b3f7..d5d2ceb8a4cfb3a0ac8dde152e2d289baec2a90c 100644 --- a/src/plugins/wp/Cstring.ml +++ b/src/plugins/wp/Cstring.ml @@ -35,7 +35,7 @@ type cst = module STR = struct type t = cst - let compare = Transitioning.Stdlib.compare (* only comparable types *) + let compare = Stdlib.compare (* only comparable types *) let pretty fmt = function | C_str s -> Format.fprintf fmt "%S" s | W_str _ -> Format.fprintf fmt "\"L<...>\"" diff --git a/src/plugins/wp/Factory.ml b/src/plugins/wp/Factory.ml index 7d388748ccf4a9b092b3dc9b26d061e00ab97770..f6a1a01a5d9e183c1405b213830f25f60c9d4c80 100644 --- a/src/plugins/wp/Factory.ml +++ b/src/plugins/wp/Factory.ml @@ -34,7 +34,7 @@ type setup = { cfloat : Cfloat.model ; } -(*[LC] All types in [model] must be Pervasives-comparable *) +(*[LC] All types in [model] must be Stdlib-comparable *) type driver = LogicBuiltins.driver @@ -304,7 +304,7 @@ module COMPILERS = Map.Make (struct type t = setup * driver let compare (s,d) (s',d') = - let cmp = Transitioning.Stdlib.compare s s' in + let cmp = Stdlib.compare s s' in if cmp <> 0 then cmp else LogicBuiltins.compare d d' end) diff --git a/src/plugins/wp/Layout.ml b/src/plugins/wp/Layout.ml index 2045b0c8586b82ab08b67d3dd4fcc6d550042a41..d1e8b5795328c83d795393ebc8b699a997b0749b 100644 --- a/src/plugins/wp/Layout.ml +++ b/src/plugins/wp/Layout.ml @@ -52,7 +52,7 @@ struct | _ , Field _ -> 1 | Index(ta,n) , Index(tb,m) -> let cmp = Typ.compare ta tb in - if cmp <> 0 then cmp else Transitioning.Stdlib.compare n m + if cmp <> 0 then cmp else Stdlib.compare n m let equal a b = (compare a b = 0) @@ -175,7 +175,7 @@ struct Usage.pretty fmt usage let compare ((da,ta):t) ((db,tb):t) = - let cmp = Transitioning.Stdlib.compare da db in + let cmp = Stdlib.compare da db in if cmp <> 0 then cmp else Typ.compare ta tb let equal a b = (compare a b = 0) diff --git a/src/plugins/wp/LogicBuiltins.ml b/src/plugins/wp/LogicBuiltins.ml index e713b74ade46db3ccab2d7746acc4c613061270b..e08114243391f957a51271c62bc30c5e258145dd 100644 --- a/src/plugins/wp/LogicBuiltins.ml +++ b/src/plugins/wp/LogicBuiltins.ml @@ -182,14 +182,14 @@ let iter_table f = Hashtbl.iter (fun a sigs -> List.iter (fun (ks,lnk) -> items := (a,ks,lnk)::!items) sigs) (cdriver_ro ()).hlogic ; - List.iter f (List.sort Transitioning.Stdlib.compare !items) + List.iter f (List.sort Stdlib.compare !items) let iter_libs f = let items = ref [] in Hashtbl.iter (fun a libs -> items := (a,libs) :: !items) (cdriver_ro ()).hdeps ; - List.iter f (List.sort Transitioning.Stdlib.compare !items) + List.iter f (List.sort Stdlib.compare !items) let dump () = Log.print_on_output diff --git a/src/plugins/wp/MemRegion.ml b/src/plugins/wp/MemRegion.ml index 427bd92700c4405e3c9b2c2abc65e405af971fa8..3723ce4232264c2faccafea2a67ae8216a808eab 100644 --- a/src/plugins/wp/MemRegion.ml +++ b/src/plugins/wp/MemRegion.ml @@ -234,7 +234,7 @@ struct Format.fprintf fmt "}@]" ; end - let compare = Transitioning.Stdlib.compare + let compare = Stdlib.compare (* Extract constant offset *) let offset k = @@ -300,7 +300,7 @@ module ARRAY = struct type t = int * int list - let compare = Transitioning.Stdlib.compare + let compare = Stdlib.compare let pretty fmt (s,ds) = Format.fprintf fmt "%d%a" s Layout.Matrix.pretty ds (* Coefficient from Matrix dimensions: c_i = \Pi_{i<j} d_j *) @@ -637,7 +637,7 @@ struct let hash m = id m let compare m m' = - if m==m then 0 else Transitioning.Stdlib.compare (id m) (id m') + if m==m then 0 else Stdlib.compare (id m) (id m') let equal m m' = m==m' || (id m = id m') let tau_of_value = function diff --git a/src/plugins/wp/MemTyped.ml b/src/plugins/wp/MemTyped.ml index 90311c627cfb9ae4ae5c87405d93f58aea8179c5..6743fc7a086ad6307be7534c6fe25eeff69917dd 100644 --- a/src/plugins/wp/MemTyped.ml +++ b/src/plugins/wp/MemTyped.ml @@ -390,7 +390,7 @@ let shift l obj k = e_fun (Shift.get obj) [l;k] module LITERAL = struct type t = int * Cstring.cst - let compare (a:t) (b:t) = Transitioning.Stdlib.compare (fst a) (fst b) + let compare (a:t) (b:t) = Stdlib.compare (fst a) (fst b) let pretty fmt (eid,cst) = Format.fprintf fmt "%a@%d" Cstring.pretty cst eid end diff --git a/src/plugins/wp/ProverScript.ml b/src/plugins/wp/ProverScript.ml index e2e434090d6de3baeb9aa0430a998943ec8bb00f..9458666cfc60d2dcc01e346c2aba174f0f1afde5 100644 --- a/src/plugins/wp/ProverScript.ml +++ b/src/plugins/wp/ProverScript.ml @@ -47,7 +47,7 @@ struct let sa = stage a in let sb = stage b in if sa = sb - then Transitioning.Stdlib.compare (time a) (time b) + then Stdlib.compare (time a) (time b) else sa - sb let sort script = List.stable_sort compare script diff --git a/src/plugins/wp/Region.ml b/src/plugins/wp/Region.ml index de1feb7484424035fcbc68725ce611bcf1a17984..0de209c12f6bf8041c70125a7fef262e4c327e6d 100644 --- a/src/plugins/wp/Region.ml +++ b/src/plugins/wp/Region.ml @@ -128,7 +128,7 @@ struct type t = region let id a = a.id let equal a b = (a.id = b.id) - let compare a b = Transitioning.Stdlib.compare a.id b.id + let compare a b = Stdlib.compare a.id b.id let pp_rid fmt id = Format.fprintf fmt "R%03d" id let pretty fmt r = pp_rid fmt r.id end diff --git a/src/plugins/wp/Splitter.ml b/src/plugins/wp/Splitter.ml index b5930b40748cf9752bc470ee58ebb5c1eb70baf0..32dfd8adf7a1bd0952154b8321635a33af8211f1 100644 --- a/src/plugins/wp/Splitter.ml +++ b/src/plugins/wp/Splitter.ml @@ -63,7 +63,7 @@ let compare p q = | _ , ELSE _ -> 1 | CASE(s1,k1) , CASE(s2,k2) -> let c = Stmt.compare s1 s2 in - if c = 0 then Transitioning.Stdlib.compare k1 k2 else c + if c = 0 then Stdlib.compare k1 k2 else c | CASE _ , _ -> (-1) | _ , CASE _ -> 1 | DEFAULT s , DEFAULT t -> Stmt.compare s t @@ -75,7 +75,7 @@ let compare p q = | CALL _ , _ -> (-1) | _ , CALL _ -> 1 | ASSERT(ip1,k1,_) , ASSERT(ip2,k2,_) -> - let c = Transitioning.Stdlib.compare ip1.ip_id ip2.ip_id in + let c = Stdlib.compare ip1.ip_id ip2.ip_id in if c = 0 then k1 - k2 else c (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/StmtSemantics.ml b/src/plugins/wp/StmtSemantics.ml index 22b7335e91ecd96befa323fb73703dcab40a81ba..234884282f5195ff5ffad5ee1f4368b93b9537cb 100644 --- a/src/plugins/wp/StmtSemantics.ml +++ b/src/plugins/wp/StmtSemantics.ml @@ -499,7 +499,7 @@ struct | NoneInfo, NoneInfo -> 0 | NoneInfo, _ -> -1 | _ , NoneInfo -> 1 - | LoopHead i, LoopHead j -> Transitioning.Stdlib.compare j i + | LoopHead i, LoopHead j -> Stdlib.compare j i module Automata = Interpreted_automata.UnrollUnnatural.Version type nodes = { diff --git a/src/plugins/wp/Strategy.ml b/src/plugins/wp/Strategy.ml index 1ce102defda986389209d719d68e8a0b6077e1c9..cf232eaa0dcd6b43601e9ec08574d6c03d827cfe 100644 --- a/src/plugins/wp/Strategy.ml +++ b/src/plugins/wp/Strategy.ml @@ -104,7 +104,7 @@ type strategy = { arguments : argument list ; } and t = strategy -let highest a b = Transitioning.Stdlib.compare b.priority a.priority +let highest a b = Stdlib.compare b.priority a.priority class pool = object diff --git a/src/plugins/wp/VCS.ml b/src/plugins/wp/VCS.ml index c92b0ca59fb985c1a5b76353ce52de64594e1ba7..1a855c3bbbe474b41d5fb2df7d0fc903235b27e0 100644 --- a/src/plugins/wp/VCS.ml +++ b/src/plugins/wp/VCS.ml @@ -354,11 +354,11 @@ let compare p q = in let r = rank q.verdict - rank p.verdict in if r <> 0 then r else - let s = Transitioning.Stdlib.compare p.prover_steps q.prover_steps in + let s = Stdlib.compare p.prover_steps q.prover_steps in if s <> 0 then s else - let t = Transitioning.Stdlib.compare p.prover_time q.prover_time in + let t = Stdlib.compare p.prover_time q.prover_time in if t <> 0 then t else - Transitioning.Stdlib.compare p.solver_time q.solver_time + Stdlib.compare p.solver_time q.solver_time let combine v1 v2 = match v1 , v2 with diff --git a/src/plugins/wp/Warning.ml b/src/plugins/wp/Warning.ml index f5c88a6dddb922970b978f3f6c85f60026691115..00664bb1670f5f34b80f9d0685150e8854932e81 100644 --- a/src/plugins/wp/Warning.ml +++ b/src/plugins/wp/Warning.ml @@ -48,7 +48,7 @@ struct match w1.severe , w2.severe with | true , false -> (-1) | false , true -> 1 - | _ -> Transitioning.Stdlib.compare w1 w2 + | _ -> Stdlib.compare w1 w2 end diff --git a/src/plugins/wp/Why3Provers.ml b/src/plugins/wp/Why3Provers.ml index 232c05888e6f00a5ba13a24e09495becd39a764c..ab50c9858b2de0a3fec9d8adc147994a3e0db981 100644 --- a/src/plugins/wp/Why3Provers.ml +++ b/src/plugins/wp/Why3Provers.ml @@ -46,9 +46,9 @@ let configure = begin try Arg.parse_argv ~current:(ref 0) args (Why3.Debug.Args.[desc_debug;desc_debug_all;desc_debug_list]) - (fun _ -> raise (Arg.Help "Unknown why3 option")) + (fun opt -> raise (Arg.Bad ("unknown option: " ^ opt))) "Why3 options" - with Arg.Bad s -> Wp_parameters.abort "%s" s + with Arg.Bad s | Arg.Help s -> Wp_parameters.abort "%s" s end; ignore (Why3.Debug.Args.option_list ()); Why3.Debug.Args.set_flags_selected (); diff --git a/src/plugins/wp/clabels.mli b/src/plugins/wp/clabels.mli index cb577648c59219916a8e69b3b22e7e95f8145423..26bdbfb4fcbdce2eb8ea16e372f9147ba91b926e 100644 --- a/src/plugins/wp/clabels.mli +++ b/src/plugins/wp/clabels.mli @@ -26,7 +26,7 @@ (** Structural representation of logic labels. - Compatible with pervasives comparison and structural equality. + Compatible with stdlib comparison and structural equality. *) type c_label diff --git a/src/plugins/wp/ctypes.ml b/src/plugins/wp/ctypes.ml index 0dcff7411efc6a4cd32044713b52b6f597d4d1f7..303790f3b75d254f3e616e6b92cfa919eafbe3fb 100644 --- a/src/plugins/wp/ctypes.ml +++ b/src/plugins/wp/ctypes.ml @@ -323,7 +323,7 @@ module AinfoComparable = struct let c = !cmp obj_a obj_b in if c <> 0 then c else match a.arr_flat , b.arr_flat with - | Some a , Some b -> Transitioning.Stdlib.compare a.arr_size b.arr_size + | Some a , Some b -> Stdlib.compare a.arr_size b.arr_size | None , Some _ -> (-1) | Some _ , None -> 1 | None , None -> 0 @@ -599,7 +599,7 @@ and compare_array_ptr_conflated a b = let c = compare_ptr_conflated obj_a obj_b in if c <> 0 then c else match a.arr_flat , b.arr_flat with - | Some a , Some b -> Transitioning.Stdlib.compare a.arr_size b.arr_size + | Some a , Some b -> Stdlib.compare a.arr_size b.arr_size | None , Some _ -> (-1) | Some _ , None -> 1 | None , None -> 0 diff --git a/src/plugins/wp/proof.ml b/src/plugins/wp/proof.ml index 9a6c21a585f181c0fe9e197fb8deed89b835d302..795ac2b52701be870f3507aab0d7a13e880c7f18 100644 --- a/src/plugins/wp/proof.ml +++ b/src/plugins/wp/proof.ml @@ -225,7 +225,7 @@ let update_hints_for_goal goal hints = try let old_hints,script,qed = Hashtbl.find scriptbase goal in let new_hints = List.sort String.compare hints in - if Transitioning.Stdlib.compare new_hints old_hints <> 0 then + if Stdlib.compare new_hints old_hints <> 0 then begin Hashtbl.replace scriptbase goal (new_hints,script,qed) ; needsave := true ; diff --git a/src/plugins/wp/wpPropId.ml b/src/plugins/wp/wpPropId.ml index 9821c657890900dac6b7ba825e5dc6c5f025f2d1..7b694b72a677719f0c8bd1f6334bd8c24ceeb5d3 100644 --- a/src/plugins/wp/wpPropId.ml +++ b/src/plugins/wp/wpPropId.ml @@ -216,7 +216,7 @@ let compare_kind k1 k2 = match k1, k2 with if cmp <> 0 then cmp else Property.compare p1 p2 - | _,_ -> Transitioning.Stdlib.compare (kind_order k1) (kind_order k2) + | _,_ -> Stdlib.compare (kind_order k1) (kind_order k2) let compare_prop_id pid1 pid2 = (* This order of comparison groups together prop_pids with same properties *) @@ -228,7 +228,7 @@ let compare_prop_id pid1 pid2 = let cmp = compare_kind pid2.p_kind pid1.p_kind in if cmp <> 0 then cmp else - Transitioning.Stdlib.compare pid1.p_part pid2.p_part + Stdlib.compare pid1.p_part pid2.p_part module PropId = Datatype.Make_with_collections( diff --git a/tests/fc_script/main.c b/tests/fc_script/main.c index 74219d5a7189f16ec2c67693fda369b891036493..d3e15e1b85ec9976597846dcbf9b4be241b9e69b 100644 --- a/tests/fc_script/main.c +++ b/tests/fc_script/main.c @@ -1,6 +1,6 @@ /* run.config NOFRAMAC: testing frama-c-script, not frama-c itself - EXECNOW: LOG GNUmakefile LOG make_template.res LOG make_template.err PTESTS_TESTING= bin/frama-c-script make-template @PTEST_DIR@/result < @PTEST_DIR@/make_template.input > @PTEST_DIR@/result/make_template.res 2> @PTEST_DIR@/result/make_template.err + EXECNOW: LOG GNUmakefile LOG make_template.res LOG make_template.err cd @PTEST_DIR@ && PTESTS_TESTING= ../../bin/frama-c-script make-template result < make_template.input > result/make_template.res 2> result/make_template.err EXECNOW: LOG list_files.res LOG list_files.err bin/frama-c-script list-files @PTEST_DIR@/list_files.json > @PTEST_DIR@/result/list_files.res 2> @PTEST_DIR@/result/list_files.err EXECNOW: LOG flamegraph.html LOG flamegraph.res LOG flamegraph.err NOGUI=1 bin/frama-c-script flamegraph @PTEST_DIR@/flamegraph.txt @PTEST_DIR@/result > @PTEST_DIR@/result/flamegraph.res 2> @PTEST_DIR@/result/flamegraph.err && rm -f @PTEST_DIR@/result/flamegraph.svg EXECNOW: LOG find_fun1.res LOG find_fun1.err bin/frama-c-script find-fun main2 @PTEST_DIR@ > @PTEST_DIR@/result/find_fun1.res 2> @PTEST_DIR@/result/find_fun1.err diff --git a/tests/fc_script/make_template.input b/tests/fc_script/make_template.input index 62488cfcb339f48b9263e611e5e7422074143ddb..6963df94935110ccdf0218b25973e3348c9e1947 100644 --- a/tests/fc_script/make_template.input +++ b/tests/fc_script/make_template.input @@ -1,7 +1,10 @@ +y fc_script_main -file1.c file*.c dir/more_files.c +for-find*.c main*.c +y y y invalid_machdep n x86_64 +y diff --git a/tests/fc_script/oracle/GNUmakefile b/tests/fc_script/oracle/GNUmakefile index 44f773a61a3c01b2d46d6407f2cda92926b65d7d..5b9a464e26fd809740a93a417ee458e204eb115c 100644 --- a/tests/fc_script/oracle/GNUmakefile +++ b/tests/fc_script/oracle/GNUmakefile @@ -1,68 +1,50 @@ -# TEMPLATE FOR MAKEFILE TO USE IN FRAMA-C/EVA CASE STUDIES - -# DO NOT EDIT THE LINES BETWEEN THE '#'S - -############################################################################### -# Improves analysis time, at the cost of extra memory usage -export FRAMA_C_MEMORY_FOOTPRINT = 8 -# -# frama-c-path.mk contains variables which are specific to each -# user and should not be versioned, such as the path to the -# frama-c binaries (e.g. FRAMAC and FRAMAC_GUI). -# It is an optional include, unnecessary if frama-c is in the PATH --include frama-c-path.mk -# -# FRAMAC is defined in frama-c-path.mk when it is included, so the -# line below will be safely ignored if this is the case -FRAMAC ?= frama-c -# -# frama-c.mk contains the main rules and targets --include $(shell $(FRAMAC)-config -print-share-path)/analysis-scripts/frama-c.mk -# +# Makefile template for Frama-C/Eva case studies. +# For details and usage information, see the Frama-C User Manual. + +### Prologue. Do not modify this block. ####################################### +-include path.mk # path.mk contains variables specific to each user + # (e.g. FRAMAC, FRAMAC_GUI) and should not be versioned. It is + # an optional include, unnecessary if frama-c is in the PATH. +FRAMAC ?= frama-c # FRAMAC is defined in path.mk when it is included, but the + # user can override it in the command-line. +include $(shell $(FRAMAC)-config -scripts)/prologue.mk ############################################################################### -# EDIT VARIABLES AND TARGETS BELOW AS NEEDED -# The flags below are only suggestions to use with Eva, and can be removed +# Edit below as needed. Suggested flags are optional. + +MACHDEP = x86_64 -# (Optional) preprocessing flags, usually handled by -json-compilation-database -CPPFLAGS += +## Preprocessing flags (for -cpp-extra-args) +CPPFLAGS += \ -# (Optional) Frama-C general flags (parsing and kernel) +## General flags FCFLAGS += \ + -json-compilation-database ../compile_commands.json \ -main eva_main \ - -machdep x86_64 \ - -json-compilation-database . \ + -add-symbolic-path=.:.. \ -kernel-warn-key annot:missing-spec=abort \ -kernel-warn-key typing:implicit-function-declaration=abort \ -# (Optional) Eva-specific flags +## Eva-specific flags EVAFLAGS += \ -eva-warn-key builtins:missing-spec=abort \ -# (MANDATORY) Name of the main target -MAIN_TARGET := fc_script_main +## GUI-only flags +FCGUIFLAGS += \ + -add-symbolic-path=.:.. \ -# Add other targets if needed -TARGETS = $(MAIN_TARGET).eva +## Analysis targets (suffixed with .eva) +TARGETS = fc_script_main.eva -# Default target -all: $(TARGETS) +### Each target <t>.eva needs a rule <t>.parse with source files as prerequisites +fc_script_main.parse: \ + fc_stubs.c \ + ../for-find-fun.c \ + ../for-find-fun2.c \ + ../main.c \ + ../main2.c \ + ../main3.c \ -# (MANDATORY) List of source files used by MAIN_TARGET. -# If there is a JSON compilation database, -# 'frama-c-script list-files' can help obtain it -$(MAIN_TARGET).parse: fc_stubs.c file1.c file*.c dir/more_files.c - - -# The following targets are optional and provided for convenience only -parse: $(TARGETS:%.eva=%.parse) -loop: $(TARGETS:%.eva=%.parse.loop) $(TARGETS:%=%.loop) -gui: $(MAIN_TARGET).eva.gui - -# Run 'make <TARGET>.eva.loop' to obtain a .loop file, fine-tune it by hand, -# then rename it to <TARGET>.slevel to prevent it from being overwritten. -# If such file exists, use it to define per-function slevel values. -ifneq (,$(wildcard $(MAIN_TARGET).slevel)) -$(MAIN_TARGET).eva: \ - EVAFLAGS += $(shell cat $(MAIN_TARGET).slevel | tr -d '\n\\') -endif +### Epilogue. Do not modify this block. ####################################### +include $(shell $(FRAMAC)-config -scripts)/epilogue.mk +############################################################################### diff --git a/tests/fc_script/oracle/make_template.res b/tests/fc_script/oracle/make_template.res index 9f38d9c7e745cbea6fe458c9fc77cdcd03f48e1e..eb6734b3ca4224a9a3b0f2b872a3d83b8317b4f6 100644 --- a/tests/fc_script/oracle/make_template.res +++ b/tests/fc_script/oracle/make_template.res @@ -1,6 +1,15 @@ +Preparing template: result/GNUmakefile Running ptests: setting up mock files... -Main target name: Source files separated by spaces (default if empty: *.c): compile_commands.json exists, add option -json-compilation-database? [Y/n] Add stub for function main (only needed if it uses command-line arguments)? [y/N] Please define the architectural model (machdep) of the target machine. +warning: result/GNUmakefile already exists. Overwrite? [y/N] Main target name: Source files (default: **/*.c): The following sources were matched (relative to result): + ../for-find-fun.c + ../for-find-fun2.c + ../main.c # defines 'main' + ../main2.c + ../main3.c # defines 'main' + +warning: 'main' seems to be defined multiple times. +Is this ok? [Y/n] compile_commands.json exists, add option -json-compilation-database? [Y/n] Add stub for function main (only needed if it uses command-line arguments)? [y/N] Please define the architectural model (machdep) of the target machine. Known machdeps: x86_16 x86_32 x86_64 gcc_x86_16 gcc_x86_32 gcc_x86_64 ppc_32 msvc_x86_64 -Please enter the machdep [x86_32]: 'invalid_machdep' is not a standard machdep. Proceed anyway? [y/N]Please enter the machdep [x86_32]: Created stub for main function: fc_stubs.c -Template created: GNUmakefile +Please enter the machdep [x86_32]: 'invalid_machdep' is not a standard machdep. Proceed anyway? [y/N]Please enter the machdep [x86_32]: warning: result/fc_stubs.c already exists. Overwrite? [y/N] Created stub for main function: result/fc_stubs.c +Template created: result/GNUmakefile Running ptests: cleaning up after tests... diff --git a/tests/syntax/string_concat.c b/tests/syntax/string_concat.c index 9669f9687ef7cf8cc60d35c0a1ce2f5b1610b5d3..924e8b423c7f30720939ddfb15b23b5c78ddc01b 100644 --- a/tests/syntax/string_concat.c +++ b/tests/syntax/string_concat.c @@ -1,5 +1,5 @@ /* run.config* -TIMEOUT: 45s +TIMEOUT: 600 OPT: -eva */ diff --git a/tests/syntax/wstring_concat.c b/tests/syntax/wstring_concat.c index 86d7d3ff4ea605a03428e8bcdae6e5be9ea69642..76f68ddd1b187f24f22591786cda9f13ce1a8209 100644 --- a/tests/syntax/wstring_concat.c +++ b/tests/syntax/wstring_concat.c @@ -1,5 +1,5 @@ /* run.config* -TIMEOUT: 45s +TIMEOUT: 600 OPT: -eva */ diff --git a/tests/value/auto_loop_unroll.c b/tests/value/auto_loop_unroll.c index d3a1a1ce141f3d23490482ee26cd1bd80b54995b..c713c76af25052cc62fbe9a6f24e02b9ab85a9cf 100644 --- a/tests/value/auto_loop_unroll.c +++ b/tests/value/auto_loop_unroll.c @@ -81,14 +81,6 @@ void various_loops () { } Frama_C_show_each_32_80(res); res = 0; - /* Other loop breaking condition. */ - for (int i = 0; i < 111; i++) { - res++; - if (undet && res > 10) - break; - } - Frama_C_show_each_11_111(res); - res = 0; /* More complex loop condition. */ int x = 24; int k = Frama_C_interval(0, 10); @@ -96,6 +88,12 @@ void various_loops () { res++; Frama_C_show_each_40_50(res); res = 0; + /* Global loop counter. */ + for (g = 0; g < 101; g++) { + res++; + } + Frama_C_show_each_101(res); + res = 0; /* Loop calling some functions that do not modify the loop counter. */ for (int i = 0; i < 25; i++) { incr_g(); @@ -113,6 +111,16 @@ void various_loops () { } Frama_C_show_each_120(res); res = 0; + /* Loop counter modified on both sides of a conditional statement. */ + for (int i = 0; i < 64;) { + if (undet) + i++; + else + i+=2; + res++; + } + Frama_C_show_each_32_64(res); + res = 0; } /* Loops that cannot be unrolled. */ @@ -120,8 +128,9 @@ void complex_loops () { /* Loop counter modified through a pointer. */ int res = 0; int i = 0; - int *p = &i; - while (i < 64) { + int j = 0; + int *p = &j; + while (j < 64) { (*p)++; res++; } @@ -147,17 +156,7 @@ void complex_loops () { } Frama_C_show_each_imprecise(res); res = 0; - i = 0; - while (i < 10) { - if (undet) - i++; - else - i++; - res++; - } - Frama_C_show_each_imprecise(res); /* Loop counter modified by a function. */ - res = 0; g = 0; while (g < 64) { incr_g(); @@ -177,16 +176,124 @@ void complex_loops () { res = 0; /* Random loop condition. */ i = 0; - while (i < 64 && undet) { + while (i < 64 || undet) { i++; res++; } Frama_C_show_each_imprecise(res); } +/* Examples of loops with other exit conditions than simple comparisons. + All loops could be automatically unrolled on the second run, but should + not be unrolled on the first run. */ +void various_conditions () { + int i, res = 0; + /* Exit conditions using equality. */ + for (i = 11; i; i--) { + res++; + } + Frama_C_show_each_11(res); + res = 0; + for (i = 0; i != 12; i++) { + res++; + } + Frama_C_show_each_12(res); + res = 0; + /* Loops with conjonction of exit conditions. */ + for (int i = 13 ; i-- && undet; ) { + res ++; + } + Frama_C_show_each_0_13(res); + res = 0; + for (int i = 14 ; undet && i-- ; ) { + res ++; + } + Frama_C_show_each_0_14(res); + res = 0; + /* Loops with several exit conditions. */ + for (int i = 0 ; undet ; i++) { + if (undet || i >= 15) + break; + res ++; + } + Frama_C_show_each_0_15(res); + res = 0; + for (int i = 0; i < 111; i++) { + res++; + if (undet && res > 10) + break; + } + Frama_C_show_each_11_111(res); + res = 0; +} + +/* Examples of loops where temporary variables are introduced by Frama-C. + All loops could be automatically unrolled on the second run, but should + not be unrolled on the first run. */ +void temporary_variables () { + int i, res = 0; + for (i = 0; i++ < 20;) { + res++; + } + Frama_C_show_each_20(res); + res = 0; + for (i = 21; i--;) { + res++; + } + Frama_C_show_each_21(res); +} + +/* Examples of loops with goto statements. */ +void loops_with_goto () { + int i, res = 0; + for (i = 0; i < 30; i++) { + res++; + if (undet) + goto middle; + } + Frama_C_show_each_30(res); + res = 0; + middle: ; + /* Should never be unrolled. */ + for (i = 0; i < 31; i++) { + res++; + if (undet) + goto middle; + } + Frama_C_show_each_top(res); + res = 0; + /* Should be unrolled, and [res] should be precise. */ + for (i = 0; i < 32; i++) { + res++; + if (undet) + goto L1; + L1:; + } + Frama_C_show_each_32(res); + res = 0; + /* Should be unrolled, but [res] should still be imprecise. */ + for (i = 0; i < 33; i++) { + L2:res++; + if (undet) + goto L2; + } + Frama_C_show_each_33_inf(res); + res = 0; + /* Should never be unrolled. */ + for (i = 0; i < 34; res++) { + L3:i++; + if (undet) + goto L3; + } + Frama_C_show_each_top(res); + res = 0; +} void main () { simple_loops (); various_loops (); complex_loops (); + various_conditions (); + temporary_variables (); + loops_with_goto (); } diff --git a/tests/value/oracle/auto_loop_unroll.0.res.oracle b/tests/value/oracle/auto_loop_unroll.0.res.oracle index 359bdb60f2fc65fdc7521795c3637c8ef6d68cbd..6f274e90fbf7abce23780edf92b50af50d198603 100644 --- a/tests/value/oracle/auto_loop_unroll.0.res.oracle +++ b/tests/value/oracle/auto_loop_unroll.0.res.oracle @@ -6,7 +6,7 @@ undet ∈ [--..--] g ∈ {0} [eva] computing for function simple_loops <- main. - Called from tests/value/auto_loop_unroll.c:189. + Called from tests/value/auto_loop_unroll.c:293. [eva] tests/value/auto_loop_unroll.c:24: starting to merge loop iterations [eva:alarm] tests/value/auto_loop_unroll.c:25: Warning: signed overflow. assert res + 1 ≤ 2147483647; @@ -27,7 +27,7 @@ [eva] Recording results for simple_loops [eva] Done for function simple_loops [eva] computing for function various_loops <- main. - Called from tests/value/auto_loop_unroll.c:190. + Called from tests/value/auto_loop_unroll.c:294. [eva] tests/value/auto_loop_unroll.c:57: starting to merge loop iterations [eva:alarm] tests/value/auto_loop_unroll.c:58: Warning: signed overflow. assert res + 1 ≤ 2147483647; @@ -45,164 +45,235 @@ signed overflow. assert res + 1 ≤ 2147483647; [eva] tests/value/auto_loop_unroll.c:82: Frama_C_show_each_32_80: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:85: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:86: Warning: - signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:90: - Frama_C_show_each_11_111: [0..2147483647] [eva] computing for function Frama_C_interval <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:94. + Called from tests/value/auto_loop_unroll.c:86. [eva] using specification for function Frama_C_interval -[eva] tests/value/auto_loop_unroll.c:94: +[eva] tests/value/auto_loop_unroll.c:86: function Frama_C_interval: precondition 'order' got status valid. [eva] Done for function Frama_C_interval -[eva] tests/value/auto_loop_unroll.c:95: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:96: Warning: +[eva] tests/value/auto_loop_unroll.c:87: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:88: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:97: +[eva] tests/value/auto_loop_unroll.c:89: Frama_C_show_each_40_50: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:92: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:93: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:95: Frama_C_show_each_101: [0..2147483647] [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr -[eva] tests/value/auto_loop_unroll.c:100: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:98: starting to merge loop iterations [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:103. + Called from tests/value/auto_loop_unroll.c:101. [eva] Recording results for incr [eva] Done for function incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g -[eva] tests/value/auto_loop_unroll.c:102: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:100: Reusing old results for call to incr [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:103. + Called from tests/value/auto_loop_unroll.c:101. [eva] Recording results for incr [eva] Done for function incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva:alarm] tests/value/auto_loop_unroll.c:14: Warning: signed overflow. assert g + 1 ≤ 2147483647; [eva] Recording results for incr_g [eva] Done for function incr_g -[eva] tests/value/auto_loop_unroll.c:102: Reusing old results for call to incr -[eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:103. -[eva] Recording results for incr -[eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr_g -[eva] tests/value/auto_loop_unroll.c:102: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:100: Reusing old results for call to incr [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:103. -[eva] Recording results for incr -[eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr_g -[eva] tests/value/auto_loop_unroll.c:102: Reusing old results for call to incr -[eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:103. + Called from tests/value/auto_loop_unroll.c:101. [eva:alarm] tests/value/auto_loop_unroll.c:18: Warning: signed overflow. assert i + 1 ≤ 2147483647; [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:105: Frama_C_show_each_25: [0..2147483647] -[eva:loop-unroll] tests/value/auto_loop_unroll.c:110: Automatic loop unrolling. -[eva] tests/value/auto_loop_unroll.c:109: starting to merge loop iterations -[eva] tests/value/auto_loop_unroll.c:110: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:111: Warning: +[eva] tests/value/auto_loop_unroll.c:103: Frama_C_show_each_25: [0..2147483647] +[eva:loop-unroll] tests/value/auto_loop_unroll.c:108: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:107: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:108: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:109: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:114: Frama_C_show_each_120: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:112: Frama_C_show_each_120: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:115: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:120: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:122: + Frama_C_show_each_32_64: [0..2147483647] [eva] Recording results for various_loops [eva] Done for function various_loops [eva] computing for function complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:191. -[eva] tests/value/auto_loop_unroll.c:124: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:126: Warning: - signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:128: - Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:132: starting to merge loop iterations + Called from tests/value/auto_loop_unroll.c:295. [eva] tests/value/auto_loop_unroll.c:133: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:134: Warning: - signed overflow. assert i + 1 ≤ 2147483647; -[eva:alarm] tests/value/auto_loop_unroll.c:137: Warning: - signed overflow. assert i + 1 ≤ 2147483647; -[eva:alarm] tests/value/auto_loop_unroll.c:136: Warning: +[eva:alarm] tests/value/auto_loop_unroll.c:135: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:139: +[eva] tests/value/auto_loop_unroll.c:137: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:143: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:141: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:142: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:143: Warning: + signed overflow. assert i + 1 ≤ 2147483647; [eva:alarm] tests/value/auto_loop_unroll.c:146: Warning: + signed overflow. assert i + 1 ≤ 2147483647; +[eva:alarm] tests/value/auto_loop_unroll.c:145: Warning: signed overflow. assert res + 1 ≤ 2147483647; [eva] tests/value/auto_loop_unroll.c:148: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:151: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:156: Warning: +[eva] tests/value/auto_loop_unroll.c:152: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:155: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:158: +[eva] tests/value/auto_loop_unroll.c:157: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:163: Reusing old results for call to incr_g -[eva] tests/value/auto_loop_unroll.c:162: starting to merge loop iterations [eva] computing for function incr_g <- complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:163. + Called from tests/value/auto_loop_unroll.c:162. [eva] Recording results for incr_g [eva] Done for function incr_g +[eva] tests/value/auto_loop_unroll.c:161: starting to merge loop iterations [eva] computing for function incr_g <- complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:163. + Called from tests/value/auto_loop_unroll.c:162. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr_g <- complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:163. + Called from tests/value/auto_loop_unroll.c:162. [eva] Recording results for incr_g [eva] Done for function incr_g -[eva] tests/value/auto_loop_unroll.c:163: Reusing old results for call to incr_g -[eva] tests/value/auto_loop_unroll.c:163: Reusing old results for call to incr_g -[eva:alarm] tests/value/auto_loop_unroll.c:165: Warning: +[eva] computing for function incr_g <- complex_loops <- main. + Called from tests/value/auto_loop_unroll.c:162. +[eva] Recording results for incr_g +[eva] Done for function incr_g +[eva] tests/value/auto_loop_unroll.c:162: Reusing old results for call to incr_g +[eva] tests/value/auto_loop_unroll.c:162: Reusing old results for call to incr_g +[eva:alarm] tests/value/auto_loop_unroll.c:164: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:167: +[eva] tests/value/auto_loop_unroll.c:166: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:172: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:174: Warning: +[eva] tests/value/auto_loop_unroll.c:171: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:173: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:176: +[eva] tests/value/auto_loop_unroll.c:175: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:180: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:182: Warning: +[eva] tests/value/auto_loop_unroll.c:179: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:181: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:184: +[eva:alarm] tests/value/auto_loop_unroll.c:180: Warning: + signed overflow. assert i + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:183: Frama_C_show_each_imprecise: [0..2147483647] [eva] Recording results for complex_loops [eva] Done for function complex_loops +[eva] computing for function various_conditions <- main. + Called from tests/value/auto_loop_unroll.c:296. +[eva] tests/value/auto_loop_unroll.c:192: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:193: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:195: Frama_C_show_each_11: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:197: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:198: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:200: Frama_C_show_each_12: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:203: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:204: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva:alarm] tests/value/auto_loop_unroll.c:203: Warning: + signed overflow. assert -2147483648 ≤ i_0 - 1; +[eva] tests/value/auto_loop_unroll.c:206: + Frama_C_show_each_0_13: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:208: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:209: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva:alarm] tests/value/auto_loop_unroll.c:208: Warning: + signed overflow. assert -2147483648 ≤ i_1 - 1; +[eva] tests/value/auto_loop_unroll.c:211: + Frama_C_show_each_0_14: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:214: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:217: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:219: + Frama_C_show_each_0_15: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:221: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:222: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:226: + Frama_C_show_each_11_111: [0..2147483647] +[eva] Recording results for various_conditions +[eva] Done for function various_conditions +[eva] computing for function temporary_variables <- main. + Called from tests/value/auto_loop_unroll.c:297. +[eva] tests/value/auto_loop_unroll.c:235: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:235: Warning: + signed overflow. assert i + 1 ≤ 2147483647; +[eva:alarm] tests/value/auto_loop_unroll.c:236: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:238: Frama_C_show_each_20: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:240: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:241: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva:alarm] tests/value/auto_loop_unroll.c:240: Warning: + signed overflow. assert -2147483648 ≤ i - 1; +[eva] tests/value/auto_loop_unroll.c:243: Frama_C_show_each_21: [0..2147483647] +[eva] Recording results for temporary_variables +[eva] Done for function temporary_variables +[eva] computing for function loops_with_goto <- main. + Called from tests/value/auto_loop_unroll.c:298. +[eva] tests/value/auto_loop_unroll.c:249: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:250: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:254: Frama_C_show_each_30: [0..2147483647] +[eva:alarm] tests/value/auto_loop_unroll.c:259: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:258: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:263: Frama_C_show_each_top: [0..2147483647] +[eva] tests/value/auto_loop_unroll.c:266: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:267: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:272: Frama_C_show_each_32: [0..2147483647] +[eva:alarm] tests/value/auto_loop_unroll.c:276: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:275: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:280: + Frama_C_show_each_33_inf: [0..2147483647] +[eva:alarm] tests/value/auto_loop_unroll.c:284: Warning: + signed overflow. assert i + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:283: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:283: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:288: Frama_C_show_each_top: [0..2147483647] +[eva] Recording results for loops_with_goto +[eva] Done for function loops_with_goto [eva] Recording results for main [eva] done for function main [eva] ====== VALUES COMPUTED ====== @@ -213,8 +284,9 @@ [eva:final-states] Values at end of function complex_loops: g ∈ {64} res ∈ [0..2147483647] - i ∈ [0..64] - p ∈ {{ &i }} + i ∈ [64..2147483647] + j ∈ {64} + p ∈ {{ &j }} t[0] ∈ {0} [1] ∈ {1} [2] ∈ {2} @@ -225,11 +297,20 @@ [7] ∈ {7} [8] ∈ {8} [9] ∈ {9} +[eva:final-states] Values at end of function loops_with_goto: + i ∈ [34..2147483647] + res ∈ {0} [eva:final-states] Values at end of function simple_loops: res ∈ {100} +[eva:final-states] Values at end of function temporary_variables: + i ∈ [-2147483648..20] + res ∈ [0..2147483647] +[eva:final-states] Values at end of function various_conditions: + i ∈ {12} + res ∈ {0} [eva:final-states] Values at end of function various_loops: Frama_C_entropy_source ∈ [--..--] - g ∈ [0..2147483647] + g ∈ [101..2147483647] res ∈ {0} x ∈ {24} k ∈ [0..10] @@ -242,8 +323,14 @@ [from] Done for function incr_g [from] Computing for function complex_loops [from] Done for function complex_loops +[from] Computing for function loops_with_goto +[from] Done for function loops_with_goto [from] Computing for function simple_loops [from] Done for function simple_loops +[from] Computing for function temporary_variables +[from] Done for function temporary_variables +[from] Computing for function various_conditions +[from] Done for function various_conditions [from] Computing for function various_loops [from] Computing for function Frama_C_interval <-various_loops [from] Done for function Frama_C_interval @@ -261,11 +348,17 @@ g FROM g [from] Function complex_loops: g FROM \nothing +[from] Function loops_with_goto: + NO EFFECTS [from] Function simple_loops: NO EFFECTS +[from] Function temporary_variables: + NO EFFECTS +[from] Function various_conditions: + NO EFFECTS [from] Function various_loops: Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) - g FROM g (and SELF) + g FROM \nothing [from] Function main: Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) g FROM \nothing @@ -279,16 +372,28 @@ [inout] Inputs for function incr_g: g [inout] Out (internal) for function complex_loops: - g; res; i; p; j; t[0..9] + g; res; i; j; p; j_0; t[0..9] [inout] Inputs for function complex_loops: undet; g +[inout] Out (internal) for function loops_with_goto: + i; res +[inout] Inputs for function loops_with_goto: + undet [inout] Out (internal) for function simple_loops: res; i; i_0; i_1; i_2 [inout] Inputs for function simple_loops: \nothing +[inout] Out (internal) for function temporary_variables: + i; res; tmp; tmp_0 +[inout] Inputs for function temporary_variables: + \nothing +[inout] Out (internal) for function various_conditions: + i; res; i_0; tmp; i_1; tmp_0; i_2; i_3 +[inout] Inputs for function various_conditions: + undet [inout] Out (internal) for function various_loops: - Frama_C_entropy_source; g; res; i; i_0; i_1; i_2; i_3; x; k; i_4; i_5; - t; i_6; j + Frama_C_entropy_source; g; res; i; i_0; i_1; i_2; x; k; i_3; i_4; t; + i_5; j; i_6 [inout] Inputs for function various_loops: Frama_C_entropy_source; undet; g [inout] Out (internal) for function main: diff --git a/tests/value/oracle/auto_loop_unroll.1.res.oracle b/tests/value/oracle/auto_loop_unroll.1.res.oracle index 775a7323bf9b696e565c5de92ee3cf0bbbcbeda7..f743d1ef27eb2cd2660b39dfd87b47cfe3c26d19 100644 --- a/tests/value/oracle/auto_loop_unroll.1.res.oracle +++ b/tests/value/oracle/auto_loop_unroll.1.res.oracle @@ -6,7 +6,7 @@ undet ∈ [--..--] g ∈ {0} [eva] computing for function simple_loops <- main. - Called from tests/value/auto_loop_unroll.c:189. + Called from tests/value/auto_loop_unroll.c:293. [eva:loop-unroll] tests/value/auto_loop_unroll.c:24: Automatic loop unrolling. [eva] tests/value/auto_loop_unroll.c:24: Trace partitioning superposing up to 100 states @@ -25,7 +25,7 @@ [eva] Recording results for simple_loops [eva] Done for function simple_loops [eva] computing for function various_loops <- main. - Called from tests/value/auto_loop_unroll.c:190. + Called from tests/value/auto_loop_unroll.c:294. [eva:loop-unroll] tests/value/auto_loop_unroll.c:57: Automatic loop unrolling. [eva] tests/value/auto_loop_unroll.c:59: Frama_C_show_each_64: {64} [eva:loop-unroll] tests/value/auto_loop_unroll.c:62: Automatic loop unrolling. @@ -36,319 +36,376 @@ [eva] tests/value/auto_loop_unroll.c:75: Trace partitioning superposing up to 100 states [eva] tests/value/auto_loop_unroll.c:82: Frama_C_show_each_32_80: [32..80] -[eva:loop-unroll] tests/value/auto_loop_unroll.c:85: Automatic loop unrolling. -[eva] tests/value/auto_loop_unroll.c:90: Frama_C_show_each_11_111: [11..111] [eva] computing for function Frama_C_interval <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:94. + Called from tests/value/auto_loop_unroll.c:86. [eva] using specification for function Frama_C_interval -[eva] tests/value/auto_loop_unroll.c:94: +[eva] tests/value/auto_loop_unroll.c:86: function Frama_C_interval: precondition 'order' got status valid. [eva] Done for function Frama_C_interval -[eva:loop-unroll] tests/value/auto_loop_unroll.c:95: Automatic loop unrolling. -[eva] tests/value/auto_loop_unroll.c:97: Frama_C_show_each_40_50: [40..50] -[eva:loop-unroll] tests/value/auto_loop_unroll.c:100: Automatic loop unrolling. +[eva:loop-unroll] tests/value/auto_loop_unroll.c:87: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:89: Frama_C_show_each_40_50: [40..50] +[eva:loop-unroll] tests/value/auto_loop_unroll.c:92: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:95: Frama_C_show_each_101: {101} +[eva:loop-unroll] tests/value/auto_loop_unroll.c:98: Automatic loop unrolling. [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr [eva] computing for function incr_g <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:101. + Called from tests/value/auto_loop_unroll.c:99. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr <- various_loops <- main. - Called from tests/value/auto_loop_unroll.c:102. + Called from tests/value/auto_loop_unroll.c:100. [eva] Recording results for incr [eva] Done for function incr -[eva] tests/value/auto_loop_unroll.c:103: Reusing old results for call to incr -[eva] tests/value/auto_loop_unroll.c:105: Frama_C_show_each_25: {25} -[eva:loop-unroll] tests/value/auto_loop_unroll.c:109: Automatic loop unrolling. -[eva:loop-unroll] tests/value/auto_loop_unroll.c:110: Automatic loop unrolling. -[eva] tests/value/auto_loop_unroll.c:114: Frama_C_show_each_120: {120} +[eva] tests/value/auto_loop_unroll.c:101: Reusing old results for call to incr +[eva] tests/value/auto_loop_unroll.c:103: Frama_C_show_each_25: {25} +[eva:loop-unroll] tests/value/auto_loop_unroll.c:107: Automatic loop unrolling. +[eva:loop-unroll] tests/value/auto_loop_unroll.c:108: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:112: Frama_C_show_each_120: {120} +[eva:loop-unroll] tests/value/auto_loop_unroll.c:115: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:122: Frama_C_show_each_32_64: [32..64] [eva] Recording results for various_loops [eva] Done for function various_loops [eva] computing for function complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:191. -[eva] tests/value/auto_loop_unroll.c:124: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:126: Warning: - signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:128: - Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:132: starting to merge loop iterations + Called from tests/value/auto_loop_unroll.c:295. [eva] tests/value/auto_loop_unroll.c:133: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:134: Warning: - signed overflow. assert i + 1 ≤ 2147483647; -[eva:alarm] tests/value/auto_loop_unroll.c:137: Warning: - signed overflow. assert i + 1 ≤ 2147483647; -[eva:alarm] tests/value/auto_loop_unroll.c:136: Warning: +[eva:alarm] tests/value/auto_loop_unroll.c:135: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:139: +[eva] tests/value/auto_loop_unroll.c:137: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:143: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:141: starting to merge loop iterations +[eva] tests/value/auto_loop_unroll.c:142: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:143: Warning: + signed overflow. assert i + 1 ≤ 2147483647; [eva:alarm] tests/value/auto_loop_unroll.c:146: Warning: + signed overflow. assert i + 1 ≤ 2147483647; +[eva:alarm] tests/value/auto_loop_unroll.c:145: Warning: signed overflow. assert res + 1 ≤ 2147483647; [eva] tests/value/auto_loop_unroll.c:148: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:151: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:156: Warning: +[eva] tests/value/auto_loop_unroll.c:152: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:155: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:158: +[eva] tests/value/auto_loop_unroll.c:157: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:163: Reusing old results for call to incr_g -[eva] tests/value/auto_loop_unroll.c:162: starting to merge loop iterations [eva] computing for function incr_g <- complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:163. + Called from tests/value/auto_loop_unroll.c:162. [eva] Recording results for incr_g [eva] Done for function incr_g +[eva] tests/value/auto_loop_unroll.c:161: starting to merge loop iterations [eva] computing for function incr_g <- complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:163. + Called from tests/value/auto_loop_unroll.c:162. [eva] Recording results for incr_g [eva] Done for function incr_g [eva] computing for function incr_g <- complex_loops <- main. - Called from tests/value/auto_loop_unroll.c:163. + Called from tests/value/auto_loop_unroll.c:162. [eva] Recording results for incr_g [eva] Done for function incr_g -[eva] tests/value/auto_loop_unroll.c:163: Reusing old results for call to incr_g -[eva] tests/value/auto_loop_unroll.c:163: Reusing old results for call to incr_g -[eva:alarm] tests/value/auto_loop_unroll.c:165: Warning: +[eva] computing for function incr_g <- complex_loops <- main. + Called from tests/value/auto_loop_unroll.c:162. +[eva] Recording results for incr_g +[eva] Done for function incr_g +[eva] tests/value/auto_loop_unroll.c:162: Reusing old results for call to incr_g +[eva] tests/value/auto_loop_unroll.c:162: Reusing old results for call to incr_g +[eva:alarm] tests/value/auto_loop_unroll.c:164: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:167: +[eva] tests/value/auto_loop_unroll.c:166: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:172: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:174: Warning: +[eva] tests/value/auto_loop_unroll.c:171: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:173: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:176: +[eva] tests/value/auto_loop_unroll.c:175: Frama_C_show_each_imprecise: [0..2147483647] -[eva] tests/value/auto_loop_unroll.c:180: starting to merge loop iterations -[eva:alarm] tests/value/auto_loop_unroll.c:182: Warning: +[eva] tests/value/auto_loop_unroll.c:179: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:181: Warning: signed overflow. assert res + 1 ≤ 2147483647; -[eva] tests/value/auto_loop_unroll.c:184: +[eva:alarm] tests/value/auto_loop_unroll.c:180: Warning: + signed overflow. assert i + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:183: Frama_C_show_each_imprecise: [0..2147483647] [eva] Recording results for complex_loops [eva] Done for function complex_loops +[eva] computing for function various_conditions <- main. + Called from tests/value/auto_loop_unroll.c:296. +[eva:loop-unroll] tests/value/auto_loop_unroll.c:192: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:195: Frama_C_show_each_11: {11} +[eva:loop-unroll] tests/value/auto_loop_unroll.c:197: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:200: Frama_C_show_each_12: {12} +[eva:loop-unroll] tests/value/auto_loop_unroll.c:203: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:206: Frama_C_show_each_0_13: [0..13] +[eva:loop-unroll] tests/value/auto_loop_unroll.c:208: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:211: Frama_C_show_each_0_14: [0..14] +[eva:loop-unroll] tests/value/auto_loop_unroll.c:214: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:219: Frama_C_show_each_0_15: [0..15] +[eva:loop-unroll] tests/value/auto_loop_unroll.c:221: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:221: + Trace partitioning superposing up to 100 states +[eva] tests/value/auto_loop_unroll.c:226: Frama_C_show_each_11_111: [11..111] +[eva] Recording results for various_conditions +[eva] Done for function various_conditions +[eva] computing for function temporary_variables <- main. + Called from tests/value/auto_loop_unroll.c:297. +[eva:loop-unroll] tests/value/auto_loop_unroll.c:235: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:238: Frama_C_show_each_20: {20} +[eva:loop-unroll] tests/value/auto_loop_unroll.c:240: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:243: Frama_C_show_each_21: {21} +[eva] Recording results for temporary_variables +[eva] Done for function temporary_variables +[eva] computing for function loops_with_goto <- main. + Called from tests/value/auto_loop_unroll.c:298. +[eva:loop-unroll] tests/value/auto_loop_unroll.c:249: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:254: Frama_C_show_each_30: {30} +[eva] tests/value/auto_loop_unroll.c:258: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:259: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:263: Frama_C_show_each_top: [0..2147483647] +[eva:loop-unroll] tests/value/auto_loop_unroll.c:266: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:272: Frama_C_show_each_32: {32} +[eva:loop-unroll] tests/value/auto_loop_unroll.c:275: Automatic loop unrolling. +[eva:loop-unroll] tests/value/auto_loop_unroll.c:276: Automatic loop unrolling. +[eva] tests/value/auto_loop_unroll.c:276: + Trace partitioning superposing up to 100 states +[eva:loop-unroll] tests/value/auto_loop_unroll.c:278: + loop not completely unrolled +[eva:alarm] tests/value/auto_loop_unroll.c:276: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:280: + Frama_C_show_each_33_inf: [33..2147483647] +[eva:alarm] tests/value/auto_loop_unroll.c:284: Warning: + signed overflow. assert i + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:283: starting to merge loop iterations +[eva:alarm] tests/value/auto_loop_unroll.c:283: Warning: + signed overflow. assert res + 1 ≤ 2147483647; +[eva] tests/value/auto_loop_unroll.c:288: Frama_C_show_each_top: [0..2147483647] +[eva] Recording results for loops_with_goto +[eva] Done for function loops_with_goto [eva] Recording results for main [eva] done for function main [eva] ====== VALUES COMPUTED ====== [eva:final-states] Values at end of function incr: __retres ∈ [1..25] [eva:final-states] Values at end of function incr_g: - g ∈ [1..63] + g ∈ [1..126] [eva:final-states] Values at end of function complex_loops: g ∈ {64} res ∈ [0..2147483647] - i ∈ [0..64] - p ∈ {{ &i }} + i ∈ [64..2147483647] + j ∈ {64} + p ∈ {{ &j }} t[0] ∈ {0} [1] ∈ {1} [2] ∈ {2} @@ -359,11 +416,20 @@ [7] ∈ {7} [8] ∈ {8} [9] ∈ {9} +[eva:final-states] Values at end of function loops_with_goto: + i ∈ [34..2147483647] + res ∈ {0} [eva:final-states] Values at end of function simple_loops: res ∈ {100} +[eva:final-states] Values at end of function temporary_variables: + i ∈ {-1} + res ∈ {21} +[eva:final-states] Values at end of function various_conditions: + i ∈ {12} + res ∈ {0} [eva:final-states] Values at end of function various_loops: Frama_C_entropy_source ∈ [--..--] - g ∈ {25} + g ∈ {126} res ∈ {0} x ∈ {24} k ∈ [0..10] @@ -376,8 +442,14 @@ [from] Done for function incr_g [from] Computing for function complex_loops [from] Done for function complex_loops +[from] Computing for function loops_with_goto +[from] Done for function loops_with_goto [from] Computing for function simple_loops [from] Done for function simple_loops +[from] Computing for function temporary_variables +[from] Done for function temporary_variables +[from] Computing for function various_conditions +[from] Done for function various_conditions [from] Computing for function various_loops [from] Computing for function Frama_C_interval <-various_loops [from] Done for function Frama_C_interval @@ -395,11 +467,17 @@ g FROM g [from] Function complex_loops: g FROM \nothing +[from] Function loops_with_goto: + NO EFFECTS [from] Function simple_loops: NO EFFECTS +[from] Function temporary_variables: + NO EFFECTS +[from] Function various_conditions: + NO EFFECTS [from] Function various_loops: Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) - g FROM g (and SELF) + g FROM \nothing [from] Function main: Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) g FROM \nothing @@ -413,16 +491,28 @@ [inout] Inputs for function incr_g: g [inout] Out (internal) for function complex_loops: - g; res; i; p; j; t[0..9] + g; res; i; j; p; j_0; t[0..9] [inout] Inputs for function complex_loops: undet; g +[inout] Out (internal) for function loops_with_goto: + i; res +[inout] Inputs for function loops_with_goto: + undet [inout] Out (internal) for function simple_loops: res; i; i_0; i_1; i_2 [inout] Inputs for function simple_loops: \nothing +[inout] Out (internal) for function temporary_variables: + i; res; tmp; tmp_0 +[inout] Inputs for function temporary_variables: + \nothing +[inout] Out (internal) for function various_conditions: + i; res; i_0; tmp; i_1; tmp_0; i_2; i_3 +[inout] Inputs for function various_conditions: + undet [inout] Out (internal) for function various_loops: - Frama_C_entropy_source; g; res; i; i_0; i_1; i_2; i_3; x; k; i_4; i_5; - t; i_6; j + Frama_C_entropy_source; g; res; i; i_0; i_1; i_2; x; k; i_3; i_4; t; + i_5; j; i_6 [inout] Inputs for function various_loops: Frama_C_entropy_source; undet; g [inout] Out (internal) for function main: