diff options
346 files changed, 7707 insertions, 3921 deletions
diff --git a/.gitignore b/.gitignore index 9cd91245f5..3a9e53dc94 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,8 @@ JAVADOC-GENERATED /bootstrap/lib/asn1 /bootstrap/lib/common_test +/bootstrap/lib/edoc +/bootstrap/lib/erl_docgen /bootstrap/lib/hipe /bootstrap/lib/ic /bootstrap/lib/orber diff --git a/HOWTO/DTRACE.md b/HOWTO/DTRACE.md index 8fa2fd9d50..90f4addefd 100644 --- a/HOWTO/DTRACE.md +++ b/HOWTO/DTRACE.md @@ -27,12 +27,10 @@ Goals Supported platforms ------------------- -* OS X 10.6.x / Snow Leopard. It should also work for 10.7 / Lion, - but I haven't personally tested it. +* OS X 10.6.x / Snow Leopard, OS X 10.7.x / Lion and probably newer versions. * Solaris 10. I have done limited testing on Solaris 11 and OpenIndiana release 151a, and both appear to work. -* FreeBSD 9.0, though please see the "FreeBSD 9.0 Release Notes" - section below! +* FreeBSD 9.0 and 10.0. * Linux via SystemTap compatibility. Please see [$ERL_TOP/HOWTO/SYSTEMTAP.md][] for more details. diff --git a/HOWTO/INSTALL-CROSS.md b/HOWTO/INSTALL-CROSS.md index 03ea4c6e76..b01de12e44 100644 --- a/HOWTO/INSTALL-CROSS.md +++ b/HOWTO/INSTALL-CROSS.md @@ -103,14 +103,6 @@ has to be provided in the `$PATH`. The Erlang/OTP for the target system will be built using this Erlang system, together with the cross compilation tools provided. -If you want to build the documentation out of the same source tree as you are -cross compiling in, you currently need a full Erlang/OTP system of the same -release as the one being built for the build machine. If this is the case, -build and install one for the build machine (or use one already built) and add -it to the `$PATH` before cross building, and building the documentation. See -the [How to Build the Documentation][] section in the [$ERL_TOP/HOWTO/INSTALL.md][] -document for information on how to build the documentation. - If you want to build using a compatible Erlang/OTP system in the `$PATH`, jump to (3). @@ -284,6 +276,14 @@ and then do the cross build of the system. `otp_build release -a` will do the same as (5), and you will after this have to do a manual install either by doing (6), or (7). +Building and Installing the Documentation +----------------------------------------- + +After the system has been cross built you can build and install the +documentation the same way as after a native build of the system. See the +[How to Build the Documentation][] section in the [$ERL_TOP/HOWTO/INSTALL.md][] +document for information on how to build the documentation. + Testing the cross compiled system --------------------------------- Some of the tests that come with erlang use native code to test. This means diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md index fd0108f1de..bbde5bc08c 100644 --- a/HOWTO/INSTALL.md +++ b/HOWTO/INSTALL.md @@ -189,16 +189,16 @@ section below before proceeding. Step 1: Start by unpacking the Erlang/OTP distribution file with your GNU compatible TAR program. - $ gunzip -c otp_src_%OTP-REL%.tar.gz | tar xf - + $ gunzip -c otp_src_%OTP-VSN%.tar.gz | tar xf - alternatively: - $ zcat otp_src_%OTP-REL%.tar.gz | tar xf - + $ zcat otp_src_%OTP-VSN%.tar.gz | tar xf - Step 2: Now cd into the base directory (`$ERL_TOP`). - $ cd otp_src_%OTP-REL% + $ cd otp_src_%OTP-VSN% ### Configuring ### @@ -279,6 +279,11 @@ Some of the available `configure` options are: `(g)cc` * `--enable-m32-build` - Build 32-bit binaries using the `-m32` flag to `(g)cc` +* `--with-assumed-cache-line-size=SIZE` - Set assumed cache-line size in + bytes. Default is 64. Valid values are powers of two between and + including 16 and 8192. The runtime system use this value in order to + try to avoid false sharing. A too large value wastes memory. A to + small value will increase the amount of false sharing. * `--{with,without}-termcap` - termcap (without implies that only the old Erlang shell can be used) * `--with-javac=JAVAC` - Specify Java compiler to use @@ -505,21 +510,11 @@ The Erlang/OTP Documentation ### How to Build the Documentation ### - $ cd $ERL_TOP - -If you have just built Erlang/OTP in the current source tree, you have -already ran `configure` and do not need to do this again; otherwise, run -`configure`. - - $ ./configure [Configure Args] - -When building the documentation you need a full Erlang/OTP-%OTP-REL% system in -the `$PATH`. - - $ export PATH=<Erlang/OTP-%OTP-REL% bin dir>:$PATH # Assuming bash/sh - -Build the documentation. +Before you can build the documentation you need to either [native build][] +or [cross build][] the Erlang/OTP system. After this you can build the +documentation as follows. + $ cd $ERL_TOP $ make docs The documentation can be installed either using the `install-docs` target, @@ -558,13 +553,13 @@ For some graphical tools to find the on-line help you have to install the HTML documentation on top of the installed OTP applications, i.e. $ cd <ReleaseDir> - $ gunzip -c otp_html_%OTP-REL%.tar.gz | tar xf - + $ gunzip -c otp_html_%OTP-VSN%.tar.gz | tar xf - For `erl -man <page>` to work the Unix manual pages have to be installed in the same way, i.e. $ cd <ReleaseDir> - $ gunzip -c otp_man_%OTP-REL%.tar.gz | tar xf - + $ gunzip -c otp_man_%OTP-VSN%.tar.gz | tar xf - Where `<ReleaseDir>` is @@ -808,9 +803,11 @@ Before modifying this document you need to have a look at the [Building in Git]: #How-to-Build-and-Install-ErlangOTP_Building-in-Git [Pre-built Source Release]: #How-to-Build-and-Install-ErlangOTP_Prebuilt-Source-Release [make and $ERL_TOP]: #How-to-Build-and-Install-ErlangOTP_make-and-ERLTOP - [html documentation]: http://www.erlang.org/download/otp_doc_html_%OTP-REL%.tar.gz - [man pages]: http://www.erlang.org/download/otp_doc_man_%OTP-REL%.tar.gz - [the released source tar ball]: http://www.erlang.org/download/otp_src_%OTP-REL%.tar.gz + [html documentation]: http://www.erlang.org/download/otp_doc_html_%OTP-VSN%.tar.gz + [man pages]: http://www.erlang.org/download/otp_doc_man_%OTP-VSN%.tar.gz + [the released source tar ball]: http://www.erlang.org/download/otp_src_%OTP-VSN%.tar.gz + [native build]: #How-to-Build-and-Install-ErlangOTP + [cross build]: INSTALL-CROSS.md [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md [?TOC]: true diff --git a/HOWTO/MARKDOWN.md b/HOWTO/MARKDOWN.md index c9ad09a1be..8e8a5c02cf 100644 --- a/HOWTO/MARKDOWN.md +++ b/HOWTO/MARKDOWN.md @@ -223,8 +223,11 @@ places. Appropriate attributes to the `X` tag will also be generated. \%CopyrightBegin\% and \%CopyrightEnd\% "tags" will be removed from the output. -* All occurrences of \%OTP-REL% will be replaced by current release number - (e.g. R14A). +* All occurrences of \%OTP-REL% will be replaced by current OTP release number + (e.g. 17). + +* All occurrences of \%OTP-VSN% will be replaced by current OTP version + (e.g. 17.0). * All occurrences of \%ERTS-VSN% will be replaced by current ERTS version (e.g. 5.8). diff --git a/Makefile.in b/Makefile.in index d3d81c8817..a64c2872fd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -172,6 +172,7 @@ endif TARGET := @TARGET@ include $(ERL_TOP)/make/target.mk export TARGET +include $(ERL_TOP)/make/otp_default_release_path.mk BOOTSTRAP_ONLY = @BOOTSTRAP_ONLY@ @@ -395,29 +396,35 @@ endif cd $(ERL_TOP)/erts && \ ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \ $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" release +ifeq ($(RELEASE_ROOT),) + $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(OTP_DEFAULT_RELEASE_PATH)" +else + $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(RELEASE_ROOT)" +endif # --------------------------------------------------------------- # Target only used when building commercial ERTS patches # --------------------------------------------------------------- -release_docs docs: mod2app + +release_docs docs: doc_bootstrap_build doc_bootstrap_copy mod2app ifeq ($(OTP_SMALL_BUILD),true) cd $(ERL_TOP)/lib && \ - PATH=$(ERL_TOP)/bin:"$${PATH}" ERL_TOP=$(ERL_TOP) \ - $(MAKE) TESTROOT="$(RELEASE_ROOT)" $@ + PATH=$(BOOT_PREFIX)"$${PATH}" ERL_TOP=$(ERL_TOP) \ + $(MAKE) TESTROOT="$(RELEASE_ROOT)" DOCGEN=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen $@ else cd $(ERL_TOP)/lib && \ - PATH=$(ERL_TOP)/bin:"$${PATH}" ERL_TOP=$(ERL_TOP) \ - $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" $@ + PATH=$(BOOT_PREFIX)"$${PATH}" ERL_TOP=$(ERL_TOP) \ + $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" DOCGEN=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen $@ endif cd $(ERL_TOP)/erts && \ - PATH=$(ERL_TOP)/bin:"$${PATH}" ERL_TOP=$(ERL_TOP) \ - $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" $@ + PATH=$(BOOT_PREFIX)"$${PATH}" ERL_TOP=$(ERL_TOP) \ + $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" DOCGEN=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen $@ cd $(ERL_TOP)/system/doc && \ - PATH=$(ERL_TOP)/bin:"$${PATH}" \ - ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT="$(RELEASE_ROOT)" $@ + PATH=$(BOOT_PREFIX)"$${PATH}" \ + ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT="$(RELEASE_ROOT)" DOCGEN=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen $@ mod2app: - PATH=$(ERL_TOP)/bin:"$${PATH}" escript $(ERL_TOP)/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml + PATH=$(BOOT_PREFIX)"$${PATH}" escript $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml # ---------------------------------------------------------------------- ERLANG_EARS=$(BOOTSTRAP_ROOT)/bootstrap/erts @@ -762,6 +769,80 @@ tertiary_bootstrap_copy: done # $(V_at)cp lib/syntax_tools/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin +doc_bootstrap_build: + $(make_verbose)cd lib && \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ + $(MAKE) opt DOC_BOOTSTRAP=true + +doc_bootstrap_copy: + $(make_verbose) +# XMERL + $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl ; fi + $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/ebin ; fi + $(V_at)for x in lib/xmerl/ebin/*.beam; do \ + BN=`basename $$x`; \ + TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/ebin/$$BN; \ + test -f $$TF && \ + test '!' -z "`find $$x -newer $$TF -print`" && \ + cp $$x $$TF; \ + test '!' -f $$TF && \ + cp $$x $$TF; \ + true; \ + done +# xmerl/include already copied in secondary bootstrap +# EDOC + $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/edoc ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/edoc ; fi + $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/edoc/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/edoc/ebin ; fi + $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/edoc/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/edoc/include ; fi + $(V_at)for x in lib/edoc/ebin/*.beam; do \ + BN=`basename $$x`; \ + TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/edoc/ebin/$$BN; \ + test -f $$TF && \ + test '!' -z "`find $$x -newer $$TF -print`" && \ + cp $$x $$TF; \ + test '!' -f $$TF && \ + cp $$x $$TF; \ + true; \ + done + $(V_at)for x in lib/edoc/include/*.hrl; do \ + BN=`basename $$x`; \ + TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/edoc/include/$$BN; \ + test -f $$TF && \ + test '!' -z "`find $$x -newer $$TF -print`" && \ + cp $$x $$TF; \ + test '!' -f $$TF && \ + cp $$x $$TF; \ + true; \ + done +# ERL_DOCGEN + $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen ; fi + $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/ebin ; fi + $(V_at)for x in lib/erl_docgen/ebin/*.beam; do \ + BN=`basename $$x`; \ + TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/ebin/$$BN; \ + test -f $$TF && \ + test '!' -z "`find $$x -newer $$TF -print`" && \ + cp $$x $$TF; \ + test '!' -f $$TF && \ + cp $$x $$TF; \ + true; \ + done + $(V_at)for d in priv priv/bin priv/css priv/dtd priv/dtd_html_entities priv/dtd_man_entities priv/images priv/js priv/js/flipmenu priv/xsl; do \ + if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/$$d ; then mkdir -p $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/$$d ; fi; \ + for x in lib/erl_docgen/$$d/*; do \ + BN=`basename $$x`; \ + if test ! -d lib/erl_docgen/$$d/$$BN ; then \ + TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/$$d/$$BN; \ + test -f $$TF && \ + test '!' -z "`find $$x -newer $$TF -print`" && \ + cp $$x $$TF; \ + test '!' -f $$TF && \ + cp $$x $$TF; \ + true; \ + fi; \ + done; \ + done + .PHONY: check_recreate_primary_bootstrap recreate_primary_bootstrap @@ -921,7 +1002,7 @@ $(TEST_DIRS): # # Order is important here, don't change it! # -INST_DEP += install.dirs install.emulator install.libs install.Install install.bin +INST_DEP += install.dirs install.emulator install.libs install.Install install.otp_version install.bin install: $(INST_DEP) @@ -950,6 +1031,13 @@ install.Install: (cd "$(ERLANG_LIBDIR)" \ && ./Install $(INSTALL_CROSS) -minimal "$(ERLANG_INST_LIBDIR)") +install.otp_version: +ifeq ($(ERLANG_LIBDIR),) + $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(OTP_DEFAULT_RELEASE_PATH)" +else + $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(ERLANG_LIBDIR)" +endif + # # Install erlang base public files # diff --git a/OTP_VERSION b/OTP_VERSION new file mode 100644 index 0000000000..c86282f25f --- /dev/null +++ b/OTP_VERSION @@ -0,0 +1 @@ +17.0-rc1 diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex 301785992c..98f7a236ae 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam Binary files differindex de1c007a77..1146ef9ab8 100644 --- a/bootstrap/lib/stdlib/ebin/otp_internal.beam +++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam diff --git a/configure.in b/configure.in index 88fd847030..f6cccb4f4b 100644 --- a/configure.in +++ b/configure.in @@ -180,18 +180,26 @@ AC_PROG_LN_S AC_PROG_RANLIB # -# Get erts version and otp release from erts/vsn.mk +# Get erts version from erts/vsn.mk # AC_MSG_CHECKING([ERTS version]) [ERTS_VSN=`sed -n "s/^VSN[ ]*=[ ]*\(.*\)/\1/p" < $ERL_TOP/erts/vsn.mk`] AC_MSG_RESULT([$ERTS_VSN]) AC_SUBST(ERTS_VSN) +# +# Get OTP release and OTP version from $ERL_TOP/OTP_VERSION +# AC_MSG_CHECKING([OTP release]) -[OTP_REL=`sed -n "s/^SYSTEM_VSN[ ]*=[ ]*\(.*\)/\1/p" < $ERL_TOP/erts/vsn.mk`] +[OTP_REL=`cat $ERL_TOP/OTP_VERSION | sed "s|\([0-9]*\).*|\1|"`] AC_MSG_RESULT([$OTP_REL]) AC_SUBST(OTP_REL) +AC_MSG_CHECKING([OTP version]) +[OTP_VSN=`cat $ERL_TOP/OTP_VERSION`] +AC_MSG_RESULT([$OTP_VSN]) +AC_SUBST(OTP_VSN) + AC_ARG_ENABLE(threads, AS_HELP_STRING([--enable-threads], [enable async thread support]) AS_HELP_STRING([--disable-threads], [disable async thread support])) @@ -367,9 +375,9 @@ AS_HELP_STRING( [verbose build output (undo: "make V=0")])dnl ]) -DEFAULT_VERBOSITY=1 -if test X${enable_silent_rules} = Xyes; then - DEFAULT_VERBOSITY=0 +DEFAULT_VERBOSITY=0 +if test X${enable_silent_rules} = Xno; then + DEFAULT_VERBOSITY=1 fi AC_SUBST(DEFAULT_VERBOSITY) diff --git a/erts/configure.in b/erts/configure.in index 0933b124df..eecd8eb889 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -347,6 +347,25 @@ AS_HELP_STRING([--enable-clock-gettime], *) clock_gettime_correction=yes ;; esac ], clock_gettime_correction=unknown) +AC_ARG_WITH(assumed-cache-line-size, +AS_HELP_STRING([--with-assumed-cache-line-size=SIZE], + [specify assumed cache line size in bytes (valid values are powers of two between and including 16 and 8192; default is 64)])) + +dnl Require the assumed cache-line size to be a power of two between 16 and 8192 +case "$with_assumed_cache_line_size" in + ""|no|yes) + with_assumed_cache_line_size=64;; + 16|32|64|128|256|512|1024|2048|4096|8192) + ;; + *) + AC_MSG_ERROR([Invalid assumed cache-line size of $with_assumed_cache_line_size bytes]) + ;; +esac + +AC_DEFINE_UNQUOTED(ASSUMED_CACHE_LINE_SIZE, + $with_assumed_cache_line_size, + [Assumed cache-line size (in bytes)]) + dnl Magic test for clearcase. OTP_RELEASE= if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then @@ -357,6 +376,16 @@ else fi AC_SUBST(OTP_RELEASE) +AC_MSG_CHECKING([OTP release]) +[SYSTEM_VSN=`cat $ERL_TOP/OTP_VERSION | sed "s|\([0-9]*\).*|\1|"`] +AC_MSG_RESULT([$SYSTEM_VSN]) +AC_SUBST(SYSTEM_VSN) + +AC_MSG_CHECKING([OTP version]) +[OTP_VERSION=`cat $ERL_TOP/OTP_VERSION`] +AC_MSG_RESULT([$OTP_VERSION]) +AC_SUBST(OTP_VERSION) + dnl OK, we might have darwin switches off different kinds, lets dnl check it all before continuing. TMPSYS=`uname -s`-`uname -m` @@ -1233,6 +1262,7 @@ if test $emu_threads != yes; then AC_MSG_CHECKING(whether dirty schedulers should be enabled) if test "x$enable_dirty_schedulers" != "xno"; then AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) + AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) @@ -1261,6 +1291,7 @@ else EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_DIRTY_SCHEDULERS" AC_DEFINE(ERTS_DIRTY_SCHEDULERS, 1, [Define if the emulator supports dirty schedulers]) AC_DEFINE(ERL_NIF_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) + AC_DEFINE(ERL_DRV_DIRTY_SCHEDULER_SUPPORT, 1, [Dirty scheduler support]) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) @@ -3853,28 +3884,26 @@ esac if test "$enable_dtrace_test" = "yes" ; then if test "$DTRACE" = "dtrace" ; then AC_CHECK_HEADERS(sys/sdt.h) + AC_MSG_CHECKING([for 1-stage DTrace precompilation]) # The OS X version of dtrace prints a spurious line here. if ! dtrace -h $DTRACE_CPP -Iemulator/beam -o ./foo-dtrace.h -s emulator/beam/erlang_dtrace.d; then AC_MSG_ERROR([Could not precompile erlang_dtrace.d: dtrace -h failed]) fi + AC_MSG_RESULT([yes]) - $RM -f dtest.{o,c} - cat > dtest.c <<_DTEST - #include "foo-dtrace.h" - int main(void) { ERLANG_DIST_PORT_BUSY_ENABLED(); return 0; } -_DTEST - $CC $CFLAGS -c -o dtest.o dtest.c - - $RM -f $DTRACE_2STEP_TEST - if dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d dtest.o && \ - test -f $DTRACE_2STEP_TEST ; then - rm $DTRACE_2STEP_TEST + AC_MSG_CHECKING([for 2-stage DTrace precompilation]) + AC_TRY_COMPILE([ #include "foo-dtrace.h" ], + [ERLANG_DIST_PORT_BUSY_ENABLED();], + [$RM -f $DTRACE_2STEP_TEST + dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d conftest.$OBJEXT 2>&AS_MESSAGE_LOG_FD + if test -f $DTRACE_2STEP_TEST; then + rm $DTRACE_2STEP_TEST DTRACE_ENABLED_2STEP=yes - AC_MSG_NOTICE([dtrace precompilation for 2-stage DTrace successful]) - else - AC_MSG_NOTICE([dtrace precompilation for 1-stage DTrace successful]) - fi - $RM -f dtest.{o,c} foo-dtrace.h + fi], + []) + AS_IF([test "x$DTRACE_ENABLED_2STEP" = "xyes"], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) DTRACE_ENABLED=yes case $OPSYS in diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index d4c6fe67d2..e8b856c3ff 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -139,7 +139,7 @@ man: $(MAN1_FILES) $(MAN3_FILES) gifs: $(GIF_FILES:%=$(HTMLDIR)/%) -$(INFO_FILE): $(INFO_FILE_SRC) ../../vsn.mk +$(INFO_FILE): $(INFO_FILE_SRC) $(ERL_TOP)/make/$(TARGET)/otp.mk sed -e 's;%RELEASE%;$(SYSTEM_VSN);' $(INFO_FILE_SRC) > $(INFO_FILE) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 4cf5631727..e440a3d270 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5203,9 +5203,16 @@ ok <c>erlang:system_info(schedulers_online)</c>. </p> <p>Returns the old value of the flag.</p> - <p><em>Note that the dirty schedulers functionality is experimental</em>, and - that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.</p> + <p>Note that the number of dirty CPU schedulers online may change if the number of + schedulers online changes. For example, if there are 12 schedulers and all are + online, and 6 dirty CPU schedulers, all online as well, and <c>system_flag/2</c> + is used to set the number of schedulers online to 6, then the number of dirty + CPU schedulers online is automatically decreased by half as well, down to 3. + Similarly, the number of dirty CPU schedulers online increases proportionally + to increases in the number of schedulers online.</p> + <p><em>Note that the dirty schedulers functionality is experimental</em>, and + that you have to enable support for dirty schedulers when building OTP in order + to try out the functionality.</p> <p>For more information see <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso> and @@ -5438,6 +5445,15 @@ ok <![CDATA[1 <= SchedulersOnline <= erlang:system_info(schedulers)]]>. </p> <p>Returns the old value of the flag.</p> + <p>Note that if the emulator was built with support for <seealso + marker="#system_flag_dirty_cpu_schedulers_online">dirty schedulers</seealso>, + changing the number of schedulers online can also change the number of dirty + CPU schedulers online. For example, if there are 12 schedulers and all are + online, and 6 dirty CPU schedulers, all online as well, and <c>system_flag/2</c> + is used to set the number of schedulers online to 6, then the number of dirty + CPU schedulers online is automatically decreased by half as well, down to 3. + Similarly, the number of dirty CPU schedulers online increases proportionally + to increases in the number of schedulers online.</p> <p>For more information see, <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>, and @@ -5817,12 +5833,13 @@ ok boot time and cannot be changed after that. The number of dirty CPU scheduler threads online can however be changed at any time. The number of dirty CPU schedulers can be set on startup by passing - the <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> command line flag, see - <seealso marker="erts:erl#+SDcpu">erl(1)</seealso>. + the <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> or + <seealso marker="erts:erl#+SDPcpu">+SDPcpu</seealso> command line flags, + see <seealso marker="erts:erl#+SDcpu">erl(1)</seealso>. </p> <p><em>Note that the dirty schedulers functionality is experimental</em>, and that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.</p> + order to try out the functionality.</p> <p>See also <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>, <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>, <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>, @@ -5844,7 +5861,7 @@ ok </p> <p><em>Note that the dirty schedulers functionality is experimental</em>, and that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.</p> + order to try out the functionality.</p> <p>For more information, see <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>, <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>, @@ -5864,7 +5881,7 @@ ok </p> <p><em>Note that the dirty schedulers functionality is experimental</em>, and that you have to enable support for dirty schedulers when building OTP in - order to try the functionality out.</p> + order to try out the functionality.</p> <p>For more information, see <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>, <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>, and @@ -6094,16 +6111,19 @@ ok <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>, and <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p> </item> - <tag><marker id="system_info_otp_correction_package"><c>otp_correction_package</c></marker></tag> - <item> - <p>Returns a string containing the OTP correction package version - number that currenly executing VM is part of. Note that other - OTP applications in the system may be part of other OTP correction - packages.</p> - </item> <tag><marker id="system_info_otp_release"><c>otp_release</c></marker></tag> <item> - <p>Returns a string containing the OTP release number.</p> + <p>Returns a string containing the OTP release number of the + OTP release that the currently executing ERTS application is + part of.</p> + <p>As of OTP release 17, the OTP release number corresponds to + the major OTP version number. There is no + <c>erlang:system_info()</c> argument giving the exact OTP + version. This since the exact OTP version in the general case + is hard to determine. For more information see + <seealso marker="doc/installation_guide:otp_version">the + documentation of the OTP version in the installation + guide</seealso>.</p> </item> <tag><marker id="system_info_port_parallelism"><c>port_parallelism</c></marker></tag> <item><p>Returns the default port parallelism scheduling hint used. diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index f88a4ccc24..5e5b98bbd6 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -513,10 +513,9 @@ ifdef DTRACE_ENABLED # global.h causes problems by including dtrace-wrapper.h which includes # the autogenerated erlang_dtrace.h ... so make erlang_dtrace.h very early. DTRACE_HEADERS = $(TARGET)/erlang_dtrace.h -generate: $(DTRACE_HEADERS) $(GENERATE) +GENERATE+= $(DTRACE_HEADERS) else DTRACE_HEADERS = -generate: $(GENERATE) endif ifdef HIPE_ENABLED @@ -574,8 +573,8 @@ $(TTF_DIR)/erl_alloc_types.h: beam/erl_alloc.types utils/make_alloc_types GENERATE += $(TTF_DIR)/erl_alloc_types.h # version include file -$(TARGET)/erl_version.h: ../vsn.mk - $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(SYSTEM_CP_VSN) $(VSN)$(SERIALNO) $(TARGET) +$(TARGET)/erl_version.h: ../vsn.mk $(ERL_TOP)/make/$(TARGET)/otp.mk + $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(OTP_VERSION) $(VSN)$(SERIALNO) $(TARGET) GENERATE += $(TARGET)/erl_version.h # driver table diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 96547ba743..d28e519ae1 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -179,6 +179,7 @@ atom dexit atom depth atom dgroup_leader atom dictionary +atom dirty_cpu_schedulers_online atom disable_trace atom disabled atom display_items diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index e36ec2a93e..a3cd08834f 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -635,6 +635,11 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) case op_i_put_tuple_rI: case op_i_put_tuple_xI: case op_i_put_tuple_yI: + case op_new_map_jdII: + case op_update_map_assoc_jsdII: + case op_update_map_exact_jsdII: + case op_i_has_map_fields_fsI: + case op_i_get_map_elements_fsI: { int n = unpacked[-1]; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1ae413d46e..80c1265541 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1203,7 +1203,11 @@ void process_main(void) if (start_time != 0) { Sint64 diff = erts_timestamp_millis() - start_time; - if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) { + if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule +#ifdef ERTS_DIRTY_SCHEDULERS + && !ERTS_SCHEDULER_IS_DIRTY(c_p->scheduler_data) +#endif + ) { BeamInstr *inptr = find_function_from_pc(start_time_i); BeamInstr *outptr = find_function_from_pc(c_p->i); monitor_long_schedule_proc(c_p,inptr,outptr,(Uint) diff); @@ -2355,6 +2359,126 @@ void process_main(void) Next(4+Arg(3)); } + OpCase(i_has_map_fields_fsI): { + map_t* mp; + Eterm map; + Eterm field; + Eterm *ks; + BeamInstr* fs; + Uint sz,n; + + GetArg1(1, map); + + /* this instruction assumes Arg1 is a map, + * i.e. that it follows a test is_map if needed. + */ + + mp = (map_t *)map_val(map); + sz = map_get_size(mp); + + if (sz == 0) { + SET_I((BeamInstr *) Arg(0)); + goto has_map_fields_fail; + } + + ks = map_get_keys(mp); + n = (Uint)Arg(2); + fs = &Arg(3); /* pattern fields */ + + ASSERT(n>0); + + while(sz) { + field = (Eterm)*fs; + if (EQ(field,*ks)) { + n--; + fs++; + if (n == 0) break; + } + ks++; sz--; + } + + if (n) { + SET_I((BeamInstr *) Arg(0)); + goto has_map_fields_fail; + } + + I += 4 + Arg(2); +has_map_fields_fail: + ASSERT(VALID_INSTR(*I)); + Goto(*I); + } + +#define PUT_TERM_REG(term, desc) \ +do { \ + switch ((desc) & _TAG_IMMED1_MASK) { \ + case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + r(0) = (term); \ + break; \ + case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + x((desc) >> _TAG_IMMED1_SIZE) = (term); \ + break; \ + case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + y((desc) >> _TAG_IMMED1_SIZE) = (term); \ + break; \ + default: \ + ASSERT(0); \ + break; \ + } \ +} while(0) + + OpCase(i_get_map_elements_fsI): { + Eterm map; + map_t *mp; + Eterm field; + Eterm *ks; + Eterm *vs; + BeamInstr *fs; + Uint sz,n; + + GetArg1(1, map); + + /* this instruction assumes Arg1 is a map, + * i.e. that it follows a test is_map if needed. + */ + + mp = (map_t *)map_val(map); + sz = map_get_size(mp); + + if (sz == 0) { + SET_I((BeamInstr *) Arg(0)); + goto get_map_elements_fail; + } + + n = (Uint)Arg(2) / 2; + fs = &Arg(3); /* pattern fields and target registers */ + ks = map_get_keys(mp); + vs = map_get_values(mp); + + while(sz) { + field = (Eterm)*fs; + if (EQ(field,*ks)) { + PUT_TERM_REG(*vs, fs[1]); + n--; + fs += 2; + /* no more values to fetch, we are done */ + if (n == 0) break; + } + ks++; sz--; + vs++; + } + + if (n) { + SET_I((BeamInstr *) Arg(0)); + goto get_map_elements_fail; + } + + I += 4 + Arg(2); +get_map_elements_fail: + ASSERT(VALID_INSTR(*I)); + Goto(*I); + } +#undef PUT_TERM_REG + OpCase(update_map_assoc_jsdII): { Eterm res; Eterm map; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index b589d1c930..53df7876d8 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -409,7 +409,7 @@ typedef struct LoaderState { __result = __result << 8 | *Stp->file_p++; \ } \ Dest = __result; \ - } while (0) + } #define GetByte(Stp, Dest) \ if ((Stp)->file_left < 1) { \ @@ -506,6 +506,9 @@ static GenOp* gen_select_literals(LoaderState* stp, GenOpArg S, static GenOp* const_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest); +static GenOp* gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest); + static int freeze_code(LoaderState* stp); static void final_touch(LoaderState* stp); @@ -3951,6 +3954,49 @@ tuple_append_put(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, return op; } +/* + * Replace a get_map_elements with one key to an instruction with one + * element + */ + +static GenOp* +gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest) +{ + GenOp* op; + + ASSERT(Size.type == TAG_u); + + NEW_GENOP(stp, op); + op->next = NULL; + op->op = genop_get_map_element_4; + op->arity = 4; + + op->a[0] = Fail; + op->a[1] = Src; + op->a[2] = Rest[0]; + op->a[3] = Rest[1]; + return op; +} + +static GenOp* +gen_has_map_field(LoaderState* stp, GenOpArg Fail, GenOpArg Src, + GenOpArg Size, GenOpArg* Rest) +{ + GenOp* op; + + ASSERT(Size.type == TAG_u); + + NEW_GENOP(stp, op); + op->next = NULL; + op->op = genop_has_map_field_3; + op->arity = 4; + + op->a[0] = Fail; + op->a[1] = Src; + op->a[2] = Rest[0]; + return op; +} /* * Freeze the code in memory, move the string table into place, diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 9c4801041f..f34045a589 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4333,7 +4333,11 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) switch (erts_set_schedulers_online(BIF_P, ERTS_PROC_LOCK_MAIN, signed_val(BIF_ARG_2), - &old_no)) { + &old_no +#ifdef ERTS_DIRTY_SCHEDULERS + , 0 +#endif + )) { case ERTS_SCHDLR_SSPND_DONE: BIF_RET(make_small(old_no)); case ERTS_SCHDLR_SSPND_YIELD_RESTART: @@ -4465,6 +4469,33 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) ref, old ? am_true : am_false); } +#if defined(ERTS_SMP) && defined(ERTS_DIRTY_SCHEDULERS) + } else if (BIF_ARG_1 == am_dirty_cpu_schedulers_online) { + Sint old_no; + if (!is_small(BIF_ARG_2)) + goto error; + switch (erts_set_schedulers_online(BIF_P, + ERTS_PROC_LOCK_MAIN, + signed_val(BIF_ARG_2), + &old_no, + 1)) { + case ERTS_SCHDLR_SSPND_DONE: + BIF_RET(make_small(old_no)); + case ERTS_SCHDLR_SSPND_YIELD_RESTART: + ERTS_VBUMP_ALL_REDS(BIF_P); + BIF_TRAP2(bif_export[BIF_system_flag_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + case ERTS_SCHDLR_SSPND_YIELD_DONE: + ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no), + am_dirty_cpu_schedulers_online); + case ERTS_SCHDLR_SSPND_EINVAL: + goto error; + default: + ASSERT(0); + BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); + break; + } +#endif } else if (ERTS_IS_ATOM_STR("scheduling_statistics", BIF_ARG_1)) { int what; if (ERTS_IS_ATOM_STR("disable", BIF_ARG_2)) diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index f83f6b39cf..942eaa47d0 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -209,8 +209,8 @@ int erts_is_allctr_wrapper_prelocked(void); void *erts_alloc_permanent_cache_aligned(ErtsAlcType_t type, Uint size); #ifndef ERTS_CACHE_LINE_SIZE -/* Assume a cache line size of 64 bytes */ -# define ERTS_CACHE_LINE_SIZE ((UWord) 64) +/* Assumed cache line size */ +# define ERTS_CACHE_LINE_SIZE ((UWord) ASSUMED_CACHE_LINE_SIZE) # define ERTS_CACHE_LINE_MASK (ERTS_CACHE_LINE_SIZE - 1) #endif diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index f25b4dbae5..2adba9b240 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -64,7 +64,7 @@ static Export *gather_gc_info_res_trap; #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) -static char otp_correction_package[] = ERLANG_OTP_CORRECTION_PACKAGE; +static char otp_version[] = ERLANG_OTP_VERSION; /* Keep erts_system_version as a global variable for easy access from a core */ static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE "%s" @@ -312,7 +312,7 @@ erts_print_system_version(int to, void *arg, Process *c_p) int i, rc = -1; char *rc_str = ""; char rc_buf[100]; - char *ocp = otp_correction_package; + char *ov = otp_version; #ifdef ERTS_SMP Uint total, online, active; #ifdef ERTS_DIRTY_SCHEDULERS @@ -323,9 +323,9 @@ erts_print_system_version(int to, void *arg, Process *c_p) (void) erts_schedulers_state(&total, &online, &active, NULL, NULL, NULL, 0); #endif #endif - for (i = 0; i < sizeof(otp_correction_package)-4; i++) { - if (ocp[i] == '-' && ocp[i+1] == 'r' && ocp[i+2] == 'c') - rc = atoi(&ocp[i+3]); + for (i = 0; i < sizeof(otp_version)-4; i++) { + if (ov[i] == '-' && ov[i+1] == 'r' && ov[i+2] == 'c') + rc = atoi(&ov[i+3]); } if (rc >= 0) { if (rc == 0) @@ -2448,10 +2448,6 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) DECL_AM(unknown); BIF_RET(AM_unknown); } - } else if (ERTS_IS_ATOM_STR("otp_correction_package", BIF_ARG_1)) { - int n = sizeof(ERLANG_OTP_CORRECTION_PACKAGE)-1; - hp = HAlloc(BIF_P, 2*n); - BIF_RET(buf_to_intlist(&hp, ERLANG_OTP_CORRECTION_PACKAGE, n, NIL)); } else if (ERTS_IS_ATOM_STR("otp_release", BIF_ARG_1)) { int n = sizeof(ERLANG_OTP_RELEASE)-1; hp = HAlloc(BIF_P, 2*n); diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 41e64fcd4f..a5d67571e2 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -125,6 +125,7 @@ get_meta_main_tab_lock(unsigned slot) static erts_smp_spinlock_t meta_main_tab_main_lock; static Uint meta_main_tab_first_free; /* Index of first free slot */ static int meta_main_tab_cnt; /* Number of active tables */ +static int meta_main_tab_top; /* Highest ever used slot + 1 */ static Uint meta_main_tab_slot_mask; /* The slot index part of an unnamed table id */ static Uint meta_main_tab_seq_incr; static Uint meta_main_tab_seq_cnt = 0; /* To give unique(-ish) table identifiers */ @@ -1469,6 +1470,10 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) ASSERT(slot>=0 && slot<db_max_tabs); meta_main_tab_first_free = GET_NEXT_FREE_SLOT(slot); meta_main_tab_cnt++; + if (slot >= meta_main_tab_top) { + ASSERT(slot == meta_main_tab_top); + meta_main_tab_top = slot + 1; + } if (is_named) { ret = BIF_ARG_1; @@ -2058,27 +2063,31 @@ BIF_RETTYPE ets_all_0(BIF_ALIST_0) { DbTable* tb; Eterm previous; - int i, j; + int i; Eterm* hp; Eterm* hendp; int t_tabs_cnt; - int t_max_tabs; + int t_top; erts_smp_spin_lock(&meta_main_tab_main_lock); t_tabs_cnt = meta_main_tab_cnt; - t_max_tabs = db_max_tabs; + t_top = meta_main_tab_top; erts_smp_spin_unlock(&meta_main_tab_main_lock); hp = HAlloc(BIF_P, 2*t_tabs_cnt); hendp = hp + 2*t_tabs_cnt; previous = NIL; - j = 0; - for(i = 0; (i < t_max_tabs && j < t_tabs_cnt); i++) { + for(i = 0; i < t_top; i++) { erts_smp_rwmtx_t *mmtl = get_meta_main_tab_lock(i); erts_smp_rwmtx_rlock(mmtl); if (IS_SLOT_ALIVE(i)) { - j++; + if (hp == hendp) { + /* Racing table creator, grab some more heap space */ + t_tabs_cnt = 10; + hp = HAlloc(BIF_P, 2*t_tabs_cnt); + hendp = hp + 2*t_tabs_cnt; + } tb = meta_main_tab[i].u.tb; previous = CONS(hp, tb->common.id, previous); hp += 2; @@ -2849,6 +2858,7 @@ void init_db(void) ERTS_ETS_MISC_MEM_ADD(size); meta_main_tab_cnt = 0; + meta_main_tab_top = 0; for (i=1; i<db_max_tabs; i++) { SET_NEXT_FREE_SLOT(i-1,i); } diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index ea013a49a3..3f829ea7ea 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -41,6 +41,13 @@ typedef struct { int suggested_stack_size; } ErlDrvThreadOpts; +#if defined(ERL_DRV_DIRTY_SCHEDULER_SUPPORT) || defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT) +typedef enum { + ERL_DRV_DIRTY_JOB_CPU_BOUND = 1, + ERL_DRV_DIRTY_JOB_IO_BOUND = 2 +} ErlDrvDirtyJobFlags; +#endif + #endif /* __ERL_DRV_NIF_H__ */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index c17256f466..5bdebc493c 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -178,6 +178,11 @@ int erts_compat_rel; static int no_schedulers; static int no_schedulers_online; +#ifdef ERTS_DIRTY_SCHEDULERS +static int no_dirty_cpu_schedulers; +static int no_dirty_cpu_schedulers_online; +static int no_dirty_io_schedulers; +#endif #ifdef DEBUG Uint32 verbose; /* See erl_debug.h for information about verbose */ @@ -304,7 +309,13 @@ erl_init(int ncpu, erts_init_sys_common_misc(); erts_init_process(ncpu, proc_tab_sz, legacy_proc_tab); erts_init_scheduling(no_schedulers, - no_schedulers_online); + no_schedulers_online +#ifdef ERTS_DIRTY_SCHEDULERS + , no_dirty_cpu_schedulers, + no_dirty_cpu_schedulers_online, + no_dirty_io_schedulers +#endif + ); erts_init_cpu_topology(); /* Must be after init_scheduling */ erts_init_gc(); /* Must be after init_scheduling */ erts_alloc_late_init(); @@ -835,7 +846,7 @@ early_init(int *argc, char **argv) /* else if (argv[i][2] == 'D') { char *arg; char *type = argv[i]+3; - if (strcmp(type, "Pcpu") == 0) { + if (strncmp(type, "Pcpu", 4) == 0) { int ptot, ponln; arg = get_arg(argv[i]+7, argv[i+1], &i); switch (sscanf(arg, "%d:%d", &ptot, &ponln)) { @@ -872,7 +883,7 @@ early_init(int *argc, char **argv) /* VERBOSE(DEBUG_SYSTEM, ("using %d:%d dirty CPU scheduler percentages\n", dirty_cpu_scheds_pctg, dirty_cpu_scheds_onln_pctg)); - } else if (strcmp(type, "cpu") == 0) { + } else if (strncmp(type, "cpu", 3) == 0) { int tot, onln; arg = get_arg(argv[i]+6, argv[i+1], &i); switch (sscanf(arg, "%d:%d", &tot, &onln)) { @@ -923,7 +934,7 @@ early_init(int *argc, char **argv) /* } VERBOSE(DEBUG_SYSTEM, ("using %d:%d dirty CPU scheduler(s)\n", tot, onln)); - } else if (strcmp(type, "io") == 0) { + } else if (strncmp(type, "io", 2) == 0) { arg = get_arg(argv[i]+5, argv[i+1], &i); dirty_io_scheds = atoi(arg); if (dirty_io_scheds < 0 || @@ -1055,9 +1066,9 @@ early_init(int *argc, char **argv) /* erts_no_schedulers = (Uint) no_schedulers; #endif #ifdef ERTS_DIRTY_SCHEDULERS - erts_no_dirty_cpu_schedulers = dirty_cpu_scheds; - erts_no_dirty_cpu_schedulers_online = dirty_cpu_scheds_online; - erts_no_dirty_io_schedulers = dirty_io_scheds; + erts_no_dirty_cpu_schedulers = no_dirty_cpu_schedulers = dirty_cpu_scheds; + no_dirty_cpu_schedulers_online = dirty_cpu_scheds_online; + erts_no_dirty_io_schedulers = no_dirty_io_schedulers = dirty_io_scheds; #endif erts_early_init_scheduling(no_schedulers); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c35f1fc2c6..63e0d6d0c2 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1237,6 +1237,19 @@ static void steal_resource_type(ErlNifResourceType* type) } } +/* The opened_rt_list is used by enif_open_resource_type() + * in order to rollback "creates" and "take-overs" in case the load fails. + */ +struct opened_resource_type +{ + struct opened_resource_type* next; + + ErlNifResourceFlags op; + ErlNifResourceType* type; + ErlNifResourceDtor* new_dtor; +}; +static struct opened_resource_type* opened_rt_list = NULL; + ErlNifResourceType* enif_open_resource_type(ErlNifEnv* env, const char* module_str, @@ -1258,22 +1271,21 @@ enif_open_resource_type(ErlNifEnv* env, if (type == NULL) { if (flags & ERL_NIF_RT_CREATE) { type = erts_alloc(ERTS_ALC_T_NIF, - sizeof(struct enif_resource_type_t)); - type->dtor = dtor; + sizeof(struct enif_resource_type_t)); type->module = module_am; type->name = name_am; erts_refc_init(&type->refc, 1); - type->owner = env->mod_nif; - type->prev = &resource_type_list; - type->next = resource_type_list.next; - type->next->prev = type; - type->prev->next = type; op = ERL_NIF_RT_CREATE; + #ifdef DEBUG + type->dtor = (void*)1; + type->owner = (void*)2; + type->prev = (void*)3; + type->next = (void*)4; + #endif } } else { - if (flags & ERL_NIF_RT_TAKEOVER) { - steal_resource_type(type); + if (flags & ERL_NIF_RT_TAKEOVER) { op = ERL_NIF_RT_TAKEOVER; } else { @@ -1281,12 +1293,13 @@ enif_open_resource_type(ErlNifEnv* env, } } if (type != NULL) { - type->owner = env->mod_nif; - type->dtor = dtor; - if (type->dtor != NULL) { - erts_refc_inc(&type->owner->rt_dtor_cnt, 1); - } - erts_refc_inc(&type->owner->rt_cnt, 1); + struct opened_resource_type* ort = erts_alloc(ERTS_ALC_T_TMP, + sizeof(struct opened_resource_type)); + ort->op = op; + ort->type = type; + ort->new_dtor = dtor; + ort->next = opened_rt_list; + opened_rt_list = ort; } if (tried != NULL) { *tried = op; @@ -1294,6 +1307,51 @@ enif_open_resource_type(ErlNifEnv* env, return type; } +static void commit_opened_resource_types(struct erl_module_nif* lib) +{ + while (opened_rt_list) { + struct opened_resource_type* ort = opened_rt_list; + + ErlNifResourceType* type = ort->type; + + if (ort->op == ERL_NIF_RT_CREATE) { + type->prev = &resource_type_list; + type->next = resource_type_list.next; + type->next->prev = type; + type->prev->next = type; + } + else { /* ERL_NIF_RT_TAKEOVER */ + steal_resource_type(type); + } + + type->owner = lib; + type->dtor = ort->new_dtor; + + if (type->dtor != NULL) { + erts_refc_inc(&lib->rt_dtor_cnt, 1); + } + erts_refc_inc(&lib->rt_cnt, 1); + + opened_rt_list = ort->next; + erts_free(ERTS_ALC_T_TMP, ort); + } +} + +static void rollback_opened_resource_types(void) +{ + while (opened_rt_list) { + struct opened_resource_type* ort = opened_rt_list; + + if (ort->op == ERL_NIF_RT_CREATE) { + erts_free(ERTS_ALC_T_NIF, ort->type); + } + + opened_rt_list = ort->next; + erts_free(ERTS_ALC_T_TMP, ort); + } +} + + static void nif_resource_dtor(Binary* bin) { ErlNifResource* resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(bin); @@ -1319,6 +1377,8 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t size) { Binary* bin = erts_create_magic_binary(SIZEOF_ErlNifResource(size), &nif_resource_dtor); ErlNifResource* resource = ERTS_MAGIC_BIN_DATA(bin); + + ASSERT(type->owner && type->next && type->prev); /* not allowed in load/upgrade */ resource->type = type; erts_refc_inc(&bin->refc, 1); #ifdef DEBUG @@ -1510,6 +1570,13 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags, a = erts_smp_atomic32_read_acqb(&proc->state); while (1) { n = state = a; + /* + * clear any current dirty flags and dirty queue indicators, + * in case the application is shifting a job from one type + * of dirty scheduler to the other + */ + n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC + |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); if (chkflgs == ERL_NIF_DIRTY_JOB_CPU_BOUND) n |= ERTS_PSFLG_DIRTY_CPU_PROC; else @@ -1540,22 +1607,15 @@ enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result, ERL_NIF_TERM (*fp)(ErlNifEnv*, ERL_NIF_TERM)) { #ifdef USE_THREADS - erts_aint32_t state, n, a; Process* proc = env->proc; Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array; Export* ep; - a = erts_smp_atomic32_read_acqb(&proc->state); - while (1) { - n = state = a; - if (!(n & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))) - break; - n &= ~(ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC - |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); - a = erts_smp_atomic32_cmpxchg_mb(&proc->state, n, state); - if (a == state) - break; - } + erts_smp_atomic32_read_band_mb(&proc->state, + ~(ERTS_PSFLG_DIRTY_CPU_PROC + |ERTS_PSFLG_DIRTY_IO_PROC + |ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q + |ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc))) alloc_proc_psd(proc, &ep); ERTS_VBUMP_ALL_REDS(proc); @@ -2040,9 +2100,15 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) lib->entry = entry; erts_refc_init(&lib->rt_cnt, 0); erts_refc_init(&lib->rt_dtor_cnt, 0); + ASSERT(opened_rt_list == NULL); lib->mod = mod; env.mod_nif = lib; - if (mod->curr.nif != NULL) { /* Reload */ + if (mod->curr.nif != NULL) { /*************** Reload ******************/ + /* + * Repeated load_nif calls from same Erlang module instance ("reload") + * is deprecated and was only ment as a development feature not to + * be used in production systems. (See warning below) + */ int k; lib->priv_data = mod->curr.nif->priv_data; @@ -2074,6 +2140,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful."); } else { + commit_opened_resource_types(lib); mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */ erts_unload_nif(mod->curr.nif); reload_warning = 1; @@ -2081,7 +2148,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } else { lib->priv_data = NULL; - if (mod->old.nif != NULL) { /* Upgrade */ + if (mod->old.nif != NULL) { /**************** Upgrade ***************/ void* prev_old_data = mod->old.nif->priv_data; if (entry->upgrade == NULL) { ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library."); @@ -2094,17 +2161,18 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) mod->old.nif->priv_data = prev_old_data; ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful."); } - /*else if (mod->old_nif->priv_data != prev_old_data) { - refresh_cached_nif_data(mod->old_code, mod->old_nif); - }*/ + else + commit_opened_resource_types(lib); } - else if (entry->load != NULL) { /* Initial load */ + else if (entry->load != NULL) { /********* Initial load ***********/ erts_pre_nif(&env, BIF_P, lib); veto = entry->load(&env, &lib->priv_data, BIF_ARG_2); erts_post_nif(&env); if (veto) { ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful."); } + else + commit_opened_resource_types(lib); } } if (ret == am_ok) { @@ -2133,6 +2201,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } else { error: + rollback_opened_resource_types(); ASSERT(ret != am_ok); if (lib != NULL) { erts_free(ERTS_ALC_T_NIF, lib); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 7613446f64..c12ba4d554 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -172,8 +172,8 @@ typedef ErlDrvThreadOpts ErlNifThreadOpts; #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { - ERL_NIF_DIRTY_JOB_CPU_BOUND = 1, - ERL_NIF_DIRTY_JOB_IO_BOUND = 2 + ERL_NIF_DIRTY_JOB_CPU_BOUND = ERL_DRV_DIRTY_JOB_CPU_BOUND, + ERL_NIF_DIRTY_JOB_IO_BOUND = ERL_DRV_DIRTY_JOB_IO_BOUND }ErlNifDirtyTaskFlags; #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 937881212a..1d0275c524 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -148,7 +148,6 @@ int erts_sched_balance_util = 0; Uint erts_no_schedulers; #ifdef ERTS_DIRTY_SCHEDULERS Uint erts_no_dirty_cpu_schedulers; -Uint erts_no_dirty_cpu_schedulers_online; Uint erts_no_dirty_io_schedulers; #endif @@ -188,6 +187,13 @@ static ErtsAuxWorkData *aux_thread_aux_work_data; #define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ erts_smp_atomic32_set_nob(&schdlr_sspnd.changing, (VAL)) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \ + erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_cpu_changing, (VAL)) +#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \ + erts_smp_atomic32_set_nob(&schdlr_sspnd.dirty_io_changing, (VAL)) +#endif + #else #define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ @@ -198,6 +204,23 @@ do { \ ASSERT(old_val__ == (OLD_VAL)); \ } while (0) +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(VAL, OLD_VAL) \ +do { \ + erts_aint32_t old_val__; \ + old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_cpu_changing, \ + (VAL)); \ + ASSERT(old_val__ == (OLD_VAL)); \ +} while (0) +#define ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(VAL, OLD_VAL) \ +do { \ + erts_aint32_t old_val__; \ + old_val__ = erts_smp_atomic32_xchg_nob(&schdlr_sspnd.dirty_io_changing, \ + (VAL)); \ + ASSERT(old_val__ == (OLD_VAL)); \ +} while (0) +#endif + #endif @@ -207,11 +230,29 @@ static struct { int online; int curr_online; int wait_curr_online; +#ifdef ERTS_DIRTY_SCHEDULERS + int dirty_cpu_online; + int dirty_cpu_curr_online; + int dirty_cpu_wait_curr_online; + int dirty_io_online; + int dirty_io_curr_online; + int dirty_io_wait_curr_online; +#endif erts_smp_atomic32_t changing; erts_smp_atomic32_t active; +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_t dirty_cpu_changing; + erts_smp_atomic32_t dirty_cpu_active; + erts_smp_atomic32_t dirty_io_changing; + erts_smp_atomic32_t dirty_io_active; +#endif struct { int ongoing; long wait_active; +#ifdef ERTS_DIRTY_SCHEDULERS + long dirty_cpu_wait_active; + long dirty_io_wait_active; +#endif ErtsProcList *procs; } msb; /* Multi Scheduling Block */ } schdlr_sspnd; @@ -1306,6 +1347,9 @@ static ERTS_INLINE void haw_thr_prgr_current_check_progress(ErtsAuxWorkData *awdp) { ErtsThrPrgrVal current = awdp->current_thr_prgr; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif if (current != ERTS_THR_PRGR_INVALID && !erts_thr_progress_equal(current, erts_thr_progress_current())) { /* @@ -1322,6 +1366,10 @@ handle_delayed_aux_work_wakeup(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, in { int jix, max_jix; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif + ASSERT(awdp->delayed_wakeup.next != ERTS_DELAYED_WAKEUP_INFINITY); if (!waiting && awdp->delayed_wakeup.next > awdp->esdp->reductions) @@ -1477,6 +1525,9 @@ handle_misc_aux_work_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) { +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif if (!erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), awdp->misc.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_MISC_THR_PRGR; @@ -1561,6 +1612,9 @@ handle_async_ready(ErtsAuxWorkData *awdp, int waiting) { ErtsSchedulerSleepInfo *ssi = awdp->ssi; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_ASYNC_READY); if (erts_check_async_ready(awdp->async_ready.queue)) { if (set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_ASYNC_READY) @@ -1585,6 +1639,9 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp, { void *thr_prgr_p; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif #ifdef ERTS_SMP if (awdp->async_ready.need_thr_prgr && !erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), @@ -1622,6 +1679,9 @@ handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) ErtsSchedulerSleepInfo *ssi = awdp->ssi; erts_aint32_t res; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif unset_aux_work_flags(ssi, (ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM | ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC)); aux_work &= ~(ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM @@ -1655,11 +1715,7 @@ erts_alloc_ensure_handle_delayed_dealloc_call(int ix) { #ifdef DEBUG ErtsSchedulerData *esdp = erts_get_scheduler_data(); -#ifdef ERTS_DIRTY_SCHEDULERS - if (esdp && ERTS_SCHEDULER_IS_DIRTY(esdp)) - return; -#endif - ASSERT(!esdp || ix == (int) esdp->no); + ASSERT(!esdp || (ERTS_SCHEDULER_IS_DIRTY(esdp) || ix == (int) esdp->no)); #endif set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(ix-1), ERTS_SSI_AUX_WORK_DD); @@ -1673,6 +1729,9 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; int more_work = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD); erts_alloc_scheduler_handle_delayed_dealloc((void *) awdp->esdp, &need_thr_progress, @@ -1712,6 +1771,9 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; ErtsThrPrgrVal current = haw_thr_prgr_current(awdp); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif if (!erts_thr_progress_has_reached_this(current, awdp->dd.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR; @@ -1759,6 +1821,9 @@ handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int wait int lops; ErtsThrPrgrVal current = haw_thr_prgr_current(awdp); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp)); +#endif for (lops = 0; lops < ERTS_MAX_THR_PRGR_LATER_OPS; lops++) { ErtsThrPrgrLaterOp *lop = awdp->later_op.first; if (!erts_thr_progress_has_reached_this(current, lop->later)) @@ -1917,6 +1982,14 @@ erts_smp_notify_check_children_needed(void) for (i = 0; i < erts_no_schedulers; i++) set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(i), ERTS_SSI_AUX_WORK_CHECK_CHILDREN); +#ifdef ERTS_DIRTY_SCHEDULERS + for (i = 0; i < erts_no_dirty_cpu_schedulers; i++) + set_aux_work_flags_wakeup_nob(ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(i), + ERTS_SSI_AUX_WORK_CHECK_CHILDREN); + for (i = 0; i < erts_no_dirty_io_schedulers; i++) + set_aux_work_flags_wakeup_nob(ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(i), + ERTS_SSI_AUX_WORK_CHECK_CHILDREN); +#endif } static ERTS_INLINE erts_aint32_t @@ -2697,15 +2770,15 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) while (1) { - aux_work = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 : - erts_atomic32_read_acqb(&ssi->aux_work); + aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { - if (!thr_prgr_active) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); - if (aux_work && erts_thr_progress_update(esdp)) + if (aux_work && !ERTS_SCHEDULER_IS_DIRTY(esdp) + && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); } @@ -2990,7 +3063,7 @@ wake_scheduler(ErtsRunQueue *rq) #ifdef ERTS_DIRTY_SCHEDULERS static void -wake_dirty_scheduler(ErtsRunQueue *rq) +wake_dirty_schedulers(ErtsRunQueue *rq, int one) { ErtsSchedulerSleepInfo *ssi; ErtsSchedulerSleepList *sl; @@ -3000,9 +3073,27 @@ wake_dirty_scheduler(ErtsRunQueue *rq) sl = &rq->sleepers; erts_smp_spin_lock(&sl->lock); ssi = sl->list; - if (!ssi) + if (!ssi) { erts_smp_spin_unlock(&sl->lock); - else { + if (one) + wake_scheduler(rq); + } else if (one) { + erts_aint32_t flgs; + if (ssi->prev) + ssi->prev->next = ssi->next; + else { + ASSERT(sl->list == ssi); + sl->list = ssi->next; + } + if (ssi->next) + ssi->next->prev = ssi->prev; + + erts_smp_spin_unlock(&sl->lock); + + ERTS_THR_MEMORY_BARRIER; + flgs = ssi_flags_set_wake(ssi); + erts_sched_finish_poke(ssi, flgs); + } else { sl->list = NULL; erts_smp_spin_unlock(&sl->lock); @@ -3154,7 +3245,7 @@ smp_notify_inc_runq(ErtsRunQueue *runq) if (runq) { #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) - wake_dirty_scheduler(runq); + wake_dirty_schedulers(runq, 1); else #endif wake_scheduler(runq); @@ -3452,6 +3543,7 @@ suspend_run_queue(ErtsRunQueue *rq) } static void scheduler_ix_resume_wake(Uint ix); +static void scheduler_ssi_resume_wake(ErtsSchedulerSleepInfo *ssi); static ERTS_INLINE void resume_run_queue(ErtsRunQueue *rq) @@ -3478,7 +3570,10 @@ resume_run_queue(ErtsRunQueue *rq) erts_smp_runq_unlock(rq); - scheduler_ix_resume_wake(rq->ix); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) +#endif + scheduler_ix_resume_wake(rq->ix); } typedef struct { @@ -3509,20 +3604,28 @@ evacuate_run_queue(ErtsRunQueue *rq, int prio_q; ErtsRunQueue *to_rq; ErtsMigrationPaths *mps; - ErtsMigrationPath *mp; + ErtsMigrationPath *mp = NULL; ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); - mps = erts_get_migration_paths_managed(); - mp = &mps->mpath[rq->ix]; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) +#endif + { + mps = erts_get_migration_paths_managed(); + mp = &mps->mpath[rq->ix]; + } /* Evacuate scheduled misc ops */ if (rq->misc.start) { ErtsMiscOpList *start, *end; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); +#endif to_rq = mp->misc_evac_runq; if (!to_rq) return; @@ -3551,6 +3654,9 @@ evacuate_run_queue(ErtsRunQueue *rq, if (rq->ports.start) { Port *prt; +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); +#endif to_rq = mp->prio[ERTS_PORT_PRIO_LEVEL].runq; if (!to_rq) return; @@ -3586,15 +3692,26 @@ evacuate_run_queue(ErtsRunQueue *rq, erts_aint32_t state; Process *proc; int notify = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + int requeue; +#endif to_rq = NULL; - if (!mp->prio[prio_q].runq) - return; - if (prio_q == PRIORITY_NORMAL && !mp->prio[PRIORITY_LOW].runq) - return; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) +#endif + { + if (!mp->prio[prio_q].runq) + return; + if (prio_q == PRIORITY_NORMAL && !mp->prio[PRIORITY_LOW].runq) + return; + } proc = dequeue_process(rq, prio_q, &state); while (proc) { +#ifdef ERTS_DIRTY_SCHEDULERS + requeue = 1; +#endif if (ERTS_PSFLG_BOUND & state) { /* Bound processes get stuck here... */ proc->next = NULL; @@ -3603,12 +3720,43 @@ evacuate_run_queue(ErtsRunQueue *rq, else sbpp->first = proc; sbpp->last = proc; +#ifdef ERTS_DIRTY_SCHEDULERS + requeue = 0; +#endif + } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (state & ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q) { + erts_aint32_t old; + old = erts_smp_atomic32_read_band_nob(&proc->state, + ~(ERTS_PSFLG_DIRTY_CPU_PROC + | ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q)); + /* assert that no other dirty flags are set */ + ASSERT(!(old & (ERTS_PSFLG_DIRTY_IO_PROC|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q))); + } else if (state & ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) { + erts_aint32_t old; + old = erts_smp_atomic32_read_band_nob(&proc->state, + ~(ERTS_PSFLG_DIRTY_IO_PROC + | ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)); + /* assert that no other dirty flags are set */ + ASSERT(!(old & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q))); } + if (requeue) { +#else else { +#endif int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state); erts_smp_runq_unlock(rq); - to_rq = mp->prio[prio].runq; +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) + /* + * dirty run queues evacuate only to run + * queue 0 during multi-scheduling blocking + */ + to_rq = ERTS_RUNQ_IX(0); + else +#endif + to_rq = mp->prio[prio].runq; RUNQ_SET_RQ(&proc->run_queue, to_rq); erts_smp_runq_lock(to_rq); @@ -4722,13 +4870,20 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) rq->wakeup_other += (left_len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC); if (rq->wakeup_other > wakeup_other.limit) { - int empty_rqs = - erts_smp_atomic32_read_acqb(&no_empty_run_queues); - if (flags & ERTS_RUNQ_FLG_PROTECTED) - (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); - if (empty_rqs != 0) - wake_scheduler_on_empty_runq(rq); - rq->wakeup_other = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && rq->waiting) + wake_dirty_schedulers(rq, 1); + else +#endif + { + int empty_rqs = + erts_smp_atomic32_read_acqb(&no_empty_run_queues); + if (flags & ERTS_RUNQ_FLG_PROTECTED) + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); + if (empty_rqs != 0) + wake_scheduler_on_empty_runq(rq); + rq->wakeup_other = 0; + } } } rq->wakeup_other_reds = 0; @@ -5009,8 +5164,12 @@ erts_sched_set_wake_cleanup_threshold(char *str) static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) { - if (!esdp || ERTS_SCHEDULER_IS_DIRTY(esdp)) + if (!esdp) awdp->sched_id = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + else if (ERTS_SCHEDULER_IS_DIRTY(esdp)) + awdp->sched_id = (int) ERTS_DIRTY_SCHEDULER_NO(esdp); +#endif else awdp->sched_id = (int) esdp->no; awdp->esdp = esdp; @@ -5076,11 +5235,11 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix)) { esdp->no = 0; - esdp->dirty_no = (Uint) num; + ERTS_DIRTY_SCHEDULER_NO(esdp) = (Uint) num; } else { esdp->no = (Uint) num; - esdp->dirty_no = 0; + ERTS_DIRTY_SCHEDULER_NO(esdp) = 0; } #else esdp->no = (Uint) num; @@ -5111,7 +5270,12 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num, } void -erts_init_scheduling(int no_schedulers, int no_schedulers_online) +erts_init_scheduling(int no_schedulers, int no_schedulers_online +#ifdef ERTS_DIRTY_SCHEDULERS + , int no_dirty_cpu_schedulers, int no_dirty_cpu_schedulers_online, + int no_dirty_io_schedulers +#endif + ) { int ix, n, no_ssi; char *daww_ptr; @@ -5133,6 +5297,12 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) ASSERT(no_schedulers_online <= no_schedulers); ASSERT(no_schedulers_online >= 1); ASSERT(no_schedulers >= 1); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(no_dirty_cpu_schedulers <= no_schedulers); + ASSERT(no_dirty_cpu_schedulers >= 1); + ASSERT(no_dirty_cpu_schedulers_online <= no_schedulers_online); + ASSERT(no_dirty_cpu_schedulers_online >= 1); +#endif /* Create and initialize run queues */ @@ -5169,10 +5339,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) #ifdef ERTS_DIRTY_SCHEDULERS #ifdef ERTS_SMP - if (ERTS_RUNQ_IX_IS_DIRTY(ix)) { + if (ERTS_RUNQ_IX_IS_DIRTY(ix)) erts_smp_spinlock_init(&rq->sleepers.lock, "dirty_run_queue_sleep_list"); - rq->sleepers.list = NULL; - } + rq->sleepers.list = NULL; #endif #endif @@ -5236,6 +5405,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) n = (int) no_schedulers; erts_no_schedulers = n; +#ifdef ERTS_DIRTY_SCHEDULERS + erts_no_dirty_cpu_schedulers = no_dirty_cpu_schedulers; + erts_no_dirty_io_schedulers = no_dirty_io_schedulers; +#endif /* Create and initialize scheduler sleep info */ #ifdef ERTS_SMP @@ -5267,21 +5440,21 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) aligned_dirty_cpu_sched_sleep_info = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_SLP_INFO, - erts_no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); - for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); + for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_cpu_sched_sleep_info[ix].ssi; erts_smp_atomic32_init_nob(&ssi->flags, 0); - ssi->event = NULL; /* initialized in sched_thread_func */ + ssi->event = NULL; /* initialized in sched_dirty_cpu_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } aligned_dirty_io_sched_sleep_info = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_SLP_INFO, - erts_no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); - for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerSleepInfo)); + for (ix = 0; ix < no_dirty_io_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_io_sched_sleep_info[ix].ssi; erts_smp_atomic32_init_nob(&ssi->flags, 0); - ssi->event = NULL; /* initialized in sched_thread_func */ + ssi->event = NULL; /* initialized in sched_dirty_io_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } #endif @@ -5314,8 +5487,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) erts_aligned_dirty_cpu_scheduler_data = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_DATA, - erts_no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerData)); - for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + no_dirty_cpu_schedulers*sizeof(ErtsAlignedSchedulerData)); + for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); init_scheduler_data(esdp, ix+1, ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix), ERTS_DIRTY_CPU_RUNQ, NULL, 0); @@ -5323,8 +5496,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) erts_aligned_dirty_io_scheduler_data = erts_alloc_permanent_cache_aligned( ERTS_ALC_T_SCHDLR_DATA, - erts_no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerData)); - for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + no_dirty_io_schedulers*sizeof(ErtsAlignedSchedulerData)); + for (ix = 0; ix < no_dirty_io_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); init_scheduler_data(esdp, ix+1, ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix), ERTS_DIRTY_IO_RUNQ, NULL, 0); @@ -5354,6 +5527,16 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) schdlr_sspnd.curr_online = no_schedulers; schdlr_sspnd.msb.ongoing = 0; erts_smp_atomic32_init_nob(&schdlr_sspnd.active, no_schedulers); +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_changing, 0); + schdlr_sspnd.dirty_cpu_online = no_dirty_cpu_schedulers_online; + schdlr_sspnd.dirty_cpu_curr_online = no_dirty_cpu_schedulers; + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_cpu_active, no_dirty_cpu_schedulers); + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_changing, 0); + schdlr_sspnd.dirty_io_online = no_dirty_io_schedulers; + schdlr_sspnd.dirty_io_curr_online = no_dirty_io_schedulers; + erts_smp_atomic32_init_nob(&schdlr_sspnd.dirty_io_active, no_dirty_io_schedulers); +#endif schdlr_sspnd.msb.procs = NULL; init_no_runqs(no_schedulers_online, no_schedulers_online); balance_info.last_active_runqs = no_schedulers; @@ -5379,6 +5562,21 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) schdlr_sspnd.curr_online *= 2; /* Boot strapping... */ ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); +#ifdef ERTS_DIRTY_SCHEDULERS + schdlr_sspnd.dirty_cpu_wait_curr_online = no_dirty_cpu_schedulers_online; + schdlr_sspnd.dirty_cpu_curr_online *= 2; + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = no_dirty_cpu_schedulers_online; ix < no_dirty_cpu_schedulers; ix++) { + ErtsSchedulerData* esdp = ERTS_DIRTY_CPU_SCHEDULER_IX(ix); + erts_smp_atomic32_read_bor_nob(&esdp->ssi->flags, ERTS_SSI_FLG_SUSPENDED); + } + + schdlr_sspnd.dirty_io_wait_curr_online = no_dirty_io_schedulers; + schdlr_sspnd.dirty_io_curr_online *= 2; + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); +#endif erts_smp_atomic32_init_nob(&doing_sys_schedule, 0); @@ -5497,9 +5695,16 @@ free_proxy_proc(Process *proxy) erts_free(ERTS_ALC_T_PROC, proxy); } +#define ERTS_ENQUEUE_NOT 0 +#define ERTS_ENQUEUE_NORMAL_QUEUE 1 +#ifdef ERTS_DIRTY_SCHEDULERS +#define ERTS_ENQUEUE_DIRTY_CPU_QUEUE 2 +#define ERTS_ENQUEUE_DIRTY_IO_QUEUE 3 +#endif static ERTS_INLINE int -check_enqueue_in_prio_queue(erts_aint32_t *prq_prio_p, +check_enqueue_in_prio_queue(Process *c_p, + erts_aint32_t *prq_prio_p, erts_aint32_t *newp, erts_aint32_t actual) { @@ -5511,56 +5716,105 @@ check_enqueue_in_prio_queue(erts_aint32_t *prq_prio_p, *prq_prio_p = aprio; #ifdef ERTS_DIRTY_SCHEDULERS - if (!(actual & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC))) { + if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) { + /* + * If we have system tasks of a priority higher + * or equal to the user priority, we enqueue + * on ordinary run-queue and take care of + * those system tasks first. + */ + if (actual & ERTS_PSFLG_ACTIVE_SYS) { + erts_aint32_t uprio, stprio, qmask; + uprio = (actual >> ERTS_PSFLGS_USR_PRIO_OFFSET) & ERTS_PSFLGS_PRIO_MASK; + if (aprio < uprio) + goto enqueue_normal_runq; /* system tasks with higher prio */ + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS); + qmask = c_p->sys_task_qs->qmask; + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS); + switch (qmask & -qmask) { + case MAX_BIT: + stprio = PRIORITY_MAX; + break; + case HIGH_BIT: + stprio = PRIORITY_HIGH; + break; + case NORMAL_BIT: + stprio = PRIORITY_NORMAL; + break; + case LOW_BIT: + stprio = PRIORITY_LOW; + break; + default: + stprio = PRIORITY_LOW+1; + break; + } + if (stprio <= uprio) + goto enqueue_normal_runq; /* system tasks with higher prio */ + } + + /* Enqueue in dirty run queue if not already enqueued */ + if (actual & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)) + return ERTS_ENQUEUE_NOT; /* already in queue */ + if (actual & ERTS_PSFLG_DIRTY_CPU_PROC) { + *newp |= ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q; + if (actual & ERTS_PSFLG_IN_RUNQ) + return -ERTS_ENQUEUE_DIRTY_CPU_QUEUE; /* use proxy */ + *newp |= ERTS_PSFLG_IN_RUNQ; + return ERTS_ENQUEUE_DIRTY_CPU_QUEUE; + } + *newp |= ERTS_PSFLG_DIRTY_IO_PROC_IN_Q; + if (actual & ERTS_PSFLG_IN_RUNQ) + return -ERTS_ENQUEUE_DIRTY_IO_QUEUE; /* use proxy */ + *newp |= ERTS_PSFLG_IN_RUNQ; + return ERTS_ENQUEUE_DIRTY_IO_QUEUE; + } + + enqueue_normal_runq: #endif - max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; - max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; - max_qbit &= -max_qbit; - /* - * max_qbit now either contain bit set for highest prio queue or a bit - * out of range (which will have a value larger than valid range). - */ + max_qbit = (actual >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET) & ERTS_PSFLGS_QMASK; + max_qbit |= 1 << ERTS_PSFLGS_QMASK_BITS; + max_qbit &= -max_qbit; + /* + * max_qbit now either contain bit set for highest prio queue or a bit + * out of range (which will have a value larger than valid range). + */ - if (qbit >= max_qbit) - return 0; /* Already queued in higher or equal prio */ + if (qbit >= max_qbit) + return ERTS_ENQUEUE_NOT; /* Already queued in higher or equal prio */ - /* Need to enqueue (if already enqueued, it is in lower prio) */ - *newp |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; + /* Need to enqueue (if already enqueued, it is in lower prio) */ + *newp |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; - if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) - != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { - /* - * Process struct already enqueued, or actual prio not - * equal to user prio, i.e., enqueue using proxy. - */ - return -1; - } -#ifdef ERTS_DIRTY_SCHEDULERS - } else { - if (actual & ERTS_PSFLG_DIRTY_CPU_PROC) - *newp |= ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q; - else - *newp |= ERTS_PSFLG_DIRTY_IO_PROC_IN_Q; + if ((actual & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLGS_USR_PRIO_MASK)) + != (aprio << ERTS_PSFLGS_USR_PRIO_OFFSET)) { + /* + * Process struct already enqueued, or actual prio not + * equal to user prio, i.e., enqueue using proxy. + */ + return -ERTS_ENQUEUE_NORMAL_QUEUE; } -#endif /* * Enqueue using process struct. */ *newp &= ~ERTS_PSFLGS_PRQ_PRIO_MASK; *newp |= ERTS_PSFLG_IN_RUNQ | (aprio << ERTS_PSFLGS_PRQ_PRIO_OFFSET); - return 1; + return ERTS_ENQUEUE_NORMAL_QUEUE; } /* - * scheduler_out_process() return with c_rq locked. + * schedule_out_process() return with c_rq locked. */ static ERTS_INLINE int schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Process *proxy) { erts_aint32_t a, e, n, enq_prio = -1; - int res = 0; int enqueue; /* < 0 -> use proxy */ + Process* sched_p; + ErtsRunQueue* runq; +#ifdef ERTS_SMP + int check_emigration_need; +#endif a = state; @@ -5569,20 +5823,20 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces ASSERT(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)); - enqueue = 0; + enqueue = ERTS_ENQUEUE_NOT; n &= ~(ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS); if (a & ERTS_PSFLG_ACTIVE_SYS || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { - enqueue = check_enqueue_in_prio_queue(&enq_prio, &n, a); + enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); } a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) break; } - if (!enqueue) { - + switch (enqueue) { + case ERTS_ENQUEUE_NOT: if (erts_system_profile_flags.runnable_procs) { if (!(a & ERTS_PSFLG_ACTIVE_SYS) @@ -5595,60 +5849,76 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, Proces if (proxy) free_proxy_proc(proxy); - } - else { - Process *sched_p; - ErtsRunQueue *runq; - ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS)); + erts_smp_runq_lock(c_rq); + return 0; #ifdef ERTS_DIRTY_SCHEDULERS #ifdef ERTS_SMP - if (ERTS_PSFLG_DIRTY_CPU_PROC & a) - runq = ERTS_DIRTY_CPU_RUNQ; - else if (ERTS_PSFLG_DIRTY_IO_PROC & a) - runq = ERTS_DIRTY_IO_RUNQ; - else + case ERTS_ENQUEUE_DIRTY_CPU_QUEUE: + case -ERTS_ENQUEUE_DIRTY_CPU_QUEUE: + runq = ERTS_DIRTY_CPU_RUNQ; + ASSERT(ERTS_SCHEDULER_IS_DIRTY_CPU(runq->scheduler)); +#ifdef ERTS_SMP + check_emigration_need = 0; +#endif + break; + + case ERTS_ENQUEUE_DIRTY_IO_QUEUE: + case -ERTS_ENQUEUE_DIRTY_IO_QUEUE: + runq = ERTS_DIRTY_IO_RUNQ; + ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(runq->scheduler)); +#ifdef ERTS_SMP + check_emigration_need = 0; +#endif + break; #endif #endif - runq = erts_get_runq_proc(p); - if (enqueue < 0) - sched_p = make_proxy_proc(proxy, p, enq_prio); - else { - sched_p = p; - if (proxy) - free_proxy_proc(proxy); - } + default: + ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE + || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE); + runq = erts_get_runq_proc(p); #ifdef ERTS_SMP - if (!(ERTS_PSFLG_BOUND & n) -#ifdef ERTS_DIRTY_SCHEDULERS - && !(n & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q)) + check_emigration_need = !(ERTS_PSFLG_BOUND & n); #endif - ) { - ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); - if (new_runq) { - RUNQ_SET_RQ(&sched_p->run_queue, new_runq); - runq = new_runq; - } + break; + } + + ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & ERTS_PSFLG_ACTIVE_SYS)); + + if (enqueue < 0) + sched_p = make_proxy_proc(proxy, p, enq_prio); + else { + sched_p = p; + if (proxy) + free_proxy_proc(proxy); + } + +#ifdef ERTS_SMP + if (check_emigration_need) { + ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio); + if (new_runq) { + RUNQ_SET_RQ(&sched_p->run_queue, new_runq); + runq = new_runq; } + } #endif - ASSERT(runq); - res = 1; - erts_smp_runq_lock(runq); + ASSERT(runq); - /* Enqueue the process */ - enqueue_process(runq, (int) enq_prio, sched_p); + erts_smp_runq_lock(runq); - if (runq == c_rq) - return res; - erts_smp_runq_unlock(runq); - smp_notify_inc_runq(runq); - } + /* Enqueue the process */ + enqueue_process(runq, (int) enq_prio, sched_p); + + if (runq == c_rq) + return 1; + erts_smp_runq_unlock(runq); + smp_notify_inc_runq(runq); erts_smp_runq_lock(c_rq); - return res; + return 1; } static ERTS_INLINE void @@ -5704,7 +5974,7 @@ change_proc_schedule_state(Process *p, erts_aint32_t e; n = e = a; - enqueue = 0; + enqueue = ERTS_ENQUEUE_NOT; if (a & ERTS_PSFLG_FREE) break; /* We don't want to schedule free processes... */ @@ -5725,13 +5995,13 @@ change_proc_schedule_state(Process *p, * process may be in a run queue via proxy, need * further inspection... */ - enqueue = check_enqueue_in_prio_queue(enq_prio_p, &n, a); + enqueue = check_enqueue_in_prio_queue(p, enq_prio_p, &n, a); } a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) break; - if (enqueue == 0 && n == a) + if (enqueue == ERTS_ENQUEUE_NOT && n == a) break; } @@ -5765,7 +6035,7 @@ schedule_process(Process *p, erts_aint32_t in_state) ERTS_PSFLG_ACTIVE, &state, &enq_prio); - if (enqueue) + if (enqueue != ERTS_ENQUEUE_NOT) add2runq(enqueue > 0 ? p : make_proxy_proc(NULL, p, enq_prio), state, enq_prio); @@ -5792,14 +6062,14 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) if (a & ERTS_PSFLG_FREE) return; /* We don't want to schedule free processes... */ - enqueue = 0; + enqueue = ERTS_ENQUEUE_NOT; n |= ERTS_PSFLG_ACTIVE_SYS; if (!(a & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) - enqueue = check_enqueue_in_prio_queue(&enq_prio, &n, a); + enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); if (a == e) break; - if (a == n && !enqueue) + if (a == n && enqueue == ERTS_ENQUEUE_NOT) goto cleanup; } @@ -5815,7 +6085,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t state, Process *proxy) } - if (enqueue) { + if (enqueue != ERTS_ENQUEUE_NOT) { Process *sched_p; if (enqueue > 0) sched_p = p; @@ -5938,6 +6208,12 @@ static void scheduler_ix_resume_wake(Uint ix) { ErtsSchedulerSleepInfo *ssi = ERTS_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); +} + +static void +scheduler_ssi_resume_wake(ErtsSchedulerSleepInfo *ssi) +{ erts_aint32_t xflgs = (ERTS_SSI_FLG_SLEEPING | ERTS_SSI_FLG_TSE_SLEEPING | ERTS_SSI_FLG_WAITING @@ -6028,12 +6304,20 @@ sched_set_suspended_sleeptype(ErtsSchedulerSleepInfo *ssi) } } +#ifdef ERTS_DIRTY_SCHEDULERS + static void suspend_scheduler(ErtsSchedulerData *esdp) { erts_aint32_t flgs; erts_aint32_t changing; +#ifdef ERTS_DIRTY_SCHEDULERS + long no = (long) (ERTS_SCHEDULER_IS_DIRTY(esdp) + ? ERTS_DIRTY_SCHEDULER_NO(esdp) + : esdp->no); +#else long no = (long) esdp->no; +#endif ErtsSchedulerSleepInfo *ssi = esdp->ssi; long active_schedulers; int curr_online = 1; @@ -6041,21 +6325,305 @@ suspend_scheduler(ErtsSchedulerData *esdp) erts_aint32_t aux_work; int thr_prgr_active = 1; ErtsStuckBoundProcesses sbp = {NULL, NULL}; + int* ss_onlinep; + int* ss_curr_onlinep; + int* ss_wait_curr_onlinep; + long* ss_wait_activep; + long ss_wait_active_target; + erts_smp_atomic32_t* ss_changingp; + erts_smp_atomic32_t* ss_activep; /* * Schedulers may be suspended in two different ways: * - A scheduler may be suspended since it is not online. * All schedulers with scheduler ids greater than - * schdlr_sspnd.online are suspended. + * schdlr_sspnd.online are suspended; same for dirty + * schedulers and schdlr_sspnd.dirty_cpu_online and + * schdlr_sspnd.dirty_io_online. * - Multi scheduling is blocked. All schedulers except the - * scheduler with scheduler id 1 are suspended. + * scheduler with scheduler id 1 are suspended, and all + * dirty CPU and dirty I/O schedulers are suspended. * * Regardless of why a scheduler is suspended, it ends up here. */ + ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) || no != 1); + #ifdef ERTS_DIRTY_SCHEDULERS - ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp)); + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (erts_smp_mtx_trylock(&schdlr_sspnd.mtx) == EBUSY) { + erts_smp_runq_unlock(esdp->run_queue); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + erts_smp_runq_lock(esdp->run_queue); + } + if (ongoing_multi_scheduling_block()) + evacuate_run_queue(esdp->run_queue, &sbp); + } else +#endif + evacuate_run_queue(esdp->run_queue, &sbp); + + erts_smp_runq_unlock(esdp->run_queue); + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) #endif + { + erts_sched_check_cpu_bind_prep_suspend(esdp); + + if (erts_system_profile_flags.scheduler) + profile_scheduler(make_small(esdp->no), am_inactive); + + sched_wall_time_change(esdp, 0); + + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + } + + flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED); + if (flgs & ERTS_SSI_FLG_SUSPENDED) { + +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { + if (ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(esdp->run_queue)) { + active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_cpu_active); + ASSERT(active_schedulers >= 0); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing); + ss_onlinep = &schdlr_sspnd.dirty_cpu_online; + ss_curr_onlinep = &schdlr_sspnd.dirty_cpu_curr_online; + ss_wait_curr_onlinep = &schdlr_sspnd.dirty_cpu_wait_curr_online; + ss_changingp = &schdlr_sspnd.dirty_cpu_changing; + ss_wait_activep = &schdlr_sspnd.msb.dirty_cpu_wait_active; + ss_activep = &schdlr_sspnd.dirty_cpu_active; + } else { + active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.dirty_io_active); + ASSERT(active_schedulers >= 0); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing); + ss_onlinep = &schdlr_sspnd.dirty_io_online; + ss_curr_onlinep = &schdlr_sspnd.dirty_io_curr_online; + ss_wait_curr_onlinep = &schdlr_sspnd.dirty_io_wait_curr_online; + ss_changingp = &schdlr_sspnd.dirty_io_changing; + ss_wait_activep = &schdlr_sspnd.msb.dirty_io_wait_active; + ss_activep = &schdlr_sspnd.dirty_io_active; + } + ss_wait_active_target = 0; + } + else +#endif + { + active_schedulers = erts_smp_atomic32_dec_read_nob(&schdlr_sspnd.active); + ASSERT(active_schedulers >= 1); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); + ss_onlinep = &schdlr_sspnd.online; + ss_curr_onlinep = &schdlr_sspnd.curr_online; + ss_wait_curr_onlinep = &schdlr_sspnd.wait_curr_online; + ss_changingp = &schdlr_sspnd.changing; + ss_wait_activep = &schdlr_sspnd.msb.wait_active; + ss_activep = &schdlr_sspnd.active; + ss_wait_active_target = 1; + } + if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) { + if (active_schedulers == *ss_wait_activep) + wake = 1; + if (active_schedulers == ss_wait_active_target) { + changing = erts_smp_atomic32_read_band_nob(ss_changingp, + ~ERTS_SCHDLR_SSPND_CHNG_MSB); + changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB; + } + } + + while (1) { + if (changing & ERTS_SCHDLR_SSPND_CHNG_ONLN) { + int changed = 0; + if (no > *ss_onlinep && curr_online) { + (*ss_curr_onlinep)--; + curr_online = 0; + changed = 1; + } + else if (no <= *ss_onlinep && !curr_online) { + (*ss_curr_onlinep)++; + curr_online = 1; + changed = 1; + } + if (changed + && *ss_curr_onlinep == *ss_wait_curr_onlinep) + wake = 1; + if (*ss_onlinep == *ss_curr_onlinep) { + changing = erts_smp_atomic32_read_band_nob(ss_changingp, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN; + } + } + + if (wake) { + erts_smp_cnd_signal(&schdlr_sspnd.cnd); + wake = 0; + } + + if (curr_online && !ongoing_multi_scheduling_block()) { + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); + if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) + break; + } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + + while (1) { + erts_aint32_t qmask; + erts_aint32_t flgs; + + qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue) + & ERTS_RUNQ_FLGS_QMASK); + aux_work = erts_atomic32_read_acqb(&ssi->aux_work); + if (aux_work|qmask) { + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + if (aux_work) + aux_work = handle_aux_work(&esdp->aux_work_data, + aux_work, + 1); + + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && + (aux_work && erts_thr_progress_update(esdp))) + erts_thr_progress_leader_update(esdp); + if (qmask) { +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + erts_smp_runq_lock(esdp->run_queue); + if (ongoing_multi_scheduling_block()) + evacuate_run_queue(esdp->run_queue, &sbp); + erts_smp_runq_unlock(esdp->run_queue); + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + } else +#endif + { + erts_smp_runq_lock(esdp->run_queue); + evacuate_run_queue(esdp->run_queue, &sbp); + erts_smp_runq_unlock(esdp->run_queue); + } + } + } + + if (!aux_work) { +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + { + if (thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + erts_thr_progress_prepare_wait(esdp); + } + flgs = sched_spin_suspended(ssi, + ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); + if (flgs == (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED)) { + flgs = sched_set_suspended_sleeptype(ssi); + if (flgs == (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_TSE_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED)) { + int res; + + do { + res = erts_tse_wait(ssi->event); + } while (res == EINTR); + } + } +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + erts_thr_progress_finalize_wait(esdp); + } + + flgs = sched_prep_spin_suspended(ssi, (ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED)); + if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) + break; + changing = erts_smp_atomic32_read_nob(ss_changingp); + if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER) + break; + } + + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + changing = erts_smp_atomic32_read_nob(ss_changingp); + } + + active_schedulers = erts_smp_atomic32_inc_read_nob(ss_activep); + changing = erts_smp_atomic32_read_nob(ss_changingp); + if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB) + && *ss_onlinep == active_schedulers) { + erts_smp_atomic32_read_band_nob(ss_changingp, + ~ERTS_SCHDLR_SSPND_CHNG_MSB); + } + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + ASSERT(no <= *ss_onlinep); + ASSERT(!ongoing_multi_scheduling_block()); + + } + + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + + ASSERT(curr_online); + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + { + if (erts_system_profile_flags.scheduler) + profile_scheduler(make_small(esdp->no), am_active); + + if (!thr_prgr_active) { + erts_thr_progress_active(esdp, thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); + } + } + + erts_smp_runq_lock(esdp->run_queue); + non_empty_runq(esdp->run_queue); + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) +#endif + { + schedule_bound_processes(esdp->run_queue, &sbp); + + erts_sched_check_cpu_bind_post_suspend(esdp); + } +} + +#else /* !ERTS_DIRTY_SCHEDULERS */ + +static void +suspend_scheduler(ErtsSchedulerData *esdp) +{ + erts_aint32_t flgs; + erts_aint32_t changing; + long no = (long) esdp->no; + ErtsSchedulerSleepInfo *ssi = esdp->ssi; + long active_schedulers; + int curr_online = 1; + int wake = 0; + erts_aint32_t aux_work; + int thr_prgr_active = 1; + ErtsStuckBoundProcesses sbp = {NULL, NULL}; + + /* + * Schedulers may be suspended in two different ways: + * - A scheduler may be suspended since it is not online. + * All schedulers with scheduler ids greater than + * schdlr_sspnd.online are suspended. + * - Multi scheduling is blocked. All schedulers except the + * scheduler with scheduler id 1 are suspended. + * + * Regardless of why a scheduler is suspended, it ends up here. + */ + ASSERT(no != 1); evacuate_run_queue(esdp->run_queue, &sbp); @@ -6219,6 +6787,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) erts_sched_check_cpu_bind_post_suspend(esdp); } +#endif + ErtsSchedSuspendResult erts_schedulers_state(Uint *total, Uint *online, @@ -6230,40 +6800,315 @@ erts_schedulers_state(Uint *total, { int res = ERTS_SCHDLR_SSPND_EINVAL; erts_aint32_t changing; - if (total) { - ASSERT(online); - ASSERT(active); - erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); - if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) - res = ERTS_SCHDLR_SSPND_YIELD_RESTART; - else { - *active = *online = schdlr_sspnd.online; - if (ongoing_multi_scheduling_block()) - *active = 1; - res = ERTS_SCHDLR_SSPND_DONE; - } - erts_smp_mtx_unlock(&schdlr_sspnd.mtx); - *total = erts_no_schedulers; + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); +#ifdef ERTS_DIRTY_SCHEDULERS + changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) + | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing)); +#endif + if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) + res = ERTS_SCHDLR_SSPND_YIELD_RESTART; + else { + if (active) + *active = schdlr_sspnd.online; + if (online) + *online = schdlr_sspnd.online; + if (ongoing_multi_scheduling_block() && active) + *active = 1; +#ifdef ERTS_DIRTY_SCHEDULERS + if (dirty_cpu_online) + *dirty_cpu_online = schdlr_sspnd.dirty_cpu_online; +#endif + res = ERTS_SCHDLR_SSPND_DONE; } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + if (total) + *total = erts_no_schedulers; #ifdef ERTS_DIRTY_SCHEDULERS if (dirty_cpu) *dirty_cpu = erts_no_dirty_cpu_schedulers; - if (dirty_cpu_online) - *dirty_cpu_online = erts_no_dirty_cpu_schedulers_online; if (dirty_io) *dirty_io = erts_no_dirty_io_schedulers; -#else - if (dirty_cpu) - *dirty_cpu = 0; - if (dirty_cpu_online) - *dirty_cpu_online = 0; - if (dirty_io) - *dirty_io = 0; #endif return res; } +#ifdef ERTS_DIRTY_SCHEDULERS + +ErtsSchedSuspendResult +erts_set_schedulers_online(Process *p, + ErtsProcLocks plocks, + Sint new_no, + Sint *old_no +#ifdef ERTS_DIRTY_SCHEDULERS + , int dirty_only +#endif + ) +{ + ErtsSchedulerData *esdp; + int ix, res = -1, no, have_unlocked_plocks, end_wait; + erts_aint32_t changing = 0; +#ifdef ERTS_DIRTY_SCHEDULERS + ErtsSchedulerSleepInfo* ssi; + int dirty_no, change_dirty; +#endif + + if (new_no < 1) + return ERTS_SCHDLR_SSPND_EINVAL; +#ifdef ERTS_DIRTY_SCHEDULERS + else if (dirty_only && erts_no_dirty_cpu_schedulers < new_no) + return ERTS_SCHDLR_SSPND_EINVAL; +#endif + else if (erts_no_schedulers < new_no) + return ERTS_SCHDLR_SSPND_EINVAL; + + esdp = ERTS_PROC_GET_SCHDATA(p); + end_wait = 0; + + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + + have_unlocked_plocks = 0; + no = (int) new_no; + +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(schdlr_sspnd.dirty_cpu_online <= erts_no_dirty_cpu_schedulers); + if (dirty_only) { + if (no > schdlr_sspnd.online) { + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + return ERTS_SCHDLR_SSPND_EINVAL; + } + dirty_no = no; + } else { + /* + * Adjust the number of dirty CPU schedulers online relative to the + * adjustment made to the number of normal schedulers online. + */ + int total_pct = erts_no_dirty_cpu_schedulers*100/erts_no_schedulers; + int onln_pct = no*total_pct/schdlr_sspnd.online; + dirty_no = schdlr_sspnd.dirty_cpu_online*onln_pct/100; + if (dirty_no == 0) + dirty_no = 1; + ASSERT(dirty_no <= erts_no_dirty_cpu_schedulers); + } +#endif + changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); +#ifdef ERTS_DIRTY_SCHEDULERS + changing |= erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing); +#endif + if (changing) { + res = ERTS_SCHDLR_SSPND_YIELD_RESTART; + } + else { + int online = *old_no = schdlr_sspnd.online; +#ifdef ERTS_DIRTY_SCHEDULERS + int dirty_online = schdlr_sspnd.dirty_cpu_online; + + if (dirty_only) { + *old_no = schdlr_sspnd.dirty_cpu_online; + if (dirty_no == schdlr_sspnd.dirty_cpu_online) { + res = ERTS_SCHDLR_SSPND_DONE; + } + change_dirty = 1; + } else { +#endif + if (no == schdlr_sspnd.online) { +#ifdef ERTS_DIRTY_SCHEDULERS + dirty_only = 1; + if (dirty_no == schdlr_sspnd.dirty_cpu_online) +#endif + res = ERTS_SCHDLR_SSPND_DONE; +#ifdef ERTS_DIRTY_SCHEDULERS + else + change_dirty = 1; +#endif + } +#ifdef ERTS_DIRTY_SCHEDULERS + else + change_dirty = (dirty_no != schdlr_sspnd.dirty_cpu_online); + } +#endif + if (res == -1) + { + int increase = (no > online); +#ifdef ERTS_DIRTY_SCHEDULERS + if (!dirty_only) { +#endif + ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + schdlr_sspnd.online = no; +#ifdef ERTS_DIRTY_SCHEDULERS + } else + increase = (dirty_no > dirty_online); + if (change_dirty) { + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + schdlr_sspnd.dirty_cpu_online = dirty_no; + } +#endif + if (increase) { + int ix; +#ifdef ERTS_DIRTY_SCHEDULERS + if (!dirty_only) { +#endif + schdlr_sspnd.wait_curr_online = no; + if (ongoing_multi_scheduling_block()) { + for (ix = online; ix < no; ix++) + erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); + } + else { + if (plocks) { + have_unlocked_plocks = 1; + erts_smp_proc_unlock(p, plocks); + } + change_no_used_runqs(no); + + for (ix = online; ix < no; ix++) + resume_run_queue(ERTS_RUNQ_IX(ix)); + + for (ix = no; ix < erts_no_run_queues; ix++) + suspend_run_queue(ERTS_RUNQ_IX(ix)); + } +#ifdef ERTS_DIRTY_SCHEDULERS + } + if (change_dirty) { + schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no; + ASSERT(schdlr_sspnd.dirty_cpu_curr_online != + schdlr_sspnd.dirty_cpu_wait_curr_online); + if (ongoing_multi_scheduling_block()) { + for (ix = dirty_online; ix < dirty_no; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_sched_poke(ssi); + } + } else { + for (ix = dirty_online; ix < dirty_no; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + erts_smp_atomic32_read_band_nob(&ssi->flags, + ~ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + } + } +#endif + res = ERTS_SCHDLR_SSPND_DONE; + } + else /* if (no < online) */ { +#ifdef ERTS_DIRTY_SCHEDULERS + if (change_dirty) { + schdlr_sspnd.dirty_cpu_wait_curr_online = dirty_no; + ASSERT(schdlr_sspnd.dirty_cpu_curr_online != + schdlr_sspnd.dirty_cpu_wait_curr_online); + if (ongoing_multi_scheduling_block()) { + for (ix = dirty_no; ix < dirty_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_sched_poke(ssi); + } + } else { + for (ix = dirty_no; ix < dirty_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + } + } + if (dirty_only) { + res = ERTS_SCHDLR_SSPND_DONE; + } + else +#endif + { + if (p->scheduler_data->no <= no) { + res = ERTS_SCHDLR_SSPND_DONE; + schdlr_sspnd.wait_curr_online = no; + } + else { + /* + * Yield! Current process needs to migrate + * before bif returns. + */ + res = ERTS_SCHDLR_SSPND_YIELD_DONE; + schdlr_sspnd.wait_curr_online = no+1; + } + + if (ongoing_multi_scheduling_block()) { + for (ix = no; ix < online; ix++) + erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(ix)); + } + else { + if (plocks) { + have_unlocked_plocks = 1; + erts_smp_proc_unlock(p, plocks); + } + + change_no_used_runqs(no); + for (ix = no; ix < erts_no_run_queues; ix++) + suspend_run_queue(ERTS_RUNQ_IX(ix)); + + for (ix = no; ix < online; ix++) { + ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); + wake_scheduler(rq); + } + } + } + } + +#ifdef ERTS_DIRTY_SCHEDULERS + if (change_dirty) { + while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing, + ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + if (!dirty_only) +#endif + { + if (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) { + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + if (plocks && !have_unlocked_plocks) { + have_unlocked_plocks = 1; + erts_smp_proc_unlock(p, plocks); + } + erts_thr_progress_active(esdp, 0); + erts_thr_progress_prepare_wait(esdp); + end_wait = 1; + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + } + + while (schdlr_sspnd.curr_online != schdlr_sspnd.wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + + ASSERT(res != ERTS_SCHDLR_SSPND_DONE + ? (ERTS_SCHDLR_SSPND_CHNG_WAITER + & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) + : (ERTS_SCHDLR_SSPND_CHNG_WAITER + == erts_smp_atomic32_read_nob(&schdlr_sspnd.changing))); + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + } + } + + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(schdlr_sspnd.dirty_cpu_online <= schdlr_sspnd.online); + if (!dirty_only) +#endif + { + if (end_wait) { + erts_thr_progress_finalize_wait(esdp); + erts_thr_progress_active(esdp, 1); + } + if (have_unlocked_plocks) + erts_smp_proc_lock(p, plocks); + } + + return res; +} + +#else /* !ERTS_DIRTY_SCHEDULERS */ + ErtsSchedSuspendResult erts_set_schedulers_online(Process *p, ErtsProcLocks plocks, @@ -6352,10 +7197,6 @@ erts_set_schedulers_online(Process *p, ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); wake_scheduler(rq); } -#ifdef ERTS_DIRTY_SCHEDULERS - wake_dirty_scheduler(ERTS_DIRTY_CPU_RUNQ); - wake_dirty_scheduler(ERTS_DIRTY_IO_RUNQ); -#endif } } @@ -6396,15 +7237,24 @@ erts_set_schedulers_online(Process *p, return res; } +#endif + ErtsSchedSuspendResult erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) { - int ix, res, have_unlocked_plocks = 0; + int ix, res, have_unlocked_plocks = 0, online; erts_aint32_t changing; ErtsProcList *plp; +#ifdef ERTS_DIRTY_SCHEDULERS + ErtsSchedulerSleepInfo* ssi; +#endif erts_smp_mtx_lock(&schdlr_sspnd.mtx); changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing); +#ifdef ERTS_DIRTY_SCHEDULERS + changing |= (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) + | erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing)); +#endif if (changing) { res = ERTS_SCHDLR_SSPND_YIELD_RESTART; /* Yield */ } @@ -6414,10 +7264,13 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp); p->flags |= F_HAVE_BLCKD_MSCHED; ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 0); + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) == 0); +#endif ASSERT(p->scheduler_data->no == 1); res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; - } - else { + } else { int online = schdlr_sspnd.online; p->flags |= F_HAVE_BLCKD_MSCHED; if (plocks) { @@ -6429,6 +7282,35 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) if (online == 1) { res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); +#ifdef ERTS_DIRTY_SCHEDULERS + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) == 1); + ASSERT(!(erts_smp_atomic32_read_nob(&ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0)->flags) + & ERTS_SSI_FLG_SUSPENDED)); + schdlr_sspnd.msb.dirty_cpu_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(0); + erts_smp_atomic32_read_bor_nob(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) + != schdlr_sspnd.msb.dirty_cpu_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + + schdlr_sspnd.msb.dirty_io_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) + != schdlr_sspnd.msb.dirty_io_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); +#endif ASSERT(p->scheduler_data->no == 1); } else { @@ -6447,6 +7329,37 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) schdlr_sspnd.msb.wait_active = 2; } +#ifdef ERTS_DIRTY_SCHEDULERS + schdlr_sspnd.msb.dirty_cpu_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_active) + != schdlr_sspnd.msb.dirty_cpu_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + ASSERT(schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_online); + + schdlr_sspnd.msb.dirty_io_wait_active = 0; + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_MSB + | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + erts_smp_atomic32_read_bor_nob(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); + while (erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_active) + != schdlr_sspnd.msb.dirty_io_wait_active) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + ASSERT(schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_online); +#endif change_no_used_runqs(1); for (ix = 1; ix < erts_no_run_queues; ix++) suspend_run_queue(ERTS_RUNQ_IX(ix)); @@ -6487,6 +7400,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) erts_smp_mtx_lock(&schdlr_sspnd.mtx); } + ASSERT(res != ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED ? (ERTS_SCHDLR_SSPND_CHNG_WAITER & erts_smp_atomic32_read_nob(&schdlr_sspnd.changing)) @@ -6527,12 +7441,12 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) p->flags &= ~F_HAVE_BLCKD_MSCHED; schdlr_sspnd.msb.ongoing = 0; if (schdlr_sspnd.online == 1) { - /* No schedulers to resume */ + /* No normal schedulers to resume */ ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1); ERTS_SCHDLR_SSPND_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_MSB); } else { - int online = schdlr_sspnd.online; + online = schdlr_sspnd.online; if (plocks) { have_unlocked_plocks = 1; erts_smp_proc_unlock(p, plocks); @@ -6547,6 +7461,27 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) for (ix = online; ix < erts_no_run_queues; ix++) suspend_run_queue(ERTS_RUNQ_IX(ix)); } +#ifdef ERTS_DIRTY_SCHEDULERS + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0); + schdlr_sspnd.msb.dirty_cpu_wait_active = schdlr_sspnd.dirty_cpu_online; + for (ix = 0; ix < schdlr_sspnd.dirty_cpu_online; ix++) { + ssi = ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + erts_smp_atomic32_read_band_nob(&ssi->flags, + ~ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_CPU_RUNQ, 0); + + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0); + schdlr_sspnd.msb.dirty_io_wait_active = erts_no_dirty_io_schedulers; + for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { + ssi = ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(ix); + scheduler_ssi_resume_wake(ssi); + erts_smp_atomic32_read_band_nob(&ssi->flags, + ~ERTS_SSI_FLG_SUSPENDED); + } + wake_dirty_schedulers(ERTS_DIRTY_IO_RUNQ, 0); +#endif res = ERTS_SCHDLR_SSPND_DONE; } } @@ -6673,7 +7608,11 @@ sched_thread_func(void *vesdp) erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing, ~ERTS_SCHDLR_SSPND_CHNG_ONLN); if (no != 1) +#ifdef ERTS_DIRTY_SCHEDULERS + erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); +#else erts_smp_cnd_signal(&schdlr_sspnd.cnd); +#endif } if (no == 1) { @@ -6710,7 +7649,9 @@ sched_dirty_cpu_thread_func(void *vesdp) { ErtsThrPrgrCallbacks callbacks; ErtsSchedulerData *esdp = vesdp; - Uint no = esdp->dirty_no; + Uint no = ERTS_DIRTY_SCHEDULER_NO(esdp); + ERTS_DIRTY_SCHEDULER_TYPE(esdp) = ERTS_DIRTY_CPU_SCHEDULER; + ASSERT(no != 0); ERTS_DIRTY_CPU_SCHED_SLEEP_INFO_IX(no-1)->event = erts_tse_fetch(); callbacks.arg = (void *) esdp->ssi; callbacks.wakeup = thr_prgr_wakeup; @@ -6738,6 +7679,24 @@ sched_dirty_cpu_thread_func(void *vesdp) #endif erts_thread_init_float(); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_cpu_changing) + & ERTS_SCHDLR_SSPND_CHNG_ONLN); + + if (--schdlr_sspnd.dirty_cpu_curr_online == schdlr_sspnd.dirty_cpu_wait_curr_online) { + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_cpu_changing, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + if (no != 1) + erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); + } + + if (no == 1) { + while (schdlr_sspnd.dirty_cpu_curr_online != schdlr_sspnd.dirty_cpu_wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_CPU_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + process_main(); /* No schedulers should *ever* terminate */ erl_exit(ERTS_ABORT_EXIT, @@ -6751,7 +7710,9 @@ sched_dirty_io_thread_func(void *vesdp) { ErtsThrPrgrCallbacks callbacks; ErtsSchedulerData *esdp = vesdp; - Uint no = esdp->dirty_no; + Uint no = ERTS_DIRTY_SCHEDULER_NO(esdp); + ERTS_DIRTY_SCHEDULER_TYPE(esdp) = ERTS_DIRTY_IO_SCHEDULER; + ASSERT(no != 0); ERTS_DIRTY_IO_SCHED_SLEEP_INFO_IX(no-1)->event = erts_tse_fetch(); callbacks.arg = (void *) esdp->ssi; callbacks.wakeup = thr_prgr_wakeup; @@ -6779,6 +7740,24 @@ sched_dirty_io_thread_func(void *vesdp) #endif erts_thread_init_float(); + erts_smp_mtx_lock(&schdlr_sspnd.mtx); + ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.dirty_io_changing) + & ERTS_SCHDLR_SSPND_CHNG_ONLN); + + if (--schdlr_sspnd.dirty_io_curr_online == schdlr_sspnd.dirty_io_wait_curr_online) { + erts_smp_atomic32_read_band_nob(&schdlr_sspnd.dirty_io_changing, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + if (no != 1) + erts_smp_cnd_broadcast(&schdlr_sspnd.cnd); + } + + if (no == 1) { + while (schdlr_sspnd.dirty_io_curr_online != schdlr_sspnd.dirty_io_wait_curr_online) + erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); + ERTS_SCHDLR_SSPND_DIRTY_IO_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_WAITER); + } + erts_smp_mtx_unlock(&schdlr_sspnd.mtx); + process_main(); /* No schedulers should *ever* terminate */ erl_exit(ERTS_ABORT_EXIT, @@ -7995,7 +8974,6 @@ Process *schedule(Process *p, int calls) || flags & ERTS_RUNQ_FLG_NONEMPTY); if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) { - if (flags & ERTS_RUNQ_FLG_SUSPENDED) { suspend_scheduler(esdp); flags = ERTS_RUNQ_FLGS_GET_NOB(rq); @@ -8006,10 +8984,17 @@ Process *schedule(Process *p, int calls) erts_sched_check_cpu_bind(esdp); } } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (ERTS_SCHEDULER_IS_DIRTY(esdp) + && (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) + & ERTS_SSI_FLG_SUSPENDED)) + suspend_scheduler(esdp); +#endif - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { + { erts_aint32_t aux_work; - int leader_update = erts_thr_progress_update(esdp); + int leader_update = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 + : erts_thr_progress_update(esdp); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); if (aux_work | leader_update) { erts_smp_runq_unlock(rq); @@ -8020,7 +9005,8 @@ Process *schedule(Process *p, int calls) erts_smp_runq_lock(rq); } - ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking()); + ERTS_SMP_LC_ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp) + || !erts_thr_progress_is_blocking()); } ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); @@ -8035,6 +9021,17 @@ Process *schedule(Process *p, int calls) flags = ERTS_RUNQ_FLGS_GET_NOB(rq); +#ifdef ERTS_DIRTY_SCHEDULERS + if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix) && rq->halt_in_progress) { + /* + * TODO: if halt in progress, need to put the dirty scheduler + * to sleep somewhere around here to prevent it from picking up + * new work + */ + } + else +#endif + if ((!(flags & ERTS_RUNQ_FLGS_QMASK) && !rq->misc.start) || (rq->halt_in_progress && ERTS_EMPTY_RUNQ_PORTS(rq))) { /* Prepare for scheduler wait */ @@ -8182,11 +9179,15 @@ Process *schedule(Process *p, int calls) + ERTS_PSFLGS_IN_PRQ_MASK_OFFSET)); #ifdef ERTS_DIRTY_SCHEDULERS - /* if a non-dirty scheduler picks up a process marked as already being - in a dirty run queue, just drop it and go get another process */ - if (state & (ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q) && - !ERTS_SCHEDULER_IS_DIRTY(esdp)) - goto pick_next_process; + ASSERT((state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) != + (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)); + if (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) { + ASSERT((ERTS_SCHEDULER_IS_DIRTY_CPU(esdp) && (state & ERTS_PSFLG_DIRTY_CPU_PROC)) || + (ERTS_SCHEDULER_IS_DIRTY_IO(esdp) && (state & ERTS_PSFLG_DIRTY_IO_PROC))); + if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && !(state & ERTS_PSFLG_ACTIVE_SYS)) + goto pick_next_process; + state &= ~(ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q); + } #endif if (!(state & ERTS_PSFLG_PROXY)) @@ -8322,7 +9323,11 @@ Process *schedule(Process *p, int calls) if (state & ERTS_PSFLG_RUNNING_SYS) { reds -= execute_sys_tasks(p, &state, reds); - if (reds <= 0) { + if (reds <= 0 +#ifdef ERTS_DIRTY_SCHEDULERS + || (state & (ERTS_PSFLG_DIRTY_CPU_PROC|ERTS_PSFLG_DIRTY_IO_PROC)) +#endif + ) { p->fcalls = reds; goto sched_out_proc; } @@ -10087,7 +11092,7 @@ save_pending_exiter(Process *p) erts_smp_runq_unlock(rq); #ifdef ERTS_DIRTY_SCHEDULERS if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - wake_dirty_scheduler(rq); + wake_dirty_schedulers(rq, 0); else #endif wake_scheduler(rq); @@ -11148,6 +12153,10 @@ void erl_halt(int code) if (-1 == erts_smp_atomic32_cmpxchg_acqb(&erts_halt_progress, erts_no_schedulers, -1)) { +#ifdef ERTS_DIRTY_SCHEDULERS + ERTS_DIRTY_CPU_RUNQ->halt_in_progress = 1; + ERTS_DIRTY_IO_RUNQ->halt_in_progress = 1; +#endif erts_halt_code = code; notify_reap_ports_relb(); } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index dcb9251d0d..f6b185c0ad 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -109,7 +109,6 @@ extern int erts_sched_balance_util; extern Uint erts_no_schedulers; #ifdef ERTS_DIRTY_SCHEDULERS extern Uint erts_no_dirty_cpu_schedulers; -extern Uint erts_no_dirty_cpu_schedulers_online; extern Uint erts_no_dirty_io_schedulers; #endif extern Uint erts_no_run_queues; @@ -544,6 +543,21 @@ typedef struct { #endif } ErtsAuxWorkData; +#ifdef ERTS_DIRTY_SCHEDULERS +typedef enum { + ERTS_DIRTY_CPU_SCHEDULER, + ERTS_DIRTY_IO_SCHEDULER +} ErtsDirtySchedulerType; + +typedef union { + struct { + ErtsDirtySchedulerType type: 1; + unsigned num: 31; + } s; + Uint no; +} ErtsDirtySchedId; +#endif + struct ErtsSchedulerData_ { /* * Keep X registers first (so we get as many low @@ -570,7 +584,7 @@ struct ErtsSchedulerData_ { Process *current_process; Uint no; /* Scheduler number for normal schedulers */ #ifdef ERTS_DIRTY_SCHEDULERS - Uint dirty_no; /* Scheduler number for dirty schedulers */ + ErtsDirtySchedId dirty_no; /* Scheduler number for dirty schedulers */ #endif Port *current_port; ErtsRunQueue *run_queue; @@ -1292,6 +1306,8 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; &erts_aligned_run_queues[(IX)].runq) #define ERTS_DIRTY_CPU_RUNQ (&erts_aligned_run_queues[-1].runq) #define ERTS_DIRTY_IO_RUNQ (&erts_aligned_run_queues[-2].runq) +#define ERTS_RUNQ_IS_DIRTY_CPU_RUNQ(RQ) ((RQ)->ix == -1) +#define ERTS_RUNQ_IS_DIRTY_IO_RUNQ(RQ) ((RQ)->ix == -2) #else #define ERTS_RUNQ_IX_IS_DIRTY(IX) 0 #endif @@ -1305,21 +1321,37 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define ERTS_DIRTY_IO_SCHEDULER_IX(IX) \ (ASSERT(0 <= (IX) && (IX) < erts_no_dirty_io_schedulers), \ &erts_aligned_dirty_io_scheduler_data[(IX)].esd) +#define ERTS_DIRTY_SCHEDULER_NO(ESDP) \ + ((ESDP)->dirty_no.s.num) +#define ERTS_DIRTY_SCHEDULER_TYPE(ESDP) \ + ((ESDP)->dirty_no.s.type) #ifdef ERTS_SMP #define ERTS_SCHEDULER_IS_DIRTY(ESDP) \ - ((ESDP)->dirty_no != 0) + ((ESDP)->dirty_no.s.num != 0) +#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) \ + ((ESDP)->dirty_no.s.type == 0) +#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) \ + ((ESDP)->dirty_no.s.type == 1) #else #define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) 0 #endif #else #define ERTS_RUNQ_IX_IS_DIRTY(IX) 0 #define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0 +#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) 0 #endif void erts_pre_init_process(void); void erts_late_init_process(void); void erts_early_init_scheduling(int); -void erts_init_scheduling(int, int); +void erts_init_scheduling(int, int +#ifdef ERTS_DIRTY_SCHEDULERS + , int, int, int +#endif + ); int erts_set_gc_state(Process *c_p, int enable); Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable); @@ -1526,7 +1558,11 @@ ErtsSchedSuspendResult erts_set_schedulers_online(Process *p, ErtsProcLocks plocks, Sint new_no, - Sint *old_no); + Sint *old_no +#ifdef ERTS_DIRTY_SCHEDULERS + , int dirty_only +#endif + ); ErtsSchedSuspendResult erts_block_multi_scheduling(Process *, ErtsProcLocks, int, int); int erts_is_multi_scheduling_blocked(void); diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 5b81d814c6..43a19e97f1 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -202,17 +202,18 @@ int eq(Eterm, Eterm); #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) #if HALFWORD_HEAP -Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); -#define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) -#define CMP(A,B) cmp_rel_opt(A,NULL,B,NULL,0) -#define CMP_TERM(A,B) cmp_rel_opt(A,NULL,B,NULL,1) +Sint erts_cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); +#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) erts_cmp_rel_opt(A,NULL,B,NULL,1) #else -Sint cmp(Eterm, Eterm, int); -#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) -#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp(A,B,1) -#define CMP(A,B) cmp(A,B,0) -#define CMP_TERM(A,B) cmp(A,B,1) +Sint cmp(Eterm, Eterm); +Sint erts_cmp(Eterm, Eterm, int); +#define cmp_rel(A,A_BASE,B,B_BASE) erts_cmp(A,B,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) erts_cmp(A,B,1) +#define CMP(A,B) erts_cmp(A,B,0) +#define CMP_TERM(A,B) erts_cmp(A,B,1) #endif #define cmp_lt(a,b) (CMP((a),(b)) < 0) diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 9ea3a3a8ea..73630fda8e 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1469,17 +1469,16 @@ apply_last I P # Map instructions in R17. # -# put_map Fail Src Dst Live Size Rest=* => jump Fail -# is_map Fail Src => jump Fail -# has_map_field Fail Src Key => jump Fail -# get_map_element Fail Src Key Dst => jump Fail - put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest -put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest -put_map_assoc F Src Dst Live Size Rest=* => \ +put_map_assoc F Src=s Dst Live Size Rest=* => \ update_map_assoc F Src Dst Live Size Rest -put_map_exact F Src Dst Live Size Rest=* => \ +put_map_assoc F Src Dst Live Size Rest=* => \ + move Src x | update_map_assoc F x Dst Live Size Rest +put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_exact F Src=s Dst Live Size Rest=* => \ update_map_exact F Src Dst Live Size Rest +put_map_exact F Src Dst Live Size Rest=* => \ + move Src x | update_map_exact F x Dst Live Size Rest new_map j d I I update_map_assoc j s d I I @@ -1492,6 +1491,15 @@ is_map f r is_map f x is_map f y +## Transform has_map_field(s) #{ K1 := _, K2 := _ } + +has_map_field/3 + +has_map_fields Fail Src Size=u==1 Rest=* => gen_has_map_field(Fail,Src,Size,Rest) +has_map_fields Fail Src Size Rest=* => i_has_map_fields Fail Src Size Rest + +i_has_map_fields f s I + has_map_field Fail Src=rxy Key=arxy => i_has_map_field Fail Src Key has_map_field Fail Src Key => move Key x | i_has_map_field Fail Src x @@ -1509,6 +1517,15 @@ i_has_map_field f r y i_has_map_field f x y i_has_map_field f y y +## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 } + +get_map_element/4 + +get_map_elements Fail Src=rxy Size=u==2 Rest=* => gen_get_map_element(Fail,Src,Size,Rest) +get_map_elements Fail Src Size Rest=* => i_get_map_elements Fail Src Size Rest + +i_get_map_elements f s I + get_map_element Fail Src=rxy Key=ax Dst => i_get_map_element Fail Src Key Dst get_map_element Fail Src=rxy Key=rycq Dst => \ move Key x | i_get_map_element Fail Src x Dst diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bc4a05d385..7da555b18d 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2425,14 +2425,24 @@ static int cmp_atoms(Eterm a, Eterm b) bb->name+3, bb->len-3); } -/* cmp(Eterm a, Eterm b, int exact) +#if !HALFWORD_HEAP +/* cmp(Eterm a, Eterm b) + * For compatibility with HiPE - arith-based compare. + */ +Sint cmp(Eterm a, Eterm b) +{ + return erts_cmp(a, b, 0); +} +#endif + +/* erts_cmp(Eterm a, Eterm b, int exact) * exact = 1 -> term-based compare * exact = 0 -> arith-based compare */ #if HALFWORD_HEAP -Sint cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) +Sint erts_cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) #else -Sint cmp(Eterm a, Eterm b, int exact) +Sint erts_cmp(Eterm a, Eterm b, int exact) #endif { DECLARE_WSTACK(stack); diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index a5294ad84e..865cb50a56 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -2561,7 +2561,7 @@ void *erts_sys_aligned_alloc(UWord alignment, UWord size) #ifdef HAVE_POSIX_MEMALIGN void *ptr = NULL; int error; - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ error = posix_memalign(&ptr, (size_t) alignment, (size_t) size); #if HAVE_ERTS_MSEG if (error || !ptr) { @@ -2584,7 +2584,7 @@ void *erts_sys_aligned_alloc(UWord alignment, UWord size) void erts_sys_aligned_free(UWord alignment, void *ptr) { - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ free(ptr); } diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 5ea4703a7a..a11ac73ebc 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -2757,7 +2757,7 @@ void erts_sys_free(ErtsAlcType_t t, void *x, void *p) void *erts_sys_aligned_alloc(UWord alignment, UWord size) { void *ptr; - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ ptr = _aligned_malloc((size_t) size, (size_t) alignment); ASSERT(!ptr || (((UWord) ptr) & (alignment - 1)) == 0); return ptr; @@ -2765,14 +2765,14 @@ void *erts_sys_aligned_alloc(UWord alignment, UWord size) void erts_sys_aligned_free(UWord alignment, void *ptr) { - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ _aligned_free(ptr); } void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old_size) { void *new_ptr; - ASSERT(alignment && (alignment & ~alignment) == 0); /* power of 2 */ + ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */ new_ptr = _aligned_realloc(ptr, (size_t) size, (size_t) alignment); ASSERT(!new_ptr || (((UWord) new_ptr) & (alignment - 1)) == 0); return new_ptr; diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 06211406b4..c62bc0c454 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1945,6 +1945,14 @@ otp_9302(Config) when is_list(Config) -> end. thr_free_drv(Config) when is_list(Config) -> + case erlang:system_info(threads) of + false -> + {skipped, "No thread support"}; + true -> + thr_free_drv_do(Config) + end. + +thr_free_drv_do(Config) -> ?line Path = ?config(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, thr_free_drv), diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c index 7c144d20cf..ad29d17f06 100644 --- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c +++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011-2013. All Rights Reserved. + * Copyright Ericsson AB 2011-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -94,7 +94,7 @@ DRIVER_INIT(otp_9302_drv) static void stop(ErlDrvData drv_data) { Otp9302Data *data = (Otp9302Data *) drv_data; - if (!data->smp) + if (data->msgq.mtx) erl_drv_mutex_destroy(data->msgq.mtx); driver_free(data); } @@ -114,13 +114,16 @@ static ErlDrvData start(ErlDrvPort port, driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); data->smp = sys_info.smp_support; + data->msgq.mtx = NULL; if (!data->smp) { data->msgq.start = NULL; data->msgq.end = NULL; - data->msgq.mtx = erl_drv_mutex_create(""); - if (!data->msgq.mtx) { - driver_free(data); - return ERL_DRV_ERROR_GENERAL; + if (sys_info.thread_support) { + data->msgq.mtx = erl_drv_mutex_create(""); + if (!data->msgq.mtx) { + driver_free(data); + return ERL_DRV_ERROR_GENERAL; + } } } @@ -143,19 +146,22 @@ static void enqueue_reply(Otp9302AsyncData *adata) Otp9302MsgQ *msgq = adata->msgq; adata->next = NULL; adata->refc++; - erl_drv_mutex_lock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_lock(msgq->mtx); if (msgq->end) msgq->end->next = adata; else msgq->end = msgq->start = adata; msgq->end = adata; - erl_drv_mutex_unlock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_unlock(msgq->mtx); } static void dequeue_replies(Otp9302AsyncData *adata) { Otp9302MsgQ *msgq = adata->msgq; - erl_drv_mutex_lock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_lock(msgq->mtx); if (--adata->refc == 0) driver_free(adata); while (msgq->start) { @@ -166,7 +172,8 @@ static void dequeue_replies(Otp9302AsyncData *adata) driver_free(adata); } msgq->start = msgq->end = NULL; - erl_drv_mutex_unlock(msgq->mtx); + if (msgq->mtx) + erl_drv_mutex_unlock(msgq->mtx); } static void async_invoke(void *data) diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index bcc1f9e5af..a854d3f05b 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -850,6 +850,138 @@ resource_takeover(Config) when is_list(Config) -> ?line ok = forget_resource(AN4), ?line [] = nif_mod_call_history(), + + %% + %% Test rollback after failed upgrade of same lib-version + %% + + {A5,BinA5} = make_resource(2, Holder, "A5"), + {NA5,BinNA5} = make_resource(0, Holder, "NA5"), + {AN5,_BinAN5} = make_resource(1, Holder, "AN5"), + + {A6,BinA6} = make_resource(2, Holder, "A6"), + {NA6,BinNA6} = make_resource(0, Holder, "NA6"), + {AN6,_BinAN6} = make_resource(1, Holder, "AN6"), + + {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + undefined = nif_mod:lib_version(), + {error,{upgrade,_}} = + nif_mod:load_nif_lib(Config, 1, + [{resource_type, 4, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, + ?RT_CREATE}, + + {return, 1} % FAIL + ]), + + undefined = nif_mod:lib_version(), + [{upgrade,1,5,105}] = nif_mod_call_history(), + + %% Make sure dtor was not changed (from A to B) + ok = forget_resource(A5), + [{{resource_dtor_A_v1,BinA5},1,6,106}] = nif_mod_call_history(), + + %% Make sure dtor was not nullified (from A to null) + ok = forget_resource(NA5), + [{{resource_dtor_A_v1,BinNA5},1,7,107}] = nif_mod_call_history(), + + %% Make sure dtor was not added (from null to A) + ok = forget_resource(AN5), + [] = nif_mod_call_history(), + + %% + %% Test rollback after failed upgrade of other lib-version + %% + + {error,{upgrade,_}} = + nif_mod:load_nif_lib(Config, 2, + [{resource_type, 4, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, + ?RT_CREATE}, + + {return, 1} % FAIL + ]), + + undefined = nif_mod:lib_version(), + [{upgrade,2,_,_}] = nif_mod_call_history(), + + %% Make sure dtor was not changed (from A to B) + ok = forget_resource(A6), + [{{resource_dtor_A_v1,BinA6},1,_,_}] = nif_mod_call_history(), + + %% Make sure dtor was not nullified (from A to null) + ok = forget_resource(NA6), + [{{resource_dtor_A_v1,BinNA6},1,_,_}] = nif_mod_call_history(), + + %% Make sure dtor was not added (from null to A) + ok = forget_resource(AN6), + [] = nif_mod_call_history(), + + %% + %% Test rolback after failed initial load + %% + false = code:purge(nif_mod), + [{unload,1,_,_}] = nif_mod_call_history(), + true = code:delete(nif_mod), + false = code:purge(nif_mod), + [] = nif_mod_call_history(), + + + {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + undefined = nif_mod:lib_version(), + {error,{load,_}} = + nif_mod:load_nif_lib(Config, 1, + [{resource_type, null, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_A_null",resource_dtor_A, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, + ?RT_CREATE}, + + {return, 1} % FAIL + ]), + + undefined = nif_mod:lib_version(), + ok = nif_mod:load_nif_lib(Config, 1, + [{resource_type, null, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A", + resource_dtor_A, ?RT_CREATE}, + + {resource_type, 1, ?RT_CREATE, "resource_type_A_null", null, + ?RT_CREATE}, + {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, + ?RT_TAKEOVER}, + + {return, 0} % SUCCESS + ]), + + ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), + + {NA7,BinNA7} = make_resource(0, Holder, "NA7"), + {AN7,BinAN7} = make_resource(1, Holder, "AN7"), + + ok = forget_resource(NA7), + [{{resource_dtor_A_v1,BinNA7},1,_,_}] = nif_mod_call_history(), + + ok = forget_resource(AN7), + [] = nif_mod_call_history(), + ?line true = lists:member(?MODULE, erlang:system_info(taints)), ?line true = lists:member(nif_mod, erlang:system_info(taints)), ?line verify_tmpmem(TmpMem), @@ -1406,7 +1538,7 @@ dirty_nif(Config) when is_list(Config) -> {skipped,"No dirty scheduler support"} end. -next_msg(Pid) -> +next_msg(_Pid) -> receive M -> M after 100 -> diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index b550d1f16d..160f4843ad 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1477,7 +1477,6 @@ static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NI { int percent; char atom[10]; - int do_repeat; if (!enif_get_int(env, argv[0], &percent) || !enif_get_atom(env, argv[1], atom, sizeof(atom), ERL_NIF_LATIN1)) { diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index e32d10057c..55a0d2ac4f 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2010. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -41,6 +41,7 @@ static ERL_NIF_TERM am_null; static ERL_NIF_TERM am_resource_type; static ERL_NIF_TERM am_resource_dtor_A; static ERL_NIF_TERM am_resource_dtor_B; +static ERL_NIF_TERM am_return; static NifModPrivData* priv_data(ErlNifEnv* env) { @@ -54,6 +55,7 @@ static void init(ErlNifEnv* env) am_resource_type = enif_make_atom(env, "resource_type"); am_resource_dtor_A = enif_make_atom(env, "resource_dtor_A"); am_resource_dtor_B = enif_make_atom(env, "resource_dtor_B"); + am_return = enif_make_atom(env, "return"); } static void add_call_with_arg(ErlNifEnv* env, NifModPrivData* data, const char* func_name, @@ -105,19 +107,15 @@ static void resource_dtor_B(ErlNifEnv* env, void* a) } /* {resource_type, Ix|null, ErlNifResourceFlags in, "TypeName", dtor(A|B|null), ErlNifResourceFlags out}*/ -static void open_resource_type(ErlNifEnv* env, ERL_NIF_TERM op_tpl) +static void open_resource_type(ErlNifEnv* env, const ERL_NIF_TERM* arr) { NifModPrivData* data = priv_data(env); - const ERL_NIF_TERM* arr; - int arity; char rt_name[30]; union { ErlNifResourceFlags e; int i; } flags, exp_res, got_res; unsigned ix; ErlNifResourceDtor* dtor; ErlNifResourceType* got_ptr; - CHECK(enif_get_tuple(env, op_tpl, &arity, &arr)); - CHECK(arity == 6); CHECK(enif_is_identical(arr[0], am_resource_type)); CHECK(enif_get_int(env, arr[2], &flags.i)); CHECK(enif_get_string(env, arr[3], rt_name, sizeof(rt_name), ERL_NIF_LATIN1) > 0); @@ -147,18 +145,32 @@ static void open_resource_type(ErlNifEnv* env, ERL_NIF_TERM op_tpl) CHECK(got_res.e == exp_res.e); } -static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) +static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info, int* retvalp) { NifModPrivData* data = priv_data(env); ERL_NIF_TERM head, tail; unsigned ix; + for (ix=0; ix<RT_MAX; ix++) { data->rt_arr[ix] = NULL; } for (head = load_info; enif_get_list_cell(env, head, &head, &tail); head = tail) { - - open_resource_type(env, head); + const ERL_NIF_TERM* arr; + int arity; + + CHECK(enif_get_tuple(env, head, &arity, &arr)); + switch (arity) { + case 6: + open_resource_type(env, arr); + break; + case 2: + CHECK(arr[0] == am_return); + CHECK(enif_get_int(env, arr[1], retvalp)); + break; + default: + CHECK(0); + } } CHECK(enif_is_empty_list(env, head)); } @@ -166,6 +178,7 @@ static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) { NifModPrivData* data; + int retval = 0; init(env); data = (NifModPrivData*) enif_alloc(sizeof(NifModPrivData)); @@ -177,38 +190,41 @@ static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) add_call(env, data, "load"); - do_load_info(env, load_info); - data->calls = 0; - return 0; + do_load_info(env, load_info, &retval); + if (retval) + NifModPrivData_release(data); + return retval; } static int reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) { NifModPrivData* data = (NifModPrivData*) *priv; + int retval = 0; init(env); add_call(env, data, "reload"); - do_load_info(env, load_info); - return 0; + do_load_info(env, load_info, &retval); + return retval; } static int upgrade(ErlNifEnv* env, void** priv, void** old_priv_data, ERL_NIF_TERM load_info) { NifModPrivData* data = (NifModPrivData*) *old_priv_data; + int retval = 0; init(env); add_call(env, data, "upgrade"); data->ref_cnt++; *priv = *old_priv_data; - do_load_info(env, load_info); + do_load_info(env, load_info, &retval); - return 0; + return retval; } static void unload(ErlNifEnv* env, void* priv) { NifModPrivData* data = (NifModPrivData*) priv; - int is_last; + add_call(env, data, "unload"); NifModPrivData_release(data); } diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.h b/erts/emulator/test/nif_SUITE_data/nif_mod.h index cd0ecf4b54..fb14fee815 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.h +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.h @@ -14,7 +14,6 @@ typedef struct call_info_t typedef struct { ErlNifMutex* mtx; - int calls; int ref_cnt; CallInfo* call_history; ErlNifResourceType* rt_arr[RT_MAX]; @@ -28,6 +27,11 @@ typedef struct enif_mutex_unlock((NMPD)->mtx); \ if (is_last) { \ enif_mutex_destroy((NMPD)->mtx); \ + while ((NMPD)->call_history) { \ + CallInfo* next = (NMPD)->call_history->next; \ + enif_free((NMPD)->call_history); \ + (NMPD)->call_history = next; \ + } \ enif_free((NMPD)); \ } \ }while (0) diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 6a43e2b0e7..3906471f87 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -54,6 +54,7 @@ sbt_cmd/1, scheduler_threads/1, scheduler_suspend/1, + dirty_scheduler_threads/1, reader_groups/1]). -define(DEFAULT_TIMEOUT, ?t:minutes(15)). @@ -68,6 +69,7 @@ all() -> equal_and_high_with_part_time_max, equal_with_high, equal_with_high_max, bound_process, {group, scheduler_bind}, scheduler_threads, scheduler_suspend, + dirty_scheduler_threads, reader_groups]. groups() -> @@ -1092,6 +1094,55 @@ scheduler_threads(Config) when is_list(Config) -> end, ok. +dirty_scheduler_threads(Config) when is_list(Config) -> + SmpSupport = erlang:system_info(smp_support), + try + erlang:system_info(dirty_cpu_schedulers), + dirty_scheduler_threads_test(Config, SmpSupport) + catch + error:badarg -> + {skipped, "No dirty scheduler support"} + end. + +dirty_scheduler_threads_test(Config, SmpSupport) -> + {Sched, SchedOnln, _} = get_dsstate(Config, ""), + {HalfSched, HalfSchedOnln} = case SmpSupport of + false -> {1,1}; + true -> + {Sched div 2, + SchedOnln div 2} + end, + Cmd1 = "+SDcpu "++integer_to_list(HalfSched)++":"++ + integer_to_list(HalfSchedOnln), + {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, Cmd1), + {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, "+SDPcpu 50:50"), + IOSched = 20, + {_, _, IOSched} = get_dsstate(Config, "+SDio "++integer_to_list(IOSched)), + {ok, Node} = start_node(Config, ""), + [ok] = mcall(Node, [fun() -> dirty_schedulers_online_test() end]), + ok. + +dirty_schedulers_online_test() -> + dirty_schedulers_online_test(erlang:system_info(smp_support)). +dirty_schedulers_online_test(false) -> ok; +dirty_schedulers_online_test(true) -> + dirty_schedulers_online_smp_test(erlang:system_info(schedulers_online)). +dirty_schedulers_online_smp_test(SchedOnln) when SchedOnln < 4 -> ok; +dirty_schedulers_online_smp_test(SchedOnln) -> + DirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), + SchedOnln = DirtyCPUSchedOnln, + HalfSchedOnln = SchedOnln div 2, + SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln), + HalfDirtyCPUSchedOnln = DirtyCPUSchedOnln div 2, + HalfDirtyCPUSchedOnln = erlang:system_flag(schedulers_online, SchedOnln), + DirtyCPUSchedOnln = erlang:system_flag(dirty_cpu_schedulers_online, + HalfDirtyCPUSchedOnln), + HalfDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), + QrtrDirtyCPUSchedOnln = HalfDirtyCPUSchedOnln div 2, + SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln), + QrtrDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online), + ok. + get_sstate(Config, Cmd) -> {ok, Node} = start_node(Config, Cmd), [SState] = mcall(Node, [fun () -> @@ -1100,6 +1151,20 @@ get_sstate(Config, Cmd) -> stop_node(Node), SState. +get_dsstate(Config, Cmd) -> + {ok, Node} = start_node(Config, Cmd), + [DSCPU] = mcall(Node, [fun () -> + erlang:system_info(dirty_cpu_schedulers) + end]), + [DSCPUOnln] = mcall(Node, [fun () -> + erlang:system_info(dirty_cpu_schedulers_online) + end]), + [DSIO] = mcall(Node, [fun () -> + erlang:system_info(dirty_io_schedulers) + end]), + stop_node(Node), + {DSCPU, DSCPUOnln, DSIO}. + scheduler_suspend(Config) when is_list(Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), ?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end, @@ -1171,16 +1236,40 @@ sst2_loop(N) -> erlang:system_flag(multi_scheduling, unblock), sst2_loop(N-1). -sst3_loop(_S, 0) -> - ok; sst3_loop(S, N) -> + try erlang:system_info(dirty_cpu_schedulers) of + DS -> + sst3_loop_with_dirty_schedulers(S, DS, N) + catch + error:badarg -> + sst3_loop_normal_schedulers_only(S, N) + end. + +sst3_loop_normal_schedulers_only(_S, 0) -> + ok; +sst3_loop_normal_schedulers_only(S, N) -> + erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(schedulers_online, 1), + erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(schedulers_online, S), + erlang:system_flag(schedulers_online, 1), + erlang:system_flag(schedulers_online, S), + sst3_loop_normal_schedulers_only(S, N-1). + +sst3_loop_with_dirty_schedulers(_S, _DS, 0) -> + ok; +sst3_loop_with_dirty_schedulers(S, DS, N) -> erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(dirty_cpu_schedulers_online, (DS div 2)+1), erlang:system_flag(schedulers_online, 1), erlang:system_flag(schedulers_online, (S div 2)+1), + erlang:system_flag(dirty_cpu_schedulers_online, 1), erlang:system_flag(schedulers_online, S), + erlang:system_flag(dirty_cpu_schedulers_online, DS), erlang:system_flag(schedulers_online, 1), erlang:system_flag(schedulers_online, S), - sst3_loop(S, N-1). + erlang:system_flag(dirty_cpu_schedulers_online, DS), + sst3_loop_with_dirty_schedulers(S, DS, N-1). reader_groups(Config) when is_list(Config) -> %% White box testing. These results are correct, but other results diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version index 02b68f2b39..0ba1c77930 100755 --- a/erts/emulator/utils/make_version +++ b/erts/emulator/utils/make_version @@ -39,10 +39,10 @@ if ($ARGV[0] eq '-o') { } my $release = shift; -defined $release or die "No release specified"; +defined $release or die "No otp release specified"; -my $correction_package = shift; -defined $correction_package or die "No correction package specified"; +my $otp_version = shift; +defined $otp_version or die "No otp version specified"; my $version = shift; defined $version or die "No version name specified"; @@ -56,7 +56,7 @@ open(FILE, ">$outputfile") or die "Can't create $outputfile: $!"; print FILE <<EOF; /* This file was created by 'make_version' -- don't modify. */ #define ERLANG_OTP_RELEASE "$release" -#define ERLANG_OTP_CORRECTION_PACKAGE "$correction_package" +#define ERLANG_OTP_VERSION "$otp_version" #define ERLANG_VERSION "$version" #define ERLANG_COMPILE_DATE "$time_str" #define ERLANG_ARCHITECTURE "$architecture" diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 5c1ce51644..83560735bc 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2012. All Rights Reserved. +# Copyright Ericsson AB 1996-2014. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -488,7 +488,7 @@ ifneq ($(INSTALL_SRC),) $(INSTALL_DATA) $(INSTALL_SRC) "$(RELEASE_PATH)/erts-$(VSN)/src" endif ifneq ($(INSTALL_EMBEDDED_DATA),) - $(INSTALL_DATA) $(INSTALL_EMBEDDED_DATA) "$(RELEASE_PATH)/erts-$(VSN)/bin" + $(INSTALL_SCRIPT) $(INSTALL_EMBEDDED_DATA) "$(RELEASE_PATH)/erts-$(VSN)/bin" endif ifneq ($(INSTALL_LIBS),) $(INSTALL_DATA) $(INSTALL_LIBS) "$(RELEASE_PATH)/erts-$(VSN)/bin" diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 2cf7280ebc..709c6f02d1 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -833,9 +833,13 @@ int main(int argc, char **argv) #ifdef ERTS_DIRTY_SCHEDULERS else if (argv[i][2] == 'D') { char* type = argv[i]+3; - if (strcmp(type, "cpu") != 0 && - strcmp(type, "Pcpu") != 0 && - strcmp(type, "io") != 0) + if (strncmp(type, "cpu", 3) != 0 && + strncmp(type, "Pcpu", 4) != 0 && + strncmp(type, "io", 2) != 0) + usage(argv[i]); + if ((argv[i][3] == 'c' && argv[i][6] != '\0') || + (argv[i][3] == 'P' && argv[i][7] != '\0') || + (argv[i][3] == 'i' && argv[i][5] != '\0')) goto the_default; } #endif diff --git a/erts/etc/win32/nsis/erlang.nsi b/erts/etc/win32/nsis/erlang.nsi index f4fd2b4cdb..162e634148 100644 --- a/erts/etc/win32/nsis/erlang.nsi +++ b/erts/etc/win32/nsis/erlang.nsi @@ -93,6 +93,7 @@ SectionIn 1 RO skip_silent_mode: SetOutPath "$INSTDIR" + File "${TESTROOT}\OTP_VERSION" File "${TESTROOT}\Install.ini" File "${TESTROOT}\Install.exe" File /r "${TESTROOT}\releases" diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi index 3333c4a9aa..3ee33e8121 100644 --- a/erts/etc/win32/nsis/erlang20.nsi +++ b/erts/etc/win32/nsis/erlang20.nsi @@ -144,6 +144,7 @@ Section "Development" SecErlangDev SectionIn 1 RO
SetOutPath "$INSTDIR"
+ File "${TESTROOT}\OTP_VERSION"
File "${TESTROOT}\Install.ini"
File "${TESTROOT}\Install.exe"
SetOutPath "$INSTDIR\releases"
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 38b8e9e9b6..3a676e2a36 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -60,7 +60,7 @@ #endif /* Assume 64-byte cache line size */ -#define ETHR_CACHE_LINE_SIZE 64 +#define ETHR_CACHE_LINE_SIZE ASSUMED_CACHE_LINE_SIZE #define ETHR_CACHE_LINE_MASK (ETHR_CACHE_LINE_SIZE - 1) #define ETHR_CACHE_LINE_ALIGN_SIZE(SZ) \ diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index dd3599f86d..b36322490a 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -235,3 +235,5 @@ /* Define if you want to turn on extra sanity checking in the ethread library */ #undef ETHR_XCHK +/* Assumed cache-line size (in bytes) */ +#undef ASSUMED_CACHE_LINE_SIZE diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile index 32e65a227f..14d5d46195 100644 --- a/erts/start_scripts/Makefile +++ b/erts/start_scripts/Makefile @@ -52,7 +52,7 @@ endif ############################################################################## # Get version numbers from the VSN files -# VSN & SYSTEM_VSN +# VSN include ../vsn.mk include $(LIBPATH)/kernel/vsn.mk include $(LIBPATH)/stdlib/vsn.mk diff --git a/erts/test/Makefile b/erts/test/Makefile index 74a5bb1ccc..6fbc19fcae 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2014. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -36,7 +36,8 @@ MODULES= \ erl_print_SUITE \ run_erl_SUITE \ erlexec_SUITE \ - z_SUITE + z_SUITE \ + upgrade_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl new file mode 100644 index 0000000000..ee84a5dfd7 --- /dev/null +++ b/erts/test/upgrade_SUITE.erl @@ -0,0 +1,447 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +-module(upgrade_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(upgr_sname,otp_upgrade). + +%% Applications that are excluded from this test because they can not +%% just be started in a new node with out specific configuration. +-define(start_exclude, + [cosEvent,cosEventDomain,cosFileTransfer,cosNotification, + cosProperty,cosTime,cosTransactions,erts,ic,netconf,orber, + safe]). + +%% Applications that are excluded from this test because their appup +%% file don't support the upgrade. +%% In specific: +%% - hipe does not support any upgrade at all +%% - dialyzer requires hipe (in the .app file) +%% - typer requires hipe (in the .app file) +-define(appup_exclude, + [dialyzer,hipe,typer]). + +init_per_suite(Config) -> + %% Check that a real release is running, not e.g. cerl + ok = application:ensure_started(sasl), + case release_handler:which_releases() of + [{_,_,[],_}] -> + %% Fake release, no applications + {skip, "Need a real release running to create other releases"}; + _ -> + rm_rf(filename:join([?config(data_dir,Config),priv_dir])), + Config + end. + +init_per_testcase(Case,Config) -> + PrivDir = filename:join([?config(data_dir,Config),priv_dir,Case]), + CreateDir = filename:join([PrivDir,create]), + InstallDir = filename:join([PrivDir,install]), + ok = filelib:ensure_dir(filename:join(CreateDir,"*")), + ok = filelib:ensure_dir(filename:join(InstallDir,"*")), + Config1 = lists:keyreplace(priv_dir,1,Config,{priv_dir,PrivDir}), + [{create_dir,CreateDir},{install_dir,InstallDir}|Config1]. + +end_per_testcase(_Case,Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes], + case ?config(tc_status,Config) of + ok -> + %% Note that priv_dir here is per test case! + rm_rf(?config(priv_dir,Config)); + _fail -> + %% Test case data can be found under DataDir/priv_dir/Case + ok + end, + ok. + +all() -> + [minor,major]. + +%% If this is major release X, then this test performs an upgrade from +%% major release X-1 to the current release. +major(Config) -> + Current = erlang:system_info(otp_release), + PreviousMajor = previous_major(Current), + upgrade_test(PreviousMajor,Current,Config). + +%% If this is a patched version of major release X, then this test +%% performs an upgrade from major release X to the current release. +minor(Config) -> + CurrentMajor = erlang:system_info(otp_release), + Current = CurrentMajor++"_patched", + upgrade_test(CurrentMajor,Current,Config). + +%%%----------------------------------------------------------------- +upgrade_test(FromVsn,ToVsn,Config) -> + OldRel = + case test_server:is_release_available(FromVsn) of + true -> + {release,FromVsn}; + false -> + case ct:get_config({otp_releases,list_to_atom(FromVsn)}) of + undefined -> + false; + Prog0 -> + case os:find_executable(Prog0) of + false -> + false; + Prog -> + {prog,Prog} + end + end + end, + case OldRel of + false -> + %% Note that priv_dir here is per test case! + rm_rf(?config(priv_dir,Config)), + {skip, "no previous release available"}; + _ -> + upgrade_test1(FromVsn,ToVsn,[{old_rel,OldRel}|Config]) + end. + +upgrade_test1(FromVsn,ToVsn,Config) -> + CreateDir = ?config(create_dir,Config), + InstallDir = ?config(install_dir,Config), + FromRelName = "otp-"++FromVsn, + ToRelName = "otp-"++ToVsn, + + {FromRel,FromApps} = target_system(FromRelName, FromVsn, + CreateDir, InstallDir,Config), + {ToRel,ToApps} = upgrade_system(FromRel, ToRelName, ToVsn, + CreateDir, InstallDir), + do_upgrade(FromVsn, FromApps, ToRel, ToApps, InstallDir). + +%%%----------------------------------------------------------------- +%%% This is similar to sasl/examples/src/target_system.erl, but with +%%% the following adjustments: +%%% - add a log directory +%%% - use an own 'start' script +%%% - chmod 'start' and 'start_erl' +target_system(RelName0,RelVsn,CreateDir,InstallDir,Config) -> + {ok,Node} = test_server:start_node(list_to_atom(RelName0),peer, + [{erl,[?config(old_rel,Config)]}]), + + {RelName,Apps,ErtsVsn} = create_relfile(Node,CreateDir,RelName0,RelVsn), + + %% Create .script and .boot + ok = rpc:call(Node,systools,make_script,[RelName]), + + %% Create base tar file - i.e. erts and all apps + ok = rpc:call(Node,systools,make_tar, + [RelName,[{erts,rpc:call(Node,code,root_dir,[])}]]), + + %% Unpack the tar to complete the installation + erl_tar:extract(RelName ++ ".tar.gz", [{cwd, InstallDir}, compressed]), + + %% Add bin and log dirs + BinDir = filename:join([InstallDir, "bin"]), + file:make_dir(BinDir), + file:make_dir(filename:join(InstallDir,"log")), + + %% Delete start scripts - they will be added later + ErtsBinDir = filename:join([InstallDir, "erts-" ++ ErtsVsn, "bin"]), + file:delete(filename:join([ErtsBinDir, "erl"])), + file:delete(filename:join([ErtsBinDir, "start"])), + file:delete(filename:join([ErtsBinDir, "start_erl"])), + + %% Copy .boot to bin/start.boot + copy_file(RelName++".boot",filename:join([BinDir, "start.boot"])), + + %% Copy scripts from erts-xxx/bin to bin + copy_file(filename:join([ErtsBinDir, "epmd"]), + filename:join([BinDir, "epmd"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), + filename:join([BinDir, "run_erl"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), + filename:join([BinDir, "to_erl"]), [preserve]), + + %% create start_erl.data and sys.config + StartErlData = filename:join([InstallDir, "releases", "start_erl.data"]), + write_file(StartErlData, io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn])), + SysConfig = filename:join([InstallDir, "releases", RelVsn, "sys.config"]), + write_file(SysConfig, "[]."), + + %% Insert 'start' script from data_dir - modified to add sname and heart + copy_file(filename:join(?config(data_dir,Config),"start.src"), + filename:join(ErtsBinDir,"start.src")), + ok = file:change_mode(filename:join(ErtsBinDir,"start.src"),8#0755), + + %% Make start_erl executable + %% (this has been fixed in OTP 17 - is is now installed with + %% $INSTALL_SCRIPT instead of $INSTALL_DATA and should therefore + %% be executable from the start) + ok = file:change_mode(filename:join(ErtsBinDir,"start_erl.src"),8#0755), + + %% Substitute variables in erl.src, start.src and start_erl.src + %% (.src found in erts-xxx/bin - result stored in bin) + subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir, + [{"FINAL_ROOTDIR", InstallDir}, {"EMU", "beam"}], + [preserve]), + + %% Create RELEASES + RelFile = filename:join([InstallDir, "releases", + filename:basename(RelName) ++ ".rel"]), + release_handler:create_RELEASES(InstallDir, RelFile), + + true = test_server:stop_node(Node), + + {RelName,Apps}. + +%%%----------------------------------------------------------------- +%%% Create a release containing the current (the test node) OTP +%%% release, including relup to allow upgrade from an earlier OTP +%%% release. +upgrade_system(FromRel, ToRelName0, ToVsn, + CreateDir, InstallDir) -> + + {RelName,Apps,_} = create_relfile(node(),CreateDir,ToRelName0,ToVsn), + FromPath = filename:join([InstallDir,lib,"*",ebin]), + + ok = systools:make_script(RelName), + ok = systools:make_relup(RelName,[FromRel],[FromRel], + [{path,[FromPath]}, + {outdir,CreateDir}]), + SysConfig = filename:join([CreateDir, "sys.config"]), + write_file(SysConfig, "[]."), + + ok = systools:make_tar(RelName,[{erts,code:root_dir()}]), + + {RelName,Apps}. + +%%%----------------------------------------------------------------- +%%% Start a new node running the release from target_system/5 +%%% above. Then upgrade to the system from upgrade_system/5. +do_upgrade(FromVsn,FromApps,ToRel,ToApps,InstallDir) -> + Start = filename:join([InstallDir,bin,start]), + {ok,Node} = start_node(Start,permanent,FromVsn,FromApps), + + [{"OTP upgrade test",FromVsn,_,permanent}] = + rpc:call(Node,release_handler,which_releases,[]), + {ok,ToVsn} = rpc:call(Node,release_handler,unpack_release,[ToRel]), + [{"OTP upgrade test",ToVsn,_,unpacked}, + {"OTP upgrade test",FromVsn,_,permanent}] = + rpc:call(Node,release_handler,which_releases,[]), + case rpc:call(Node,release_handler,install_release,[ToVsn]) of + {ok,FromVsn,_} -> + ok; + {continue_after_restart,FromVsn,_} -> + wait_node_up(current,ToVsn,ToApps) + end, + [{"OTP upgrade test",ToVsn,_,current}, + {"OTP upgrade test",FromVsn,_,permanent}] = + rpc:call(Node,release_handler,which_releases,[]), + ok = rpc:call(Node,release_handler,make_permanent,[ToVsn]), + [{"OTP upgrade test",ToVsn,_,permanent}, + {"OTP upgrade test",FromVsn,_,old}] = + rpc:call(Node,release_handler,which_releases,[]), + + erlang:monitor_node(Node,true), + _ = rpc:call(Node,init,stop,[]), + receive {nodedown,Node} -> ok end, + + ok. + +%%%----------------------------------------------------------------- +%%% Library functions +previous_major("17") -> + "r16"; +previous_major(Rel) -> + integer_to_list(list_to_integer(Rel)-1). + +create_relfile(Node,CreateDir,RelName0,RelVsn) -> + LibDir = rpc:call(Node,code,lib_dir,[]), + SplitLibDir = filename:split(LibDir), + Paths = rpc:call(Node,code,get_path,[]), + Exclude = ?start_exclude ++ ?appup_exclude, + Apps = lists:flatmap( + fun(Path) -> + case lists:prefix(LibDir,Path) of + true -> + case filename:split(Path) -- SplitLibDir of + [AppVsn,"ebin"] -> + case string:tokens(AppVsn,"-") of + [AppStr,Vsn] -> + App = list_to_atom(AppStr), + case lists:member(App,Exclude) of + true -> + []; + false -> + [{App,Vsn,restart_type(App)}] + end; + _ -> + [] + end; + _ -> + [] + end; + false -> + [] + end + end, + Paths), + + ErtsVsn = rpc:call(Node, erlang, system_info, [version]), + + %% Create the .rel file + RelContent = {release, {"OTP upgrade test", RelVsn}, {erts, ErtsVsn}, Apps}, + RelName = filename:join(CreateDir,RelName0), + RelFile = RelName++".rel", + {ok,Fd} = file:open(RelFile,[write,{encoding,utf8}]), + io:format(Fd,"~tp.~n",[RelContent]), + ok = file:close(Fd), + {RelName,Apps,ErtsVsn}. + +restart_type(App) when App==kernel; App==stdlib; App==sasl -> + permanent; +restart_type(_) -> + temporary. + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). + +copy_file(Src, Dest, Opts) -> + {ok,_} = file:copy(Src, Dest), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + + +write_file(FName, Conts) -> + Enc = file:native_name_encoding(), + {ok, Fd} = file:open(FName, [write]), + file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)), + file:close(Fd). + + +subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> + lists:foreach(fun(Script) -> + subst_src_script(Script, SrcDir, DestDir, + Vars, Opts) + end, Scripts). + +subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> + subst_file(filename:join([SrcDir, Script ++ ".src"]), + filename:join([DestDir, Script]), + Vars, Opts). + +subst_file(Src, Dest, Vars, Opts) -> + {ok, Bin} = file:read_file(Src), + Conts = binary_to_list(Bin), + NConts = subst(Conts, Vars), + write_file(Dest, NConts), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +%% subst(Str, Vars) +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Str, using the list +%% of variables in Vars. +%% +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + + +%%%----------------------------------------------------------------- +%%% +start_node(Start,ExpStatus,ExpVsn,ExpApps) -> + case open_port({spawn_executable, Start}, []) of + Port when is_port(Port) -> + unlink(Port), + erlang:port_close(Port), + wait_node_up(ExpStatus,ExpVsn,ExpApps); + Error -> + Error + end. + +wait_node_up(ExpStatus,ExpVsn,ExpApps0) -> + ExpApps = [{A,V} || {A,V,_T} <- ExpApps0], + Node = node_name(?upgr_sname), + wait_node_up(Node,ExpStatus,ExpVsn,lists:keysort(1,ExpApps),60). + +wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,0) -> + ct:fail({app_check_failed,ExpVsn,ExpApps, + rpc:call(Node,release_handler,which_releases,[ExpStatus]), + rpc:call(Node,application,which_applications,[])}); +wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,N) -> + timer:sleep(2000), + case {rpc:call(Node,release_handler,which_releases,[ExpStatus]), + rpc:call(Node, application, which_applications, [])} of + {[{_,ExpVsn,_,_}],Apps} when is_list(Apps) -> + case [{A,V} || {A,_,V} <- lists:keysort(1,Apps)] of + ExpApps -> {ok,Node}; + _ -> wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,N-1) + end; + _ -> + wait_node_up(Node,ExpStatus,ExpVsn,ExpApps,N-1) + end. + +node_name(Sname) -> + {ok,Host} = inet:gethostname(), + list_to_atom(atom_to_list(Sname) ++ "@" ++ Host). + +rm_rf(Dir) -> + case file:read_file_info(Dir) of + {ok, #file_info{type = directory}} -> + {ok, Content} = file:list_dir_all(Dir), + [rm_rf(filename:join(Dir,C)) || C <- Content], + ok=file:del_dir(Dir), + ok; + {ok, #file_info{}} -> + ok=file:delete(Dir); + _ -> + ok + end. diff --git a/erts/test/upgrade_SUITE_data/start.src b/erts/test/upgrade_SUITE_data/start.src new file mode 100644 index 0000000000..70d1a322c9 --- /dev/null +++ b/erts/test/upgrade_SUITE_data/start.src @@ -0,0 +1,36 @@ +#!/bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2014. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# +ROOTDIR=%FINAL_ROOTDIR% + +if [ -z "$RELDIR" ] +then + RELDIR=$ROOTDIR/releases +fi + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -sname otp_upgrade -heart" diff --git a/erts/vsn.mk b/erts/vsn.mk index 88a393f3d5..081fb66398 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -19,11 +19,6 @@ VSN = 6.0 -# OTP major version -SYSTEM_VSN = 17 -# OTP correction package version -SYSTEM_CP_VSN = 17.0-rc1 - # Port number 4365 in 4.2 # Port number 4366 in 4.3 # Port number 4368 in 4.4.0 - 4.6.2 diff --git a/lib/Makefile b/lib/Makefile index 6301c882b2..4b979fc28a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -25,10 +25,11 @@ ERTS_APPLICATIONS = stdlib sasl kernel compiler # Then these have to be build ERLANG_APPLICATIONS = tools test_server common_test runtime_tools \ - inets xmerl edoc erl_docgen parsetools + inets parsetools # These are only build if -a is given to otp_build or make is used directly -ALL_ERLANG_APPLICATIONS = snmp otp_mibs erl_interface asn1 jinterface \ +ALL_ERLANG_APPLICATIONS = xmerl edoc erl_docgen snmp otp_mibs erl_interface \ + asn1 jinterface \ wx debugger reltool gs \ ic mnesia crypto orber os_mon syntax_tools \ public_key ssl observer odbc diameter \ @@ -59,10 +60,14 @@ else else ifdef TERTIARY_BOOTSTRAP SUB_DIRECTORIES = snmp sasl jinterface ic syntax_tools wx - else # Not bootstrap build - SUB_DIRECTORIES = $(ERTS_APPLICATIONS) \ - $(ERLANG_APPLICATIONS) \ - $(EXTRA_APPLICATIONS) + else + ifdef DOC_BOOTSTRAP + SUB_DIRECTORIES = xmerl edoc erl_docgen + else # Not bootstrap build + SUB_DIRECTORIES = $(ERTS_APPLICATIONS) \ + $(ERLANG_APPLICATIONS) \ + $(EXTRA_APPLICATIONS) + endif endif endif endif diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml index eb9f000e75..ee54fdffd7 100644 --- a/lib/asn1/doc/src/asn1_ug.xml +++ b/lib/asn1/doc/src/asn1_ug.xml @@ -205,16 +205,13 @@ ok is saved in the <c>People.asn1db</c> file, the generated Erlang code is compiled using the Erlang compiler and loaded into the Erlang runtime system. Now there is a user interface - of encode/2 and decode/2 in the module People, which is invoked by: + for <c>encode/2</c> and <c>decode/2</c> in the module People, + which is invoked by: <br></br> <c><![CDATA['People':encode(<Type name>,<Value>),]]></c> <br></br> or <br></br> -<c><![CDATA['People':decode(<Type name>,<Value>),]]></c> <br></br> - - Alternatively one can use the <c><![CDATA[asn1rt:encode(<Module name> ,<Type name>,<Value>)]]></c> and <c><![CDATA[asn1rt:decode(< Module name>,<Type name>,<Value>)]]></c> calls. - However, they are not as efficient as the previous methods since they - result in an additional <c>apply/3</c> call.</p> +<c><![CDATA['People':decode(<Type name>,<Value>),]]></c></p> <p>Assume there is a network application which receives instances of the ASN.1 defined type Person, modifies and sends them back again:</p> @@ -241,16 +238,14 @@ receive encoding-rules. <br></br> The encoder and the decoder can also be run from - the shell. The following dialogue with the shell illustrates - how the functions - <c>asn1rt:encode/3</c> and <c>asn1rt:decode/3</c> are used.</p> + the shell.</p> <pre> 2> <input>Rockstar = {'Person',"Some Name",roving,50}.</input> {'Person',"Some Name",roving,50} -3> <input>{ok,Bin} = asn1rt:encode('People','Person',Rockstar).</input> +3> <input>{ok,Bin} = 'People':encode('Person',Rockstar).</input> {ok,<<243,17,19,9,83,111,109,101,32,78,97,109,101,2,1,2, 2,1,50>>} -4> <input>{ok,Person} = asn1rt:decode('People','Person',Bin).</input> +4> <input>{ok,Person} = 'People':decode('Person',Bin).</input> {ok,{'Person',"Some Name",roving,50}} 5> </pre> </section> @@ -279,11 +274,8 @@ The encoder and the decoder can also be run from (including the compiler).</p> </item> <item> - <p>The module <c>asn1rt</c> which provides the run-time functions. - However, it is preferable to use the generated <c>encode/2</c> and - <c>decode/2</c> functions in each module, ie. - Module:encode(Type,Value), in favor of the <c>asn1rt</c> - interface.</p> + <p>The module <c>asn1rt_nif</c> which provides the run-time functions + for the ASN.1 decoder for the BER back-end.</p> </item> </list> <p>The reason for the division of the interface into compile-time @@ -384,25 +376,9 @@ asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre> <section> <title>Run-time Functions</title> - <p>A brief description of the major functions is given here. For a - complete description of each function see - <seealso marker="asn1rt"> the Asn1 Reference Manual</seealso>, the <c>asn1rt</c> module.</p> - <p>The generic run-time encode and decode functions can be invoked as below:</p> - <pre> -asn1rt:encode('H323-MESSAGES','SomeChoiceType',{call,"octetstring"}). -asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre> - <p>Or, preferable like:</p> - <pre> -'H323-MESSAGES':encode('SomeChoiceType',{call,"octetstring"}). -'H323-MESSAGES':decode('SomeChoiceType',Bytes). </pre> - <p>The asn1 nif is enabled in two occasions: encoding of - asn1 values when the asn1 spec is compiled with <c>per</c> and - or decode of encoded asn1 values when the asn1 spec is - compiled with <c>ber</c>. In - those cases the nif will be loaded automatically at the first call - to <c>encode</c>/<c>decode</c>. If one doesn't want the performance - overhead of the nif being loaded at the first call it is possible - to load the nif separately by loading the <c>asn1rt_nif</c> module.</p> + <p>When an ASN.1 specification is compiled with the <c>ber</c> + option, the module <c>asn1rt_nif</c> module and the NIF library in + <c>asn1/priv_dir</c> will be needed at run-time.</p> <p>By invoking the function <c>info/0</c> in a generated module, one gets information about which compiler options were used.</p> </section> @@ -414,8 +390,8 @@ asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre> a line number indicating where in the source file the error was detected. If no errors are found, an Erlang ASN.1 module will be created as default.</p> - <p>The run-time encoders and decoders (in the <c>asn1rt</c> module) do - execute within a catch and returns <c>{ok, Data}</c> or + <p>The run-time encoders and decoders execute within a catch and + returns <c>{ok, Data}</c> or <c>{error, {asn1, Description}}</c> where <c>Description</c> is an Erlang term describing the error. </p> diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml index 5871c8ad68..4d5a1a402a 100644 --- a/lib/asn1/doc/src/asn1ct.xml +++ b/lib/asn1/doc/src/asn1ct.xml @@ -61,7 +61,7 @@ and <c>uper_bin</c> options will still work, but will print a warning. </p> <p>Another change in R16 is that the generated <c>encode/2</c> - function (and <c>asn1rt:encode/3</c>) always returns a binary. + function always returns a binary. The <c>encode/2</c> function for the BER back-end used to return an iolist.</p> </note> @@ -326,6 +326,8 @@ File3.asn </pre> not always checked. Returns <c>{ok, Bytes}</c> if successful or <c>{error, Reason}</c> if an error occurred. </p> + <p>This function is deprecated. + Use <c>Module:encode(Type, Value)</c> instead.</p> </desc> </func> <func> @@ -339,6 +341,8 @@ File3.asn </pre> <desc> <p>Decodes <c>Type</c> from <c>Module</c> from the binary <c>Bytes</c>. Returns <c>{ok, Value}</c> if successful.</p> + <p>This function is deprecated. + Use <c>Module:decode(Type, Bytes)</c> instead.</p> </desc> </func> <func> diff --git a/lib/asn1/doc/src/asn1rt.xml b/lib/asn1/doc/src/asn1rt.xml index 6e22e45d93..3cf56b01ca 100644 --- a/lib/asn1/doc/src/asn1rt.xml +++ b/lib/asn1/doc/src/asn1rt.xml @@ -34,9 +34,12 @@ <module>asn1rt</module> <modulesummary>ASN.1 runtime support functions</modulesummary> <description> - <p>This module is the interface module for the ASN.1 runtime support functions. - To encode and decode ASN.1 types in runtime the functions in this module - should be used.</p> + <warning> + <p> + All functions in this module are deprecated and will be + removed in a future release. + </p> + </warning> </description> <funcs> @@ -52,6 +55,7 @@ <desc> <p>Decodes <c>Type</c> from <c>Module</c> from the binary <c>Bytes</c>. Returns <c>{ok,Value}</c> if successful.</p> + <p>Use <c>Module:decode(Type, Bytes)</c> instead of this function.</p> </desc> </func> @@ -65,16 +69,13 @@ <v>Reason = term()</v> </type> <desc> - <p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1 module - <c>Module</c>. Returns a possibly nested list of bytes and or binaries - if successful. To get as fast execution as possible the - encode function only performs rudimentary tests that the input - <c>Value</c> - is a correct instance of <c>Type</c>. The length of strings is for example - not always checked. </p> - <note> - <p>Starting in R16, <c>Bytes</c> is always a binary.</p> - </note> + <p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1 + module <c>Module</c>. Returns a binary if successful. To get + as fast execution as possible the encode function only + performs rudimentary tests that the input <c>Value</c> is a + correct instance of <c>Type</c>. The length of strings is, for + example, not always checked. </p> + <p>Use <c>Module:encode(Type, Value)</c> instead of this function.</p> </desc> </func> @@ -90,6 +91,7 @@ <p><c>info/1</c> returns the version of the asn1 compiler that was used to compile the module. It also returns the compiler options that was used.</p> + <p>Use <c>Module:info()</c> instead of this function.</p> </desc> </func> @@ -106,6 +108,7 @@ to a list of integers, where each integer represents one character as its unicode value. The function fails if the binary is not a properly encoded UTF8 string.</p> + <p>Use <seealso marker="stdlib:unicode#characters_to_list-1">unicode:characters_to_list/1</seealso> instead of this function.</p> </desc> </func> @@ -121,6 +124,7 @@ <p><c>utf8_list_to_binary/1</c> Transforms a list of integers, where each integer represents one character as its unicode value, to a UTF8 encoded binary.</p> + <p>Use <seealso marker="stdlib:unicode#characters_to_binary-1">unicode:characters_to_binary/1</seealso> instead of this function.</p> </desc> </func> diff --git a/lib/asn1/src/asn1.appup.src b/lib/asn1/src/asn1.appup.src index 2d11eddfbf..e4b3508cc4 100644 --- a/lib/asn1/src/asn1.appup.src +++ b/lib/asn1/src/asn1.appup.src @@ -1,11 +1,21 @@ +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% {"%VSN%", -% This version does not change anything of the runtime modules -% Only changes in compile time modules and thus no need for upgrade on target -[ - ], - [ - ]}. - - - - + [{<<".*">>,[{restart_application, asn1}]}], + [{<<".*">>,[{restart_application, asn1}]}] +}. diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 531a4935fe..9ec43197bf 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -19,6 +19,10 @@ %% %% -module(asn1ct). +-deprecated([decode/3,encode/3]). +-compile([{nowarn_deprecated_function,{asn1rt,decode,3}}, + {nowarn_deprecated_function,{asn1rt,encode,2}}, + {nowarn_deprecated_function,{asn1rt,encode,3}}]). %% Compile Time functions for ASN.1 (e.g ASN.1 compiler). @@ -1040,7 +1044,7 @@ get_file_list1(Stream,Dir,Includes,Acc) -> Ret = io:get_line(Stream,''), case Ret of eof -> - file:close(Stream), + ok = file:close(Stream), lists:reverse(Acc); FileName -> SuffixedNameList = @@ -1926,8 +1930,9 @@ read_config_file(ModuleName) -> Includes = [I || {i,I} <- Options], read_config_file1(ModuleName,Includes); {error,Reason} -> - file:format_error(Reason), - throw({error,{"error reading asn1 config file",Reason}}) + Error = "error reading asn1 config file: " ++ + file:format_error(Reason), + throw({error,Error}) end. read_config_file1(ModuleName,[]) -> case filename:extension(ModuleName) of @@ -1945,8 +1950,9 @@ read_config_file1(ModuleName,[H|T]) -> {error,enoent} -> read_config_file1(ModuleName,T); {error,Reason} -> - file:format_error(Reason), - throw({error,{"error reading asn1 config file",Reason}}) + Error = "error reading asn1 config file: " ++ + file:format_error(Reason), + throw({error,Error}) end. get_config_info(CfgList,InfoType) -> diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index df034c96ad..b9f2cb876a 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -2464,7 +2464,7 @@ normalize_value(S0, Type, {'DEFAULT',Value}, NameList) -> {'BIT STRING',CType,_} -> normalize_bitstring(S,Value,CType); {'OCTET STRING',CType,_} -> - normalize_octetstring(S,Value,CType); + normalize_octetstring(S0, Value, CType); {'NULL',_CType,_} -> %%normalize_null(Value); 'NULL'; @@ -2612,20 +2612,9 @@ normalize_octetstring(S,Value,CType) -> fun normalize_octetstring/3,[]); {Name,String} when is_atom(Name) -> normalize_octetstring(S,String,CType); - List when is_list(List) -> - %% check if list elements are valid octet values - lists:map(fun([])-> ok; - (H)when H > 255-> - asn1ct:warning("not legal octet value ~p in OCTET STRING, ~p~n", - [H,List],S, - "not legal octet value ~p in OCTET STRING"); - (_)-> ok - end, List), - List; - Other -> - asn1ct:warning("unknown default value ~p~n",[Other],S, - "unknown default value"), - Value + _ -> + Item = S#state.value, + throw(asn1_error(S, Item, illegal_octet_string_value)) end. normalize_objectidentifier(S, Value) -> @@ -3091,7 +3080,6 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> Ct=maybe_illicit_implicit_tag(open_type,Tag), TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct}; 'INTEGER' -> - check_integer(S,[],Constr), TempNewDef#newt{tag= merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))}; @@ -3938,9 +3926,9 @@ check_constraint(S,{simpletable,Type}) -> #'Externaltypereference'{} -> ERef = check_externaltypereference(S,C), {simpletable,ERef#'Externaltypereference'.type}; - #type{def=#'Externaltypereference'{type=T}} -> - check_externaltypereference(S,C#type.def), - {simpletable,T}; + #type{def=#'Externaltypereference'{}=ExtTypeRef} -> + ERef = check_externaltypereference(S, ExtTypeRef), + {simpletable,ERef#'Externaltypereference'.type}; {valueset,#type{def=ERef=#'Externaltypereference'{}}} -> % this is an object set {_,TDef} = get_referenced_type(S,ERef), case TDef#typedef.typespec of @@ -6814,6 +6802,8 @@ asn1_error(#state{mname=Where}, Item, Error) -> format_error({already_defined,Name,PrevLine}) -> io_lib:format("the name ~p has already been defined at line ~p", [Name,PrevLine]); +format_error(illegal_octet_string_value) -> + "expecting a bstring or an hstring as value for an OCTET STRING"; format_error({invalid_fields,Fields,Obj}) -> io_lib:format("invalid ~s in ~p", [format_fields(Fields),Obj]); format_error({missing_mandatory_fields,Fields,Obj}) -> diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 71d870b4ce..4707e517b4 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -71,7 +71,7 @@ pgen_module(OutFile,Erules,Module, HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Options,Indent), asn1ct_name:start(), ErlFile = lists:concat([OutFile,".erl"]), - open_output_file(ErlFile), + _ = open_output_file(ErlFile), asn1ct_func:start_link(), gen_head(Erules,Module,HrlGenerated), pgen_exports(Erules,Module,TypeOrVal), @@ -190,7 +190,7 @@ pgen_check_defaultval(Erules,Module) -> "********~n~n",[X]) end, lists:foreach(Fun,CheckObjects), - file:close(IoDevice); + ok = file:close(IoDevice); _ -> ok end, gen_check_defaultval(Erules,Module,CheckObjects). @@ -1124,7 +1124,7 @@ pgen_info() -> open_hrl(OutFile,Module) -> File = lists:concat([OutFile,".hrl"]), - open_output_file(File), + _ = open_output_file(File), gen_hrlhead(Module). %% EMIT functions ************************ diff --git a/lib/asn1/src/asn1ct_table.erl b/lib/asn1/src/asn1ct_table.erl index a5eb6d0413..2eca80eda3 100644 --- a/lib/asn1/src/asn1ct_table.erl +++ b/lib/asn1/src/asn1ct_table.erl @@ -22,34 +22,25 @@ %% Table abstraction module for ASN.1 compiler -export([new/1]). --export([new/2]). -export([new_reuse/1]). --export([new_reuse/2]). -export([exists/1]). -export([size/1]). -export([insert/2]). -export([lookup/2]). -export([match/2]). -export([to_list/1]). --export([delete/1]). % TODO: Remove (since we run in a separate process) +-export([delete/1]). -%% Always creates a new table -new(Table) -> new(Table, []). -new(Table, Options) -> - TableId = case get(Table) of - undefined -> - ets:new(Table, Options); - _ -> - delete(Table), - ets:new(Table, Options) - end, +%% Always create a new table. +new(Table) -> + undefined = get(Table), %Assertion. + TableId = ets:new(Table, []), put(Table, TableId). -%% Only create it if it doesn't exist yet -new_reuse(Table) -> new_reuse(Table, []). -new_reuse(Table, Options) -> - not exists(Table) andalso new(Table, Options). +%% Only create it if it doesn't exist yet. +new_reuse(Table) -> + not exists(Table) andalso new(Table). exists(Table) -> get(Table) =/= undefined. @@ -63,14 +54,17 @@ match(Table, MatchSpec) -> ets:match(get(Table), MatchSpec). to_list(Table) -> ets:tab2list(get(Table)). +%% Deleting tables is no longer strictly necessary since each compilation +%% runs in separate process, but it will reduce memory consumption +%% especially when many compilations are run in parallel. + delete(Tables) when is_list(Tables) -> [delete(T) || T <- Tables], true; delete(Table) when is_atom(Table) -> - case get(Table) of + case erase(Table) of undefined -> true; TableId -> - ets:delete(TableId), - erase(Table) + ets:delete(TableId) end. diff --git a/lib/asn1/src/asn1ct_tok.erl b/lib/asn1/src/asn1ct_tok.erl index 85199c65ec..33f4379173 100644 --- a/lib/asn1/src/asn1ct_tok.erl +++ b/lib/asn1/src/asn1ct_tok.erl @@ -36,7 +36,7 @@ process(Stream,Lno,R) -> process(io:get_line(Stream, ''), Stream,Lno+1,R). process(eof, Stream,Lno,R) -> - file:close(Stream), + ok = file:close(Stream), lists:flatten(lists:reverse([{'$end',Lno}|R])); diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl index a86c963b9d..221cd991a7 100644 --- a/lib/asn1/src/asn1ct_value.erl +++ b/lib/asn1/src/asn1ct_value.erl @@ -18,6 +18,7 @@ %% %% -module(asn1ct_value). +-compile([{nowarn_deprecated_function,{asn1rt,utf8_list_to_binary,1}}]). %% Generate Erlang values for ASN.1 types. %% The value is randomized within it's constraints @@ -352,7 +353,7 @@ random_unnamed_bit_string(M, C) -> random(Upper) -> {A1,A2,A3} = erlang:now(), - random:seed(A1,A2,A3), + _ = random:seed(A1, A2, A3), random:uniform(Upper). size_random(C) -> diff --git a/lib/asn1/src/asn1rt.erl b/lib/asn1/src/asn1rt.erl index d18f81346a..ad8b879c38 100644 --- a/lib/asn1/src/asn1rt.erl +++ b/lib/asn1/src/asn1rt.erl @@ -18,14 +18,13 @@ %% %% -module(asn1rt). +-deprecated(module). %% Runtime functions for ASN.1 (i.e encode, decode) -export([encode/2,encode/3,decode/3,load_driver/0,unload_driver/0,info/1]). -export([utf8_binary_to_list/1,utf8_list_to_binary/1]). - --deprecated([load_driver/0,unload_driver/0]). encode(Module,{Type,Term}) -> encode(Module,Type,Term). diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 3b34feb5a3..d438300596 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -860,7 +860,7 @@ duplicate_tags(Config) -> rtUI(Config) -> test(Config, fun rtUI/3). rtUI(Config, Rule, Opts) -> asn1_test_lib:compile("Prim", Config, [Rule|Opts]), - {ok, _} = asn1rt:info('Prim'), + _ = 'Prim':info(), Rule = 'Prim':encoding_rule(), io:format("Default BIT STRING format: ~p\n", ['Prim':bit_string_format()]). diff --git a/lib/asn1/test/asn1_appup_test.erl b/lib/asn1/test/asn1_appup_test.erl index a2c1423eda..7391959645 100644 --- a/lib/asn1/test/asn1_appup_test.erl +++ b/lib/asn1/test/asn1_appup_test.erl @@ -21,8 +21,8 @@ %% Purpose: Verify the application specifics of the asn1 application %%---------------------------------------------------------------------- -module(asn1_appup_test). --compile({no_auto_import,[error/1]}). -compile(export_all). +-include_lib("common_test/include/ct.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -44,16 +44,9 @@ end_per_group(_GroupName, Config) -> init_per_suite(suite) -> []; init_per_suite(doc) -> []; init_per_suite(Config) when is_list(Config) -> - AppFile = file_name(asn1, ".app"), - AppupFile = file_name(asn1, ".appup"), - [{app_file, AppFile}, {appup_file, AppupFile}|Config]. + Config. -file_name(App, Ext) -> - LibDir = code:lib_dir(App), - filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]). - - end_per_suite(suite) -> []; end_per_suite(doc) -> []; end_per_suite(Config) when is_list(Config) -> @@ -62,349 +55,7 @@ end_per_suite(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -appup(suite) -> - []; -appup(doc) -> - "perform a simple check of the appup file"; +appup() -> + [{doc, "perform a simple check of the asn1 appup file"}]. appup(Config) when is_list(Config) -> - AppupFile = key1search(appup_file, Config), - AppFile = key1search(app_file, Config), - Modules = modules(AppFile), - check_appup(AppupFile, Modules). - -modules(File) -> - case file:consult(File) of - {ok, [{application,asn1,Info}]} -> - case lists:keysearch(modules,1,Info) of - {value, {modules, Modules}} -> - Modules; - false -> - fail({bad_appinfo, Info}) - end; - Error -> - fail({bad_appfile, Error}) - end. - - -check_appup(AppupFile, Modules) -> - case file:consult(AppupFile) of - {ok, [{V, UpFrom, DownTo}]} -> - io:format("V= ~p, UpFrom= ~p, DownTo= ~p, Modules= ~p~n", - [V, UpFrom, DownTo, Modules]), - check_appup(V, UpFrom, DownTo, Modules); - Else -> - fail({bad_appupfile, Else}) - end. - - -check_appup(V, UpFrom, DownTo, Modules) -> - check_version(V), - check_depends(up, UpFrom, Modules), - check_depends(down, DownTo, Modules), - ok. - - -check_depends(_, [], _) -> - ok; -check_depends(UpDown, [Dep|Deps], Modules) -> - check_depend(UpDown, Dep, Modules), - check_depends(UpDown, Deps, Modules). - - -check_depend(up,I={add_application,_App},Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [up,I , Modules]), - ok; -check_depend(down,I={remove_application,_App},Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [down,I , Modules]), - ok; -check_depend(UpDown, {V, Instructions}, Modules) -> - d("check_instructions(~w) -> entry with" - "~n V: ~p" - "~n Modules: ~p", [UpDown, V, Modules]), - check_version(V), - case check_instructions(UpDown, - Instructions, Instructions, [], [], Modules) of - {_Good, []} -> - ok; - {_, Bad} -> - fail({bad_instructions, Bad, UpDown}) - end. - - -check_instructions(_, [], _, Good, Bad, _) -> - {lists:reverse(Good), lists:reverse(Bad)}; -check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instr: ~p", [UpDown,Instr]), - case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of - ok -> - check_instructions(UpDown, Instrs, AllInstr, - [Instr|Good], Bad, Modules); - {error, Reason} -> - d("check_instructions(~w) -> bad instruction: " - "~n Reason: ~p", [UpDown,Reason]), - check_instructions(UpDown, Instrs, AllInstr, Good, - [{Instr, Reason}|Bad], Modules) - end. - -%% A new module is added -check_instruction(up, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when up-add_module instruction with" - "~n Module: ~p", [Module]), - check_module(Module, Modules); - -%% An old module is re-added -check_instruction(down, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when down-add_module instruction with" - "~n Module: ~p", [Module]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - ok; - ok -> - error({existing_readded_module, Module}) - end; - -%% Removing a module on upgrade: -%% - the module has been removed from the app-file. -%% - check that no module depends on this (removed) module -check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - d("check_instruction -> entry when up-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - check_purge(Pre), - check_purge(Post); - ok -> - error({existing_removed_module, Module}) - end; - -%% Removing a module on downgrade: the module exist -%% in the app-file. -check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - d("check_instruction -> entry when down-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - ok -> - check_purge(Pre), - check_purge(Post), - check_no_remove_depends(Module, AllInstr); - {error, {unknown_module, Module, Modules}} -> - error({nonexisting_removed_module, Module}) - end; - -check_instruction(_, {load_module, Module, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - d("check_instruction -> entry when load_module instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {update, Module, Change, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - d("check_instruction -> entry when update instruction with" - "~n Module: ~p" - "~n Change: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Change, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_change(Change), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {apply, {Module, Function, Args}}, - _AllInstr, Modules) - when is_atom(Module), is_atom(Function), is_list(Args) -> - d("check_instruction -> entry when apply instruction with" - "~n Module: ~p" - "~n Function: ~p" - "~n Args: ~p", [Module, Function, Args]), - check_module(Module, Modules), - check_apply(Module,Function,Args); - -check_instruction(_, Instr, _AllInstr, _Modules) -> - d("check_instruction -> entry when unknown instruction with" - "~n Instr: ~p", [Instr]), - error({error, {unknown_instruction, Instr}}). - - -%% If Module X depends on Module Y, then module Y must have an update -%% instruction of some sort (otherwise the depend is faulty). -updated_modules([], Modules) -> - d("update_modules -> entry when done with" - "~n Modules: ~p", [Modules]), - Modules; -updated_modules([Instr|Instrs], Modules) -> - d("update_modules -> entry with" - "~n Instr: ~p" - "~n Modules: ~p", [Instr,Modules]), - Module = instruction_module(Instr), - d("update_modules -> Module: ~p", [Module]), - updated_modules(Instrs, [Module|Modules]). - -instruction_module({add_module, Module}) -> - Module; -instruction_module({remove, {Module, _, _}}) -> - Module; -instruction_module({load_module, Module, _, _, _}) -> - Module; -instruction_module({update, Module, _, _, _, _}) -> - Module; -instruction_module({apply, {Module, _, _}}) -> - Module; -instruction_module(Instr) -> - d("instruction_module -> entry when unknown instruction with" - "~n Instr: ~p", [Instr]), - error({error, {unknown_instruction, Instr}}). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -check_version(V) when is_list(V) -> - ok; -check_version(V) -> - error({bad_version, V}). - - -check_module(M, Modules) when is_atom(M) -> - case lists:member(M,Modules) of - true -> - ok; - false -> - error({unknown_module, M, Modules}) - end; -check_module(M, _) -> - error({bad_module, M}). - -check_apply(Module,Function,Args) -> - case (catch Module:module_info()) of - Info when is_list(Info) -> - check_exported(Function,Args,Info); - {'EXIT',{undef,_}} -> - error({not_existing_module,Module}) - end. - -check_exported(Function,Args,Info) -> - case lists:keysearch(exports,1,Info) of - {value,{exports,FunList}} -> - case lists:keysearch(Function,1,FunList) of - {value,{_,Arity}} when Arity==length(Args) -> - ok; - _ -> - error({not_exported_function,Function,length(Args)}) - end; - _ -> - error({bad_export,Info}) - end. - -check_module_depend(M, [], _) when is_atom(M) -> - d("check_module_depend -> entry with" - "~n M: ~p", [M]), - ok; -check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) -> - d("check_module_depend -> entry with" - "~n M: ~p" - "~n Deps: ~p" - "~n Modules: ~p", [M, Deps, Modules]), - case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of - [] -> - ok; - Unknown -> - error({unknown_depend_modules, Unknown}) - end; -check_module_depend(_M, D, _Modules) -> - d("check_module_depend -> entry when bad depend with" - "~n D: ~p", [D]), - error({bad_depend, D}). - - -check_no_remove_depends(_Module, []) -> - ok; -check_no_remove_depends(Module, [Instr|Instrs]) -> - check_no_remove_depend(Module, Instr), - check_no_remove_depends(Module, Instrs). - -check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, load_module, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, update, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(_, _) -> - ok. - - -check_change(soft) -> - ok; -check_change({advanced, _Something}) -> - ok; -check_change(Change) -> - error({bad_change, Change}). - - -check_purge(soft_purge) -> - ok; -check_purge(brutal_purge) -> - ok; -check_purge(Purge) -> - error({bad_purge, Purge}). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -error(Reason) -> - throw({error, Reason}). - -fail(Reason) -> - exit({suite_failed, Reason}). - -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> - fail({not_found, Key, L}); - {value, {Key, Value}} -> - Value - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -d(F, A) -> - d(false, F, A). - -d(true, F, A) -> - io:format(F ++ "~n", A); -d(_, _, _) -> - ok. - - + ok = ?t:appup_test(asn1). diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl index 6451f81c01..930b44cea6 100644 --- a/lib/asn1/test/error_SUITE.erl +++ b/lib/asn1/test/error_SUITE.erl @@ -19,7 +19,7 @@ -module(error_SUITE). -export([suite/0,all/0,groups/0, - already_defined/1,enumerated/1,objects/1]). + already_defined/1,enumerated/1,objects/1,values/1]). -include_lib("test_server/include/test_server.hrl"). @@ -29,9 +29,11 @@ all() -> [{group,p}]. groups() -> - [{p,parallel(),[already_defined, - enumerated, - objects]}]. + [{p,parallel(), + [already_defined, + enumerated, + objects, + values]}]. parallel() -> case erlang:system_info(schedulers) > 1 of @@ -138,6 +140,25 @@ objects(Config) -> } = run(P, Config), ok. +values(Config) -> + M = 'Values', + P = {M, + <<"Values DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + " os1 OCTET STRING ::= \"abc\"\n" + " os2 OCTET STRING ::= 42\n" + " os3 OCTET STRING ::= { 1, 3 }\n" + "END\n">>}, + {error, + [ + {structured_error,{M,2},asn1ct_check, + illegal_octet_string_value}, + {structured_error,{M,3},asn1ct_check, + illegal_octet_string_value}, + {structured_error,{M,4},asn1ct_check, + illegal_octet_string_value} + ] + } = run(P, Config), + ok. run({Mod,Spec}, Config) -> diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index 3262d61a4d..155d6f6ff5 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -18,6 +18,8 @@ %% %% -module(testPrimStrings). +-compile([{nowarn_deprecated_function,{asn1rt,utf8_list_to_binary,1}}, + {nowarn_deprecated_function,{asn1rt,utf8_binary_to_list,1}}]). -export([bit_string/2]). -export([octet_string/1]). diff --git a/lib/common_test/src/common_test.appup.src b/lib/common_test/src/common_test.appup.src index 0fbe5f23f7..4dfd9f1b0d 100644 --- a/lib/common_test/src/common_test.appup.src +++ b/lib/common_test/src/common_test.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}.
\ No newline at end of file +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, common_test}]}], + [{<<".*">>,[{restart_application, common_test}]}] +}. diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl index 3dfa67a771..fe4f473846 100644 --- a/lib/compiler/src/beam_a.erl +++ b/lib/compiler/src/beam_a.erl @@ -92,6 +92,8 @@ rename_instr({put_map_assoc,Fail,S,D,R,L}) -> {put_map,Fail,assoc,S,D,R,L}; rename_instr({put_map_exact,Fail,S,D,R,L}) -> {put_map,Fail,exact,S,D,R,L}; +rename_instr({test,has_map_fields,Fail,Src,{list,List}}) -> + {test,has_map_fields,Fail,[Src|List]}; rename_instr({select_val=I,Reg,Fail,{list,List}}) -> {select,I,Reg,Fail,List}; rename_instr({select_tuple_arity=I,Reg,Fail,{list,List}}) -> diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl index 112b087f3c..f8cf178d2e 100644 --- a/lib/compiler/src/beam_asm.erl +++ b/lib/compiler/src/beam_asm.erl @@ -324,6 +324,8 @@ make_op({gc_bif,Bif,Fail,Live,Args,Dest}, Dict) -> encode_op(BifOp, [Fail,Live,{extfunc,erlang,Bif,Arity}|Args++[Dest]],Dict); make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) -> encode_op(Op, [Fail,Src1,Src2,Unit,Dest], Dict); +make_op({test,Cond,Fail,Src,{list,_}=Ops}, Dict) -> + encode_op(Cond, [Fail,Src,Ops], Dict); make_op({test,Cond,Fail,Ops}, Dict) when is_list(Ops) -> encode_op(Cond, [Fail|Ops], Dict); make_op({test,Cond,Fail,Live,[Op|Ops],Dst}, Dict) when is_list(Ops) -> diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 3723cc19e1..7a30c68593 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -154,8 +154,8 @@ collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list}; collect(remove_message) -> {set,[],[],remove_message}; collect({put_map,F,Op,S,D,R,{list,Puts}}) -> {set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}; -collect({get_map_element,F,S,K,D}) -> - {set,[D],[S],{get_map_element,K,F}}; +collect({get_map_elements,F,S,{list,Gets}}) -> + {set,Gets,[S],{get_map_elements,F}}; collect({'catch',R,L}) -> {set,[R],[],{'catch',L}}; collect(fclearerror) -> {set,[],[],fclearerror}; collect({fcheckerror,{f,0}}) -> {set,[],[],fcheckerror}; @@ -240,7 +240,7 @@ move_allocates_2(Alloc, [], Acc) -> alloc_may_pass({set,_,_,{alloc,_,_}}) -> false; alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false; -alloc_may_pass({set,_,_,{get_map_element,_,_}}) -> false; +alloc_may_pass({set,_,_,{get_map_elements,_}}) -> false; alloc_may_pass({set,_,_,put_list}) -> false; alloc_may_pass({set,_,_,put}) -> false; alloc_may_pass({set,_,_,_}) -> true. @@ -291,7 +291,11 @@ opt_moves([X0,Y0], Is0) -> not_possible -> {[X,Y0],Is2}; {X,_} -> {[X,Y0],Is2}; {Y,Is} -> {[X,Y],Is} - end. + end; +opt_moves(Ds, Is) -> + %% multiple destinations -> pass through + {Ds,Is}. + %% opt_move(Dest, [Instruction]) -> {UpdatedDest,[Instruction]} | not_possible %% If there is a {move,Dest,FinalDest} instruction diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index 55f985ad0e..b653998252 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -262,8 +262,8 @@ replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> replace([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{I,{f,label(Lbl, D)},Op,Src,Dst,Live,List}|Acc], D); -replace([{get_map_element=I,{f,Lbl},Src,Key,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Src,Key,Dst}|Acc], D); +replace([{get_map_elements=I,{f,Lbl},Src,List}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Src,List}|Acc], D); replace([I|Is], Acc, D) -> replace(Is, [I|Acc], D); replace([], Acc, _) -> Acc. diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index 212b9fb03a..ea51673fa3 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,16 +29,24 @@ -type label() :: non_neg_integer(). +-type index() :: non_neg_integer(). + +-type atom_tab() :: gb_trees:tree(atom(), index()). +-type import_tab() :: gb_trees:tree(mfa(), index()). +-type fname_tab() :: gb_trees:tree(Name :: term(), index()). +-type line_tab() :: gb_trees:tree({Fname :: index(), Line :: term()}, index()). +-type literal_tab() :: dict:dict(Literal :: term(), index()). + -record(asm, - {atoms = gb_trees:empty() :: gb_tree(), %{Atom,Index} + {atoms = gb_trees:empty() :: atom_tab(), exports = [] :: [{label(), arity(), label()}], locals = [] :: [{label(), arity(), label()}], - imports = gb_trees:empty() :: gb_tree(), %{{M,F,A},Index} + imports = gb_trees:empty() :: import_tab(), strings = <<>> :: binary(), %String pool lambdas = [], %[{...}] - literals = dict:new() :: dict(), %Format: {Literal,Number} - fnames = gb_trees:empty() :: gb_tree(), %{Name,Index} - lines = gb_trees:empty() :: gb_tree(), %{{Fname,Line},Index} + literals = dict:new() :: literal_tab(), + fnames = gb_trees:empty() :: fname_tab(), + lines = gb_trees:empty() :: line_tab(), num_lines = 0 :: non_neg_integer(), %Number of line instructions next_import = 0 :: non_neg_integer(), string_offset = 0 :: non_neg_integer(), diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index 57fdf95677..4bdfe4e0c2 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,7 +37,8 @@ %%----------------------------------------------------------------------- --type literals() :: 'none' | gb_tree(). +-type index() :: non_neg_integer(). +-type literals() :: 'none' | gb_trees:tree(index(), term()). -type symbolic_tag() :: 'a' | 'f' | 'h' | 'i' | 'u' | 'x' | 'y' | 'z'. -type disasm_tag() :: symbolic_tag() | 'fr' | 'atom' | 'float' | 'literal'. -type disasm_term() :: 'nil' | {disasm_tag(), _}. @@ -216,7 +217,8 @@ optional_chunk(F, ChunkTag) -> %%----------------------------------------------------------------------- -type l_info() :: {non_neg_integer(), {_,_,_,_,_,_}}. --spec beam_disasm_lambdas('none' | binary(), gb_tree()) -> 'none' | [l_info()]. +-spec beam_disasm_lambdas('none' | binary(), gb_trees:tree(index(), _)) -> + 'none' | [l_info()]. beam_disasm_lambdas(none, _) -> none; beam_disasm_lambdas(<<_:32,Tab/binary>>, Atoms) -> @@ -366,9 +368,13 @@ disasm_instr(B, Bs, Atoms, Literals) -> select_tuple_arity -> disasm_select_inst(select_tuple_arity, Bs, Atoms, Literals); put_map_assoc -> - disasm_map_inst(put_map_assoc, Bs, Atoms, Literals); + disasm_map_inst(put_map_assoc, Arity, Bs, Atoms, Literals); put_map_exact -> - disasm_map_inst(put_map_exact, Bs, Atoms, Literals); + disasm_map_inst(put_map_exact, Arity, Bs, Atoms, Literals); + get_map_elements -> + disasm_map_inst(get_map_elements, Arity, Bs, Atoms, Literals); + has_map_fields -> + disasm_map_inst(has_map_fields, Arity, Bs, Atoms, Literals); _ -> try decode_n_args(Arity, Bs, Atoms, Literals) of {Args, RestBs} -> @@ -399,16 +405,15 @@ disasm_select_inst(Inst, Bs, Atoms, Literals) -> {List, RestBs} = decode_n_args(Len, Bs4, Atoms, Literals), {{Inst, [X,F,{Z,U,List}]}, RestBs}. -disasm_map_inst(Inst, Bs0, Atoms, Literals) -> - {F, Bs1} = decode_arg(Bs0, Atoms, Literals), - {S, Bs2} = decode_arg(Bs1, Atoms, Literals), - {X, Bs3} = decode_arg(Bs2, Atoms, Literals), - {N, Bs4} = decode_arg(Bs3, Atoms, Literals), - {Z, Bs5} = decode_arg(Bs4, Atoms, Literals), - {U, Bs6} = decode_arg(Bs5, Atoms, Literals), - {u, Len} = U, - {List, RestBs} = decode_n_args(Len, Bs6, Atoms, Literals), - {{Inst, [F,S,X,N,{Z,U,List}]}, RestBs}. +disasm_map_inst(Inst, Arity, Bs0, Atoms, Literals) -> + {Args0,Bs1} = decode_n_args(Arity, Bs0, Atoms, Literals), + %% no droplast .. + [Z|Args1] = lists:reverse(Args0), + Args = lists:reverse(Args1), + {U, Bs2} = decode_arg(Bs1, Atoms, Literals), + {u, Len} = U, + {List, RestBs} = decode_n_args(Len, Bs2, Atoms, Literals), + {{Inst, Args ++ [{Z,U,List}]}, RestBs}. %%----------------------------------------------------------------------- %% decode_arg([Byte]) -> {Arg, [Byte]} @@ -432,7 +437,8 @@ decode_arg([B|Bs]) -> decode_int(Tag, B, Bs) end. --spec decode_arg([byte(),...], gb_tree(), literals()) -> {disasm_term(), [byte()]}. +-spec decode_arg([byte(),...], gb_trees:tree(index(), _), literals()) -> + {disasm_term(), [byte()]}. decode_arg([B|Bs0], Atoms, Literals) -> Tag = decode_tag(B band 2#111), @@ -1150,13 +1156,15 @@ resolve_inst({is_map,Args0},_,_,_) -> [FLbl|Args] = resolve_args(Args0), {test, is_map, FLbl, Args}; -resolve_inst({has_map_field,Args0},_,_,_) -> - [FLbl|Args] = resolve_args(Args0), - {test,has_map_field,FLbl,Args}; +resolve_inst({has_map_fields,Args0},_,_,_) -> + [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0, + List = resolve_args(List0), + {test,has_map_fields,FLbl,Src,{list,List}}; -resolve_inst({get_map_element,Args},_,_,_) -> - [FLbl,Src,Key,Dst] = resolve_args(Args), - {get_map_element,FLbl,Src,Key,Dst}; +resolve_inst({get_map_elements,Args0},_,_,_) -> + [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0, + List = resolve_args(List0), + {get_map_elements,FLbl,Src,{list,List}}; %% %% Catches instructions that are not yet handled. diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl index 534bc6d954..46835bece1 100644 --- a/lib/compiler/src/beam_flatten.erl +++ b/lib/compiler/src/beam_flatten.erl @@ -63,8 +63,8 @@ norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I}; norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2}; norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) -> {put_map,F,Op,S,D,R,{list,Puts}}; -norm({set,[D],[S],{get_map_element,K,F}}) -> - {get_map_element,F,S,K,D}; +norm({set,Gets,[S],{get_map_elements,F}}) -> + {get_map_elements,F,S,{list,Gets}}; norm({set,[],[],remove_message}) -> remove_message; norm({set,[],[],fclearerror}) -> fclearerror; norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}. diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index 1f720b94c3..0fc8d45c80 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -529,7 +529,7 @@ ulbl({bs_put,Lbl,_,_}, Used) -> mark_used(Lbl, Used); ulbl({put_map,Lbl,_Op,_Src,_Dst,_Live,_List}, Used) -> mark_used(Lbl, Used); -ulbl({get_map_element,Lbl,_Src,_Key,_Dst}, Used) -> +ulbl({get_map_elements,Lbl,_Src,_List}, Used) -> mark_used(Lbl, Used); ulbl(_, Used) -> Used. diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl index 638a4826ea..688bba9a94 100644 --- a/lib/compiler/src/beam_split.erl +++ b/lib/compiler/src/beam_split.erl @@ -53,9 +53,9 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{put_map,Fail,Op,S,D,R,{list,Puts}}| make_block(Bl, Acc)]); -split_block([{set,[D],[S],{get_map_element,K,{f,Lbl}=Fail}}|Is], Bl, Acc) +split_block([{set,Gets,[S],{get_map_elements,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 -> - split_block(Is, [], [{get_map_element,Fail,S,K,D}|make_block(Bl, Acc)]); + split_block(Is, [], [{get_map_elements,Fail,S,{list,Gets}}|make_block(Bl, Acc)]); split_block([{set,[R],[],{'catch',L}}|Is], Bl, Acc) -> split_block(Is, [], [{'catch',R,L}|make_block(Bl, Acc)]); split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) -> diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index a3f16cfa8f..27034aecce 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -185,6 +185,7 @@ is_pure_test({test,is_lt,_,[_,_]}) -> true; is_pure_test({test,is_nil,_,[_]}) -> true; is_pure_test({test,is_nonempty_list,_,[_]}) -> true; is_pure_test({test,test_arity,_,[_,_]}) -> true; +is_pure_test({test,has_map_fields,_,[_,{list,_}]}) -> true; is_pure_test({test,Op,_,Ops}) -> erl_internal:new_type_test(Op, length(Ops)). diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 2486d486ed..9d5563d13b 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -213,9 +213,12 @@ validate_error_1(Error, Module, Name, Ar) -> {{Module,Name,Ar}, {internal_error,'_',{Error,erlang:get_stacktrace()}}}. +-type index() :: non_neg_integer(). +-type reg_tab() :: gb_trees:tree(index(), 'none' | {'value', _}). + -record(st, %Emulation state - {x=init_regs(0, term) :: gb_tree(), %x register info. - y=init_regs(0, initialized) :: gb_tree(), %y register info. + {x=init_regs(0, term) :: reg_tab(),%x register info. + y=init_regs(0, initialized) :: reg_tab(),%y register info. f=init_fregs(), % numy=none, %Number of y registers. h=0, %Available heap size. @@ -227,11 +230,16 @@ validate_error_1(Error, Module, Name, Ar) -> setelem=false %Previous instruction was setelement/3. }). +-type label() :: integer(). +-type label_set() :: gb_sets:set(label()). +-type branched_tab() :: gb_trees:tree(label(), #st{}). +-type ft_tab() :: gb_trees:tree(). + -record(vst, %Validator state {current=none :: #st{} | 'none', %Current state - branched=gb_trees:empty() :: gb_tree(), %States at jumps - labels=gb_sets:empty() :: gb_set(), %All defined labels - ft=gb_trees:empty() :: gb_tree() %Some other functions + branched=gb_trees:empty() :: branched_tab(), %States at jumps + labels=gb_sets:empty() :: label_set(), %All defined labels + ft=gb_trees:empty() :: ft_tab() %Some other functions % in the module (those that start with bs_start_match2). }). @@ -770,6 +778,10 @@ valfun_4({test,is_nonempty_list,{f,Lbl},[Cons]}, Vst) -> valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) -> assert_type(tuple, Tuple, Vst), set_type_reg({tuple,Sz}, Tuple, branch_state(Lbl, Vst)); +valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) -> + validate_src([Src], Vst), + assert_strict_literal_termorder(List), + branch_state(Lbl, Vst); valfun_4({test,_Op,{f,Lbl},Src}, Vst) -> validate_src(Src, Vst), branch_state(Lbl, Vst); @@ -871,14 +883,23 @@ valfun_4({put_map_assoc,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> verify_put_map(Fail, Src, Dst, Live, List, Vst); valfun_4({put_map_exact,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> verify_put_map(Fail, Src, Dst, Live, List, Vst); -valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) -> - assert_term(Src, Vst0), - assert_term(Key, Vst0), - Vst = branch_state(Fail, Vst0), - set_type_reg(term, Dst, Vst); +valfun_4({get_map_elements,{f,Fail},Src,{list,List}}, Vst) -> + verify_get_map(Fail, Src, List, Vst); valfun_4(_, _) -> error(unknown_instruction). +verify_get_map(Fail, Src, List, Vst0) -> + assert_term(Src, Vst0), + Vst1 = branch_state(Fail, Vst0), + Lits = mmap(fun(L,_R) -> [L] end, List), + assert_strict_literal_termorder(Lits), + verify_get_map_pair(List,Vst0,Vst1). + +verify_get_map_pair([],_,Vst) -> Vst; +verify_get_map_pair([Src,Dst|Vs],Vst0,Vsti) -> + assert_term(Src, Vst0), + verify_get_map_pair(Vs,Vst0,set_type_reg(term,Dst,Vsti)). + verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> verify_live(Live, Vst0), verify_y_init(Vst0), @@ -1099,6 +1120,39 @@ assert_freg_set({fr,Fr}=Freg, #vst{current=#st{f=Fregs}}) end; assert_freg_set(Fr, _) -> error({bad_source,Fr}). +%%% Maps + +%% ensure that a list of literals has a strict +%% ascending term order (also meaning unique literals) +assert_strict_literal_termorder(Ls) -> + Vs = lists:map(fun (L) -> get_literal(L) end, Ls), + case check_strict_value_termorder(Vs) of + true -> ok; + false -> error({not_strict_order, Ls}) + end. + +%% usage: +%% mmap(fun(A,B) -> [{A,B}] end, [1,2,3,4]), +%% [{1,2},{3,4}] + +mmap(F,List) -> + {arity,Ar} = erlang:fun_info(F,arity), + mmap(F,Ar,List). +mmap(_F,_,[]) -> []; +mmap(F,Ar,List) -> + {Hd,Tl} = lists:split(Ar,List), + apply(F,Hd) ++ mmap(F,Ar,Tl). + +check_strict_value_termorder([]) -> true; +check_strict_value_termorder([_]) -> true; +check_strict_value_termorder([V1,V2]) -> + erts_internal:cmp_term(V1,V2) < 0; +check_strict_value_termorder([V1,V2|Vs]) -> + case erts_internal:cmp_term(V1,V2) < 0 of + true -> check_strict_value_termorder([V2|Vs]); + false -> false + end. + %%% %%% Binary matching. %%% @@ -1334,6 +1388,7 @@ assert_term(Src, Vst) -> %% number Integer or Float of unknown value %% + assert_type(WantedType, Term, Vst) -> assert_type(WantedType, get_term_type(Term, Vst)). @@ -1349,7 +1404,6 @@ assert_type({tuple_element,I}, {tuple,Sz}) assert_type(Needed, Actual) -> error({bad_type,{needed,Needed},{actual,Actual}}). - %% upgrade_tuple_type(NewTupleType, OldType) -> TupleType. %% upgrade_tuple_type/2 is used when linear code finds out more and %% more information about a tuple type, so that the type gets more @@ -1430,6 +1484,15 @@ get_term_type_1({y,Y}=Reg, #vst{current=#st{y=Ys}}) when is_integer(Y) -> get_term_type_1(Src, _) -> error({bad_source,Src}). +%% get_literal(Src) -> literal_value(). +get_literal(nil) -> []; +get_literal({atom,A}) when is_atom(A) -> A; +get_literal({float,F}) when is_float(F) -> F; +get_literal({integer,I}) when is_integer(I) -> I; +get_literal({literal,L}) -> L; +get_literal(T) -> error({not_literal,T}). + + branch_arities([], _, #vst{}=Vst) -> Vst; branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0) when is_integer(Sz) -> diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl index 9953a48710..c2a6ef604e 100644 --- a/lib/compiler/src/beam_z.erl +++ b/lib/compiler/src/beam_z.erl @@ -78,6 +78,18 @@ undo_rename({put_map,Fail,assoc,S,D,R,L}) -> {put_map_assoc,Fail,S,D,R,L}; undo_rename({put_map,Fail,exact,S,D,R,L}) -> {put_map_exact,Fail,S,D,R,L}; +undo_rename({test,has_map_fields,Fail,[Src|List]}) -> + {test,has_map_fields,Fail,Src,{list,[to_typed_literal(V)||V<-List]}}; +undo_rename({get_map_elements,Fail,Src,{list, List}}) -> + {get_map_elements,Fail,Src,{list,[to_typed_literal(V)||V<-List]}}; undo_rename({select,I,Reg,Fail,List}) -> {I,Reg,Fail,{list,List}}; undo_rename(I) -> I. + +%% to_typed_literal(Arg) +%% transform Arg to specific literal i.e. float | integer | atom if applicable +to_typed_literal({literal, V}) when is_float(V) -> {float, V}; +to_typed_literal({literal, V}) when is_atom(V) -> {atom, V}; +to_typed_literal({literal, V}) when is_integer(V) -> {integer, V}; +to_typed_literal({literal, []}) -> nil; +to_typed_literal(V) -> V. diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 60a8559950..3c121f3b04 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -124,8 +124,9 @@ %% keep map exports here for now map_es/1, - update_c_map/2, - ann_c_map/2, + map_val/1, + update_c_map/3, + ann_c_map/3, map_pair_op/1,map_pair_key/1,map_pair_val/1, update_c_map_pair/4, ann_c_map_pair/4 @@ -1579,11 +1580,15 @@ ann_make_list(_, [], Node) -> map_es(#c_map{es = Es}) -> Es. -ann_c_map(As, Es) -> - #c_map{es = Es, anno = As }. +-spec map_val(c_map()) -> cerl(). +map_val(#c_map{var = M}) -> + M. -update_c_map(Old, Es) -> - #c_map{es = Es, anno = get_ann(Old)}. +ann_c_map(As,M,Es) -> + #c_map{var=M,es = Es, anno = As }. + +update_c_map(Old,M,Es) -> + #c_map{var=M, es = Es, anno = get_ann(Old)}. map_pair_key(#c_map_pair{key=K}) -> K. map_pair_val(#c_map_pair{val=V}) -> V. diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl index 3837b57750..44293bb8ce 100644 --- a/lib/compiler/src/cerl_inline.erl +++ b/lib/compiler/src/cerl_inline.erl @@ -64,7 +64,7 @@ seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1, type/1, values_es/1, var_name/1, - map_es/1, update_c_map/2, + map_val/1, map_es/1, update_c_map/3, update_c_map_pair/4, map_pair_op/1, map_pair_key/1, map_pair_val/1 ]). @@ -1334,12 +1334,12 @@ i_bitstr(E, Ren, Env, S) -> i_map(E, Ctx, Ren, Env, S) -> %% Visit the segments for value. - {Es, S1} = mapfoldl(fun (E, S) -> - i_map_pair(E, Ctx, Ren, Env, S) - end, - S, map_es(E)), - S2 = count_size(weight(map), S1), - {update_c_map(E, Es), S2}. + {M1, S1} = i(map_val(E), value, Ren, Env, S), + {Es, S2} = mapfoldl(fun (E, S) -> + i_map_pair(E, Ctx, Ren, Env, S) + end, S1, map_es(E)), + S3 = count_size(weight(map), S2), + {update_c_map(E, M1,Es), S3}. i_map_pair(E, Ctx, Ren, Env, S) -> %% It is not necessary to visit the Op and Key fields, @@ -1411,13 +1411,15 @@ i_pattern(E, Ren, Env, Ren0, Env0, S) -> S2 = count_size(weight(binary), S1), {update_c_binary(E, Es), S2}; map -> + %% map patterns should not have vals + M = map_val(E), + {Es, S1} = mapfoldl(fun (E, S) -> - i_map_pair_pattern(E, Ren, Env, - Ren0, Env0, S) - end, - S, map_es(E)), + i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S) + end, + S, map_es(E)), S2 = count_size(weight(map), S1), - {update_c_map(E, Es), S2}; + {update_c_map(E, M, Es), S2}; _ -> case is_literal(E) of true -> diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl index 2542841eef..2ebeab243f 100644 --- a/lib/compiler/src/cerl_trees.erl +++ b/lib/compiler/src/cerl_trees.erl @@ -57,9 +57,9 @@ update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2, update_c_values/2, values_es/1, var_name/1, - map_es/1, - ann_c_map/2, - update_c_map/2, + map_val/1, map_es/1, + ann_c_map/3, + update_c_map/3, map_pair_key/1,map_pair_val/1,map_pair_op/1, ann_c_map_pair/4, update_c_map_pair/4 @@ -138,7 +138,7 @@ map_1(F, T) -> tuple -> update_c_tuple_skel(T, map_list(F, tuple_es(T))); map -> - update_c_map(T, map_list(F, map_es(T))); + update_c_map(T, map(F,map_val(T)), map_list(F, map_es(T))); map_pair -> update_c_map_pair(T, map(F, map_pair_op(T)), map(F, map_pair_key(T)), @@ -372,8 +372,9 @@ mapfold(F, S0, T) -> {Ts, S1} = mapfold_list(F, S0, tuple_es(T)), F(update_c_tuple_skel(T, Ts), S1); map -> - {Ts, S1} = mapfold_list(F, S0, map_es(T)), - F(update_c_map(T, Ts), S1); + {M , S1} = mapfold(F, S0, map_val(T)), + {Ts, S2} = mapfold_list(F, S1, map_es(T)), + F(update_c_map(T, M, Ts), S2); map_pair -> {Op, S1} = mapfold(F, S0, map_pair_op(T)), {Key, S2} = mapfold(F, S1, map_pair_key(T)), @@ -723,9 +724,10 @@ label(T, N, Env) -> {As, N2} = label_ann(T, N1), {ann_c_tuple_skel(As, Ts), N2}; map -> - {Ts, N1} = label_list(map_es(T), N, Env), - {As, N2} = label_ann(T, N1), - {ann_c_map(As, Ts), N2}; + {M, N1} = label(map_val(T), N, Env), + {Ts, N2} = label_list(map_es(T), N1, Env), + {As, N3} = label_ann(T, N2), + {ann_c_map(As, M, Ts), N3}; map_pair -> {Op, N1} = label(map_pair_op(T), N, Env), {Val, N2} = label(map_pair_key(T), N1, Env), diff --git a/lib/compiler/src/compiler.appup.src b/lib/compiler/src/compiler.appup.src index 54a63833e6..fe273b269c 100644 --- a/lib/compiler/src/compiler.appup.src +++ b/lib/compiler/src/compiler.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, compiler}]}], + [{<<".*">>,[{restart_application, compiler}]}] +}. diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index 79b467f949..7d6bf56ccb 100755 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -529,10 +529,10 @@ BEAM_FORMAT_NUMBER=0 153: line/1 -# R16 +# R17 154: put_map_assoc/5 155: put_map_exact/5 156: is_map/2 -157: has_map_field/3 -158: get_map_element/4 +157: has_map_fields/3 +158: get_map_elements/3 diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl index 31a1f8b0b7..ddcd461bc0 100644 --- a/lib/compiler/src/rec_env.erl +++ b/lib/compiler/src/rec_env.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -95,7 +95,7 @@ test_1(_,0, Env) -> %% ===================================================================== %% @type environment(). An abstract environment. --type mapping() :: {'map', dict()} | {'rec', dict(), dict()}. +-type mapping() :: {'map', dict:dict()} | {'rec', dict:dict(), dict:dict()}. -type environment() :: [mapping(),...]. %% ===================================================================== diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index e2b9213891..eb9c302334 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -3146,9 +3146,6 @@ format_error(result_ignored) -> "(suppress the warning by assigning the expression to the _ variable)"; format_error(useless_building) -> "a term is constructed, but never used"; -format_error({map_pair_key_overloaded,K}) -> - M = io_lib:format("the key ~p is used multiple times in map value association",[K]), - flatten(M); format_error(bin_opt_alias) -> "INFO: the '=' operator will prevent delayed sub binary optimization"; format_error(bin_partition) -> diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 4d155c0fd0..e00ee1f3ad 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -938,22 +938,39 @@ select_map_val(V, Es, B, Fail, I, Vdb, Bef, St0) -> {Bis,Aft,St2} = match_cg(B, Fail, Int, St1), {Eis++Bis,Aft,St2}. +select_extract_map(_, [], _, _, _, Bef, St) -> {[],Bef,St}; select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) -> - F = fun ({map_pair,Key,{var,V}}, Int0) -> - Rsrc = fetch_var(Src, Int0), + %% First split the instruction flow + %% We want one set of each + %% 1) has_map_fields (no target registers) + %% 2) get_map_elements (with target registers) + %% Assume keys are term-sorted + Rsrc = fetch_var(Src, Bef), + + {{HasKs,GetVs},Aft} = lists:foldr(fun + ({map_pair,Key,{var,V}},{{HasKsi,GetVsi},Int0}) -> case vdb_find(V, Vdb) of {V,_,L} when L =< I -> - {[{test,has_map_field,{f,Fail},[Rsrc,Key]}],Int0}; + {{[Key|HasKsi],GetVsi},Int0}; _Other -> Reg1 = put_reg(V, Int0#sr.reg), Int1 = Int0#sr{reg=Reg1}, - {[{get_map_element,{f,Fail}, - Rsrc,Key,fetch_reg(V, Reg1)}], - Int1} + {{HasKsi,[Key,fetch_reg(V, Reg1)|GetVsi]},Int1} end - end, - {Es,Aft} = flatmapfoldl(F, Bef, Vs), - {Es,Aft,St}. + end, {{[],[]},Bef}, Vs), + + Code = case {HasKs,GetVs} of + {[],[]} -> {[],Aft,St}; + {HasKs,[]} -> + [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}}]; + {[],GetVs} -> + [{get_map_elements, {f,Fail},Rsrc,{list,GetVs}}]; + {HasKs,GetVs} -> + [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}}, + {get_map_elements, {f,Fail},Rsrc,{list,GetVs}}] + end, + {Code, Aft, St}. + select_extract_cons(Src, [{var,Hd}, {var,Tl}], I, Vdb, Bef, St) -> {Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 3435a46ca9..a50b46bd7b 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1479,8 +1479,50 @@ pattern({cons,L,H,T}, St) -> pattern({tuple,L,Ps}, St) -> ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St)); pattern({map,L,Ps}, St) -> - #c_map{anno=lineno_anno(L, St), es=sort(pattern_list(Ps, St))}; -pattern({map_field_exact,L,K,V}, St) -> + #c_map{anno=lineno_anno(L, St), es=pattern_map_pairs(Ps, St)}; +pattern({bin,L,Ps}, St) -> + %% We don't create a #ibinary record here, since there is + %% no need to hold any used/new annotations in a pattern. + #c_binary{anno=lineno_anno(L, St),segments=pat_bin(Ps, St)}; +pattern({match,_,P1,P2}, St) -> + pat_alias(pattern(P1, St), pattern(P2, St)). + +%% pattern_map_pairs([MapFieldExact],State) -> [#c_map_pairs{}] +pattern_map_pairs(Ps, St) -> + %% check literal key uniqueness (dict is needed) + %% pattern all pairs + {CMapPairs, Kdb} = lists:mapfoldl(fun + (P,Kdbi) -> + #c_map_pair{key=Ck,val=Cv} = CMapPair = pattern_map_pair(P,St), + K = core_lib:literal_value(Ck), + case dict:find(K,Kdbi) of + {ok, Vs} -> + {CMapPair, dict:store(K,[Cv|Vs],Kdbi)}; + _ -> + {CMapPair, dict:store(K,[Cv],Kdbi)} + end + end, dict:new(), Ps), + pattern_alias_map_pairs(CMapPairs,Kdb,dict:new(),St). + +pattern_alias_map_pairs([],_,_,_) -> []; +pattern_alias_map_pairs([#c_map_pair{key=Ck}=Pair|Pairs],Kdb,Kset,St) -> + %% alias same keys if needed + K = core_lib:literal_value(Ck), + case dict:find(K,Kset) of + {ok,processed} -> + pattern_alias_map_pairs(Pairs,Kdb,Kset,St); + _ -> + Cvs = dict:fetch(K,Kdb), + Cv = pattern_alias_map_pair_patterns(Cvs), + Kset1 = dict:store(K, processed, Kset), + [Pair#c_map_pair{val=Cv}|pattern_alias_map_pairs(Pairs,Kdb,Kset1,St)] + end. + +pattern_alias_map_pair_patterns([Cv]) -> Cv; +pattern_alias_map_pair_patterns([Cv1,Cv2|Cvs]) -> + pattern_alias_map_pair_patterns([pat_alias(Cv1,Cv2)|Cvs]). + +pattern_map_pair({map_field_exact,L,K,V}, St) -> %% FIXME: Better way to construct literals? or missing case %% {Key,_,_} = expr(K, St), Key = case K of @@ -1497,13 +1539,8 @@ pattern({map_field_exact,L,K,V}, St) -> #c_map_pair{anno=lineno_anno(L, St), op=#c_literal{val=exact}, key=Key, - val=pattern(V, St)}; -pattern({bin,L,Ps}, St) -> - %% We don't create a #ibinary record here, since there is - %% no need to hold any used/new annotations in a pattern. - #c_binary{anno=lineno_anno(L, St),segments=pat_bin(Ps, St)}; -pattern({match,_,P1,P2}, St) -> - pat_alias(pattern(P1, St), pattern(P2, St)). + val=pattern(V, St)}. + %% pat_bin([BinElement], State) -> [BinSeg]. diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index bc5ca0314a..5675572092 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -496,7 +496,6 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) -> translate_fc(Args) -> [#c_literal{val=function_clause},make_list(Args)]. - %{Kes,Ep,St2} = map_pairs(Ces, Sub, St1), map_split_pairs(A, Var, Ces, Sub, St0) -> %% two steps %% 1. force variables @@ -718,12 +717,8 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) -> {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), {#k_tuple{anno=A,es=Kes},Osub1,St1}; pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> - {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), + {Kes,Osub1,St1} = pattern_map_pairs(Ces, Isub, Osub0, St0), {#k_map{anno=A,op=exact,es=Kes},Osub1,St1}; -pattern(#c_map_pair{op=#c_literal{val=exact},anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> - {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), - {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), - {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2}; pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) -> {Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0), {#k_binary{anno=A,segs=Kv},Osub1,St1}; @@ -738,6 +733,25 @@ flatten_alias(#c_alias{var=V,pat=P}) -> {[V|Vs],Pat}; flatten_alias(Pat) -> {[],Pat}. +pattern_map_pairs(Ces0, Isub, Osub0, St0) -> + %% It is assumed that all core keys are literals + %% It is later assumed that these keys are term sorted + %% so we need to sort them here + Ces1 = lists:sort(fun + (#c_map_pair{key=CkA},#c_map_pair{key=CkB}) -> + A = core_lib:literal_value(CkA), + B = core_lib:literal_value(CkB), + erts_internal:cmp_term(A,B) < 0 + end, Ces0), + %% pattern the pair keys and values as normal + {Kes,{Osub1,St1}} = lists:mapfoldl(fun + (#c_map_pair{anno=A,key=Ck,val=Cv},{Osubi0,Sti0}) -> + {Kk,Osubi1,Sti1} = pattern(Ck, Isub, Osubi0, Sti0), + {Kv,Osubi2,Sti2} = pattern(Cv, Isub, Osubi1, Sti1), + {#k_map_pair{anno=A,key=Kk,val=Kv},{Osubi2,Sti2}} + end, {Osub0, St0}, Ces1), + {Kes,Osub1,St1}. + pattern_bin(Es, Isub, Osub0, St0) -> {Kbin,{_,Osub},St} = pattern_bin_1(Es, Isub, Osub0, St0), {Kbin,Osub,St}. @@ -1388,13 +1402,13 @@ get_match(#k_bin_int{}=BinInt, St0) -> get_match(#k_tuple{es=Es}, St0) -> {Mes,St1} = new_vars(length(Es), St0), {#k_tuple{es=Mes},Mes,St1}; -get_match(#k_map{es=Es0}, St0) -> +get_match(#k_map{op=exact,es=Es0}, St0) -> {Mes,St1} = new_vars(length(Es0), St0), {Es,_} = mapfoldl(fun (#k_map_pair{}=Pair, [V|Vs]) -> {Pair#k_map_pair{val=V},Vs} end, Mes, Es0), - {#k_map{es=Es},Mes,St1}; + {#k_map{op=exact,es=Es},Mes,St1}; get_match(M, St) -> {M,[],St}. @@ -1519,7 +1533,7 @@ arg_val(Arg, C) -> end || Pair <- Es], %% multiple keys may have the same name %% do not use ordsets - lists:sort(Keys) + lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Keys) end. %% ubody_used_vars(Expr, State) -> [UsedVar] diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index 639c6737e2..b4e486f97c 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -115,7 +115,7 @@ format_1(#k_map{op=assoc,es=Es}, Ctxt) -> format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), "}~" ]; -format_1(#k_map{op=exact,es=Es}, Ctxt) -> +format_1(#k_map{es=Es}, Ctxt) -> ["::{", format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), "}::" diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index 39c9fea5d9..0b56a49cd6 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -69,6 +69,7 @@ INLINE= \ fun \ guard \ lc \ + map \ match \ misc \ num_bif \ diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 34c4b1e264..8cb7d1b55b 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -24,7 +24,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - app_test/1, + app_test/1,appup_test/1, file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1, binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1, other_output/1, encrypted_abstr/1, @@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> test_lib:recompile(?MODULE), - [app_test, file_1, forms_2, module_mismatch, big_file, outdir, + [app_test, appup_test, file_1, forms_2, module_mismatch, big_file, outdir, binary, makedep, cond_and_ifdef, listings, listings_big, other_output, encrypted_abstr, {group, bad_record_use}, strict_record, @@ -71,6 +71,10 @@ end_per_group(_GroupName, Config) -> app_test(Config) when is_list(Config) -> ?line ?t:app_test(compiler). +%% Test that the Application upgrade file has no `basic' errors."; +appup_test(Config) when is_list(Config) -> + ok = ?t:appup_test(compiler). + %% Tests that we can compile and run a simple Erlang program, %% using compile:file/1. diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl index 859c4571ea..5cdf429a5f 100644 --- a/lib/compiler/test/error_SUITE.erl +++ b/lib/compiler/test/error_SUITE.erl @@ -23,7 +23,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1, - transforms/1]). + transforms/1,forbidden_maps/1]). %% Used by transforms/1 test case. -export([parse_transform/2]). @@ -36,7 +36,7 @@ all() -> groups() -> [{p,test_lib:parallel(), - [head_mismatch_line,warnings_as_errors,bif_clashes,transforms]}]. + [head_mismatch_line,warnings_as_errors,bif_clashes,transforms,forbidden_maps]}]. init_per_suite(Config) -> Config. @@ -240,6 +240,21 @@ parse_transform(_, _) -> error(too_bad). +forbidden_maps(Config) when is_list(Config) -> + Ts1 = [{map_illegal_use_of_pattern, + <<" + -export([t/0]). + t() -> + V = 32, + #{<<\"hi\",V,\"all\">> := 1} = id(#{<<\"hi all\">> => 1}). + id(I) -> I. + ">>, + [return], + {error,[{5,erl_lint,{illegal_map_key_variable,'V'}}], []}}], + [] = run2(Config, Ts1), + ok. + + run(Config, Tests) -> ?line File = test_filename(Config), run(Tests, File, dont_write_beam). diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index 86f65c3ed6..b4baef461b 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -30,6 +30,7 @@ t_list_comprehension/1, t_map_sort_literals/1, t_map_size/1, + t_build_and_match_aliasing/1, %% warnings t_warn_useless_build/1, @@ -54,6 +55,8 @@ all() -> [ t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_sort_literals, + t_map_size, + t_build_and_match_aliasing, %% warnings t_warn_useless_build, @@ -103,8 +106,6 @@ t_build_and_match_literals(Config) when is_list(Config) -> id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), %% error case - %V = 32, - %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))), @@ -112,6 +113,20 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. +t_build_and_match_aliasing(Config) when is_list(Config) -> + M1 = id(#{a=>1,b=>2,c=>3,d=>4}), + #{c:=C1=_=_=C2} = M1, + true = C1 =:= C2, + #{a:=A,a:=A,a:=A,b:=B,b:=B} = M1, + #{a:=A,a:=A,a:=A,b:=B,b:=B,b:=2} = M1, + #{a:=A=1,a:=A,a:=A,b:=B=2,b:=B,b:=2} = M1, + #{c:=C1, c:=_, c:=3, c:=_, c:=C2} = M1, + #{c:=C=_=3=_=C} = M1, + + M2 = id(#{"a"=>1,"b"=>2,"c"=>3,"d"=>4}), + #{"a":=A2,"a":=A2,"a":=A2,"b":=B2,"b":=B2,"b":=2} = M2, + #{"a":=_,"a":=_,"a":=_,"b":=_,"b":=_,"b":=2} = M2, + ok. t_map_size(Config) when is_list(Config) -> 0 = map_size(id(#{})), @@ -403,8 +418,12 @@ t_guard_fun(Config) when is_list(Config) -> {l,V} = F2(#{s=>l,v=>[V,V]}), %% error case - {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), - ok. + case (catch F1(#{s=>none,v=>none})) of + {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} -> ok; + {'EXIT', {{case_clause,_},_}} -> {comment,inlined}; + Other -> + test_server:fail({no_match, Other}) + end. t_map_sort_literals(Config) when is_list(Config) -> @@ -485,8 +504,12 @@ t_build_and_match_empty_val(Config) when is_list(Config) -> ok = F(id(#{"hi"=>ok,{1,2}=>ok,1337=>ok})), %% error case - {'EXIT',{function_clause,_}} = (catch (F(id(#{"hi"=>ok})))), - ok. + case (catch (F(id(#{"hi"=>ok})))) of + {'EXIT',{function_clause,_}} -> ok; + {'EXIT', {{case_clause,_},_}} -> {comment,inlined}; + Other -> + test_server:fail({no_match, Other}) + end. t_build_and_match_val(Config) when is_list(Config) -> F = fun @@ -499,8 +522,12 @@ t_build_and_match_val(Config) when is_list(Config) -> {2,"second"} = F(id(#{"hi"=>second,v=>"second"})), %% error case - {'EXIT',{function_clause,_}} = (catch (F(id(#{"hi"=>ok})))), - ok. + case (catch (F(id(#{"hi"=>ok})))) of + {'EXIT',{function_clause,_}} -> ok; + {'EXIT', {{case_clause,_},_}} -> {comment,inlined}; + Other -> + test_server:fail({no_match, Other}) + end. %% Use this function to avoid compile-time evaluation of an expression. diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 925ad0c091..2e1a21178f 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2013. All Rights Reserved. + * Copyright Ericsson AB 2010-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -444,6 +444,15 @@ static ERL_NIF_TERM atom_ppbasis; static ERL_NIF_TERM atom_onbasis; #endif +static ErlNifResourceType* hmac_context_rtype; +struct hmac_context +{ + ErlNifMutex* mtx; + int alive; + HMAC_CTX ctx; +}; +static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*); + /* #define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n") #define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1) @@ -498,6 +507,15 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info) return 0; } + hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context", + (ErlNifResourceDtor*) hmac_context_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (!hmac_context_rtype) { + PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'"); + return 0; + } + if (library_refc > 0) { /* Repeated loading of this library (module upgrade). * Atoms and callbacks are already set, we are done. @@ -1280,11 +1298,19 @@ static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM #endif } +static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) +{ + if (obj->alive) { + HMAC_CTX_cleanup(&obj->ctx); + obj->alive = 0; + } + enif_mutex_destroy(obj->mtx); +} + static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type, Key) */ ErlNifBinary key; - ERL_NIF_TERM ret; - unsigned char * ctx_buf; + struct hmac_context* obj; const EVP_MD *md; if (argv[0] == atom_sha) md = EVP_sha1(); @@ -1309,57 +1335,60 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return enif_make_badarg(env); } - ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret); - HMAC_CTX_init((HMAC_CTX *) ctx_buf); - HMAC_Init((HMAC_CTX *) ctx_buf, key.data, key.size, md); + obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context)); + obj->mtx = enif_mutex_create("crypto.hmac"); + obj->alive = 1; + HMAC_CTX_init(&obj->ctx); + HMAC_Init(&obj->ctx, key.data, key.size, md); - return ret; + return enif_make_resource(env, obj); } static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Context, Data) */ - ErlNifBinary context, data; - ERL_NIF_TERM ret; - unsigned char * ctx_buf; + ErlNifBinary data; + struct hmac_context* obj; - if (!enif_inspect_binary(env, argv[0], &context) - || !enif_inspect_iolist_as_binary(env, argv[1], &data) - || context.size != sizeof(HMAC_CTX)) { + if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj) + || !enif_inspect_iolist_as_binary(env, argv[1], &data)) { + return enif_make_badarg(env); + } + enif_mutex_lock(obj->mtx); + if (!obj->alive) { + enif_mutex_unlock(obj->mtx); return enif_make_badarg(env); } + HMAC_Update(&obj->ctx, data.data, data.size); + enif_mutex_unlock(obj->mtx); - ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret); - memcpy(ctx_buf, context.data, context.size); - HMAC_Update((HMAC_CTX *)ctx_buf, data.data, data.size); CONSUME_REDS(env,data); - - return ret; + return argv[0]; } static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Context) or (Context, HashLen) */ - ErlNifBinary context; ERL_NIF_TERM ret; - HMAC_CTX ctx; + struct hmac_context* obj; unsigned char mac_buf[EVP_MAX_MD_SIZE]; unsigned char * mac_bin; unsigned int req_len = 0; unsigned int mac_len; - if (!enif_inspect_binary(env, argv[0], &context)) { - return enif_make_badarg(env); - } - if (argc == 2 && !enif_get_uint(env, argv[1], &req_len)) { + if (!enif_get_resource(env,argv[0],hmac_context_rtype, (void**)&obj) + || (argc == 2 && !enif_get_uint(env, argv[1], &req_len))) { return enif_make_badarg(env); } - if (context.size != sizeof(ctx)) { - return enif_make_badarg(env); + enif_mutex_lock(obj->mtx); + if (!obj->alive) { + enif_mutex_unlock(obj->mtx); + return enif_make_badarg(env); } - memcpy(&ctx, context.data, context.size); - HMAC_Final(&ctx, mac_buf, &mac_len); - HMAC_CTX_cleanup(&ctx); + HMAC_Final(&obj->ctx, mac_buf, &mac_len); + HMAC_CTX_cleanup(&obj->ctx); + obj->alive = 0; + enif_mutex_unlock(obj->mtx); if (argc == 2 && req_len < mac_len) { /* Only truncate to req_len bytes if asked. */ diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c index 81106b4cc2..a08dcec463 100644 --- a/lib/crypto/c_src/crypto_callback.c +++ b/lib/crypto/c_src/crypto_callback.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012. All Rights Reserved. + * Copyright Ericsson AB 2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -17,6 +17,7 @@ * %CopyrightEnd% */ +#include <stdio.h> #include <string.h> #include <openssl/opensslconf.h> @@ -51,13 +52,28 @@ DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks); static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */ +static void nomem(size_t size, const char* op) +{ + fprintf(stderr, "Out of memory abort. Crypto failed to %s %zu bytes.\r\n", + op, size); + abort(); +} + static void* crypto_alloc(size_t size) { - return enif_alloc(size); + void *ret = enif_alloc(size); + + if (!ret && size) + nomem(size, "allocate"); + return ret; } static void* crypto_realloc(void* ptr, size_t size) { - return enif_realloc(ptr, size); + void* ret = enif_realloc(ptr, size); + + if (!ret && size) + nomem(size, "reallocate"); + return ret; } static void crypto_free(void* ptr) { diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 40f829e704..c95827c371 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2013</year> + <year>1999</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -366,7 +366,11 @@ or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and <seealso marker="#hmac_final_n-2">hmac_final_n</seealso> </p> - + <warning><p>Do not use a <c>Context</c> as argument in more than one + call to hmac_update or hmac_final. The semantics of reusing old contexts + in any way is undefined and could even crash the VM in earlier releases. + The reason for this limitation is a lack of support in the underlying + OpenSSL API.</p></warning> </desc> </func> diff --git a/lib/crypto/src/crypto.appup.src b/lib/crypto/src/crypto.appup.src index 5b4ce5acee..42816773c1 100644 --- a/lib/crypto/src/crypto.appup.src +++ b/lib/crypto/src/crypto.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,13 +16,7 @@ %% under the License. %% %% %CopyrightEnd% -%% {"%VSN%", - [ - {<<"2\\.*">>, [{restart_application, crypto}]} - {<<"1\\.*">>, [{restart_application, crypto}]} - ], - [ - {<<"2\\.*">>, [{restart_application, crypto}]} - {<<"1\\.*">>, [{restart_application, crypto}]} - ]}. + [{<<".*">>,[{restart_application, crypto}]}], + [{<<".*">>,[{restart_application, crypto}]}] +}. diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index d1be7cea68..8efd962283 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -30,6 +30,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [app, + appup, {group, md4}, {group, md5}, {group, ripemd160}, @@ -142,6 +143,11 @@ app() -> app(Config) when is_list(Config) -> ok = ?t:app_test(crypto). %%-------------------------------------------------------------------- +appup() -> + [{doc, "Test that the crypto appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(crypto). +%%-------------------------------------------------------------------- hash() -> [{doc, "Test all different hash functions"}]. hash(Config) when is_list(Config) -> diff --git a/lib/debugger/src/debugger.appup.src b/lib/debugger/src/debugger.appup.src index 7a435e9b22..81d2fab05a 100644 --- a/lib/debugger/src/debugger.appup.src +++ b/lib/debugger/src/debugger.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,5 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% -{"%VSN%",[],[]}. +{"%VSN%", + [{<<".*">>,[{restart_application, debugger}]}], + [{<<".*">>,[{restart_application, debugger}]}] +}. diff --git a/lib/debugger/test/bs_construct_SUITE.erl b/lib/debugger/test/bs_construct_SUITE.erl index 4870c87d74..8a6798c6ad 100644 --- a/lib/debugger/test/bs_construct_SUITE.erl +++ b/lib/debugger/test/bs_construct_SUITE.erl @@ -647,19 +647,27 @@ make_sub_bin(Bin0) -> %% give the same result. dynamic(Config) when is_list(Config) -> - ?line dynamic_1(fun dynamic_big/5), - ?line dynamic_1(fun dynamic_little/5), + Ps = [spawn_monitor(fun() -> + dynamic_1(Fun) + end) || Fun <- [fun dynamic_big/5, + fun dynamic_little/5]], + [receive + {'DOWN',Ref,process,Pid,normal} -> + ok; + {'DOWN',Ref,process,Pid,Exit} -> + ?t:fail({Pid,Exit}) + end || {Pid,Ref} <- Ps], ok. dynamic_1(Dynamic) -> - <<Lpad:128>> = erlang:md5([0]), - <<Rpad:128>> = erlang:md5([1]), - <<Int:128>> = erlang:md5([2]), - 8385 = dynamic_2(0, {Int,Lpad,Rpad,Dynamic}, 0). + <<Lpad:64,_/binary>> = erlang:md5([0]), + <<Rpad:64,_/binary>> = erlang:md5([1]), + <<Int:64,_/binary>> = erlang:md5([2]), + 2145 = dynamic_2(0, {Int,Lpad,Rpad,Dynamic}, 0). -dynamic_2(129, _, Count) -> Count; +dynamic_2(64+1, _, Count) -> Count; dynamic_2(Bef, Data, Count0) -> - Count = dynamic_3(Bef, 128-Bef, Data, Count0), + Count = dynamic_3(Bef, 64-Bef, Data, Count0), dynamic_2(Bef+1, Data, Count). dynamic_3(_, -1, _, Count) -> Count; @@ -680,13 +688,13 @@ dynamic_big(Bef, N, Int, Lpad, Rpad) -> <<MaskedInt:N>> = NumBin, %% Construct the binary in two different ways. - Bin = id(<<Lpad:Bef,NumBin/bitstring,Rpad:(128-Bef-N)>>), - Bin = <<Lpad:Bef,Int:N,Rpad:(128-Bef-N)>>, + Bin = id(<<Lpad:Bef,NumBin/bitstring,Rpad:(64-Bef-N)>>), + Bin = <<Lpad:Bef,Int:N,Rpad:(64-Bef-N)>>, %% Further verify the result by matching. LpadMasked = Lpad band ((1 bsl Bef) - 1), - RpadMasked = Rpad band ((1 bsl (128-Bef-N)) - 1), - Rbits = (128-Bef-N), + RpadMasked = Rpad band ((1 bsl (64-Bef-N)) - 1), + Rbits = (64-Bef-N), <<LpadMasked:Bef,MaskedInt:N,RpadMasked:Rbits>> = id(Bin), ok. @@ -696,13 +704,13 @@ dynamic_little(Bef, N, Int, Lpad, Rpad) -> <<MaskedInt:N/little>> = NumBin, %% Construct the binary in two different ways. - Bin = id(<<Lpad:Bef/little,NumBin/bitstring,Rpad:(128-Bef-N)/little>>), - Bin = <<Lpad:Bef/little,Int:N/little,Rpad:(128-Bef-N)/little>>, + Bin = id(<<Lpad:Bef/little,NumBin/bitstring,Rpad:(64-Bef-N)/little>>), + Bin = <<Lpad:Bef/little,Int:N/little,Rpad:(64-Bef-N)/little>>, %% Further verify the result by matching. LpadMasked = Lpad band ((1 bsl Bef) - 1), - RpadMasked = Rpad band ((1 bsl (128-Bef-N)) - 1), - Rbits = (128-Bef-N), + RpadMasked = Rpad band ((1 bsl (64-Bef-N)) - 1), + Rbits = (64-Bef-N), <<LpadMasked:Bef/little,MaskedInt:N/little,RpadMasked:Rbits/little>> = id(Bin), ok. diff --git a/lib/debugger/test/debugger_SUITE.erl b/lib/debugger/test/debugger_SUITE.erl index 6f5442e97d..c74550be86 100644 --- a/lib/debugger/test/debugger_SUITE.erl +++ b/lib/debugger/test/debugger_SUITE.erl @@ -27,13 +27,13 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - app_test/1,erts_debug/1,encrypted_debug_info/1, + app_test/1,appup_test/1,erts_debug/1,encrypted_debug_info/1, no_abstract_code/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app_test, erts_debug, no_abstract_code, + [app_test, appup_test, erts_debug, no_abstract_code, encrypted_debug_info]. groups() -> @@ -64,6 +64,9 @@ app_test(Config) when is_list(Config) -> ?line ?t:app_test(debugger), ok. +appup_test(Config) when is_list(Config) -> + ok = ?t:appup_test(debugger). + erts_debug(Config) when is_list(Config) -> c:l(erts_debug), ok. diff --git a/lib/dialyzer/src/dialyzer.appup.src b/lib/dialyzer/src/dialyzer.appup.src index 26d14ee8f4..1e293e407a 100644 --- a/lib/dialyzer/src/dialyzer.appup.src +++ b/lib/dialyzer/src/dialyzer.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,6 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% - -{"%VSN%",[],[]}. +{"%VSN%", + [{<<".*">>,[{restart_application, dialyzer}]}], + [{<<".*">>,[{restart_application, dialyzer}]}] +}. diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 0fbaf1d47c..2a633c5e37 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -2,7 +2,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -39,6 +39,8 @@ one_file_result/0, compile_result/0]). +-export_type([no_warn_unused/0]). + -include("dialyzer.hrl"). -record(analysis_state, @@ -48,7 +50,7 @@ defines = [] :: [dial_define()], doc_plt :: dialyzer_plt:plt(), include_dirs = [] :: [file:filename()], - no_warn_unused :: set(), + no_warn_unused :: no_warn_unused(), parent :: pid(), plt :: dialyzer_plt:plt(), start_from = byte_code :: start_from(), @@ -59,6 +61,8 @@ -record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}). +-type no_warn_unused() :: sets:set(mfa()). + %%-------------------------------------------------------------------- %% Main %%-------------------------------------------------------------------- @@ -168,7 +172,7 @@ analysis_start(Parent, Analysis) -> throw:{error, _ErrorMsg} = Error -> exit(Error) end, NewPlt0 = dialyzer_plt:insert_types(Plt, dialyzer_codeserver:get_records(NewCServer)), - ExpTypes = dialyzer_codeserver:get_exported_types(NewCServer), + ExpTypes = dialyzer_codeserver:get_exported_types(NewCServer), NewPlt1 = dialyzer_plt:insert_exported_types(NewPlt0, ExpTypes), State0 = State#analysis_state{plt = NewPlt1}, dump_callgraph(Callgraph, State0, Analysis), @@ -423,11 +427,8 @@ abs_get_nowarn(Abs, M) -> false -> [{M, F, A} || {function, _, F, A, _} <- Abs]; % all functions true -> - OnLoad = - lists:flatten([{M, F, A} || {attribute, _, on_load, {F, A}} <- Abs]), - OnLoad ++ [{M, F, A} || - {nowarn_unused_function, FAs} <- Opts, - {F, A} <- lists:flatten([FAs])] + [{M, F, A} || {nowarn_unused_function, FAs} <- Opts, + {F, A} <- lists:flatten([FAs])] end. get_exported_types_from_core(Core) -> diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index 2b9a69ce77..1d458b49fc 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,15 +40,17 @@ -type behaviour() :: atom(). +-type rectab() :: erl_types:type_table(). + -record(state, {plt :: dialyzer_plt:plt(), codeserver :: dialyzer_codeserver:codeserver(), filename :: file:filename(), behlines :: [{behaviour(), non_neg_integer()}], - records :: dict()}). + records :: rectab()}). %%-------------------------------------------------------------------- --spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], dict(), +-spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], rectab(), dialyzer_plt:plt(), dialyzer_codeserver:codeserver()) -> [dial_warning()]. diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index bc32110751..00b01fbd36 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -64,14 +64,16 @@ put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2, get_behaviour_api_calls/1, dispose_race_server/1, duplicate/1]). --export_type([callgraph/0, mfa_or_funlbl/0, callgraph_edge/0]). +-export_type([callgraph/0, mfa_or_funlbl/0, callgraph_edge/0, mod_deps/0]). -include("dialyzer.hrl"). %%---------------------------------------------------------------------- -type scc() :: [mfa_or_funlbl()]. --type mfa_calls() :: [{mfa_or_funlbl(), mfa_or_funlbl()}]. +-type mfa_call() :: {mfa_or_funlbl(), mfa_or_funlbl()}. +-type mfa_calls() :: [mfa_call()]. +-type mod_deps() :: dict:dict(module(), [module()]). %%----------------------------------------------------------------------------- %% A callgraph is a directed graph where the nodes are functions and a @@ -93,7 +95,7 @@ %% whenever applicable. %%----------------------------------------------------------------------------- --record(callgraph, {digraph = digraph:new() :: digraph(), +-record(callgraph, {digraph = digraph:new() :: digraph:graph(), active_digraph :: active_digraph(), esc :: ets:tid(), letrec_map :: ets:tid(), @@ -105,7 +107,7 @@ race_detection = false :: boolean(), race_data_server = new_race_data_server() :: pid()}). --record(race_data_state, {race_code = dict:new() :: dict(), +-record(race_data_state, {race_code = dict:new() :: dict:dict(), public_tables = [] :: [label()], named_tables = [] :: [string()], beh_api_calls = [] :: [{mfa(), mfa()}]}). @@ -114,7 +116,7 @@ -type callgraph() :: #callgraph{}. --type active_digraph() :: {'d', digraph()} | {'e', ets:tid(), ets:tid()}. +-type active_digraph() :: {'d', digraph:graph()} | {'e', ets:tid(), ets:tid()}. %%---------------------------------------------------------------------- @@ -222,7 +224,10 @@ non_local_calls(#callgraph{digraph = DG}) -> Edges = digraph_edges(DG), find_non_local_calls(Edges, sets:new()). --spec find_non_local_calls([{mfa_or_funlbl(), mfa_or_funlbl()}], set()) -> mfa_calls(). +-type call_tab() :: sets:set(mfa_call()). + +-spec find_non_local_calls([{mfa_or_funlbl(), mfa_or_funlbl()}], call_tab()) -> + mfa_calls(). find_non_local_calls([{{M,_,_}, {M,_,_}}|Left], Set) -> find_non_local_calls(Left, Set); @@ -267,7 +272,7 @@ get_required_by(SCC, #callgraph{active_digraph = {'d', DG}}) -> modules(#callgraph{digraph = DG}) -> ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]). --spec module_postorder(callgraph()) -> {[module()], {'d', digraph()}}. +-spec module_postorder(callgraph()) -> {[module()], {'d', digraph:graph()}}. module_postorder(#callgraph{digraph = DG}) -> Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), @@ -287,7 +292,7 @@ edge_fold(_, Set) -> Set. %% The module deps of a module are modules that depend on the module --spec module_deps(callgraph()) -> dict(). +-spec module_deps(callgraph()) -> mod_deps(). module_deps(#callgraph{digraph = DG}) -> Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), @@ -301,7 +306,7 @@ module_deps(#callgraph{digraph = DG}) -> digraph_delete(MDG), dict:from_list(Deps). --spec strip_module_deps(dict(), set()) -> dict(). +-spec strip_module_deps(mod_deps(), sets:set(module())) -> mod_deps(). strip_module_deps(ModDeps, StripSet) -> FilterFun1 = fun(Val) -> not sets:is_element(Val, StripSet) end, @@ -575,7 +580,7 @@ digraph_reaching_subgraph(Funs, DG) -> %% Races %%---------------------------------------------------------------------- --spec renew_race_info(callgraph(), dict(), [label()], [string()]) -> +-spec renew_race_info(callgraph(), dict:dict(), [label()], [string()]) -> callgraph(). renew_race_info(#callgraph{race_data_server = RaceDataServer} = CG, @@ -641,7 +646,7 @@ duplicate(#callgraph{race_data_server = RaceDataServer} = Callgraph) -> dispose_race_server(#callgraph{race_data_server = RaceDataServer}) -> race_data_server_cast(stop, RaceDataServer). --spec get_digraph(callgraph()) -> digraph(). +-spec get_digraph(callgraph()) -> digraph:graph(). get_digraph(#callgraph{digraph = Digraph}) -> Digraph. @@ -656,7 +661,7 @@ get_named_tables(#callgraph{race_data_server = RaceDataServer}) -> get_public_tables(#callgraph{race_data_server = RaceDataServer}) -> race_data_server_call(get_public_tables, RaceDataServer). --spec get_race_code(callgraph()) -> dict(). +-spec get_race_code(callgraph()) -> dict:dict(). get_race_code(#callgraph{race_data_server = RaceDataServer}) -> race_data_server_call(get_race_code, RaceDataServer). @@ -677,12 +682,12 @@ race_code_new(#callgraph{race_data_server = RaceDataServer} = CG) -> ok = race_data_server_cast(race_code_new, RaceDataServer), CG. --spec put_digraph(digraph(), callgraph()) -> callgraph(). +-spec put_digraph(digraph:graph(), callgraph()) -> callgraph(). put_digraph(Digraph, Callgraph) -> Callgraph#callgraph{digraph = Digraph}. --spec put_race_code(dict(), callgraph()) -> callgraph(). +-spec put_race_code(dict:dict(), callgraph()) -> callgraph(). put_race_code(RaceCode, #callgraph{race_data_server = RaceDataServer} = CG) -> ok = race_data_server_cast({put_race_code, RaceCode}, RaceDataServer), diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index cda801bf6c..3e68d64d53 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -40,7 +40,7 @@ external_calls = [] :: [mfa()], external_types = [] :: [mfa()], legal_warnings = ordsets:new() :: [dial_warn_tag()], - mod_deps = dict:new() :: dict(), + mod_deps = dict:new() :: dialyzer_callgraph:mod_deps(), output = standard_io :: io:device(), output_format = formatted :: format(), filename_opt = basename :: fopt(), diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index 216e06219c..aab3d6add6 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -64,6 +64,12 @@ -type dict_ets() :: ets:tid(). -type set_ets() :: ets:tid(). +-type types() :: erl_types:type_table(). +-type mod_records() :: dict:dict(module(), types()). + +-type contracts() :: dict:dict(mfa(),dialyzer_contracts:file_contract()). +-type mod_contracts() :: dict:dict(module(), contracts()). + -record(codeserver, {next_core_label = 0 :: label(), code :: dict_ets(), exported_types :: set_ets(), % set(mfa()) @@ -160,12 +166,12 @@ insert(Mod, ModCode, CS) -> true = ets:insert(CS#codeserver.code, [ModEntry|Funs]), CS. --spec get_temp_exported_types(codeserver()) -> set(). +-spec get_temp_exported_types(codeserver()) -> sets:set(mfa()). get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> ets_set_to_set(TempExpTypes). --spec insert_temp_exported_types(set(), codeserver()) -> codeserver(). +-spec insert_temp_exported_types(sets:set(mfa()), codeserver()) -> codeserver(). insert_temp_exported_types(Set, CS) -> TempExportedTypes = CS#codeserver.temp_exported_types, @@ -183,17 +189,17 @@ insert_exports(List, #codeserver{exports = Exports} = CS) -> is_exported(MFA, #codeserver{exports = Exports}) -> ets_set_is_element(MFA, Exports). --spec get_exported_types(codeserver()) -> set(). % set(mfa()) +-spec get_exported_types(codeserver()) -> sets:set(mfa()). get_exported_types(#codeserver{exported_types = ExpTypes}) -> ets_set_to_set(ExpTypes). --spec get_exports(codeserver()) -> set(). % set(mfa()) +-spec get_exports(codeserver()) -> sets:set(mfa()). get_exports(#codeserver{exports = Exports}) -> ets_set_to_set(Exports). --spec finalize_exported_types(set(), codeserver()) -> codeserver(). +-spec finalize_exported_types(sets:set(mfa()), codeserver()) -> codeserver(). finalize_exported_types(Set, CS) -> ExportedTypes = ets_read_concurrent_table(dialyzer_codeserver_exported_types), @@ -222,7 +228,7 @@ get_next_core_label(#codeserver{next_core_label = NCL}) -> set_next_core_label(NCL, CS) -> CS#codeserver{next_core_label = NCL}. --spec lookup_mod_records(atom(), codeserver()) -> dict(). +-spec lookup_mod_records(atom(), codeserver()) -> types(). lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) -> case ets_dict_find(Mod, RecDict) of @@ -230,12 +236,12 @@ lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) -> {ok, Dict} -> Dict end. --spec get_records(codeserver()) -> dict(). +-spec get_records(codeserver()) -> mod_records(). get_records(#codeserver{records = RecDict}) -> ets_dict_to_dict(RecDict). --spec store_temp_records(atom(), dict(), codeserver()) -> codeserver(). +-spec store_temp_records(module(), types(), codeserver()) -> codeserver(). store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS) when is_atom(Mod) -> @@ -244,12 +250,12 @@ store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS) false -> CS#codeserver{temp_records = ets_dict_store(Mod, Dict, TempRecDict)} end. --spec get_temp_records(codeserver()) -> dict(). +-spec get_temp_records(codeserver()) -> mod_records(). get_temp_records(#codeserver{temp_records = TempRecDict}) -> ets_dict_to_dict(TempRecDict). --spec set_temp_records(dict(), codeserver()) -> codeserver(). +-spec set_temp_records(mod_records(), codeserver()) -> codeserver(). set_temp_records(Dict, CS) -> true = ets:delete(CS#codeserver.temp_records), @@ -257,7 +263,7 @@ set_temp_records(Dict, CS) -> true = ets_dict_store_dict(Dict, TempRecords), CS#codeserver{temp_records = TempRecords}. --spec finalize_records(dict(), codeserver()) -> codeserver(). +-spec finalize_records(mod_records(), codeserver()) -> codeserver(). finalize_records(Dict, CS) -> true = ets:delete(CS#codeserver.temp_records), @@ -265,7 +271,7 @@ finalize_records(Dict, CS) -> true = ets_dict_store_dict(Dict, Records), CS#codeserver{records = Records, temp_records = clean}. --spec lookup_mod_contracts(atom(), codeserver()) -> dict(). +-spec lookup_mod_contracts(atom(), codeserver()) -> contracts(). lookup_mod_contracts(Mod, #codeserver{contracts = ContDict}) when is_atom(Mod) -> @@ -284,7 +290,7 @@ get_contract_pair(Key, ContDict) -> lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) -> ets_dict_find(MFA, ContDict). --spec get_contracts(codeserver()) -> dict(). +-spec get_contracts(codeserver()) -> mod_contracts(). get_contracts(#codeserver{contracts = ContDict}) -> ets_dict_to_dict(ContDict). @@ -294,7 +300,7 @@ get_contracts(#codeserver{contracts = ContDict}) -> get_callbacks(#codeserver{callbacks = CallbDict}) -> ets:tab2list(CallbDict). --spec store_temp_contracts(atom(), dict(), dict(), codeserver()) -> +-spec store_temp_contracts(module(), contracts(), contracts(), codeserver()) -> codeserver(). store_temp_contracts(Mod, SpecDict, CallbackDict, @@ -313,13 +319,14 @@ store_temp_contracts(Mod, SpecDict, CallbackDict, CS1#codeserver{temp_callbacks = ets_dict_store(Mod, CallbackDict, Cb)} end. --spec get_temp_contracts(codeserver()) -> {dict(), dict()}. +-spec get_temp_contracts(codeserver()) -> {mod_contracts(), mod_contracts()}. get_temp_contracts(#codeserver{temp_contracts = TempContDict, temp_callbacks = TempCallDict}) -> {ets_dict_to_dict(TempContDict), ets_dict_to_dict(TempCallDict)}. --spec finalize_contracts(dict(), dict(), codeserver()) -> codeserver(). +-spec finalize_contracts(mod_contracts(), mod_contracts(), codeserver()) -> + codeserver(). finalize_contracts(SpecDict, CallbackDict, CS) -> Contracts = ets_read_concurrent_table(dialyzer_codeserver_contracts), diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 3467ab4e65..46eaeaa303 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -52,7 +52,7 @@ %% to expand records and/or remote types that they might contain. %%----------------------------------------------------------------------- --type tmp_contract_fun() :: fun((set(), dict()) -> contract_pair()). +-type tmp_contract_fun() :: fun((sets:set(mfa()), types()) -> contract_pair()). -record(tmp_contract, {contract_funs = [] :: [tmp_contract_fun()], forms = [] :: [{_, _}]}). @@ -163,8 +163,10 @@ process_contract_remote_types(CodeServer) -> -type opaques() :: [erl_types:erl_type()] | 'universe'. -type opaques_fun() :: fun((module()) -> opaques()). +-type fun_types() :: dict:dict(label(), erl_types:type_table()). + -spec check_contracts([{mfa(), file_contract()}], - dialyzer_callgraph:callgraph(), dict(), + dialyzer_callgraph:callgraph(), fun_types(), opaques_fun()) -> plt_contracts(). check_contracts(Contracts, Callgraph, FunTypes, FindOpaques) -> @@ -292,7 +294,8 @@ is_not_nil_list(Type) -> erl_types:t_is_list(Type) andalso not erl_types:t_is_nil(Type). %% This is the heart of the "range function" --spec process_contracts([contract_pair()], [erl_types:erl_type()]) -> erl_types:erl_type(). +-spec process_contracts([contract_pair()], [erl_types:erl_type()]) -> + erl_types:erl_type(). process_contracts(OverContracts, Args) -> process_contracts(OverContracts, Args, erl_types:t_none()). @@ -307,7 +310,8 @@ process_contracts([OverContract|Left], Args, AccRange) -> process_contracts([], _Args, AccRange) -> AccRange. --spec process_contract(contract_pair(), [erl_types:erl_type()]) -> 'error' | {'ok', erl_types:erl_type()}. +-spec process_contract(contract_pair(), [erl_types:erl_type()]) -> + 'error' | {'ok', erl_types:erl_type()}. process_contract({Contract, Constraints}, CallTypes0) -> CallTypesFun = erl_types:t_fun(CallTypes0, erl_types:t_any()), @@ -343,8 +347,11 @@ solve_constraints(Contract, Call, Constraints) -> %% ?debug("Inf: ~s\n", [erl_types:t_to_string(Inf)]), %% erl_types:t_assign_variables_to_subtype(Contract, Inf). +-type contracts() :: dict:dict(mfa(),dialyzer_contracts:file_contract()). + %% Checks the contracts for functions that are not implemented --spec contracts_without_fun(dict(), [_], dialyzer_callgraph:callgraph()) -> [dial_warning()]. +-spec contracts_without_fun(contracts(), [_], dialyzer_callgraph:callgraph()) -> + [dial_warning()]. contracts_without_fun(Contracts, AllFuns0, Callgraph) -> AllFuns1 = [{dialyzer_callgraph:lookup_name(Label, Callgraph), Arity} @@ -377,7 +384,10 @@ insert_constraints([{subtype, Type1, Type2}|Left], Dict) -> end; insert_constraints([], Dict) -> Dict. --spec store_tmp_contract(mfa(), file_line(), [_], dict(), dict()) -> dict(). +-type types() :: erl_types:type_table(). + +-spec store_tmp_contract(mfa(), file_line(), [_], contracts(), types()) -> + contracts(). store_tmp_contract(MFA, FileLine, TypeSpec, SpecDict, RecordsDict) -> %% io:format("contract from form: ~p\n", [TypeSpec]), @@ -404,7 +414,8 @@ contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], RecDict, throw({error, NewMsg}) end, NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords), - {NewType, []} + NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType), + {NewTypeNoVars, []} end, NewTypeAcc = [TypeFun | TypeAcc], NewFormAcc = [{Form, []} | FormAcc], @@ -418,7 +429,8 @@ contract_from_form([{type, _L1, bounded_fun, process_constraints(Constr, RecDict, ExpTypes, AllRecords), Type = erl_types:t_from_form(Form, RecDict, VarDict), NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords), - {NewType, Constr1} + NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType), + {NewTypeNoVars, Constr1} end, NewTypeAcc = [TypeFun | TypeAcc], NewFormAcc = [{Form, Constr} | FormAcc], diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index b00e0465e0..692684cd99 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -86,39 +86,53 @@ %%-------------------------------------------------------------------- +-type type() :: erl_types:erl_type(). +-type types() :: erl_types:type_table(). + -define(no_arg, no_arg). -define(TYPE_LIMIT, 3). -record(state, {callgraph :: dialyzer_callgraph:callgraph(), - envs :: dict(), - fun_tab :: dict(), + envs :: env_tab(), + fun_tab :: fun_tab(), plt :: dialyzer_plt:plt(), - opaques :: [erl_types:erl_type()], + opaques :: [type()], races = dialyzer_races:new() :: dialyzer_races:races(), - records = dict:new() :: dict(), - tree_map :: dict(), + records = dict:new() :: types(), + tree_map :: dict:dict(label(), cerl:cerl()), warning_mode = false :: boolean(), warnings = [] :: [dial_warning()], - work :: {[_], [_], set()}, + work :: {[_], [_], sets:set()}, module :: module() }). --record(map, {dict = dict:new() :: dict(), - subst = dict:new() :: dict(), +-record(map, {dict = dict:new() :: type_tab(), + subst = dict:new() :: subst_tab(), modified = [] :: [Key :: term()], modified_stack = [] :: [{[Key :: term()],reference()}], ref = undefined :: reference() | undefined}). +-type nowarn() :: dialyzer_analysis_callgraph:no_warn_unused(). +-type env_tab() :: dict:dict(label(), #map{}). +-type fun_entry() :: {Args :: [type()], RetType :: type()}. +-type fun_tab() :: dict:dict('top' | label(), + {'not_handled', fun_entry()} | fun_entry()). +-type key() :: label() | cerl:cerl(). +-type type_tab() :: dict:dict(key(), type()). +-type subst_tab() :: dict:dict(key(), cerl:cerl()). + %% Exported Types -opaque state() :: #state{}. %%-------------------------------------------------------------------- +-type fun_types() :: dict:dict(label(), type()). + -spec get_warnings(cerl:c_module(), dialyzer_plt:plt(), - dialyzer_callgraph:callgraph(), dict(), set()) -> - {[dial_warning()], dict()}. + dialyzer_callgraph:callgraph(), types(), nowarn()) -> + {[dial_warning()], fun_types()}. get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> State1 = analyze_module(Tree, Plt, Callgraph, Records, true), @@ -129,7 +143,8 @@ get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> {State4#state.warnings, state__all_fun_types(State4)}. -spec get_fun_types(cerl:c_module(), dialyzer_plt:plt(), - dialyzer_callgraph:callgraph(), dict()) -> dict(). + dialyzer_callgraph:callgraph(), + types()) -> fun_types(). get_fun_types(Tree, Plt, Callgraph, Records) -> State = analyze_module(Tree, Plt, Callgraph, Records, false), @@ -2299,7 +2314,7 @@ bind_guard_list([], Map, _Env, _Eval, _State, Acc) -> -type eval() :: 'pos' | 'neg' | 'dont_know'. --spec signal_guard_fail(eval(), cerl:c_call(), [erl_types:erl_type()], +-spec signal_guard_fail(eval(), cerl:c_call(), [type()], state()) -> no_return(). signal_guard_fail(Eval, Guard, ArgTypes, State) -> @@ -3161,7 +3176,7 @@ state__get_callgraph(#state{callgraph = Callgraph}) -> state__get_races(#state{races = Races}) -> Races. --spec state__get_records(state()) -> dict(). +-spec state__get_records(state()) -> types(). state__get_records(#state{records = Records}) -> Records. @@ -3260,7 +3275,7 @@ get_file([_|Tail]) -> get_file(Tail). is_compiler_generated(Ann) -> lists:member(compiler_generated, Ann) orelse (get_line(Ann) < 1). --spec format_args([cerl:cerl()], [erl_types:erl_type()], state()) -> +-spec format_args([cerl:cerl()], [type()], state()) -> nonempty_string(). format_args([], [], _State) -> @@ -3295,17 +3310,17 @@ format_arg(Arg) -> Default end. --spec format_type(erl_types:erl_type(), state()) -> string(). +-spec format_type(type(), state()) -> string(). format_type(Type, #state{records = R}) -> t_to_string(Type, R). --spec format_field_diffs(erl_types:erl_type(), state()) -> string(). +-spec format_field_diffs(type(), state()) -> string(). format_field_diffs(RecConstruction, #state{records = R}) -> erl_types:record_field_diffs_to_string(RecConstruction, R). --spec format_sig_args(erl_types:erl_type(), state()) -> string(). +-spec format_sig_args(type(), state()) -> string(). format_sig_args(Type, #state{opaques = Opaques} = State) -> SigArgs = t_fun_args(Type, Opaques), diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl index a81ea1a98b..f1ac41ff04 100644 --- a/lib/dialyzer/src/dialyzer_dep.erl +++ b/lib/dialyzer/src/dialyzer_dep.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -59,13 +59,13 @@ %% -spec analyze(cerl:c_module()) -> - {dict(), ordset('external' | label()), dict(), dict()}. + {dict:dict(), ordset('external' | label()), dict:dict(), dict:dict()}. analyze(Tree) -> %% io:format("Handling ~w\n", [cerl:atom_val(cerl:module_name(Tree))]), {_, State} = traverse(Tree, map__new(), state__new(Tree), top), Esc = state__esc(State), - %% Add dependency from 'external' to all escaping function + %% Add dependency from 'external' to all escaping functions State1 = state__add_deps(external, output(Esc), State), Deps = state__deps(State1), Calls = state__calls(State1), @@ -309,7 +309,7 @@ primop(Tree, ArgFuns, State) -> %% Set %% --record(set, {set :: set()}). +-record(set, {set :: sets:set()}). set__singleton(Val) -> #set{set = sets:add_element(Val, sets:new())}. @@ -478,19 +478,30 @@ all_vars(Tree, AccIn) -> -type local_set() :: 'none' | #set{}. --record(state, {deps :: dict(), +-record(state, {deps :: dict:dict(), esc :: local_set(), - call :: dict(), - arities :: dict(), - letrecs :: dict()}). + call :: dict:dict(), + arities :: dict:dict(), + letrecs :: dict:dict()}). state__new(Tree) -> Exports = set__from_list([X || X <- cerl:module_exports(Tree)]), - InitEsc = set__from_list([cerl_trees:get_label(Fun) - || {Var, Fun} <- cerl:module_defs(Tree), - set__is_element(Var, Exports)]), + %% get the labels of all exported functions + ExpLs = [cerl_trees:get_label(Fun) || {Var, Fun} <- cerl:module_defs(Tree), + set__is_element(Var, Exports)], + %% make sure to also initiate an analysis from all functions called + %% from on_load attributes; in Core these exist as a list of {F,A} pairs + OnLoadFAs = lists:flatten([cerl:atom_val(Args) + || {Attr, Args} <- cerl:module_attrs(Tree), + cerl:atom_val(Attr) =:= on_load]), + OnLoadLs = [cerl_trees:get_label(Fun) + || {Var, Fun} <- cerl:module_defs(Tree), + lists:member(cerl:var_name(Var), OnLoadFAs)], + %% init the escaping function labels to exported + called from on_load + InitEsc = set__from_list(OnLoadLs ++ ExpLs), Arities = cerl_trees:fold(fun find_arities/2, dict:new(), Tree), - #state{deps = map__new(), esc = InitEsc, call = map__new(), arities = Arities, letrecs = map__new()}. + #state{deps = map__new(), esc = InitEsc, call = map__new(), + arities = Arities, letrecs = map__new()}. find_arities(Tree, AccMap) -> case cerl:is_c_fun(Tree) of diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 5f64099210..63798f44b1 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -2,7 +2,7 @@ %%---------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -67,7 +67,7 @@ %%---------------------------------------------------------------------- --type mod_deps() :: dict(). +-type mod_deps() :: dialyzer_callgraph:mod_deps(). -type deep_string() :: string() | [deep_string()]. @@ -80,11 +80,11 @@ %%---------------------------------------------------------------------- --record(plt, {info = table_new() :: dict(), - types = table_new() :: dict(), - contracts = table_new() :: dict(), - callbacks = table_new() :: dict(), - exported_types = sets:new() :: set()}). +-record(plt, {info = table_new() :: dict:dict(), + types = table_new() :: dict:dict(), + contracts = table_new() :: dict:dict(), + callbacks = table_new() :: dict:dict(), + exported_types = sets:new() :: sets:set()}). -record(mini_plt, {info :: ets:tid(), contracts :: ets:tid(), @@ -96,15 +96,15 @@ -include("dialyzer.hrl"). -type file_md5() :: {file:filename(), binary()}. --type plt_info() :: {[file_md5()], dict()}. +-type plt_info() :: {[file_md5()], dict:dict()}. -record(file_plt, {version = "" :: string(), file_md5_list = [] :: [file_md5()], - info = dict:new() :: dict(), - contracts = dict:new() :: dict(), - callbacks = dict:new() :: dict(), - types = dict:new() :: dict(), - exported_types = sets:new() :: set(), + info = dict:new() :: dict:dict(), + contracts = dict:new() :: dict:dict(), + callbacks = dict:new() :: dict:dict(), + types = dict:new() :: dict:dict(), + exported_types = sets:new() :: sets:set(), mod_deps :: mod_deps(), implementation_md5 = [] :: [file_md5()]}). @@ -184,22 +184,22 @@ lookup(Plt, Label) when is_integer(Label) -> lookup_1(#mini_plt{info = Info}, MFAorLabel) -> ets_table_lookup(Info, MFAorLabel). --spec insert_types(plt(), dict()) -> plt(). +-spec insert_types(plt(), dict:dict()) -> plt(). insert_types(PLT, Rec) -> PLT#plt{types = Rec}. --spec insert_exported_types(plt(), set()) -> plt(). +-spec insert_exported_types(plt(), sets:set()) -> plt(). insert_exported_types(PLT, Set) -> PLT#plt{exported_types = Set}. --spec get_types(plt()) -> dict(). +-spec get_types(plt()) -> dict:dict(). get_types(#plt{types = Types}) -> Types. --spec get_exported_types(plt()) -> set(). +-spec get_exported_types(plt()) -> sets:set(). get_exported_types(#plt{exported_types = ExpTypes}) -> ExpTypes. @@ -211,7 +211,7 @@ get_exported_types(#plt{exported_types = ExpTypes}) -> lookup_module(#plt{info = Info}, M) when is_atom(M) -> table_lookup_module(Info, M). --spec all_modules(plt()) -> set(). +-spec all_modules(plt()) -> sets:set(). all_modules(#plt{info = Info, contracts = Cs}) -> sets:union(table_all_modules(Info), table_all_modules(Cs)). diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl index 2aa8343bce..48fcde8014 100644 --- a/lib/dialyzer/src/dialyzer_races.erl +++ b/lib/dialyzer/src/dialyzer_races.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -98,14 +98,14 @@ def_vars :: [core_vars()], arg_types :: [erl_types:erl_type()], call_vars :: [core_vars()], - var_map :: dict()}). + var_map :: dict:dict()}). -record(dep_call, {call_name :: dep_calls(), args :: args(), arg_types :: [erl_types:erl_type()], vars :: [core_vars()], state :: _, %% XXX: recursive file_line :: file_line(), - var_map :: dict()}). + var_map :: dict:dict()}). -record(fun_call, {caller :: dialyzer_callgraph:mfa_or_funlbl(), callee :: dialyzer_callgraph:mfa_or_funlbl(), arg_types :: [erl_types:erl_type()], @@ -114,7 +114,7 @@ arg :: var_to_map1()}). -record(warn_call, {call_name :: warn_calls(), args :: args(), - var_map :: dict()}). + var_map :: dict:dict()}). -type case_tags() :: 'beg_case' | #beg_clause{} | #end_clause{} | #end_case{}. -type code() :: [#dep_call{} | #fun_call{} | #warn_call{} | @@ -1565,7 +1565,7 @@ any_args(StrList) -> end end. --spec bind_dict_vars(label(), label(), dict()) -> dict(). +-spec bind_dict_vars(label(), label(), dict:dict()) -> dict:dict(). bind_dict_vars(Key, Label, RaceVarMap) -> case Key =:= Label of @@ -1751,7 +1751,7 @@ compare_vars(Var1, Var2, RaceVarMap) when is_integer(Var1), is_integer(Var2) -> compare_vars(_Var1, _Var2, _RaceVarMap) -> false. --spec compare_var_list(label_type(), [label_type()], dict()) -> boolean(). +-spec compare_var_list(label_type(), [label_type()], dict:dict()) -> boolean(). compare_var_list(Var, VarList, RaceVarMap) -> lists:any(fun (V) -> compare_vars(Var, V, RaceVarMap) end, VarList). @@ -1956,7 +1956,8 @@ mnesia_tuple_argtypes(TupleStr) -> [TupleStr2|_T] = string:tokens(TupleStr1, " ,"), lists:flatten(string:tokens(TupleStr2, " |")). --spec race_var_map(var_to_map1(), var_to_map2(), dict(), op()) -> dict(). +-spec race_var_map(var_to_map1(), var_to_map2(), dict:dict(), op()) -> + dict:dict(). race_var_map(Vars1, Vars2, RaceVarMap, Op) -> case Vars1 =:= ?no_arg orelse Vars1 =:= ?bypassed diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index f0488b5ee3..ef9b00e203 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -72,7 +72,7 @@ -record(st, {callgraph :: dialyzer_callgraph:callgraph(), codeserver :: dialyzer_codeserver:codeserver(), - no_warn_unused :: set(), + no_warn_unused :: sets:set(mfa()), parent = none :: parent(), timing_server :: dialyzer_timing:timing_server(), solvers :: [solver()], @@ -137,7 +137,7 @@ get_refined_success_typings(SCCs, #st{callgraph = Callgraph, -type doc_plt() :: 'undefined' | dialyzer_plt:plt(). -spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(), - doc_plt(), dialyzer_codeserver:codeserver(), set(), + doc_plt(), dialyzer_codeserver:codeserver(), sets:set(mfa()), dialyzer_timing:timing_server(), [solver()], pid()) -> {[dial_warning()], dialyzer_plt:plt(), doc_plt()}. diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index 70e14781f7..31ceaf5ac5 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -83,7 +83,7 @@ list :: [constr()], deps :: [dep()], masks :: [{dep(),[non_neg_integer()]}] | - {'d',dict()}, + {'d',dict:dict(dep(), [non_neg_integer()])}, id :: {'list', dep()}}). -type constraint_list() :: #constraint_list{}. @@ -94,25 +94,30 @@ -type constr() :: constraint() | constraint_list() | constraint_ref(). --type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, dict()}]. +-type types() :: erl_types:type_table(). + +-type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, types()}]. -type typesig_funmap() :: [{type_var(), type_var()}]. %% Orddict --type dict_or_ets() :: {'d', dict()} | {'e', ets:tid()}. +-type prop_types() :: dict:dict(label(), types()). + +-type dict_or_ets() :: {'d', prop_types()} | {'e', ets:tid()}. -record(state, {callgraph :: dialyzer_callgraph:callgraph(), cs = [] :: [constr()], cmap = {'d', dict:new()} :: dict_or_ets(), fun_map = [] :: typesig_funmap(), - fun_arities = dict:new() :: dict(), + fun_arities = dict:new() :: dict:dict(type_var(), arity()), in_match = false :: boolean(), in_guard = false :: boolean(), module :: module(), - name_map = dict:new() :: dict(), + name_map = dict:new() :: dict:dict(mfa(), + cerl:c_fun()), next_label = 0 :: label(), self_rec :: 'false' | erl_types:erl_type(), plt :: dialyzer_plt:plt(), prop_types = {'d', dict:new()} :: dict_or_ets(), - records = dict:new() :: dict(), + records = dict:new() :: types(), scc = [] :: [type_var()], mfas :: [tuple()], solvers = [] :: [solver()] @@ -167,7 +172,7 @@ -spec analyze_scc(typesig_scc(), label(), dialyzer_callgraph:callgraph(), - dialyzer_plt:plt(), dict(), [solver()]) -> dict(). + dialyzer_plt:plt(), prop_types(), [solver()]) -> prop_types(). analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) -> Solvers = solvers(Solvers0), @@ -1890,7 +1895,7 @@ sane_maps(Map1, Map2, Keys, _S1, _S2) -> %% Solver v2 --record(v2_state, {constr_data = dict:new() :: dict(), +-record(v2_state, {constr_data = dict:new() :: dict:dict(), state :: #state{}}). v2_solve_ref(Fun, Map, State) -> diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index a4c4d37a0f..95a138dd73 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -164,14 +164,14 @@ get_core_from_abstract_code(AbstrCode, Opts) -> %% ============================================================================ -spec get_record_and_type_info(abstract_code()) -> - {'ok', dict()} | {'error', string()}. + {'ok', dict:dict()} | {'error', string()}. get_record_and_type_info(AbstractCode) -> Module = get_module(AbstractCode), get_record_and_type_info(AbstractCode, Module, dict:new()). --spec get_record_and_type_info(abstract_code(), module(), dict()) -> - {'ok', dict()} | {'error', string()}. +-spec get_record_and_type_info(abstract_code(), module(), dict:dict()) -> + {'ok', dict:dict()} | {'error', string()}. get_record_and_type_info(AbstractCode, Module, RecDict) -> get_record_and_type_info(AbstractCode, Module, [], RecDict). @@ -304,7 +304,7 @@ process_record_remote_types(CServer) -> CServer1 = dialyzer_codeserver:finalize_records(NewRecords, CServer), dialyzer_codeserver:finalize_exported_types(TempExpTypes, CServer1). --spec merge_records(dict(), dict()) -> dict(). +-spec merge_records(dict:dict(), dict:dict()) -> dict:dict(). merge_records(NewRecords, OldRecords) -> dict:merge(fun(_Key, NewVal, _OldVal) -> NewVal end, NewRecords, OldRecords). @@ -315,10 +315,10 @@ merge_records(NewRecords, OldRecords) -> %% %% ============================================================================ --type spec_dict() :: dict(). --type callback_dict() :: dict(). +-type spec_dict() :: dict:dict(). +-type callback_dict() :: dict:dict(). --spec get_spec_info(atom(), abstract_code(), dict()) -> +-spec get_spec_info(atom(), abstract_code(), dict:dict()) -> {'ok', spec_dict(), callback_dict()} | {'error', string()}. get_spec_info(ModName, AbstractCode, RecordsDict) -> @@ -383,7 +383,7 @@ get_spec_info([], SpecDict, CallbackDict, _RecordsDict, _ModName, _File) -> %% %% ============================================================================ --spec sets_filter([module()], set()) -> set(). +-spec sets_filter([module()], sets:set()) -> sets:set(). sets_filter([], ExpTypes) -> ExpTypes; @@ -434,7 +434,7 @@ format_errors([]) -> format_sig(Type) -> format_sig(Type, dict:new()). --spec format_sig(erl_types:erl_type(), dict()) -> string(). +-spec format_sig(erl_types:erl_type(), dict:dict()) -> string(). format_sig(Type, RecDict) -> "fun(" ++ Sig = lists:flatten(erl_types:t_to_string(Type, RecDict)), diff --git a/lib/dialyzer/test/dialyzer_SUITE.erl b/lib/dialyzer/test/dialyzer_SUITE.erl index 63214c915d..1b62291a00 100644 --- a/lib/dialyzer/test/dialyzer_SUITE.erl +++ b/lib/dialyzer/test/dialyzer_SUITE.erl @@ -30,12 +30,12 @@ -export([init_per_testcase/2, end_per_testcase/2]). %% Test cases must be exported. --export([app_test/1]). +-export([app_test/1, appup_test/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app_test]. + [app_test, appup_test]. groups() -> []. @@ -71,3 +71,7 @@ app_test(suite) -> []; app_test(Config) when is_list(Config) -> ?line ?t:app_test(dialyzer). + +%% Test that the .appup file does not contain any `basic' errors +appup_test(Config) when is_list(Config) -> + ok = ?t:appup_test(dialyzer). diff --git a/lib/dialyzer/test/file_utils.erl b/lib/dialyzer/test/file_utils.erl index 36b368760c..e1314ec8de 100644 --- a/lib/dialyzer/test/file_utils.erl +++ b/lib/dialyzer/test/file_utils.erl @@ -106,7 +106,7 @@ lcs_fast(S1, S2) -> -spec lcs_fast([string()], [string()], pos_integer(), pos_integer(), - non_neg_integer(), array()) -> {[string()], array()}. + non_neg_integer(), array:array()) -> {[string()], array:array()}. lcs_fast([], _, _, _, _, Acc) -> {[], Acc}; diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/array b/lib/dialyzer/test/opaque_SUITE_data/results/array index b05d088a03..9921b61669 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/array +++ b/lib/dialyzer/test/opaque_SUITE_data/results/array @@ -1,3 +1,3 @@ -array_use.erl:12: The type test is_tuple(array()) breaks the opaqueness of the term array() -array_use.erl:9: The attempt to match a term of type array() against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term +array_use.erl:12: The type test is_tuple(array:array(_)) breaks the opaqueness of the term array:array(_) +array_use.erl:9: The attempt to match a term of type array:array(_) against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/dict b/lib/dialyzer/test/opaque_SUITE_data/results/dict index 5c6bf6a927..42f6663191 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/dict +++ b/lib/dialyzer/test/opaque_SUITE_data/results/dict @@ -1,15 +1,15 @@ -dict_use.erl:41: The attempt to match a term of type dict() against the pattern 'gazonk' breaks the opaqueness of the term -dict_use.erl:45: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term -dict_use.erl:46: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term -dict_use.erl:51: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term -dict_use.erl:52: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term -dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict() -dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict() -dict_use.erl:64: Guard test length(D::dict()) breaks the opaqueness of its argument -dict_use.erl:65: Guard test is_atom(D::dict()) breaks the opaqueness of its argument -dict_use.erl:66: Guard test is_list(D::dict()) breaks the opaqueness of its argument -dict_use.erl:70: The type test is_list(dict()) breaks the opaqueness of the term dict() -dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict() as 2nd argument +dict_use.erl:41: The attempt to match a term of type dict:dict(_,_) against the pattern 'gazonk' breaks the opaqueness of the term +dict_use.erl:45: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opaqueness of the term +dict_use.erl:46: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opaqueness of the term +dict_use.erl:51: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opaqueness of the term +dict_use.erl:52: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opaqueness of the term +dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict:dict(_,_) +dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict:dict(_,_) +dict_use.erl:64: Guard test length(D::dict:dict(_,_)) breaks the opaqueness of its argument +dict_use.erl:65: Guard test is_atom(D::dict:dict(_,_)) breaks the opaqueness of its argument +dict_use.erl:66: Guard test is_list(D::dict:dict(_,_)) breaks the opaqueness of its argument +dict_use.erl:70: The type test is_list(dict:dict(_,_)) breaks the opaqueness of the term dict:dict(_,_) +dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict:dict(_,_) as 2nd argument dict_use.erl:76: The call dict:merge(Fun::any(),42,[1 | 2,...]) does not have opaque terms as 2nd and 3rd arguments -dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict() as 3rd argument +dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict:dict(_,_) as 3rd argument diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/ets b/lib/dialyzer/test/opaque_SUITE_data/results/ets index e79696bc30..e11c7a8352 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/ets +++ b/lib/dialyzer/test/opaque_SUITE_data/results/ets @@ -1,4 +1,4 @@ -ets_use.erl:12: Guard test is_integer(T::atom() | tid()) breaks the opaqueness of its argument -ets_use.erl:20: The type test is_integer(atom() | tid()) breaks the opaqueness of the term atom() | tid() -ets_use.erl:7: Guard test is_integer(T::tid()) breaks the opaqueness of its argument +ets_use.erl:12: Guard test is_integer(T::atom() | ets:tid()) breaks the opaqueness of its argument +ets_use.erl:20: The type test is_integer(atom() | ets:tid()) breaks the opaqueness of the term atom() | ets:tid() +ets_use.erl:7: Guard test is_integer(T::ets:tid()) breaks the opaqueness of its argument diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/ewgi b/lib/dialyzer/test/opaque_SUITE_data/results/ewgi index 5bc6b87fbb..209f27b2f2 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/ewgi +++ b/lib/dialyzer/test/opaque_SUITE_data/results/ewgi @@ -1,4 +1,4 @@ -ewgi_api.erl:55: The call gb_trees:to_list({non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_tree() as 1st argument -ewgi_testapp.erl:35: The call ewgi_testapp:htmlise_data("request_data",{non_neg_integer(),'nil' | {_,_,_,_}}) does not have a term of type [{_,_}] | gb_tree() (with opaque subterms) as 2nd argument -ewgi_testapp.erl:43: The call gb_trees:to_list(T::{non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_tree() as 1st argument +ewgi_api.erl:55: The call gb_trees:to_list({non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_trees:tree(_,_) as 1st argument +ewgi_testapp.erl:35: The call ewgi_testapp:htmlise_data("request_data",{non_neg_integer(),'nil' | {_,_,_,_}}) does not have a term of type [{_,_}] | gb_trees:tree(_,_) (with opaque subterms) as 2nd argument +ewgi_testapp.erl:43: The call gb_trees:to_list(T::{non_neg_integer(),'nil' | {_,_,_,_}}) does not have an opaque term of type gb_trees:tree(_,_) as 1st argument diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1 b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1 index 4fe5fcfe2d..ac5ef14041 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1 +++ b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1 @@ -2,4 +2,4 @@ inf_loop1.erl:119: The pattern [{_, LNorms}] can never match the type [] inf_loop1.erl:121: The pattern [{LinksA, LNormA}, {LinksB, LNormB}] can never match the type [] inf_loop1.erl:129: The pattern [{_, Norm} | _] can never match the type [] -inf_loop1.erl:71: The call gb_trees:get(Edge::any(),Etab::array()) does not have an opaque term of type gb_tree() as 2nd argument +inf_loop1.erl:71: The call gb_trees:get(Edge::any(),Etab::array:array(_)) does not have an opaque term of type gb_trees:tree(_,_) as 2nd argument diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2 b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2 index 4f0b79eb35..8cd2abe8cd 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2 +++ b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop2 @@ -2,4 +2,4 @@ inf_loop2.erl:122: The pattern [{_, LNorms}] can never match the type [] inf_loop2.erl:124: The pattern [{LinksA, LNormA}, {LinksB, LNormB}] can never match the type [] inf_loop2.erl:132: The pattern [{_, Norm} | _] can never match the type [] -inf_loop2.erl:74: The call gb_trees:get(Edge::any(),Etab::array()) does not have an opaque term of type gb_tree() as 2nd argument +inf_loop2.erl:74: The call gb_trees:get(Edge::any(),Etab::array:array(_)) does not have an opaque term of type gb_trees:tree(_,_) as 2nd argument diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/para b/lib/dialyzer/test/opaque_SUITE_data/results/para new file mode 100644 index 0000000000..3aaa238de6 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/results/para @@ -0,0 +1,21 @@ + +para1.erl:18: The test para1:t(atom()) =:= para1:t(integer()) can never evaluate to 'true' +para1.erl:23: The test para1:t(atom()) =:= para1:t() can never evaluate to 'true' +para1.erl:28: The test para1:t() =:= para1:t(integer()) can never evaluate to 'true' +para1.erl:33: The test {3,2} =:= {'a','b'} can never evaluate to 'true' +para1.erl:38: Attempt to test for equality between a term of type para1_adt:t(integer()) and a term of opaque type para1_adt:t(atom()) +para1.erl:43: Attempt to test for equality between a term of type para1_adt:t() and a term of opaque type para1_adt:t(atom()) +para1.erl:48: Attempt to test for equality between a term of type para1_adt:t(integer()) and a term of opaque type para1_adt:t() +para1.erl:53: The test {3,2} =:= {'a','b'} can never evaluate to 'true' +para2.erl:103: Attempt to test for equality between a term of type para2_adt:circ({{integer(),integer()},{integer(),integer()}},{{integer(),integer()},{integer(),integer()}}) and a term of opaque type para2_adt:circ({{integer(),integer()},{integer(),integer()}}) +para2.erl:117: Attempt to test for equality between a term of type para2_adt:un(atom(),integer()) and a term of opaque type para2_adt:un(integer(),atom()) +para2.erl:31: The test 'a' =:= 'b' can never evaluate to 'true' +para2.erl:61: Attempt to test for equality between a term of type para2_adt:c2() and a term of opaque type para2_adt:c1() +para2.erl:66: The test 'a' =:= 'b' can never evaluate to 'true' +para2.erl:88: The test para2:circ({{integer(),integer()},{integer(),integer()}}) =:= para2:circ({{integer(),integer()},{integer(),integer()}},{{integer(),integer()},{integer(),integer()}}) can never evaluate to 'true' +para3.erl:28: Invalid type specification for function para3:ot2/0. The success typing is () -> 'foo' +para3.erl:36: The pattern {{{17}}} can never match the type {{{{{{_,_,_,_,_}}}}}} +para3.erl:55: Invalid type specification for function para3:t2/0. The success typing is () -> 'foo' +para3.erl:65: The attempt to match a term of type {{{{{para3_adt:ot1(_,_,_,_,_)}}}}} against the pattern {{{{{17}}}}} breaks the opaqueness of para3_adt:ot1(_,_,_,_,_) +para3.erl:68: The pattern {{{{17}}}} can never match the type {{{{{para3_adt:ot1(_,_,_,_,_)}}}}} +para3.erl:74: Invalid type specification for function para3:exp_adt/0. The success typing is () -> 3 diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/queue b/lib/dialyzer/test/opaque_SUITE_data/results/queue index 59ce33f098..5b3813c418 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/queue +++ b/lib/dialyzer/test/opaque_SUITE_data/results/queue @@ -1,11 +1,11 @@ -queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue() as 1st argument -queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue() as 2nd argument -queue_use.erl:27: The attempt to match a term of type queue() against the pattern {"*", Q2} breaks the opaqueness of the term -queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue() -queue_use.erl:36: The attempt to match a term of type queue() against the pattern {F, _R} breaks the opaqueness of the term -queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue() as 1st argument -queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions -queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue()} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue() -queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue()} (with opaque subterms) as 1st argument -queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue() as 2nd argument +queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue:queue(_) as 1st argument +queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue:queue(_) as 2nd argument +queue_use.erl:27: The attempt to match a term of type queue:queue(_) against the pattern {"*", Q2} breaks the opaqueness of the term +queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue:queue(_) +queue_use.erl:36: The attempt to match a term of type queue:queue(_) against the pattern {F, _R} breaks the opaqueness of the term +queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue:queue(_) as 1st argument +queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue:queue(_)}) contains an opaque term as 2nd argument when terms of different types are expected in these positions +queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue:queue(_)} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue:queue(_) +queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue:queue(_)} (with opaque subterms) as 1st argument +queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue:queue(_) as 2nd argument diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/simple b/lib/dialyzer/test/opaque_SUITE_data/results/simple index f55b384cbe..072ac9be8f 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/simple +++ b/lib/dialyzer/test/opaque_SUITE_data/results/simple @@ -1,6 +1,6 @@ -exact_api.erl:17: The call exact_api:set_type(A::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph() as 1st argument -exact_api.erl:23: The call digraph:delete(G::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph() as 1st argument +exact_api.erl:17: The call exact_api:set_type(A::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph:graph() as 1st argument +exact_api.erl:23: The call digraph:delete(G::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph:graph() as 1st argument exact_api.erl:55: The attempt to match a term of type exact_adt:exact_adt() against the pattern {'exact_adt'} breaks the opaqueness of the term exact_api.erl:59: The call exact_adt:exact_adt_set_type2(A::#exact_adt{}) does not have an opaque term of type exact_adt:exact_adt() as 1st argument is_rec.erl:10: The call erlang:is_record(simple1_adt:d1(),'r',2) contains an opaque term as 1st argument when terms of different types are expected in these positions @@ -73,7 +73,7 @@ simple1_api.erl:536: Guard test A::simple1_adt:d1() == 3 contains an opaque term simple1_api.erl:538: Guard test A::simple1_adt:d1() =:= 3 contains an opaque term as 1st argument simple1_api.erl:548: The call erlang:'<'(A::simple1_adt:d1(),3) contains an opaque term as 1st argument when terms of different types are expected in these positions simple1_api.erl:558: The call erlang:'=<'(A::simple1_adt:d1(),B::simple1_adt:d2()) contains an opaque term as 1st argument when terms of different types are expected in these positions -simple1_api.erl:565: Guard test {digraph(),3} > {digraph(),atom() | tid()} contains an opaque term as 2nd argument +simple1_api.erl:565: Guard test {digraph:graph(),3} > {digraph:graph(),atom() | ets:tid()} contains an opaque term as 2nd argument simple1_api.erl:91: Invalid type specification for function simple1_api:tup/0. The success typing is () -> {'a','b'} simple2_api.erl:100: The call lists:flatten(A::simple1_adt:tuple1()) contains an opaque term as 1st argument when a structured term of type [any()] is expected simple2_api.erl:116: The call lists:flatten({simple1_adt:tuple1()}) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/wings b/lib/dialyzer/test/opaque_SUITE_data/results/wings index 0ca91ae331..511263b70a 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/wings +++ b/lib/dialyzer/test/opaque_SUITE_data/results/wings @@ -1,11 +1,11 @@ -wings_dissolve.erl:103: Guard test is_list(List::gb_set()) breaks the opaqueness of its argument -wings_dissolve.erl:19: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument -wings_dissolve.erl:272: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument -wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_set() as 1st argument +wings_dissolve.erl:103: Guard test is_list(List::gb_sets:set(_)) breaks the opaqueness of its argument +wings_dissolve.erl:19: Guard test is_list(Faces::gb_sets:set(_)) breaks the opaqueness of its argument +wings_dissolve.erl:272: Guard test is_list(Faces::gb_sets:set(_)) breaks the opaqueness of its argument +wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_sets:set(_) as 1st argument wings_edge.erl:205: The pattern <Edge, 'hard', Htab> can never match the type <_,'soft',_> -wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_set()) does not have an opaque term of type gb_tree() as 1st argument +wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_sets:set(_)) does not have an opaque term of type gb_trees:tree(_,_) as 1st argument wings_edge_cmd.erl:32: The pattern [_ | Parts] can never match the type [] wings_edge_cmd.erl:32: The pattern [{_, P} | _] can never match the type [] -wings_io.erl:30: The attempt to match a term of type {'empty',queue()} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue() -wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_tree()) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected +wings_io.erl:30: The attempt to match a term of type {'empty',queue:queue(_)} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue:queue(_) +wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_trees:tree(_,_)) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl index 8a2cd86f43..a4cec065ab 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl @@ -24,7 +24,7 @@ ok3() -> ok4() -> dict:fetch(foo, dict:new()). -ok5() -> % this is OK since some_mod:new/0 might be returning a dict() +ok5() -> % this is OK since some_mod:new/0 might be returning a dict:dict() dict:fetch(foo, some_mod:new()). ok6() -> diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl index 4eb202f16a..593d9a669d 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl @@ -17,6 +17,6 @@ t3() -> is_atom(n()). % no warning since atom() is possible t4() -> - is_integer(n()). % opaque warning since tid() is opaque + is_integer(n()). % opaque warning since ets:tid() is opaque -n() -> ets:new(n, [named_table]). % -> atom() | tid() +n() -> ets:new(n, [named_table]). % -> atom() | ets:tid() diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl index 0b98f550f1..5cbc79f948 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi/ewgi.hrl @@ -28,7 +28,7 @@ %% @type bag() = gb_tree() -ifdef(HAS_GB_TREE_SPEC). --type bag() :: gb_tree(). +-type bag() :: gb_trees:tree(). -else. -type bag() :: {non_neg_integer(), {any(), any(), any(), any()} | 'nil'}. -endif. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl index 5da8ff0ecf..d8e15cb081 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/ewgi2/ewgi.hrl @@ -29,7 +29,7 @@ %% @type bag() = gb_tree() -ifdef(HAS_GB_TREE_SPEC). --type bag() :: gb_tree(). +-type bag() :: gb_trees:tree(). -else. -type bag() :: {non_neg_integer(), {any(), any(), any(), any()} | 'nil'}. -endif. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl b/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl index 008b0a486a..7c34b01c2d 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl @@ -12,12 +12,12 @@ -export([new/0, get_g/1]). --record(rec, {g :: gb_set()}). +-record(rec, {g :: gb_sets:set()}). -spec new() -> #rec{}. new() -> #rec{g = gb_sets:empty()}. --spec get_g(#rec{}) -> gb_set(). +-spec get_g(#rec{}) -> gb_sets:set(). get_g(R) -> R#rec.g. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl index 0dff16cf14..3275736e75 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl @@ -1,7 +1,7 @@ %% -*- erlang-indent-level: 2 -*- %%---------------------------------------------------------------------------- %% Non-sensical (i.e., stripped-down) program that sends the analysis -%% into an infinite loop. The #we.es field was originally a gb_tree() +%% into an infinite loop. The #we.es field was originally a gb_trees:tree() %% but the programmer declared it as an array in order to change it to %% that data type instead. In the file, there are two calls to function %% gb_trees:get/2 which seem to be the ones responsible for sending the @@ -14,7 +14,7 @@ -export([command/1]). -record(we, {id, - es = array:new() :: array(), + es = array:new() :: array:array(), vp, mirror = none}). -record(edge, {vs,ve,a = none,b = none,lf,rf,ltpr,ltsu,rtpr,rtsu}). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl index 659ccaf015..3787fc6750 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop2.erl @@ -4,7 +4,7 @@ %% restored. %% Non-sensical (i.e., stripped-down) program that sends the analysis -%% into an infinite loop. The #we.es field was originally a gb_tree() +%% into an infinite loop. The #we.es field was originally a gb_trees:tree() %% but the programmer declared it as an array in order to change it to %% that data type instead. In the file, there are two calls to function %% gb_trees:get/2 which seem to be the ones responsible for sending the @@ -17,7 +17,7 @@ -export([command/1]). -record(we, {id, - es = array:new() :: array(), + es = array:new() :: array:array(), vp, mirror = none}). -record(edge, {vs,ve,a = none,b = none,lf,rf,ltpr,ltsu,rtpr,rtsu}). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl b/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl index 9e695cec1d..e9f7ad825b 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl @@ -2,7 +2,7 @@ -export([weird/1]). --spec weird(dict() | gb_tree()) -> 42. +-spec weird(dict:dict() | gb_trees:tree()) -> 42. weird(gazonk) -> 42. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1.erl new file mode 100644 index 0000000000..68e2c60368 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1.erl @@ -0,0 +1,93 @@ +-module(para1). + +-compile(export_all). + +%% Parameterized opaque types + +-export_type([t/0, t/1]). + +-opaque t() :: {integer(), integer()}. + +-opaque t(A) :: {A, A}. + +-type y(A) :: {A, A}. + +tt1() -> + I = t1(), + A = t2(), + A =:= I. % never 'true' + +tt2() -> + I = t0(), + A = t2(), + A =:= I. % never 'true' + +tt3() -> + I1 = t0(), + I2 = t1(), + I1 =:= I2. % never true + +tt4() -> + I1 = y1(), + I2 = y2(), + I1 =:= I2. % cannot evaluate to true + +adt_tt1() -> + I = adt_t1(), + A = adt_t2(), + A =:= I. % opaque attempt + +adt_tt2() -> + I = adt_t0(), + A = adt_t2(), + A =:= I. % opaque attempt + +adt_tt3() -> + I1 = adt_t0(), + I2 = adt_t1(), + I1 =:= I2. % opaque attempt + +adt_tt4() -> + I1 = adt_y1(), + I2 = adt_y2(), + I1 =:= I2. % cannot evaluate to true + +-spec t0() -> t(). + +t0() -> + {3, 2}. + +-spec t1() -> t(integer()). + +t1() -> + {3, 3}. + +-spec t2() -> t(atom()). + +t2() -> + {a, b}. + +-spec y1() -> y(integer()). + +y1() -> + {3, 2}. + +-spec y2() -> y(atom()). + +y2() -> + {a, b}. + +adt_t0() -> + para1_adt:t0(). + +adt_t1() -> + para1_adt:t1(). + +adt_t2() -> + para1_adt:t2(). + +adt_y1() -> + para1_adt:y1(). + +adt_y2() -> + para1_adt:y2(). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para1_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1_adt.erl new file mode 100644 index 0000000000..95ac6b7982 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para1_adt.erl @@ -0,0 +1,36 @@ +-module(para1_adt). + +-export([t0/0, t1/0, t2/0, y1/0, y2/0]). + +-export_type([t/0, t/1, y/1]). + +-opaque t() :: {integer(), integer()}. + +-opaque t(A) :: {A, A}. + +-type y(A) :: {A, A}. + +-spec t0() -> t(). + +t0() -> + {3, 2}. + +-spec t1() -> t(integer()). + +t1() -> + {3, 3}. + +-spec t2() -> t(atom()). + +t2() -> + {a, b}. + +-spec y1() -> y(integer()). + +y1() -> + {3, 2}. + +-spec y2() -> y(atom()). + +y2() -> + {a, b}. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2.erl new file mode 100644 index 0000000000..09b2235fa5 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2.erl @@ -0,0 +1,123 @@ +-module(para2). + +-compile(export_all). + +%% More parameterized opaque types + +-export_type([strange/1]). + +-export_type([c1/0, c2/0]). + +-export_type([circ/1, circ/2]). + +-opaque strange(A) :: {B, B, A}. + +-spec t(strange(integer())) -> strange(atom()). + +t({3, 4, 5}) -> + {a, b, c}. + +-opaque c1() :: c2(). +-opaque c2() :: c1(). + +c() -> + A = c1(), + B = c2(), + A =:= B. + +t() -> + A = ct1(), + B = ct2(), + A =:= B. % can never evaluate to 'true' + +-spec c1() -> c1(). + +c1() -> + a. + +-spec c2() -> c2(). + +c2() -> + a. + +-type ct1() :: ct2(). +-type ct2() :: ct1(). + +-spec ct1() -> ct1(). + +ct1() -> + a. + +-spec ct2() -> ct2(). + +ct2() -> + b. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +c_adt() -> + A = c1_adt(), + B = c2_adt(), + A =:= B. % opaque attempt + +t_adt() -> + A = ct1_adt(), + B = ct2_adt(), + A =:= B. % can never evaluate to true + +c1_adt() -> + para2_adt:c1(). + +c2_adt() -> + para2_adt:c2(). + +ct1_adt() -> + para2_adt:ct1(). + +ct2_adt() -> + para2_adt:ct2(). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-opaque circ(A) :: circ(A, A). +-opaque circ(A, B) :: circ({A, B}). + +tcirc() -> + A = circ1(), + B = circ2(), + A =:= B. % can never evaluate to 'true' (but the types are not OK, or?) + +-spec circ1() -> circ(integer()). + +circ1() -> + 3. + +-spec circ2() -> circ(integer(), integer()). + +circ2() -> + {3, 3}. + +tcirc_adt() -> + A = circ1_adt(), + B = circ2_adt(), + A =:= B. % opaque attempt (one would expect them to be the same...) + +circ1_adt() -> + para2_adt:circ1(). + +circ2_adt() -> + para2_adt:circ2(). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +u_adt() -> + A = u1_adt(), + B = u2_adt(), + %% The resulting types are equal, but not the parameters: + A =:= B. % opaque attempt + +u1_adt() -> + para2_adt:u1(). + +u2_adt() -> + para2_adt:u2(). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para2_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2_adt.erl new file mode 100644 index 0000000000..96df437c67 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para2_adt.erl @@ -0,0 +1,64 @@ +-module(para2_adt). + +%% More parameterized opaque types + +-export_type([c1/0, c2/0]). + +-export_type([ct1/0, ct2/0]). + +-export_type([circ/1, circ/2]). + +-export_type([un/2]). + +-export([c1/0, c2/0, ct1/0, ct2/0, circ1/0, circ2/0, u1/0, u2/0]). + +-opaque c1() :: c2(). +-opaque c2() :: c1(). + +-spec c1() -> c1(). + +c1() -> + a. + +-spec c2() -> c2(). + +c2() -> + a. + +-type ct1() :: ct2(). +-type ct2() :: ct1(). + +-spec ct1() -> ct1(). + +ct1() -> + a. + +-spec ct2() -> ct2(). + +ct2() -> + b. + +-opaque circ(A) :: circ(A, A). +-opaque circ(A, B) :: circ({A, B}). + +-spec circ1() -> circ(integer()). + +circ1() -> + 3. + +-spec circ2() -> circ(integer(), integer()). + +circ2() -> + {3, 3}. + +-opaque un(A, B) :: A | B. + +-spec u1() -> un(integer(), atom()). + +u1() -> + 3. + +-spec u2() -> un(atom(), integer()). + +u2() -> + 3. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl new file mode 100644 index 0000000000..792ae40d39 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl @@ -0,0 +1,77 @@ +-module(para3). + +-export([t/0, t1/1, t2/0, ot1/1, ot2/0, t1_adt/0, t2_adt/0]). + +-export([exp_adt/0]). + +%% More opaque tests. + +-export_type([ot1/0, ot1/1, ot1/2, ot1/3, ot1/4, ot1/5]). + +-opaque ot1() :: {ot1(_)}. + +-opaque ot1(A) :: {ot1(A, A)}. + +-opaque ot1(A, B) :: {ot1(A, B, A)}. + +-opaque ot1(A, B, C) :: {ot1(A, B, C, A)}. + +-opaque ot1(A, B, C, D) :: {ot1(A, B, C, D, A)}. + +-opaque ot1(A, B, C, D, E) :: {A, B, C, D, E}. + +-spec ot1(_) -> ot1(). + +ot1(A) -> + {{{{{A, A, A, A, A}}}}}. + +-spec ot2() -> ot1(). % invalid type spec + +ot2() -> + foo. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +t() -> + {{{17}}} = t1(3). %% pattern can never match + +-type t1() :: {t1(_)}. + +-type t1(A) :: {t1(A, A)}. + +-type t1(A, B) :: {t1(A, B, A)}. + +-type t1(A, B, C) :: {t1(A, B, C, A)}. + +-type t1(A, B, C, D) :: {t1(A, B, C, D, A)}. + +-type t1(A, B, C, D, E) :: {A, B, C, D, E}. + +-spec t1(_) -> t1(). + +t1(A) -> + {{{{{A, A, A, A, A}}}}}. + +-spec t2() -> t1(). % invalid type spec + +t2() -> + foo. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Shows that the list TypeNames in t_from_form must include ArgsLen. + +t1_adt() -> + {{{{{17}}}}} = para3_adt:t1(3). % breaks the opaqueness + +t2_adt() -> + {{{{17}}}} = para3_adt:t1(3). % can never match + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-type exp() :: para3_adt:exp1(para3_adt:exp2()). + +-spec exp_adt() -> exp(). + +exp_adt() -> + 3. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para3_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3_adt.erl new file mode 100644 index 0000000000..3919b846e6 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3_adt.erl @@ -0,0 +1,27 @@ +-module(para3_adt). + +-export([t1/1]). + +-export_type([t1/0, t1/1, t1/2, t1/3, t1/4, ot1/5]). + +-export_type([exp1/1, exp2/0]). + +-type t1() :: {t1(_)}. + +-type t1(A) :: {t1(A, A)}. + +-type t1(A, B) :: {t1(A, B, A)}. + +-type t1(A, B, C) :: {t1(A, B, C, A)}. + +-type t1(A, B, C, D) :: {ot1(A, B, C, D, A)}. + +-opaque ot1(A, B, C, D, E) :: {A, B, C, D, E}. + +-spec t1(_) -> t1(). + +t1(A) -> + {{{{{A, A, A, A, A}}}}}. + +-opaque exp1(T) :: T. +-opaque exp2() :: integer(). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl b/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl index 5f7ab4f3aa..c19330eb30 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl @@ -10,14 +10,14 @@ ntab = notable :: ets:tab(), cyclic = true :: boolean()}). --spec new() -> digraph(). +-spec new() -> digraph:graph(). new() -> A = #digraph{}, set_type(A), % does not have an opaque term as 1st argument A. --spec set_type(digraph()) -> true. +-spec set_type(digraph:graph()) -> true. set_type(G) -> digraph:delete(G). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl index 8f0da1f5dc..ca6bc0ab4a 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl @@ -14,12 +14,12 @@ rel2fam(Rel) -> sofs:to_external(sofs:relation_to_family(sofs:relation(Rel))). -%% a definition that does not violate the opaqueness of gb_tree() +%% a definition that does not violate the opaqueness of gb_trees:tree() gb_trees_smallest_key(Tree) -> {Key, _V} = gb_trees:smallest(Tree), Key. -%% a definition that violates the opaqueness of gb_tree() +%% a definition that violates the opaqueness of gb_trees:tree() gb_trees_largest_key({_, Tree}) -> largest_key1(Tree). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl index 38c6051c58..e094d1982b 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl @@ -2,7 +2,7 @@ -export([get/2]). --opaque data() :: gb_tree(). +-opaque data() :: gb_trees:tree(). -spec get(term(), data()) -> term(). diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3 b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3 index d640f564cd..0382627cfc 100644 --- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3 +++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3 @@ -1,3 +1,3 @@ -ets_insert_control_flow3.erl:21: The call ets:insert(Table::atom() | tid(),{'root',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'root') call in ets_insert_control_flow3.erl on line 12 -ets_insert_control_flow3.erl:23: The call ets:insert(Table::atom() | tid(),{'user',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'user') call in ets_insert_control_flow3.erl on line 13 +ets_insert_control_flow3.erl:21: The call ets:insert(Table::atom() | ets:tid(),{'root',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'root') call in ets_insert_control_flow3.erl on line 12 +ets_insert_control_flow3.erl:23: The call ets:insert(Table::atom() | ets:tid(),{'user',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'user') call in ets_insert_control_flow3.erl on line 13 diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4 b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4 index 6f34e75902..22944fd066 100644 --- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4 +++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4 @@ -1,3 +1,3 @@ -ets_insert_control_flow4.erl:21: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 13 -ets_insert_control_flow4.erl:23: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 13 +ets_insert_control_flow4.erl:21: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 13 +ets_insert_control_flow4.erl:23: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow4.erl on line 13 diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5 b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5 index 5af592f43f..e172887f34 100644 --- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5 +++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5 @@ -1,5 +1,5 @@ -ets_insert_control_flow5.erl:22: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16 -ets_insert_control_flow5.erl:23: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 13 -ets_insert_control_flow5.erl:25: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16 -ets_insert_control_flow5.erl:26: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 13 +ets_insert_control_flow5.erl:22: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16 +ets_insert_control_flow5.erl:23: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 13 +ets_insert_control_flow5.erl:25: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16 +ets_insert_control_flow5.erl:26: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_control_flow5.erl on line 13 diff --git a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param index 58f934a190..6a34337a2c 100644 --- a/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param +++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_param @@ -1,5 +1,5 @@ -ets_insert_param.erl:13: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_param.erl on line 10 -ets_insert_param.erl:14: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 14, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 15 -ets_insert_param.erl:17: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_param.erl on line 10 -ets_insert_param.erl:18: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 18 +ets_insert_param.erl:13: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_param.erl on line 10 +ets_insert_param.erl:14: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_param.erl on line 14, the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_param.erl on line 15 +ets_insert_param.erl:17: The call ets:insert(Table::atom() | ets:tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'welcome_msg') call in ets_insert_param.erl on line 10 +ets_insert_param.erl:18: The call ets:insert(Table::atom() | ets:tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | ets:tid(),'pass') call in ets_insert_param.erl on line 18 diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes index 173ff3a9f1..bfa33cd296 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes +++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes @@ -8,15 +8,15 @@ contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{ contracts_with_subtypes.erl:142: The pattern 1 can never match the type string() contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()} contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()} -contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',X} -contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',X} -contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',X} -contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',X} +contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',_} +contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',_} +contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',_} +contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',_} contracts_with_subtypes.erl:202: The pattern 1 can never match the type string() -contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,string()} -contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,string()} -contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,string()} -contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,string()} +contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',_,string()} +contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',_,string()} +contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',_,string()} +contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',_,string()} contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed') contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something' diff --git a/lib/dialyzer/test/small_SUITE_data/results/predef b/lib/dialyzer/test/small_SUITE_data/results/predef new file mode 100644 index 0000000000..85e210d6e4 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/predef @@ -0,0 +1,8 @@ + +predef.erl:19: Invalid type specification for function predef:array/1. The success typing is (array:array(_)) -> array:array(_) +predef.erl:24: Invalid type specification for function predef:dict/1. The success typing is (dict:dict(_,_)) -> dict:dict(_,_) +predef.erl:29: Invalid type specification for function predef:digraph/1. The success typing is (digraph:graph()) -> [any()] +predef.erl:39: Invalid type specification for function predef:gb_set/1. The success typing is (gb_sets:set(_)) -> gb_sets:set(_) +predef.erl:44: Invalid type specification for function predef:gb_tree/1. The success typing is (gb_trees:tree(_,_)) -> gb_trees:tree(_,_) +predef.erl:49: Invalid type specification for function predef:queue/1. The success typing is (queue:queue(_)) -> queue:queue(_) +predef.erl:54: Invalid type specification for function predef:set/1. The success typing is (sets:set(_)) -> sets:set(_) diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_construct b/lib/dialyzer/test/small_SUITE_data/results/record_construct index c0110b144f..4c40fec298 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/record_construct +++ b/lib/dialyzer/test/small_SUITE_data/results/record_construct @@ -1,6 +1,6 @@ record_construct.erl:15: Function t_opa/0 has no local return -record_construct.erl:16: Record construction #r_opa{b::gb_set(),c::42,e::'false'} violates the declared type of field c::boolean() +record_construct.erl:16: Record construction #r_opa{b::gb_sets:set(_),c::42,e::'false'} violates the declared type of field c::boolean() record_construct.erl:20: Function t_rem/0 has no local return record_construct.erl:21: Record construction #r_rem{a::'gazonk'} violates the declared type of field a::string() record_construct.erl:6: Function t_loc/0 has no local return diff --git a/lib/dialyzer/test/small_SUITE_data/src/on_load.erl b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl index 16533a9caa..7242ac2016 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/on_load.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl @@ -1,4 +1,6 @@ %%% This is to ensure that "on_load" functions are never reported as unused. +%%% In addition, all functions called by a function in an on_load attribute +%%% should be considered as called by an entry point of the module. -module(on_load). @@ -8,4 +10,7 @@ foo() -> ok. -bar() -> ok. +bar() -> gazonk(17). + +gazonk(N) when N < 42 -> gazonk(N+1); +gazonk(42) -> ok. diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef.erl b/lib/dialyzer/test/small_SUITE_data/src/predef.erl new file mode 100644 index 0000000000..c2364fd1c2 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/predef.erl @@ -0,0 +1,67 @@ +-module(predef). + +-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1, + queue/1, set/1, tid/0, tid2/0]). + +-export_type([array/0, digraph/0, gb_set/0]). + +%% Before R17B local re-definitions of pre-defined opaque types were +%% ignored but did not generate any warning. +-opaque array() :: atom(). +-opaque digraph() :: atom(). +-opaque gb_set() :: atom(). +-type dict() :: atom(). +-type gb_tree() :: atom(). +-type queue() :: atom(). +-type set() :: atom(). +-type tid() :: atom(). + +-spec array(array()) -> array:array(). + +array(A) -> + array:relax(A). + +-spec dict(dict()) -> dict:dict(). + +dict(D) -> + dict:store(1, a, D). + +-spec digraph(digraph()) -> [digraph:edge()]. + +digraph(G) -> + digraph:edges(G). + +-spec digraph2(digraph:graph()) -> [digraph:edge()]. + +digraph2(G) -> + digraph:edges(G). + +-spec gb_set(gb_set()) -> gb_sets:set(). + +gb_set(S) -> + gb_sets:balance(S). + +-spec gb_tree(gb_tree()) -> gb_trees:tree(). + +gb_tree(S) -> + gb_trees:balance(S). + +-spec queue(queue()) -> queue:queue(). + +queue(Q) -> + queue:reverse(Q). + +-spec set(set()) -> sets:set(). + +set(S) -> + sets:union([S]). + +-spec tid() -> tid(). + +tid() -> + ets:new(tid, []). + +-spec tid2() -> ets:tid(). + +tid2() -> + ets:new(tid, []). diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef2.erl b/lib/dialyzer/test/small_SUITE_data/src/predef2.erl new file mode 100644 index 0000000000..b1d941a49a --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/predef2.erl @@ -0,0 +1,56 @@ +-module(predef2). + +-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1, + queue/1, set/1, tid/0, tid2/0]). + +-export_type([array/0, digraph/0, gb_set/0]). + +-spec array(array()) -> array:array(). + +array(A) -> + array:relax(A). + +-spec dict(dict()) -> dict:dict(). + +dict(D) -> + dict:store(1, a, D). + +-spec digraph(digraph()) -> [digraph:edge()]. + +digraph(G) -> + digraph:edges(G). + +-spec digraph2(digraph:graph()) -> [digraph:edge()]. + +digraph2(G) -> + digraph:edges(G). + +-spec gb_set(gb_set()) -> gb_sets:set(). + +gb_set(S) -> + gb_sets:balance(S). + +-spec gb_tree(gb_tree()) -> gb_trees:tree(). + +gb_tree(S) -> + gb_trees:balance(S). + +-spec queue(queue()) -> queue:queue(). + +queue(Q) -> + queue:reverse(Q). + +-spec set(set()) -> sets:set(). + +set(S) -> + sets:union([S]). + +-spec tid() -> tid(). + +tid() -> + ets:new(tid, []). + +-spec tid2() -> ets:tid(). + +tid2() -> + ets:new(tid, []). diff --git a/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl b/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl index 54cc2601bd..b250c6ee65 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl @@ -7,7 +7,7 @@ t_loc() -> #r_loc{}. -record(r_opa, {a :: atom(), - b = gb_sets:new() :: gb_set(), + b = gb_sets:new() :: gb_sets:set(), c = 42 :: boolean(), d, % untyped on purpose e = false :: boolean()}). diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings index 2784f2119e..24b44c1b5c 100644 --- a/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings +++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings @@ -1,5 +1,5 @@ lc_warnings.erl:32: Expression produces a value of type [opaque_atom_adt:opaque_atom()], but this value is unmatched -lc_warnings.erl:43: Expression produces a value of type [array()], but this value is unmatched +lc_warnings.erl:43: Expression produces a value of type [array:array(_)], but this value is unmatched lc_warnings.erl:65: Expression produces a value of type [lc_warnings:opaque_tuple()], but this value is unmatched lc_warnings.erl:7: Expression produces a value of type ['ok' | {'error',atom()}], but this value is unmatched diff --git a/lib/edoc/src/edoc.appup.src b/lib/edoc/src/edoc.appup.src index 54a63833e6..45b4046ed8 100644 --- a/lib/edoc/src/edoc.appup.src +++ b/lib/edoc/src/edoc.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, edoc}]}], + [{<<".*">>,[{restart_application, edoc}]}] +}. diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 466c9df951..211a354c74 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ %% Exported functions %% --spec type(Form::syntaxTree(), TypeDocs::dict()) -> #tag{}. +-spec type(Form::syntaxTree(), TypeDocs::dict:dict()) -> #tag{}. %% @doc Convert an Erlang type to EDoc representation. %% TypeDocs is a dict of {Name, Doc}. @@ -88,7 +88,7 @@ dummy_spec(Form) -> -spec docs(Forms::[syntaxTree()], CommentFun :: fun( ([syntaxTree()], Line :: term()) -> #tag{} )) - -> dict(). + -> dict:dict(). %% @doc Find comments after -type/-opaque declarations. %% Postcomments "inside" the type are skipped. diff --git a/lib/edoc/test/edoc_SUITE.erl b/lib/edoc/test/edoc_SUITE.erl index b649971e99..c9c7811afb 100644 --- a/lib/edoc/test/edoc_SUITE.erl +++ b/lib/edoc/test/edoc_SUITE.erl @@ -22,12 +22,12 @@ init_per_group/2,end_per_group/2]). %% Test cases --export([build_std/1,build_map_module/1]). +-export([app/1,appup/1,build_std/1,build_map_module/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [build_std,build_map_module]. + [app,appup,build_std,build_map_module]. groups() -> []. @@ -44,6 +44,13 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +%% Test that the .app file does not contain any `basic' errors +app(Config) when is_list(Config) -> + ok = ?t:app_test(edoc). + +%% Test that the .appup file does not contain any `basic' errors +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(edoc). build_std(suite) -> []; build_std(doc) -> ["Build some documentation using standard EDoc layout"]; diff --git a/lib/eldap/src/eldap.appup.src b/lib/eldap/src/eldap.appup.src index 8d33482f11..7a9a5ef548 100644 --- a/lib/eldap/src/eldap.appup.src +++ b/lib/eldap/src/eldap.appup.src @@ -1,6 +1,21 @@ %% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% {"%VSN%", - [ - ], - [ - ]}. + [{<<".*">>,[{restart_application, eldap}]}], + [{<<".*">>,[{restart_application, eldap}]}] +}. diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl index af5bf94c97..a86fd39adb 100644 --- a/lib/eldap/src/eldap.erl +++ b/lib/eldap/src/eldap.erl @@ -743,7 +743,7 @@ request(S, Data, ID, Request) -> send_request(S, Data, ID, Request) -> Message = #'LDAPMessage'{messageID = ID, protocolOp = Request}, - {ok,Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message), + {ok,Bytes} = 'ELDAPv3':encode('LDAPMessage', Message), case do_send(S, Data, Bytes) of {error,Reason} -> throw({gen_tcp_error,Reason}); Else -> Else @@ -762,7 +762,7 @@ do_recv(S, #eldap{using_tls=true, timeout=Timeout}, Len) -> recv_response(S, Data) -> case do_recv(S, Data, 0) of {ok, Packet} -> - case asn1rt:decode('ELDAPv3', 'LDAPMessage', Packet) of + case 'ELDAPv3':decode('LDAPMessage', Packet) of {ok,Resp} -> {ok,Resp}; Error -> throw(Error) end; diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl index bf5fa83c3c..6c3d303da0 100644 --- a/lib/eldap/test/eldap_basic_SUITE.erl +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -84,6 +84,7 @@ end_per_testcase(_TestCase, Config) -> all() -> [app, + appup, api, ssl_api, start_tls, @@ -95,7 +96,11 @@ all() -> app(doc) -> "Test that the eldap app file is ok"; app(suite) -> []; app(Config) when is_list(Config) -> - ok = test_server:app_test(public_key). + ok = test_server:app_test(eldap). + +%% Test that the eldap appup file is ok +appup(Config) when is_list(Config) -> + ok = test_server:appup_test(eldap). api(doc) -> "Basic test that all api functions works as expected"; api(suite) -> []; diff --git a/lib/erl_docgen/src/erl_docgen.appup.src b/lib/erl_docgen/src/erl_docgen.appup.src index 54a63833e6..972a881c41 100644 --- a/lib/erl_docgen/src/erl_docgen.appup.src +++ b/lib/erl_docgen/src/erl_docgen.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, erl_docgen}]}], + [{<<".*">>,[{restart_application, erl_docgen}]}] +}. diff --git a/lib/erl_docgen/test/Makefile b/lib/erl_docgen/test/Makefile new file mode 100644 index 0000000000..9f4cc04105 --- /dev/null +++ b/lib/erl_docgen/test/Makefile @@ -0,0 +1,65 @@ +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + erl_docgen_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/erl_docgen_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) $(GEN_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) erl_docgen.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + +release_docs_spec: diff --git a/lib/erl_docgen/test/erl_docgen.spec b/lib/erl_docgen/test/erl_docgen.spec new file mode 100644 index 0000000000..98833614df --- /dev/null +++ b/lib/erl_docgen/test/erl_docgen.spec @@ -0,0 +1 @@ +{suites,"../erl_docgen_test",all}. diff --git a/lib/erl_docgen/test/erl_docgen_SUITE.erl b/lib/erl_docgen/test/erl_docgen_SUITE.erl new file mode 100644 index 0000000000..20fbc1229b --- /dev/null +++ b/lib/erl_docgen/test/erl_docgen_SUITE.erl @@ -0,0 +1,50 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(erl_docgen_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [app, appup]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the erl_docgen app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(erl_docgen). + +appup() -> + [{doc, "Test that the erl_docgen appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(erl_docgen). diff --git a/lib/et/src/et.appup.src b/lib/et/src/et.appup.src index b6344a9387..5f15b00386 100644 --- a/lib/et/src/et.appup.src +++ b/lib/et/src/et.appup.src @@ -1,8 +1,7 @@ -%% This is an -*- erlang -*- file. -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,8 +15,7 @@ %% under the License. %% %% %CopyrightEnd% - {"%VSN%", - [ ] + [{<<".*">>,[{restart_application, et}]}], + [{<<".*">>,[{restart_application, et}]}] }. - diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile index 3817079b5f..1be5aac3fd 100644 --- a/lib/et/test/Makefile +++ b/lib/et/test/Makefile @@ -25,6 +25,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES= \ ett \ + et_SUITE \ et_wx_SUITE \ et_test_lib diff --git a/lib/et/test/et_SUITE.erl b/lib/et/test/et_SUITE.erl new file mode 100644 index 0000000000..e0bce41e15 --- /dev/null +++ b/lib/et/test/et_SUITE.erl @@ -0,0 +1,50 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(et_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [app, appup]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the et app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(et). + +appup() -> + [{doc, "Test that the et appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(et). diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src index 431abac98b..5e16dfa2ce 100644 --- a/lib/eunit/src/eunit.app.src +++ b/lib/eunit/src/eunit.app.src @@ -14,6 +14,7 @@ eunit_striptests, eunit_surefire, eunit_test, + eunit_tests, eunit_tty]}, {registered,[]}, {applications, [kernel,stdlib]}, diff --git a/lib/eunit/src/eunit.appup.src b/lib/eunit/src/eunit.appup.src index 54a63833e6..18934d44c2 100644 --- a/lib/eunit/src/eunit.appup.src +++ b/lib/eunit/src/eunit.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, eunit}]}], + [{<<".*">>,[{restart_application, eunit}]}] +}. diff --git a/lib/eunit/src/eunit_serial.erl b/lib/eunit/src/eunit_serial.erl index 80e79116e3..1a0179a5df 100644 --- a/lib/eunit/src/eunit_serial.erl +++ b/lib/eunit/src/eunit_serial.erl @@ -56,9 +56,9 @@ %% future use, and/or cancel the current item and possibly one or more %% of its parent groups. --record(state, {listeners :: set(), - cancelled = eunit_lib:trie_new() :: gb_tree(), - messages = dict:new() :: dict()}). +-record(state, {listeners :: sets:set(), + cancelled = eunit_lib:trie_new() :: gb_trees:tree(), + messages = dict:new() :: dict:dict()}). start(Pids) -> spawn(fun () -> serializer(Pids) end). diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl index 47c2435d63..d13dc73923 100644 --- a/lib/eunit/test/eunit_SUITE.erl +++ b/lib/eunit/test/eunit_SUITE.erl @@ -19,14 +19,15 @@ -module(eunit_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,eunit_test/1]). + init_per_group/2,end_per_group/2, + app_test/1,appup_test/1,eunit_test/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [eunit_test]. + [app_test, appup_test, eunit_test]. groups() -> []. @@ -43,6 +44,11 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +app_test(Config) when is_list(Config) -> + ok = ?t:app_test(eunit). + +appup_test(Config) when is_list(Config) -> + ok = ?t:appup_test(eunit). eunit_test(Config) when is_list(Config) -> ok = file:set_cwd(code:lib_dir(eunit)), diff --git a/lib/gs/src/gs.appup.src b/lib/gs/src/gs.appup.src index 54a63833e6..2cb3b547e8 100644 --- a/lib/gs/src/gs.appup.src +++ b/lib/gs/src/gs.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, gs}]}], + [{<<".*">>,[{restart_application, gs}]}] +}. diff --git a/lib/gs/test/Makefile b/lib/gs/test/Makefile new file mode 100644 index 0000000000..493770745f --- /dev/null +++ b/lib/gs/test/Makefile @@ -0,0 +1,65 @@ +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + gs_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/gs_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) $(GEN_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) gs.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + +release_docs_spec: diff --git a/lib/gs/test/gs.spec b/lib/gs/test/gs.spec new file mode 100644 index 0000000000..46e6b2061e --- /dev/null +++ b/lib/gs/test/gs.spec @@ -0,0 +1 @@ +{suites,"../gs_test",all}. diff --git a/lib/gs/test/gs_SUITE.erl b/lib/gs/test/gs_SUITE.erl new file mode 100644 index 0000000000..01ce90df0b --- /dev/null +++ b/lib/gs/test/gs_SUITE.erl @@ -0,0 +1,50 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(gs_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [app, appup]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the gs app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(gs, tolerant). + +appup() -> + [{doc, "Test that the gs appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(gs). diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl index 021acd5b35..1b325703ae 100644 --- a/lib/hipe/cerl/cerl_closurean.erl +++ b/lib/hipe/cerl/cerl_closurean.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -78,7 +78,8 @@ %% function; see `analyze' for details. -spec annotate(cerl:cerl()) -> - {cerl:cerl(), outlist(), dict(), escapes(), dict(), dict()}. + {cerl:cerl(), outlist(), dict:dict(), + escapes(), dict:dict(), dict:dict()}. annotate(Tree) -> {Xs, Out, Esc, Deps, Par} = analyze(Tree), @@ -206,7 +207,8 @@ append_ann(Tag, Val, []) -> %% variable labeled `escape', which will hold the set of escaped labels. %% initially it contains `top' and `external'. --spec analyze(cerl:cerl()) -> {outlist(), dict(), escapes(), dict(), dict()}. +-spec analyze(cerl:cerl()) -> + {outlist(), dict:dict(), escapes(), dict:dict(), dict:dict()}. analyze(Tree) -> %% Note that we use different name spaces for variable labels and diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl index ca812a0f0d..7911b875a9 100644 --- a/lib/hipe/cerl/cerl_messagean.erl +++ b/lib/hipe/cerl/cerl_messagean.erl @@ -1,7 +1,7 @@ %% ===================================================================== %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -182,7 +182,7 @@ -type label() :: integer() | 'external' | 'top'. -type ordset(X) :: [X]. % XXX: TAKE ME OUT --spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict()}. +-spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict:dict()}. annotate(Tree) -> {Esc0, Vars} = analyze(Tree), diff --git a/lib/hipe/cerl/cerl_typean.erl b/lib/hipe/cerl/cerl_typean.erl index ccd8903658..f694c07c82 100644 --- a/lib/hipe/cerl/cerl_typean.erl +++ b/lib/hipe/cerl/cerl_typean.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -242,7 +242,7 @@ delete_ann(_, []) -> -type labelset() :: ordset(label()). -type outlist() :: [labelset()] | 'none'. --spec analyze(cerl:cerl()) -> {outlist(), dict(), dict()}. +-spec analyze(cerl:cerl()) -> {outlist(), dict:dict(), dict:dict()}. analyze(Tree) -> analyze(Tree, ?DEF_LIMIT). diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index af34e355bb..32390045e3 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -228,7 +228,7 @@ -export([t_is_identifier/1]). -endif. --export_type([erl_type/0]). +-export_type([erl_type/0, type_table/0, var_table/0]). %%-define(DEBUG, true). @@ -363,6 +363,15 @@ -type opaques() :: [erl_type()] | 'universe'. +-type record_key() :: {'record', atom()}. +-type type_key() :: {'type' | 'opaque', atom(), arity()}. +-type record_value() :: orddict:orddict(). % XXX. To be refined +-type type_value() :: {module(), erl_type(), atom()}. +-type type_table() :: dict:dict(record_key(), record_value()) + | dict:dict(type_key(), type_value()). + +-type var_table() :: dict:dict(atom(), erl_type()). + %%----------------------------------------------------------------------------- %% Unions %% @@ -645,15 +654,16 @@ decorate_with_opaque(Type, ?opaque(Set2), Opaques) -> end. decoration([#opaque{struct = S} = Opaque|OpaqueTypes], Type, Opaques, - NewOpaqueTypes, All) -> + NewOpaqueTypes0, All) -> IsOpaque = is_opaque_type2(Opaque, Opaques), I = t_inf(Type, S), - case not IsOpaque orelse t_is_none(I = t_inf(Type, S)) of - true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, All); + case not IsOpaque orelse t_is_none(I) of + true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes0, All); false -> NewOpaque = Opaque#opaque{struct = decorate(I, S, Opaques)}, NewAll = All orelse t_is_equal(I, Type), - decoration(OpaqueTypes, Type, Opaques, [NewOpaque|NewOpaqueTypes], NewAll) + NewOpaqueTypes = [NewOpaque|NewOpaqueTypes0], + decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, NewAll) end; decoration([], _Type, _Opaques, NewOpaqueTypes, All) -> {NewOpaqueTypes, All}. @@ -722,7 +732,7 @@ decorate_tuples_in_sets([T1|Tuples], L2, Opaques, Acc) -> decorate_tuples_in_sets([], _L, _Opaques, Acc) -> lists:reverse(Acc). --spec t_opaque_from_records(dict()) -> [erl_type()]. +-spec t_opaque_from_records(type_table()) -> [erl_type()]. t_opaque_from_records(RecDict) -> OpaqueRecDict = @@ -733,13 +743,14 @@ t_opaque_from_records(RecDict) -> end end, RecDict), OpaqueTypeDict = - dict:map(fun({opaque, Name, _Arity}, {Module, Type, ArgNames}) -> - case ArgNames of - [] -> - t_opaque(Module, Name, [], t_from_form(Type, RecDict)); - _ -> - throw({error,"Polymorphic opaque types not supported yet"}) - end + dict:map(fun({opaque, Name, _Arity}, {Module, _Type, ArgNames}) -> + %% Args = args_to_types(ArgNames), + %% List = lists:zip(ArgNames, Args), + %% TmpVarDict = dict:from_list(List), + %% Rep = t_from_form(Type, RecDict, TmpVarDict), + Rep = t_none(), % not used for anything right now + Args = [t_any() || _ <- ArgNames], + skip_opaque_alias(Rep, Module, Name, Args) end, OpaqueRecDict), [OpaqueType || {_Key, OpaqueType} <- dict:to_list(OpaqueTypeDict)]. @@ -796,7 +807,9 @@ t_is_remote(Type) -> is_remote(?remote(_)) -> true; is_remote(_) -> false. --spec t_solve_remote(erl_type(), set(), dict()) -> erl_type(). +-type mod_records() :: dict:dict(module(), type_table()). + +-spec t_solve_remote(erl_type(), sets:set(mfa()), mod_records()) -> erl_type(). t_solve_remote(Type, ExpTypes, Records) -> {RT, _RR} = t_solve_remote(Type, ExpTypes, Records, []), @@ -833,8 +846,12 @@ t_solve_remote(?union(List), ET, R, C) -> {t_sup(RL), RR}; t_solve_remote(T, _ET, _R, _C) -> {T, []}. -t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType, +t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args0} = RemType, ET, R, C) -> + Args = lists:map(fun(A) -> + {Arg, _} = t_solve_remote(A, ET, R, C), + Arg + end, Args0), ArgsLen = length(Args), case dict:find(RemMod, R) of error -> @@ -880,9 +897,7 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType, true -> t_limit(NewRep, ?REC_TYPE_LIMIT); false -> NewRep end, - {t_from_form({opaque, -1, Name, {Mod, Args, RT1}}, - RemDict, TmpVarDict), - RetRR}; + {skip_opaque_alias(RT1, Mod, Name, Args), RetRR}; error -> Msg = io_lib:format("Unable to find remote type ~w:~w()\n", [RemMod, Name]), @@ -1958,20 +1973,24 @@ t_timeout() -> -spec t_array() -> erl_type(). t_array() -> - t_opaque(array, array, [], + t_opaque(array, array, [t_any()], t_tuple([t_atom('array'), t_sup([t_atom('undefined'), t_non_neg_integer()]), t_sup([t_atom('undefined'), t_non_neg_integer()]), - t_any(), t_any()])). + t_any(), + t_any()])). -spec t_dict() -> erl_type(). t_dict() -> - t_opaque(dict, dict, [], + t_opaque(dict, dict, [t_any(), t_any()], t_tuple([t_atom('dict'), - t_non_neg_integer(), t_non_neg_integer(), - t_non_neg_integer(), t_non_neg_integer(), - t_non_neg_integer(), t_non_neg_integer(), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), t_sup([t_atom('undefined'), t_tuple()]), t_sup([t_atom('undefined'), t_tuple()])])). @@ -2000,12 +2019,12 @@ t_gb_tree() -> -spec t_queue() -> erl_type(). t_queue() -> - t_opaque(queue, queue, [], t_tuple([t_list(), t_list()])). + t_opaque(queue, queue, [t_any()], t_tuple([t_list(), t_list()])). -spec t_set() -> erl_type(). t_set() -> - t_opaque(sets, set, [], + t_opaque(sets, set, [t_any()], t_tuple([t_atom('set'), t_non_neg_integer(), t_non_neg_integer(), t_pos_integer(), t_non_neg_integer(), t_non_neg_integer(), t_non_neg_integer(), @@ -2023,18 +2042,6 @@ all_opaque_builtins() -> [t_array(), t_dict(), t_digraph(), t_gb_set(), t_gb_tree(), t_queue(), t_set(), t_tid()]. --spec is_opaque_builtin(atom(), atom()) -> boolean(). - -is_opaque_builtin(array, array) -> true; -is_opaque_builtin(dict, dict) -> true; -is_opaque_builtin(digraph, digraph) -> true; -is_opaque_builtin(gb_sets, gb_set) -> true; -is_opaque_builtin(gb_trees, gb_tree) -> true; -is_opaque_builtin(queue, queue) -> true; -is_opaque_builtin(sets, set) -> true; -is_opaque_builtin(ets, tid) -> true; -is_opaque_builtin(_, _) -> false. - %%------------------------------------ %% ?none is allowed in products. A product of size 1 is not a product. @@ -2082,11 +2089,11 @@ t_has_var(?tuple(Elements, _, _)) -> t_has_var_list(Elements); t_has_var(?tuple_set(_) = T) -> t_has_var_list(t_tuple_subtypes(T)); -%% t_has_var(?opaque(_)=T) -> -%% %% "Polymorphic opaque types not supported yet" -%% t_has_var(t_opaque_structure(T)); -%% t_has_var(?union(_) = U) -> -%% exit(flat_format("Union happens in t_has_var/1 ~p\n",[U])); +t_has_var(?opaque(Set)) -> + %% Assume variables in 'args' are also present i 'struct' + t_has_var_list([O#opaque.struct || O <- set_to_list(Set)]); +t_has_var(?union(List)) -> + t_has_var_list(List); t_has_var(_) -> false. -spec t_has_var_list([erl_type()]) -> boolean(). @@ -2117,9 +2124,10 @@ t_collect_vars(?tuple(Types, _, _), Acc) -> t_collect_vars(?tuple_set(_) = TS, Acc) -> lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, t_tuple_subtypes(TS)); -%% t_collect_vars(?opaque(_)=T, Acc) -> -%% %% "Polymorphic opaque types not supported yet" -%% t_collect_vars(t_opaque_structure(T), Acc); +t_collect_vars(?opaque(Set), Acc) -> + %% Assume variables in 'args' are also present i 'struct' + lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, + [O#opaque.struct || O <- set_to_list(Set)]); t_collect_vars(_, Acc) -> Acc. @@ -2442,8 +2450,8 @@ sup_opaque(List) -> ?opaque(ordsets:from_list(L)). sup_opaq(L0) -> - L1 = [{{Mod,Name}, T} || - #opaque{mod = Mod, name = Name}=T <- L0], + L1 = [{{Mod,Name,Args}, T} || + #opaque{mod = Mod, name = Name, args = Args}=T <- L0], F = family(L1), [supl(Ts) || {_, Ts} <- F]. @@ -2809,31 +2817,35 @@ inf_collect(_T1, [], _Opaques, OpL) -> OpL. combine(S, T1, T2) -> - #opaque{mod = Mod1, name = Name1} = T1, - #opaque{mod = Mod2, name = Name2} = T2, - case {Mod1, Name1} =:= {Mod2, Name2} of - true -> [comb(Mod1, Name1, S, T1)]; - false -> [comb(Mod1, Name1, S, T1), comb(Mod2, Name2, S, T2)] + #opaque{mod = Mod1, name = Name1, args = Args1} = T1, + #opaque{mod = Mod2, name = Name2, args = Args2} = T2, + case is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of + true -> [comb(Mod1, Name1, Args1, S, T1)]; + false -> [comb(Mod1, Name1, Args1, S, T1), comb(Mod2, Name2, Args2, S, T2)] end. -comb(Mod, Name, S, T) -> - case is_same_name(Mod, Name, S) of +comb(Mod, Name, Args, S, T) -> + case is_same_name(Mod, Name, Args, S) of true -> S; false -> T#opaque{struct = S} end. -is_same_name(Mod, Name, ?opaque([#opaque{mod = Mod, name = Name}])) -> true; -is_same_name(_Mod, _Name, _Opaque) -> false. +is_same_name(Mod1, Name1, Args1, + ?opaque([#opaque{mod = Mod2, name = Name2, args = Args2}])) -> + is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}); +is_same_name(_, _, _, _) -> false. %% Combining two lists this way can be very time consuming... +%% Note: two parameterized opaque types are not the same if their +%% actual parameters differ inf_opaque(Set1, Set2, Opaques) -> List1 = inf_look_up(Set1, 1, Opaques), List2 = inf_look_up(Set2, 2, Opaques), List0 = [combine(Inf, T1, T2) || - {Is1, ModName1, T1} <- List1, - {Is2, ModName2, T2} <- List2, - not t_is_none(Inf = inf_opaque_types(Is1, ModName1, T1, - Is2, ModName2, T2, + {Is1, ModNameArgs1, T1} <- List1, + {Is2, ModNameArgs2, T2} <- List2, + not t_is_none(Inf = inf_opaque_types(Is1, ModNameArgs1, T1, + Is2, ModNameArgs2, T2, Opaques))], List = lists:sort(lists:append(List0)), sup_opaque(List). @@ -2841,18 +2853,22 @@ inf_opaque(Set1, Set2, Opaques) -> %% Optimization: do just one lookup. inf_look_up(Set, Pos, Opaques) -> [{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Pos, Opaques), - {M, N}, T} || - #opaque{mod = M, name = N} = T <- set_to_list(Set)]. + {M, N, Args}, T} || + #opaque{mod = M, name = N, args = Args} = T <- set_to_list(Set)]. inf_is_opaque_type2(T, Pos, {match, Opaques}) -> is_opaque_type2(T, Opaques) orelse throw(Pos); inf_is_opaque_type2(T, _Pos, Opaques) -> is_opaque_type2(T, Opaques). -inf_opaque_types(IsOpaque1, ModName1, T1, IsOpaque2, ModName2, T2, Opaques) -> +inf_opaque_types(IsOpaque1, ModNameArgs1, T1, + IsOpaque2, ModNameArgs2, T2, Opaques) -> #opaque{struct = S1}=T1, #opaque{struct = S2}=T2, - case Opaques =:= 'universe' orelse ModName1 =:= ModName2 of + case + Opaques =:= 'universe' orelse + is_same_type_name(ModNameArgs1, ModNameArgs2) + of true -> t_inf(S1, S2, Opaques); false -> case {IsOpaque1, IsOpaque2} of @@ -3018,13 +3034,13 @@ findfirst(N1, N2, U1, B1, U2, B2) -> %% to types. Hans Bolinder suggested the use of lists of Key-Value pairs for %% this data structure and measurements showed a non-trivial speedup when using %% them for operations within this module (e.g. in t_unify/2). However, there -%% is code outside erl_types that still passes a dict() in the 2nd argument. +%% is code outside erl_types that still passes a dict:dict() in the 2nd argument. %% So, for the time being, this module provides a t_subst/2 function for these %% external calls and a clone of it (t_subst_kv/2) which is used from all calls %% from within this module. This code duplication needs to be eliminated at %% some point. --spec t_subst(erl_type(), dict()) -> erl_type(). +-spec t_subst(erl_type(), dict:dict(atom(), erl_type())) -> erl_type(). t_subst(T, Dict) -> case t_has_var(T) of @@ -3060,11 +3076,13 @@ t_subst_dict(?tuple(Elements, _Arity, _Tag), Dict) -> t_tuple([t_subst_dict(E, Dict) || E <- Elements]); t_subst_dict(?tuple_set(_) = TS, Dict) -> t_sup([t_subst_dict(T, Dict) || T <- t_tuple_subtypes(TS)]); -%% t_subst_dict(?opaque(Es), Dict) -> -%% %% "Polymorphic opaque types not supported yet" -%% List = [Opaque#opaque{struct = t_subst_dict(S, Dict)} || -%% Opaque = #opaque{struct = S} <- set_to_list(Es)], -%% ?opaque(ordsets:from_list(List)); +t_subst_dict(?opaque(Es), Dict) -> + List = [Opaque#opaque{args = [t_subst_dict(Arg, Dict) || Arg <- Args], + struct = t_subst_dict(S, Dict)} || + Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)], + ?opaque(ordsets:from_list(List)); +t_subst_dict(?union(List), Dict) -> + ?union([t_subst_dict(E, Dict) || E <- List]); t_subst_dict(T, _Dict) -> T. @@ -3107,11 +3125,13 @@ t_subst_aux(?tuple(Elements, _Arity, _Tag), VarMap) -> t_tuple([t_subst_aux(E, VarMap) || E <- Elements]); t_subst_aux(?tuple_set(_) = TS, VarMap) -> t_sup([t_subst_aux(T, VarMap) || T <- t_tuple_subtypes(TS)]); -%% t_subst_aux(?opaque(Es), VarMap) -> -%% %% "Polymorphic opaque types not supported yet" -%% List = [Opaque#opaque{struct = t_subst_aux(S, VarMap)} || -%% Opaque = #opaque{struct = S} <- set_to_list(Es)], -%% ?opaque(ordsets:from_list(List)); +t_subst_aux(?opaque(Es), VarMap) -> + List = [Opaque#opaque{args = [t_subst_aux(Arg, VarMap) || Arg <- Args], + struct = t_subst_aux(S, VarMap)} || + Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)], + ?opaque(ordsets:from_list(List)); +t_subst_aux(?union(List), VarMap) -> + ?union([t_subst_aux(E, VarMap) || E <- List]); t_subst_aux(T, _VarMap) -> T. @@ -3232,15 +3252,20 @@ unify_union(List) -> is_opaque_type(?opaque(Elements), Opaques) -> lists:any(fun(Opaque) -> is_opaque_type2(Opaque, Opaques) end, Elements). -is_opaque_type2(#opaque{mod = Mod1, name = Name1}, Opaques) -> +is_opaque_type2(#opaque{mod = Mod1, name = Name1, args = Args1}, Opaques) -> F1 = fun(?opaque(Es)) -> - F2 = fun(#opaque{mod = Mod, name = Name}) -> - Mod1 =:= Mod andalso Name1 =:= Name + F2 = fun(#opaque{mod = Mod, name = Name, args = Args}) -> + is_type_name(Mod1, Name1, Args1, Mod, Name, Args) end, lists:any(F2, Es) end, lists:any(F1, Opaques). +is_type_name(Mod, Name, Args1, Mod, Name, Args2) -> + length(Args1) =:= length(Args2); +is_type_name(Mod1, Name1, Args1, Mod2, Name2, Args2) -> + is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2). + %% Two functions since t_unify is not symmetric. unify_tuple_set_and_tuple1(?tuple_set([{Arity, List}]), ?tuple(Elements2, Arity, _), VarMap) -> @@ -3759,7 +3784,7 @@ t_limit_k(T, _K) -> T. %% %%============================================================================ --spec t_abstract_records(erl_type(), dict()) -> erl_type(). +-spec t_abstract_records(erl_type(), type_table()) -> erl_type(). t_abstract_records(?list(Contents, Termination, Size), RecDict) -> case t_abstract_records(Contents, RecDict) of @@ -3839,7 +3864,7 @@ t_map(Fun, T) -> t_to_string(T) -> t_to_string(T, dict:new()). --spec t_to_string(erl_type(), dict()) -> string(). +-spec t_to_string(erl_type(), type_table()) -> string(). t_to_string(?any, _RecDict) -> "any()"; @@ -3885,8 +3910,8 @@ t_to_string(?identifier(Set), _RecDict) -> string:join([flat_format("~w()", [T]) || T <- set_to_list(Set)], " | ") end; t_to_string(?opaque(Set), RecDict) -> - string:join([opaque_type(Mod, Name, S, RecDict) || - #opaque{mod = Mod, name = Name, struct = S} + string:join([opaque_type(Mod, Name, Args, S, RecDict) || + #opaque{mod = Mod, name = Name, struct = S, args = Args} <- set_to_list(Set)], " | "); t_to_string(?matchstate(Pres, Slots), RecDict) -> @@ -4025,7 +4050,7 @@ record_fields_to_string([F|Fs], [{FName, _DefType}|FDefs], RecDict, Acc) -> record_fields_to_string([], [], _RecDict, Acc) -> lists:reverse(Acc). --spec record_field_diffs_to_string(erl_type(), dict()) -> string(). +-spec record_field_diffs_to_string(erl_type(), type_table()) -> string(). record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) -> [TagAtom] = atom_vals(Tag), @@ -4059,11 +4084,14 @@ union_sequence(Types, RecDict) -> string:join(List, " | "). -ifdef(DEBUG). -opaque_type(Mod, Name, S, RecDict) -> - opaque_name(Mod, Name, t_to_string(S, RecDict)). +opaque_type(Mod, Name, _Args, S, RecDict) -> + ArgsString = comma_sequence(_Args, RecDict), + String = t_to_string(S, RecDict), + opaque_name(Mod, Name, ArgsString) ++ "[" ++ String ++ "]". -else. -opaque_type(Mod, Name, _S, _RecDict) -> - opaque_name(Mod, Name, ""). +opaque_type(Mod, Name, Args, _S, RecDict) -> + ArgsString = comma_sequence(Args, RecDict), + opaque_name(Mod, Name, ArgsString). -endif. opaque_name(Mod, Name, Extra) -> @@ -4071,11 +4099,16 @@ opaque_name(Mod, Name, Extra) -> flat_format("~s(~s)", [S, Extra]). mod_name(Mod, Name) -> - case is_opaque_builtin(Mod, Name) of + case is_obsolete_opaque_builtin(Mod, Name) of true -> flat_format("~w", [Name]); false -> flat_format("~w:~w", [Mod, Name]) end. +is_obsolete_opaque_builtin(digraph, digraph) -> true; +is_obsolete_opaque_builtin(gb_sets, gb_set) -> true; +is_obsolete_opaque_builtin(gb_trees, gb_tree) -> true; +is_obsolete_opaque_builtin(_, _) -> false. + %%============================================================================= %% %% Build a type from parse forms. @@ -4087,19 +4120,20 @@ mod_name(Mod, Name) -> t_from_form(Form) -> t_from_form(Form, dict:new()). --spec t_from_form(parse_form(), dict()) -> erl_type(). +-spec t_from_form(parse_form(), type_table()) -> erl_type(). t_from_form(Form, RecDict) -> t_from_form(Form, RecDict, dict:new()). --spec t_from_form(parse_form(), dict(), dict()) -> erl_type(). +-spec t_from_form(parse_form(), type_table(), var_table()) -> erl_type(). t_from_form(Form, RecDict, VarDict) -> {T, _R} = t_from_form(Form, [], RecDict, VarDict), T. --type type_names() :: [{'type' | 'opaque' | 'record', atom()}]. --spec t_from_form(parse_form(), type_names(), dict(), dict()) -> +-type type_names() :: [type_key() | record_key()]. + +-spec t_from_form(parse_form(), type_names(), type_table(), var_table()) -> {erl_type(), type_names()}. t_from_form({var, _L, '_'}, _TypeNames, _RecDict, _VarDict) -> @@ -4138,8 +4172,8 @@ t_from_form({type, _L, any, []}, _TypeNames, _RecDict, _VarDict) -> {t_any(), []}; t_from_form({type, _L, arity, []}, _TypeNames, _RecDict, _VarDict) -> {t_arity(), []}; -t_from_form({type, _L, array, []}, _TypeNames, _RecDict, _VarDict) -> - {t_array(), []}; +t_from_form({type, _L, array, []}, TypeNames, RecDict, VarDict) -> + builtin_type(array, t_array(), TypeNames, RecDict, VarDict); t_from_form({type, _L, atom, []}, _TypeNames, _RecDict, _VarDict) -> {t_atom(), []}; t_from_form({type, _L, binary, []}, _TypeNames, _RecDict, _VarDict) -> @@ -4161,10 +4195,10 @@ t_from_form({type, _L, byte, []}, _TypeNames, _RecDict, _VarDict) -> {t_byte(), []}; t_from_form({type, _L, char, []}, _TypeNames, _RecDict, _VarDict) -> {t_char(), []}; -t_from_form({type, _L, dict, []}, _TypeNames, _RecDict, _VarDict) -> - {t_dict(), []}; -t_from_form({type, _L, digraph, []}, _TypeNames, _RecDict, _VarDict) -> - {t_digraph(), []}; +t_from_form({type, _L, dict, []}, TypeNames, RecDict, VarDict) -> + builtin_type(dict, t_dict(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, digraph, []}, TypeNames, RecDict, VarDict) -> + builtin_type(digraph, t_digraph(), TypeNames, RecDict, VarDict); t_from_form({type, _L, float, []}, _TypeNames, _RecDict, _VarDict) -> {t_float(), []}; t_from_form({type, _L, function, []}, _TypeNames, _RecDict, _VarDict) -> @@ -4180,10 +4214,10 @@ t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]}, {L, R1} = list_from_form(Domain, TypeNames, RecDict, VarDict), {T, R2} = t_from_form(Range, TypeNames, RecDict, VarDict), {t_fun(L, T), R1 ++ R2}; -t_from_form({type, _L, gb_set, []}, _TypeNames, _RecDict, _VarDict) -> - {t_gb_set(), []}; -t_from_form({type, _L, gb_tree, []}, _TypeNames, _RecDict, _VarDict) -> - {t_gb_tree(), []}; +t_from_form({type, _L, gb_set, []}, TypeNames, RecDict, VarDict) -> + builtin_type(gb_set, t_gb_set(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, gb_tree, []}, TypeNames, RecDict, VarDict) -> + builtin_type(gb_tree, t_gb_tree(), TypeNames, RecDict, VarDict); t_from_form({type, _L, identifier, []}, _TypeNames, _RecDict, _VarDict) -> {t_identifier(), []}; t_from_form({type, _L, integer, []}, _TypeNames, _RecDict, _VarDict) -> @@ -4256,8 +4290,8 @@ t_from_form({type, _L, maybe_improper_list, [Content, Termination]}, t_from_form({type, _L, product, Elements}, TypeNames, RecDict, VarDict) -> {L, R} = list_from_form(Elements, TypeNames, RecDict, VarDict), {t_product(L), R}; -t_from_form({type, _L, queue, []}, _TypeNames, _RecDict, _VarDict) -> - {t_queue(), []}; +t_from_form({type, _L, queue, []}, TypeNames, RecDict, VarDict) -> + builtin_type(queue, t_queue(), TypeNames, RecDict, VarDict); t_from_form({type, _L, range, [From, To]} = Type, _TypeNames, _RecDict, _VarDict) -> case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of @@ -4269,14 +4303,14 @@ t_from_form({type, _L, record, [Name|Fields]}, TypeNames, RecDict, VarDict) -> record_from_form(Name, Fields, TypeNames, RecDict, VarDict); t_from_form({type, _L, reference, []}, _TypeNames, _RecDict, _VarDict) -> {t_reference(), []}; -t_from_form({type, _L, set, []}, _TypeNames, _RecDict, _VarDict) -> - {t_set(), []}; +t_from_form({type, _L, set, []}, TypeNames, RecDict, VarDict) -> + builtin_type(set, t_set(), TypeNames, RecDict, VarDict); t_from_form({type, _L, string, []}, _TypeNames, _RecDict, _VarDict) -> {t_string(), []}; t_from_form({type, _L, term, []}, _TypeNames, _RecDict, _VarDict) -> {t_any(), []}; -t_from_form({type, _L, tid, []}, _TypeNames, _RecDict, _VarDict) -> - {t_tid(), []}; +t_from_form({type, _L, tid, []}, TypeNames, RecDict, VarDict) -> + builtin_type(tid, t_tid(), TypeNames, RecDict, VarDict); t_from_form({type, _L, timeout, []}, _TypeNames, _RecDict, _VarDict) -> {t_timeout(), []}; t_from_form({type, _L, tuple, any}, _TypeNames, _RecDict, _VarDict) -> @@ -4288,61 +4322,67 @@ t_from_form({type, _L, union, Args}, TypeNames, RecDict, VarDict) -> {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict), {t_sup(L), R}; t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) -> + type_from_form(Name, Args, TypeNames, RecDict, VarDict); +t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, + _RecDict, _VarDict) -> + {t_opaque(Mod, Name, Args, Rep), []}. + +builtin_type(Name, Type, TypeNames, RecDict, VarDict) -> + case lookup_type(Name, 0, RecDict) of + {_, {_M, _T, _A}} -> + type_from_form(Name, [], TypeNames, RecDict, VarDict); + error -> + {Type, []} + end. + +type_from_form(Name, Args, TypeNames, RecDict, VarDict) -> ArgsLen = length(Args), + ArgTypes = forms_to_types(Args, TypeNames, RecDict, VarDict), case lookup_type(Name, ArgsLen, RecDict) of {type, {_Module, Type, ArgNames}} -> - case can_unfold_more({type, Name}, TypeNames) of + TypeName = {type, Name, ArgsLen}, + case can_unfold_more(TypeName, TypeNames) of true -> - List = lists:zipwith( - fun(ArgName, ArgType) -> - {Ttemp, _R} = t_from_form(ArgType, TypeNames, - RecDict, VarDict), - {ArgName, Ttemp} - end, - ArgNames, Args), + List = lists:zip(ArgNames, ArgTypes), TmpVarDict = dict:from_list(List), - {T, R} = t_from_form(Type, [{type, Name}|TypeNames], + {T, R} = t_from_form(Type, [TypeName|TypeNames], RecDict, TmpVarDict), - case lists:member({type, Name}, R) of + case lists:member(TypeName, R) of true -> {t_limit(T, ?REC_TYPE_LIMIT), R}; false -> {T, R} end; - false -> {t_any(), [{type, Name}]} + false -> {t_any(), [TypeName]} end; {opaque, {Module, Type, ArgNames}} -> + TypeName = {opaque, Name, ArgsLen}, {Rep, Rret} = - case can_unfold_more({opaque, Name}, TypeNames) of + case can_unfold_more(TypeName, TypeNames) of true -> - List = lists:zipwith( - fun(ArgName, ArgType) -> - {Ttemp, _R} = t_from_form(ArgType, TypeNames, - RecDict, VarDict), - {ArgName, Ttemp} - end, - ArgNames, Args), + List = lists:zip(ArgNames, ArgTypes), TmpVarDict = dict:from_list(List), - {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames], + {T, R} = t_from_form(Type, [TypeName|TypeNames], RecDict, TmpVarDict), - case lists:member({opaque, Name}, R) of + case lists:member(TypeName, R) of true -> {t_limit(T, ?REC_TYPE_LIMIT), R}; false -> {T, R} end; - false -> {t_any(), [{opaque, Name}]} + false -> {t_any(), [TypeName]} end, - Tret = t_from_form({opaque, -1, Name, {Module, Args, Rep}}, - RecDict, VarDict), - {Tret, Rret}; + Args2 = [subst_all_vars_to_any(ArgType) || ArgType <- ArgTypes], + {skip_opaque_alias(Rep, Module, Name, Args2), Rret}; error -> Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]), throw({error, Msg}) - end; -t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, - _RecDict, _VarDict) -> - case Args of - [] -> {t_opaque(Mod, Name, Args, Rep), []}; - _ -> throw({error, "Polymorphic opaque types not supported yet"}) end. +forms_to_types(Forms, TypeNames, RecDict, VarDict) -> + {Types, _} = list_from_form(Forms, TypeNames, RecDict, VarDict), + Types. + +skip_opaque_alias(?opaque(_) = T, _Mod, _Name, _Args) -> T; +skip_opaque_alias(T, Module, Name, Args) -> + t_opaque(Module, Name, Args, T). + record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) -> case can_unfold_more({record, Name}, TypeNames) of true -> @@ -4403,8 +4443,10 @@ build_field_dict([], _TypeNames, _RecDict, _VarDict, Acc) -> get_mod_record([{FieldName, DeclType}|Left1], [{FieldName, ModType}|Left2], Acc) -> - case t_is_var(ModType) orelse t_is_remote(ModType) orelse - t_is_subtype(ModType, DeclType) of + ModTypeNoVars = subst_all_vars_to_any(ModType), + case + t_is_remote(ModTypeNoVars) orelse t_is_subtype(ModTypeNoVars, DeclType) + of false -> {error, FieldName}; true -> get_mod_record(Left1, Left2, [{FieldName, ModType}|Acc]) end; @@ -4561,7 +4603,7 @@ is_erl_type(?unit) -> true; is_erl_type(#c{}) -> true; is_erl_type(_) -> false. --spec lookup_record(atom(), dict()) -> +-spec lookup_record(atom(), type_table()) -> 'error' | {'ok', [{atom(), parse_form() | erl_type()}]}. lookup_record(Tag, RecDict) when is_atom(Tag) -> @@ -4576,7 +4618,8 @@ lookup_record(Tag, RecDict) when is_atom(Tag) -> error end. --spec lookup_record(atom(), arity(), dict()) -> 'error' | {'ok', [{atom(), erl_type()}]}. +-spec lookup_record(atom(), arity(), type_table()) -> + 'error' | {'ok', [{atom(), erl_type()}]}. lookup_record(Tag, Arity, RecDict) when is_atom(Tag) -> case dict:find({record, Tag}, RecDict) of @@ -4595,7 +4638,8 @@ lookup_type(Name, Arity, RecDict) -> {ok, Found} -> {type, Found} end. --spec type_is_defined('type' | 'opaque', atom(), arity(), dict()) -> boolean(). +-spec type_is_defined('type' | 'opaque', atom(), arity(), type_table()) -> + boolean(). type_is_defined(TypeOrOpaque, Name, Arity, RecDict) -> dict:is_key({TypeOrOpaque, Name, Arity}, RecDict). @@ -4627,6 +4671,30 @@ do_opaque(?union(List) = Type, Opaques, Pred) -> do_opaque(Type, _Opaques, Pred) -> Pred(Type). +is_same_type_name(ModNameArgs, ModNameArgs) -> true; +is_same_type_name({Mod, Name, Args1}, {Mod, Name, Args2}) -> + all_any(Args1) orelse all_any(Args2); +is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) -> + is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2). + +all_any([]) -> true; +all_any([T|L]) -> + t_is_any(T) andalso all_any(L); +all_any(_) -> false. + +%% Compatibility. In Erlang/OTP 17 the pre-defined opaque types +%% digraph() and so on can be used, but there are also new types such +%% as digraph:graph() with the exact same meaning. In Erlang/OTP R18.0 +%% all but the last clause can be removed. + +is_same_type_name2(digraph, digraph, [], digraph, graph, []) -> true; +is_same_type_name2(digraph, graph, [], digraph, digraph, []) -> true; +is_same_type_name2(gb_sets, gb_set, [], gb_sets, set, [_]) -> true; +is_same_type_name2(gb_sets, set, [_], gb_sets, gb_set, []) -> true; +is_same_type_name2(gb_trees, gb_tree, [], gb_trees, tree, [_, _]) -> true; +is_same_type_name2(gb_trees, tree, [_, _], gb_trees, gb_tree, []) -> true; +is_same_type_name2(_, _, _, _, _, _) -> false. + %% ----------------------------------- %% Set %% diff --git a/lib/hipe/flow/cfg.hrl b/lib/hipe/flow/cfg.hrl index 95bf5f7194..1f7a162f27 100644 --- a/lib/hipe/flow/cfg.hrl +++ b/lib/hipe/flow/cfg.hrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -42,12 +42,12 @@ %% %% Data is a triple with a dict of constants, a list of labels and an integer %% --type cfg_data() :: {dict(), [cfg_lbl()], non_neg_integer()}. +-type cfg_data() :: {dict:dict(), [cfg_lbl()], non_neg_integer()}. %% %% The following is to be used by other modules %% --record(cfg, {table = gb_trees:empty() :: gb_tree(), +-record(cfg, {table = gb_trees:empty() :: gb_trees:tree(), info :: #cfg_info{}, data :: cfg_data()}). -type cfg() :: #cfg{}. diff --git a/lib/hipe/flow/cfg.inc b/lib/hipe/flow/cfg.inc index 62f399a81c..f0b1a75737 100644 --- a/lib/hipe/flow/cfg.inc +++ b/lib/hipe/flow/cfg.inc @@ -4,7 +4,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -534,7 +534,7 @@ breadth_list([], Vis, _CFG, BO) -> {Vis, BO}. -endif. --spec none_visited() -> gb_set(). +-spec none_visited() -> gb_sets:set(). none_visited() -> gb_sets:empty(). diff --git a/lib/hipe/flow/hipe_dominators.erl b/lib/hipe/flow/hipe_dominators.erl index 113a32c3b7..50d45c7c72 100644 --- a/lib/hipe/flow/hipe_dominators.erl +++ b/lib/hipe/flow/hipe_dominators.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -57,7 +57,7 @@ -record(domTree, {root :: cfg_lbl(), size = 0 :: non_neg_integer(), - nodes = gb_trees:empty() :: gb_tree()}). + nodes = gb_trees:empty() :: gb_trees:tree()}). -type domTree() :: #domTree{}. %%>----------------------------------------------------------------------< @@ -590,7 +590,7 @@ domTree_pp_children([], _) -> %% %%======================================================================== --type domFrontier() :: gb_tree(). +-type domFrontier() :: gb_trees:tree(). %%>----------------------------------------------------------------------< %% Procedure : domFrontier_create diff --git a/lib/hipe/flow/liveness.inc b/lib/hipe/flow/liveness.inc index 9c5eaf3e68..6f161fb269 100644 --- a/lib/hipe/flow/liveness.inc +++ b/lib/hipe/flow/liveness.inc @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -71,7 +71,7 @@ %% The generic liveness analysis %% --spec analyze(cfg()) -> gb_tree(). +-spec analyze(cfg()) -> gb_trees:tree(). -ifdef(HIPE_LIVENESS_CALC_LARGEST_LIVESET). analyze(CFG) -> @@ -209,7 +209,7 @@ successors(L, Liveness) -> {_GK, _LiveIn, Successors} = liveness_lookup(L, Liveness), Successors. --spec livein(gb_tree(), _) -> [_]. +-spec livein(gb_trees:tree(), _) -> [_]. livein(Liveness, L) -> {_GK, LiveIn, _Successors} = liveness_lookup(L, Liveness), diff --git a/lib/hipe/icode/hipe_icode_callgraph.erl b/lib/hipe/icode/hipe_icode_callgraph.erl index 5789328f47..ccf97ecc17 100644 --- a/lib/hipe/icode/hipe_icode_callgraph.erl +++ b/lib/hipe/icode/hipe_icode_callgraph.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -46,7 +46,7 @@ -type mfa_icode() :: {mfa(), #icode{}}. --record(icode_callgraph, {codedict :: dict(), ordered_sccs :: [[mfa()]]}). +-record(icode_callgraph, {codedict :: dict:dict(), ordered_sccs :: [[mfa()]]}). %%------------------------------------------------------------------------ %% Exported functions diff --git a/lib/hipe/icode/hipe_icode_cfg.erl b/lib/hipe/icode/hipe_icode_cfg.erl index 9b4a10e273..f6c2b0600b 100644 --- a/lib/hipe/icode/hipe_icode_cfg.erl +++ b/lib/hipe/icode/hipe_icode_cfg.erl @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,8 +54,8 @@ -spec postorder(cfg()) -> [icode_lbl()]. -spec reverse_postorder(cfg()) -> [icode_lbl()]. --spec is_visited(icode_lbl(), gb_set()) -> boolean(). --spec visit(icode_lbl(), gb_set()) -> gb_set(). +-spec is_visited(icode_lbl(), gb_sets:set()) -> boolean(). +-spec visit(icode_lbl(), gb_sets:set()) -> gb_sets:set(). -spec bb(cfg(), icode_lbl()) -> 'not_found' | bb(). -spec bb_add(cfg(), icode_lbl(), bb()) -> cfg(). diff --git a/lib/hipe/icode/hipe_icode_coordinator.erl b/lib/hipe/icode/hipe_icode_coordinator.erl index 79e3304e6f..c69db9afa9 100644 --- a/lib/hipe/icode/hipe_icode_coordinator.erl +++ b/lib/hipe/icode/hipe_icode_coordinator.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,9 +49,9 @@ coordinate(CG, Escaping, NonEscaping, Mod) -> -type mfalists() :: {[mfa()], [mfa()]}. --spec coordinate(mfalists(), hipe_digraph:hdg(), gb_tree(), - fun((mfalists(), gb_tree()) -> mfalists()), - fun((gb_tree()) -> 'ok'), pid()) -> no_return(). +-spec coordinate(mfalists(), hipe_digraph:hdg(), gb_trees:tree(), + fun((mfalists(), gb_trees:tree()) -> mfalists()), + fun((gb_trees:tree()) -> 'ok'), pid()) -> no_return(). coordinate(MFALists, CG, PM, Restart, LastAction, ServerPid) -> case MFALists of diff --git a/lib/hipe/icode/hipe_icode_exceptions.erl b/lib/hipe/icode/hipe_icode_exceptions.erl index 00caffb24b..6191c536ad 100644 --- a/lib/hipe/icode/hipe_icode_exceptions.erl +++ b/lib/hipe/icode/hipe_icode_exceptions.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -397,9 +397,9 @@ get_renaming(C, Map) -> succ :: #cfg{}, pred :: #cfg{}, start_labels :: [icode_lbl(),...], - visited = hipe_icode_cfg:none_visited() :: gb_set(), - out = gb_trees:empty() :: gb_tree(), - in = gb_trees:empty() :: gb_tree() + visited = hipe_icode_cfg:none_visited() :: gb_sets:set(), + out = gb_trees:empty() :: gb_trees:tree(), + in = gb_trees:empty() :: gb_trees:tree() }). init_state(CFG) -> diff --git a/lib/hipe/icode/hipe_icode_fp.erl b/lib/hipe/icode/hipe_icode_fp.erl index a2ca6132d1..c0cd9bd2d1 100644 --- a/lib/hipe/icode/hipe_icode_fp.erl +++ b/lib/hipe/icode/hipe_icode_fp.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -33,8 +33,8 @@ -include("hipe_icode.hrl"). -include("../flow/cfg.hrl"). --record(state, {edge_map = gb_trees:empty() :: gb_tree(), - fp_ebb_map = gb_trees:empty() :: gb_tree(), +-record(state, {edge_map = gb_trees:empty() :: gb_trees:tree(), + fp_ebb_map = gb_trees:empty() :: gb_trees:tree(), cfg :: #cfg{}}). %%-------------------------------------------------------------------- diff --git a/lib/hipe/icode/hipe_icode_instruction_counter.erl b/lib/hipe/icode/hipe_icode_instruction_counter.erl index 92658d294a..f44adfe149 100644 --- a/lib/hipe/icode/hipe_icode_instruction_counter.erl +++ b/lib/hipe/icode/hipe_icode_instruction_counter.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -64,7 +64,8 @@ walktrough_bb(BB, Info) -> %% The counter specific functions %%------------------------------------------------------------------- --spec compare(gb_tree(), gb_tree(), gb_tree()) -> gb_tree(). +-spec compare(gb_trees:tree(), gb_trees:tree(), gb_trees:tree()) -> + gb_trees:tree(). compare(Name, Old, New) -> NewList = gb_trees:to_list(New), diff --git a/lib/hipe/icode/hipe_icode_range.erl b/lib/hipe/icode/hipe_icode_range.erl index 1a2cbfae31..fbc58f3568 100644 --- a/lib/hipe/icode/hipe_icode_range.erl +++ b/lib/hipe/icode/hipe_icode_range.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -72,8 +72,8 @@ -type final_fun() :: fun((mfa(), [range()]) -> 'ok'). -type data() :: {mfa(), args_fun(), call_fun(), final_fun()}. -type label() :: non_neg_integer(). --type info() :: gb_tree(). --type work_list() :: {[label()], [label()], set()}. +-type info() :: gb_trees:tree(). +-type work_list() :: {[label()], [label()], sets:set()}. -type variable() :: #icode_variable{}. -type annotated_variable() :: #icode_variable{}. -type argument() :: #icode_const{} | variable(). @@ -82,9 +82,9 @@ -type last_instr_return() :: {instr_split_info(), range()}. -record(state, {info_map = gb_trees:empty() :: info(), - counter = dict:new() :: dict(), + counter = dict:new() :: dict:dict(), cfg :: cfg(), - liveness = gb_trees:empty() :: gb_tree(), + liveness = gb_trees:empty() :: gb_trees:tree(), ret_type :: range(), lookup_fun :: call_fun(), result_action :: final_fun()}). diff --git a/lib/hipe/icode/hipe_icode_ssa.erl b/lib/hipe/icode/hipe_icode_ssa.erl index 4607a96dda..2c4b6d9409 100644 --- a/lib/hipe/icode/hipe_icode_ssa.erl +++ b/lib/hipe/icode/hipe_icode_ssa.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ -include("../ssa/hipe_ssa.inc"). %% Declarations for exported functions which are Icode-specific. --spec ssa_liveness__analyze(#cfg{}) -> gb_tree(). +-spec ssa_liveness__analyze(#cfg{}) -> gb_trees:tree(). -spec ssa_liveness__livein(_, icode_lbl()) -> [#icode_variable{}]. %% -spec ssa_liveness__livein(_, icode_lbl(), _) -> [#icode_var{}]. diff --git a/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl b/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl index 2337ef9323..772e30eada 100644 --- a/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl +++ b/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -70,9 +70,9 @@ %% var - maps variables to expression value numbers. These variables are %% defined or used by the structure expressions. --record(maps, {var = gb_trees:empty() :: gb_tree(), - instr = gb_trees:empty() :: gb_tree(), - expr = gb_trees:empty() :: gb_tree()}). +-record(maps, {var = gb_trees:empty() :: gb_trees:tree(), + instr = gb_trees:empty() :: gb_trees:tree(), + expr = gb_trees:empty() :: gb_trees:tree()}). maps_var(#maps{var = Out}) -> Out. maps_instr(#maps{instr = Out}) -> Out. @@ -211,10 +211,10 @@ varinfo_use_add(#varinfo{use = UseSet} = I, Use) -> pred = none :: 'none' | [icode_lbl()], succ = none :: 'none' | [icode_lbl()], code = [] :: [tuple()], % [illegal_icode_instr()] - phi = gb_trees:empty() :: gb_tree(), + phi = gb_trees:empty() :: gb_trees:tree(), varmap = [] :: [{icode_var(), icode_var()}], pre_loop = false :: boolean(), - non_struct_defs = gb_sets:new() :: gb_set(), + non_struct_defs = gb_sets:new() :: gb_sets:set(), up_expr = none :: 'none' | ?SETS:?SET(_), killed_expr = none :: 'none' | ?SETS:?SET(_), sub_inserts = ?SETS:new() :: ?SETS:?SET(_), @@ -319,7 +319,7 @@ node_create(Label, Pred, Succ) -> start_label = none :: 'none' | icode_lbl(), rev_postorder = none :: 'none' | [icode_lbl()], all_expr = none :: 'none' | [non_neg_integer()], - tree = gb_trees:empty() :: gb_tree()}). + tree = gb_trees:empty() :: gb_trees:tree()}). nodes_postorder(#nodes{postorder = Out}) -> Out. nodes_rev_postorder(#nodes{rev_postorder = Out}) -> Out. @@ -356,7 +356,7 @@ nodes_create() -> #nodes{}. %% del_red_test - flag that is set to true when the reduction test %% has been inserted is used to move the reduction test. --record(update, {inserted = gb_trees:empty() :: gb_tree(), +-record(update, {inserted = gb_trees:empty() :: gb_trees:tree(), del_red_test = false :: boolean()}). update_inserted_lookup(#update{inserted = Inserted}, ExprId) -> diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl index 046949d2f2..65876b83ea 100644 --- a/lib/hipe/icode/hipe_icode_type.erl +++ b/lib/hipe/icode/hipe_icode_type.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -97,9 +97,9 @@ t_pid/0, t_port/0, t_reference/0, t_subtract/2, t_sup/2, t_to_tlist/1, t_tuple/0, t_tuple/1, t_tuple_sizes/1]). --record(state, {info_map = gb_trees:empty() :: gb_tree(), +-record(state, {info_map = gb_trees:empty() :: gb_trees:tree(), cfg :: cfg(), - liveness = gb_trees:empty() :: gb_tree(), + liveness = gb_trees:empty() :: gb_trees:tree(), arg_types :: [erl_types:erl_type()], ret_type = [t_none()] :: [erl_types:erl_type()], lookupfun :: call_fun(), diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src index 7db4db8a57..bcdfcb0e03 100644 --- a/lib/hipe/main/hipe.app.src +++ b/lib/hipe/main/hipe.app.src @@ -192,7 +192,6 @@ hipe_tagscheme, hipe_temp_map, hipe_timing, - hipe_tool, hipe_vectors, hipe_x86, hipe_x86_assemble, diff --git a/lib/hipe/main/hipe.appup.src b/lib/hipe/main/hipe.appup.src index 1d5a0d93f5..02679fab21 100644 --- a/lib/hipe/main/hipe.appup.src +++ b/lib/hipe/main/hipe.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,5 +15,4 @@ %% under the License. %% %% %CopyrightEnd% -%% -{"%VSN%",[],[]}. +{"%VSN%", [], []}. diff --git a/lib/hipe/misc/hipe_consttab.erl b/lib/hipe/misc/hipe_consttab.erl index c381e6a057..2b02f54b5c 100644 --- a/lib/hipe/misc/hipe_consttab.erl +++ b/lib/hipe/misc/hipe_consttab.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -462,7 +462,7 @@ update_referred_labels(Table, LabelMap) -> tree_keys(T) -> dict:fetch_keys(T). --spec tree_to_list(dict()) -> [{_, _}]. +-spec tree_to_list(dict:dict()) -> [{_, _}]. tree_to_list(T) -> dict:to_list(T). @@ -486,11 +486,11 @@ tree_lookup(Key, T) -> none end. --spec tree_empty() -> dict(). +-spec tree_empty() -> dict:dict(). tree_empty() -> dict:new(). --spec tree_lookup_key_for_value(ctdata(), dict()) -> 'none' | {'value', _}. +-spec tree_lookup_key_for_value(ctdata(), dict:dict()) -> 'none' | {'value', _}. tree_lookup_key_for_value(Val, T) -> tree_lookup_key_for_value_1(tree_to_list(T), Val). diff --git a/lib/hipe/misc/hipe_consttab.hrl b/lib/hipe/misc/hipe_consttab.hrl index 39018dac34..aea3c5bc88 100644 --- a/lib/hipe/misc/hipe_consttab.hrl +++ b/lib/hipe/misc/hipe_consttab.hrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,6 +22,6 @@ -type ct_alignment() :: 4 | 8. -type hipe_constlbl() :: non_neg_integer(). --type hipe_consttab() :: {dict(), [hipe_constlbl()], hipe_constlbl()}. +-type hipe_consttab() :: {dict:dict(), [hipe_constlbl()], hipe_constlbl()}. %%----------------------------------------------------------------------------- diff --git a/lib/hipe/misc/hipe_pack_constants.erl b/lib/hipe/misc/hipe_pack_constants.erl index e214d7ebbc..ca8a9e6bf7 100644 --- a/lib/hipe/misc/hipe_pack_constants.erl +++ b/lib/hipe/misc/hipe_pack_constants.erl @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -198,7 +198,7 @@ compact_dests([], Dest, AccofDest, Acc) -> slim_constmap(Map) -> slim_constmap(Map, gb_sets:new(), []). --spec slim_constmap([#pcm_entry{}], gb_set(), [raw_data()]) -> [raw_data()]. +-spec slim_constmap([#pcm_entry{}], gb_sets:set(), [raw_data()]) -> [raw_data()]. slim_constmap([#pcm_entry{const_num=ConstNo, start=Offset, type=Type, raw_data=Term}|Rest], Inserted, Acc) -> case gb_sets:is_member(ConstNo, Inserted) of diff --git a/lib/hipe/misc/hipe_sdi.erl b/lib/hipe/misc/hipe_sdi.erl index ef1b5b48c5..9a2ff78ecf 100644 --- a/lib/hipe/misc/hipe_sdi.erl +++ b/lib/hipe/misc/hipe_sdi.erl @@ -3,7 +3,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %%% %%% The contents of this file are subject to the Erlang Public License, %%% Version 1.1, (the "License"); you may not use this file except in @@ -50,7 +50,7 @@ -record(pass1, {prevSdi :: integer(), preS = [] :: [#pre_sdi_data{}], - labelMap = gb_trees:empty() :: gb_tree()}). + labelMap = gb_trees:empty() :: gb_trees:tree()}). -record(sdi_data, {address :: address(), label_address :: address(), @@ -105,11 +105,11 @@ pass1_add_sdi(Pass1, Address, Label, SdiInfo) -> PreSdiData = #pre_sdi_data{address=Address, label=Label, si=SdiInfo}, Pass1#pass1{prevSdi=PrevSdi+1, preS=[PreSdiData|PreS]}. --spec pass1_finalise(#pass1{}) -> {non_neg_integer(),tuple(),gb_tree()}. +-spec pass1_finalise(#pass1{}) -> {non_neg_integer(),tuple(),gb_trees:tree()}. pass1_finalise(#pass1{prevSdi=PrevSdi, preS=PreS, labelMap=LabelMap}) -> {PrevSdi+1, pass1_finalise_preS(PreS, LabelMap, []), LabelMap}. --spec pass1_finalise_preS([#pre_sdi_data{}], gb_tree(), [#sdi_data{}]) -> +-spec pass1_finalise_preS([#pre_sdi_data{}], gb_trees:tree(), [#sdi_data{}]) -> tuple(). pass1_finalise_preS([], _LabelMap, S) -> vector_from_list(S); pass1_finalise_preS([PreSdiData|PreS], LabelMap, S) -> @@ -122,7 +122,7 @@ pass1_finalise_preS([PreSdiData|PreS], LabelMap, S) -> %%% Pass2. --spec pass2(#pass1{}) -> {gb_tree(), non_neg_integer()}. +-spec pass2(#pass1{}) -> {gb_trees:tree(), non_neg_integer()}. pass2(Pass1) -> {N,SDIS,LabelMap} = pass1_finalise(Pass1), LONG = mk_long(N), @@ -339,13 +339,14 @@ initINCR(SdiNr, PrevIncr, N, LONG, INCREMENT) -> %%% a and previous sdi i is remapped to a+incr(i), where %%% incr(i) = if i < 0 then 0 else INCREMENT[i]. --spec adjust_label_map(gb_tree(), hipe_array()) -> gb_tree(). +-spec adjust_label_map(gb_trees:tree(), hipe_array()) -> gb_trees:tree(). adjust_label_map(LabelMap, INCREMENT) -> applyIncr(gb_trees:to_list(LabelMap), INCREMENT, gb_trees:empty()). -type label_pair() :: {label(), #label_data{}}. --spec applyIncr([label_pair()], hipe_array(), gb_tree()) -> gb_tree(). +-spec applyIncr([label_pair()], hipe_array(), gb_trees:tree()) -> + gb_trees:tree(). applyIncr([], _INCREMENT, LabelMap) -> LabelMap; applyIncr([{Label,LabelData}|List], INCREMENT, LabelMap) -> #label_data{address=Address, prevSdi=PrevSdi} = LabelData, diff --git a/lib/hipe/regalloc/hipe_ig_moves.erl b/lib/hipe/regalloc/hipe_ig_moves.erl index 186c87a690..ebc6ebc20d 100644 --- a/lib/hipe/regalloc/hipe_ig_moves.erl +++ b/lib/hipe/regalloc/hipe_ig_moves.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ -record(ig_moves, {movelist :: hipe_vector(), nrmoves = 0 :: non_neg_integer(), moveinsns = [] :: [{_,_}], - moveset = gb_sets:empty() :: gb_set()}). + moveset = gb_sets:empty() :: gb_sets:set()}). %%----------------------------------------------------------------------------- diff --git a/lib/hipe/ssa/hipe_ssa_const_prop.inc b/lib/hipe/ssa/hipe_ssa_const_prop.inc index 2fce384197..0876fca34a 100644 --- a/lib/hipe/ssa/hipe_ssa_const_prop.inc +++ b/lib/hipe/ssa/hipe_ssa_const_prop.inc @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -72,10 +72,10 @@ visit_expressions([Inst | Insts], Environment, FlowWork, SSAWork) -> %%----------------------------------------------------------------------------- -record(env, {cfg :: #cfg{}, - executable_flags = gb_sets:empty() :: gb_set(), - handled_blocks = gb_sets:empty() :: gb_set(), - lattice_values = gb_trees:empty() :: gb_tree(), - ssa_edges = gb_trees:empty() :: gb_tree() + executable_flags = gb_sets:empty() :: gb_sets:set(), + handled_blocks = gb_sets:empty() :: gb_sets:set(), + lattice_values = gb_trees:empty() :: gb_trees:tree(), + ssa_edges = gb_trees:empty() :: gb_trees:tree() }). create_env(CFG) -> diff --git a/lib/hipe/test/Makefile b/lib/hipe/test/Makefile new file mode 100644 index 0000000000..19fa227912 --- /dev/null +++ b/lib/hipe/test/Makefile @@ -0,0 +1,65 @@ +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + hipe_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/hipe_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) $(GEN_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) hipe.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + +release_docs_spec: diff --git a/lib/hipe/test/hipe.spec b/lib/hipe/test/hipe.spec new file mode 100644 index 0000000000..6b0b226dc3 --- /dev/null +++ b/lib/hipe/test/hipe.spec @@ -0,0 +1 @@ +{suites,"../hipe_test",all}. diff --git a/lib/hipe/test/hipe_SUITE.erl b/lib/hipe/test/hipe_SUITE.erl new file mode 100644 index 0000000000..554bc972f6 --- /dev/null +++ b/lib/hipe/test/hipe_SUITE.erl @@ -0,0 +1,55 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(hipe_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [app, appup]. + +groups() -> + []. + +init_per_suite(Config) -> + case erlang:system_info(hipe_architecture) of + undefined -> {skip, "HiPE not available or enabled"}; + _ -> Config + end. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the hipe app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(hipe, tolerant). + +appup() -> + [{doc, "Test that the hipe appup file is ok"}]. +appup(Config) when is_list(Config) -> + AppupFile = "hipe.appup", + AppupPath = filename:join([code:lib_dir(hipe), "ebin", AppupFile]), + {ok, [{_Vsn, [], []}]} = file:consult(AppupPath). diff --git a/lib/hipe/util/hipe_digraph.erl b/lib/hipe/util/hipe_digraph.erl index fcfaa64684..01b1f8c77c 100644 --- a/lib/hipe/util/hipe_digraph.erl +++ b/lib/hipe/util/hipe_digraph.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,10 +36,10 @@ -type ordset(T) :: [T]. % XXX: temporarily --record(hipe_digraph, {edges = dict:new() :: dict(), - rev_edges = dict:new() :: dict(), +-record(hipe_digraph, {edges = dict:new() :: dict:dict(), + rev_edges = dict:new() :: dict:dict(), leaves = ordsets:new() :: ordset(_), % ??? - nodes = sets:new() :: set()}). + nodes = sets:new() :: sets:set()}). -opaque hdg() :: #hipe_digraph{}. diff --git a/lib/hipe/util/hipe_dot.erl b/lib/hipe/util/hipe_dot.erl index e4a47ae0c4..94f7fd60cc 100644 --- a/lib/hipe/util/hipe_dot.erl +++ b/lib/hipe/util/hipe_dot.erl @@ -2,7 +2,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %%% %%% The contents of this file are subject to the Erlang Public License, %%% Version 1.1, (the "License"); you may not use this file except in @@ -70,13 +70,13 @@ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec translate_digraph(digraph(), string(), string()) -> 'ok'. +-spec translate_digraph(digraph:graph(), string(), string()) -> 'ok'. translate_digraph(G, FileName, GName) -> translate_digraph(G, FileName, GName, fun(X) -> io_lib:format("~p", [X]) end, []). --spec translate_digraph(digraph(), string(), string(), +-spec translate_digraph(digraph:graph(), string(), string(), fun((_) -> string()), [_]) -> 'ok'. translate_digraph(G, FileName, GName, Fun, Opts) -> diff --git a/lib/hipe/util/hipe_vectors.hrl b/lib/hipe/util/hipe_vectors.hrl index 043faf4c91..5e24db238d 100644 --- a/lib/hipe/util/hipe_vectors.hrl +++ b/lib/hipe/util/hipe_vectors.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -24,5 +24,5 @@ -endif. -ifdef(USE_GBTREES). --type hipe_vector() :: gb_tree(). +-type hipe_vector() :: gb_trees:tree(). -endif. diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 80c8b2439e..a89a457a51 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2013. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -55,8 +55,8 @@ headers, % #http_response_h{} body, % binary() mfa, % {Module, Function, Args} - pipeline = queue:new(), % queue() - keep_alive = queue:new(), % queue() + pipeline = queue:new(), % queue:queue() + keep_alive = queue:new(), % queue:queue() status, % undefined | new | pipeline | keep_alive | close | {ssl_tunnel, Request} canceled = [], % [RequestId] max_header_size = nolimit, % nolimit | integer() diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index c63dcafa6c..7a909b2f3f 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -1,7 +1,7 @@ -%% This is an -*- erlang -*- file. +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,11 +15,7 @@ %% under the License. %% %% %CopyrightEnd% - {"%VSN%", - [ - {<<"5\\.*">>, [{restart_application, inets}]} - ], - [ - {<<"5\\.*">>, [{restart_application, inets}]} -]}. + [{<<"5\\..*">>,[{restart_application, inets}]}], + [{<<"5\\..*">>,[{restart_application, inets}]}] +}. diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl index d563b52ae7..a8051c6c85 100644 --- a/lib/inets/test/inets_appup_test.erl +++ b/lib/inets/test/inets_appup_test.erl @@ -23,13 +23,7 @@ -module(inets_appup_test). -compile(export_all). --compile({no_auto_import,[error/1]}). - --include("inets_test_lib.hrl"). - - - % t() -> megaco_test_lib:t(?MODULE). - % t(Case) -> megaco_test_lib:t({?MODULE, Case}). +-include_lib("common_test/include/ct.hrl"). %% Test server callbacks @@ -59,16 +53,9 @@ end_per_group(_GroupName, Config) -> init_per_suite(suite) -> []; init_per_suite(doc) -> []; init_per_suite(Config) when is_list(Config) -> - AppFile = file_name(inets, ".app"), - AppupFile = file_name(inets, ".appup"), - [{app_file, AppFile}, {appup_file, AppupFile}|Config]. + Config. -file_name(App, Ext) -> - LibDir = code:lib_dir(App), - filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]). - - end_per_suite(suite) -> []; end_per_suite(doc) -> []; end_per_suite(Config) when is_list(Config) -> @@ -77,282 +64,7 @@ end_per_suite(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -appup(suite) -> - []; -appup(doc) -> - "perform a simple check of the appup file"; +appup() -> + [{doc, "Perform a simple check of the inets appup file"}]. appup(Config) when is_list(Config) -> - AppupFile = key1search(appup_file, Config), - AppFile = key1search(app_file, Config), - Modules = modules(AppFile), - check_appup(AppupFile, Modules). - -modules(File) -> - case file:consult(File) of - {ok, [{application,inets,Info}]} -> - case lists:keysearch(modules,1,Info) of - {value, {modules, Modules}} -> - Modules; - false -> - fail({bad_appinfo, Info}) - end; - Error -> - fail({bad_appfile, Error}) - end. - - -check_appup(AppupFile, Modules) -> - case file:consult(AppupFile) of - {ok, [{V, UpFrom, DownTo}]} -> -% io:format("~p => " -% "~n ~p" -% "~n ~p" -% "~n", [V, UpFrom, DownTo]), - check_appup(V, UpFrom, DownTo, Modules); - Else -> - fail({bad_appupfile, Else}) - end. - - -check_appup(V, UpFrom, DownTo, Modules) -> - check_version(V), - check_depends(up, UpFrom, Modules), - check_depends(down, DownTo, Modules), - ok. - - -check_depends(_, [], _) -> - ok; -check_depends(UpDown, [Dep|Deps], Modules) -> - check_depend(UpDown, Dep, Modules), - check_depends(UpDown, Deps, Modules). - - -check_depend(UpDown, {V, Instructions}, Modules) -> - check_version(V), - case check_instructions(UpDown, - Instructions, Instructions, [], [], Modules) of - {_Good, []} -> - ok; - {_, Bad} -> - fail({bad_instructions, Bad, UpDown}) - end. - - -check_instructions(_, [], _, Good, Bad, _) -> - {lists:reverse(Good), lists:reverse(Bad)}; -check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) -> - case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of - ok -> - check_instructions(UpDown, Instrs, AllInstr, - [Instr|Good], Bad, Modules); - {error, Reason} -> - check_instructions(UpDown, Instrs, AllInstr, Good, - [{Instr, Reason}|Bad], Modules) - end; -check_instructions(UpDown, Instructions, _, _, _, _) -> - fail({bad_instructions, {UpDown, Instructions}}). - -%% A new module is added -check_instruction(up, {add_module, Module}, _, Modules) - when is_atom(Module) -> - check_module(Module, Modules); - -%% An old module is re-added -check_instruction(down, {add_module, Module}, _, Modules) - when is_atom(Module) -> - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - ok; - ok -> - error({existing_readded_module, Module}) - end; - -%% Removing a module on upgrade: -%% - the module has been removed from the app-file. -%% - check that no module depends on this (removed) module -check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - check_purge(Pre), - check_purge(Post); - ok -> - error({existing_removed_module, Module}) - end; - -%% Removing a module on downgrade: the module exist -%% in the app-file. -check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - case (catch check_module(Module, Modules)) of - ok -> - check_purge(Pre), - check_purge(Post), - check_no_remove_depends(Module, AllInstr); - {error, {unknown_module, Module, Modules}} -> - error({nonexisting_removed_module, Module}) - end; - -check_instruction(up, {load_module, Module, Pre, Post, Depend}, _, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_purge(Pre), - check_purge(Post); - -check_instruction(down, {load_module, Module, Pre, Post, Depend}, _, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - check_module(Module, Modules), - % Can not be sure that the the dependent module exists in the new appfile - %%check_module_depend(Module, Depend, Modules), - check_purge(Pre), - check_purge(Post); - - - -check_instruction(up, {delete_module, Module}, _, Modules) - when is_atom(Module) -> - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - ok; - ok -> - error({existing_module_deleted, Module}) - end; - -check_instruction(down, {delete_module, Module}, _, Modules) - when is_atom(Module) -> - check_module(Module, Modules); - - -check_instruction(_, {apply, {Module, Function, Args}}, _, _) when is_atom(Module), is_atom(Function), is_list(Args) -> - ok; - -check_instruction(_, {update, Module, supervisor}, _, Modules) when is_atom(Module) -> - check_module(Module, Modules); - -check_instruction(_, {update, Module, {advanced, _}, DepMods}, _, Modules) when is_atom(Module), is_list(DepMods) -> - check_module(Module, Modules), - check_module_depend(Module, DepMods, Modules); - -check_instruction(_, {update, Module, Change, Pre, Post, Depend}, _, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_change(Change), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {restart_application, inets}, _AllInstr, _Modules) -> - ok; - -check_instruction(_, {update, Module, {advanced, _}}, _, Modules) -> - check_module(Module, Modules); - -check_instruction(_, Instr, _AllInstr, _Modules) -> - error({error, {unknown_instruction, Instr}}). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -check_version(V) when is_list(V) -> - ok; -check_version(REBin) when is_binary(REBin) -> - try - begin - RE = binary_to_list(REBin), - case re:compile(RE) of - {ok, _} -> - ok; - {error, _} -> - error({bad_version, REBin}) - end - end - catch - _T:_E -> - error({bad_version, REBin}) - end; -check_version(V) -> - error({bad_version, V}). - - -check_module(M, Modules) when is_atom(M) -> - case lists:member(M,Modules) of - true -> - ok; - false -> - error({unknown_module, M, Modules}) - end; -check_module(M, _) -> - error({bad_module, M}). - - -check_module_depend(M, [], _) when is_atom(M) -> - ok; -check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) -> - case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of - [] -> - ok; - Unknown -> - error({unknown_depend_modules, Unknown}) - end; -check_module_depend(_M, D, _Modules) -> - error({bad_depend, D}). - - -check_no_remove_depends(_Module, []) -> - ok; -check_no_remove_depends(Module, [Instr|Instrs]) -> - check_no_remove_depend(Module, Instr), - check_no_remove_depends(Module, Instrs). - -check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, load_module, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, update, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(_, _) -> - ok. - - -check_change(soft) -> - ok; -check_change({advanced, _Something}) -> - ok; -check_change(Change) -> - error({bad_change, Change}). - - -check_purge(soft_purge) -> - ok; -check_purge(brutal_purge) -> - ok; -check_purge(Purge) -> - error({bad_purge, Purge}). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -error(Reason) -> - throw({error, Reason}). - -fail(Reason) -> - exit({suite_failed, Reason}). - -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> - fail({not_found, Key, L}); - {value, {Key, Value}} -> - Value - end. + ok = ?t:appup_test(inets). diff --git a/lib/kernel/src/application_master.erl b/lib/kernel/src/application_master.erl index 68f78c6eb8..bc15b5a7de 100644 --- a/lib/kernel/src/application_master.erl +++ b/lib/kernel/src/application_master.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ -include("application_master.hrl"). --record(state, {child, appl_data, children = [], procs = 0, gleader}). +-record(state, {child, appl_data, children = [], procs = 0, gleader, req=[]}). %%----------------------------------------------------------------- %% Func: start_link/1 @@ -205,22 +205,25 @@ terminate_loop(Child, State) -> %%----------------------------------------------------------------- %% The Application Master is linked to *all* processes in the group -%% (application). +%% (application). %%----------------------------------------------------------------- handle_msg({get_child, Tag, From}, State) -> - From ! {Tag, get_child_i(State#state.child)}, - State; + get_child_i(State, Tag, From); handle_msg({stop, Tag, From}, State) -> catch terminate(normal, State), From ! {Tag, ok}, exit(normal); +handle_msg({child, Ref, GrandChild, Mod}, #state{req=Reqs0}=State) -> + {value, {_, Tag, From}, Reqs} = lists:keytake(Ref, 1, Reqs0), + From ! {Tag, {GrandChild, Mod}}, + State#state{req=Reqs}; handle_msg(_, State) -> State. - -terminate(Reason, State) -> - terminate_child(State#state.child, State), - kill_children(State#state.children), +terminate(Reason, State = #state{child=Child, children=Children, req=Reqs}) -> + _ = [From ! {Tag, error} || {_, Tag, From} <- Reqs], + terminate_child(Child, State), + kill_children(Children), exit(Reason). @@ -342,8 +345,8 @@ start_supervisor(Type, M, A) -> loop_it(Parent, Child, Mod, AppState) -> receive - {Parent, get_child} -> - Parent ! {self(), Child, Mod}, + {Parent, get_child, Ref} -> + Parent ! {child, Ref, Child, Mod}, loop_it(Parent, Child, Mod, AppState); {Parent, terminate} -> NewAppState = prep_stop(Mod, AppState), @@ -382,10 +385,15 @@ prep_stop(Mod, AppState) -> NewAppState end. -get_child_i(Child) -> - Child ! {self(), get_child}, - receive - {Child, GrandChild, Mod} -> {GrandChild, Mod} +get_child_i(#state{child=Child, req=Reqs}=State, Tag, From) -> + Ref = erlang:make_ref(), + case erlang:is_process_alive(Child) of + true -> + Child ! {self(), get_child, Ref}, + State#state{req=[{Ref, Tag, From}|Reqs]}; + false -> + From ! {Tag, error}, + State end. terminate_child_i(Child, State) -> diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 792593246a..41d422d7d4 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -597,8 +597,7 @@ getservbyname(Name, Protocol) when is_atom(Name) -> Error -> Error end. --spec ntoa(IpAddress) -> - {ok, Address} | {error, einval} when +-spec ntoa(IpAddress) -> Address | {error, einval} when Address :: string(), IpAddress :: ip_address(). ntoa(Addr) -> diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index b946c2d1af..f8f4cc1ec2 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,12 +16,10 @@ %% %% %CopyrightEnd% {"%VSN%", - %% Up from - max two major revisions back + %% Up from - max one major revision back [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 - {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 - {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15 - %% Down to - max two major revisions back + {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16 + %% Down to - max one major revision back [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 - {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 - {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15 + {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16 }. diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index 7dc51495f7..2300b7e901 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -66,7 +66,7 @@ %%------------------------------------------------------------------------ --type state() :: gb_tree(). +-type state() :: gb_trees:tree(pid(), {pid(), reference()}). %%------------------------------------------------------------------------ diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index ff62297f2d..c6cbd1a0ef 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ -export([config_change/1, persistent_env/1, distr_changed_tc1/1, distr_changed_tc2/1, ensure_started/1, ensure_all_started/1, - shutdown_func/1, do_shutdown/1, shutdown_timeout/1]). + shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1]). -define(TESTCASE, testcase_name). -define(testcase, ?config(?TESTCASE, Config)). @@ -54,6 +54,7 @@ all() -> script_start, nodedown_start, permit_false_start_local, permit_false_start_dist, get_key, get_env, ensure_all_started, {group, distr_changed}, config_change, shutdown_func, shutdown_timeout, + shutdown_deadlock, persistent_env]. groups() -> @@ -961,7 +962,7 @@ nodedown_start(Conf) when is_list(Conf) -> ensure_started(suite) -> []; ensure_started(doc) -> ["Test application:ensure_started/1."]; -ensure_started(Conf) -> +ensure_started(_Conf) -> {ok, Fd} = file:open("app1.app", [write]), w_app1(Fd), @@ -981,7 +982,7 @@ ensure_started(Conf) -> ensure_all_started(suite) -> []; ensure_all_started(doc) -> ["Test application:ensure_all_started/1-2."]; -ensure_all_started(Conf) -> +ensure_all_started(_Conf) -> {ok, Fd1} = file:open("app1.app", [write]), w_app1(Fd1), @@ -2096,7 +2097,32 @@ shutdown_timeout(Config) when is_list(Config) -> end, ok. +%%%----------------------------------------------------------------- +%%% Provokes a (previous) application shutdown deadlock +%%%----------------------------------------------------------------- +shutdown_deadlock(Config) when is_list(Config) -> + DataDir = ?config(data_dir,Config), + code:add_path(filename:join([DataDir,deadlock])), + %% ok = rpc:call(Cp1, application, start, [sasl]), + ok = application:start(deadlock), + Tester = self(), + application:set_env(deadlock, fail_stop, Tester), + spawn(fun() -> Tester ! {stop, application:stop(deadlock)} end), + receive + {deadlock, Server} -> + spawn(fun() -> + Master = application_controller:get_master(deadlock), + Child = application_master:get_child(Master), + Tester ! {child, Child} + end), + timer:sleep(100), + erlang:display({self(), "Sending Continue", Server}), + Server ! continue + end, + [_|_] = application:which_applications(), + application:unload(deadlock), % clean up! + ok. %%----------------------------------------------------------------- diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app index 0c1001bed6..233c7a3f76 100644 --- a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app +++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app @@ -4,5 +4,5 @@ {applications, [kernel, stdlib, sasl]}, {modules, [deadlock]}, {mod, {deadlock, []}}, - {env, [{fail_start, false}]} + {env, [{fail_start, false}, {fail_stop, false}]} ]}. diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl index 5f68bf9078..3ef6105371 100644 --- a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl +++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl @@ -21,7 +21,7 @@ init([sup]) -> {ok, {{one_for_one, 5, 10}, [ { sasl_syslog_dm, {?MODULE, start_link, []}, - permanent, brutal_kill, worker, + permanent, 25000, worker, [deadlock] } ]}}; @@ -32,6 +32,8 @@ init([sup]) -> init([child]) -> case application:get_env(deadlock, fail_start) of {ok, false} -> + process_flag(trap_exit, true), + io:format("~p: Traps exit~n",[?MODULE]), %% we must not fail on the first init, otherwise supervisor %% terminates immediately {ok, []}; @@ -50,6 +52,14 @@ handle_info(_Msg, State) -> {noreply, State}. terminate(_Reason, _State) -> + case application:get_env(deadlock, fail_stop) of + {ok, false} -> ok; + {ok, Tester} -> + Tester ! {deadlock, self()}, + io:format("~p: Waiting in terminate (~p)~n",[?MODULE,Tester]), + receive continue -> ok end + end, + io:format("~p: terminates~n", [?MODULE]), ok. code_change(_OldVsn, State, _Extra) -> diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl index 75f51eb076..78f5e93fc3 100644 --- a/lib/kernel/test/kernel_SUITE.erl +++ b/lib/kernel/test/kernel_SUITE.erl @@ -23,9 +23,6 @@ -include_lib("test_server/include/test_server.hrl"). -% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(1)). - % Test server specific exports -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). @@ -59,11 +56,8 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + Config. +end_per_testcase(_Case, _Config) -> ok. % @@ -78,17 +72,18 @@ app_test(Config) when is_list(Config) -> ok. -%% Test that appup allows upgrade from/downgrade to a maximum of two -%% major releases back. +%% Test that appup allows upgrade from/downgrade to a maximum of one +%% major release back. appup_test(_Config) -> - do_appup_tests(create_test_vsns()). + appup_tests(kernel,create_test_vsns(kernel)). -do_appup_tests({[],[]}) -> +appup_tests(_App,{[],[]}) -> {skip,"no previous releases available"}; -do_appup_tests({OkVsns,NokVsns}) -> - application:load(kernel), - {_,_,Vsn} = lists:keyfind(kernel,1,application:loaded_applications()), - AppupFile = filename:join([code:lib_dir(kernel),ebin,"kernel.appup"]), +appup_tests(App,{OkVsns,NokVsns}) -> + application:load(App), + {_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()), + AppupFileName = atom_to_list(App) ++ ".appup", + AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]), {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile), ct:log("~p~n",[AppupScript]), ct:log("Testing ok versions: ~p~n",[OkVsns]), @@ -99,13 +94,12 @@ do_appup_tests({OkVsns,NokVsns}) -> check_appup(NokVsns,DownTo,error), ok. -create_test_vsns() -> +create_test_vsns(App) -> This = erlang:system_info(otp_release), FirstMajor = previous_major(This), SecondMajor = previous_major(FirstMajor), - ThirdMajor = previous_major(SecondMajor), - Ok = kernel_vsn([FirstMajor,SecondMajor]), - Nok0 = kernel_vsn([ThirdMajor]), + Ok = app_vsn(App,[FirstMajor]), + Nok0 = app_vsn(App,[SecondMajor]), Nok = case Ok of [Ok1|_] -> [Ok1 ++ ",1" | Nok0]; % illegal @@ -121,18 +115,36 @@ previous_major("r"++Rel) -> previous_major(Rel) -> integer_to_list(list_to_integer(Rel)-1). -kernel_vsn([R|Rs]) -> - case test_server:is_release_available(R) of - true -> - {ok,N} = test_server:start_node(prevrel,peer,[{erl,[{release,R}]}]), - As = rpc:call(N,application,which_applications,[]), - {_,_,KV} = lists:keyfind(kernel,1,As), - test_server:stop_node(N), - [KV|kernel_vsn(Rs)]; +app_vsn(App,[R|Rs]) -> + OldRel = + case test_server:is_release_available(R) of + true -> + {release,R}; + false -> + case ct:get_config({otp_releases,list_to_atom(R)}) of + undefined -> + false; + Prog0 -> + case os:find_executable(Prog0) of + false -> + false; + Prog -> + {prog,Prog} + end + end + end, + case OldRel of false -> - kernel_vsn(Rs) + app_vsn(App,Rs); + _ -> + {ok,N} = test_server:start_node(prevrel,peer,[{erl,[OldRel]}]), + _ = rpc:call(N,application,load,[App]), + As = rpc:call(N,application,loaded_applications,[]), + {_,_,V} = lists:keyfind(App,1,As), + test_server:stop_node(N), + [V|app_vsn(App,Rs)] end; -kernel_vsn([]) -> +app_vsn(_App,[]) -> []. check_appup([Vsn|Vsns],Instrs,Expected) -> diff --git a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl index 8a4f4e7509..7d82262a59 100644 --- a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl +++ b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl @@ -66,7 +66,7 @@ version_of(_EC, Binary, 3, [AsnModV1, AsnModV2, AsnModV3]) version_of([], _Binary, Err) -> {error, {decode_failed, lists:reverse(Err)}}; version_of([AsnMod|AsnMods], Binary, Errs) when is_atom(AsnMod) -> - case (catch asn1rt:decode(AsnMod, 'MegacoMessage', Binary)) of + case (catch AsnMod:decode('MegacoMessage', Binary)) of {ok, M} -> V = (M#'MegacoMessage'.mess)#'Message'.version, {ok, V}; @@ -82,14 +82,14 @@ version_of([AsnMod|AsnMods], Binary, Errs) when is_atom(AsnMod) -> encode_message([native], MegaMsg, AsnMod, _TransMod, binary) when is_record(MegaMsg, 'MegacoMessage') -> - asn1rt:encode(AsnMod, 'MegacoMessage', MegaMsg); + AsnMod:encode('MegacoMessage', MegaMsg); encode_message(EC, MegaMsg, AsnMod, TransMod, binary) when is_list(EC) andalso is_record(MegaMsg, 'MegacoMessage') -> case (catch TransMod:tr_message(MegaMsg, encode, EC)) of {'EXIT', Reason} -> {error, Reason}; MegaMsg2 -> - asn1rt:encode(AsnMod, 'MegacoMessage', MegaMsg2) + AsnMod:encode('MegacoMessage', MegaMsg2) end; encode_message(EC, MegaMsg, AsnMod, TransMod, io_list) -> case encode_message(EC, MegaMsg, AsnMod, TransMod, binary) of @@ -276,7 +276,7 @@ decode_message_dynamic(_EC, _BadBin, _Mods, _Type) -> decode_message(EC, Bin, AsnMod, TransMod, _) -> - case asn1rt:decode(AsnMod, 'MegacoMessage', Bin) of + case AsnMod:decode('MegacoMessage', Bin) of {ok, MegaMsg} -> case EC of [native] -> diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index 914ec77721..72e9bd7e8f 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2013</year> + <year>1996</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -1204,7 +1204,11 @@ mnesia:create_table(person, <desc> <p>Performs a user initiated dump of the local log file. This is usually not necessary since Mnesia, by default, - manages this automatically.</p> + manages this automatically. + See configuration parameters + <seealso marker="#dump_log_time_threshold">dump_log_time_threshold</seealso> and + <seealso marker="#dump_log_write_threshold">dump_log_write_threshold</seealso>. + </p> </desc> </func> <func> @@ -2208,6 +2212,18 @@ mnesia:create_table(employee, </desc> </func> <func> + <name>sync_log() -> ok | {error, Reason} </name> + <fsummary>Perform a file sync of the local log file.</fsummary> + <desc> + <p>Ensures that the local transaction log file is synced to disk. + On a single node system data written to disk tables, since the last dump, + can be lost in case of a power outage. + See <seealso marker="#dump_log/0">dump_log/0</seealso>. + </p> + </desc> + </func> + + <func> <name>sync_transaction(Fun, [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun} </name> <fsummary>Synchronously execute a transaction.</fsummary> <desc> @@ -2445,7 +2461,7 @@ mnesia:create_table(employee, <name>table(Tab [,[Option]]) -> QueryHandle </name> <fsummary>Return a QLC query handle.</fsummary> <desc> - <p> <marker id="qlc_table"></marker> + <p><marker id="qlc_table"></marker> Returns a QLC (Query List Comprehension) query handle, see <seealso marker="stdlib:qlc">qlc(3)</seealso>.The module <c>qlc</c> implements a query language, it can use mnesia tables as sources of data. Calling @@ -3015,6 +3031,7 @@ raise(Name, Amount) -> performed on the original data file. The default is <c>true</c></p> </item> <item> + <marker id=" dump_log_write_threshold"></marker> <p><c>-mnesia dump_log_write_threshold Max</c>, where <c>Max</c> is an integer which specifies the maximum number of writes allowed to the transaction log before a new dump of the log @@ -3022,13 +3039,14 @@ raise(Name, Amount) -> </p> </item> <item> + <marker id=" dump_log_time_threshold"></marker> <p><c>-mnesia dump_log_time_threshold Max</c>, where <c>Max</c> is an integer which specifies the dump log interval in milliseconds. It defaults to 3 minutes. If a dump has not been performed within <c>dump_log_time_threshold</c> milliseconds, then a new dump is performed regardless of how many writes have been - performed. + performed. </p> </item> <item> diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 70466d10d7..b7d80c1370 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -104,7 +104,8 @@ set_master_nodes/1, set_master_nodes/2, %% Misc admin - dump_log/0, subscribe/1, unsubscribe/1, report_event/1, + dump_log/0, sync_log/0, + subscribe/1, unsubscribe/1, report_event/1, %% Snmp snmp_open_table/2, snmp_close_table/1, @@ -1808,7 +1809,7 @@ do_dirty_rpc(Tab, Node, M, F, Args) -> {badrpc, Reason} -> timer:sleep(20), %% Do not be too eager, and can't use yield on SMP %% Sync with mnesia_monitor - try sys:get_status(mnesia_monitor) catch _:_ -> ok end, + _ = try sys:get_status(mnesia_monitor) catch _:_ -> ok end, case mnesia_controller:call({check_w2r, Node, Tab}) of % Sync NewNode when NewNode =:= Node -> ErrorTag = mnesia_lib:dirty_rpc_error_tag(Reason), @@ -2554,6 +2555,9 @@ set_master_nodes(Tab, Nodes) -> dump_log() -> mnesia_controller:sync_dump_log(user). +sync_log() -> + mnesia_monitor:sync_log(latest_log). + subscribe(What) -> mnesia_subscr:subscribe(self(), What). diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index 78f7bfa325..a83e55ac62 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -198,7 +198,8 @@ sync_dump_log(InitBy) -> call({sync_dump_log, InitBy}). async_dump_log(InitBy) -> - ?SERVER_NAME ! {async_dump_log, InitBy}. + ?SERVER_NAME ! {async_dump_log, InitBy}, + ok. %% Wait for tables to be active %% If needed, we will wait for Mnesia to start @@ -293,10 +294,11 @@ update(Fun) -> mnesia_down(Node) -> - case cast({mnesia_down, Node}) of - {error, _} -> mnesia_monitor:mnesia_down(?SERVER_NAME, Node); - _Pid -> ok + case whereis(?SERVER_NAME) of + undefined -> mnesia_monitor:mnesia_down(?SERVER_NAME, Node); + Pid -> gen_server:cast(Pid, {mnesia_down, Node}) end. + wait_for_schema_commit_lock() -> link(whereis(?SERVER_NAME)), unsafe_call(wait_for_schema_commit_lock). @@ -467,7 +469,7 @@ connect_nodes2(Father, Ns, UserFun) -> process_flag(trap_exit, true), Res = try_merge_schema(New, [], UserFun), Msg = {schema_is_merged, [], late_merge, []}, - multicall([node()|Ns], Msg), + _ = multicall([node()|Ns], Msg), After = val({current, db_nodes}), Father ! {?MODULE, self(), Res, mnesia_lib:intersect(Ns,After)}, unlink(Father), @@ -548,7 +550,7 @@ schema_is_merged() -> cast(Msg) -> case whereis(?SERVER_NAME) of - undefined ->{error, {node_not_running, node()}}; + undefined -> ok; Pid -> gen_server:cast(Pid, Msg) end. @@ -1789,7 +1791,7 @@ sync_and_block_table_whereabouts(Tab, ToNode, RemoteS, AccessMode) when Tab /= s true -> Current -- [ToNode]; false -> Current end, - remote_call(ToNode, block_table, [Tab]), + _ = remote_call(ToNode, block_table, [Tab]), [remote_call(Node, add_active_replica, [Tab, ToNode, RemoteS, AccessMode]) || Node <- [ToNode | Ns]], ok. diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index e2a0aa3bda..14665797a0 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -85,7 +85,7 @@ adjust_log_writes(DoCast) -> %% Don't care if we lost a few writes mnesia_lib:set_counter(trans_log_writes_left, Max), Diff = Max - Left, - mnesia_lib:incr_counter(trans_log_writes, Diff), + _ = mnesia_lib:incr_counter(trans_log_writes, Diff), global:del_lock(Token, [node()]) end. @@ -451,7 +451,8 @@ disc_delete_table(Tab, Storage) -> Storage == disc_only_copies; Tab == schema -> mnesia_monitor:unsafe_close_dets(Tab), Dat = mnesia_lib:tab2dat(Tab), - file:delete(Dat); + file:delete(Dat), + ok; true -> DclFile = mnesia_lib:tab2dcl(Tab), case get({?MODULE,Tab}) of @@ -466,13 +467,14 @@ disc_delete_table(Tab, Storage) -> file:delete(DcdFile), ok end, - erase({?MODULE, Tab}); + erase({?MODULE, Tab}), + ok; false -> - ignore + ok end. disc_delete_indecies(_Tab, _Cs, Storage) when Storage /= disc_only_copies -> - ignore; + ok; disc_delete_indecies(Tab, Cs, disc_only_copies) -> Indecies = Cs#cstruct.index, mnesia_index:del_transient(Tab, Indecies, disc_only_copies). @@ -522,10 +524,11 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace, {disc_copies, ram_copies} when Tab == schema -> mnesia_lib:set(use_dir, false), mnesia_monitor:unsafe_close_dets(Tab), - file:delete(Dat); + ok = file:delete(Dat); {disc_copies, ram_copies} -> - file:delete(Dcl), - file:delete(Dcd); + _ = file:delete(Dcl), + _ = file:delete(Dcd), + ok; {ram_copies, disc_only_copies} -> ok = ensure_rename(Dmp, Dat), true = open_files(Tab, disc_only_copies, InPlace, InitBy), @@ -544,7 +547,8 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace, startup -> ignore; _ -> - mnesia_controller:get_disc_copy(Tab) + mnesia_controller:get_disc_copy(Tab), + ok end, disc_delete_table(Tab, disc_only_copies); {disc_copies, disc_only_copies} -> @@ -553,8 +557,9 @@ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace, mnesia_schema:ram_delete_table(Tab, FromS), PosList = Cs#cstruct.index, mnesia_index:init_indecies(Tab, disc_only_copies, PosList), - file:delete(Dcl), - file:delete(Dcd); + _ = file:delete(Dcl), + _ = file:delete(Dcd), + ok; {disc_only_copies, disc_copies} -> mnesia_monitor:unsafe_close_dets(Tab), disc_delete_indecies(Tab, Cs, disc_only_copies), diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl index 35fe2d4035..67ec9d7399 100644 --- a/lib/mnesia/src/mnesia_event.erl +++ b/lib/mnesia/src/mnesia_event.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -63,7 +63,7 @@ handle_event(Event, State) -> %%----------------------------------------------------------------- handle_info(Msg, State) -> - handle_any_event(Msg, State), + {ok, _} = handle_any_event(Msg, State), {ok, State}. %%----------------------------------------------------------------- diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl index 54db45e3ba..8fef611a48 100644 --- a/lib/mnesia/src/mnesia_index.erl +++ b/lib/mnesia/src/mnesia_index.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -229,7 +229,7 @@ del_transient(Tab, Storage) -> PosList = val({Tab, index}), del_transient(Tab, PosList, Storage). -del_transient(_, [], _) -> done; +del_transient(_, [], _) -> ok; del_transient(Tab, [Pos | Tail], Storage) -> delete_transient_index(Tab, Pos, Storage), del_transient(Tab, Tail, Storage). @@ -237,7 +237,7 @@ del_transient(Tab, [Pos | Tail], Storage) -> delete_transient_index(Tab, Pos, disc_only_copies) -> Tag = {Tab, index, Pos}, mnesia_monitor:unsafe_close_dets(Tag), - file:delete(tab2filename(Tab, Pos)), + _ = file:delete(tab2filename(Tab, Pos)), del_index_info(Tab, Pos), %% Uses val(..) mnesia_lib:unset({Tab, {index, Pos}}); @@ -255,7 +255,7 @@ init_disc_index(_Tab, []) -> init_disc_index(Tab, [Pos | Tail]) when is_integer(Pos) -> Fn = tab2filename(Tab, Pos), IxTag = {Tab, index, Pos}, - file:delete(Fn), + _ = file:delete(Fn), Args = [{file, Fn}, {keypos, 1}, {type, bag}], mnesia_monitor:open_dets(IxTag, Args), Storage = disc_only_copies, diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl index ae6631646c..109e924971 100644 --- a/lib/mnesia/src/mnesia_lib.erl +++ b/lib/mnesia/src/mnesia_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -296,11 +296,7 @@ active_here(Tab) -> not_active_here(Tab) -> not active_here(Tab). -exists(Fname) -> - case file:open(Fname, [raw,read]) of - {ok, F} ->file:close(F), true; - _ -> false - end. +exists(Fname) -> filelib:is_regular(Fname). dir() -> mnesia_monitor:get_env(dir). @@ -596,7 +592,7 @@ coredump(CrashInfo) -> Core = mkcore(CrashInfo), Out = core_file(), important("Writing Mnesia core to file: ~p...~p~n", [Out, CrashInfo]), - file:write_file(Out, Core), + _ = file:write_file(Out, Core), Out. core_file() -> @@ -620,7 +616,7 @@ mkcore(CrashInfo) -> Core = [ CrashInfo, {time, {date(), time()}}, - {self, catch process_info(self())}, + {self, proc_dbg_info(self())}, {nodes, catch rpc:multicall(Nodes, ?MODULE, get_node_number, [])}, {applications, catch lists:sort(application:loaded_applications())}, {flags, catch init:get_arguments()}, @@ -697,7 +693,7 @@ relatives() -> Info = fun(Name) -> case whereis(Name) of undefined -> false; - Pid -> {true, {Name, Pid, catch process_info(Pid)}} + Pid -> {true, {Name, Pid, proc_dbg_info(Pid)}} end end, lists:zf(Info, mnesia:ms()). @@ -706,14 +702,14 @@ workers({workers, Loaders, Senders, Dumper}) -> Info = fun({Pid, {send_table, Tab, _Receiver, _St}}) -> case Pid of undefined -> false; - Pid -> {true, {Pid, Tab, catch process_info(Pid)}} + Pid -> {true, {Pid, Tab, proc_dbg_info(Pid)}} end; ({Pid, What}) when is_pid(Pid) -> - {true, {Pid, What, catch process_info(Pid)}}; + {true, {Pid, What, proc_dbg_info(Pid)}}; ({Name, Pid}) -> case Pid of undefined -> false; - Pid -> {true, {Name, Pid, catch process_info(Pid)}} + Pid -> {true, {Name, Pid, proc_dbg_info(Pid)}} end end, SInfo = lists:zf(Info, Senders), @@ -727,13 +723,21 @@ locking_procs(LockList) when is_list(LockList) -> Pid = Tid#tid.pid, case node(Pid) == node() of true -> - {true, {Pid, catch process_info(Pid)}}; + {true, {Pid, proc_dbg_info(Pid)}}; _ -> false end end, lists:zf(Info, UT). +proc_dbg_info(Pid) -> + try + [process_info(Pid, current_stacktrace)| + process_info(Pid)] + catch _:R -> + [{process_info,crashed,R}] + end. + view() -> Bin = mkcore({crashinfo, {"view only~n", []}}), vcore(Bin). @@ -806,9 +810,9 @@ vcore(File) -> vcore_elem({schema_file, {ok, B}}) -> Fname = "/tmp/schema.DAT", - file:write_file(Fname, B), - dets:view(Fname), - file:delete(Fname); + _ = file:write_file(Fname, B), + _ = dets:view(Fname), + _ = file:delete(Fname); vcore_elem({logfile, {ok, BinList}}) -> Fun = fun({F, Info}) -> @@ -922,7 +926,7 @@ random_time(Retries, _Counter0) -> case get(random_seed) of undefined -> {X, Y, Z} = erlang:now(), %% time() - random:seed(X, Y, Z), + _ = random:seed(X, Y, Z), Time = Dup + random:uniform(MaxIntv), %% dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]), Time; @@ -958,20 +962,17 @@ report_system_event({'EXIT', Reason}, Event) -> unlink(Pid), %% We get an exit signal if server dies - receive - {'EXIT', Pid, _Reason} -> - {error, {node_not_running, node()}} - after 0 -> - gen_event:stop(mnesia_event), - ok + receive {'EXIT', Pid, _Reason} -> ok + after 0 -> gen_event:stop(mnesia_event) end; Error -> Msg = "Mnesia(~p): Cannot report event ~p: ~p (~p)~n", error_logger:format(Msg, [node(), Event, Reason, Error]) - end; + end, + ok; report_system_event(_Res, _Event) -> - ignore. + ok. %% important messages are reported regardless of debug level important(Format, Args) -> @@ -1025,8 +1026,8 @@ copy_file(From, To) -> case file:open(To, [raw, binary, write]) of {ok, T} -> Res = copy_file_loop(F, T, 8000), - file:close(F), - file:close(T), + ok = file:close(F), + ok = file:close(T), Res; {error, Reason} -> {error, Reason} @@ -1038,7 +1039,7 @@ copy_file(From, To) -> copy_file_loop(F, T, ChunkSize) -> case file:read(F, ChunkSize) of {ok, Bin} -> - file:write(T, Bin), + ok = file:write(T, Bin), copy_file_loop(F, T, ChunkSize); eof -> ok; @@ -1205,7 +1206,7 @@ dets_to_ets(Tabname, Tab, File, Type, Rep, Lock) -> {keypos, 2}, {repair, Rep}]) of {ok, Tabname} -> Res = dets:to_ets(Tabname, Tab), - Close(Tabname), + ok = Close(Tabname), trav_ret(Res, Tab); Other -> Other diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index c4fe370ec1..32cea903c9 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -103,7 +103,8 @@ val(Var) -> end. reply(From, R) -> - From ! {?MODULE, node(), R}. + From ! {?MODULE, node(), R}, + true. %% Quiets dialyzer l_request(Node, X, Store) -> {?MODULE, Node} ! {self(), X}, diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl index 18303869ed..d2fd04a60b 100644 --- a/lib/mnesia/src/mnesia_log.erl +++ b/lib/mnesia/src/mnesia_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -393,13 +393,15 @@ unsafe_close_log(Log) -> purge_some_logs() -> mnesia_monitor:unsafe_close_log(latest_log), - file:delete(latest_log_file()), - file:delete(decision_tab_file()). + _ = file:delete(latest_log_file()), + _ = file:delete(decision_tab_file()), + ok. purge_all_logs() -> - file:delete(previous_log_file()), - file:delete(latest_log_file()), - file:delete(decision_tab_file()). + _ = file:delete(previous_log_file()), + _ = file:delete(latest_log_file()), + _ = file:delete(decision_tab_file()), + ok. %% Prepare dump by renaming the open logfile if possible %% Returns a tuple on the following format: {Res, OpenLog} diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl index c7b905a1bf..6fc1a394a6 100644 --- a/lib/mnesia/src/mnesia_monitor.erl +++ b/lib/mnesia/src/mnesia_monitor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -44,6 +44,7 @@ set_env/2, start/0, start_proc/4, + sync_log/1, terminate_proc/3, unsafe_close_dets/1, unsafe_close_log/1, @@ -118,6 +119,9 @@ open_log(Args) -> reopen_log(Name, Fname, Head) -> unsafe_call({reopen_log, Name, Fname, Head}). +sync_log(Name) -> + unsafe_call({sync_log, Name}). + close_log(Name) -> unsafe_call({close_log, Name}). @@ -202,7 +206,7 @@ needs_protocol_conversion(Node) -> cast(Msg) -> case whereis(?MODULE) of - undefined -> ignore; + undefined -> ok; Pid -> gen_server:cast(Pid, Msg) end. @@ -382,6 +386,9 @@ handle_call({reopen_log, Name, Fname, Head}, _From, State) -> {noreply, State} end; +handle_call({sync_log, Name}, _From, State) -> + {reply, disk_log:sync(Name), State}; + handle_call({close_log, Name}, _From, State) -> case disk_log:close(Name) of ok -> @@ -395,7 +402,7 @@ handle_call({close_log, Name}, _From, State) -> end; handle_call({unsafe_close_log, Name}, _From, State) -> - disk_log:close(Name), + _ = disk_log:close(Name), {reply, ok, State}; handle_call({negotiate_protocol, Mon, _Version, _Protocols}, _From, State) @@ -439,7 +446,7 @@ handle_call({negotiate_protocol, Nodes}, From, State) -> end; handle_call(init, _From, State) -> - net_kernel:monitor_nodes(true), + _ = net_kernel:monitor_nodes(true), EarlyNodes = State#state.early_connects, State2 = State#state{tm_started = true}, {reply, EarlyNodes, State2}; diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl index 7aa03bda37..0548a25ebf 100644 --- a/lib/mnesia/src/mnesia_recover.erl +++ b/lib/mnesia/src/mnesia_recover.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index 17af0cad44..af658150da 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -183,7 +183,8 @@ mnesia_down(Node) -> undefined -> mnesia_monitor:mnesia_down(?MODULE, Node); Pid -> - Pid ! {mnesia_down, Node} + Pid ! {mnesia_down, Node}, + ok end. prepare_checkpoint(Nodes, Cp) -> diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl index e0004ecb51..921ebb71e9 100644 --- a/lib/mnesia/test/mnesia_SUITE.erl +++ b/lib/mnesia/test/mnesia_SUITE.erl @@ -21,6 +21,7 @@ -module(mnesia_SUITE). -author('[email protected]'). -compile([export_all]). +-include_lib("common_test/include/ct.hrl"). -include("mnesia_test_lib.hrl"). init_per_testcase(Func, Conf) -> @@ -50,7 +51,7 @@ suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. %% and do not involve the normal test machinery. all() -> - [{group, light}, {group, medium}, {group, heavy}, + [app, appup, {group, light}, {group, medium}, {group, heavy}, clean_up_suite]. groups() -> @@ -144,6 +145,18 @@ silly() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test structure of the mnesia application resource file +app(Config) when is_list(Config) -> + ok = ?t:app_test(mnesia). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Test that all required versions have appup directives +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(mnesia). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + clean_up_suite(doc) -> ["Not a test case only kills mnesia and nodes, that where" "started during the tests"]; clean_up_suite(suite) -> diff --git a/lib/mnesia/test/mnesia_durability_test.erl b/lib/mnesia/test/mnesia_durability_test.erl index 4434abaa1e..366fda7044 100644 --- a/lib/mnesia/test/mnesia_durability_test.erl +++ b/lib/mnesia/test/mnesia_durability_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -48,19 +48,19 @@ groups() -> load_directly_when_all_are_ram_copiesB, {group, late_load_when_all_are_ram_copies_on_ram_nodes}, load_when_last_replica_becomes_available, - load_when_we_have_down_from_all_other_replica_nodes, + load_when_down_from_all_other_replica_nodes, late_load_transforms_into_disc_load, late_load_leads_to_hanging, force_load_when_nobody_intents_to_load, force_load_when_someone_has_decided_to_load, - force_load_when_someone_else_already_has_loaded, + force_load_when_someone_else_has_loaded, force_load_when_we_has_loaded, force_load_on_a_non_local_table, force_load_when_the_table_does_not_exist, {group, load_tables_with_master_tables}]}, {late_load_when_all_are_ram_copies_on_ram_nodes, [], - [late_load_when_all_are_ram_copies_on_ram_nodes1, - late_load_when_all_are_ram_copies_on_ram_nodes2]}, + [late_load_all_ram_cs_ram_nodes1, + late_load_all_ram_cs_ram_nodes2]}, {load_tables_with_master_tables, [], [master_nodes, starting_master_nodes, master_on_non_local_tables, @@ -292,8 +292,8 @@ load_directly_when_all_are_ram_copiesB(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -late_load_when_all_are_ram_copies_on_ram_nodes1(suite) -> []; -late_load_when_all_are_ram_copies_on_ram_nodes1(Config) when is_list(Config) -> +late_load_all_ram_cs_ram_nodes1(suite) -> []; +late_load_all_ram_cs_ram_nodes1(Config) when is_list(Config) -> [N1, N2] = mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]}, delete_schema, {reload_appls, [mnesia]}], @@ -303,8 +303,8 @@ late_load_when_all_are_ram_copies_on_ram_nodes1(Config) when is_list(Config) -> 2, Config, ?FILE, ?LINE), Res. -late_load_when_all_are_ram_copies_on_ram_nodes2(suite) -> []; -late_load_when_all_are_ram_copies_on_ram_nodes2(Config) when is_list(Config) -> +late_load_all_ram_cs_ram_nodes2(suite) -> []; +late_load_all_ram_cs_ram_nodes2(Config) when is_list(Config) -> [N1, N2, N3] = mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]}, delete_schema, {reload_appls, [mnesia]}], @@ -439,13 +439,13 @@ load_when_last_replica_becomes_available(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -load_when_we_have_down_from_all_other_replica_nodes(doc) -> +load_when_down_from_all_other_replica_nodes(doc) -> ["The table can be loaded if this node was the last one surviving. ", "Check this by having N1, N2, N3 and a table replicated on all those ", "nodes. Then kill them in the N1, N2, N3 order. Then start N3 and ", "verify that the table is available with correct contents."]; -load_when_we_have_down_from_all_other_replica_nodes(suite) -> []; -load_when_we_have_down_from_all_other_replica_nodes(Config) when is_list(Config) -> +load_when_down_from_all_other_replica_nodes(suite) -> []; +load_when_down_from_all_other_replica_nodes(Config) when is_list(Config) -> [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config), ?match({atomic,ok}, mnesia:create_table(test_rec, @@ -773,14 +773,14 @@ wait_for_signal() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -force_load_when_someone_else_already_has_loaded(doc) -> +force_load_when_someone_else_has_loaded(doc) -> ["Normal case. Do a force load when somebody else has loaded the table. ", "Start N1, N2, kill in N1, N2 order. Start N2 load the table, start N1 ", "force load. Did it work? (i.e: did N1 load the table from N2 as that", "one is the latest version and it is available on N2)"]; -force_load_when_someone_else_already_has_loaded(suite) -> []; -force_load_when_someone_else_already_has_loaded(Config) when is_list(Config) -> +force_load_when_someone_else_has_loaded(suite) -> []; +force_load_when_someone_else_has_loaded(Config) when is_list(Config) -> [N1, N2] = Nodes = ?acquire_nodes(2, Config), Table = test_rec, Trec1 = #test_rec{key=1,val=111}, diff --git a/lib/mnesia/test/mnesia_nice_coverage_test.erl b/lib/mnesia/test/mnesia_nice_coverage_test.erl index 78eab67b11..4b28ac634f 100644 --- a/lib/mnesia/test/mnesia_nice_coverage_test.erl +++ b/lib/mnesia/test/mnesia_nice_coverage_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -189,6 +189,7 @@ adm(Attrs, Node1, Node2) -> ?match({atomic, ok}, mnesia:move_table_copy(nice_tab, Node2, Node1)), ?match(yes, mnesia:force_load_table(nice_counter_tab)), + ?match(ok, mnesia:sync_log()), ?match(dumped, mnesia:dump_log()), ok. diff --git a/lib/mnesia/test/mnesia_schema_recovery_test.erl b/lib/mnesia/test/mnesia_schema_recovery_test.erl index 0fe26efd0b..2301b291c2 100644 --- a/lib/mnesia/test/mnesia_schema_recovery_test.erl +++ b/lib/mnesia/test/mnesia_schema_recovery_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,66 +49,69 @@ groups() -> [{interrupted_before_log_dump, [], [interrupted_before_create_ram, interrupted_before_create_disc, - interrupted_before_create_disc_only, + interrupted_before_create_do, interrupted_before_create_nostore, interrupted_before_delete_ram, interrupted_before_delete_disc, - interrupted_before_delete_disc_only, - interrupted_before_add_ram, interrupted_before_add_disc, - interrupted_before_add_disc_only, + interrupted_before_delete_do, + interrupted_before_add_ram, + interrupted_before_add_disc, + interrupted_before_add_do, interrupted_before_add_kill_copier, interrupted_before_move_ram, interrupted_before_move_disc, - interrupted_before_move_disc_only, + interrupted_before_move_do, interrupted_before_move_kill_copier, interrupted_before_delcopy_ram, interrupted_before_delcopy_disc, - interrupted_before_delcopy_disc_only, + interrupted_before_delcopy_do, interrupted_before_delcopy_kill_copier, interrupted_before_addindex_ram, interrupted_before_addindex_disc, - interrupted_before_addindex_disc_only, + interrupted_before_addindex_do, interrupted_before_delindex_ram, interrupted_before_delindex_disc, - interrupted_before_delindex_disc_only, + interrupted_before_delindex_do, interrupted_before_change_type_ram2disc, - interrupted_before_change_type_ram2disc_only, + interrupted_before_change_type_ram2do, interrupted_before_change_type_disc2ram, - interrupted_before_change_type_disc2disc_only, - interrupted_before_change_type_disc_only2ram, - interrupted_before_change_type_disc_only2disc, + interrupted_before_change_type_disc2do, + interrupted_before_change_type_do2ram, + interrupted_before_change_type_do2disc, interrupted_before_change_type_other_node, interrupted_before_change_schema_type]}, {interrupted_after_log_dump, [], [interrupted_after_create_ram, interrupted_after_create_disc, - interrupted_after_create_disc_only, + interrupted_after_create_do, interrupted_after_create_nostore, interrupted_after_delete_ram, interrupted_after_delete_disc, - interrupted_after_delete_disc_only, - interrupted_after_add_ram, interrupted_after_add_disc, - interrupted_after_add_disc_only, + interrupted_after_delete_do, + interrupted_after_add_ram, + interrupted_after_add_disc, + interrupted_after_add_do, interrupted_after_add_kill_copier, - interrupted_after_move_ram, interrupted_after_move_disc, - interrupted_after_move_disc_only, + interrupted_after_move_ram, + interrupted_after_move_disc, + interrupted_after_move_do, interrupted_after_move_kill_copier, interrupted_after_delcopy_ram, interrupted_after_delcopy_disc, - interrupted_after_delcopy_disc_only, + interrupted_after_delcopy_do, interrupted_after_delcopy_kill_copier, interrupted_after_addindex_ram, interrupted_after_addindex_disc, - interrupted_after_addindex_disc_only, + interrupted_after_addindex_do, interrupted_after_delindex_ram, interrupted_after_delindex_disc, - interrupted_after_delindex_disc_only, + interrupted_after_delindex_do, interrupted_after_change_type_ram2disc, - interrupted_after_change_type_ram2disc_only, + interrupted_after_change_type_ram2do, interrupted_after_change_type_disc2ram, - interrupted_after_change_type_disc2disc_only, - interrupted_after_change_type_disc_only2ram, - interrupted_after_change_type_disc_only2disc, + interrupted_after_change_type_disc2do, + interrupted_after_change_type_do2ram, + interrupted_after_change_type_do2disc, interrupted_after_change_type_other_node, interrupted_after_change_schema_type]}]. @@ -128,8 +131,8 @@ interrupted_before_create_disc(Config) when is_list(Config) -> KillAt = {mnesia_dumper, dump_schema_op}, interrupted_create(Config, disc_copies, all, KillAt). -interrupted_before_create_disc_only(suite) -> []; -interrupted_before_create_disc_only(Config) when is_list(Config) -> +interrupted_before_create_do(suite) -> []; +interrupted_before_create_do(Config) when is_list(Config) -> KillAt = {mnesia_dumper, dump_schema_op}, interrupted_create(Config, disc_only_copies, all, KillAt). @@ -148,8 +151,8 @@ interrupted_after_create_disc(Config) when is_list(Config) -> KillAt = {mnesia_dumper, post_dump}, interrupted_create(Config, disc_copies, all, KillAt). -interrupted_after_create_disc_only(suite) -> []; -interrupted_after_create_disc_only(Config) when is_list(Config) -> +interrupted_after_create_do(suite) -> []; +interrupted_after_create_do(Config) when is_list(Config) -> KillAt = {mnesia_dumper, post_dump}, interrupted_create(Config, disc_only_copies, all, KillAt). @@ -204,8 +207,8 @@ interrupted_before_delete_disc(suite) -> []; interrupted_before_delete_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_delete(Config, disc_copies, Debug_Point). -interrupted_before_delete_disc_only(suite) -> []; -interrupted_before_delete_disc_only(Config) when is_list(Config) -> +interrupted_before_delete_do(suite) -> []; +interrupted_before_delete_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_delete(Config, disc_only_copies, Debug_Point). @@ -217,8 +220,8 @@ interrupted_after_delete_disc(suite) -> []; interrupted_after_delete_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_delete(Config, disc_copies, Debug_Point). -interrupted_after_delete_disc_only(suite) -> []; -interrupted_after_delete_disc_only(Config) when is_list(Config) -> +interrupted_after_delete_do(suite) -> []; +interrupted_after_delete_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_delete(Config, disc_only_copies, Debug_Point). @@ -249,8 +252,8 @@ interrupted_before_add_disc(suite) -> []; interrupted_before_add_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_add(Config, disc_copies, kill_reciever, Debug_Point). -interrupted_before_add_disc_only(suite) -> []; -interrupted_before_add_disc_only(Config) when is_list(Config) -> +interrupted_before_add_do(suite) -> []; +interrupted_before_add_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point). interrupted_before_add_kill_copier(suite) -> []; @@ -266,8 +269,8 @@ interrupted_after_add_disc(suite) -> []; interrupted_after_add_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_add(Config, disc_copies, kill_reciever, Debug_Point). -interrupted_after_add_disc_only(suite) -> []; -interrupted_after_add_disc_only(Config) when is_list(Config) -> +interrupted_after_add_do(suite) -> []; +interrupted_after_add_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point). interrupted_after_add_kill_copier(suite) -> []; @@ -327,8 +330,8 @@ interrupted_before_move_disc(suite) -> []; interrupted_before_move_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_move(Config, disc_copies, kill_reciever, Debug_Point). -interrupted_before_move_disc_only(suite) -> []; -interrupted_before_move_disc_only(Config) when is_list(Config) -> +interrupted_before_move_do(suite) -> []; +interrupted_before_move_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point). interrupted_before_move_kill_copier(suite) -> []; @@ -344,8 +347,8 @@ interrupted_after_move_disc(suite) -> []; interrupted_after_move_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_move(Config, disc_copies, kill_reciever, Debug_Point). -interrupted_after_move_disc_only(suite) -> []; -interrupted_after_move_disc_only(Config) when is_list(Config) -> +interrupted_after_move_do(suite) -> []; +interrupted_after_move_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point). interrupted_after_move_kill_copier(suite) -> []; @@ -408,8 +411,8 @@ interrupted_before_delcopy_disc(suite) -> []; interrupted_before_delcopy_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point). -interrupted_before_delcopy_disc_only(suite) -> []; -interrupted_before_delcopy_disc_only(Config) when is_list(Config) -> +interrupted_before_delcopy_do(suite) -> []; +interrupted_before_delcopy_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point). interrupted_before_delcopy_kill_copier(suite) -> []; @@ -425,8 +428,8 @@ interrupted_after_delcopy_disc(suite) -> []; interrupted_after_delcopy_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point). -interrupted_after_delcopy_disc_only(suite) -> []; -interrupted_after_delcopy_disc_only(Config) when is_list(Config) -> +interrupted_after_delcopy_do(suite) -> []; +interrupted_after_delcopy_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point). interrupted_after_delcopy_kill_copier(suite) -> []; @@ -487,8 +490,8 @@ interrupted_before_addindex_disc(suite) -> []; interrupted_before_addindex_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_addindex(Config, disc_copies, Debug_Point). -interrupted_before_addindex_disc_only(suite) -> []; -interrupted_before_addindex_disc_only(Config) when is_list(Config) -> +interrupted_before_addindex_do(suite) -> []; +interrupted_before_addindex_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_addindex(Config, disc_only_copies, Debug_Point). @@ -500,8 +503,8 @@ interrupted_after_addindex_disc(suite) -> []; interrupted_after_addindex_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_addindex(Config, disc_copies, Debug_Point). -interrupted_after_addindex_disc_only(suite) -> []; -interrupted_after_addindex_disc_only(Config) when is_list(Config) -> +interrupted_after_addindex_do(suite) -> []; +interrupted_after_addindex_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_addindex(Config, disc_only_copies, Debug_Point). @@ -555,8 +558,8 @@ interrupted_before_delindex_disc(suite) -> []; interrupted_before_delindex_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_delindex(Config, disc_copies, Debug_Point). -interrupted_before_delindex_disc_only(suite) -> []; -interrupted_before_delindex_disc_only(Config) when is_list(Config) -> +interrupted_before_delindex_do(suite) -> []; +interrupted_before_delindex_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_delindex(Config, disc_only_copies, Debug_Point). @@ -568,8 +571,8 @@ interrupted_after_delindex_disc(suite) -> []; interrupted_after_delindex_disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_delindex(Config, disc_copies, Debug_Point). -interrupted_after_delindex_disc_only(suite) -> []; -interrupted_after_delindex_disc_only(Config) when is_list(Config) -> +interrupted_after_delindex_do(suite) -> []; +interrupted_after_delindex_do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_delindex(Config, disc_only_copies, Debug_Point). @@ -613,24 +616,24 @@ interrupted_before_change_type_ram2disc(suite) -> []; interrupted_before_change_type_ram2disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point). -interrupted_before_change_type_ram2disc_only(suite) -> []; -interrupted_before_change_type_ram2disc_only(Config) when is_list(Config) -> +interrupted_before_change_type_ram2do(suite) -> []; +interrupted_before_change_type_ram2do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point). interrupted_before_change_type_disc2ram(suite) -> []; interrupted_before_change_type_disc2ram(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point). -interrupted_before_change_type_disc2disc_only(suite) -> []; -interrupted_before_change_type_disc2disc_only(Config) when is_list(Config) -> +interrupted_before_change_type_disc2do(suite) -> []; +interrupted_before_change_type_disc2do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point). -interrupted_before_change_type_disc_only2ram(suite) -> []; -interrupted_before_change_type_disc_only2ram(Config) when is_list(Config) -> +interrupted_before_change_type_do2ram(suite) -> []; +interrupted_before_change_type_do2ram(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point). -interrupted_before_change_type_disc_only2disc(suite) -> []; -interrupted_before_change_type_disc_only2disc(Config) when is_list(Config) -> +interrupted_before_change_type_do2disc(suite) -> []; +interrupted_before_change_type_do2disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, dump_schema_op}, interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point). interrupted_before_change_type_other_node(suite) -> []; @@ -642,24 +645,24 @@ interrupted_after_change_type_ram2disc(suite) -> []; interrupted_after_change_type_ram2disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point). -interrupted_after_change_type_ram2disc_only(suite) -> []; -interrupted_after_change_type_ram2disc_only(Config) when is_list(Config) -> +interrupted_after_change_type_ram2do(suite) -> []; +interrupted_after_change_type_ram2do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point). interrupted_after_change_type_disc2ram(suite) -> []; interrupted_after_change_type_disc2ram(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point). -interrupted_after_change_type_disc2disc_only(suite) -> []; -interrupted_after_change_type_disc2disc_only(Config) when is_list(Config) -> +interrupted_after_change_type_disc2do(suite) -> []; +interrupted_after_change_type_disc2do(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point). -interrupted_after_change_type_disc_only2ram(suite) -> []; -interrupted_after_change_type_disc_only2ram(Config) when is_list(Config) -> +interrupted_after_change_type_do2ram(suite) -> []; +interrupted_after_change_type_do2ram(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point). -interrupted_after_change_type_disc_only2disc(suite) -> []; -interrupted_after_change_type_disc_only2disc(Config) when is_list(Config) -> +interrupted_after_change_type_do2disc(suite) -> []; +interrupted_after_change_type_do2disc(Config) when is_list(Config) -> Debug_Point = {mnesia_dumper, post_dump}, interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point). interrupted_after_change_type_other_node(suite) -> []; diff --git a/lib/observer/src/observer.appup.src b/lib/observer/src/observer.appup.src index 1d5a0d93f5..9fde365ff3 100644 --- a/lib/observer/src/observer.appup.src +++ b/lib/observer/src/observer.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,5 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% -{"%VSN%",[],[]}. +{"%VSN%", + [{<<".*">>,[{restart_application, observer}]}], + [{<<".*">>,[{restart_application, observer}]}] +}. diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl index b6665cb70b..c076c5e81e 100644 --- a/lib/observer/test/observer_SUITE.erl +++ b/lib/observer/test/observer_SUITE.erl @@ -26,7 +26,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). %% Test cases --export([app_file/1]). +-export([app_file/1, appup_file/1]). %% Default timetrap timeout (set in init_per_testcase) -define(default_timeout, ?t:minutes(1)). @@ -43,7 +43,7 @@ end_per_testcase(_Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app_file]. + [app_file, appup_file]. groups() -> []. @@ -68,3 +68,7 @@ app_file(doc) -> app_file(Config) when is_list(Config) -> ?line ok = ?t:app_test(observer), ok. + +%% Testing .appup file +appup_file(Config) when is_list(Config) -> + ok = ?t:appup_test(observer). diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src index c7c83ea079..bf8872eae4 100644 --- a/lib/odbc/src/odbc.appup.src +++ b/lib/odbc/src/odbc.appup.src @@ -1,8 +1,24 @@ %% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% {"%VSN%", [ - {<<"2\\.*">>, [{restart_application, odbc}]} + {<<"2\\..*">>, [{restart_application, odbc}]} ], [ - {<<"2\\.*">>, [{restart_application, odbc}]} + {<<"2\\..*">>, [{restart_application, odbc}]} ]}. diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl index e3a3440559..a7bb1d0ffe 100644 --- a/lib/odbc/test/odbc_start_SUITE.erl +++ b/lib/odbc/test/odbc_start_SUITE.erl @@ -109,8 +109,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case odbc_test_lib:odbc_check() of - ok -> [start]; - Other -> {skip, Other} + ok -> [app, appup, start]; + Other -> [app, appup] end. groups() -> @@ -127,6 +127,14 @@ end_per_group(_GroupName, Config) -> %% Test cases starts here. %%-------------------------------------------------------------------- +%% Test that the odbc app file is ok +app(Config) when is_list(Config) -> + ok = ?t:app_test(odbc). + +%% Test that the odbc appup file is ok +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(odbc). + start(doc) -> ["Test start/stop of odbc"]; start(suite) -> diff --git a/lib/orber/src/Makefile b/lib/orber/src/Makefile index 1c6781e5fd..d96350f4d5 100644 --- a/lib/orber/src/Makefile +++ b/lib/orber/src/Makefile @@ -21,9 +21,6 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk -# To get hold of SYSTEM_VSN (e.g. R9C). -#include $(ERL_TOP)/erts/vsn.mk - # ---------------------------------------------------- # Application version # ---------------------------------------------------- diff --git a/lib/os_mon/src/os_mon.appup.src b/lib/os_mon/src/os_mon.appup.src index f8e09a7d87..480f5d9511 100644 --- a/lib/os_mon/src/os_mon.appup.src +++ b/lib/os_mon/src/os_mon.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,26 +16,7 @@ %% %% %CopyrightEnd% %% - {"%VSN%", - [ - {"2.1", - [{load_module, cpu_sup}, - {load_module, disksup}, - {load_module, memsup}, - {load_module, os_mon}, - {load_module, os_mon_mib}]}, - {"2.1.1", - [{load_module, os_mon_mib}]} - ], - [ - {"2.1", - [{load_module, cpu_sup}, - {load_module, disksup}, - {load_module, memsup}, - {load_module, os_mon}, - {load_module, os_mon_mib}]}, - {"2.1.1", - [{load_module, os_mon_mib}]} - ] + [{<<".*">>,[{restart_application, os_mon}]}], + [{<<".*">>,[{restart_application, os_mon}]}] }. diff --git a/lib/os_mon/test/os_mon_SUITE.erl b/lib/os_mon/test/os_mon_SUITE.erl index f074657d4c..08ad8436dd 100644 --- a/lib/os_mon/test/os_mon_SUITE.erl +++ b/lib/os_mon/test/os_mon_SUITE.erl @@ -25,7 +25,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). %% Test cases --export([app_file/1, config/1]). +-export([app_file/1, appup_file/1, config/1]). %% Default timetrap timeout (set in init_per_testcase) -define(default_timeout, ?t:minutes(1)). @@ -43,8 +43,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case test_server:os_type() of - {unix, sunos} -> [app_file, config]; - _OS -> [app_file] + {unix, sunos} -> [app_file, appup_file, config]; + _OS -> [app_file, appup_file] end. groups() -> @@ -71,6 +71,9 @@ app_file(Config) when is_list(Config) -> ?line ok = test_server:app_test(os_mon), ok. +appup_file(Config) when is_list(Config) -> + ok = test_server:appup_test(os_mon). + config(suite) -> []; config(doc) -> diff --git a/lib/otp_mibs/src/otp_mibs.appup.src b/lib/otp_mibs/src/otp_mibs.appup.src index 5e99dfe325..fd5ce1e391 100644 --- a/lib/otp_mibs/src/otp_mibs.appup.src +++ b/lib/otp_mibs/src/otp_mibs.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,6 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% - -{"%VSN%",[],[]}. +{"%VSN%", + [{<<".*">>,[{restart_application, otp_mibs}]}], + [{<<".*">>,[{restart_application, otp_mibs}]}] +}. diff --git a/lib/otp_mibs/test/otp_mibs_SUITE.erl b/lib/otp_mibs/test/otp_mibs_SUITE.erl index 5fd52ac2ac..5376c54210 100644 --- a/lib/otp_mibs/test/otp_mibs_SUITE.erl +++ b/lib/otp_mibs/test/otp_mibs_SUITE.erl @@ -45,7 +45,7 @@ end_per_testcase/2]). % Test cases must be exported. --export([nt_basic_types/1, nt_high_reduction_count/1]). +-export([app/1, appup/1, nt_basic_types/1, nt_high_reduction_count/1]). -define(TRAP_UDP, 5000). -define(AGENT_UDP, 4000). @@ -75,9 +75,10 @@ end_per_testcase(_Case, Config) when is_list(Config) -> suite() -> [{ct_hooks,[ts_install_cth]}, {require, snmp_mgr_agent, snmp}]. -all() -> [{group, node_table}]. +all() -> [{group, app}, {group, node_table}]. -groups() -> [{node_table, [], [nt_basic_types, nt_high_reduction_count]}]. +groups() -> [{app, [], [app, appup]}, + {node_table, [], [nt_basic_types, nt_high_reduction_count]}]. init_per_group(_GroupName, Config) -> Config. @@ -118,6 +119,14 @@ end_per_suite(Config) -> %% Test cases %%--------------------------------------------------------------------- +%% Test that the otp_mibs app file is ok +app(Config) when is_list(Config) -> + ok = ?t:app_test(otp_mibs). + +%% Test that the otp_mibs appup file is ok +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(otp_mibs). + nt_basic_types(suite) -> []; nt_basic_types(doc) -> diff --git a/lib/parsetools/src/parsetools.appup.src b/lib/parsetools/src/parsetools.appup.src index 54a63833e6..0e02099893 100644 --- a/lib/parsetools/src/parsetools.appup.src +++ b/lib/parsetools/src/parsetools.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, parsetools}]}], + [{<<".*">>,[{restart_application, parsetools}]}] +}. diff --git a/lib/parsetools/test/Makefile b/lib/parsetools/test/Makefile index 6455f6ade7..7c7cc13965 100644 --- a/lib/parsetools/test/Makefile +++ b/lib/parsetools/test/Makefile @@ -20,6 +20,7 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ + app_SUITE \ leex_SUITE \ yecc_SUITE diff --git a/lib/parsetools/test/app_SUITE.erl b/lib/parsetools/test/app_SUITE.erl new file mode 100644 index 0000000000..88ac95e311 --- /dev/null +++ b/lib/parsetools/test/app_SUITE.erl @@ -0,0 +1,50 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(app_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [app, appup]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the parsetools app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(parsetools). + +appup() -> + [{doc, "Test that the parsetools appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(parsetools). diff --git a/lib/percept/src/percept.appup.src b/lib/percept/src/percept.appup.src index 4fc2852878..23e67f772f 100644 --- a/lib/percept/src/percept.appup.src +++ b/lib/percept/src/percept.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% - -{"%VSN%",[],[]}. - +{"%VSN%", + [{<<".*">>,[{restart_application, percept}]}], + [{<<".*">>,[{restart_application, percept}]}] +}. diff --git a/lib/percept/test/percept_SUITE.erl b/lib/percept/test/percept_SUITE.erl index e415d92a04..aea2462b2e 100644 --- a/lib/percept/test/percept_SUITE.erl +++ b/lib/percept/test/percept_SUITE.erl @@ -27,6 +27,8 @@ %% Test cases -export([ + app/1, + appup/1, profile/1, analyze/1, analyze_dist/1, @@ -54,7 +56,7 @@ end_per_testcase(_Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [webserver, profile, analyze, analyze_dist]. + [app, appup, webserver, profile, analyze, analyze_dist]. groups() -> []. @@ -70,6 +72,14 @@ end_per_group(_GroupName, Config) -> %% Tests %%---------------------------------------------------------------------- +%% Test that the percept app file is ok +app(Config) when is_list(Config) -> + ok = ?t:app_test(percept). + +%% Test that the percept appup file is ok +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(percept). + webserver(suite) -> []; webserver(doc) -> diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src index aacd3b866d..5278732c87 100644 --- a/lib/public_key/src/public_key.appup.src +++ b/lib/public_key/src/public_key.appup.src @@ -1,8 +1,21 @@ %% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% {"%VSN%", - [ - {<<"0\\.*">>, [{restart_application, public_key}]} - ], - [ - {<<"0\\.*">>, [{restart_application, public_key}]} - ]}. + [{<<".*">>,[{restart_application, public_key}]}], + [{<<".*">>,[{restart_application, public_key}]}] +}. diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index d3e9bf7cf6..163f5f4413 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -36,7 +36,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app, + [app, appup, {group, pem_decode_encode}, {group, ssh_public_key_decode_encode}, encrypt_decrypt, @@ -95,6 +95,13 @@ app(Config) when is_list(Config) -> %%-------------------------------------------------------------------- +appup() -> + [{doc, "Test that the public_key appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(public_key). + +%%-------------------------------------------------------------------- + dsa_pem() -> [{doc, "DSA PEM-file decode/encode"}]. dsa_pem(Config) when is_list(Config) -> diff --git a/lib/reltool/src/reltool.appup.src b/lib/reltool/src/reltool.appup.src index c02edd2afb..79ecdbd392 100644 --- a/lib/reltool/src/reltool.appup.src +++ b/lib/reltool/src/reltool.appup.src @@ -1,8 +1,7 @@ -%% This is an -*- erlang -*- file. -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,7 +15,7 @@ %% under the License. %% %% %CopyrightEnd% - {"%VSN%", - [ ] + [{<<".*">>,[{restart_application, reltool}]}], + [{<<".*">>,[{restart_application, reltool}]}] }. diff --git a/lib/reltool/test/reltool_app_SUITE.erl b/lib/reltool/test/reltool_app_SUITE.erl index a6e00cde08..9abc7fea41 100644 --- a/lib/reltool/test/reltool_app_SUITE.erl +++ b/lib/reltool/test/reltool_app_SUITE.erl @@ -26,6 +26,7 @@ -compile(export_all). -include("reltool_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). t() -> reltool_test_lib:t(?MODULE). @@ -64,7 +65,7 @@ end_per_testcase(Func,Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [fields, modules, export_all, app_depend, undef_funcs]. + [fields, modules, export_all, app_depend, undef_funcs, appup]. groups() -> []. @@ -290,3 +291,9 @@ key1search(Key, L) -> {value, {Key, Value}} -> Value end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Test that the reltool appup file is ok +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(reltool). diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 68ef04f20c..fea0854042 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2013. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -227,7 +227,9 @@ fetch_stats(Parent, Time) -> fetch_stats_loop(Parent, Time) -> erlang:system_flag(scheduler_wall_time, true), receive - _Msg -> erlang:system_flag(scheduler_wall_time, false) + _Msg -> + %% erlang:system_flag(scheduler_wall_time, false) + ok after Time -> _M = Parent ! {stats, 1, erlang:statistics(scheduler_wall_time), @@ -244,17 +246,6 @@ etop_collect(Collector) -> %% utilization in etop). Next time the flag will be true and then %% there will be a measurement. SchedulerWallTime = erlang:statistics(scheduler_wall_time), - - %% Turn off the flag while collecting data per process etc. - case erlang:system_flag(scheduler_wall_time,false) of - false -> - %% First time and the flag was false - start a monitoring - %% process to set the flag back to false when etop is stopped. - spawn(fun() -> flag_holder_proc(Collector) end); - _ -> - ok - end, - ProcInfo = etop_collect(processes(), []), Collector ! {self(),#etop_info{now = now(), @@ -264,13 +255,22 @@ etop_collect(Collector) -> memi = etop_memi(), procinfo = ProcInfo }}, + + case SchedulerWallTime of + undefined -> + spawn(fun() -> flag_holder_proc(Collector) end); + _ -> + ok + end, + erlang:system_flag(scheduler_wall_time,true). flag_holder_proc(Collector) -> Ref = erlang:monitor(process,Collector), receive {'DOWN',Ref,_,_,_} -> - erlang:system_flag(scheduler_wall_time,false) + %% erlang:system_flag(scheduler_wall_time,false) + ok end. etop_memi() -> diff --git a/lib/runtime_tools/src/runtime_tools.appup.src b/lib/runtime_tools/src/runtime_tools.appup.src index 7a435e9b22..0c2bab316f 100644 --- a/lib/runtime_tools/src/runtime_tools.appup.src +++ b/lib/runtime_tools/src/runtime_tools.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,5 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% -{"%VSN%",[],[]}. +{"%VSN%", + [{<<".*">>,[{restart_application, runtime_tools}]}], + [{<<".*">>,[{restart_application, runtime_tools}]}] +}. diff --git a/lib/runtime_tools/test/runtime_tools_SUITE.erl b/lib/runtime_tools/test/runtime_tools_SUITE.erl index 62497ab527..48ed810918 100644 --- a/lib/runtime_tools/test/runtime_tools_SUITE.erl +++ b/lib/runtime_tools/test/runtime_tools_SUITE.erl @@ -25,7 +25,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). %% Test cases --export([app_file/1, start_stop_app/1]). +-export([app_file/1, appup_file/1, start_stop_app/1]). %% Default timetrap timeout (set in init_per_testcase) -define(default_timeout, ?t:minutes(1)). @@ -43,6 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [app_file, + appup_file, start_stop_app]. groups() -> @@ -65,6 +66,9 @@ app_file(_Config) -> ?line ok = ?t:app_test(runtime_tools), ok. +appup_file(_Config) -> + ok = ?t:appup_test(runtime_tools). + start_stop_app(_Config) -> ok = application:start(runtime_tools), Sup = whereis(runtime_tools_sup), diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src index 0c4d80a74f..e789853eea 100644 --- a/lib/sasl/src/sasl.appup.src +++ b/lib/sasl/src/sasl.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,12 +16,10 @@ %% %% %CopyrightEnd% {"%VSN%", - %% Up from - max two major revisions back + %% Up from - max one major revision back [{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 - {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 - {<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}], %% R15 - %% Down to - max two major revisions back + {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}], %% R16 + %% Down to - max one major revision back [{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 - {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 - {<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15 + {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16 }. diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl index 54c327410d..76f753c3d0 100644 --- a/lib/sasl/src/systools_rc.erl +++ b/lib/sasl/src/systools_rc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -365,14 +365,22 @@ translate_application_instrs(Script, Appls, PreAppls) -> case lists:keysearch(Appl, #application.name, Appls) of {value, PostApplication} -> PostMods = PostApplication#application.modules, + Type = PostApplication#application.type, + Apply = + case Type of + none -> []; + load -> [{apply, {application, load, + [Appl]}}]; + _ -> [{apply, {application, start, + [Appl, Type]}}] + end, [{apply, {application, stop, [Appl]}}] ++ [{remove, {M, brutal_purge, brutal_purge}} || M <- PreMods] ++ [{purge, PreMods}] ++ [{add_module, M, []} || M <- PostMods] ++ - [{apply, {application, start, - [Appl, permanent]}}]; + Apply; false -> throw({error, {no_such_application, Appl}}) end; diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl index e799230338..f4455f7e9b 100644 --- a/lib/sasl/test/sasl_SUITE.erl +++ b/lib/sasl/test/sasl_SUITE.erl @@ -19,11 +19,6 @@ -module(sasl_SUITE). -include_lib("common_test/include/ct.hrl"). - -%% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(1)). --define(application, sasl). - %% Test server specific exports -export([all/0,groups/0,init_per_group/2,end_per_group/2]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -47,28 +42,26 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> - Dog=test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + Config. +end_per_testcase(_Case, _Config) -> ok. app_test(Config) when is_list(Config) -> ?t:app_test(sasl, allow), ok. -%% Test that appup allows upgrade from/downgrade to a maximum of two -%% major releases back. +%% Test that appup allows upgrade from/downgrade to a maximum of one +%% major release back. appup_test(_Config) -> - do_appup_tests(create_test_vsns()). + appup_tests(sasl,create_test_vsns(sasl)). -do_appup_tests({[],[]}) -> +appup_tests(_App,{[],[]}) -> {skip,"no previous releases available"}; -do_appup_tests({OkVsns,NokVsns}) -> - application:load(sasl), - {_,_,Vsn} = lists:keyfind(sasl,1,application:loaded_applications()), - AppupFile = filename:join([code:lib_dir(sasl),ebin,"sasl.appup"]), +appup_tests(App,{OkVsns,NokVsns}) -> + application:load(App), + {_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()), + AppupFileName = atom_to_list(App) ++ ".appup", + AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]), {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile), ct:log("~p~n",[AppupScript]), ct:log("Testing ok versions: ~p~n",[OkVsns]), @@ -79,13 +72,12 @@ do_appup_tests({OkVsns,NokVsns}) -> check_appup(NokVsns,DownTo,error), ok. -create_test_vsns() -> +create_test_vsns(App) -> This = erlang:system_info(otp_release), FirstMajor = previous_major(This), SecondMajor = previous_major(FirstMajor), - ThirdMajor = previous_major(SecondMajor), - Ok = sasl_vsn([FirstMajor,SecondMajor]), - Nok0 = sasl_vsn([ThirdMajor]), + Ok = app_vsn(App,[FirstMajor]), + Nok0 = app_vsn(App,[SecondMajor]), Nok = case Ok of [Ok1|_] -> [Ok1 ++ ",1" | Nok0]; % illegal @@ -101,19 +93,36 @@ previous_major("r"++Rel) -> previous_major(Rel) -> integer_to_list(list_to_integer(Rel)-1). -sasl_vsn([R|Rs]) -> - case test_server:is_release_available(R) of - true -> - {ok,N} = test_server:start_node(prevrel,peer,[{erl,[{release,R}]}]), - _ = rpc:call(N,application,load,[sasl]), +app_vsn(App,[R|Rs]) -> + OldRel = + case test_server:is_release_available(R) of + true -> + {release,R}; + false -> + case ct:get_config({otp_releases,list_to_atom(R)}) of + undefined -> + false; + Prog0 -> + case os:find_executable(Prog0) of + false -> + false; + Prog -> + {prog,Prog} + end + end + end, + case OldRel of + false -> + app_vsn(App,Rs); + _ -> + {ok,N} = test_server:start_node(prevrel,peer,[{erl,[OldRel]}]), + _ = rpc:call(N,application,load,[App]), As = rpc:call(N,application,loaded_applications,[]), - {_,_,V} = lists:keyfind(sasl,1,As), + {_,_,V} = lists:keyfind(App,1,As), test_server:stop_node(N), - [V|sasl_vsn(Rs)]; - false -> - sasl_vsn(Rs) + [V|app_vsn(App,Rs)] end; -sasl_vsn([]) -> +app_vsn(_App,[]) -> []. check_appup([Vsn|Vsns],Instrs,Expected) -> diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl index 0cb6e63cf3..5efab7c028 100644 --- a/lib/sasl/test/systools_rc_SUITE.erl +++ b/lib/sasl/test/systools_rc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2012. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -436,25 +436,19 @@ translate(Config) when is_list(Config) -> translate_app(Config) when is_list(Config) -> PreApps = - [#application{name = test, - description = "TEST", - vsn = "1.0", - modules = [foo,bar,baz], - regs = [], - mod = {sasl, []}}, + [Test = #application{name = test, + description = "TEST", + vsn = "1.0", + modules = [foo,bar,baz], + regs = [], + mod = {sasl, []}}, #application{name = pelle, description = "PELLE", vsn = "1.0", modules = [pelle, kalle], regs = [], mod = {pelle, []}}], - Apps = - [#application{name = test, - description = "TEST", - vsn = "1.0", - modules = [foo,bar,baz], - regs = [], - mod = {sasl, []}}], + Apps = [Test], %% Simple translation (1) Up1 = [{add_module, foo}, {add_module, bar}], @@ -475,6 +469,56 @@ translate_app(Config) when is_list(Config) -> {load,{baz,brutal_purge,brutal_purge}}, {apply,{application,start,[test,permanent]}}] = X2, + %% Translate add_application with different restart types + %% permanent + Up2_1 = [{add_application, test, permanent}], + {ok, X2_1} = systools_rc:translate_scripts([Up2_1], Apps, []), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,permanent]}}] = X2_1, + + %% transient + Up2_2 = [{add_application, test, transient}], + {ok, X2_2} = systools_rc:translate_scripts([Up2_2], Apps, []), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,transient]}}] = X2_2, + + %% temporary + Up2_3 = [{add_application, test, temporary}], + {ok, X2_3} = systools_rc:translate_scripts([Up2_3], Apps, []), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,temporary]}}] = X2_3, + + %% load + Up2_4 = [{add_application, test, load}], + {ok, X2_4} = systools_rc:translate_scripts([Up2_4], Apps, []), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,load,[test]}}] = X2_4, + + %% none + Up2_5 = [{add_application, test, none}], + {ok, X2_5} = systools_rc:translate_scripts([Up2_5], Apps, []), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}] = X2_5, + %% Simple translation (3) Up3 = [{remove_application, pelle}], {ok, X3} = systools_rc:translate_scripts([Up3], Apps, PreApps), @@ -484,6 +528,102 @@ translate_app(Config) when is_list(Config) -> {remove,{kalle,brutal_purge,brutal_purge}}, {purge,[pelle,kalle]}, {apply,{application,unload,[pelle]}}] = X3, + + %% Simple translation (4) + Up4 = [{restart_application, test}], + {ok, X4} = systools_rc:translate_scripts([Up4], Apps, PreApps), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {apply,{application,stop,[test]}}, + {remove,{foo,brutal_purge,brutal_purge}}, + {remove,{bar,brutal_purge,brutal_purge}}, + {remove,{baz,brutal_purge,brutal_purge}}, + {purge,[foo,bar,baz]}, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,permanent]}}] = X4, + + %% Translate restart_application with different restart types + %% permanent + {ok, X4_1} = systools_rc:translate_scripts([Up4], + [Test#application{type=permanent}], + [Test]), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {apply,{application,stop,[test]}}, + {remove,{foo,brutal_purge,brutal_purge}}, + {remove,{bar,brutal_purge,brutal_purge}}, + {remove,{baz,brutal_purge,brutal_purge}}, + {purge,[foo,bar,baz]}, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,permanent]}}] = X4_1, + + %% transient + {ok, X4_2} = systools_rc:translate_scripts([Up4], + [Test#application{type=transient}], + [Test]), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {apply,{application,stop,[test]}}, + {remove,{foo,brutal_purge,brutal_purge}}, + {remove,{bar,brutal_purge,brutal_purge}}, + {remove,{baz,brutal_purge,brutal_purge}}, + {purge,[foo,bar,baz]}, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,transient]}}] = X4_2, + + %% temporary + {ok, X4_3} = systools_rc:translate_scripts([Up4], + [Test#application{type=temporary}], + [Test]), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {apply,{application,stop,[test]}}, + {remove,{foo,brutal_purge,brutal_purge}}, + {remove,{bar,brutal_purge,brutal_purge}}, + {remove,{baz,brutal_purge,brutal_purge}}, + {purge,[foo,bar,baz]}, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,temporary]}}] = X4_3, + + %% load + {ok, X4_4} = systools_rc:translate_scripts([Up4], + [Test#application{type=load}], + [Test]), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {apply,{application,stop,[test]}}, + {remove,{foo,brutal_purge,brutal_purge}}, + {remove,{bar,brutal_purge,brutal_purge}}, + {remove,{baz,brutal_purge,brutal_purge}}, + {purge,[foo,bar,baz]}, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,load,[test]}}] = X4_4, + + %% none + {ok, X4_5} = systools_rc:translate_scripts([Up4], + [Test#application{type=none}], + [Test]), + [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {apply,{application,stop,[test]}}, + {remove,{foo,brutal_purge,brutal_purge}}, + {remove,{bar,brutal_purge,brutal_purge}}, + {remove,{baz,brutal_purge,brutal_purge}}, + {purge,[foo,bar,baz]}, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}] = X4_5, + ok. diff --git a/lib/sasl/test/test_lib.hrl b/lib/sasl/test/test_lib.hrl index 37aa44c198..c8a4e92f24 100644 --- a/lib/sasl/test/test_lib.hrl +++ b/lib/sasl/test/test_lib.hrl @@ -1,3 +1,3 @@ -define(ertsvsn,"4.4"). --define(kernelvsn,"2.15.3"). --define(stdlibvsn,"1.18.3"). +-define(kernelvsn,"2.16.4"). +-define(stdlibvsn,"1.19.4"). diff --git a/lib/snmp/test/snmp_appup_test.erl b/lib/snmp/test/snmp_appup_test.erl index 99994a2410..021d42a978 100644 --- a/lib/snmp/test/snmp_appup_test.erl +++ b/lib/snmp/test/snmp_appup_test.erl @@ -63,33 +63,9 @@ end_per_group(_GroupName, Config) -> init_per_suite(suite) -> []; init_per_suite(doc) -> []; init_per_suite(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), - TopDir = filename:join(PrivDir, appup), - case file:make_dir(TopDir) of - ok -> - ok; - Error -> - fail({failed_creating_subsuite_top_dir, Error}) - end, - AppFile = file_name(?APPLICATION, ".app"), - AppupFile = file_name(?APPLICATION, ".appup"), - [{app_file, AppFile}, - {appup_file, AppupFile}, - {appup_topdir, TopDir} | Config]. + Config. -file_name(App, Ext) -> - Env = init:get_arguments(), - LibDir = - case lists:keysearch(clearcase, 1, Env) of - false -> - code:lib_dir(App); - _ -> - ".." - end, - filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]). - - end_per_suite(suite) -> []; end_per_suite(doc) -> []; end_per_suite(Config) when is_list(Config) -> @@ -108,467 +84,6 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -appup_file(suite) -> - []; -appup_file(doc) -> - "Perform a simple check of the appup file"; +%% Perform a simple check of the appup file appup_file(Config) when is_list(Config) -> - AppupFile = key1search(appup_file, Config), - AppFile = key1search(app_file, Config), - Modules = modules(AppFile), - check_appup(AppupFile, Modules). - -modules(File) -> - case file:consult(File) of - {ok, [{application,snmp,Info}]} -> - case lists:keysearch(modules,1,Info) of - {value, {modules, Modules}} -> - Modules; - false -> - fail({bad_appinfo, Info}) - end; - Error -> - fail({bad_appfile, Error, File}) - end. - - -check_appup(AppupFile, Modules) -> - case file:consult(AppupFile) of - {ok, [{V, UpFrom, DownTo}]} -> - check_appup(V, UpFrom, DownTo, Modules); - Else -> - fail({bad_appupfile, Else}) - end. - - -check_appup(V, UpFrom, DownTo, Modules) -> - check_version(V), - check_depends(up, UpFrom, Modules), - check_depends(down, DownTo, Modules), - check_module_subset(up, UpFrom), - check_module_subset(down, DownTo), - ok. - -check_depends(_, [], _) -> - ok; -check_depends(UpDown, [Dep|Deps], Modules) -> - check_depend(UpDown, Dep, Modules), - check_depends(UpDown, Deps, Modules). - - -check_depend(up = UpDown, {add_application, ?APPLICATION} = Instr, Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [UpDown, Instr, Modules]), - ok; -check_depend(down = UpDown, {remove_application, ?APPLICATION} = Instr, - Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [UpDown, Instr, Modules]), - ok; -check_depend(UpDown, {V, Instructions}, Modules) -> - d("check_instructions(~w) -> entry with" - "~n V: ~p" - "~n Modules: ~p", [UpDown, V, Modules]), - check_version(V), - case check_instructions(UpDown, - Instructions, Instructions, [], [], Modules) of - {_Good, []} -> - ok; - {_, Bad} -> - fail({bad_instructions, Bad, UpDown}) - end. - - -check_instructions(_, [], _, Good, Bad, _) -> - {lists:reverse(Good), lists:reverse(Bad)}; -check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instr: ~p", [UpDown,Instr]), - case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of - ok -> - check_instructions(UpDown, Instrs, AllInstr, - [Instr|Good], Bad, Modules); - {error, Reason} -> - check_instructions(UpDown, Instrs, AllInstr, Good, - [{Instr, Reason}|Bad], Modules) - end; -check_instructions(UpDown, Instructions, _, _, _, _) -> - fail({bad_instructions, {UpDown, Instructions}}). - -check_instruction(_, {restart_application, ?APPLICATION}, _, _Modules) -> - d("check_instruction -> entry when restart_application instruction"), - ok; - -%% A new module is added -check_instruction(up, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when up-add_module instruction with" - "~n Module: ~p", [Module]), - check_module(Module, Modules); - -%% An old module is re-added -check_instruction(down, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when down-add_module instruction with" - "~n Module: ~p", [Module]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - ok; - ok -> - error({existing_readded_module, Module}) - end; - -check_instruction(up, {delete_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when up-delete_module instruction with" - "~n Module: ~p", [Module]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - ok; - ok -> - error({module_cannot_be_deleted, Module}) - end; - -%% An new module is deleted -check_instruction(down, {delete_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when down-delete_module instruction with" - "~n Module: ~p", [Module]), - check_module(Module, Modules); - -%% Removing a module on upgrade: -%% - the module has been removed from the app-file. -%% - check that no module depends on this (removed) module -check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules) - when is_atom(Module) and is_atom(Pre) and is_atom(Post) -> - d("check_instruction -> entry when up-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - check_purge(Pre), - check_purge(Post); - ok -> - error({existing_removed_module, Module}) - end; - -%% Removing a module on downgrade: the module exist -%% in the app-file. -check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules) - when is_atom(Module) and is_atom(Pre) and is_atom(Post) -> - d("check_instruction -> entry when down-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - ok -> - check_purge(Pre), - check_purge(Post), - check_no_remove_depends(Module, AllInstr); - {error, {unknown_module, Module, Modules}} -> - error({nonexisting_removed_module, Module}) - end; - -check_instruction(_, {load_module, Module, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module) and - is_atom(Pre) and - is_atom(Post) and - is_list(Depend) -> - d("check_instruction -> entry when load_module instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {update, Module, Change, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module) and - is_atom(Pre) and - is_atom(Post) and - is_list(Depend) -> - d("check_instruction -> entry when update instruction with" - "~n Module: ~p" - "~n Change: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Change, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_change(Change), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {update, Module, supervisor}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when supervisor update instruction with" - "~n Module: ~p", [Module]), - check_module(Module, Modules); - -check_instruction(_, {apply, {Module, Function, Args}}, _, _Modules) - when is_atom(Module) and is_atom(Function) and is_list(Args) -> - d("check_instruction -> entry when apply instruction with" - "~n Module: ~p" - "~n Function: ~p" - "~n Args: ~p", [Module, Function, Args]), - check_apply(Module, Function, Args); - -check_instruction(_, Instr, _AllInstr, _Modules) -> - error({error, {unknown_instruction, Instr}}). - -%% If Module X depends on Module Y, then module Y must have an update -%% instruction of some sort (otherwise the depend is faulty). -updated_modules([], Modules) -> - d("updated_modules -> entry when done with" - "~n Modules: ~p", [Modules]), - Modules; -updated_modules([Instr|Instrs], Modules) -> - d("updated_modules -> entry with" - "~n Instr: ~p" - "~n Modules: ~p", [Instr,Modules]), - case instruction_module(Instr) of - {module, Module} -> - d("updated_modules -> Module: ~p", [Module]), - updated_modules(Instrs, [Module|Modules]); - no_module -> - updated_modules(Instrs, Modules) - end. - -instruction_module({add_module, Module}) -> - {module, Module}; -instruction_module({delete_module, Module}) -> - {module, Module}; -instruction_module({remove, {Module, _, _}}) -> - {module, Module}; -instruction_module({load_module, Module, _, _, _}) -> - {module, Module}; -instruction_module({update, Module, _, _, _, _}) -> - {module, Module}; -instruction_module({update, Module, _}) -> - {module, Module}; -instruction_module({apply, {_, _, _}}) -> - no_module; -instruction_module(Instr) -> - d("instruction_module -> entry when unknown instruction with" - "~n Instr: ~p", [Instr]), - error({error, {unknown_instruction, Instr}}). - - -%% Check that the modules handled in an instruction set for version X -%% is a subset of the instruction set for version X-1. -check_module_subset(Direction, Instructions) -> - d("check_module_subset(~w) -> entry when" - "~n Instructions: ~p", [Direction,Instructions]), - do_check_module_subset(modules_of(Instructions)). - -do_check_module_subset([]) -> - ok; -do_check_module_subset([_]) -> - ok; -do_check_module_subset([{_V1, Mods1}|T]) -> - d("do_check_module_subset -> entry with" - "~n V1: ~s" - "~n Mods1: ~p", [_V1, Mods1]), - {V2, Mods2} = hd(T), - d("do_check_module_subset -> " - "~n V2: ~s" - "~n Mods2: ~p", [V2, Mods2]), - %% Check that the modules in V1 is a subset of V2 - case do_check_module_subset2(Mods1, Mods2) of - ok -> - do_check_module_subset(T); - {error, Modules} -> - fail({subset_missing_instructions, V2, Modules}) - end. - -do_check_module_subset2(_Mods1, [{restart_application, ?APPLICATION}]) -> - ok; -do_check_module_subset2(Mods1, Mods2) -> - do_check_module_subset2(Mods1, Mods2, []). - -do_check_module_subset2([], _, []) -> - ok; -do_check_module_subset2([], _, Acc) -> - {error, lists:reverse(Acc)}; -do_check_module_subset2([Mod|Mods], Mods2, Acc) -> - case lists:member(Mod, Mods2) of - true -> - do_check_module_subset2(Mods, Mods2, Acc); - false -> - do_check_module_subset2(Mods, Mods2, [Mod|Acc]) - end. - - -modules_of(Instructions) -> - modules_of(Instructions, []). - -modules_of([], Acc) -> - lists:reverse(Acc); -modules_of([{_V,[{restart_application, ?APPLICATION}]}|T], Acc) -> - modules_of(T, Acc); -modules_of([{V,Instructions}|T], Acc) -> - Mods = modules_of2(Instructions, []), - modules_of(T, [{V, Mods}|Acc]). - -modules_of2([], Acc) -> - lists:reverse(Acc); -modules_of2([Instr|Instructions], Acc) -> - d("module_of -> entry with" - "~n Instr: ~p", [Instr]), - case module_of(Instr) of - {value, Mod} -> - d("module_of -> Mod: ~p", [Mod]), - modules_of2(Instructions, [Mod|Acc]); - false -> - modules_of2(Instructions, Acc) - end. - -module_of({add_module, Module}) -> - {value, Module}; -module_of({delete_module, Module}) -> - {value, Module}; -module_of({remove, {Module, _Pre, _Post}}) -> - {value, Module}; -module_of({load_module, Module, _Pre, _Post, _Depend}) -> - {value, Module}; -module_of({update, Module, _Change, _Pre, _Post, _Depend}) -> - {value, Module}; -module_of({update, Module, supervisor}) -> - {value, Module}; -module_of(_) -> - false. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -check_version(V) when is_list(V) -> - ok; -check_version(V) -> - error({bad_version, V}). - - -check_module(M, Modules) when is_atom(M) -> - case lists:member(M, Modules) of - true -> - ok; - false -> - error({unknown_module, M, Modules}) - end; -check_module(M, _) -> - error({bad_module, M}). - - -check_module_depend(M, [], _) when is_atom(M) -> - ok; -check_module_depend(M, Deps, Modules) when is_atom(M) and is_list(Deps) -> - case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of - [] -> - ok; - Unknown -> - error({unknown_depend_modules, Unknown}) - end; -check_module_depend(_M, D, _Modules) -> - error({bad_depend, D}). - - -check_no_remove_depends(_Module, []) -> - ok; -check_no_remove_depends(Module, [Instr|Instrs]) -> - check_no_remove_depend(Module, Instr), - check_no_remove_depends(Module, Instrs). - -check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, load_module, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, update, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(_, _) -> - ok. - - -check_change(soft) -> - ok; -check_change({advanced, _Something}) -> - ok; -check_change(Change) -> - error({bad_change, Change}). - - -check_purge(soft_purge) -> - ok; -check_purge(brutal_purge) -> - ok; -check_purge(Purge) -> - error({bad_purge, Purge}). - -check_apply(Module, Function, Args) -> - case (catch Module:module_info()) of - Info when is_list(Info) -> - check_exported(Function, Args, Info); - {'EXIT', {undef, _}} -> - error({not_existing_module, Module}) - end. - -check_exported(Function, Args, Info) -> - case lists:keysearch(exports, 1, Info) of - {value, {exports, FuncList}} -> - Arity = length(Args), - Arities = [A || {F, A} <- FuncList, F == Function], - case lists:member(Arity, Arities) of - true -> - ok; - false -> - fail({not_exported_function, Function, Arity}) - end; - _ -> - error({bad_export, Info}) - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -error(Reason) -> - throw({error, Reason}). - -fail(Reason) -> - exit({suite_failed, Reason}). - -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> - fail({not_found, Key, L}); - {value, {Key, Value}} -> - Value - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -d(F) -> - d(F, []). - -d(F, A) -> - d(true, F, A). - -d(true, F, A) -> - io:format(F ++ "~n", A); -d(_, _, _) -> - ok. - + ok = ?t:appup_test(snmp). diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 32f7cc470b..df34a5a3ff 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,13 +19,13 @@ {"%VSN%", [ - {<<"2.1\\.*">>, [{restart_application, ssh}]}, - {<<"2.0\\.*">>, [{restart_application, ssh}]}, - {<<"1\\.*">>, [{restart_application, ssh}]} + {<<"2\\.1\\..*">>, [{restart_application, ssh}]}, + {<<"2\\.0\\..*">>, [{restart_application, ssh}]}, + {<<"1\\..*">>, [{restart_application, ssh}]} ], [ - {<<"2.1\\.*">>,[{restart_application, ssh}]}, - {<<"2.0\\.*">>, [{restart_application, ssh}]}, - {<<"1\\.*">>, [{restart_application, ssh}]} + {<<"2\\.1\\..*">>,[{restart_application, ssh}]}, + {<<"2\\.0\\..*">>, [{restart_application, ssh}]}, + {<<"1\\..*">>, [{restart_application, ssh}]} ] }. diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index b4e3871efd..d2e52379fa 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -38,6 +38,7 @@ suite() -> all() -> [app_test, + appup_test, {group, dsa_key}, {group, rsa_key}, {group, dsa_pass_key}, @@ -150,6 +151,11 @@ app_test(Config) when is_list(Config) -> ?t:app_test(ssh), ok. %%-------------------------------------------------------------------- +appup_test() -> + [{doc, "Appup file consistency test."}]. +appup_test(Config) when is_list(Config) -> + ok = ?t:appup_test(ssh). +%%-------------------------------------------------------------------- misc_ssh_options() -> [{doc, "Test that we can set some misc options not tested elsewhere, " "some options not yet present are not decided if we should support or " diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 82106935cb..e283e6079e 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1757,12 +1757,12 @@ handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport, Connection:handle_close_alert(Data, StateName, State) end. -handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>}}) -> +handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cacerts = []}}) -> %% No trusted certs specified ok; handle_trusted_certs_db(#state{cert_db_ref = Ref, cert_db = CertDb, - ssl_options = #ssl_options{cacertfile = undefined}}) -> + ssl_options = #ssl_options{cacertfile = <<>>}}) -> %% Certs provided as DER directly can not be shared %% with other connections and it is safe to delete them when the connection ends. ssl_pkix_db:remove_trusted_certs(Ref, CertDb); diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index adb2e1debe..341a4217e4 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -73,7 +73,7 @@ renegotiation :: undefined | {boolean(), From::term() | internal | peer}, start_or_recv_from :: term(), timer :: undefined | reference(), % start_or_recive_timer - send_queue :: queue(), + send_queue :: queue:queue(), terminated = false ::boolean(), allow_renegotiate = true ::boolean(), expecting_next_protocol_negotiation = false ::boolean(), diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 2e216b32fa..523760aba6 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -84,6 +84,7 @@ all_versions_groups ()-> basic_tests() -> [app, + appup, alerts, send_close, version_option, @@ -292,6 +293,11 @@ app() -> app(Config) when is_list(Config) -> ok = ?t:app_test(ssl). %%-------------------------------------------------------------------- +appup() -> + [{doc, "Test that the ssl appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(ssl). +%%-------------------------------------------------------------------- alerts() -> [{doc, "Test ssl_alert:alert_txt/1"}]. alerts(Config) when is_list(Config) -> @@ -2208,7 +2214,14 @@ der_input(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + ssl_test_lib:close(Client), + + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + [CADb | _] = element(5, State), + [] = ets:tab2list(CADb). + %%-------------------------------------------------------------------- der_input_opts(Opts) -> Certfile = proplists:get_value(certfile, Opts), diff --git a/lib/stdlib/doc/src/array.xml b/lib/stdlib/doc/src/array.xml index b0b3aa6dc1..b03a2fa0cc 100644 --- a/lib/stdlib/doc/src/array.xml +++ b/lib/stdlib/doc/src/array.xml @@ -3,7 +3,7 @@ <erlref> <header> <copyright> - <year>2007</year><year>2013</year> + <year>2007</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -84,7 +84,7 @@ the default value cannot be confused with the values of set entries.</p> {'EXIT',{badarg,_}} = (catch array:get(18, A3)).</pre></description> <datatypes> <datatype> - <name><marker id="type-array">array()</marker></name> + <name name="array" n_vars="1"/> <desc> <p>A functional, extendible array. The representation is not documented and is subject to change without notice. Note that @@ -92,6 +92,12 @@ the default value cannot be confused with the values of set entries.</p> </desc> </datatype> <datatype> + <name name="array" n_vars="0"/> + <desc> + <p><c>array()</c> is equivalent to <c>array(term())</c>.</p> + </desc> + </datatype> + <datatype> <name name="array_indx"/> </datatype> <datatype> @@ -189,7 +195,7 @@ the default value cannot be confused with the values of set entries.</p> <desc><marker id="from_orddict-2"/> -<p>Convert an ordered list of pairs <c>{Index, Value}</c> to a +<p>Convert an ordered list of pairs <c>{Index, <anno>Value</anno>}</c> to a corresponding extendible array. <c><anno>Default</anno></c> is used as the value for uninitialized entries of the array. If <c><anno>Orddict</anno></c> is not a proper, ordered list of pairs whose first elements are nonnegative @@ -455,7 +461,7 @@ cannot be changed once the array has been created.</p> <desc><marker id="sparse_to_orddict-1"/> -<p>Convert the array to an ordered list of pairs <c>{Index, Value}</c>, +<p>Convert the array to an ordered list of pairs <c>{Index, <anno>Value</anno>}</c>, skipping default-valued entries. </p> <p><em>See also:</em> <seealso marker="#to_orddict-1">to_orddict/1</seealso>.</p> @@ -476,7 +482,7 @@ cannot be changed once the array has been created.</p> <desc><marker id="to_orddict-1"/> -<p>Convert the array to an ordered list of pairs <c>{Index, Value}</c>. +<p>Convert the array to an ordered list of pairs <c>{Index, <anno>Value</anno>}</c>. </p> <p><em>See also:</em> <seealso marker="#from_orddict-2">from_orddict/2</seealso>, <seealso marker="#sparse_to_orddict-1">sparse_to_orddict/1</seealso>.</p> </desc></func></funcs> diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml index 6ff81b56ee..942fd1f45e 100644 --- a/lib/stdlib/doc/src/dict.xml +++ b/lib/stdlib/doc/src/dict.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2013</year> + <year>1996</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -41,9 +41,15 @@ <datatypes> <datatype> - <name><marker id="type-dict">dict()</marker></name> + <name name="dict" n_vars="2"/> <desc><p>Dictionary as returned by <c>new/0</c>.</p></desc> </datatype> + <datatype> + <name name="dict" n_vars="0"/> + <desc> + <p><c>dict()</c> is equivalent to <c>dict(term(), term())</c>.</p> + </desc> + </datatype> </datatypes> <funcs> <func> diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml index c5d5707f4f..9b9b48584b 100644 --- a/lib/stdlib/doc/src/digraph.xml +++ b/lib/stdlib/doc/src/digraph.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2013</year> + <year>1996</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -98,7 +98,7 @@ <name name="d_protection"/> </datatype> <datatype> - <name><marker id="type-digraph">digraph()</marker></name> + <name name="graph"/> <desc><p>A digraph as returned by <c>new/0,1</c>.</p></desc> </datatype> <datatype> diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 21cf8e4149..3df24bf688 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2013</year> + <year>1996</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -171,6 +171,10 @@ <p>Returns a list of all tables at the node. Named tables are given by their names, unnamed tables are given by their table identifiers.</p> + <p>There is no guarantee of consistency in the returned list. Tables created + or deleted by other processes "during" the ets:all() call may or may + not be included in the list. Only tables created/deleted <em>before</em> + ets:all() is called are guaranteed to be included/excluded.</p> </desc> </func> <func> diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml index 8ca95df8fc..ea96c14472 100644 --- a/lib/stdlib/doc/src/gb_sets.xml +++ b/lib/stdlib/doc/src/gb_sets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2013</year> + <year>2001</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -115,28 +115,40 @@ <datatypes> <datatype> - <name><marker id="type-gb_set">gb_set()</marker></name> + <name name="set" n_vars="1"/> <desc><p>A GB set.</p></desc> </datatype> <datatype> - <name name="iter"/> + <name name="set" n_vars="0"/> + <desc> + <p><c>set()</c> is equivalent to <c>set(term())</c>.</p> + </desc> + </datatype> + <datatype> + <name name="iter" n_vars="1"/> <desc><p>A GB set iterator.</p></desc> </datatype> + <datatype> + <name name="iter" n_vars="0"/> + <desc> + <p><c>iter()</c> is equivalent to <c>iter(term())</c>.</p> + </desc> + </datatype> </datatypes> <funcs> <func> <name name="add" arity="2"/> <name name="add_element" arity="2"/> - <fsummary>Add a (possibly existing) element to a gb_set</fsummary> + <fsummary>Add a (possibly existing) element to a set</fsummary> <desc> - <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <p>Returns a new set formed from <c><anno>Set1</anno></c> with <c><anno>Element</anno></c> inserted. If <c><anno>Element</anno></c> is already an element in <c><anno>Set1</anno></c>, nothing is changed.</p> </desc> </func> <func> <name name="balance" arity="1"/> - <fsummary>Rebalance tree representation of a gb_set</fsummary> + <fsummary>Rebalance tree representation of a set</fsummary> <desc> <p>Rebalances the tree representation of <c><anno>Set1</anno></c>. Note that this is rarely necessary, but may be motivated when a large @@ -148,9 +160,9 @@ </func> <func> <name name="delete" arity="2"/> - <fsummary>Remove an element from a gb_set</fsummary> + <fsummary>Remove an element from a set</fsummary> <desc> - <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <p>Returns a new set formed from <c><anno>Set1</anno></c> with <c><anno>Element</anno></c> removed. Assumes that <c><anno>Element</anno></c> is present in <c><anno>Set1</anno></c>.</p> </desc> @@ -158,9 +170,9 @@ <func> <name name="delete_any" arity="2"/> <name name="del_element" arity="2"/> - <fsummary>Remove a (possibly non-existing) element from a gb_set</fsummary> + <fsummary>Remove a (possibly non-existing) element from a set</fsummary> <desc> - <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <p>Returns a new set formed from <c><anno>Set1</anno></c> with <c><anno>Element</anno></c> removed. If <c><anno>Element</anno></c> is not an element in <c><anno>Set1</anno></c>, nothing is changed.</p> </desc> @@ -168,7 +180,7 @@ <func> <name name="difference" arity="2"/> <name name="subtract" arity="2"/> - <fsummary>Return the difference of two gb_sets</fsummary> + <fsummary>Return the difference of two sets</fsummary> <desc> <p>Returns only the elements of <c><anno>Set1</anno></c> which are not also elements of <c><anno>Set2</anno></c>.</p> @@ -177,14 +189,14 @@ <func> <name name="empty" arity="0"/> <name name="new" arity="0"/> - <fsummary>Return an empty gb_set</fsummary> + <fsummary>Return an empty set</fsummary> <desc> - <p>Returns a new empty gb_set.</p> + <p>Returns a new empty set.</p> </desc> </func> <func> <name name="filter" arity="2"/> - <fsummary>Filter gb_set elements</fsummary> + <fsummary>Filter set elements</fsummary> <desc> <p>Filters elements in <c><anno>Set1</anno></c> using predicate function <c><anno>Pred</anno></c>.</p> @@ -192,7 +204,7 @@ </func> <func> <name name="fold" arity="3"/> - <fsummary>Fold over gb_set elements</fsummary> + <fsummary>Fold over set elements</fsummary> <desc> <p>Folds <c><anno>Function</anno></c> over every element in <c><anno>Set</anno></c> returning the final value of the accumulator.</p> @@ -200,46 +212,46 @@ </func> <func> <name name="from_list" arity="1"/> - <fsummary>Convert a list into a gb_set</fsummary> + <fsummary>Convert a list into a set</fsummary> <desc> - <p>Returns a gb_set of the elements in <c><anno>List</anno></c>, where + <p>Returns a set of the elements in <c><anno>List</anno></c>, where <c><anno>List</anno></c> may be unordered and contain duplicates.</p> </desc> </func> <func> <name name="from_ordset" arity="1"/> - <fsummary>Make a gb_set from an ordset list</fsummary> + <fsummary>Make a set from an ordset list</fsummary> <desc> - <p>Turns an ordered-set list <c><anno>List</anno></c> into a gb_set. The list + <p>Turns an ordered-set list <c><anno>List</anno></c> into a set. The list must not contain duplicates.</p> </desc> </func> <func> <name name="insert" arity="2"/> - <fsummary>Add a new element to a gb_set</fsummary> + <fsummary>Add a new element to a set</fsummary> <desc> - <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <p>Returns a new set formed from <c><anno>Set1</anno></c> with <c><anno>Element</anno></c> inserted. Assumes that <c><anno>Element</anno></c> is not present in <c><anno>Set1</anno></c>.</p> </desc> </func> <func> <name name="intersection" arity="2"/> - <fsummary>Return the intersection of two gb_sets</fsummary> + <fsummary>Return the intersection of two sets</fsummary> <desc> <p>Returns the intersection of <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p> </desc> </func> <func> <name name="intersection" arity="1"/> - <fsummary>Return the intersection of a list of gb_sets</fsummary> + <fsummary>Return the intersection of a list of sets</fsummary> <desc> - <p>Returns the intersection of the non-empty list of gb_sets.</p> + <p>Returns the intersection of the non-empty list of sets.</p> </desc> </func> <func> <name name="is_disjoint" arity="2"/> - <fsummary>Check whether two gb_sets are disjoint</fsummary> + <fsummary>Check whether two sets are disjoint</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c> are disjoint (have no elements in common), @@ -248,7 +260,7 @@ </func> <func> <name name="is_empty" arity="1"/> - <fsummary>Test for empty gb_set</fsummary> + <fsummary>Test for empty set</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set</anno></c> is an empty set, and <c>false</c> otherwise.</p> @@ -257,7 +269,7 @@ <func> <name name="is_member" arity="2"/> <name name="is_element" arity="2"/> - <fsummary>Test for membership of a gb_set</fsummary> + <fsummary>Test for membership of a set</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of <c><anno>Set</anno></c>, otherwise <c>false</c>.</p> @@ -265,9 +277,9 @@ </func> <func> <name name="is_set" arity="1"/> - <fsummary>Test for a gb_set</fsummary> + <fsummary>Test for a set</fsummary> <desc> - <p>Returns <c>true</c> if <c><anno>Term</anno></c> appears to be a gb_set, + <p>Returns <c>true</c> if <c><anno>Term</anno></c> appears to be a set, otherwise <c>false</c>.</p> </desc> </func> @@ -281,7 +293,7 @@ </func> <func> <name name="iterator" arity="1"/> - <fsummary>Return an iterator for a gb_set</fsummary> + <fsummary>Return an iterator for a set</fsummary> <desc> <p>Returns an iterator that can be used for traversing the entries of <c><anno>Set</anno></c>; see <c>next/1</c>. The implementation @@ -303,7 +315,7 @@ </func> <func> <name name="next" arity="1"/> - <fsummary>Traverse a gb_set with an iterator</fsummary> + <fsummary>Traverse a set with an iterator</fsummary> <desc> <p>Returns <c>{<anno>Element</anno>, <anno>Iter2</anno>}</c> where <c><anno>Element</anno></c> is the smallest element referred to by the iterator <c><anno>Iter1</anno></c>, @@ -314,14 +326,14 @@ </func> <func> <name name="singleton" arity="1"/> - <fsummary>Return a gb_set with one element</fsummary> + <fsummary>Return a set with one element</fsummary> <desc> - <p>Returns a gb_set containing only the element <c><anno>Element</anno></c>.</p> + <p>Returns a set containing only the element <c><anno>Element</anno></c>.</p> </desc> </func> <func> <name name="size" arity="1"/> - <fsummary>Return the number of elements in a gb_set</fsummary> + <fsummary>Return the number of elements in a set</fsummary> <desc> <p>Returns the number of elements in <c><anno>Set</anno></c>.</p> </desc> @@ -356,24 +368,24 @@ </func> <func> <name name="to_list" arity="1"/> - <fsummary>Convert a gb_set into a list</fsummary> + <fsummary>Convert a set into a list</fsummary> <desc> <p>Returns the elements of <c><anno>Set</anno></c> as a list.</p> </desc> </func> <func> <name name="union" arity="2"/> - <fsummary>Return the union of two gb_sets</fsummary> + <fsummary>Return the union of two sets</fsummary> <desc> - <p>Returns the merged (union) gb_set of <c><anno>Set1</anno></c> and + <p>Returns the merged (union) set of <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p> </desc> </func> <func> <name name="union" arity="1"/> - <fsummary>Return the union of a list of gb_sets</fsummary> + <fsummary>Return the union of a list of sets</fsummary> <desc> - <p>Returns the merged (union) gb_set of the list of gb_sets.</p> + <p>Returns the merged (union) set of the list of sets.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml index 57e60eacb7..b2f237e1d7 100644 --- a/lib/stdlib/doc/src/gb_trees.xml +++ b/lib/stdlib/doc/src/gb_trees.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2013</year> + <year>2001</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -59,13 +59,25 @@ <datatypes> <datatype> - <name><marker id="type-gb_tree">gb_tree()</marker></name> + <name name="tree" n_vars="2"/> <desc><p>A GB tree.</p></desc> </datatype> <datatype> - <name name="iter"/> + <name name="tree" n_vars="0"/> + <desc> + <p><c>tree()</c> is equivalent to <c>tree(term(), term())</c>.</p> + </desc> + </datatype> + <datatype> + <name name="iter" n_vars="2"/> <desc><p>A GB tree iterator.</p></desc> </datatype> + <datatype> + <name name="iter" n_vars="0"/> + <desc> + <p><c>iter()</c> is equivalent to <c>iter(term(), term())</c>.</p> + </desc> + </datatype> </datatypes> <funcs> <func> @@ -108,9 +120,9 @@ <name name="enter" arity="3"/> <fsummary>Insert or update key with value in a tree</fsummary> <desc> - <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c> if + <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> into <c><anno>Tree1</anno></c> if the key is not present in the tree, otherwise updates - <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>. Returns the + <c><anno>Key</anno></c> to value <c><anno>Value</anno></c> in <c><anno>Tree1</anno></c>. Returns the new tree.</p> </desc> </func> @@ -135,7 +147,7 @@ <name name="insert" arity="3"/> <fsummary>Insert a new key and value in a tree</fsummary> <desc> - <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c>; + <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> into <c><anno>Tree1</anno></c>; returns the new tree. Assumes that the key is not present in the tree, crashes otherwise.</p> </desc> @@ -181,8 +193,8 @@ <name name="largest" arity="1"/> <fsummary>Return largest key and value</fsummary> <desc> - <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the largest - key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated + <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>}</c>, where <c><anno>Key</anno></c> is the largest + key in <c><anno>Tree</anno></c>, and <c><anno>Value</anno></c> is the value associated with this key. Assumes that the tree is nonempty.</p> </desc> </func> @@ -191,7 +203,7 @@ <fsummary>Look up a key in a tree</fsummary> <desc> <p>Looks up <c><anno>Key</anno></c> in <c><anno>Tree</anno></c>; returns - <c>{value, <anno>Val</anno>}</c>, or <c>none</c> if <c><anno>Key</anno></c> is not + <c>{value, <anno>Value</anno>}</c>, or <c>none</c> if <c><anno>Key</anno></c> is not present.</p> </desc> </func> @@ -207,7 +219,7 @@ <name name="next" arity="1"/> <fsummary>Traverse a tree with an iterator</fsummary> <desc> - <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Iter2</anno>}</c> where <c><anno>Key</anno></c> is the + <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, <anno>Iter2</anno>}</c> where <c><anno>Key</anno></c> is the smallest key referred to by the iterator <c><anno>Iter1</anno></c>, and <c><anno>Iter2</anno></c> is the new iterator to be used for traversing the remaining nodes, or the atom <c>none</c> if no @@ -225,8 +237,8 @@ <name name="smallest" arity="1"/> <fsummary>Return smallest key and value</fsummary> <desc> - <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the smallest - key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated + <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>}</c>, where <c><anno>Key</anno></c> is the smallest + key in <c><anno>Tree</anno></c>, and <c><anno>Value</anno></c> is the value associated with this key. Assumes that the tree is nonempty.</p> </desc> </func> @@ -234,8 +246,8 @@ <name name="take_largest" arity="1"/> <fsummary>Extract largest key and value</fsummary> <desc> - <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the - largest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value + <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the + largest key in <c><anno>Tree1</anno></c>, <c><anno>Value</anno></c> is the value associated with this key, and <c><anno>Tree2</anno></c> is this tree with the corresponding node deleted. Assumes that the tree is nonempty.</p> @@ -245,8 +257,8 @@ <name name="take_smallest" arity="1"/> <fsummary>Extract smallest key and value</fsummary> <desc> - <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the - smallest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value + <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the + smallest key in <c><anno>Tree1</anno></c>, <c><anno>Value</anno></c> is the value associated with this key, and <c><anno>Tree2</anno></c> is this tree with the corresponding node deleted. Assumes that the tree is nonempty.</p> @@ -263,7 +275,7 @@ <name name="update" arity="3"/> <fsummary>Update a key to new value in a tree</fsummary> <desc> - <p>Updates <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>; + <p>Updates <c><anno>Key</anno></c> to value <c><anno>Value</anno></c> in <c><anno>Tree1</anno></c>; returns the new tree. Assumes that the key is present in the tree.</p> </desc> diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index bcf063bf0f..9c994154d4 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2013</year> + <year>1996</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -90,9 +90,15 @@ <datatypes> <datatype> - <name><marker id="type-queue">queue()</marker></name> + <name name="queue" n_vars="1"/> <desc><p>As returned by <c>new/0</c>.</p></desc> </datatype> + <datatype> + <name name="queue" n_vars="0"/> + <desc> + <p><c>queue()</c> is equivalent to <c>queue(term())</c>.</p> + </desc> + </datatype> </datatypes> <funcs> diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml index 4b220bdd85..c5b8dce4b7 100644 --- a/lib/stdlib/doc/src/sets.xml +++ b/lib/stdlib/doc/src/sets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2013</year> + <year>2000</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -45,9 +45,15 @@ <datatypes> <datatype> - <name><marker id="type-dict">set()</marker></name> + <name name="set" n_vars="1"/> <desc><p>As returned by <c>new/0</c>.</p></desc> </datatype> + <datatype> + <name name="set" n_vars="0"/> + <desc> + <p><c>set()</c> is equivalent to <c>set(term())</c>.</p> + </desc> + </datatype> </datatypes> <funcs> <func> diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl index 2f69e2b0a4..10d2ccea45 100644 --- a/lib/stdlib/src/array.erl +++ b/lib/stdlib/src/array.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -86,6 +86,8 @@ foldr/3, sparse_foldl/3, sparse_foldr/3, fix/1, relax/1, is_fix/1, resize/1, resize/2]). +-export_type([array/0, array/1]). + %%-define(TEST,1). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). @@ -144,18 +146,28 @@ %%-------------------------------------------------------------------------- +-type element_tuple(T) :: + {T, T, T, T, T, T, T, T, T, T} + | {element_tuple(T), element_tuple(T), element_tuple(T), + element_tuple(T), element_tuple(T), element_tuple(T), + element_tuple(T), element_tuple(T), element_tuple(T), + element_tuple(T), non_neg_integer()}. + +-type elements(T) :: non_neg_integer() + | element_tuple(T) + | nil(). % kill reference, for GC + -record(array, {size :: non_neg_integer(), %% number of defined entries max :: non_neg_integer(), %% maximum number of entries %% in current tree default, %% the default value (usually 'undefined') - elements %% the tuple tree + elements :: elements(_) %% the tuple tree }). -%% A declaration equivalent to the following one is hard-coded in erl_types. -%% That declaration contains hard-coded information about the #array{} -%% structure and the types of its fields. So, please make sure that any -%% changes to its structure are also propagated to erl_types.erl. -%% -%% -opaque array() :: #array{}. + +-opaque array() :: array(term()). + +-opaque array(Type) :: + #array{default :: Type, elements :: elements(Type)}. %% %% Types @@ -164,13 +176,13 @@ -type array_indx() :: non_neg_integer(). -type array_opt() :: {'fixed', boolean()} | 'fixed' - | {'default', Value :: term()} + | {'default', Type :: term()} | {'size', N :: non_neg_integer()} | (N :: non_neg_integer()). -type array_opts() :: array_opt() | [array_opt()]. --type indx_pair() :: {Index :: array_indx(), Value :: term()}. --type indx_pairs() :: [indx_pair()]. +-type indx_pair(Type) :: {Index :: array_indx(), Type}. +-type indx_pairs(Type) :: [indx_pair(Type)]. %%-------------------------------------------------------------------------- @@ -321,7 +333,7 @@ size(_) -> erlang:error(badarg). %% %% @see new/2 --spec default(Array :: array()) -> term(). +-spec default(Array :: array(Type)) -> Value :: Type. default(#array{default = D}) -> D; default(_) -> erlang:error(badarg). @@ -404,7 +416,7 @@ new_test_() -> %% automatically upon insertion; see also {@link set/3}. %% @see relax/1 --spec fix(Array :: array()) -> array(). +-spec fix(Array :: array(Type)) -> array(Type). fix(#array{}=A) -> A#array{max = 0}. @@ -452,7 +464,7 @@ fix_test_() -> %% fix/1}.) %% @see fix/1 --spec relax(Array :: array()) -> array(). +-spec relax(Array :: array(Type)) -> array(Type). relax(#array{size = N}=A) -> A#array{max = find_max(N-1, ?LEAFSIZE)}. @@ -477,7 +489,8 @@ relax_test_() -> %% integer, the call fails with reason `badarg'. If the given array has %% fixed size, the resulting array will also have fixed size. --spec resize(Size :: non_neg_integer(), Array :: array()) -> array(). +-spec resize(Size :: non_neg_integer(), Array :: array(Type)) -> + array(Type). resize(Size, #array{size = N, max = M, elements = E}=A) when is_integer(Size), Size >= 0 -> @@ -508,7 +521,7 @@ resize(_Size, _) -> %% @see resize/2 %% @see sparse_size/1 --spec resize(Array :: array()) -> array(). +-spec resize(Array :: array(Type)) -> array(Type). resize(Array) -> resize(sparse_size(Array), Array). @@ -558,7 +571,7 @@ resize_test_() -> %% @see get/2 %% @see reset/2 --spec set(I :: array_indx(), Value :: term(), Array :: array()) -> array(). +-spec set(I :: array_indx(), Value :: Type, Array :: array(Type)) -> array(Type). set(I, Value, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -621,7 +634,7 @@ expand(I, _S, X, D) -> %% @see set/3 --spec get(I :: array_indx(), Array :: array()) -> term(). +-spec get(I :: array_indx(), Array :: array(Type)) -> Value :: Type. get(I, #array{size = N, max = M, elements = E, default = D}) when is_integer(I), I >= 0 -> @@ -661,7 +674,7 @@ get_1(I, E, _D) -> %% TODO: a reset_range function --spec reset(I :: array_indx(), Array :: array()) -> array(). +-spec reset(I :: array_indx(), Array :: array(Type)) -> array(Type). reset(I, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -747,7 +760,7 @@ set_get_test_() -> %% @see from_list/2 %% @see sparse_to_list/1 --spec to_list(Array :: array()) -> list(). +-spec to_list(Array :: array(Type)) -> list(Value :: Type). to_list(#array{size = 0}) -> []; @@ -820,7 +833,7 @@ to_list_test_() -> %% %% @see to_list/1 --spec sparse_to_list(Array :: array()) -> list(). +-spec sparse_to_list(Array :: array(Type)) -> list(Value :: Type). sparse_to_list(#array{size = 0}) -> []; @@ -887,7 +900,7 @@ sparse_to_list_test_() -> %% @equiv from_list(List, undefined) --spec from_list(List :: list()) -> array(). +-spec from_list(List :: list(Value :: Type)) -> array(Type). from_list(List) -> from_list(List, undefined). @@ -899,7 +912,7 @@ from_list(List) -> %% @see new/2 %% @see to_list/1 --spec from_list(List :: list(), Default :: term()) -> array(). +-spec from_list(List :: list(Value :: Type), Default :: term()) -> array(Type). from_list([], Default) -> new({default,Default}); @@ -998,7 +1011,7 @@ from_list_test_() -> %% @see from_orddict/2 %% @see sparse_to_orddict/1 --spec to_orddict(Array :: array()) -> indx_pairs(). +-spec to_orddict(Array :: array(Type)) -> indx_pairs(Value :: Type). to_orddict(#array{size = 0}) -> []; @@ -1035,16 +1048,16 @@ to_orddict_3(N, R, D, L, E, S) -> to_orddict_2(element(N, E), R, D, L), E, S). --spec push_pairs(non_neg_integer(), array_indx(), term(), indx_pairs()) -> - indx_pairs(). +-spec push_pairs(non_neg_integer(), array_indx(), term(), indx_pairs(Type)) -> + indx_pairs(Type). push_pairs(0, _I, _E, L) -> L; push_pairs(N, I, E, L) -> push_pairs(N-1, I-1, E, [{I, E} | L]). --spec push_tuple_pairs(non_neg_integer(), array_indx(), term(), indx_pairs()) -> - indx_pairs(). +-spec push_tuple_pairs(non_neg_integer(), array_indx(), term(), indx_pairs(Type)) -> + indx_pairs(Type). push_tuple_pairs(0, _I, _T, L) -> L; @@ -1090,7 +1103,7 @@ to_orddict_test_() -> %% %% @see to_orddict/1 --spec sparse_to_orddict(Array :: array()) -> indx_pairs(). +-spec sparse_to_orddict(Array :: array(Type)) -> indx_pairs(Value :: Type). sparse_to_orddict(#array{size = 0}) -> []; @@ -1128,7 +1141,7 @@ sparse_to_orddict_3(N, R, D, L, E, S) -> E, S). -spec sparse_push_tuple_pairs(non_neg_integer(), array_indx(), - _, _, indx_pairs()) -> indx_pairs(). + _, _, indx_pairs(Type)) -> indx_pairs(Type). sparse_push_tuple_pairs(0, _I, _D, _T, L) -> L; @@ -1170,7 +1183,7 @@ sparse_to_orddict_test_() -> %% @equiv from_orddict(Orddict, undefined) --spec from_orddict(Orddict :: indx_pairs()) -> array(). +-spec from_orddict(Orddict :: indx_pairs(Value :: Type)) -> array(Type). from_orddict(Orddict) -> from_orddict(Orddict, undefined). @@ -1184,7 +1197,8 @@ from_orddict(Orddict) -> %% @see new/2 %% @see to_orddict/1 --spec from_orddict(Orddict :: indx_pairs(), Default :: term()) -> array(). +-spec from_orddict(Orddict :: indx_pairs(Value :: Type), Default :: Type) -> + array(Type). from_orddict([], Default) -> new({default,Default}); @@ -1379,8 +1393,8 @@ from_orddict_test_() -> %% @see foldr/3 %% @see sparse_map/2 --spec map(Function, Array :: array()) -> array() when - Function :: fun((Index :: array_indx(), Value :: _) -> _). +-spec map(Function, Array :: array(Type1)) -> array(Type2) when + Function :: fun((Index :: array_indx(), Type1) -> Type2). map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1471,8 +1485,8 @@ map_test_() -> %% %% @see map/2 --spec sparse_map(Function, Array :: array()) -> array() when - Function :: fun((Index :: array_indx(), Value :: _) -> _). +-spec sparse_map(Function, Array :: array(Type1)) -> array(Type2) when + Function :: fun((Index :: array_indx(), Type1) -> Type2). sparse_map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1567,8 +1581,8 @@ sparse_map_test_() -> %% @see map/2 %% @see sparse_foldl/3 --spec foldl(Function, InitialAcc :: A, Array :: array()) -> B when - Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). +-spec foldl(Function, InitialAcc :: A, Array :: array(Type)) -> B when + Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B). foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1640,8 +1654,8 @@ foldl_test_() -> %% @see foldl/3 %% @see sparse_foldr/3 --spec sparse_foldl(Function, InitialAcc :: A, Array :: array()) -> B when - Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). +-spec sparse_foldl(Function, InitialAcc :: A, Array :: array(Type)) -> B when + Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B). sparse_foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1717,8 +1731,8 @@ sparse_foldl_test_() -> %% @see foldl/3 %% @see map/2 --spec foldr(Function, InitialAcc :: A, Array :: array()) -> B when - Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). +-spec foldr(Function, InitialAcc :: A, Array :: array(Type)) -> B when + Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B). foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1796,8 +1810,8 @@ foldr_test_() -> %% @see foldr/3 %% @see sparse_foldl/3 --spec sparse_foldr(Function, InitialAcc :: A, Array :: array()) -> B when - Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). +-spec sparse_foldr(Function, InitialAcc :: A, Array :: array(Type)) -> B when + Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B). sparse_foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl index 7e198a2469..6088e1a2dd 100644 --- a/lib/stdlib/src/dict.erl +++ b/lib/stdlib/src/dict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -41,6 +41,8 @@ -export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]). -export([fold/3,map/2,filter/2,merge/3]). +-export_type([dict/0, dict/2]). + %% Low-level interface. %%-export([get_slot/2,get_bucket/2,on_bucket/3,fold_dict/3, %% maybe_expand/2,maybe_contract/2]). @@ -53,6 +55,9 @@ -define(exp_size, (?seg_size * ?expand_load)). -define(con_size, (?seg_size * ?contract_load)). +-type segs(K, V) :: tuple() + | {K, V}. % dummy + %% Define a hashtable. The default values are the standard ones. -record(dict, {size=0 :: non_neg_integer(), % Number of elements @@ -62,14 +67,13 @@ exp_size=?exp_size :: non_neg_integer(), % Size to expand at con_size=?con_size :: non_neg_integer(), % Size to contract at empty :: tuple(), % Empty segment - segs :: tuple() % Segments + segs :: segs(_, _) % Segments }). -%% A declaration equivalent to the following one is hard-coded in erl_types. -%% That declaration contains hard-coded information about the #dict{} -%% structure and the types of its fields. So, please make sure that any -%% changes to its structure are also propagated to erl_types.erl. -%% -%% -opaque dict() :: #dict{}. + + +-opaque dict() :: dict(_, _). + +-opaque dict(Key, Value) :: #dict{segs :: segs(Key, Value)}. -define(kv(K,V), [K|V]). % Key-Value pair format %%-define(kv(K,V), {K,V}). % Key-Value pair format @@ -81,8 +85,7 @@ new() -> #dict{empty=Empty,segs={Empty}}. -spec is_key(Key, Dict) -> boolean() when - Key :: term(), - Dict :: dict(). + Dict :: dict(Key, Value :: term()). is_key(Key, D) -> Slot = get_slot(D, Key), @@ -94,15 +97,15 @@ find_key(K, [_|Bkt]) -> find_key(K, Bkt); find_key(_, []) -> false. -spec to_list(Dict) -> List when - Dict :: dict(), - List :: [{Key :: term(), Value :: term()}]. + Dict :: dict(Key, Value), + List :: [{Key, Value}]. to_list(D) -> fold(fun (Key, Val, List) -> [{Key,Val}|List] end, [], D). -spec from_list(List) -> Dict when - List :: [{Key :: term(), Value :: term()}], - Dict :: dict(). + Dict :: dict(Key, Value), + List :: [{Key, Value}]. from_list(L) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, new(), L). @@ -118,9 +121,7 @@ size(#dict{size=N}) when is_integer(N), N >= 0 -> N. is_empty(#dict{size=N}) -> N =:= 0. -spec fetch(Key, Dict) -> Value when - Key :: term(), - Dict :: dict(), - Value :: term(). + Dict :: dict(Key, Value). fetch(Key, D) -> Slot = get_slot(D, Key), @@ -135,9 +136,7 @@ fetch_val(K, [_|Bkt]) -> fetch_val(K, Bkt); fetch_val(_, []) -> throw(badarg). -spec find(Key, Dict) -> {'ok', Value} | 'error' when - Key :: term(), - Dict :: dict(), - Value :: term(). + Dict :: dict(Key, Value). find(Key, D) -> Slot = get_slot(D, Key), @@ -149,16 +148,16 @@ find_val(K, [_|Bkt]) -> find_val(K, Bkt); find_val(_, []) -> error. -spec fetch_keys(Dict) -> Keys when - Dict :: dict(), - Keys :: [term()]. + Dict :: dict(Key, Value :: term()), + Keys :: [Key]. fetch_keys(D) -> fold(fun (Key, _Val, Keys) -> [Key|Keys] end, [], D). -spec erase(Key, Dict1) -> Dict2 when - Key :: term(), - Dict1 :: dict(), - Dict2 :: dict(). + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value). + %% Erase all elements with key Key. erase(Key, D0) -> @@ -174,10 +173,8 @@ erase_key(Key, [E|Bkt0]) -> erase_key(_, []) -> {[],0}. -spec store(Key, Value, Dict1) -> Dict2 when - Key :: term(), - Value :: term(), - Dict1 :: dict(), - Dict2 :: dict(). + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value). store(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -194,10 +191,8 @@ store_bkt_val(Key, New, [Other|Bkt0]) -> store_bkt_val(Key, New, []) -> {[?kv(Key,New)],1}. -spec append(Key, Value, Dict1) -> Dict2 when - Key :: term(), - Value :: term(), - Dict1 :: dict(), - Dict2 :: dict(). + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value). append(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -214,10 +209,9 @@ append_bkt(Key, Val, [Other|Bkt0]) -> append_bkt(Key, Val, []) -> {[?kv(Key,[Val])],1}. -spec append_list(Key, ValList, Dict1) -> Dict2 when - Key :: term(), - ValList :: [Value :: term()], - Dict1 :: dict(), - Dict2 :: dict(). + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value), + ValList :: [Value]. append_list(Key, L, D0) -> Slot = get_slot(D0, Key), @@ -288,10 +282,9 @@ app_list_bkt(Key, L, []) -> {[?kv(Key,L)],1}. %% {[Other|Bkt1],Dc}. -spec update(Key, Fun, Dict1) -> Dict2 when - Key :: term(), - Fun :: fun((Value1 :: term()) -> Value2 :: term()), - Dict1 :: dict(), - Dict2 :: dict(). + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value), + Fun :: fun((Value1 :: Value) -> Value2 :: Value). update(Key, F, D0) -> Slot = get_slot(D0, Key), @@ -311,11 +304,10 @@ update_bkt(_Key, _F, []) -> throw(badarg). -spec update(Key, Fun, Initial, Dict1) -> Dict2 when - Key :: term(), - Initial :: term(), - Fun :: fun((Value1 :: term()) -> Value2 :: term()), - Dict1 :: dict(), - Dict2 :: dict(). + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value), + Fun :: fun((Value1 :: Value) -> Value2 :: Value), + Initial :: Value. update(Key, F, Init, D0) -> Slot = get_slot(D0, Key), @@ -331,10 +323,9 @@ update_bkt(Key, F, I, [Other|Bkt0]) -> update_bkt(Key, F, I, []) when is_function(F, 1) -> {[?kv(Key,I)],1}. -spec update_counter(Key, Increment, Dict1) -> Dict2 when - Key :: term(), - Increment :: number(), - Dict1 :: dict(), - Dict2 :: dict(). + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value), + Increment :: number(). update_counter(Key, Incr, D0) when is_number(Incr) -> Slot = get_slot(D0, Key), @@ -351,36 +342,35 @@ counter_bkt(Key, I, []) -> {[?kv(Key,I)],1}. -spec fold(Fun, Acc0, Dict) -> Acc1 when Fun :: fun((Key, Value, AccIn) -> AccOut), - Key :: term(), - Value :: term(), - Acc0 :: term(), - Acc1 :: term(), - AccIn :: term(), - AccOut :: term(), - Dict :: dict(). + Dict :: dict(Key, Value), + Acc0 :: Acc, + Acc1 :: Acc, + AccIn :: Acc, + AccOut :: Acc. + %% Fold function Fun over all "bags" in Table and return Accumulator. fold(F, Acc, D) -> fold_dict(F, Acc, D). -spec map(Fun, Dict1) -> Dict2 when - Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), - Dict1 :: dict(), - Dict2 :: dict(). + Fun :: fun((Key, Value1) -> Value2), + Dict1 :: dict(Key, Value1), + Dict2 :: dict(Key, Value2). map(F, D) -> map_dict(F, D). -spec filter(Pred, Dict1) -> Dict2 when - Pred :: fun((Key :: term(), Value :: term()) -> boolean()), - Dict1 :: dict(), - Dict2 :: dict(). + Pred :: fun((Key , Value) -> boolean()), + Dict1 :: dict(Key, Value), + Dict2 :: dict(Key, Value). filter(F, D) -> filter_dict(F, D). -spec merge(Fun, Dict1, Dict2) -> Dict3 when - Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), - Dict1 :: dict(), - Dict2 :: dict(), - Dict3 :: dict(). + Fun :: fun((Key, Value1, Value2) -> Value), + Dict1 :: dict(Key, Value1), + Dict2 :: dict(Key, Value2), + Dict3 :: dict(Key, Value). merge(F, D1, D2) when D1#dict.size < D2#dict.size -> fold_dict(fun (K, V1, D) -> diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl index 78f74631dc..0c21271529 100644 --- a/lib/stdlib/src/digraph.erl +++ b/lib/stdlib/src/digraph.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,18 +36,14 @@ -export([get_short_path/3, get_short_cycle/2]). --export_type([digraph/0, d_type/0, vertex/0, edge/0]). +-export_type([graph/0, d_type/0, vertex/0, edge/0]). -record(digraph, {vtab = notable :: ets:tab(), etab = notable :: ets:tab(), ntab = notable :: ets:tab(), cyclic = true :: boolean()}). -%% A declaration equivalent to the following one is hard-coded in erl_types. -%% That declaration contains hard-coded information about the #digraph{} -%% record and the types of its fields. So, please make sure that any -%% changes to its structure are also propagated to erl_types.erl. -%% -%% -opaque digraph() :: #digraph{}. + +-opaque graph() :: #digraph{}. -type edge() :: term(). -type label() :: term(). @@ -67,11 +63,11 @@ -type d_cyclicity() :: 'acyclic' | 'cyclic'. -type d_type() :: d_cyclicity() | d_protection(). --spec new() -> digraph(). +-spec new() -> graph(). new() -> new([]). --spec new(Type) -> digraph() when +-spec new(Type) -> graph() when Type :: [d_type()]. new(Type) -> @@ -106,7 +102,7 @@ check_type(_, _, _) -> error. %% %% Set graph type %% --spec set_type([{'cyclic', boolean()}], digraph()) -> digraph(). +-spec set_type([{'cyclic', boolean()}], graph()) -> graph(). set_type([{cyclic,V} | Ks], G) -> set_type(Ks, G#digraph{cyclic = V}); @@ -116,7 +112,7 @@ set_type([], G) -> G. %% Data access functions -spec delete(G) -> 'true' when - G :: digraph(). + G :: graph(). delete(G) -> ets:delete(G#digraph.vtab), @@ -124,7 +120,7 @@ delete(G) -> ets:delete(G#digraph.ntab). -spec info(G) -> InfoList when - G :: digraph(), + G :: graph(), InfoList :: [{'cyclicity', Cyclicity :: d_cyclicity()} | {'memory', NoWords :: non_neg_integer()} | {'protection', Protection :: d_protection()}]. @@ -142,20 +138,20 @@ info(G) -> [{cyclicity, Cyclicity}, {memory, Memory}, {protection, Protection}]. -spec add_vertex(G) -> vertex() when - G :: digraph(). + G :: graph(). add_vertex(G) -> do_add_vertex({new_vertex_id(G), []}, G). -spec add_vertex(G, V) -> vertex() when - G :: digraph(), + G :: graph(), V :: vertex(). add_vertex(G, V) -> do_add_vertex({V, []}, G). -spec add_vertex(G, V, Label) -> vertex() when - G :: digraph(), + G :: graph(), V :: vertex(), Label :: label(). @@ -163,21 +159,21 @@ add_vertex(G, V, D) -> do_add_vertex({V, D}, G). -spec del_vertex(G, V) -> 'true' when - G :: digraph(), + G :: graph(), V :: vertex(). del_vertex(G, V) -> do_del_vertex(V, G). -spec del_vertices(G, Vertices) -> 'true' when - G :: digraph(), + G :: graph(), Vertices :: [vertex()]. del_vertices(G, Vs) -> do_del_vertices(Vs, G). -spec vertex(G, V) -> {V, Label} | 'false' when - G :: digraph(), + G :: graph(), V :: vertex(), Label :: label(). @@ -188,37 +184,37 @@ vertex(G, V) -> end. -spec no_vertices(G) -> non_neg_integer() when - G :: digraph(). + G :: graph(). no_vertices(G) -> ets:info(G#digraph.vtab, size). -spec vertices(G) -> Vertices when - G :: digraph(), + G :: graph(), Vertices :: [vertex()]. vertices(G) -> ets:select(G#digraph.vtab, [{{'$1', '_'}, [], ['$1']}]). --spec source_vertices(digraph()) -> [vertex()]. +-spec source_vertices(graph()) -> [vertex()]. source_vertices(G) -> collect_vertices(G, in). --spec sink_vertices(digraph()) -> [vertex()]. +-spec sink_vertices(graph()) -> [vertex()]. sink_vertices(G) -> collect_vertices(G, out). -spec in_degree(G, V) -> non_neg_integer() when - G :: digraph(), + G :: graph(), V :: vertex(). in_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {in, V})). -spec in_neighbours(G, V) -> Vertex when - G :: digraph(), + G :: graph(), V :: vertex(), Vertex :: [vertex()]. @@ -228,7 +224,7 @@ in_neighbours(G, V) -> collect_elems(ets:lookup(NT, {in, V}), ET, 2). -spec in_edges(G, V) -> Edges when - G :: digraph(), + G :: graph(), V :: vertex(), Edges :: [edge()]. @@ -236,14 +232,14 @@ in_edges(G, V) -> ets:select(G#digraph.ntab, [{{{in, V}, '$1'}, [], ['$1']}]). -spec out_degree(G, V) -> non_neg_integer() when - G :: digraph(), + G :: graph(), V :: vertex(). out_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {out, V})). -spec out_neighbours(G, V) -> Vertices when - G :: digraph(), + G :: graph(), V :: vertex(), Vertices :: [vertex()]. @@ -253,7 +249,7 @@ out_neighbours(G, V) -> collect_elems(ets:lookup(NT, {out, V}), ET, 3). -spec out_edges(G, V) -> Edges when - G :: digraph(), + G :: graph(), V :: vertex(), Edges :: [edge()]. @@ -261,7 +257,7 @@ out_edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V}, '$1'}, [], ['$1']}]). -spec add_edge(G, V1, V2) -> edge() | {'error', add_edge_err_rsn()} when - G :: digraph(), + G :: graph(), V1 :: vertex(), V2 :: vertex(). @@ -269,7 +265,7 @@ add_edge(G, V1, V2) -> do_add_edge({new_edge_id(G), V1, V2, []}, G). -spec add_edge(G, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when - G :: digraph(), + G :: graph(), V1 :: vertex(), V2 :: vertex(), Label :: label(). @@ -278,7 +274,7 @@ add_edge(G, V1, V2, D) -> do_add_edge({new_edge_id(G), V1, V2, D}, G). -spec add_edge(G, E, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when - G :: digraph(), + G :: graph(), E :: edge(), V1 :: vertex(), V2 :: vertex(), @@ -288,34 +284,34 @@ add_edge(G, E, V1, V2, D) -> do_add_edge({E, V1, V2, D}, G). -spec del_edge(G, E) -> 'true' when - G :: digraph(), + G :: graph(), E :: edge(). del_edge(G, E) -> do_del_edges([E], G). -spec del_edges(G, Edges) -> 'true' when - G :: digraph(), + G :: graph(), Edges :: [edge()]. del_edges(G, Es) -> do_del_edges(Es, G). -spec no_edges(G) -> non_neg_integer() when - G :: digraph(). + G :: graph(). no_edges(G) -> ets:info(G#digraph.etab, size). -spec edges(G) -> Edges when - G :: digraph(), + G :: graph(), Edges :: [edge()]. edges(G) -> ets:select(G#digraph.etab, [{{'$1', '_', '_', '_'}, [], ['$1']}]). -spec edges(G, V) -> Edges when - G :: digraph(), + G :: graph(), V :: vertex(), Edges :: [edge()]. @@ -324,7 +320,7 @@ edges(G, V) -> {{{in, V}, '$1'}, [], ['$1']}]). -spec edge(G, E) -> {E, V1, V2, Label} | 'false' when - G :: digraph(), + G :: graph(), E :: edge(), V1 :: vertex(), V2 :: vertex(), @@ -339,7 +335,7 @@ edge(G, E) -> %% %% Generate a "unique" edge identifier (relative to this graph) %% --spec new_edge_id(digraph()) -> edge(). +-spec new_edge_id(graph()) -> edge(). new_edge_id(G) -> NT = G#digraph.ntab, @@ -351,7 +347,7 @@ new_edge_id(G) -> %% %% Generate a "unique" vertex identifier (relative to this graph) %% --spec new_vertex_id(digraph()) -> vertex(). +-spec new_vertex_id(graph()) -> vertex(). new_vertex_id(G) -> NT = G#digraph.ntab, @@ -371,7 +367,7 @@ collect_elems([{_,Key}|Keys], Table, Index, Acc) -> [ets:lookup_element(Table, Key, Index)|Acc]); collect_elems([], _, _, Acc) -> Acc. --spec do_add_vertex({vertex(), label()}, digraph()) -> vertex(). +-spec do_add_vertex({vertex(), label()}, graph()) -> vertex(). do_add_vertex({V, _Label} = VL, G) -> ets:insert(G#digraph.vtab, VL), @@ -430,14 +426,14 @@ do_del_edge(E, V1, V2, G) -> {{{out,V1}, E}, [], [true]}]), ets:delete(G#digraph.etab, E). --spec rm_edges([vertex(),...], digraph()) -> 'true'. +-spec rm_edges([vertex(),...], graph()) -> 'true'. rm_edges([V1, V2|Vs], G) -> rm_edge(V1, V2, G), rm_edges([V2|Vs], G); rm_edges(_, _) -> true. --spec rm_edge(vertex(), vertex(), digraph()) -> 'ok'. +-spec rm_edge(vertex(), vertex(), graph()) -> 'ok'. rm_edge(V1, V2, G) -> Es = out_edges(G, V1), @@ -456,7 +452,7 @@ rm_edge_0([], _, _, #digraph{}) -> ok. %% %% Check that endpoints exist %% --spec do_add_edge({edge(), vertex(), vertex(), label()}, digraph()) -> +-spec do_add_edge({edge(), vertex(), vertex(), label()}, graph()) -> edge() | {'error', add_edge_err_rsn()}. do_add_edge({E, V1, V2, Label}, G) -> @@ -484,14 +480,14 @@ other_edge_exists(#digraph{etab = ET}, E, V1, V2) -> false end. --spec do_insert_edge(edge(), vertex(), vertex(), label(), digraph()) -> edge(). +-spec do_insert_edge(edge(), vertex(), vertex(), label(), graph()) -> edge(). do_insert_edge(E, V1, V2, Label, #digraph{ntab=NT, etab=ET}) -> ets:insert(NT, [{{out, V1}, E}, {{in, V2}, E}]), ets:insert(ET, {E, V1, V2, Label}), E. --spec acyclic_add_edge(edge(), vertex(), vertex(), label(), digraph()) -> +-spec acyclic_add_edge(edge(), vertex(), vertex(), label(), graph()) -> edge() | {'error', {'bad_edge', [vertex()]}}. acyclic_add_edge(_E, V1, V2, _L, _G) when V1 =:= V2 -> @@ -507,7 +503,7 @@ acyclic_add_edge(E, V1, V2, Label, G) -> %% -spec del_path(G, V1, V2) -> 'true' when - G :: digraph(), + G :: graph(), V1 :: vertex(), V2 :: vertex(). @@ -529,7 +525,7 @@ del_path(G, V1, V2) -> %% -spec get_cycle(G, V) -> Vertices | 'false' when - G :: digraph(), + G :: graph(), V :: vertex(), Vertices :: [vertex(),...]. @@ -550,7 +546,7 @@ get_cycle(G, V) -> %% -spec get_path(G, V1, V2) -> Vertices | 'false' when - G :: digraph(), + G :: graph(), V1 :: vertex(), V2 :: vertex(), Vertices :: [vertex(),...]. @@ -589,7 +585,7 @@ one_path([], _, [], _, _, _, _, _Counter) -> false. %% -spec get_short_cycle(G, V) -> Vertices | 'false' when - G :: digraph(), + G :: graph(), V :: vertex(), Vertices :: [vertex(),...]. @@ -602,7 +598,7 @@ get_short_cycle(G, V) -> %% -spec get_short_path(G, V1, V2) -> Vertices | 'false' when - G :: digraph(), + G :: graph(), V1 :: vertex(), V2 :: vertex(), Vertices :: [vertex(),...]. diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl index 0e248df453..011bcd0260 100644 --- a/lib/stdlib/src/digraph_utils.erl +++ b/lib/stdlib/src/digraph_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -43,28 +43,28 @@ %% -spec components(Digraph) -> [Component] when - Digraph :: digraph(), + Digraph :: digraph:graph(), Component :: [digraph:vertex()]. components(G) -> forest(G, fun inout/3). -spec strong_components(Digraph) -> [StrongComponent] when - Digraph :: digraph(), + Digraph :: digraph:graph(), StrongComponent :: [digraph:vertex()]. strong_components(G) -> forest(G, fun in/3, revpostorder(G)). -spec cyclic_strong_components(Digraph) -> [StrongComponent] when - Digraph :: digraph(), + Digraph :: digraph:graph(), StrongComponent :: [digraph:vertex()]. cyclic_strong_components(G) -> remove_singletons(strong_components(G), G, []). -spec reachable(Vertices, Digraph) -> Reachable when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()], Reachable :: [digraph:vertex()]. @@ -72,7 +72,7 @@ reachable(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, first)). -spec reachable_neighbours(Vertices, Digraph) -> Reachable when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()], Reachable :: [digraph:vertex()]. @@ -80,7 +80,7 @@ reachable_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, not_first)). -spec reaching(Vertices, Digraph) -> Reaching when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()], Reaching :: [digraph:vertex()]. @@ -88,7 +88,7 @@ reaching(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, first)). -spec reaching_neighbours(Vertices, Digraph) -> Reaching when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()], Reaching :: [digraph:vertex()]. @@ -96,7 +96,7 @@ reaching_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, not_first)). -spec topsort(Digraph) -> Vertices | 'false' when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()]. topsort(G) -> @@ -107,13 +107,13 @@ topsort(G) -> end. -spec is_acyclic(Digraph) -> boolean() when - Digraph :: digraph(). + Digraph :: digraph:graph(). is_acyclic(G) -> loop_vertices(G) =:= [] andalso topsort(G) =/= false. -spec arborescence_root(Digraph) -> 'no' | {'yes', Root} when - Digraph :: digraph(), + Digraph :: digraph:graph(), Root :: digraph:vertex(). arborescence_root(G) -> @@ -136,29 +136,29 @@ arborescence_root(G) -> end. -spec is_arborescence(Digraph) -> boolean() when - Digraph :: digraph(). + Digraph :: digraph:graph(). is_arborescence(G) -> arborescence_root(G) =/= no. -spec is_tree(Digraph) -> boolean() when - Digraph :: digraph(). + Digraph :: digraph:graph(). is_tree(G) -> (digraph:no_edges(G) =:= digraph:no_vertices(G) - 1) andalso (length(components(G)) =:= 1). -spec loop_vertices(Digraph) -> Vertices when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()]. loop_vertices(G) -> [V || V <- digraph:vertices(G), is_reflexive_vertex(V, G)]. -spec subgraph(Digraph, Vertices) -> SubGraph when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()], - SubGraph :: digraph(). + SubGraph :: digraph:graph(). subgraph(G, Vs) -> try @@ -169,8 +169,8 @@ subgraph(G, Vs) -> end. -spec subgraph(Digraph, Vertices, Options) -> SubGraph when - Digraph :: digraph(), - SubGraph :: digraph(), + Digraph :: digraph:graph(), + SubGraph :: digraph:graph(), Vertices :: [digraph:vertex()], Options :: [{'type', SubgraphType} | {'keep_labels', boolean()}], SubgraphType :: 'inherit' | [digraph:d_type()]. @@ -184,8 +184,8 @@ subgraph(G, Vs, Opts) -> end. -spec condensation(Digraph) -> CondensedDigraph when - Digraph :: digraph(), - CondensedDigraph :: digraph(). + Digraph :: digraph:graph(), + CondensedDigraph :: digraph:graph(). condensation(G) -> SCs = strong_components(G), @@ -209,14 +209,14 @@ condensation(G) -> SCG. -spec preorder(Digraph) -> Vertices when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()]. preorder(G) -> lists:reverse(revpreorder(G)). -spec postorder(Digraph) -> Vertices when - Digraph :: digraph(), + Digraph :: digraph:graph(), Vertices :: [digraph:vertex()]. postorder(G) -> diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl index 0033010dce..a2b4663219 100644 --- a/lib/stdlib/src/edlin_expand.erl +++ b/lib/stdlib/src/edlin_expand.erl @@ -73,7 +73,7 @@ to_atom(Str) -> error end. -match(Prefix, Alts, Extra) -> +match(Prefix, Alts, Extra0) -> Len = length(Prefix), Matches = lists:sort( [{S, A} || {H, A} <- Alts, @@ -89,13 +89,11 @@ match(Prefix, Alts, Extra) -> {yes, Remain, []} end; {complete, Str} -> - {_, Arity} = lists:keyfind(Str, 1, Matches), - case {Arity, Extra} of - {0, "("} -> - {yes, nthtail(Len, Str) ++ "()", []}; - _ -> - {yes, nthtail(Len, Str) ++ Extra, []} - end; + Extra = case {Extra0,Matches} of + {"(",[{Str,0}]} -> "()"; + {_,_} -> Extra0 + end, + {yes, nthtail(Len, Str) ++ Extra, []}; no -> {no, [], []} end. diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 4fd302e612..68e079b7e5 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -46,8 +46,8 @@ istk=[], %Ifdef stack sstk=[], %State stack path=[], %Include-path - macs = dict:new() :: dict(), %Macros (don't care locations) - uses = dict:new() :: dict(), %Macro use structure + macs = dict:new() :: dict:dict(),%Macros (don't care locations) + uses = dict:new() :: dict:dict(),%Macro use structure pre_opened = false :: boolean() }). @@ -640,7 +640,7 @@ leave_file(From, St) -> Ms = dict:store({atom,'FILE'}, {none,[{string,CurrLoc,OldName2}]}, St#epp.macs), - NextSt = OldSt#epp{sstk=Sts,macs=Ms}, + NextSt = OldSt#epp{sstk=Sts,macs=Ms,uses=St#epp.uses}, enter_file_reply(From, OldName, CurrLoc, CurrLoc), case OldName2 =:= OldName of true -> diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 0c6f41f594..9f5be2da37 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -85,8 +85,8 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> -record(usage, { calls = dict:new(), %Who calls who imported = [], %Actually imported functions - used_records=sets:new() :: set(), %Used record definitions - used_types = dict:new() :: dict() %Used type definitions + used_records=sets:new() :: sets:set(),%Used record definitions + used_types = dict:new() :: dict:dict()%Used type definitions }). %% Define the lint state record. @@ -95,13 +95,13 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> -record(lint, {state=start :: 'start' | 'attribute' | 'function', module=[], %Module behaviour=[], %Behaviour - exports=gb_sets:empty() :: gb_set(), %Exports + exports=gb_sets:empty() :: gb_sets:set(),%Exports imports=[], %Imports compile=[], %Compile flags - records=dict:new() :: dict(), %Record definitions - locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned) - no_auto=gb_sets:empty() :: gb_set() | 'all', %Functions explicitly not autoimported - defined=gb_sets:empty() :: gb_set(), %Defined fuctions + records=dict:new() :: dict:dict(), %Record definitions + locals=gb_sets:empty() :: gb_sets:set(),%All defined functions (prescanned) + no_auto=gb_sets:empty() :: gb_sets:set() | 'all',%Functions explicitly not autoimported + defined=gb_sets:empty() :: gb_sets:set(),%Defined fuctions on_load=[] :: [fa()], %On-load function on_load_line=0 :: line(), %Line for on_load clashes=[], %Exported functions named as BIFs @@ -118,10 +118,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> new = false :: boolean(), %Has user-defined 'new/N' called= [] :: [{fa(),line()}], %Called functions usage = #usage{} :: #usage{}, - specs = dict:new() :: dict(), %Type specifications - callbacks = dict:new() :: dict(), %Callback types - types = dict:new() :: dict(), %Type definitions - exp_types=gb_sets:empty():: gb_set() %Exported types + specs = dict:new() :: dict:dict(), %Type specifications + callbacks = dict:new() :: dict:dict(), %Callback types + types = dict:new() :: dict:dict(), %Type definitions + exp_types=gb_sets:empty():: gb_sets:set()%Exported types }). -type lint_state() :: #lint{}. @@ -344,9 +344,10 @@ format_error(spec_wrong_arity) -> "spec has the wrong arity"; format_error(callback_wrong_arity) -> "callback has the wrong arity"; -format_error({imported_predefined_type, Name}) -> - io_lib:format("referring to built-in type ~w as a remote type; " - "please take out the module name", [Name]); +format_error({deprecated_type, {Name, Arity}, {Mod, NewName}, Rel}) -> + io_lib:format("type ~w/~w is deprecated and will be " + "removed in ~s; use ~w:~w/~w", + [Name, Arity, Rel, Mod, NewName, Arity]); format_error({not_exported_opaque, {TypeName, Arity}}) -> io_lib:format("opaque type ~w~s is not exported", [TypeName, gen_type_paren(Arity)]); @@ -1155,7 +1156,7 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) -> add_error(Line, {bad_export_type, ETs}, St0) end. --spec exports(lint_state()) -> gb_set(). +-spec exports(lint_state()) -> gb_sets:set(). exports(#lint{compile = Opts, defined = Defs, exports = Es}) -> case lists:member(export_all, Opts) of @@ -1888,7 +1889,7 @@ is_guard_test(Expression, Forms) -> end, start(), RecordAttributes), is_guard_test2(zip_file_and_line(Expression, "nofile"), St0#lint.records). -%% is_guard_test2(Expression, RecordDefs :: dict()) -> boolean(). +%% is_guard_test2(Expression, RecordDefs :: dict:dict()) -> boolean(). is_guard_test2({call,Line,{atom,Lr,record},[E,A]}, RDs) -> is_gexpr({call,Line,{atom,Lr,is_record},[E,A]}, RDs); is_guard_test2({call,_Line,{atom,_La,Test},As}=Call, RDs) -> @@ -2566,18 +2567,12 @@ check_type({paren_type, _L, [Type]}, SeenVars, St) -> check_type(Type, SeenVars, St); check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]}, SeenVars, #lint{module=CurrentMod} = St) -> - St1 = - case is_default_type({Name, length(Args)}) - orelse is_var_arity_type(Name) of - true -> add_error(L, {imported_predefined_type, Name}, St); - false -> St - end, case Mod =:= CurrentMod of - true -> check_type({type, L, Name, Args}, SeenVars, St1); + true -> check_type({type, L, Name, Args}, SeenVars, St); false -> lists:foldl(fun(T, {AccSeenVars, AccSt}) -> check_type(T, AccSeenVars, AccSt) - end, {SeenVars, St1}, Args) + end, {SeenVars, St}, Args) end; check_type({integer, _L, _}, SeenVars, St) -> {SeenVars, St}; check_type({atom, _L, _}, SeenVars, St) -> {SeenVars, St}; @@ -2635,14 +2630,33 @@ check_type({type, _L, product, Args}, SeenVars, St) -> lists:foldl(fun(T, {AccSeenVars, AccSt}) -> check_type(T, AccSeenVars, AccSt) end, {SeenVars, St}, Args); -check_type({type, La, TypeName, Args}, SeenVars, #lint{usage=Usage} = St) -> +check_type({type, La, TypeName, Args}, SeenVars, St) -> + #lint{usage=Usage, module = Module, types=Types} = St, Arity = length(Args), + TypePair = {TypeName, Arity}, St1 = case is_var_arity_type(TypeName) of true -> St; false -> - OldUsed = Usage#usage.used_types, - UsedTypes = dict:store({TypeName, Arity}, La, OldUsed), - St#lint{usage=Usage#usage{used_types=UsedTypes}} + Obsolete = obsolete_type(TypePair), + IsObsolete = + case Obsolete of + {deprecated, {M, _}, _} when M =/= Module -> + case dict:find(TypePair, Types) of + {ok, _} -> false; + error -> true + end; + _ -> false + end, + case IsObsolete of + true -> + {deprecated, Replacement, Rel} = Obsolete, + W = {deprecated_type, TypePair, Replacement, Rel}, + add_warning(La, W, St); + false -> + OldUsed = Usage#usage.used_types, + UsedTypes = dict:store(TypePair, La, OldUsed), + St#lint{usage=Usage#usage{used_types=UsedTypes}} + end end, check_type({type, -1, product, Args}, SeenVars, St1); check_type(I, SeenVars, St) -> @@ -2765,6 +2779,17 @@ is_newly_introduced_builtin_type({set, 0}) -> true; % opaque is_newly_introduced_builtin_type({boolean, 0}) -> true; is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false. +%% Obsolete in OTP 17.0. +obsolete_type({array, 0}) -> {deprecated, {array, array}, "OTP 18.0"}; +obsolete_type({dict, 0}) -> {deprecated, {dict, dict}, "OTP 18.0"}; +obsolete_type({digraph, 0}) -> {deprecated, {digraph, graph}, "OTP 18.0"}; +obsolete_type({gb_set, 0}) -> {deprecated, {gb_sets, set}, "OTP 18.0"}; +obsolete_type({gb_tree, 0}) -> {deprecated, {gb_trees, tree}, "OTP 18.0"}; +obsolete_type({queue, 0}) -> {deprecated, {queue, queue}, "OTP 18.0"}; +obsolete_type({set, 0}) -> {deprecated, {sets, set}, "OTP 18.0"}; +obsolete_type({tid, 0}) -> {deprecated, {ets, tid}, "OTP 18.0"}; +obsolete_type({Name, _}) when is_atom(Name) -> no. + %% spec_decl(Line, Fun, Types, State) -> State. spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) -> diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index cc5e69f574..42b11a97e2 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -53,10 +53,8 @@ | {tab(),integer(),integer(),binary(),list(),integer()} | {tab(),_,_,integer(),binary(),list(),integer(),integer()}. -%% a similar definition is also in erl_types -opaque tid() :: integer(). -%% these ones are also defined in erl_bif_types -type match_pattern() :: atom() | tuple(). -type match_spec() :: [{match_pattern(), [_], [_]}]. diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index 237317ac94..0a26d0182d 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -196,31 +196,32 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Some types. --export_type([iter/0]). +-export_type([set/0, set/1, iter/0, iter/1]). --type gb_set_node() :: 'nil' | {term(), _, _}. +-type gb_set_node(Element) :: 'nil' | {Element, _, _}. +-type gb_set_node() :: gb_set_node(_). +-opaque set(Element) :: {non_neg_integer(), gb_set_node(Element)}. +-opaque set() :: set(_). +-opaque iter(Element) :: [gb_set_node(Element)]. -opaque iter() :: [gb_set_node()]. -%% A declaration equivalent to the following is currently hard-coded -%% in erl_types.erl -%% -%% -opaque gb_set() :: {non_neg_integer(), gb_set_node()}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% gb_sets:set() in OTP 17 only. + -spec empty() -> Set when - Set :: gb_set(). + Set :: gb_sets:set(). empty() -> {0, nil}. -spec new() -> Set when - Set :: gb_set(). + Set :: gb_sets:set(). new() -> empty(). -spec is_empty(Set) -> boolean() when - Set :: gb_set(). + Set :: gb_sets:set(). is_empty({0, nil}) -> true; @@ -228,27 +229,24 @@ is_empty(_) -> false. -spec size(Set) -> non_neg_integer() when - Set :: gb_set(). + Set :: gb_sets:set(). size({Size, _}) -> Size. --spec singleton(Element) -> gb_set() when - Element :: term(). +-spec singleton(Element) -> set(Element). singleton(Key) -> {1, {Key, nil, nil}}. -spec is_element(Element, Set) -> boolean() when - Element :: term(), - Set :: gb_set(). + Set :: set(Element). is_element(Key, S) -> is_member(Key, S). -spec is_member(Element, Set) -> boolean() when - Element :: term(), - Set :: gb_set(). + Set :: set(Element). is_member(Key, {_, T}) -> is_member_1(Key, T). @@ -263,9 +261,8 @@ is_member_1(_, nil) -> false. -spec insert(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). insert(Key, {S, T}) -> S1 = S + 1, @@ -322,8 +319,8 @@ count(nil) -> {1, 0}. -spec balance(Set1) -> Set2 when - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). balance({S, T}) -> {S, balance(T, S)}. @@ -349,17 +346,15 @@ balance_list_1(L, 0) -> {nil, L}. -spec add_element(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). add_element(X, S) -> add(X, S). -spec add(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). add(X, S) -> case is_member(X, S) of @@ -370,32 +365,30 @@ add(X, S) -> end. -spec from_list(List) -> Set when - List :: [term()], - Set :: gb_set(). + List :: [Element], + Set :: set(Element). from_list(L) -> from_ordset(ordsets:from_list(L)). -spec from_ordset(List) -> Set when - List :: [term()], - Set :: gb_set(). + List :: [Element], + Set :: set(Element). from_ordset(L) -> S = length(L), {S, balance_list(L, S)}. -spec del_element(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). del_element(Key, S) -> delete_any(Key, S). -spec delete_any(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). delete_any(Key, S) -> case is_member(Key, S) of @@ -406,9 +399,8 @@ delete_any(Key, S) -> end. -spec delete(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). delete(Key, {S, T}) -> {S - 1, delete_1(Key, T)}. @@ -431,9 +423,8 @@ merge(Smaller, Larger) -> {Key, Smaller, Larger1}. -spec take_smallest(Set1) -> {Element, Set2} when - Set1 :: gb_set(), - Set2 :: gb_set(), - Element :: term(). + Set1 :: set(Element), + Set2 :: set(Element). take_smallest({S, T}) -> {Key, Larger} = take_smallest1(T), @@ -445,8 +436,8 @@ take_smallest1({Key, Smaller, Larger}) -> {Key1, Smaller1} = take_smallest1(Smaller), {Key1, {Key, Smaller1, Larger}}. --spec smallest(Set) -> term() when - Set :: gb_set(). +-spec smallest(Set) -> Element when + Set :: set(Element). smallest({_, T}) -> smallest_1(T). @@ -457,9 +448,8 @@ smallest_1({_Key, Smaller, _Larger}) -> smallest_1(Smaller). -spec take_largest(Set1) -> {Element, Set2} when - Set1 :: gb_set(), - Set2 :: gb_set(), - Element :: term(). + Set1 :: set(Element), + Set2 :: set(Element). take_largest({S, T}) -> {Key, Smaller} = take_largest1(T), @@ -471,8 +461,8 @@ take_largest1({Key, Smaller, Larger}) -> {Key1, Larger1} = take_largest1(Larger), {Key1, {Key, Smaller, Larger1}}. --spec largest(Set) -> term() when - Set :: gb_set(). +-spec largest(Set) -> Element when + Set :: set(Element). largest({_, T}) -> largest_1(T). @@ -483,8 +473,8 @@ largest_1({_Key, _Smaller, Larger}) -> largest_1(Larger). -spec to_list(Set) -> List when - Set :: gb_set(), - List :: [term()]. + Set :: set(Element), + List :: [Element]. to_list({_, T}) -> to_list(T, []). @@ -496,8 +486,8 @@ to_list({Key, Small, Big}, L) -> to_list(nil, L) -> L. -spec iterator(Set) -> Iter when - Set :: gb_set(), - Iter :: iter(). + Set :: set(Element), + Iter :: iter(Element). iterator({_, T}) -> iterator(T, []). @@ -513,9 +503,8 @@ iterator(nil, As) -> As. -spec next(Iter1) -> {Element, Iter2} | 'none' when - Iter1 :: iter(), - Iter2 :: iter(), - Element :: term(). + Iter1 :: iter(Element), + Iter2 :: iter(Element). next([{X, _, T} | As]) -> {X, iterator(T, As)}; @@ -546,9 +535,9 @@ next([]) -> %% overhead. -spec union(Set1, Set2) -> Set3 when - Set1 :: gb_set(), - Set2 :: gb_set(), - Set3 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). union({N1, T1}, {N2, T2}) when N2 < N1 -> union(to_list_1(T2), N2, T1, N1); @@ -570,7 +559,7 @@ union(L, N1, T2, N2) -> union_1(L, mk_set(N2, T2)) end. --spec mk_set(non_neg_integer(), gb_set_node()) -> gb_set(). +-spec mk_set(non_neg_integer(), gb_set_node(T)) -> set(T). mk_set(N, T) -> {N, T}. @@ -651,8 +640,8 @@ balance_revlist_1(L, 0) -> {nil, L}. -spec union(SetList) -> Set when - SetList :: [gb_set(),...], - Set :: gb_set(). + SetList :: [set(Element),...], + Set :: set(Element). union([S | Ss]) -> union_list(S, Ss); @@ -666,9 +655,9 @@ union_list(S, []) -> S. %% The rest is modelled on the above. -spec intersection(Set1, Set2) -> Set3 when - Set1 :: gb_set(), - Set2 :: gb_set(), - Set3 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). intersection({N1, T1}, {N2, T2}) when N2 < N1 -> intersection(to_list_1(T2), N2, T1, N1); @@ -717,8 +706,8 @@ intersection_2(_, [], As, S) -> {S, balance_revlist(As, S)}. -spec intersection(SetList) -> Set when - SetList :: [gb_set(),...], - Set :: gb_set(). + SetList :: [set(Element),...], + Set :: set(Element). intersection([S | Ss]) -> intersection_list(S, Ss). @@ -728,8 +717,8 @@ intersection_list(S, [S1 | Ss]) -> intersection_list(S, []) -> S. -spec is_disjoint(Set1, Set2) -> boolean() when - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). is_disjoint({N1, T1}, {N2, T2}) when N1 < N2 -> is_disjoint_1(T1, T2); @@ -758,17 +747,17 @@ is_disjoint_1(_, nil) -> %% traverse the whole element list of the left operand. -spec subtract(Set1, Set2) -> Set3 when - Set1 :: gb_set(), - Set2 :: gb_set(), - Set3 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). subtract(S1, S2) -> difference(S1, S2). -spec difference(Set1, Set2) -> Set3 when - Set1 :: gb_set(), - Set2 :: gb_set(), - Set3 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). difference({N1, T1}, {N2, T2}) -> difference(to_list_1(T1), N1, T2, N2). @@ -817,8 +806,8 @@ difference_2(Xs, [], As, S) -> %% without the construction of a new set. -spec is_subset(Set1, Set2) -> boolean() when - Set1 :: gb_set(), - Set2 :: gb_set(). + Set1 :: set(Element), + Set2 :: set(Element). is_subset({N1, T1}, {N2, T2}) -> is_subset(to_list_1(T1), N1, T2, N2). @@ -867,20 +856,20 @@ is_set({N, {_, _, _}}) when is_integer(N), N >= 0 -> true; is_set(_) -> false. -spec filter(Pred, Set1) -> Set2 when - Pred :: fun((E :: term()) -> boolean()), - Set1 :: gb_set(), - Set2 :: gb_set(). + Pred :: fun((Element) -> boolean()), + Set1 :: set(Element), + Set2 :: set(Element). filter(F, S) -> from_ordset([X || X <- to_list(S), F(X)]). -spec fold(Function, Acc0, Set) -> Acc1 when - Function :: fun((E :: term(), AccIn) -> AccOut), - Acc0 :: term(), - Acc1 :: term(), - AccIn :: term(), - AccOut :: term(), - Set :: gb_set(). + Function :: fun((Element, AccIn) -> AccOut), + Acc0 :: Acc, + Acc1 :: Acc, + AccIn :: Acc, + AccOut :: Acc, + Set :: set(Element). fold(F, A, {_, T}) when is_function(F, 2) -> fold_1(F, A, T). diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index 7a4dfe1a0b..7069b61873 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -152,25 +152,25 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Some types. --export_type([iter/0]). +-export_type([tree/0, tree/2, iter/0, iter/2]). --type gb_tree_node() :: 'nil' | {_, _, _, _}. +-type gb_tree_node(K, V) :: 'nil' + | {K, V, gb_tree_node(K, V), gb_tree_node(K, V)}. +-type gb_tree_node() :: gb_tree_node(_, _). +-opaque tree(Key, Value) :: {non_neg_integer(), gb_tree_node(Key, Value)}. +-opaque tree() :: tree(_, _). +-opaque iter(Key, Value) :: [gb_tree_node(Key, Value)]. -opaque iter() :: [gb_tree_node()]. -%% A declaration equivalent to the following is currently hard-coded -%% in erl_types.erl -%% -%% -opaque gb_tree() :: {non_neg_integer(), gb_tree_node()}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec empty() -> gb_tree(). +-spec empty() -> tree(). empty() -> {0, nil}. -spec is_empty(Tree) -> boolean() when - Tree :: gb_tree(). + Tree :: tree(). is_empty({0, nil}) -> true; @@ -178,17 +178,15 @@ is_empty(_) -> false. -spec size(Tree) -> non_neg_integer() when - Tree :: gb_tree(). + Tree :: tree(). size({Size, _}) when is_integer(Size), Size >= 0 -> Size. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec lookup(Key, Tree) -> 'none' | {'value', Val} when - Key :: term(), - Val :: term(), - Tree :: gb_tree(). +-spec lookup(Key, Tree) -> 'none' | {'value', Value} when + Tree :: tree(Key, Value). lookup(Key, {_, T}) -> lookup_1(Key, T). @@ -214,8 +212,7 @@ lookup_1(_, nil) -> %% This is a specialized version of `lookup'. -spec is_defined(Key, Tree) -> boolean() when - Key :: term(), - Tree :: gb_tree(). + Tree :: tree(Key, Value :: term()). is_defined(Key, {_, T}) -> is_defined_1(Key, T). @@ -233,10 +230,8 @@ is_defined_1(_, nil) -> %% This is a specialized version of `lookup'. --spec get(Key, Tree) -> Val when - Key :: term(), - Tree :: gb_tree(), - Val :: term(). +-spec get(Key, Tree) -> Value when + Tree :: tree(Key, Value). get(Key, {_, T}) -> get_1(Key, T). @@ -250,11 +245,9 @@ get_1(_, {_, Value, _, _}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec update(Key, Val, Tree1) -> Tree2 when - Key :: term(), - Val :: term(), - Tree1 :: gb_tree(), - Tree2 :: gb_tree(). +-spec update(Key, Value, Tree1) -> Tree2 when + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). update(Key, Val, {S, T}) -> T1 = update_1(Key, Val, T), @@ -271,11 +264,9 @@ update_1(Key, Value, {_, _, Smaller, Bigger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec insert(Key, Val, Tree1) -> Tree2 when - Key :: term(), - Val :: term(), - Tree1 :: gb_tree(), - Tree2 :: gb_tree(). +-spec insert(Key, Value, Tree1) -> Tree2 when + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). insert(Key, Val, {S, T}) when is_integer(S) -> S1 = S+1, @@ -324,11 +315,9 @@ insert_1(Key, _, _, _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec enter(Key, Val, Tree1) -> Tree2 when - Key :: term(), - Val :: term(), - Tree1 :: gb_tree(), - Tree2 :: gb_tree(). +-spec enter(Key, Value, Tree1) -> Tree2 when + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). enter(Key, Val, T) -> case is_defined(Key, T) of @@ -352,8 +341,8 @@ count(nil) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec balance(Tree1) -> Tree2 when - Tree1 :: gb_tree(), - Tree2 :: gb_tree(). + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). balance({S, T}) -> {S, balance(T, S)}. @@ -379,8 +368,8 @@ balance_list_1(L, 0) -> {nil, L}. -spec from_orddict(List) -> Tree when - List :: [{Key :: term(), Val :: term()}], - Tree :: gb_tree(). + List :: [{Key, Value}], + Tree :: tree(Key, Value). from_orddict(L) -> S = length(L), @@ -389,9 +378,8 @@ from_orddict(L) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec delete_any(Key, Tree1) -> Tree2 when - Key :: term(), - Tree1 :: gb_tree(), - Tree2 :: gb_tree(). + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). delete_any(Key, T) -> case is_defined(Key, T) of @@ -404,9 +392,8 @@ delete_any(Key, T) -> %%% delete. Assumes that key is present. -spec delete(Key, Tree1) -> Tree2 when - Key :: term(), - Tree1 :: gb_tree(), - Tree2 :: gb_tree(). + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). delete(Key, {S, T}) when is_integer(S), S >= 0 -> {S - 1, delete_1(Key, T)}. @@ -432,11 +419,9 @@ merge(Smaller, Larger) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec take_smallest(Tree1) -> {Key, Val, Tree2} when - Tree1 :: gb_tree(), - Tree2 :: gb_tree(), - Key :: term(), - Val :: term(). +-spec take_smallest(Tree1) -> {Key, Value, Tree2} when + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). take_smallest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Larger} = take_smallest1(Tree), @@ -448,10 +433,8 @@ take_smallest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Smaller1} = take_smallest1(Smaller), {Key1, Value1, {Key, Value, Smaller1, Larger}}. --spec smallest(Tree) -> {Key, Val} when - Tree :: gb_tree(), - Key :: term(), - Val :: term(). +-spec smallest(Tree) -> {Key, Value} when + Tree :: tree(Key, Value). smallest({_, Tree}) -> smallest_1(Tree). @@ -461,11 +444,9 @@ smallest_1({Key, Value, nil, _Larger}) -> smallest_1({_Key, _Value, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(Tree1) -> {Key, Val, Tree2} when - Tree1 :: gb_tree(), - Tree2 :: gb_tree(), - Key :: term(), - Val :: term(). +-spec take_largest(Tree1) -> {Key, Value, Tree2} when + Tree1 :: tree(Key, Value), + Tree2 :: tree(Key, Value). take_largest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Smaller} = take_largest1(Tree), @@ -477,10 +458,8 @@ take_largest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Larger1} = take_largest1(Larger), {Key1, Value1, {Key, Value, Smaller, Larger1}}. --spec largest(Tree) -> {Key, Val} when - Tree :: gb_tree(), - Key :: term(), - Val :: term(). +-spec largest(Tree) -> {Key, Value} when + Tree :: tree(Key, Value). largest({_, Tree}) -> largest_1(Tree). @@ -492,10 +471,8 @@ largest_1({_Key, _Value, _Smaller, Larger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec to_list(Tree) -> [{Key, Val}] when - Tree :: gb_tree(), - Key :: term(), - Val :: term(). +-spec to_list(Tree) -> [{Key, Value}] when + Tree :: tree(Key, Value). to_list({_, T}) -> to_list(T, []). @@ -509,8 +486,7 @@ to_list(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec keys(Tree) -> [Key] when - Tree :: gb_tree(), - Key :: term(). + Tree :: tree(Key, Value :: term()). keys({_, T}) -> keys(T, []). @@ -521,9 +497,8 @@ keys(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec values(Tree) -> [Val] when - Tree :: gb_tree(), - Val :: term(). +-spec values(Tree) -> [Value] when + Tree :: tree(Key :: term(), Value). values({_, T}) -> values(T, []). @@ -535,8 +510,8 @@ values(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec iterator(Tree) -> Iter when - Tree :: gb_tree(), - Iter :: iter(). + Tree :: tree(Key, Value), + Iter :: iter(Key, Value). iterator({_, T}) -> iterator_1(T). @@ -554,11 +529,9 @@ iterator({_, _, L, _} = T, As) -> iterator(nil, As) -> As. --spec next(Iter1) -> 'none' | {Key, Val, Iter2} when - Iter1 :: iter(), - Iter2 :: iter(), - Key :: term(), - Val :: term(). +-spec next(Iter1) -> 'none' | {Key, Value, Iter2} when + Iter1 :: iter(Key, Value), + Iter2 :: iter(Key, Value). next([{X, V, _, T} | As]) -> {X, V, iterator(T, As)}; @@ -568,9 +541,9 @@ next([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec map(Function, Tree1) -> Tree2 when - Function :: fun((K :: term(), V1 :: term()) -> V2 :: term()), - Tree1 :: gb_tree(), - Tree2 :: gb_tree(). + Function :: fun((K :: Key, V1 :: Value1) -> V2 :: Value2), + Tree1 :: tree(Key, Value1), + Tree2 :: tree(Key, Value2). map(F, {Size, Tree}) when is_function(F, 2) -> {Size, map_1(F, Tree)}. diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index cebc9c91bd..380bc3eccc 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -577,6 +577,24 @@ obsolete_1(wxCursor, new, 3) -> obsolete_1(wxCursor, new, 4) -> {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +%% Added in OTP 17. +obsolete_1(asn1ct, decode,3) -> + {deprecated,"deprecated; use Mod:decode/2 instead"}; +obsolete_1(asn1ct, encode, 3) -> + {deprecated,"deprecated; use Mod:encode/2 instead"}; +obsolete_1(asn1rt, decode,3) -> + {deprecated,"deprecated; use Mod:decode/2 instead"}; +obsolete_1(asn1rt, encode, 2) -> + {deprecated,"deprecated; use Mod:encode/2 instead"}; +obsolete_1(asn1rt, encode, 3) -> + {deprecated,"deprecated; use Mod:encode/2 instead"}; +obsolete_1(asn1rt, info, 1) -> + {deprecated,"deprecated; use Mod:info/0 instead"}; +obsolete_1(asn1rt, utf8_binary_to_list, 1) -> + {deprecated,{unicode,characters_to_list,1}}; +obsolete_1(asn1rt, utf8_list_to_binary, 1) -> + {deprecated,{unicode,characters_to_binary,1}}; + obsolete_1(_, _, _) -> no. diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl index 4bbf5de8a5..472d503b99 100644 --- a/lib/stdlib/src/queue.erl +++ b/lib/stdlib/src/queue.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -32,6 +32,8 @@ -export([cons/2,head/1,tail/1, snoc/2,last/1,daeh/1,init/1,liat/1,lait/1]). +-export_type([queue/0, queue/1]). + %%-------------------------------------------------------------------------- %% Efficient implementation of double ended fifo queues %% @@ -44,10 +46,9 @@ %% that is; the RearList is reversed. %% -%% A declaration equivalent to the following is currently hard-coded -%% in erl_types.erl -%% -%% -opaque queue() :: {list(), list()}. +-opaque queue(Item) :: {list(Item), list(Item)}. + +-opaque queue() :: queue(_). %% Creation, inspection and conversion @@ -79,7 +80,7 @@ len(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec to_list(Q :: queue()) -> list(). +-spec to_list(Q :: queue(Item)) -> list(Item). to_list({In,Out}) when is_list(In), is_list(Out) -> Out++lists:reverse(In, []); to_list(Q) -> @@ -88,7 +89,7 @@ to_list(Q) -> %% Create queue from list %% %% O(length(L)) --spec from_list(L :: list()) -> queue(). +-spec from_list(L :: list(Item)) -> queue(Item). from_list(L) when is_list(L) -> f2r(L); from_list(L) -> @@ -97,7 +98,7 @@ from_list(L) -> %% Return true or false depending on if element is in queue %% %% O(length(Q)) worst case --spec member(Item :: term(), Q :: queue()) -> boolean(). +-spec member(Item, Q :: queue(Item)) -> boolean(). member(X, {R,F}) when is_list(R), is_list(F) -> lists:member(X, R) orelse lists:member(X, F); member(X, Q) -> @@ -110,7 +111,7 @@ member(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). +-spec in(Item, Q1 :: queue(Item)) -> Q2 :: queue(Item). in(X, {[_]=In,[]}) -> {[X], In}; in(X, {In,Out}) when is_list(In), is_list(Out) -> @@ -122,7 +123,7 @@ in(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in_r(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). +-spec in_r(Item, Q1 :: queue(Item)) -> Q2 :: queue(Item). in_r(X, {[],[_]=F}) -> {F,[X]}; in_r(X, {R,F}) when is_list(R), is_list(F) -> @@ -133,9 +134,9 @@ in_r(X, Q) -> %% Take from head/front %% %% O(1) amortized, O(len(Q)) worst case --spec out(Q1 :: queue()) -> - {{value, Item :: term()}, Q2 :: queue()} | - {empty, Q1 :: queue()}. +-spec out(Q1 :: queue(Item)) -> + {{value, Item}, Q2 :: queue(Item)} | + {empty, Q1 :: queue(Item)}. out({[],[]}=Q) -> {empty,Q}; out({[V],[]}) -> @@ -153,9 +154,9 @@ out(Q) -> %% Take from tail/rear %% %% O(1) amortized, O(len(Q)) worst case --spec out_r(Q1 :: queue()) -> - {{value, Item :: term()}, Q2 :: queue()} | - {empty, Q1 :: queue()}. +-spec out_r(Q1 :: queue(Item)) -> + {{value, Item}, Q2 :: queue(Item)} | + {empty, Q1 :: queue(Item)}. out_r({[],[]}=Q) -> {empty,Q}; out_r({[],[V]}) -> @@ -176,7 +177,7 @@ out_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get(Q :: queue()) -> Item :: term(). +-spec get(Q :: queue(Item)) -> Item. get({[],[]}=Q) -> erlang:error(empty, [Q]); get({R,F}) when is_list(R), is_list(F) -> @@ -195,7 +196,7 @@ get([_|R], []) -> % malformed queue -> O(len(Q)) %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get_r(Q :: queue()) -> Item :: term(). +-spec get_r(Q :: queue(Item)) -> Item. get_r({[],[]}=Q) -> erlang:error(empty, [Q]); get_r({[H|_],F}) when is_list(F) -> @@ -210,7 +211,7 @@ get_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek(Q :: queue()) -> empty | {value,Item :: term()}. +-spec peek(Q :: queue(Item)) -> empty | {value, Item}. peek({[],[]}) -> empty; peek({R,[H|_]}) when is_list(R) -> @@ -225,7 +226,7 @@ peek(Q) -> %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek_r(Q :: queue()) -> empty | {value,Item :: term()}. +-spec peek_r(Q :: queue(Item)) -> empty | {value, Item}. peek_r({[],[]}) -> empty; peek_r({[H|_],F}) when is_list(F) -> @@ -240,7 +241,7 @@ peek_r(Q) -> %% Remove the first element and return resulting queue %% %% O(1) amortized --spec drop(Q1 :: queue()) -> Q2 :: queue(). +-spec drop(Q1 :: queue(Item)) -> Q2 :: queue(Item). drop({[],[]}=Q) -> erlang:error(empty, [Q]); drop({[_],[]}) -> @@ -258,7 +259,7 @@ drop(Q) -> %% Remove the last element and return resulting queue %% %% O(1) amortized --spec drop_r(Q1 :: queue()) -> Q2 :: queue(). +-spec drop_r(Q1 :: queue(Item)) -> Q2 :: queue(Item). drop_r({[],[]}=Q) -> erlang:error(empty, [Q]); drop_r({[],[_]}) -> @@ -279,7 +280,7 @@ drop_r(Q) -> %% Return reversed queue %% %% O(1) --spec reverse(Q1 :: queue()) -> Q2 :: queue(). +-spec reverse(Q1 :: queue(Item)) -> Q2 :: queue(Item). reverse({R,F}) when is_list(R), is_list(F) -> {F,R}; reverse(Q) -> @@ -289,7 +290,7 @@ reverse(Q) -> %% %% Q2 empty: O(1) %% else: O(len(Q1)) --spec join(Q1 :: queue(), Q2 :: queue()) -> Q3 :: queue(). +-spec join(Q1 :: queue(Item), Q2 :: queue(Item)) -> Q3 :: queue(Item). join({R,F}=Q, {[],[]}) when is_list(R), is_list(F) -> Q; join({[],[]}, {R,F}=Q) when is_list(R), is_list(F) -> @@ -303,8 +304,8 @@ join(Q1, Q2) -> %% %% N = 0..len(Q) %% O(max(N, len(Q))) --spec split(N :: non_neg_integer(), Q1 :: queue()) -> - {Q2 :: queue(),Q3 :: queue()}. +-spec split(N :: non_neg_integer(), Q1 :: queue(Item)) -> + {Q2 :: queue(Item),Q3 :: queue(Item)}. split(0, {R,F}=Q) when is_list(R), is_list(F) -> {{[],[]},Q}; split(N, {R,F}=Q) when is_integer(N), N >= 1, is_list(R), is_list(F) -> @@ -345,8 +346,8 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) -> %% %% Fun(_) -> List: O(length(List) * len(Q)) %% else: O(len(Q) --spec filter(Fun, Q1 :: queue()) -> Q2 :: queue() when - Fun :: fun((Item :: term()) -> boolean() | list()). +-spec filter(Fun, Q1 :: queue(Item)) -> Q2 :: queue(Item) when + Fun :: fun((Item) -> boolean() | list(Item)). filter(Fun, {R0,F0}) when is_function(Fun, 1), is_list(R0), is_list(F0) -> F = filter_f(Fun, F0), R = filter_r(Fun, R0), @@ -422,7 +423,7 @@ filter_r(Fun, [X|R0]) -> %% Cons to head %% --spec cons(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). +-spec cons(Item, Q1 :: queue(Item)) -> Q2 :: queue(Item). cons(X, Q) -> in_r(X, Q). @@ -431,7 +432,7 @@ cons(X, Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec head(Q :: queue()) -> Item :: term(). +-spec head(Q :: queue(Item)) -> Item. head({[],[]}=Q) -> erlang:error(empty, [Q]); head({R,F}) when is_list(R), is_list(F) -> @@ -441,7 +442,7 @@ head(Q) -> %% Remove head element and return resulting queue %% --spec tail(Q1 :: queue()) -> Q2 :: queue(). +-spec tail(Q1 :: queue(Item)) -> Q2 :: queue(Item). tail(Q) -> drop(Q). @@ -449,22 +450,22 @@ tail(Q) -> %% Cons to tail %% --spec snoc(Q1 :: queue(), Item :: term()) -> Q2 :: queue(). +-spec snoc(Q1 :: queue(Item), Item) -> Q2 :: queue(Item). snoc(Q, X) -> in(X, Q). %% Return last element --spec daeh(Q :: queue()) -> Item :: term(). +-spec daeh(Q :: queue(Item)) -> Item. daeh(Q) -> get_r(Q). --spec last(Q :: queue()) -> Item :: term(). +-spec last(Q :: queue(Item)) -> Item. last(Q) -> get_r(Q). %% Remove last element and return resulting queue --spec liat(Q1 :: queue()) -> Q2 :: queue(). +-spec liat(Q1 :: queue(Item)) -> Q2 :: queue(Item). liat(Q) -> drop_r(Q). --spec lait(Q1 :: queue()) -> Q2 :: queue(). +-spec lait(Q1 :: queue(Item)) -> Q2 :: queue(Item). lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one. --spec init(Q1 :: queue()) -> Q2 :: queue(). +-spec init(Q1 :: queue(Item)) -> Q2 :: queue(Item). init(Q) -> drop_r(Q). %%-------------------------------------------------------------------------- diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index ebf011a7d9..be4b600f25 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -43,6 +43,8 @@ -export([subtract/2,is_subset/2]). -export([fold/3,filter/2]). +-export_type([set/0, set/1]). + %% Note: mk_seg/1 must be changed too if seg_size is changed. -define(seg_size, 16). -define(max_seg, 32). @@ -54,7 +56,8 @@ %%------------------------------------------------------------------------------ -type seg() :: tuple(). --type segs() :: tuple(). +-type segs(E) :: tuple() + | E. % dummy %% Define a hash set. The default values are the standard ones. -record(set, @@ -65,14 +68,12 @@ exp_size=?exp_size :: non_neg_integer(), % Size to expand at con_size=?con_size :: non_neg_integer(), % Size to contract at empty :: seg(), % Empty segment - segs :: segs() % Segments + segs :: segs(_) % Segments }). -%% A declaration equivalent to the following one is hard-coded in erl_types. -%% That declaration contains hard-coded information about the #set{} -%% record and the types of its fields. So, please make sure that any -%% changes to its structure are also propagated to erl_types.erl. -%% -%% -opaque set() :: #set{}. + +-opaque set() :: set(_). + +-opaque set(Element) :: #set{segs :: segs(Element)}. %%------------------------------------------------------------------------------ @@ -98,24 +99,23 @@ size(S) -> S#set.size. %% to_list(Set) -> [Elem]. %% Return the elements in Set as a list. -spec to_list(Set) -> List when - Set :: set(), - List :: [term()]. + Set :: set(Element), + List :: [Element]. to_list(S) -> fold(fun (Elem, List) -> [Elem|List] end, [], S). %% from_list([Elem]) -> Set. %% Build a set from the elements in List. -spec from_list(List) -> Set when - List :: [term()], - Set :: set(). + List :: [Element], + Set :: set(Element). from_list(L) -> lists:foldl(fun (E, S) -> add_element(E, S) end, new(), L). %% is_element(Element, Set) -> boolean(). %% Return 'true' if Element is an element of Set, else 'false'. -spec is_element(Element, Set) -> boolean() when - Element :: term(), - Set :: set(). + Set :: set(Element). is_element(E, S) -> Slot = get_slot(S, E), Bkt = get_bucket(S, Slot), @@ -124,9 +124,8 @@ is_element(E, S) -> %% add_element(Element, Set) -> Set. %% Return Set with Element inserted in it. -spec add_element(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: set(), - Set2 :: set(). + Set1 :: set(Element), + Set2 :: set(Element). add_element(E, S0) -> Slot = get_slot(S0, E), {S1,Ic} = on_bucket(fun (B0) -> add_bkt_el(E, B0, B0) end, S0, Slot), @@ -141,9 +140,8 @@ add_bkt_el(E, [], Bkt) -> {[E|Bkt],1}. %% del_element(Element, Set) -> Set. %% Return Set but with Element removed. -spec del_element(Element, Set1) -> Set2 when - Element :: term(), - Set1 :: set(), - Set2 :: set(). + Set1 :: set(Element), + Set2 :: set(Element). del_element(E, S0) -> Slot = get_slot(S0, E), {S1,Dc} = on_bucket(fun (B0) -> del_bkt_el(E, B0) end, S0, Slot), @@ -159,9 +157,9 @@ del_bkt_el(_, []) -> {[],0}. %% union(Set1, Set2) -> Set %% Return the union of Set1 and Set2. -spec union(Set1, Set2) -> Set3 when - Set1 :: set(), - Set2 :: set(), - Set3 :: set(). + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). union(S1, S2) when S1#set.size < S2#set.size -> fold(fun (E, S) -> add_element(E, S) end, S2, S1); union(S1, S2) -> @@ -170,14 +168,14 @@ union(S1, S2) -> %% union([Set]) -> Set %% Return the union of the list of sets. -spec union(SetList) -> Set when - SetList :: [set()], - Set :: set(). + SetList :: [set(Element)], + Set :: set(Element). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); union([S]) -> S; union([]) -> new(). --spec union1(set(), [set()]) -> set(). +-spec union1(set(E), [set(E)]) -> set(E). union1(S1, [S2|Ss]) -> union1(union(S1, S2), Ss); union1(S1, []) -> S1. @@ -185,9 +183,9 @@ union1(S1, []) -> S1. %% intersection(Set1, Set2) -> Set. %% Return the intersection of Set1 and Set2. -spec intersection(Set1, Set2) -> Set3 when - Set1 :: set(), - Set2 :: set(), - Set3 :: set(). + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). intersection(S1, S2) when S1#set.size < S2#set.size -> filter(fun (E) -> is_element(E, S2) end, S1); intersection(S1, S2) -> @@ -196,13 +194,13 @@ intersection(S1, S2) -> %% intersection([Set]) -> Set. %% Return the intersection of the list of sets. -spec intersection(SetList) -> Set when - SetList :: [set(),...], - Set :: set(). + SetList :: [set(Element),...], + Set :: set(Element). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection([S]) -> S. --spec intersection1(set(), [set()]) -> set(). +-spec intersection1(set(E), [set(E)]) -> set(E). intersection1(S1, [S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection1(S1, []) -> S1. @@ -210,8 +208,8 @@ intersection1(S1, []) -> S1. %% is_disjoint(Set1, Set2) -> boolean(). %% Check whether Set1 and Set2 are disjoint. -spec is_disjoint(Set1, Set2) -> boolean() when - Set1 :: set(), - Set2 :: set(). + Set1 :: set(Element), + Set2 :: set(Element). is_disjoint(S1, S2) when S1#set.size < S2#set.size -> fold(fun (_, false) -> false; (E, true) -> not is_element(E, S2) @@ -225,9 +223,9 @@ is_disjoint(S1, S2) -> %% Return all and only the elements of Set1 which are not also in %% Set2. -spec subtract(Set1, Set2) -> Set3 when - Set1 :: set(), - Set2 :: set(), - Set3 :: set(). + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). subtract(S1, S2) -> filter(fun (E) -> not is_element(E, S2) end, S1). @@ -235,34 +233,34 @@ subtract(S1, S2) -> %% Return 'true' when every element of Set1 is also a member of %% Set2, else 'false'. -spec is_subset(Set1, Set2) -> boolean() when - Set1 :: set(), - Set2 :: set(). + Set1 :: set(Element), + Set2 :: set(Element). is_subset(S1, S2) -> fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1). %% fold(Fun, Accumulator, Set) -> Accumulator. %% Fold function Fun over all elements in Set and return Accumulator. -spec fold(Function, Acc0, Set) -> Acc1 when - Function :: fun((E :: term(),AccIn) -> AccOut), - Set :: set(), - Acc0 :: T, - Acc1 :: T, - AccIn :: T, - AccOut :: T. + Function :: fun((Element, AccIn) -> AccOut), + Set :: set(Element), + Acc0 :: Acc, + Acc1 :: Acc, + AccIn :: Acc, + AccOut :: Acc. fold(F, Acc, D) -> fold_set(F, Acc, D). %% filter(Fun, Set) -> Set. %% Filter Set with Fun. -spec filter(Pred, Set1) -> Set2 when - Pred :: fun((E :: term()) -> boolean()), - Set1 :: set(), - Set2 :: set(). + Pred :: fun((Element) -> boolean()), + Set1 :: set(Element), + Set2 :: set(Element). filter(F, D) -> filter_set(F, D). %% get_slot(Hashdb, Key) -> Slot. %% Get the slot. First hash on the new range, if we hit a bucket %% which has not been split use the unsplit buddy bucket. --spec get_slot(set(), term()) -> non_neg_integer(). +-spec get_slot(set(E), E) -> non_neg_integer(). get_slot(T, Key) -> H = erlang:phash(Key, T#set.maxn), if @@ -276,8 +274,8 @@ get_bucket(T, Slot) -> get_bucket_s(T#set.segs, Slot). %% on_bucket(Fun, Hashdb, Slot) -> {NewHashDb,Result}. %% Apply Fun to the bucket in Slot and replace the returned bucket. --spec on_bucket(fun((_) -> {[_], 0 | 1}), set(), non_neg_integer()) -> - {set(), 0 | 1}. +-spec on_bucket(fun((_) -> {[_], 0 | 1}), set(E), non_neg_integer()) -> + {set(E), 0 | 1}. on_bucket(F, T, Slot) -> SegI = ((Slot-1) div ?seg_size) + 1, BktI = ((Slot-1) rem ?seg_size) + 1, @@ -351,7 +349,7 @@ put_bucket_s(Segs, Slot, Bkt) -> Seg = setelement(BktI, element(SegI, Segs), Bkt), setelement(SegI, Segs, Seg). --spec maybe_expand(set(), 0 | 1) -> set(). +-spec maybe_expand(set(E), 0 | 1) -> set(E). maybe_expand(T0, Ic) when T0#set.size + Ic > T0#set.exp_size -> T = maybe_expand_segs(T0), %Do we need more segments. N = T#set.n + 1, %Next slot to expand into @@ -369,14 +367,14 @@ maybe_expand(T0, Ic) when T0#set.size + Ic > T0#set.exp_size -> segs = Segs2}; maybe_expand(T, Ic) -> T#set{size = T#set.size + Ic}. --spec maybe_expand_segs(set()) -> set(). +-spec maybe_expand_segs(set(E)) -> set(E). maybe_expand_segs(T) when T#set.n =:= T#set.maxn -> T#set{maxn = 2 * T#set.maxn, bso = 2 * T#set.bso, segs = expand_segs(T#set.segs, T#set.empty)}; maybe_expand_segs(T) -> T. --spec maybe_contract(set(), non_neg_integer()) -> set(). +-spec maybe_contract(set(E), non_neg_integer()) -> set(E). maybe_contract(T, Dc) when T#set.size - Dc < T#set.con_size, T#set.n > ?seg_size -> N = T#set.n, @@ -395,7 +393,7 @@ maybe_contract(T, Dc) when T#set.size - Dc < T#set.con_size, segs = Segs2}); maybe_contract(T, Dc) -> T#set{size = T#set.size - Dc}. --spec maybe_contract_segs(set()) -> set(). +-spec maybe_contract_segs(set(E)) -> set(E). maybe_contract_segs(T) when T#set.n =:= T#set.bso -> T#set{maxn = T#set.maxn div 2, bso = T#set.bso div 2, @@ -422,7 +420,7 @@ mk_seg(16) -> {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}. %% of segments. We special case the powers of 2 upto 32, this should %% catch most case. N.B. the last element in the segments tuple is %% an extra element containing a default empty segment. --spec expand_segs(segs(), seg()) -> segs(). +-spec expand_segs(segs(E), seg()) -> segs(E). expand_segs({B1}, Empty) -> {B1,Empty}; expand_segs({B1,B2}, Empty) -> @@ -440,7 +438,7 @@ expand_segs(Segs, Empty) -> list_to_tuple(tuple_to_list(Segs) ++ lists:duplicate(tuple_size(Segs), Empty)). --spec contract_segs(segs()) -> segs(). +-spec contract_segs(segs(E)) -> segs(E). contract_segs({B1,_}) -> {B1}; contract_segs({B1,B2,_,_}) -> diff --git a/lib/stdlib/src/sofs.erl b/lib/stdlib/src/sofs.erl index 34eb224647..0bd67db100 100644 --- a/lib/stdlib/src/sofs.erl +++ b/lib/stdlib/src/sofs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1509,7 +1509,7 @@ family_projection(SetFun, F) when ?IS_SET(F) -> %%% -spec(family_to_digraph(Family) -> Graph when - Graph :: digraph(), + Graph :: digraph:graph(), Family :: family()). family_to_digraph(F) when ?IS_SET(F) -> case ?TYPE(F) of @@ -1519,7 +1519,7 @@ family_to_digraph(F) when ?IS_SET(F) -> end. -spec(family_to_digraph(Family, GraphType) -> Graph when - Graph :: digraph(), + Graph :: digraph:graph(), Family :: family(), GraphType :: [digraph:d_type()]). family_to_digraph(F, Type) when ?IS_SET(F) -> @@ -1541,7 +1541,7 @@ family_to_digraph(F, Type) when ?IS_SET(F) -> end. -spec(digraph_to_family(Graph) -> Family when - Graph :: digraph(), + Graph :: digraph:graph(), Family :: family()). digraph_to_family(G) -> case catch digraph_family(G) of @@ -1550,7 +1550,7 @@ digraph_to_family(G) -> end. -spec(digraph_to_family(Graph, Type) -> Family when - Graph :: digraph(), + Graph :: digraph:graph(), Family :: family(), Type :: type()). digraph_to_family(G, T) -> diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 749a9a4201..22eefb2514 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,12 +16,10 @@ %% %% %CopyrightEnd% {"%VSN%", - %% Up from - max two major revisions back + %% Up from - max one major revision back [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 - {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 - {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15 - %% Down to - max two major revisions back + {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16 + %% Down to - max one major revision back [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 - {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 - {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15 + {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16 }. diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 974583bc28..ede2742875 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -77,14 +77,15 @@ modules = [] :: modules()}). -type child_rec() :: #child{}. --define(DICT, dict). +-define(DICTS, dict). +-define(DICT, dict:dict). -define(SETS, sets). --define(SET, set). +-define(SET, sets:set). -record(state, {name, strategy :: strategy(), children = [] :: [child_rec()], - dynamics :: ?DICT() | ?SET(), + dynamics :: ?DICT(pid(), list()) | ?SET(pid()), intensity :: non_neg_integer(), period :: pos_integer(), restarts = [], @@ -444,7 +445,7 @@ handle_call(which_children, _From, #state{children = [#child{restart_type = RTyp State) when ?is_simple(State) -> Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods}; ({Pid, _}) -> {undefined, Pid, CT, Mods} end, - ?DICT:to_list(dynamics_db(RType, State#state.dynamics))), + ?DICTS:to_list(dynamics_db(RType, State#state.dynamics))), {reply, Reply, State}; handle_call(which_children, _From, State) -> @@ -483,7 +484,7 @@ handle_call(count_children, _From, #state{children = [#child{restart_type = RTy child_type = CT}]} = State) when ?is_simple(State) -> {Active, Count} = - ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) -> + ?DICTS:fold(fun(Pid, _Val, {Alive, Tot}) -> case is_pid(Pid) andalso is_process_alive(Pid) of true -> {Alive+1, Tot +1}; @@ -777,17 +778,17 @@ restart(Child, State) -> restart(simple_one_for_one, Child, State) -> #child{pid = OldPid, mfargs = {M, F, A}} = Child, - Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type, + Dynamics = ?DICTS:erase(OldPid, dynamics_db(Child#child.restart_type, State#state.dynamics)), case do_start_child_i(M, F, A) of {ok, Pid} -> - NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, + NState = State#state{dynamics = ?DICTS:store(Pid, A, Dynamics)}, {ok, NState}; {ok, Pid, _Extra} -> - NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, + NState = State#state{dynamics = ?DICTS:store(Pid, A, Dynamics)}, {ok, NState}; {error, Error} -> - NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A, + NState = State#state{dynamics = ?DICTS:store(restarting(OldPid), A, Dynamics)}, report_error(start_error, Error, Child, State#state.name), {try_again, NState} @@ -980,7 +981,7 @@ terminate_dynamic_children(Child, Dynamics, SupName) -> wait_dynamic_children(Child, Pids, Sz, TRef, EStack0) end, %% Unroll stacked errors and report them - ?DICT:fold(fun(Reason, Ls, _) -> + ?DICTS:fold(fun(Reason, Ls, _) -> report_error(shutdown_error, Reason, Child#child{pid=Ls}, SupName) end, ok, EStack). @@ -994,22 +995,22 @@ monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) -> {error, normal} -> {Pids, EStack}; {error, Reason} -> - {Pids, ?DICT:append(Reason, P, EStack)} + {Pids, ?DICTS:append(Reason, P, EStack)} end - end, {?SETS:new(), ?DICT:new()}, Dynamics); + end, {?SETS:new(), ?DICTS:new()}, Dynamics); monitor_dynamic_children(#child{restart_type=RType}, Dynamics) -> - ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) -> + ?DICTS:fold(fun(P, _, {Pids, EStack}) when is_pid(P) -> case monitor_child(P) of ok -> {?SETS:add_element(P, Pids), EStack}; {error, normal} when RType =/= permanent -> {Pids, EStack}; {error, Reason} -> - {Pids, ?DICT:append(Reason, P, EStack)} + {Pids, ?DICTS:append(Reason, P, EStack)} end; (?restarting(_), _, {Pids, EStack}) -> {Pids, EStack} - end, {?SETS:new(), ?DICT:new()}, Dynamics). + end, {?SETS:new(), ?DICTS:new()}, Dynamics). wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) -> @@ -1033,7 +1034,7 @@ wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz, {'DOWN', _MRef, process, Pid, Reason} -> wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, - TRef, ?DICT:append(Reason, Pid, EStack)) + TRef, ?DICTS:append(Reason, Pid, EStack)) end; wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, TRef, EStack) -> @@ -1048,7 +1049,7 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, {'DOWN', _MRef, process, Pid, Reason} -> wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, - TRef, ?DICT:append(Reason, Pid, EStack)); + TRef, ?DICTS:append(Reason, Pid, EStack)); {timeout, TRef, kill} -> ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids), @@ -1073,12 +1074,12 @@ save_child(Child, #state{children = Children} = State) -> save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) -> State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))}; save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) -> - State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}. + State#state{dynamics = ?DICTS:store(Pid, Args, dynamics_db(RestartType, Dynamics))}. dynamics_db(temporary, undefined) -> ?SETS:new(); dynamics_db(_, undefined) -> - ?DICT:new(); + ?DICTS:new(); dynamics_db(_,Dynamics) -> Dynamics. @@ -1087,14 +1088,14 @@ dynamic_child_args(Pid, Dynamics) -> true -> {ok, undefined}; false -> - ?DICT:find(Pid, Dynamics) + ?DICTS:find(Pid, Dynamics) end. state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) -> NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)), State#state{dynamics = NDynamics}; state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) -> - NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)), + NDynamics = ?DICTS:erase(Pid, dynamics_db(RType, State#state.dynamics)), State#state{dynamics = NDynamics}; state_del_child(Child, State) -> NChildren = del_child(Child#child.name, State#state.children), @@ -1157,7 +1158,7 @@ is_dynamic_pid(Pid, Dynamics) -> true -> ?SETS:is_element(Pid, Dynamics); false -> - ?DICT:is_key(Pid, Dynamics) + ?DICTS:is_key(Pid, Dynamics) end. replace_child(Child, State) -> diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl index 2c1c5acf5a..43c980e994 100644 --- a/lib/stdlib/test/edlin_expand_SUITE.erl +++ b/lib/stdlib/test/edlin_expand_SUITE.erl @@ -26,11 +26,11 @@ -include_lib("test_server/include/test_server.hrl"). -% Default timetrap timeout (set in init_per_testcase). +%% Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?default_timeout), + Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), @@ -67,23 +67,21 @@ normal(doc) -> normal(suite) -> []; normal(Config) when is_list(Config) -> - ?line {module,expand_test} = c:l(expand_test), - % These tests might fail if another module with the prefix "expand_" happens - % to also be loaded. - ?line {yes, "test:", []} = edlin_expand:expand(lists:reverse("expand_")), - ?line {no, [], []} = edlin_expand:expand(lists:reverse("expandXX_")), - ?line {no,[], - [{"a_fun_name",1}, - {"a_less_fun_name",1}, - {"b_comes_after_a",1}, - {"expand0arity_entirely",0}, - {"module_info",0}, - {"module_info",1}]} = edlin_expand:expand(lists:reverse("expand_test:")), - ?line {yes,[],[{"a_fun_name",1}, - {"a_less_fun_name",1}]} = edlin_expand:expand( - lists:reverse("expand_test:a_")), - ?line {yes,"arity_entirely()",[]} = edlin_expand:expand( - lists:reverse("expand_test:expand0")), + {module,expand_test} = c:l(expand_test), + %% These tests might fail if another module with the prefix + %% "expand_" happens to also be loaded. + {yes, "test:", []} = do_expand("expand_"), + {no, [], []} = do_expand("expandXX_"), + {no,[], + [{"a_fun_name",1}, + {"a_less_fun_name",1}, + {"b_comes_after_a",1}, + {"expand0arity_entirely",0}, + {"module_info",0}, + {"module_info",1}]} = do_expand("expand_test:"), + {yes,[],[{"a_fun_name",1}, + {"a_less_fun_name",1}]} = do_expand("expand_test:a_"), + {yes,"arity_entirely()",[]} = do_expand("expand_test:expand0"), ok. quoted_fun(doc) -> @@ -91,38 +89,35 @@ quoted_fun(doc) -> quoted_fun(suite) -> []; quoted_fun(Config) when is_list(Config) -> - ?line {module,expand_test} = c:l(expand_test), - ?line {module,expand_test1} = c:l(expand_test1), + {module,expand_test} = c:l(expand_test), + {module,expand_test1} = c:l(expand_test1), %% should be no colon after test this time - ?line {yes, "test", []} = edlin_expand:expand(lists:reverse("expand_")), - ?line {no, [], []} = edlin_expand:expand(lists:reverse("expandXX_")), - ?line {no,[],[{"'#weird-fun-name'",1}, - {"'Quoted_fun_name'",0}, - {"'Quoted_fun_too'",0}, - {"a_fun_name",1}, - {"a_less_fun_name",1}, - {"b_comes_after_a",1}, - {"module_info",0}, - {"module_info",1}]} = edlin_expand:expand( - lists:reverse("expand_test1:")), - ?line {yes,"_",[]} = edlin_expand:expand( - lists:reverse("expand_test1:a")), - ?line {yes,[],[{"a_fun_name",1}, - {"a_less_fun_name",1}]} = edlin_expand:expand( - lists:reverse("expand_test1:a_")), - ?line {yes,[], - [{"'#weird-fun-name'",1}, + {yes, "test", []} = do_expand("expand_"), + {no, [], []} = do_expand("expandXX_"), + {no,[],[{"'#weird-fun-name'",1}, {"'Quoted_fun_name'",0}, - {"'Quoted_fun_too'",0}]} = edlin_expand:expand( - lists:reverse("expand_test1:'")), - ?line {yes,"uoted_fun_",[]} = edlin_expand:expand( - lists:reverse("expand_test1:'Q")), - ?line {yes,[], - [{"'Quoted_fun_name'",0}, - {"'Quoted_fun_too'",0}]} = edlin_expand:expand( - lists:reverse("expand_test1:'Quoted_fun_")), - ?line {yes,"weird-fun-name'(",[]} = edlin_expand:expand( - lists:reverse("expand_test1:'#")), + {"'Quoted_fun_too'",0}, + {"a_fun_name",1}, + {"a_less_fun_name",1}, + {"b_comes_after_a",1}, + {"module_info",0}, + {"module_info",1}]} = do_expand("expand_test1:"), + {yes,"_",[]} = do_expand("expand_test1:a"), + {yes,[],[{"a_fun_name",1}, + {"a_less_fun_name",1}]} = do_expand("expand_test1:a_"), + {yes,[], + [{"'#weird-fun-name'",1}, + {"'Quoted_fun_name'",0}, + {"'Quoted_fun_too'",0}]} = do_expand("expand_test1:'"), + {yes,"uoted_fun_",[]} = do_expand("expand_test1:'Q"), + {yes,[], + [{"'Quoted_fun_name'",0}, + {"'Quoted_fun_too'",0}]} = do_expand("expand_test1:'Quoted_fun_"), + {yes,"weird-fun-name'(",[]} = do_expand("expand_test1:'#"), + + %% Since there is a module_info/1 as well as a module_info/0 + %% there should not be a closing parenthesis added. + {yes,"(",[]} = do_expand("expand_test:module_info"), ok. quoted_module(doc) -> @@ -130,51 +125,46 @@ quoted_module(doc) -> quoted_module(suite) -> []; quoted_module(Config) when is_list(Config) -> - ?line {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'), - ?line {yes, "Caps':", []} = edlin_expand:expand(lists:reverse("'ExpandTest")), - ?line {no,[], - [{"a_fun_name",1}, - {"a_less_fun_name",1}, - {"b_comes_after_a",1}, - {"module_info",0}, - {"module_info",1}]} = edlin_expand:expand(lists:reverse("'ExpandTestCaps':")), - ?line {yes,[],[{"a_fun_name",1}, - {"a_less_fun_name",1}]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps':a_")), + {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'), + {yes, "Caps':", []} = do_expand("'ExpandTest"), + {no,[], + [{"a_fun_name",1}, + {"a_less_fun_name",1}, + {"b_comes_after_a",1}, + {"module_info",0}, + {"module_info",1}]} = do_expand("'ExpandTestCaps':"), + {yes,[],[{"a_fun_name",1}, + {"a_less_fun_name",1}]} = do_expand("'ExpandTestCaps':a_"), ok. quoted_both(suite) -> []; quoted_both(Config) when is_list(Config) -> - ?line {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'), - ?line {module,'ExpandTestCaps1'} = c:l('ExpandTestCaps1'), + {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'), + {module,'ExpandTestCaps1'} = c:l('ExpandTestCaps1'), %% should be no colon (or quote) after test this time - ?line {yes, "Caps", []} = edlin_expand:expand(lists:reverse("'ExpandTest")), - ?line {no,[],[{"'#weird-fun-name'",0}, - {"'Quoted_fun_name'",0}, - {"'Quoted_fun_too'",0}, - {"a_fun_name",1}, - {"a_less_fun_name",1}, - {"b_comes_after_a",1}, - {"module_info",0}, - {"module_info",1}]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps1':")), - ?line {yes,"_",[]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps1':a")), - ?line {yes,[],[{"a_fun_name",1}, - {"a_less_fun_name",1}]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps1':a_")), - ?line {yes,[], - [{"'#weird-fun-name'",0}, + {yes, "Caps", []} = do_expand("'ExpandTest"), + {no,[],[{"'#weird-fun-name'",0}, {"'Quoted_fun_name'",0}, - {"'Quoted_fun_too'",0}]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps1':'")), - ?line {yes,"uoted_fun_",[]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps1':'Q")), - ?line {yes,[], - [{"'Quoted_fun_name'",0}, - {"'Quoted_fun_too'",0}]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps1':'Quoted_fun_")), - ?line {yes,"weird-fun-name'()",[]} = edlin_expand:expand( - lists:reverse("'ExpandTestCaps1':'#")), + {"'Quoted_fun_too'",0}, + {"a_fun_name",1}, + {"a_less_fun_name",1}, + {"b_comes_after_a",1}, + {"module_info",0}, + {"module_info",1}]} = do_expand("'ExpandTestCaps1':"), + {yes,"_",[]} = do_expand("'ExpandTestCaps1':a"), + {yes,[],[{"a_fun_name",1}, + {"a_less_fun_name",1}]} = do_expand("'ExpandTestCaps1':a_"), + {yes,[], + [{"'#weird-fun-name'",0}, + {"'Quoted_fun_name'",0}, + {"'Quoted_fun_too'",0}]} = do_expand("'ExpandTestCaps1':'"), + {yes,"uoted_fun_",[]} = do_expand("'ExpandTestCaps1':'Q"), + {yes,[], + [{"'Quoted_fun_name'",0}, + {"'Quoted_fun_too'",0}]} = do_expand("'ExpandTestCaps1':'Quoted_fun_"), + {yes,"weird-fun-name'()",[]} = do_expand("'ExpandTestCaps1':'#"), ok. + +do_expand(String) -> + edlin_expand:expand(lists:reverse(String)). diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index 0cbdf76270..0b4726c07a 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,7 +25,8 @@ variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1, otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1, - otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1]). + otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1, + otp_11728/1]). -export([epp_parse_erl_form/2]). @@ -67,7 +68,7 @@ all() -> {group, variable}, otp_4870, otp_4871, otp_5362, pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, overload_mac, otp_8388, otp_8470, otp_8503, otp_8562, - otp_8665, otp_8911, otp_10302, otp_10820]. + otp_8665, otp_8911, otp_10302, otp_10820, otp_11728]. groups() -> [{upcase_mac, [], [upcase_mac_1, upcase_mac_2]}, @@ -1387,6 +1388,31 @@ do_otp_10820(File, C, PC) -> true = test_server:stop_node(Node), ok. +otp_11728(doc) -> + ["OTP-11728. Bugfix circular macro."]; +otp_11728(suite) -> + []; +otp_11728(Config) when is_list(Config) -> + Dir = ?config(priv_dir, Config), + H = <<"-define(MACRO,[[]++?MACRO]).">>, + HrlFile = filename:join(Dir, "otp_11728.hrl"), + ok = file:write_file(HrlFile, H), + C = <<"-module(otp_11728). + -compile(export_all). + + -include(\"otp_11728.hrl\"). + + function_name()-> + A=?MACRO, % line 7 + ok">>, + ErlFile = filename:join(Dir, "otp_11728.erl"), + ok = file:write_file(ErlFile, C), + {ok, L} = epp:parse_file(ErlFile, [Dir], []), + true = lists:member({error,{7,epp,{circular,'MACRO',none}}}, L), + _ = file:delete(HrlFile), + _ = file:delete(ErlFile), + ok. + check(Config, Tests) -> eval_tests(Config, fun check_test/2, Tests). diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 48f50d5f43..1614a2722f 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -60,7 +60,8 @@ format_warn/1, on_load_successful/1, on_load_failing/1, too_many_arguments/1, - basic_errors/1,bin_syntax_errors/1 + basic_errors/1,bin_syntax_errors/1, + predef/1 ]). % Default timetrap timeout (set in init_per_testcase). @@ -87,7 +88,7 @@ all() -> otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, {group, on_load}, - too_many_arguments, basic_errors, bin_syntax_errors]. + too_many_arguments, basic_errors, bin_syntax_errors, predef]. groups() -> [{unused_vars_warn, [], @@ -3241,6 +3242,23 @@ bin_syntax_errors(Config) -> [] = run(Config, Ts), ok. +predef(doc) -> + "Predefined types: array(), digraph(), and so on"; +predef(suite) -> []; +predef(Config) when is_list(Config) -> + W = get_compilation_warnings(Config, "predef", []), + [] = W, + W2 = get_compilation_warnings(Config, "predef2", []), + [{7,erl_lint,{deprecated_type,{array,0},{array,array},"OTP 18.0"}}, + {12,erl_lint,{deprecated_type,{dict,0},{dict,dict},"OTP 18.0"}}, + {17,erl_lint,{deprecated_type,{digraph,0},{digraph,graph},"OTP 18.0"}}, + {27,erl_lint,{deprecated_type,{gb_set,0},{gb_sets,set},"OTP 18.0"}}, + {32,erl_lint,{deprecated_type,{gb_tree,0},{gb_trees,tree},"OTP 18.0"}}, + {37,erl_lint,{deprecated_type,{queue,0},{queue,queue},"OTP 18.0"}}, + {42,erl_lint,{deprecated_type,{set,0},{sets,set},"OTP 18.0"}}, + {47,erl_lint,{deprecated_type,{tid,0},{ets,tid},"OTP 18.0"}}] = W2, + ok. + run(Config, Tests) -> F = fun({N,P,Ws,E}, BadL) -> case catch run_test(Config, P, Ws) of @@ -3263,8 +3281,10 @@ get_compilation_warnings(Conf, Filename, Warnings) -> FileS = binary_to_list(Bin), {match,[{Start,Length}|_]} = re:run(FileS, "-module.*\\n"), Test = lists:nthtail(Start+Length, FileS), - {warnings, Ws} = run_test(Conf, Test, Warnings), - Ws. + case run_test(Conf, Test, Warnings) of + {warnings, Ws} -> Ws; + [] -> [] + end. %% Compiles a test module and returns the list of errors and warnings. diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl new file mode 100644 index 0000000000..c2364fd1c2 --- /dev/null +++ b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl @@ -0,0 +1,67 @@ +-module(predef). + +-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1, + queue/1, set/1, tid/0, tid2/0]). + +-export_type([array/0, digraph/0, gb_set/0]). + +%% Before R17B local re-definitions of pre-defined opaque types were +%% ignored but did not generate any warning. +-opaque array() :: atom(). +-opaque digraph() :: atom(). +-opaque gb_set() :: atom(). +-type dict() :: atom(). +-type gb_tree() :: atom(). +-type queue() :: atom(). +-type set() :: atom(). +-type tid() :: atom(). + +-spec array(array()) -> array:array(). + +array(A) -> + array:relax(A). + +-spec dict(dict()) -> dict:dict(). + +dict(D) -> + dict:store(1, a, D). + +-spec digraph(digraph()) -> [digraph:edge()]. + +digraph(G) -> + digraph:edges(G). + +-spec digraph2(digraph:graph()) -> [digraph:edge()]. + +digraph2(G) -> + digraph:edges(G). + +-spec gb_set(gb_set()) -> gb_sets:set(). + +gb_set(S) -> + gb_sets:balance(S). + +-spec gb_tree(gb_tree()) -> gb_trees:tree(). + +gb_tree(S) -> + gb_trees:balance(S). + +-spec queue(queue()) -> queue:queue(). + +queue(Q) -> + queue:reverse(Q). + +-spec set(set()) -> sets:set(). + +set(S) -> + sets:union([S]). + +-spec tid() -> tid(). + +tid() -> + ets:new(tid, []). + +-spec tid2() -> ets:tid(). + +tid2() -> + ets:new(tid, []). diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef2.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef2.erl new file mode 100644 index 0000000000..b1d941a49a --- /dev/null +++ b/lib/stdlib/test/erl_lint_SUITE_data/predef2.erl @@ -0,0 +1,56 @@ +-module(predef2). + +-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1, + queue/1, set/1, tid/0, tid2/0]). + +-export_type([array/0, digraph/0, gb_set/0]). + +-spec array(array()) -> array:array(). + +array(A) -> + array:relax(A). + +-spec dict(dict()) -> dict:dict(). + +dict(D) -> + dict:store(1, a, D). + +-spec digraph(digraph()) -> [digraph:edge()]. + +digraph(G) -> + digraph:edges(G). + +-spec digraph2(digraph:graph()) -> [digraph:edge()]. + +digraph2(G) -> + digraph:edges(G). + +-spec gb_set(gb_set()) -> gb_sets:set(). + +gb_set(S) -> + gb_sets:balance(S). + +-spec gb_tree(gb_tree()) -> gb_trees:tree(). + +gb_tree(S) -> + gb_trees:balance(S). + +-spec queue(queue()) -> queue:queue(). + +queue(Q) -> + queue:reverse(Q). + +-spec set(set()) -> sets:set(). + +set(S) -> + sets:union([S]). + +-spec tid() -> tid(). + +tid() -> + ets:new(tid, []). + +-spec tid2() -> ets:tid(). + +tid2() -> + ets:new(tid, []). diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 82c3e7ecaf..8dc8b2c291 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -75,6 +75,7 @@ -export([otp_9932/1]). -export([otp_9423/1]). -export([otp_10182/1]). +-export([ets_all/1]). -export([memory_check_summary/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -151,6 +152,7 @@ all() -> otp_10182, otp_9932, otp_9423, + ets_all, memory_check_summary]. % MUST BE LAST @@ -5565,7 +5567,19 @@ otp_10182(Config) when is_list(Config) -> ets:delete(Db), In = Out. - +%% Test that ets:all include/exclude tables that we know are created/deleted +ets_all(Config) when is_list(Config) -> + Pids = [spawn_link(fun() -> ets_all_run() end) || _ <- [1,2]], + receive after 3*1000 -> ok end, + [begin unlink(P), exit(P,kill) end || P <- Pids], + ok. + +ets_all_run() -> + Table = ets:new(undefined, []), + true = lists:member(Table, ets:all()), + ets:delete(Table), + false = lists:member(Table, ets:all()), + ets_all_run(). % diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl index 8fff5e2e05..53a34511d9 100644 --- a/lib/stdlib/test/stdlib_SUITE.erl +++ b/lib/stdlib/test/stdlib_SUITE.erl @@ -23,10 +23,6 @@ -include_lib("test_server/include/test_server.hrl"). -% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(1)). --define(application, stdlib). - % Test server specific exports -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). @@ -60,11 +56,8 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + Config. +end_per_testcase(_Case, _Config) -> ok. % @@ -78,17 +71,18 @@ app_test(Config) when is_list(Config) -> ?t:app_test(stdlib), ok. -%% Test that appup allows upgrade from/downgrade to a maximum of two -%% major releases back. +%% Test that appup allows upgrade from/downgrade to a maximum of one +%% major release back. appup_test(_Config) -> - do_appup_tests(create_test_vsns()). + appup_tests(stdlib,create_test_vsns(stdlib)). -do_appup_tests({[],[]}) -> +appup_tests(_App,{[],[]}) -> {skip,"no previous releases available"}; -do_appup_tests({OkVsns,NokVsns}) -> - application:load(stdlib), - {_,_,Vsn} = lists:keyfind(stdlib,1,application:loaded_applications()), - AppupFile = filename:join([code:lib_dir(stdlib),ebin,"stdlib.appup"]), +appup_tests(App,{OkVsns,NokVsns}) -> + application:load(App), + {_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()), + AppupFileName = atom_to_list(App) ++ ".appup", + AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]), {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile), ct:log("~p~n",[AppupScript]), ct:log("Testing ok versions: ~p~n",[OkVsns]), @@ -99,13 +93,12 @@ do_appup_tests({OkVsns,NokVsns}) -> check_appup(NokVsns,DownTo,error), ok. -create_test_vsns() -> +create_test_vsns(App) -> This = erlang:system_info(otp_release), FirstMajor = previous_major(This), SecondMajor = previous_major(FirstMajor), - ThirdMajor = previous_major(SecondMajor), - Ok = stdlib_vsn([FirstMajor,SecondMajor]), - Nok0 = stdlib_vsn([ThirdMajor]), + Ok = app_vsn(App,[FirstMajor]), + Nok0 = app_vsn(App,[SecondMajor]), Nok = case Ok of [Ok1|_] -> [Ok1 ++ ",1" | Nok0]; % illegal @@ -121,18 +114,36 @@ previous_major("r"++Rel) -> previous_major(Rel) -> integer_to_list(list_to_integer(Rel)-1). -stdlib_vsn([R|Rs]) -> - case test_server:is_release_available(R) of - true -> - {ok,N} = test_server:start_node(prevrel,peer,[{erl,[{release,R}]}]), - As = rpc:call(N,application,which_applications,[]), - {_,_,KV} = lists:keyfind(stdlib,1,As), - test_server:stop_node(N), - [KV|stdlib_vsn(Rs)]; +app_vsn(App,[R|Rs]) -> + OldRel = + case test_server:is_release_available(R) of + true -> + {release,R}; + false -> + case ct:get_config({otp_releases,list_to_atom(R)}) of + undefined -> + false; + Prog0 -> + case os:find_executable(Prog0) of + false -> + false; + Prog -> + {prog,Prog} + end + end + end, + case OldRel of false -> - stdlib_vsn(Rs) + app_vsn(App,Rs); + _ -> + {ok,N} = test_server:start_node(prevrel,peer,[{erl,[OldRel]}]), + _ = rpc:call(N,application,load,[App]), + As = rpc:call(N,application,loaded_applications,[]), + {_,_,V} = lists:keyfind(App,1,As), + test_server:stop_node(N), + [V|app_vsn(App,Rs)] end; -stdlib_vsn([]) -> +app_vsn(_App,[]) -> []. check_appup([Vsn|Vsns],Instrs,Expected) -> diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 2f4c9ac309..ed9decd4b5 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -453,33 +453,38 @@ %% </tr><tr> %% <td>list_comp</td> %% <td>macro</td> +%% <td>map_expr</td> +%% <td>map_field_assoc</td> +%% </tr><tr> +%% <td>map_field_exact</td> %% <td>match_expr</td> %% <td>module_qualifier</td> -%% </tr><tr> %% <td>named_fun_expr</td> +%% </tr><tr> %% <td>nil</td> %% <td>operator</td> %% <td>parentheses</td> -%% </tr><tr> %% <td>prefix_expr</td> +%% </tr><tr> %% <td>receive_expr</td> %% <td>record_access</td> %% <td>record_expr</td> -%% </tr><tr> %% <td>record_field</td> +%% </tr><tr> %% <td>record_index_expr</td> %% <td>rule</td> %% <td>size_qualifier</td> -%% </tr><tr> %% <td>string</td> +%% </tr><tr> %% <td>text</td> %% <td>try_expr</td> %% <td>tuple</td> -%% </tr><tr> %% <td>underscore</td> +%% </tr><tr> %% <td>variable</td> %% <td>warning_marker</td> %% <td></td> +%% <td></td> %% </tr> %% </table></center> %% @@ -520,6 +525,9 @@ %% @see list/2 %% @see list_comp/2 %% @see macro/2 +%% @see map_expr/2 +%% @see map_field_assoc/2 +%% @see map_field_exact/2 %% @see match_expr/2 %% @see module_qualifier/2 %% @see named_fun_expr/2 diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index e4665b99fc..2f0488abec 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -288,7 +288,7 @@ mapfoldl(_, S, []) -> %% %% @see //stdlib/sets --spec variables(erl_syntax:syntaxTree()) -> set(). +-spec variables(erl_syntax:syntaxTree()) -> sets:set(atom()). variables(Tree) -> variables(Tree, sets:new()). @@ -343,7 +343,7 @@ default_variable_name(N) -> %% %% @see new_variable_name/2 --spec new_variable_name(set()) -> atom(). +-spec new_variable_name(sets:set(atom())) -> atom(). new_variable_name(S) -> new_variable_name(fun default_variable_name/1, S). @@ -369,7 +369,7 @@ new_variable_name(S) -> %% @see //stdlib/sets %% @see //stdlib/random --spec new_variable_name(fun((integer()) -> atom()), set()) -> atom(). +-spec new_variable_name(fun((integer()) -> atom()), sets:set(atom())) -> atom(). new_variable_name(F, S) -> R = start_range(S), @@ -416,7 +416,7 @@ generate(_Key, Range) -> %% %% @see new_variable_name/1 --spec new_variable_names(integer(), set()) -> [atom()]. +-spec new_variable_names(integer(), sets:set(atom())) -> [atom()]. new_variable_names(N, S) -> new_variable_names(N, fun default_variable_name/1, S). @@ -432,7 +432,7 @@ new_variable_names(N, S) -> %% %% @see new_variable_name/2 --spec new_variable_names(integer(), fun((integer()) -> atom()), set()) -> +-spec new_variable_names(integer(), fun((integer()) -> atom()), sets:set(atom())) -> [atom()]. new_variable_names(N, F, S) when is_integer(N) -> diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl index 7444d8dc67..38e0c2099b 100644 --- a/lib/syntax_tools/src/erl_tidy.erl +++ b/lib/syntax_tools/src/erl_tidy.erl @@ -14,7 +14,7 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% @copyright 1999-2006 Richard Carlsson +%% @copyright 1999-2014 Richard Carlsson %% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== @@ -958,7 +958,7 @@ hidden_uses_2(Tree, Used) -> -record(env, {file :: file:filename(), module :: atom(), current :: fa(), - imports = dict:new() :: dict(), + imports = dict:new() :: dict:dict(atom(), atom()), context = normal :: context(), verbosity = 1 :: 0 | 1 | 2, quiet = false :: boolean(), @@ -970,12 +970,12 @@ hidden_uses_2(Tree, Used) -> old_guard_tests = false :: boolean()}). -record(st, {varc :: non_neg_integer(), - used = sets:new() :: set(), - imported :: set(), - vars :: set(), - functions :: set(), + used = sets:new() :: sets:set({atom(), arity()}), + imported :: sets:set({atom(), arity()}), + vars :: sets:set(atom()), + functions :: sets:set({atom(), arity()}), new_forms = [] :: [erl_syntax:syntaxTree()], - rename :: dict()}). + rename :: dict:dict(mfa(), {atom(), atom()})}). visit_used(Names, Defs, Roots, Imports, Module, Opts) -> File = proplists:get_value(file, Opts, ""), diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index 19b1cd592f..e6aff7b20a 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -14,7 +14,7 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% @copyright 1998-2006 Richard Carlsson +%% @copyright 1998-2014 Richard Carlsson %% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== @@ -695,7 +695,7 @@ merge_files1(Files, Opts) -> preserved :: boolean(), no_headers :: boolean(), notes :: notes(), - redirect :: dict(), % = dict(atom(), atom()) + redirect :: dict:dict(atom(), atom()), no_imports :: ordsets:ordset(atom()), options :: [option()] }). @@ -727,7 +727,7 @@ merge_sources(Name, Sources, Opts) -> %% Data structure for keeping state during transformation. --record(state, {export :: set()}). +-record(state, {export :: sets:set(atom(), arity())}). state__add_export(Name, Arity, S) -> S#state{export = sets:add_element({Name, Arity}, @@ -1039,7 +1039,7 @@ make_stub(M, Map, Env) -> -type atts() :: 'delete' | 'kill'. -type file_atts() :: 'delete' | 'keep' | 'kill'. --record(filter, {records :: set(), +-record(filter, {records :: sets:set(atom()), file_attributes :: file_atts(), attributes :: atts()}). @@ -1588,17 +1588,17 @@ alias_expansions_2(Modules, Table) -> -record(code, {module :: atom(), target :: atom(), - sources :: set(), % set(atom()), - static :: set(), % set(atom()), - safe :: set(), % set(atom()), + sources :: sets:set(atom()), + static :: sets:set(atom()), + safe :: sets:set(atom()), preserved :: boolean(), no_headers :: boolean(), notes :: notes(), map :: map_fun(), renaming :: fun((atom()) -> map_fun()), - expand :: dict(), % = dict({atom(), integer()}, - % {atom(), {atom(), integer()}}) - redirect :: dict() % = dict(atom(), atom()) + expand :: dict:dict({atom(), integer()}, + {atom(), {atom(), integer()}}), + redirect :: dict:dict(atom(), atom()) }). %% `Trees' must be a list of syntax trees of type `form_list'. The diff --git a/lib/syntax_tools/src/syntax_tools.appup.src b/lib/syntax_tools/src/syntax_tools.appup.src index 54a63833e6..89c25d14d7 100644 --- a/lib/syntax_tools/src/syntax_tools.appup.src +++ b/lib/syntax_tools/src/syntax_tools.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, syntax_tools}]}], + [{<<".*">>,[{restart_application, syntax_tools}]}] +}. diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index b673b70a95..d4c54a72aa 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -24,12 +24,12 @@ init_per_group/2,end_per_group/2]). %% Test cases --export([smoke_test/1,revert/1]). +-export([app_test/1,appup_test/1,smoke_test/1,revert/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [smoke_test,revert]. + [app_test,appup_test,smoke_test,revert]. groups() -> []. @@ -46,6 +46,11 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +app_test(Config) when is_list(Config) -> + ok = ?t:app_test(syntax_tools). + +appup_test(Config) when is_list(Config) -> + ok = ?t:appup_test(syntax_tools). %% Read and parse all source in the OTP release. smoke_test(Config) when is_list(Config) -> diff --git a/lib/test_server/doc/src/test_server.xml b/lib/test_server/doc/src/test_server.xml index 4a47a8f45c..ed5569e1fe 100644 --- a/lib/test_server/doc/src/test_server.xml +++ b/lib/test_server/doc/src/test_server.xml @@ -625,6 +625,31 @@ Only valid for peer nodes. Note that slave nodes always </desc> </func> <func> + <name>appup_test(App) -> ok | test_server:fail()</name> + <fsummary>Checks an applications .appup file for obvious errors</fsummary> + <type> + <v>App = term()</v> + <d>The name of the application to test</d> + </type> + <desc> + <p>Checks an applications .appup file for obvious errors. + The following is checked: + </p> + <list type="bulleted"> + <item>syntax + </item> + <item>that .app file version and .appup file version match + </item> + <item>for non-library applications: validity of high-level upgrade + instructions, specifying no instructions is explicitly allowed + (in this case the application is not upgradeable)</item> + <item>for library applications: that there is exactly one wildcard + regexp clause restarting the application when upgrading or + downgrading from any version</item> + </list> + </desc> + </func> + <func> <name>comment(Comment) -> ok</name> <fsummary>Print a comment on the HTML result page</fsummary> <type> diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in index 067663feb4..cd723bcd4d 100644 --- a/lib/test_server/src/configure.in +++ b/lib/test_server/src/configure.in @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script for Erlang. dnl dnl %CopyrightBegin% dnl -dnl Copyright Ericsson AB 1997-2013. All Rights Reserved. +dnl Copyright Ericsson AB 1997-2014. All Rights Reserved. dnl dnl The contents of this file are subject to the Erlang Public License, dnl Version 1.1, (the "License"); you may not use this file except in @@ -170,7 +170,30 @@ case $system in fi SHLIB_EXTRACT_ALL="" ;; - *-netbsd*|*-freebsd*|*-openbsd*|*-dragonfly*) + *-openbsd*) + # Not available on all versions: check for include file. + AC_CHECK_HEADER(dlfcn.h, [ + SHLIB_CFLAGS="-fpic" + SHLIB_LD="${CC}" + SHLIB_LDFLAGS="$LDFLAGS -shared" + SHLIB_SUFFIX=".so" + if test X${enable_m64_build} = Xyes; then + AC_MSG_ERROR(don't know how to link 64-bit dynamic drivers) + fi + if test X${enable_m32_build} = Xyes; then + AC_MSG_ERROR(don't know how to link 32-bit dynamic drivers) + fi + ], [ + # No dynamic loading. + SHLIB_CFLAGS="" + SHLIB_LD="ld" + SHLIB_LDFLAGS="" + SHLIB_SUFFIX="" + AC_MSG_ERROR(don't know how to compile and link dynamic drivers) + ]) + SHLIB_EXTRACT_ALL="" + ;; + *-netbsd*|*-freebsd*|*-dragonfly*) # Not available on all versions: check for include file. AC_CHECK_HEADER(dlfcn.h, [ SHLIB_CFLAGS="-fpic" diff --git a/lib/test_server/src/test_server.appup.src b/lib/test_server/src/test_server.appup.src index 0fbe5f23f7..42c6fe2e46 100644 --- a/lib/test_server/src/test_server.appup.src +++ b/lib/test_server/src/test_server.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}.
\ No newline at end of file +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, test_server}]}], + [{<<".*">>,[{restart_application, test_server}]}] +}. diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index d367e7b5d6..82672521f7 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -40,7 +40,7 @@ -export([call_crash/3,call_crash/4,call_crash/5]). -export([temp_name/1]). -export([start_node/3, stop_node/1, wait_for_node/1, is_release_available/1]). --export([app_test/1, app_test/2]). +-export([app_test/1, app_test/2, appup_test/1]). -export([is_native/1]). -export([comment/1, make_priv_dir/0]). -export([os_type/0]). @@ -2448,8 +2448,11 @@ app_test(App) -> app_test(App, Mode) -> test_server_sup:app_test(App, Mode). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% appup_test/1 +%% +appup_test(App) -> + test_server_sup:appup_test(App). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% is_native(Mod) -> true | false diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl index 70ead42b1b..3cfa84a52f 100644 --- a/lib/test_server/src/test_server_sup.erl +++ b/lib/test_server/src/test_server_sup.erl @@ -30,7 +30,8 @@ framework_call/2,framework_call/3,framework_call/4, format_loc/1, util_start/0, util_stop/0, unique_name/0, - call_trace/1]). + call_trace/1, + appup_test/1]). -include("test_server_internal.hrl"). -define(crash_dump_tar,"crash_dumps.tar.gz"). -define(src_listing_ext, ".src.html"). @@ -265,6 +266,249 @@ app_check_export_all([Mod|Mods]) -> end end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% appup_test/1 +%% +%% Checks one applications .appup file for obvious errors. +%% Checks.. +%% * .. syntax +%% * .. that version in app file matches appup file version +%% * .. validity of appup instructions +%% +%% For library application this function checks that the proper +%% 'restart_application' upgrade and downgrade clauses exist. +appup_test(Application) -> + case is_app(Application) of + {ok, AppFile} -> + case is_appup(Application, proplists:get_value(vsn, AppFile)) of + {ok, Up, Down} -> + StartMod = proplists:get_value(mod, AppFile), + Modules = proplists:get_value(modules, AppFile), + do_appup_tests(StartMod, Application, Up, Down, Modules); + Error -> + test_server:fail(Error) + end; + Error -> + test_server:fail(Error) + end. + +is_appup(Application, Version) -> + AppupFile = atom_to_list(Application) ++ ".appup", + AppupPath = filename:join([code:lib_dir(Application), "ebin", AppupFile]), + case file:consult(AppupPath) of + {ok, [{Version, Up, Down}]} when is_list(Up), is_list(Down) -> + {ok, Up, Down}; + _ -> + test_server:format( + minor, + "Application upgrade (.appup) file not found, " + "or it has very bad syntax.~n"), + {error, appup_not_readable} + end. + +do_appup_tests(undefined, Application, Up, Down, _Modules) -> + %% library application + case Up of + [{<<".*">>, [{restart_application, Application}]}] -> + case Down of + [{<<".*">>, [{restart_application, Application}]}] -> + ok; + _ -> + test_server:format( + minor, + "Library application needs restart_application " + "downgrade instruction.~n"), + {error, library_downgrade_instruction_malformed} + end; + _ -> + test_server:format( + minor, + "Library application needs restart_application " + "upgrade instruction.~n"), + {error, library_upgrade_instruction_malformed} + end; +do_appup_tests(_, _Application, Up, Down, Modules) -> + %% normal application + case check_appup_clauses_plausible(Up, up, Modules) of + ok -> + case check_appup_clauses_plausible(Down, down, Modules) of + ok -> + test_server:format(minor, "OK~n"); + Error -> + test_server:format(minor, "ERROR ~p~n", [Error]), + test_server:fail(Error) + end; + Error -> + test_server:format(minor, "ERROR ~p~n", [Error]), + test_server:fail(Error) + end. + +check_appup_clauses_plausible([], _Direction, _Modules) -> + ok; +check_appup_clauses_plausible([{Re, Instrs} | Rest], Direction, Modules) + when is_binary(Re) -> + case re:compile(Re) of + {ok, _} -> + case check_appup_instructions(Instrs, Direction, Modules) of + ok -> + check_appup_clauses_plausible(Rest, Direction, Modules); + Error -> + Error + end; + {error, Error} -> + {error, {version_regex_malformed, Re, Error}} + end; +check_appup_clauses_plausible([{V, Instrs} | Rest], Direction, Modules) + when is_list(V) -> + case check_appup_instructions(Instrs, Direction, Modules) of + ok -> + check_appup_clauses_plausible(Rest, Direction, Modules); + Error -> + Error + end; +check_appup_clauses_plausible(Clause, _Direction, _Modules) -> + {error, {clause_malformed, Clause}}. + +check_appup_instructions(Instrs, Direction, Modules) -> + case check_instructions(Direction, Instrs, Instrs, [], [], Modules) of + {_Good, []} -> + ok; + {_, Bad} -> + {error, {bad_instructions, Bad}} + end. + +check_instructions(_, [], _, Good, Bad, _) -> + {lists:reverse(Good), lists:reverse(Bad)}; +check_instructions(UpDown, [Instr | Rest], All, Good, Bad, Modules) -> + case catch check_instruction(UpDown, Instr, All, Modules) of + ok -> + check_instructions(UpDown, Rest, All, [Instr | Good], Bad, Modules); + {error, Reason} -> + NewBad = [{Instr, Reason} | Bad], + check_instructions(UpDown, Rest, All, Good, NewBad, Modules) + end. + +check_instruction(up, {add_module, Module}, _, Modules) -> + %% A new module is added + check_module(Module, Modules); +check_instruction(down, {add_module, Module}, _, Modules) -> + %% An old module is re-added + case (catch check_module(Module, Modules)) of + {error, {unknown_module, Module, Modules}} -> ok; + ok -> throw({error, {existing_readded_module, Module}}) + end; +check_instruction(_, {load_module, Module}, _, Modules) -> + check_module(Module, Modules); +check_instruction(_, {load_module, Module, DepMods}, _, Modules) -> + check_module(Module, Modules), + check_depend(DepMods); +check_instruction(_, {load_module, Module, Pre, Post, DepMods}, _, Modules) -> + check_module(Module, Modules), + check_depend(DepMods), + check_purge(Pre), + check_purge(Post); +check_instruction(up, {delete_module, Module}, _, Modules) -> + case (catch check_module(Module, Modules)) of + {error, {unknown_module, Module, Modules}} -> + ok; + ok -> + throw({error,{existing_module_deleted, Module}}) + end; +check_instruction(down, {delete_module, Module}, _, Modules) -> + check_module(Module, Modules); +check_instruction(_, {update, Module}, _, Modules) -> + check_module(Module, Modules); +check_instruction(_, {update, Module, supervisor}, _, Modules) -> + check_module(Module, Modules); +check_instruction(_, {update, Module, DepMods}, _, Modules) + when is_list(DepMods) -> + check_module(Module, Modules); +check_instruction(_, {update, Module, Change}, _, Modules) -> + check_module(Module, Modules), + check_change(Change); +check_instruction(_, {update, Module, Change, DepMods}, _, Modules) -> + check_module(Module, Modules), + check_change(Change), + check_depend(DepMods); +check_instruction(_, {update, Module, Change, Pre, Post, DepMods}, _, Modules) -> + check_module(Module, Modules), + check_change(Change), + check_purge(Pre), + check_purge(Post), + check_depend(DepMods); +check_instruction(_, + {update, Module, Timeout, Change, Pre, Post, DepMods}, + _, + Modules) -> + check_module(Module, Modules), + check_timeout(Timeout), + check_change(Change), + check_purge(Pre), + check_purge(Post), + check_depend(DepMods); +check_instruction(_, + {update, Module, ModType, Timeout, Change, Pre, Post, DepMods}, + _, + Modules) -> + check_module(Module, Modules), + check_mod_type(ModType), + check_timeout(Timeout), + check_change(Change), + check_purge(Pre), + check_purge(Post), + check_depend(DepMods); +check_instruction(_, {restart_application, Application}, _, _) -> + check_application(Application); +check_instruction(_, {remove_application, Application}, _, _) -> + check_application(Application); +check_instruction(_, {add_application, Application}, _, _) -> + check_application(Application); +check_instruction(_, {add_application, Application, Type}, _, _) -> + check_application(Application), + check_restart_type(Type); +check_instruction(_, Instr, _, _) -> + throw({error, {low_level_or_invalid_instruction, Instr}}). + +check_module(Module, Modules) when is_atom(Module) -> + case {is_atom(Module), lists:member(Module, Modules)} of + {true, true} -> ok; + {true, false} -> throw({error, {unknown_module, Module}}); + {false, _} -> throw({error, {bad_module, Module}}) + end. + +check_application(App) -> + case is_atom(App) of + true -> ok; + false -> throw({error, {bad_application, App}}) + end. + +check_depend(Dep) when is_list(Dep) -> ok; +check_depend(Dep) -> throw({error, {bad_depend, Dep}}). + +check_restart_type(permanent) -> ok; +check_restart_type(transient) -> ok; +check_restart_type(temporary) -> ok; +check_restart_type(load) -> ok; +check_restart_type(none) -> ok; +check_restart_type(Type) -> throw({error, {bad_restart_type, Type}}). + +check_timeout(T) when is_integer(T), T > 0 -> ok; +check_timeout(default) -> ok; +check_timeout(infinity) -> ok; +check_timeout(T) -> throw({error, {bad_timeout, T}}). + +check_mod_type(static) -> ok; +check_mod_type(dynamic) -> ok; +check_mod_type(Type) -> throw({error, {bad_mod_type, Type}}). + +check_purge(soft_purge) -> ok; +check_purge(brutal_purge) -> ok; +check_purge(Purge) -> throw({error, {bad_purge, Purge}}). + +check_change(soft) -> ok; +check_change({advanced, _}) -> ok; +check_change(Change) -> throw({error, {bad_change, Change}}). + %% Given two sorted lists, L1 and L2, returns {NotInL2, NotInL1}, %% NotInL2 is the elements of L1 which don't occurr in L2, %% NotInL1 is the elements of L2 which don't ocurr in L1. diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index 3a868f1300..f007f780eb 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -664,6 +664,7 @@ resulting regexp is surrounded by \\_< and \\_>." "is_function" "is_integer" "is_list" + "is_map" "is_number" "is_pid" "is_port" @@ -715,7 +716,8 @@ resulting regexp is surrounded by \\_< and \\_>." "pos_integer" "string" "term" - "timeout") + "timeout" + "map") "Erlang type specs types")) (eval-and-compile @@ -772,6 +774,7 @@ resulting regexp is surrounded by \\_< and \\_>." "is_function" "is_integer" "is_list" + "is_map" "is_number" "is_pid" "is_port" @@ -791,6 +794,7 @@ resulting regexp is surrounded by \\_< and \\_>." "list_to_tuple" "load_module" "make_ref" + "map_size" "max" "min" "module_loaded" diff --git a/lib/tools/src/tools.appup.src b/lib/tools/src/tools.appup.src index 8de1ec76c9..9a27456a81 100644 --- a/lib/tools/src/tools.appup.src +++ b/lib/tools/src/tools.appup.src @@ -1,19 +1,21 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2001-2009. All Rights Reserved. -# -# The contents of this file are subject to the Erlang Public License, -# Version 1.1, (the "License"); you may not use this file except in -# compliance with the License. You should have received a copy of the -# Erlang Public License along with this software. If not, it can be -# retrieved online at http://www.erlang.org/. -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -# the License for the specific language governing rights and limitations -# under the License. -# -# %CopyrightEnd% -# -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, tools}]}], + [{<<".*">>,[{restart_application, tools}]}] +}. diff --git a/lib/tools/src/xref_compiler.erl b/lib/tools/src/xref_compiler.erl index f0fed502a5..c4b5c04c12 100644 --- a/lib/tools/src/xref_compiler.erl +++ b/lib/tools/src/xref_compiler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -885,7 +885,7 @@ evaluate([pop | P], T, [_ | S]) -> evaluate([], T, [R]) -> {T, R}. -%% (PossibleGraph, 1 | -1, dict()) -> dict() +%% (PossibleGraph, 1 | -1, dict:dict()) -> dict:dict() %% Use the same table for everything... Here: Reference counters for digraphs. update_graph_counter(Value, Inc, T) -> case catch digraph:info(Value) of diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl index 7b72165e6f..49c397a140 100644 --- a/lib/tools/src/xref_utils.erl +++ b/lib/tools/src/xref_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -437,7 +437,7 @@ regexpr({ModExpr, FunExpr, ArityExpr}, Var) -> V2 end. -%% -> digraph() +%% -> digraph:graph() relation_to_graph(S) -> G = digraph:new(), Fun = fun({From, To}) -> diff --git a/lib/tools/test/eprof_SUITE.erl b/lib/tools/test/eprof_SUITE.erl index 26685a6a84..1227d5b841 100644 --- a/lib/tools/test/eprof_SUITE.erl +++ b/lib/tools/test/eprof_SUITE.erl @@ -127,6 +127,14 @@ basic(Config) when is_list(Config) -> ok. basic_option(Config) when is_list(Config) -> + %% Eprof is not supported on native-compile code. + case lists:module_info(native_addresses) of + [] -> basic_option_1(Config); + [_|_] -> {skip,"lists is native-compiled"} + end. + +basic_option_1(Config) -> + %% load eprof_test and change directory {ok, OldCurDir} = file:get_cwd(), diff --git a/lib/tools/test/tools_SUITE.erl b/lib/tools/test/tools_SUITE.erl index ea3f59dbe1..e3582b995b 100644 --- a/lib/tools/test/tools_SUITE.erl +++ b/lib/tools/test/tools_SUITE.erl @@ -30,12 +30,12 @@ -export([init_per_testcase/2, end_per_testcase/2]). %% Test cases must be exported. --export([app_test/1]). +-export([app_test/1, appup_test/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app_test]. + [app_test, appup_test]. groups() -> []. @@ -71,3 +71,7 @@ app_test(suite) -> []; app_test(Config) when is_list(Config) -> ?line ?t:app_test(tools, tolerant). + +%% Test that the .appup file does not contain any `basic' errors +appup_test(Config) when is_list(Config) -> + ok = ?t:appup_test(tools). diff --git a/lib/typer/src/typer.appup.src b/lib/typer/src/typer.appup.src index 54a63833e6..bebe7a159c 100644 --- a/lib/typer/src/typer.appup.src +++ b/lib/typer/src/typer.appup.src @@ -1 +1,21 @@ -{"%VSN%",[],[]}. +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +{"%VSN%", + [{<<".*">>,[{restart_application, typer}]}], + [{<<".*">>,[{restart_application, typer}]}] +}. diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 20ae4d6066..572bf24ca4 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1094,7 +1094,7 @@ rcv_ext_types(Self, ExtTypes) -> %% specialized for the uses in this module %%-------------------------------------------------------------------- --type map_dict() :: dict(). +-type map_dict() :: dict:dict(). -spec map__new() -> map_dict(). map__new() -> diff --git a/lib/typer/test/Makefile b/lib/typer/test/Makefile new file mode 100644 index 0000000000..d6dd22b6cf --- /dev/null +++ b/lib/typer/test/Makefile @@ -0,0 +1,65 @@ +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + typer_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/typer_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) $(GEN_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) typer.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + +release_docs_spec: diff --git a/lib/typer/test/typer.spec b/lib/typer/test/typer.spec new file mode 100644 index 0000000000..79f51b6781 --- /dev/null +++ b/lib/typer/test/typer.spec @@ -0,0 +1 @@ +{suites,"../typer_test",all}. diff --git a/lib/typer/test/typer_SUITE.erl b/lib/typer/test/typer_SUITE.erl new file mode 100644 index 0000000000..99c4facbad --- /dev/null +++ b/lib/typer/test/typer_SUITE.erl @@ -0,0 +1,56 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(typer_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + case application:ensure_all_started(typer) of + {ok, Apps} -> + [application:stop(App) || App <- lists:reverse(Apps)], + [app, appup]; + _ -> + [appup] + end. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the typer app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(typer). + +appup() -> + [{doc, "Test that the typer appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(typer). diff --git a/lib/webtool/src/Makefile b/lib/webtool/src/Makefile index f28c777240..af565c8895 100644 --- a/lib/webtool/src/Makefile +++ b/lib/webtool/src/Makefile @@ -66,7 +66,7 @@ ERL_COMPILE_FLAGS += +warn_obsolete_guard debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) clean: - rm -f $(TARGET_FILES) $(APP_TARGET) $(APP_TARGET) + rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) rm -f core docs: diff --git a/lib/webtool/src/webtool.appup.src b/lib/webtool/src/webtool.appup.src index 7a435e9b22..9e6f4b9b5b 100644 --- a/lib/webtool/src/webtool.appup.src +++ b/lib/webtool/src/webtool.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,5 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% -{"%VSN%",[],[]}. +{"%VSN%", + [{<<".*">>,[{restart_application, webtool}]}], + [{<<".*">>,[{restart_application, webtool}]}] +}. diff --git a/lib/webtool/test/Makefile b/lib/webtool/test/Makefile new file mode 100644 index 0000000000..93aa1c09eb --- /dev/null +++ b/lib/webtool/test/Makefile @@ -0,0 +1,65 @@ +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + webtool_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/webtool_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) $(GEN_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) webtool.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + +release_docs_spec: diff --git a/lib/webtool/test/webtool.spec b/lib/webtool/test/webtool.spec new file mode 100644 index 0000000000..134e6ed40c --- /dev/null +++ b/lib/webtool/test/webtool.spec @@ -0,0 +1 @@ +{suites,"../webtool_test",all}. diff --git a/lib/webtool/test/webtool_SUITE.erl b/lib/webtool/test/webtool_SUITE.erl new file mode 100644 index 0000000000..64ff221a1b --- /dev/null +++ b/lib/webtool/test/webtool_SUITE.erl @@ -0,0 +1,50 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(webtool_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [app, appup]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the webtool app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(webtool). + +appup() -> + [{doc, "Test that the webtool appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(webtool). diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in index 93a191378f..4a7342f714 100644 --- a/lib/wx/c_src/Makefile.in +++ b/lib/wx/c_src/Makefile.in @@ -80,8 +80,8 @@ TARGET_DIR = ../priv/$(SYS_TYPE) COMMON_CFLAGS = @DEFS@ $(ERL_INCS) CC = @CC@ -CPP = @CXX@ -LD = $(CPP) +CXX = @CXX@ +LD = $(CXX) LDFLAGS = @LDFLAGS@ RESCOMP = @WX_RESCOMP@ @@ -111,7 +111,7 @@ GL_LIBS = @GL_LIBS@ CC_O = $(V_CC) -c $(CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS) OBJC_CC_O = $(OBJC_CC) -c $(CFLAGS) $(OBJC_CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS) -CPP_O = $(V_CPP) -c $(CXX_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS) +CXX_O = $(V_CXX) -c $(CXX_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS) # Targets @@ -141,7 +141,7 @@ $(WX_OBJECTS): $(GENERATED_H) $(GENERAL_H) $(SYS_TYPE)/%.o: %.cpp $(V_at)mkdir -p $(SYS_TYPE) - $(CPP_O) $< -o $@ + $(CXX_O) $< -o $@ $(SYS_TYPE)/%.o: %.c $(V_at)mkdir -p $(SYS_TYPE) @@ -153,7 +153,7 @@ $(SYS_TYPE)/wxe_ps_init.o: wxe_ps_init.c $(SYS_TYPE)/%.o: gen/%.cpp $(V_at)mkdir -p $(SYS_TYPE) - $(CPP_O) $< -o $@ + $(CXX_O) $< -o $@ $(SYS_TYPE)/%.o: gen/%.c $(V_at)mkdir -p $(SYS_TYPE) diff --git a/lib/wx/src/wx.appup.src b/lib/wx/src/wx.appup.src index 1102af612e..311c0c0f52 100644 --- a/lib/wx/src/wx.appup.src +++ b/lib/wx/src/wx.appup.src @@ -1,8 +1,7 @@ -%% This is an -*- erlang -*- file. -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,8 +15,7 @@ %% under the License. %% %% %CopyrightEnd% - {"%VSN%", - [ ], - [ ] + [{<<".*">>,[{restart_application, wx}]}], + [{<<".*">>,[{restart_application, wx}]}] }. diff --git a/lib/wx/test/wx_app_SUITE.erl b/lib/wx/test/wx_app_SUITE.erl index 162923eaa3..6331180ece 100644 --- a/lib/wx/test/wx_app_SUITE.erl +++ b/lib/wx/test/wx_app_SUITE.erl @@ -26,6 +26,7 @@ -compile(export_all). -include("wx_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). t() -> wx_test_lib:t(?MODULE). @@ -50,7 +51,7 @@ end_per_testcase(Func,Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [fields, modules, exportall, app_depend, undef_funcs]. + [fields, modules, exportall, app_depend, undef_funcs, appup]. groups() -> []. @@ -281,3 +282,9 @@ key1search(Key, L) -> {value, {Key, Value}} -> Value end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Test that the wx appup file is ok +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(wx). diff --git a/lib/xmerl/src/xmerl.appup.src b/lib/xmerl/src/xmerl.appup.src index 0d8aa4eb04..0a84966576 100644 --- a/lib/xmerl/src/xmerl.appup.src +++ b/lib/xmerl/src/xmerl.appup.src @@ -1,14 +1,21 @@ +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% {"%VSN%", - [ - {"1.1.11", - [ - ] - } - ], - [ - {"1.1.11", - [ - ] - } - ] + [{<<".*">>,[{restart_application, xmerl}]}], + [{<<".*">>,[{restart_application, xmerl}]}] }. diff --git a/lib/xmerl/test/xmerl_appup_test.erl b/lib/xmerl/test/xmerl_appup_test.erl index 80c8d8e4fd..ff6b368bcc 100644 --- a/lib/xmerl/test/xmerl_appup_test.erl +++ b/lib/xmerl/test/xmerl_appup_test.erl @@ -23,20 +23,7 @@ -module(xmerl_appup_test). -compile(export_all). - -%-include("megaco_test_lib.hrl"). - - -%t() -> megaco_test_lib:t(?MODULE). -%t(Case) -> megaco_test_lib:t({?MODULE, Case}). - - -%% Test server callbacks -% init_per_testcase(Case, Config) -> -% megaco_test_lib:init_per_testcase(Case, Config). - -% end_per_testcase(Case, Config) -> -% megaco_test_lib:end_per_testcase(Case, Config). +-include_lib("common_test/include/ct.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -58,14 +45,7 @@ end_per_group(_GroupName, Config) -> init_per_suite(suite) -> []; init_per_suite(doc) -> []; init_per_suite(Config) when is_list(Config) -> - AppFile = file_name(xmerl, ".app"), - AppupFile = file_name(xmerl, ".appup"), - [{app_file, AppFile}, {appup_file, AppupFile}|Config]. - - -file_name(App, Ext) -> - LibDir = code:lib_dir(App), - filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]). + Config. end_per_suite(suite) -> []; @@ -76,317 +56,6 @@ end_per_suite(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -appup(suite) -> - []; -appup(doc) -> - "perform a simple check of the appup file"; +%% perform a simple check of the appup file appup(Config) when is_list(Config) -> - AppupFile = key1search(appup_file, Config), - AppFile = key1search(app_file, Config), - Modules = modules(AppFile), - check_appup(AppupFile, Modules). - -modules(File) -> - case file:consult(File) of - {ok, [{application,xmerl,Info}]} -> - case lists:keysearch(modules,1,Info) of - {value, {modules, Modules}} -> - Modules; - false -> - fail({bad_appinfo, Info}) - end; - Error -> - fail({bad_appfile, Error}) - end. - - -check_appup(AppupFile, Modules) -> - case file:consult(AppupFile) of - {ok, [{V, UpFrom, DownTo}]} -> - io:format("V= ~p, UpFrom= ~p, DownTo= ~p, Modules= ~p~n", - [V, UpFrom, DownTo, Modules]), - check_appup(V, UpFrom, DownTo, Modules); - Else -> - fail({bad_appupfile, Else}) - end. - - -check_appup(V, UpFrom, DownTo, Modules) -> - check_version(V), - check_depends(up, UpFrom, Modules), - check_depends(down, DownTo, Modules), - ok. - - -check_depends(_, [], _) -> - ok; -check_depends(UpDown, [Dep|Deps], Modules) -> - check_depend(UpDown, Dep, Modules), - check_depends(UpDown, Deps, Modules). - - -check_depend(up,I={add_application, _App}, Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [up,I , Modules]), - ok; -check_depend(down,I={remove_application, _App}, Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [down,I , Modules]), - ok; -check_depend(UpDown, {V, Instructions}, Modules) -> - d("check_instructions(~w) -> entry with" - "~n V: ~p" - "~n Modules: ~p", [UpDown, V, Modules]), - check_version(V), - case check_instructions(UpDown, - Instructions, Instructions, [], [], Modules) of - {_Good, []} -> - ok; - {_, Bad} -> - fail({bad_instructions, Bad, UpDown}) - end. - - -check_instructions(_, [], _, Good, Bad, _) -> - {lists:reverse(Good), lists:reverse(Bad)}; -check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instr: ~p", [UpDown,Instr]), - case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of - ok -> - check_instructions(UpDown, Instrs, AllInstr, - [Instr|Good], Bad, Modules); - {error, Reason} -> - d("check_instructions(~w) -> bad instruction: " - "~n Reason: ~p", [UpDown,Reason]), - check_instructions(UpDown, Instrs, AllInstr, Good, - [{Instr, Reason}|Bad], Modules) - end. - -%% A new module is added -check_instruction(up, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when up-add_module instruction with" - "~n Module: ~p", [Module]), - check_module(Module, Modules); - -%% An old module is re-added -check_instruction(down, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when down-add_module instruction with" - "~n Module: ~p", [Module]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - ok; - ok -> - local_error({existing_readded_module, Module}) - end; - -%% Removing a module on upgrade: -%% - the module has been removed from the app-file. -%% - check that no module depends on this (removed) module -check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - d("check_instruction -> entry when up-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - check_purge(Pre), - check_purge(Post); - ok -> - local_error({existing_removed_module, Module}) - end; - -%% Removing a module on downgrade: the module exist -%% in the app-file. -check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - d("check_instruction -> entry when down-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - ok -> - check_purge(Pre), - check_purge(Post), - check_no_remove_depends(Module, AllInstr); - {error, {unknown_module, Module, Modules}} -> - local_error({nonexisting_removed_module, Module}) - end; - -check_instruction(_, {load_module, Module, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - d("check_instruction -> entry when load_module instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {update, Module, Change, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - d("check_instruction -> entry when update instruction with" - "~n Module: ~p" - "~n Change: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Change, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_change(Change), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, Instr, _AllInstr, _Modules) -> - d("check_instruction -> entry when unknown instruction with" - "~n Instr: ~p", [Instr]), - local_error({error, {unknown_instruction, Instr}}). - - -%% If Module X depends on Module Y, then module Y must have an update -%% instruction of some sort (otherwise the depend is faulty). -updated_modules([], Modules) -> - d("update_modules -> entry when done with" - "~n Modules: ~p", [Modules]), - Modules; -updated_modules([Instr |Instrs], Modules) -> - d("update_modules -> entry with" - "~n Instr: ~p" - "~n Modules: ~p", [Instr,Modules]), - Module = instruction_module(Instr), - d("update_modules -> Module: ~p", [Module]), - updated_modules(Instrs, [Module|Modules]). - -instruction_module({add_module, Module}) -> - Module; -instruction_module({remove, {Module, _, _}}) -> - Module; -instruction_module({load_module, Module, _, _, _}) -> - Module; -instruction_module({update, Module, _, _, _, _}) -> - Module; -instruction_module(Instr) -> - d("instruction_module -> entry when unknown instruction with" - "~n Instr: ~p", [Instr]), - local_error({error, {unknown_instruction, Instr}}). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -check_version(V) when is_list(V) -> - ok; -check_version(V) -> - local_error({bad_version, V}). - - -check_module(M, Modules) when is_atom(M) -> - case lists:member(M,Modules) of - true -> - ok; - false -> - local_error({unknown_module, M, Modules}) - end; -check_module(M, _) -> - local_error({bad_module, M}). - - -check_module_depend(M, [], _) when is_atom(M) -> - d("check_module_depend -> entry with" - "~n M: ~p", [M]), - ok; -check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) -> - d("check_module_depend -> entry with" - "~n M: ~p" - "~n Deps: ~p" - "~n Modules: ~p", [M, Deps, Modules]), - case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of - [] -> - ok; - Unknown -> - local_error({unknown_depend_modules, Unknown}) - end; -check_module_depend(_M, D, _Modules) -> - d("check_module_depend -> entry when bad depend with" - "~n D: ~p", [D]), - local_error({bad_depend, D}). - - -check_no_remove_depends(_Module, []) -> - ok; -check_no_remove_depends(Module, [Instr|Instrs]) -> - check_no_remove_depend(Module, Instr), - check_no_remove_depends(Module, Instrs). - -check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - local_error({removed_module_in_depend, load_module, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - local_error({removed_module_in_depend, update, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(_, _) -> - ok. - - -check_change(soft) -> - ok; -check_change({advanced, _Something}) -> - ok; -check_change(Change) -> - local_error({bad_change, Change}). - - -check_purge(soft_purge) -> - ok; -check_purge(brutal_purge) -> - ok; -check_purge(Purge) -> - local_error({bad_purge, Purge}). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -local_error(Reason) -> - throw({error, Reason}). - -fail(Reason) -> - exit({suite_failed, Reason}). - -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> - fail({not_found, Key, L}); - {value, {Key, Value}} -> - Value - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -d(F, A) -> - d(false, F, A). - -d(true, F, A) -> - io:format(F ++ "~n", A); -d(_, _, _) -> - ok. - - + ok = ?t:appup_test(xmerl). diff --git a/make/emd2exml.in b/make/emd2exml.in index 5bfe89894e..48473349d0 100644 --- a/make/emd2exml.in +++ b/make/emd2exml.in @@ -334,6 +334,8 @@ text(Line) -> text("%ERTS-VSN%" ++ Cs, Acc) -> text(Cs, ["@ERTS_VSN@"|Acc]); +text("%OTP-VSN%" ++ Cs, Acc) -> + text(Cs, ["@OTP_VSN@"|Acc]); text("%OTP-REL%" ++ Cs, Acc) -> text(Cs, ["@OTP_REL@"|Acc]); @@ -357,6 +359,8 @@ put_text(#state{c = CTag, emphasis = EmTag} = S, Line) -> put_text(S, "%ERTS-VSN%"++Cs, CTag, EmTag, Acc) -> put_text(S, Cs, CTag, EmTag, ["@ERTS_VSN@"|Acc]); +put_text(S, "%OTP-VSN%"++Cs, CTag, EmTag, Acc) -> + put_text(S, Cs, CTag, EmTag, ["@OTP_VSN@"|Acc]); put_text(S, "%OTP-REL%"++Cs, CTag, EmTag, Acc) -> put_text(S, Cs, CTag, EmTag, ["@OTP_REL@"|Acc]); @@ -560,6 +564,8 @@ code(Line) -> code("%ERTS-VSN%" ++ Cs, Acc) -> code(Cs, ["@ERTS_VSN@"|Acc]); +code("%OTP-VSN%" ++ Cs, Acc) -> + code(Cs, ["@OTP_VSN@"|Acc]); code("%OTP-REL%" ++ Cs, Acc) -> code(Cs, ["@OTP_REL@"|Acc]); diff --git a/make/otp.mk.in b/make/otp.mk.in index 785926b997..a89bc37820 100644 --- a/make/otp.mk.in +++ b/make/otp.mk.in @@ -30,6 +30,13 @@ include $(ERL_TOP)/make/output.mk # ---------------------------------------------------- +# Version +# ---------------------------------------------------- + +OTP_VERSION = @OTP_VERSION@ +SYSTEM_VSN = @SYSTEM_VSN@ + +# ---------------------------------------------------- # Cross Compiling # ---------------------------------------------------- CROSS_COMPILING = @CROSS_COMPILING@ diff --git a/make/otp_default_release_path.mk b/make/otp_default_release_path.mk new file mode 100644 index 0000000000..932bbb2f6d --- /dev/null +++ b/make/otp_default_release_path.mk @@ -0,0 +1,24 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2014. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# +# Where to release to by default +# + +OTP_DEFAULT_RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)" diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk index b6afcd1c8b..fcac2ff695 100644 --- a/make/otp_release_targets.mk +++ b/make/otp_release_targets.mk @@ -17,6 +17,8 @@ # %CopyrightEnd% # +include $(ERL_TOP)/make/otp_default_release_path.mk + # ---------------------------------------------------- # Targets for the new documentation support # ---------------------------------------------------- @@ -137,7 +139,7 @@ endif ifeq ($(TESTROOT),) release release_docs release_tests release_html: - $(MAKE) $(MFLAGS) RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)" \ + $(MAKE) $(MFLAGS) RELEASE_PATH=$(OTP_DEFAULT_RELEASE_PATH) \ $(TARGET_MAKEFILE) $@_spec else diff --git a/make/output.mk.in b/make/output.mk.in index 51d9401280..f2f738a2ce 100644 --- a/make/output.mk.in +++ b/make/output.mk.in @@ -65,9 +65,9 @@ cc_verbose_0 = @echo " CC "$@; cc_verbose = $(cc_verbose_$(V)) V_CC = $(cc_verbose)$(CC) -cpp_verbose_0 = @echo " CPP "$@; -cpp_verbose = $(cpp_verbose_$(V)) -V_CPP = $(cpp_verbose)$(CPP) +cxx_verbose_0 = @echo " CXX "$@; +cxx_verbose = $(cxx_verbose_$(V)) +V_CXX = $(cxx_verbose)$(CXX) # For the diameter compiler. dia_verbose_0 = @echo " DIA "$@; @@ -1214,8 +1214,12 @@ do_patch_app () echo "No OTP $otp_major_vsn installation in $target_dir" 1>&2 exit 1 fi + shift + otp_version=`cat "$target_dir/OTP_VERSION"` || { echo "Not able to read $target_dir/OTP_VERSION" 1>&2; exit 1; } + { echo "$otp_version" | sed "s|^\([^\*]*\)\**|\1\*\*|g" > $target_dir/OTP_VERSION; } 2>/dev/null || { echo "Not able to update $target_dir/OTP_VERSION" 1>&2; exit 1; } + # Build all applications to target for app in "$@"; do if [ "$app" = "erts" ] && [ -d $ERL_TOP/$app ]; then @@ -1428,9 +1432,9 @@ cd $ERL_TOP determine_version_controller -# Unset ERL_FLAGS and ERL_OTP<Major-VSN>_FLAGS during bootstrap to +# Unset ERL_FLAGS and ERL_OTP<OTP Release>_FLAGS during bootstrap to # prevent potential problems -otp_major_vsn=`cat erts/vsn.mk | grep SYSTEM_VSN | sed "s|SYSTEM_VSN[^=]*=[^0-9]*\([0-9]*\).*|\1|g"` +otp_major_vsn=`cat OTP_VERSION | sed "s|\([0-9]*\).*|\1|"` erl_otp_flags="ERL_OTP${otp_major_vsn}_FLAGS" unset ERL_FLAGS unset ${erl_otp_flags} diff --git a/system/doc/installation_guide/otp_version.xml b/system/doc/installation_guide/otp_version.xml new file mode 100644 index 0000000000..dcba2d1702 --- /dev/null +++ b/system/doc/installation_guide/otp_version.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2014</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>OTP version</title> + <prepared></prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>2014-02-19</date> + <rev></rev> + <file>otp_version.xml</file> + </header> + <p>As of OTP release 17, the OTP release number corresponds to + the major OTP version number. In the normal case, the OTP version + number will be constructed as + <c><Major>.<Minor>.<Patch></c>. However more + dot separated parts may exist. If all parts less significant than + <c>Minor</c> equals <c>0</c>, they are omitted when printing the + version. Release candidates have an <c>-rc<N></c> suffix. The + OTP version as a concept was introduced in OTP 17.</p> + <p>OTP of a specific version is a set of applications of + specific versions. The application versions identified by + an OTP version corresponds to application versions that + have been tested together by the Erlang/OTP team at Ericsson AB. + An OTP system can however be put together with applications from + different OTP versions. Such a combination of application versions + has not been tested by the Erlang/OTP team. It is therefore + <em>always preferred to use OTP applications from one single OTP + version</em>.</p> + <p>In an OTP source code tree as well as in an installed OTP + development system, the OTP version can be read from the text + file <c>OTP_VERSION</c> in the OTP installation root directory + (<seealso marker="kernel:code#root_dir/0"><c>code:root_dir()</c></seealso>).</p> + <p>If the version read from the <c>OTP_VERSION</c> file in a + development system has a <c>**</c> suffix, the system has been + patched using the <c>$ERL_TOP/otp_build patch_app</c> tool. In + this case, the system consists of application versions from + multiple OTP versions. The version preceding the <c>**</c> + suffix corresponds to the OTP version of the base system that + has been patched.</p> + <p>On a target system (see the + <seealso marker="doc/system_principles:create_target">system principles</seealso> + documentation) no <c>OTP_VERSION</c> file will exist. This since + one easily can create a target system where it is hard to even + determine the base OTP version.</p> + <p>Note that if a development system is updated by other means + than <c>$ERL_TOP/otp_build patch_app</c>, the <c>OTP_VERSION</c> + file may identify wrong OTP version.</p> +</chapter> + diff --git a/system/doc/installation_guide/part.xml b/system/doc/installation_guide/part.xml index 19808fd165..150df39512 100644 --- a/system/doc/installation_guide/part.xml +++ b/system/doc/installation_guide/part.xml @@ -31,6 +31,7 @@ <description> <p>How to install Erlang/OTP on UNIX or Windows.</p> </description> + <xi:include href="otp_version.xml"/> <xi:include href="install-binary.xml"/> <xi:include href="verification.xml"/> <xi:include href="INSTALL.xml"/> diff --git a/system/doc/installation_guide/xmlfiles.mk b/system/doc/installation_guide/xmlfiles.mk index 3995c607af..245491ab94 100644 --- a/system/doc/installation_guide/xmlfiles.mk +++ b/system/doc/installation_guide/xmlfiles.mk @@ -17,6 +17,7 @@ # %CopyrightEnd% # INST_GUIDE_CHAPTER_FILES = \ + otp_version.xml \ install-binary.xml \ verification.xml \ INSTALL.xml \ diff --git a/system/doc/reference_manual/code_loading.xml b/system/doc/reference_manual/code_loading.xml index 23871dfa83..b5b5704df5 100644 --- a/system/doc/reference_manual/code_loading.xml +++ b/system/doc/reference_manual/code_loading.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2013</year> + <year>2003</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -121,9 +121,22 @@ loop() -> <title>Running a function when a module is loaded</title> <warning> - <p>We recommend that the feature described in this section is to - be used only for loading NIF libraries. For other usages this - feature should be considered experimental.</p> + <p>The <c>on_load</c> feature should be considered experimental + as there are a number of known weak points in current semantics + which therefore might also change in future releases:</p> + <list> + <item><p>Doing external call in on_load to the module itself + leads to deadlock.</p></item> + <item><p>At module upgrade, other processes calling the module + get suspended waiting for on_load to finish. This can be very bad + for applications with demands on realtime characteristics.</p></item> + <item><p>At module upgrade, no rollback is done if the on_load function fails. + The system will be left in a bad limbo state without any working + and reachable instance of the module.</p></item> + </list> + <p>The problems with module upgrade described above could be fixed in future + releases by changing the behaviour to not make the module reachable until + after the on_load function has successfully returned.</p> </warning> <p>The <c>-on_load()</c> directive names a function that should diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile index 20ef9fe781..79fd875d68 100644 --- a/system/doc/top/Makefile +++ b/system/doc/top/Makefile @@ -198,7 +198,7 @@ $(GLOSSARY): $(GLOSSARY_SCRIPT) #-------------------------------------------------------------------------- -PR.template: PR.template.src $(ERL_TOP)/erts/vsn.mk +PR.template: PR.template.src $(ERL_TOP)/make/$(TARGET)/otp.mk sed -e 's;%VSN%;$(VSN);' \ -e 's;%SYSTEM_VSN%;$(SYSTEM_VSN);' \ $< > $@ |