diff --git a/.travis.yml b/.travis.yml index 7a0da72810896c3401baf4d4cf3b9b07d895df9a..93f8c9148bf519f309ca183f35149a3bb853bf6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,10 @@ - sudo free -m -t # Turn OOM killer off to stop it from killing JUnit tests. - sudo sysctl vm.overcommit_memory=2 + # Unset funny _JAVA_OPTIONS which breaks our tests + - unset _JAVA_OPTIONS -## Disable instable UI tests for now. It means we won't need maven and can run shortly running ant directly. +## Disable instable UI tests for now. It means we won't need maven and can run ant directly. # # Install Window Manager. # - sudo apt-get install --no-install-recommends metacity libwebkit-dev # # Start X & Window Manager. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..b33fd1ceb4ed671b4e45a8b66a26f990d9c8fceb --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2017 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index 6bfd1641eee2def3d263c14e68080f111c189181..5e06bd854fd2f8d8993d0e1d9b65a5aa80e822f3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ For more information, visit https://research.microsoft.com/en-us/um/people/lampo Contributing ------------ -Please read our [contribution guide](https://raw.githubusercontent.com/tlaplus/tlaplus/master/CONTRIBUTING.md) before you start working on your contribution. We also have a [feature wishlist](https://github.com/tlaplus/tlaplus/blob/master/general/docs/contributions.md). +Please read our [contribution guide](https://github.com/tlaplus/tlaplus/blob/master/CONTRIBUTING.md) before you start working on your contribution. We also have a [feature wishlist](https://github.com/tlaplus/tlaplus/blob/master/general/docs/contributions.md). License ------- diff --git a/general/ide/README.md b/general/ide/README.md index 04e6c3f1021a90ad59e7ab845dfaed234aed777b..62b103e25fcc569639bd1751a7006e80d95cb5df 100644 --- a/general/ide/README.md +++ b/general/ide/README.md @@ -7,7 +7,7 @@ Eclipse Oomph is a tool to simplify and automate the setup of Eclipse developmen 2. Start the downloaded Oomph installer 3. Switch to "Advanced Mode" by clicking the button in the right upper corner depicted with three horizontal lines 4. Select "Eclipse Platform" on the Product list (expand "Eclipse.org" node) - 1. Choose "Neon" as the product version at the bottom  + 1. Choose "Oxygen" as the product version at the bottom  5. On the next screen, expand "Github Project" in the tree and select the check-box left to "TLA+" 1. Verify that "TLA+" shows up under "Project" at the bottom table and that the "Master" stream is selected  6. On the next page, select whether to use anonymous Github access (read-only) from the "TLA+ Github Repository" dropdown list  diff --git a/general/ide/TLA.setup b/general/ide/TLA.setup index 4c867312cc2fe42dc56e4580fd43472727620ed0..9b003e053c2d464fff36756e040be9d96d5315c3 100644 --- a/general/ide/TLA.setup +++ b/general/ide/TLA.setup @@ -17,8 +17,8 @@ label="TLA+"> <setupTask xsi:type="jdt:JRETask" - version="JavaSE-1.7" - location="${jre.location-1.7}"> + version="JavaSE-1.8" + location="${jre.location-1.8}"> <description>Define the JRE needed to compile and run the Java projects of ${scope.project.label}</description> </setupTask> <setupTask @@ -74,7 +74,7 @@ <repository url="http://download.eclipse.org/technology/swtbot/releases/latest/"/> <repository - url="http://download.eclipse.org/tools/ajdt/45/dev/update/"/> + url="http://download.eclipse.org/tools/ajdt/47/dev/update/"/> <repository url="http://download.eclipse.org/technology/m2e/releases/"/> <repository diff --git a/general/ide/images/00_PlatformSelection.png b/general/ide/images/00_PlatformSelection.png index 82994e4a73b4134112ffd33c7b5ff198d508955d..37c1af3cb3bedc09aaea695e30962a26233974fe 100644 Binary files a/general/ide/images/00_PlatformSelection.png and b/general/ide/images/00_PlatformSelection.png differ diff --git a/general/performance/PaxosCommit/MC.cfg b/general/performance/PaxosCommit/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0a98a485e81cde62d0b1c244b6a7d5729e6a7156 --- /dev/null +++ b/general/performance/PaxosCommit/MC.cfg @@ -0,0 +1,33 @@ +\* MV CONSTANT declarations +CONSTANTS +a1 = a1 +a2 = a2 +a3 = a3 +\* MV CONSTANT declarations +CONSTANTS +rm1 = rm1 +rm2 = rm2 +\* MV CONSTANT definitions +CONSTANT +Acceptor <- const_145717642420826000 +\* MV CONSTANT definitions +CONSTANT +RM <- const_145717642421827000 +\* SYMMETRY definition +SYMMETRY symm_145717642422828000 +\* CONSTANT definitions +CONSTANT +Ballot <- const_145717642423829000 +\* CONSTANT definitions +CONSTANT +Majority <- const_145717642424830000 +\* SPECIFICATION definition +SPECIFICATION +spec_145717642425831000 +\* INVARIANT definition +INVARIANT +inv_145717642426932000 +\* PROPERTY definition +PROPERTY +prop_145717642427933000 +\* Generated on Sat Mar 05 12:13:44 CET 2016 \ No newline at end of file diff --git a/general/performance/PaxosCommit/MC.tla b/general/performance/PaxosCommit/MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..8ab7db045748997fb3097954c48b3e077d8e1f16 --- /dev/null +++ b/general/performance/PaxosCommit/MC.tla @@ -0,0 +1,53 @@ +---- MODULE MC ---- +EXTENDS PaxosCommit, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a1, a2, a3 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +rm1, rm2 +---- + +\* MV CONSTANT definitions Acceptor +const_145717642420826000 == +{a1, a2, a3} +---- + +\* MV CONSTANT definitions RM +const_145717642421827000 == +{rm1, rm2} +---- + +\* SYMMETRY definition +symm_145717642422828000 == +Permutations(const_145717642420826000) \union Permutations(const_145717642421827000) +---- + +\* CONSTANT definitions @modelParameterConstants:0Ballot +const_145717642423829000 == +{0, 1, 2} +---- + +\* CONSTANT definitions @modelParameterConstants:2Majority +const_145717642424830000 == +{{a1, a2}, {a1, a3}, {a2, a3}} +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_145717642425831000 == +PCSpec +---- +\* INVARIANT definition @modelCorrectnessInvariants:0 +inv_145717642426932000 == +PCTypeOK +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_145717642427933000 == +TC!TCSpec +---- +============================================================================= +\* Modification History +\* Created Sat Mar 05 12:13:44 CET 2016 by markus diff --git a/general/performance/PaxosCommit/PaxosCommit.tla b/general/performance/PaxosCommit/PaxosCommit.tla new file mode 100644 index 0000000000000000000000000000000000000000..21c6b998bb6a37ac2141da371b216fcd1507cd61 --- /dev/null +++ b/general/performance/PaxosCommit/PaxosCommit.tla @@ -0,0 +1,276 @@ +----------------------------- MODULE PaxosCommit ---------------------------- +(***************************************************************************) +(* This module specifies the Paxos Commit algorithm. We specify only *) +(* safety properties, not liveness properties. We simplify the *) +(* specification in the following ways. *) +(* *) +(* - As in the specification of module TwoPhase, and for the same *) +(* reasons, we let the variable msgs be the set of all messages that *) +(* have ever been sent. If a message is sent to a set of recipients, *) +(* only one copy of the message appears in msgs. *) +(* *) +(* - We do not explicitly model the receipt of messages. If an *) +(* operation can be performed when a process has received a certain set *) +(* of messages, then the operation is represented by an action that is *) +(* enabled when those messages are in the set msgs of sent messages. *) +(* (We are specifying only safety properties, which assert what events *) +(* can occur, and the operation can occur if the messages that enable *) +(* it have been sent.) *) +(* *) +(* - We do not model leader selection. We define actions that the *) +(* current leader may perform, but do not specify who performs them. *) +(* *) +(* As in the specification of Two-Phase commit in module TwoPhase, we have *) +(* RMs spontaneously issue Prepared messages and we ignore Prepare *) +(* messages. *) +(***************************************************************************) +EXTENDS Integers + +Maximum(S) == + (*************************************************************************) + (* If S is a set of numbers, then this define Maximum(S) to be the *) + (* maximum of those numbers, or -1 if S is empty. *) + (*************************************************************************) + IF S = {} THEN -1 + ELSE CHOOSE n \in S : \A m \in S : n \geq m + +CONSTANT RM, \* The set of resource managers. + Acceptor, \* The set of acceptors. + Majority, \* The set of majorities of acceptors + Ballot \* The set of ballot numbers + +ASSUME \* We assume these properties of the declared constants. + /\ Ballot \subseteq Nat + /\ 0 \in Ballot + /\ Majority \subseteq SUBSET Acceptor + /\ \A MS1, MS2 \in Majority : MS1 \cap MS2 # {} + (********************************************************************) + (* All we assume about the set Majority of majorities is that any *) + (* two majorities have non-empty intersection. *) + (********************************************************************) + +Message == + (*************************************************************************) + (* The set of all possible messages. There are messages of type *) + (* "Commit" and "Abort" to announce the decision, as well as messages *) + (* for each phase of each instance of ins of the Paxos consensus *) + (* algorithm. The acc field indicates the sender of a message from an *) + (* acceptor to the leader; messages from a leader are broadcast to all *) + (* acceptors. *) + (*************************************************************************) + [type : {"phase1a"}, ins : RM, bal : Ballot \ {0}] + \cup + [type : {"phase1b"}, ins : RM, mbal : Ballot, bal : Ballot \cup {-1}, + val : {"prepared", "aborted", "none"}, acc : Acceptor] + \cup + [type : {"phase2a"}, ins : RM, bal : Ballot, val : {"prepared", "aborted"}] + \cup + [type : {"phase2b"}, acc : Acceptor, ins : RM, bal : Ballot, + val : {"prepared", "aborted"}] + \cup + [type : {"Commit", "Abort"}] +----------------------------------------------------------------------------- +VARIABLES + rmState, \* rmState[rm] is the state of resource manager rm. + aState, \* aState[ins][ac] is the state of acceptor ac for instance + \* ins of the Paxos algorithm. + msgs \* The set of all messages ever sent. + +PCTypeOK == + (*************************************************************************) + (* The type-correctness invariant. Each acceptor maintains the values *) + (* mbal, bal, and val for each instance of the Paxos consensus *) + (* algorithm. *) + (*************************************************************************) + /\ rmState \in [RM -> {"working", "prepared", "committed", "aborted"}] + /\ aState \in [RM -> [Acceptor -> [mbal : Ballot, + bal : Ballot \cup {-1}, + val : {"prepared", "aborted", "none"}]]] + /\ msgs \in SUBSET Message + +PCInit == \* The initial predicate. + /\ rmState = [rm \in RM |-> "working"] + /\ aState = [ins \in RM |-> + [ac \in Acceptor + |-> [mbal |-> 0, bal |-> -1, val |-> "none"]]] + /\ msgs = {} +----------------------------------------------------------------------------- +(***************************************************************************) +(* THE ACTIONS *) +(***************************************************************************) +Send(m) == msgs' = msgs \cup {m} + (*************************************************************************) + (* An action expression that describes the sending of message m. *) + (*************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(*{\large \textbf{RM Actions}} *) +(***************************************************************************) +RMPrepare(rm) == + (*************************************************************************) + (* Resource manager rm prepares by sending a phase 2a message for ballot *) + (* number 0 with value "prepared". *) + (*************************************************************************) + /\ rmState[rm] = "working" + /\ rmState' = [rmState EXCEPT ![rm] = "prepared"] + /\ Send([type |-> "phase2a", ins |-> rm, bal |-> 0, val |-> "prepared"]) + /\ UNCHANGED aState + +RMChooseToAbort(rm) == + (*************************************************************************) + (* Resource manager rm spontaneously decides to abort. It may (but need *) + (* not) send a phase 2a message for ballot number 0 with value *) + (* "aborted". *) + (*************************************************************************) + /\ rmState[rm] = "working" + /\ rmState' = [rmState EXCEPT ![rm] = "aborted"] + /\ Send([type |-> "phase2a", ins |-> rm, bal |-> 0, val |-> "aborted"]) + /\ UNCHANGED aState + +RMRcvCommitMsg(rm) == + (*************************************************************************) + (* Resource manager rm is told by the leader to commit. When this *) + (* action is enabled, rmState[rm] must equal either "prepared" or *) + (* "committed". In the latter case, the action leaves the state *) + (* unchanged (it is a ``stuttering step''). *) + (*************************************************************************) + /\ [type |-> "Commit"] \in msgs + /\ rmState' = [rmState EXCEPT ![rm] = "committed"] + /\ UNCHANGED <<aState, msgs>> + +RMRcvAbortMsg(rm) == + (*************************************************************************) + (* Resource manager rm is told by the leader to abort. It could be in *) + (* any state except "committed". *) + (*************************************************************************) + /\ [type |-> "Abort"] \in msgs + /\ rmState' = [rmState EXCEPT ![rm] = "aborted"] + /\ UNCHANGED <<aState, msgs>> +----------------------------------------------------------------------------- +(***************************************************************************) +(* LEADER ACTIONS *) +(* *) +(* The following actions are performed by any process that believes itself *) +(* to be the current leader. Since leader selection is not assumed to be *) +(* reliable, multiple processes could simultaneously consider themselves *) +(* to be the leader. *) +(***************************************************************************) +Phase1a(bal, rm) == + (*************************************************************************) + (* If the leader times out without learning that a decision has been *) + (* reached on resource manager rm's prepare/abort decision, it can *) + (* perform this action to initiate a new ballot bal. (Sending duplicate *) + (* phase 1a messages is harmless.) *) + (*************************************************************************) + /\ Send([type |-> "phase1a", ins |-> rm, bal |-> bal]) + /\ UNCHANGED <<rmState, aState>> + +Phase2a(bal, rm) == + (*************************************************************************) + (* The action in which a leader sends a phase 2a message with ballot *) + (* bal > 0 in instance rm, if it has received phase 1b messages for *) + (* *) + (* ballot number bal from a majority of acceptors. If the leader *) + (* received a phase 1b message from some acceptor that had sent a phase *) + (* 2b message for this instance, then maxbal \geq 0 and the value val *) + (* the leader sends is determined by the phase 1b messages. (If *) + (* val = "prepared", then rm must have prepared.) Otherwise, maxbal = -1 *) + (* and the leader sends the value "aborted". *) + (* *) + (* The first conjunct asserts that the action is disabled if any commit *) + (* leader has already sent a phase 2a message with ballot number bal. *) + (* In practice, this is implemented by having ballot numbers partitioned *) + (* among potential leaders, and having a leader record in stable storage *) + (* the largest ballot number for which it sent a phase 2a message. *) + (*************************************************************************) + /\ ~\E m \in msgs : /\ m.type = "phase2a" + /\ m.bal = bal + /\ m.ins = rm + /\ \E MS \in Majority : + LET mset == {m \in msgs : /\ m.type = "phase1b" + /\ m.ins = rm + /\ m.mbal = bal + /\ m.acc \in MS} + maxbal == Maximum({m.bal : m \in mset}) + val == IF maxbal = -1 + THEN "aborted" + ELSE (CHOOSE m \in mset : m.bal = maxbal).val + IN /\ \A ac \in MS : \E m \in mset : m.acc = ac + /\ Send([type |-> "phase2a", ins |-> rm, bal |-> bal, val |-> val]) + /\ UNCHANGED <<rmState, aState>> + +Decide == + (*************************************************************************) + (* A leader can decide that Paxos Commit has reached a result and send a *) + (* message announcing the result if it has received the necessary phase *) + (* 2b messages. *) + (*************************************************************************) + /\ LET Decided(rm, v) == + (****************************************************************) + (* True iff instance rm of the Paxos consensus algorithm has *) + (* chosen the value v. *) + (****************************************************************) + \E b \in Ballot, MS \in Majority : + \A ac \in MS : [type |-> "phase2b", ins |-> rm, + bal |-> b, val |-> v, acc |-> ac ] \in msgs + IN \/ /\ \A rm \in RM : Decided(rm, "prepared") + /\ Send([type |-> "Commit"]) + \/ /\ \E rm \in RM : Decided(rm, "aborted") + /\ Send([type |-> "Abort"]) + /\ UNCHANGED <<rmState, aState>> +----------------------------------------------------------------------------- +(***************************************************************************) +(* ACCEPTOR ACTIONS *) +(***************************************************************************) +Phase1b(acc) == + \E m \in msgs : + /\ m.type = "phase1a" + /\ aState[m.ins][acc].mbal < m.bal + /\ aState' = [aState EXCEPT ![m.ins][acc].mbal = m.bal] + /\ Send([type |-> "phase1b", + ins |-> m.ins, + mbal |-> m.bal, + bal |-> aState[m.ins][acc].bal, + val |-> aState[m.ins][acc].val, + acc |-> acc]) + /\ UNCHANGED rmState + +Phase2b(acc) == + /\ \E m \in msgs : + /\ m.type = "phase2a" + /\ aState[m.ins][acc].mbal \leq m.bal + /\ aState' = [aState EXCEPT ![m.ins][acc].mbal = m.bal, + ![m.ins][acc].bal = m.bal, + ![m.ins][acc].val = m.val] + /\ Send([type |-> "phase2b", ins |-> m.ins, bal |-> m.bal, + val |-> m.val, acc |-> acc]) + /\ UNCHANGED rmState +----------------------------------------------------------------------------- +PCNext == \* The next-state action + \/ \E rm \in RM : \/ RMPrepare(rm) + \/ RMChooseToAbort(rm) + \/ RMRcvCommitMsg(rm) + \/ RMRcvAbortMsg(rm) + \/ \E bal \in Ballot \ {0}, rm \in RM : Phase1a(bal, rm) \/ Phase2a(bal, rm) + \/ Decide + \/ \E acc \in Acceptor : Phase1b(acc) \/ Phase2b(acc) +----------------------------------------------------------------------------- +PCSpec == PCInit /\ [][PCNext]_<<rmState, aState, msgs>> + (*************************************************************************) + (* The complete spec of the Paxos Commit protocol. *) + (*************************************************************************) + +THEOREM PCSpec => PCTypeOK +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now assert that the two-phase commit protocol implements the *) +(* transaction commit protocol of module TCommit. The following statement *) +(* defines TC!TCSpec to be the formula TCSpec of module TCommit. (The *) +(* TLA+ INSTANCE statement must is used to rename the operators defined in *) +(* module TCommit to avoid possible name conflicts with operators in the *) +(* current module having the same name.) *) +(***************************************************************************) +TC == INSTANCE TCommit + +THEOREM PCSpec => TC!TCSpec +============================================================================= diff --git a/general/performance/PaxosCommit/TCommit.tla b/general/performance/PaxosCommit/TCommit.tla new file mode 100644 index 0000000000000000000000000000000000000000..f944cf065ffbdcdae3dc8138664b19486b9e1a88 --- /dev/null +++ b/general/performance/PaxosCommit/TCommit.tla @@ -0,0 +1,66 @@ +------------------------------- MODULE TCommit ------------------------------ +CONSTANT RM \* The set of participating resource managers +VARIABLE rmState \* rmState[rm] is the state of resource manager rm. +----------------------------------------------------------------------------- +TCTypeOK == + (*************************************************************************) + (* The type-correctness invariant *) + (*************************************************************************) + rmState \in [RM -> {"working", "prepared", "committed", "aborted"}] + +TCInit == rmState = [rm \in RM |-> "working"] + (*************************************************************************) + (* The initial predicate. *) + (*************************************************************************) + +canCommit == \A rm \in RM : rmState[rm] \in {"prepared", "committed"} + (*************************************************************************) + (* True iff all RMs are in the "prepared" or "committed" state. *) + (*************************************************************************) + +notCommitted == \A rm \in RM : rmState[rm] # "committed" + (*************************************************************************) + (* True iff no resource manager has decided to commit. *) + (*************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the actions that may be performed by the RMs, and then *) +(* define the complete next-state action of the specification to be the *) +(* disjunction of the possible RM actions. *) +(***************************************************************************) +Prepare(rm) == /\ rmState[rm] = "working" + /\ rmState' = [rmState EXCEPT ![rm] = "prepared"] + +Decide(rm) == \/ /\ rmState[rm] = "prepared" + /\ canCommit + /\ rmState' = [rmState EXCEPT ![rm] = "committed"] + \/ /\ rmState[rm] \in {"working", "prepared"} + /\ notCommitted + /\ rmState' = [rmState EXCEPT ![rm] = "aborted"] + +TCNext == \E rm \in RM : Prepare(rm) \/ Decide(rm) + (*************************************************************************) + (* The next-state action. *) + (*************************************************************************) +----------------------------------------------------------------------------- +TCSpec == TCInit /\ [][TCNext]_rmState + (*************************************************************************) + (* The complete specification of the protocol. *) + (*************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now assert invariance properties of the specification. *) +(***************************************************************************) +TCConsistent == + (*************************************************************************) + (* A state predicate asserting that two RMs have not arrived at *) + (* conflicting decisions. *) + (*************************************************************************) + \A rm1, rm2 \in RM : ~ /\ rmState[rm1] = "aborted" + /\ rmState[rm2] = "committed" + +THEOREM TCSpec => [](TCTypeOK /\ TCConsistent) + (*************************************************************************) + (* Asserts that TCTypeOK and TCInvariant are invariants of the protocol. *) + (*************************************************************************) +============================================================================= diff --git a/general/performance/csv.sh b/general/performance/csv.sh new file mode 100755 index 0000000000000000000000000000000000000000..8629d95422b10c2f8b9323100187a0b2aa8abd45 --- /dev/null +++ b/general/performance/csv.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +echo "Implementation,Processors,Insertions,Duration,Memory,LoadFactor,Locks,TblCnt,JunitTime,NUMABalancing,PageDefrag,Govenor" + +for file in $1/*-tlc.txt; do + PROCESSORS=$(echo $file | cut -d "-" -f3) + IMPL=$(basename $file | cut -d "_" -f1) + + FILE=$(cat $file) + INSERTIONS=$(echo $FILE | grep -Po 'Total puts: \K[0-9]+') + DURATION=$(echo $FILE | grep -Po 'duration: \K[^ ]+') + LOADFACTOR=$(echo $FILE | grep -Po 'total load factor: \K[0-9.]+') + LOCKS=$(echo $FILE | grep -Po 'FPSet lock count is: \K[0-9]+') + MEM=$(echo $FILE | grep -Po 'FPSet table count is: \K[^ ]+') + TBLCNT=$(echo $FILE | grep -Po 'FPSet bucket count is: \K[0-9]+') + JUNITTIME=$(echo $FILE | grep -Po 'Time: \K[0-9,.]*') + NUMA=$(echo $FILE | grep -Po 'kernel.numa_balancing = \K[0-9,.]*') + DEFRAG=$(echo $FILE | grep 'page_defrag = ' | grep -Po '\[.*\]') + GOVENOR=$(echo $FILE | grep -Po 'scaling_governor = \K[a-z]*') + + echo $IMPL,$PROCESSORS,$INSERTIONS,$DURATION,$(echo $MEM | sed s/,//g),$LOADFACTOR,$LOCKS,$TBLCNT,$(echo $JUNITTIME | sed s/,//g),$NUMA,$DEFRAG,$GOVENOR +done diff --git a/general/performance/measureFPSet.sh b/general/performance/measureFPSet.sh new file mode 100755 index 0000000000000000000000000000000000000000..881fed40c8072753f07ee39059aa30eacacf12a6 --- /dev/null +++ b/general/performance/measureFPSet.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +# Performance related properties +WORKERS=${5-"$(nproc --all)"} +HEAP_MEM=${6-"512g"} +DIRECT_MEM=${7-"512g"} +FPSET_IMPL="tlc2.tool.fp.OffHeapDiskFPSet" + +# TLC version +REV=$(git rev-parse HEAD) + +# measure.sh's fs path +#DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# TLC's code location +TLATOOLS_HOME=../../tlatools/ + +# Trap interrupts and exit instead of continuing the for loop below +trap "echo Exited!; exit;" SIGINT SIGTERM + +###################################### + +# Repeat N times to even out noise... +for i in {1..3}; do + # For varying worker (core) counts... + #for ((w=$WORKERS; w > 0;w=w/2)); do + #for w in 128 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 4 2 1; do + for w in 128 64 32 16 8 4 2 1 24 40 48 56 82 80 88 96 104; do + + ## MSB Output/log files + MSB_JFR_OUTPUT_FILE="MSB_"$REV'_w'$(printf "%03d" $w)'_i'$i.jfr + MSB_TIME_OUTPUT_FILE="MSB_"$REV-$(printf "%03d" $w).txt + MSB_TLC_OUTPUT_FILE="MSB_"$REV-$i-$(printf "%03d" $w)-tlc.txt + + /usr/bin/time --append --output=$MSB_TIME_OUTPUT_FILE \ + java \ + -XX:+UnlockCommercialFeatures \ + -XX:+UnlockDiagnosticVMOptions \ + -XX:+DebugNonSafepoints \ + -XX:+FlightRecorder \ + -XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/tmp,dumponexit=true,dumponexitpath=$MSB_JFR_OUTPUT_FILE,maxage=12h,settings=$TLATOOLS_HOME/jfr/tlc.jfc \ + -javaagent:$TLATOOLS_HOME/jfr/jmx2jfr.jar=$TLATOOLS_HOME/jfr/jmxprobes.xml \ + -Xmx$HEAP_MEM -Xms$HEAP_MEM \ + -Djava.util.logging.config.file=$TLATOOLS_HOME/logging.properties \ + -Dtlc2.tool.fp.MultiThreadedFPSetTest.numThreads=$w \ + -Dtlc2.tool.fp.MultiThreadedFPSetTest.excludes=_BatchedFingerPrintGenerator_LongVecFingerPrintGenerator_PartitionedFingerPrintGenerator \ + -Dtlc2.tool.fp.MultiThreadedFPSetTest.insertions=8589934592 \ + -Djava.io.tmpdir=/mnt/markus/tmp/ \ + -cp $TLATOOLS_HOME/class:$TLATOOLS_HOME/lib/* \ + org.junit.runner.JUnitCore tlc2.tool.fp.MultiThreadedMSBDiskFPSetTest 2>&1 | tee $MSB_TLC_OUTPUT_FILE; + + ## OffHeap + JFR_OUTPUT_FILE="Off_"$REV'_w'$(printf "%03d" $w)'_i'$i.jfr + TIME_OUTPUT_FILE="Off_"$REV-$(printf "%03d" $w).txt + TLC_OUTPUT_FILE="Off_"$REV-$i-$(printf "%03d" $w)-tlc.txt + + /usr/bin/time --append --output=$TIME_OUTPUT_FILE \ + java \ + -XX:+UnlockCommercialFeatures \ + -XX:+UnlockDiagnosticVMOptions \ + -XX:+DebugNonSafepoints \ + -XX:+FlightRecorder \ + -XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/tmp,dumponexit=true,dumponexitpath=$JFR_OUTPUT_FILE,maxage=12h,settings=$TLATOOLS_HOME/jfr/tlc.jfc \ + -javaagent:$TLATOOLS_HOME/jfr/jmx2jfr.jar=$TLATOOLS_HOME/jfr/jmxprobes.xml \ + -XX:MaxDirectMemorySize=$DIRECT_MEM \ + -Djava.util.logging.config.file=$TLATOOLS_HOME/logging.properties \ + -Dtlc2.tool.fp.MultiThreadedFPSetTest.numThreads=$w \ + -Dtlc2.tool.fp.MultiThreadedFPSetTest.excludes=_BatchedFingerPrintGenerator_LongVecFingerPrintGenerator_PartitionedFingerPrintGenerator \ + -Dtlc2.tool.fp.MultiThreadedFPSetTest.insertions=34359738368 \ + -Djava.io.tmpdir=/mnt/markus/tmp/ \ + -cp $TLATOOLS_HOME/class:$TLATOOLS_HOME/lib/* \ + org.junit.runner.JUnitCore tlc2.tool.fp.MultiThreadedOffHeapDiskFPSetTest 2>&1 | tee $TLC_OUTPUT_FILE; + + echo "page_defrag = $(cat /sys/kernel/mm/transparent_hugepage/defrag)" >> $TLC_OUTPUT_FILE + echo "scaling_governor = $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)" >> $TLC_OUTPUT_FILE + sysctl kernel.numa_balancing >> $TLC_OUTPUT_FILE + done +done + +## Honest profiler +# -agentpath:/mnt/markus/tlaplus/profiler/liblagent.so=interval=7,logPath=/mnt/markus/tlaplus/profiler/$REV-$i-$w.hpl \ diff --git a/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF index f1b0e4ee3f7d789ff6067c82e85e8c83666b2be0..9def4af82f3faf4b40790d8ce0b2a62710384e78 100644 --- a/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF @@ -2,8 +2,13 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: TLA+ Toolbox Help Bundle-SymbolicName: org.lamport.tla.toolbox.doc; singleton:=true -Bundle-Version: 1.5.3.qualifier +Bundle-Version: 1.5.4.qualifier Bundle-RequiredExecutionEnvironment: J2SE-1.4 Bundle-Vendor: Simon Zambrovski, Leslie Lamport Bundle-ActivationPolicy: lazy Eclipse-BundleShape: dir +Require-Bundle: org.lamport.tla.toolbox;bundle-version="1.0.0", + org.eclipse.ui;bundle-version="3.107.0", + org.eclipse.help.ui;bundle-version="4.0.100", + org.eclipse.core.resources;bundle-version="3.10.1" +Bundle-Activator: org.lamport.tla.toolbox.doc.HelpActivator diff --git a/org.lamport.tla.toolbox.doc/build.properties b/org.lamport.tla.toolbox.doc/build.properties index baf92ee5b8219b5f28ff1a6821cd1563dc70c75c..20b165d708bb344d5a29071c708d446fccbf359a 100644 --- a/org.lamport.tla.toolbox.doc/build.properties +++ b/org.lamport.tla.toolbox.doc/build.properties @@ -4,4 +4,5 @@ bin.includes = plugin.xml,\ META-INF/,\ .,\ html/,\ - *.xml + *.xml,\ + pdfs/ diff --git a/org.lamport.tla.toolbox.doc/html/cloudtlc/index.html b/org.lamport.tla.toolbox.doc/html/cloudtlc/index.html index 422635bd70b426180390a3e90fde5f2e3ea0a21f..9740f4365009c194bb78ffeb10bac356790dfc49 100644 --- a/org.lamport.tla.toolbox.doc/html/cloudtlc/index.html +++ b/org.lamport.tla.toolbox.doc/html/cloudtlc/index.html @@ -1,355 +1,520 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> -<meta name="generator" content="http://www.nongnu.org/elyxer/"/> -<meta name="create-date" content="2014-07-18"/> - <link href="../style.css" rel="stylesheet" type="text/css"/> -<title>Cloud based distributed TLC</title> + <meta name="generator" content= + "HTML Tidy for Linux (vers 25 March 2009), see www.w3.org" /> + <meta http-equiv="Content-Type" content= + "text/html; charset=us-ascii" /> + <meta name="generator" content="http://www.nongnu.org/elyxer/" /> + <meta name="create-date" content="2014-07-18" /> + <link href="../style.css" rel="stylesheet" type="text/css" /> + + <title>Cloud based distributed TLC</title> </head> + <body> -<div id="globalWrapper"> -<h1 class="title"> -Cloud based distributed TLC -</h1> -<h1 class="Section"> -<a class="toc" name="toc-Section-1">1</a> Motivation -</h1> -<ul> -<li> -Move long running model checking off local machine into the cloud (Short running models not ideal because instance spin-up time is approximately five minutes) -</li> -<li> -Maximize cloud instance resource utilization by providing fine-tuned TLC parameter pre-sets optimized for the given cloud instance type -</li> -<li> -Minimizes costs by terminating cloud instances immediately after TLC model checking has ended<ul> -<li> -Unless email delivery of model checking result fails (n this case the machine remains running for the user to pick up the result manually) -</li> -<li> -User then has to terminate the instance manually! -</li> - -</ul> - -</li> - -</ul> -<h1 class="Section"> -<a class="toc" name="toc-Section-2">2</a> Warning -</h1> -<div class="Unindented"> -Using cloud based TLC launches compute instances at your cloud provider which may incur costs. While the cloud based distributed TLC tries to minimize costs by terminating instances as soon as possible, do not rely on it. Always check if cloud instances have been correctly terminated. -</div> -<h1 class="Section"> -<a class="toc" name="toc-Section-3">3</a> Limitation -</h1> -<ul> -<li> -Only supports two cloud providers (<a class="URL" href="http://aws.amazon.com/ec2/">Amazon EC2</a> and <a class="URL" href="https://azure.microsoft.com/en-us/">Microsoft Azure</a>) as of now -<ul> -<li> -On Azure, the VM instances just stoppes but does not <a class="URL" href="http://blogs.technet.com/b/gbanin/archive/2015/04/22/difference-between-the-states-of-azure-virtual-machines-stopped-and-stopped-deallocated.aspx">deallocate</a> automatically. Please make sure to manually shutdown the VM instance after TLC finishes. -</li> -</ul> -</li> -<li> -Runs TLC in non-distributed mode on a single cloud instance only as of now -</li> -<li> -Only a single instance type per cloud (<a class="URL" href="http://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud#Instance_types">m4.2xlarge</a> and <a class="URL" href="https://azure.microsoft.com/en-us/pricing/details/virtual-machines/">D14</a>) supported as of now -</li> -<li> -Cloud based distributed TLC cannot recover from a checkpoint -</li> - -</ul> -<h1 class="Section"> -<a class="toc" name="toc-Section-4">4</a> Usage -</h1> - -<div align="left"> -<div align="center" style="width:500px;height:45px;border:1px solid #000;"> -<em>Additionally to the instructions below, you can <a href="https://vimeo.com/126244715">watch a eight minutes introductory video</a> online</em>. -</div> -</div> - -<ol> -<li> -Set <a class="URL" href="http://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html">AWS access key and secret</a> as <a class="URL" href="http://en.wikipedia.org/wiki/Environment_variable">environment variables</a> prior to launching the Toolbox. See the example with dummy keys below.<ol> -<li> -<div class="listing"> -<pre class="listing">export AWS_ACCESS_KEY_ID=AKIA7D89HCLJKHZASD7F + <div id="globalWrapper"> + <h1 class="title">Cloud based distributed TLC</h1> + + <h1 class="Section"><a class="toc" name="toc-Section-1" id= + "toc-Section-1">1</a> Motivation</h1> + + <ul> + <li>Move long running model checking off local machine into + the cloud (Short running models not ideal because instance + spin-up time is approximately five minutes)</li> + + <li>Maximize cloud instance resource utilization by providing + fine-tuned TLC parameter pre-sets optimized for the given + cloud instance type</li> + + <li>Minimizes costs by terminating cloud instances + immediately after TLC model checking has ended + + <ul> + <li>Unless email delivery of model checking result fails + (n this case the machine remains running for the user to + pick up the result manually)</li> + + <li>User then has to terminate the instance + manually!</li> + </ul> + </li> + </ul> + + <h1 class="Section"><a class="toc" name="toc-Section-2" id= + "toc-Section-2">2</a> Warning</h1> + + <div class="Unindented"> + Using cloud based TLC launches compute instances at your + cloud provider which may incur costs. While the cloud based + distributed TLC tries to minimize costs by terminating + instances as soon as possible, do not rely on it. Always + check if cloud instances have been correctly terminated. + </div> + + <h1 class="Section"><a class="toc" name="toc-Section-3" id= + "toc-Section-3">3</a> Limitation</h1> + + <ul> + <li>Only supports two cloud providers (<a class="URL" href= + "http://aws.amazon.com/ec2/">Amazon EC2</a> and <a class= + "URL" href="https://azure.microsoft.com/en-us/">Microsoft + Azure</a>) as of now + + <ul> + <li>On Azure, the VM instances just stoppes but does not + <a class="URL" href= + "http://blogs.technet.com/b/gbanin/archive/2015/04/22/difference-between-the-states-of-azure-virtual-machines-stopped-and-stopped-deallocated.aspx"> + deallocate</a> automatically. Please make sure to + manually shutdown the VM instance after TLC + finishes.</li> + </ul> + </li> + + <li>Runs TLC in non-distributed mode on a single cloud + instance only as of now</li> + + <li>Only a single instance type per cloud (<a class="URL" + href= + "http://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud#Instance_types">m4.2xlarge</a> + and <a class="URL" href= + "https://azure.microsoft.com/en-us/pricing/details/virtual-machines/"> + D14</a>) supported as of now</li> + + <li>Cloud based distributed TLC cannot recover from a + checkpoint</li> + </ul> + + <h1 class="Section"><a class="toc" name="toc-Section-4" id= + "toc-Section-4">4</a> Usage</h1> + + <div align="left"> + <div align="center" style= + "width:500px;height:45px;border:1px solid #000;"> + <em>Additionally to the instructions below, you can + <a href="https://vimeo.com/126244715">watch a eight minutes + introductory video</a> online</em>. + </div> + </div> + + <ol> + <li>Set <a class="URL" href= + "http://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html"> + AWS access key and secret</a> as <a class="URL" href= + "http://en.wikipedia.org/wiki/Environment_variable">environment + variables</a> prior to launching the Toolbox. See the + example with dummy keys below. + + <div style="margin-left: 2em"> + <ul> + <li style="list-style: none; display: inline"> + <div class="listing"> + <pre class="listing"> +export AWS_ACCESS_KEY_ID=AKIA7D89HCLJKHZASD7F export AWS_SECRET_ACCESS_KEY=6FDASAIG7DAS976TYDKHCGQAS5D\FA77 </pre> -</div> + </div> + </li> -</li> + <li>Alternatively for Azure, first create a certificate + (the command below uses keytool which is part of the + Java installation to create the azure.p12 file): -Alternatively for Azure, after the one-time <a class="URL" href="https://azure.microsoft.com/en-us/documentation/articles/java-create-azure-website-using-java-sdk/#create-a-certificate">creation and installation of a certificate</a>, set: +<!-- "https://web.archive.org/web/20150321180025/http://azure.microsoft.com/en-us/documentation/articles/java-create-azure-website-using-java-sdk/#create-a-certificate" --> -<li> -<div class="listing"> -<pre class="listing">export AZURE_COMPUTE_CREDENTIALS=ThePasswordUsedForTheCertificate (The "password" supplied during certificate creation) + <div class="listing"> + <pre class="listing"> +/absolute/path/to/Java/installation/bin/keytool -genkey -alias TLAToolbox + -keystore /absolute/path/to/the/azure.p12 -storepass ThePasswordUsedForTheCertificate + -validity 3650 -keyalg RSA -keysize 2048 -storetype pkcs12 + -dname "CN=TLAToolbox" +</pre> + </div>Next, create the azure.cer file from the + azure.p12 file. + + <div class="listing"> + <pre class="listing"> +/absolute/path/to/Java/installation/bin/keytool -export -alias TLAToolbox + -storetype pkcs12 -keystore /absolute/path/to/the/azure.p12 + -storepass ThePasswordUsedForTheCertificate -rfc -file /absolute/path/to/the/azure.cer +</pre> + </div> + </li> + + <li> + Upload azure.cer to <a href="portal.azure.com">portal.azure.com</a> as described in <a href="https://docs.microsoft.com/en-us/azure/azure-api-management-certs">Upload an Azure Management API Management Certificate</a>. + </li> + + <li> + Finally configure the Azure specific environment variables as shown below for Command Prompt and PowerShell. + <div class="listing"> + <pre class="listing"> +export AZURE_COMPUTE_CREDENTIALS=ThePasswordUsedForTheCertificate (The "password" supplied during certificate creation) export AZURE_COMPUTE_IDENTITY=/absolute/path/to/the/azure.p12 -export AZURE_COMPUTE_SUBSCRIPTION=YourAzureSubscriptionId (Extract the ID from the <a class="URL" href="https://ms.portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade">Azure portal under "Subscriptions"</a>) +export AZURE_COMPUTE_SUBSCRIPTION=YourAzureSubscriptionId (Extract the ID from the <a class="URL" +href= +"https://ms.portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade">Azure portal under "Subscriptions"</a>) + </pre> -</div></li> -</ol> -</li> -<li> -Create a specification and a model -</li> -<li> -Open your model in the model editor <div class="float"> -<a class="Label" name="Figure-1"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="ModelEditorA.png" alt="figure ModelEditorA.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 1 Model Editor -</div> - -</div> - -</div> - -</li> -<li> -Expand the “How to run” section of the “Model Overview” page <div class="float"> -<a class="Label" name="Figure-2"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="ModelEditorB.png" alt="figure ModelEditorB.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 2 How to run section -</div> - -</div> - -</div> - -</li> -<li> -Select “aws-ec2” from the “Run in distributed mode” drop down <div class="float"> -<a class="Label" name="Figure-3"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="ModelEditorC.png" alt="figure ModelEditorC.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 3 Select your cloud provider -</div> - -</div> - -</div> - -</li> -<li> -Enter an email address into “Result mailto address” at which you want to receive the model checking result <div class="float"> -<a class="Label" name="Figure-4"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="ModelEditorD.png" alt="figure ModelEditorD.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 4 Enter your email address -</div> - -</div> - -</div> - -</li> -<li> -Click “Run TLC” to start model checking in the cloud and wait for the start-up to finish (it takes approximately five minutes to set-up the cloud instance)<ol> -<li> -The Toolbox switches to the “Model checking results” page and opens a progress dialog indicating the state of cloud instance provisioning <div class="float"> -<a class="Label" name="Figure-5"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="ProgressBar.png" alt="figure ProgressBar.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 5 Progress Dialog -</div> - -</div> - -</div> - -</li> -<li> -After provisioning the cloud instance, the Toolbox prompts the user to open a website in a browser. <div class="float"> -<a class="Label" name="Figure-6"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="ProgressBarFinal.png" alt="figure ProgressBarFinal.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 6 Progress Dialog Final -</div> - -</div> - -</div> - This website is hosted on the cloud instance and shows the TLC process output as well as runtime statistics similar to Toolbox stats <div class="float"> -<a class="Label" name="Figure-7"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="MCoutInBrowser.png" alt="figure MCoutInBrowser.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 7 TLC runtime statistics in your browser -</div> - -</div> - -</div> - -</li> - -</ol> - -</li> -<li> -Walk out and enjoy the weekend while TLC is busy checking -</li> -<li> -On Monday expect to find an email in your inbox <div class="float"> -<a class="Label" name="Figure-8"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="EMailResult.png" alt="figure EMailResult.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 8 Email Result -</div> - -</div> - -</div> - -</li> -<li> -Save MC.out file somewhere to disc -</li> -<li> -Switch back to the Toolbox and open the model editor -</li> -<li> -Open the “Model Checking Results” page <div class="float"> -<a class="Label" name="Figure-9"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="LoadResultIntoToolbox.png" alt="figure LoadResultIntoToolbox.png" style="max-width: 800px; max-height: 600px;"/> -<div class="caption"> -Figure 9 Load result into Toolbox -</div> - -</div> - -</div> - -</li> -<li> -<a class="Label" name="enu:Import-the-MC.out"> </a>Import the MC.out from disc <div class="float"> -<a class="Label" name="Figure-10"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="LoadIntoResultPageB.png" alt="figure LoadIntoResultPageB.png" style="max-width: 1073px; max-height: 791px;"/> -<div class="caption"> -Figure 10 Load into results page -</div> - -</div> - -</div> - -</li> -<li> -Voilá <div class="float"> -<a class="Label" name="Figure-11"> </a><div class="figure" style="max-width: 100%;"> -<img class="figure" src="FinalResultLoaded.png" alt="figure FinalResultLoaded.png" style="max-width: 1074px; max-height: 789px;"/> -<div class="caption"> -Figure 11 Final result loaded -</div> - -</div> - -</div> - -</li> - -</ol> - - -<h1 class="Section"> -<a class="toc" name="toc-Section-5">5</a> Common problems -</h1> - -<ul> - <li> - The Toolbox fails to start the cloud instance + </div> + </li> + + <li>On Windows in the Command Prompt, set the + environment variable with: + <pre class="listing"> +set AZURE_COMPUTE_CREDENTIALS=ThePasswordUsedForTheCertificate +set AZURE_COMPUTE_IDENTITY=C:\absolute\path\to\the\azure.p12 +set AZURE_COMPUTE_SUBSCRIPTION=YourAzureSubscriptionId +</pre>If you prefer PowerShell, do (notice the quotes): + <pre class="listing"> +$env:AZURE_COMPUTE_CREDENTIALS="ThePasswordUsedForTheCertificate" +$env:AZURE_COMPUTE_IDENTITY="C:\absolute\path\to\the\azure.p12" +$env:AZURE_COMPUTE_SUBSCRIPTION="YourAzureSubscriptionId" + +</pre>To set the variables permanently, use the + <a href="https://technet.microsoft.com/en-us/library/cc755104(v=ws.11).aspx"> + setx</a> command. + + <p>DO NOT use Cygwin shell to set the environment + variables and launch the Toolbox. It will lead to + obscure exceptions at runtime.</p> + </li> + </ul> + </div> + </li> + + <li>Create a specification and a model</li> + + <li>Open your model in the model editor + + <div class="float"> + <a class="Label" name="Figure-1" id="Figure-1"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="ModelEditorA.png" alt= + "figure ModelEditorA.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 1 Model Editor + </div> + </div> + </div> + </li> + + <li>Expand the “How to run” section of the + “Model Overview” page + + <div class="float"> + <a class="Label" name="Figure-2" id="Figure-2"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="ModelEditorB.png" alt= + "figure ModelEditorB.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 2 How to run section + </div> + </div> + </div> + </li> + + <li>Select “aws-ec2” from the “Run in + distributed mode” drop down + + <div class="float"> + <a class="Label" name="Figure-3" id="Figure-3"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="ModelEditorC.png" alt= + "figure ModelEditorC.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 3 Select your cloud provider + </div> + </div> + </div> + </li> + + <li>Enter an email address into “Result mailto + address” at which you want to receive the model + checking result + + <div class="float"> + <a class="Label" name="Figure-4" id="Figure-4"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="ModelEditorD.png" alt= + "figure ModelEditorD.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 4 Enter your email address + </div> + </div> + </div> + </li> + + <li>Click “Run TLC” to start model checking in + the cloud and wait for the start-up to finish (it takes + approximately five minutes to set-up the cloud instance) + + <ol> + <li>The Toolbox switches to the “Model checking + results” page and opens a progress dialog + indicating the state of cloud instance provisioning + + <div class="float"> + <a class="Label" name="Figure-5" id="Figure-5"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="ProgressBar.png" alt= + "figure ProgressBar.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 5 Progress Dialog + </div> + </div> + </div> + </li> + + <li>After provisioning the cloud instance, the Toolbox + prompts the user to open a website in a browser. + + <div class="float"> + <a class="Label" name="Figure-6" id="Figure-6"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="ProgressBarFinal.png" alt= + "figure ProgressBarFinal.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 6 Progress Dialog Final + </div> + </div> + </div>This website is hosted on the cloud instance and + shows the TLC process output as well as runtime + statistics similar to Toolbox stats + + <div class="float"> + <a class="Label" name="Figure-7" id="Figure-7"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="MCoutInBrowser.png" alt= + "figure MCoutInBrowser.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 7 TLC runtime statistics + in your browser + </div> + </div> + </div> + </li> + </ol> + </li> + + <li>Walk out and enjoy the weekend while TLC is busy + checking</li> + + <li>On Monday expect to find an email in your inbox + + <div class="float"> + <a class="Label" name="Figure-8" id="Figure-8"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="EMailResult.png" alt= + "figure EMailResult.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 8 Email Result + </div> + </div> + </div> + </li> + + <li>Save MC.out file somewhere to disc</li> + + <li>Switch back to the Toolbox and open the model editor</li> + + <li>Open the “Model Checking Results” page + + <div class="float"> + <a class="Label" name="Figure-9" id="Figure-9"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="LoadResultIntoToolbox.png" + alt="figure LoadResultIntoToolbox.png" style= + "max-width: 800px; max-height: 600px;" /> + + <div class="caption"> + Figure 9 Load result into Toolbox + </div> + </div> + </div> + </li> + + <li> + <a class="Label" name="enu:Import-the-MC.out" id= + "enu:Import-the-MC.out"></a>Import the MC.out from disc + + <div class="float"> + <a class="Label" name="Figure-10" id="Figure-10"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="LoadIntoResultPageB.png" alt= + "figure LoadIntoResultPageB.png" style= + "max-width: 1073px; max-height: 791px;" /> + + <div class="caption"> + Figure 10 Load into results page + </div> + </div> + </div> + </li> + + <li>Voilá + + <div class="float"> + <a class="Label" name="Figure-11" id="Figure-11"></a> + + <div class="figure" style="max-width: 100%;"> + <img class="figure" src="FinalResultLoaded.png" alt= + "figure FinalResultLoaded.png" style= + "max-width: 1074px; max-height: 789px;" /> + + <div class="caption"> + Figure 11 Final result loaded + </div> + </div> + </div> + </li> + </ol> + + <h1 class="Section"><a class="toc" name="toc-Section-5" id= + "toc-Section-5">5</a> Common problems</h1> + + <ul> + <li>The Toolbox fails to start the cloud instance + <ul> - <li> - Re-check your credentials - </li> - <li> - If your credentials are correct, please turn on debug logging (start the Toolbox executable with “toolbox -console -consolelog”) and send us the output. You might have encountered a bug in cloud based distributed TLC. - </li> + <li>Re-check your credentials</li> + + <li>If your credentials are correct, please turn on debug + logging (start the Toolbox executable with + "toolbox -console + -consolelog") and send us the output. You + might have encountered a bug in cloud based distributed + TLC.</li> </ul> - </li> + </li> - <li> - The cloud instance sends "system notification" emails warning that the remaining disc space is at a critical level: - <pre class="listing"> + <li>The cloud instance sends "system notification" emails + warning that the remaining disc space is at a critical level: + <pre class="listing"> <i>"tlc :: tlc :: Disk usage in percent CRITICALs: / is 92.23..."</i> - </pre> - <ul> - <li> - A long running TLC process writes unexplored states to the system disk for later exploration. With very large models, this can potentially use up all of the available disk space. If this happens, TLC will crash. Thus, if you received such a warning, please create a checkpoint, <a class="URL" href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-add-volume-to-instance.html">attach a larger disk to your VM</a> and continue TLC by recovering from the checkpoint. - <ul> - <li> - <a class="URL" href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-connect-to-instance-linux.html#using-putty">Connect to the cloud instance with ssh</a> - </li> - <li> - <pre class="listing"> + +</pre> + + <ul> + <li>A long running TLC process writes unexplored states + to the system disk for later exploration. With very large + models, this can potentially use up all of the available + disk space. If this happens, TLC will crash. Thus, if you + received such a warning, please create a checkpoint, + <a class="URL" href= + "http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-add-volume-to-instance.html"> + attach a larger disk to your VM</a> and continue TLC by + recovering from the checkpoint. + + <ul> + <li><a class="URL" href= + "http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-connect-to-instance-linux.html#using-putty"> + Connect to the cloud instance with ssh</a></li> + + <li> + <pre class="listing"> # Download a jmx commandline client wget http://crawler.archive.org/cmdline-jmxclient/cmdline-jmxclient-0.10.3.jar - </pre> - </li> - <li> - <pre class="listing"> + +</pre> + </li> + + <li> + <pre class="listing"> # Connect to the locally running TLC and have it create a checkpoint java -jar cmdline-jmxclient-0.10.3.jar - localhost:5400 tlc2.tool:type=ModelChecker checkpoint - </pre> - </li> - <li> - Re-attach to the screen session running TLC and terminate TLC wth CTRL^C - </li> - <li> - <pre class="listing"> + +</pre> + </li> + + <li>Re-attach to the screen session running TLC and + terminate TLC wth CTRL^C</li> + + <li> + <pre class="listing"> # Restart TLC and have it recover from the checkpoint created java -jar tla2tools.jar -recover /path/to/states/15-12-16-12-16-04/ - </pre> - </li> - </ul> - </li> - </ul> - </li> - <ul> - <li> - The cloud instance sends "system notification" email warnings directly after startup. - <ul> - <li> - Ignore these emails :-). - </li> - <li> - This happens primarily on Azure when the monitoring runs too earlier before the cloud instance is fully started. - </li> - </ul> - </li> - <li> - The runtime statistics (web browser) indicate that TLC has finished model checking, but no result is sent via email. - <ul> - <li> - Check your email spam folder if the model checking result has incorrectly been classified as spam - </li> - <li> - Another reason why mail delivery might fail, are too strict spam counter measures at the mail server level. You might want to try to use a different email address (domain part) in the future. For Azure, it works best to use an Outlook.com email address. - </li> - <ul> - <li> - Copy & paste the MC.out content from the browser window into a plain text file and load it from there (see <a class="Reference" href="#enu:Import-the-MC.out">4↑</a>) - </li> - </ul> - </ul> - </li> + +</pre> + </li> + </ul> + </li> </ul> -</ul> - + </li> - - -</div> + <li style="list-style: none; display: inline"> + <ul> + <li>The cloud instance sends "system notification" email + warnings directly after startup. + + <ul> + <li>Ignore these emails :-).</li> + + <li>This happens primarily on Azure when the + monitoring runs too earlier before the cloud instance + is fully started.</li> + </ul> + </li> + + <li>The runtime statistics (web browser) indicate that + TLC has finished model checking, but no result is sent + via email. + + <ul> + <li>Check your email spam folder if the model + checking result has incorrectly been classified as + spam</li> + + <li>Another reason why mail delivery might fail, are + too strict spam counter measures at the mail server + level. You might want to try to use a different email + address (domain part) in the future. For Azure, it + works best to use an Outlook.com email address.</li> + + <li style="list-style: none; display: inline"> + <ul> + <li>Copy & paste the MC.out content from the + browser window into a plain text file and load it + from there (see <a class="Reference" href= + "#enu:Import-the-MC.out">4↑</a>)</li> + </ul> + </li> + </ul> + </li> + </ul> + </li> + </ul> + </div> </body> </html> diff --git a/org.lamport.tla.toolbox.doc/html/model/advanced-page.html b/org.lamport.tla.toolbox.doc/html/model/advanced-page.html index 2f5c0115eb4994ed0fffafd1c2dd1c9437a2e83b..e522d7323e7a0ee1cfa429c0fd501aa02c400e03 100644 --- a/org.lamport.tla.toolbox.doc/html/model/advanced-page.html +++ b/org.lamport.tla.toolbox.doc/html/model/advanced-page.html @@ -196,7 +196,11 @@ the depth of its search. (Limiting the depth ensures that only a finite set of states is explored, even if the complete set of reachable states is infinite.) With depth-first search, TLC will usually not produce -a shortest-length error trace. +a shortest-length error trace. When running in depth-first +mode, TLC does not compute the entire state graph and does not use a state +queue, so it does not produce any + <i>Diameter</i> or <i>Queue size</i> + statistics. </p> <p> <b>Warning:</b> Depth-first search is an experimental TLC @@ -226,9 +230,21 @@ If you want to know what specifying the <em>Seed</em> and <em>Aril</em> does, look them up in <em><a href= "http://research.microsoft.com/en-us/um/people/lamport/tla/book.html" target="_blank"> -Specifying Systems</a></em>. +Specifying Systems</a></em>. When run in simulation mode, the only +statistics TLC reports are for <i>States Found</i>. </p> +<h3>Verify temporal properties upon termination only</h3> +<p> +Defer the verification of temporal properties (liveness) to the end of model checking. +<b>This reduces the overall model checking time</b> with the additional side effect that invariant (safety) +violations will always be found first. In other words, check liveness only after the <em>complete</em> +state space has been checked for safety violations. +If unchecked, temporal properties are checked periodically on the <em>incomplete</em> +state graph. Defering verification of temporal properties is especially useful if it is highly +likely that the model does not violate its temporal properties (e.g. a smaller instance +of the model has successfully been verified for liveness violations). + <h3>Fingerprint seed index</h3> <p> TLC saves only 64-bit fingerprints (hashes) of the reachable states that it finds, not the @@ -254,9 +270,24 @@ Contact us for more information. <h3> Cardinality of largest enumerable set</h3> If TLC tries to enumerate the elements of a set, it will report an error if the set -contains more than this number of elements. TLC enumerates sets when computing -the set of all initial states. If you are trying to run a spec with lots of -initial states, you may have to increase the value of this parameter. +contains more than this number of elements. + +<h3>Visualize state graph after completion of model checking</h3> +<p> +Visualize the generated state graph graphically after completion of model checking. The +visualization helps to better understand the the system being specified. Initial states are represented by gray vertices. +<p><b>Warning: </b>Can reasonably only visualize small state graphs with a few dozen to hundred states.</b></p> +<p> +In order to visualize a state graph, the path to the <em>dot</em> executable of the +<a href="http://www.graphviz.org/">GraphViz</a> project has to be set on the <em>State Graph</em> preference +page on the File/Preferences menu. On macOS dot is most easily installed via the ports system. On Windows, can +be obtained through Cygwin. +</p> +<p> +On Windows and Linux the state graph will be visualized with either the built-in or standalone PDF viewer +depending on which is selected on the PDF viewer preference page +(selecting the standalone viewer is advised for best results). +</p> <h3><A name="jvmargs">JVM arguments</A></h3> @@ -296,4 +327,4 @@ feature with care. </hr> </body> -</html> \ No newline at end of file +</html> diff --git a/org.lamport.tla.toolbox.doc/html/model/executing-tlc.html b/org.lamport.tla.toolbox.doc/html/model/executing-tlc.html index 94762a93b2c3bb9f695e9f2908f56804379d7136..372384bd5af702212a3caff7a0842bf5d285a477 100644 --- a/org.lamport.tla.toolbox.doc/html/model/executing-tlc.html +++ b/org.lamport.tla.toolbox.doc/html/model/executing-tlc.html @@ -223,6 +223,16 @@ the Toolbox should <a href="#validating">validate the model</a> when it is saved The model is automatically saved when you run or validate it, so you will probably have no reason to save it explicitly. </p> + +<h3>Take snapshot of model after completion of model checking</h3> +<p> +Re-running a model erases the corresponding model checking results if any. With snapshots +activate, the Toolbox creates a history of model runs. The history allows one to trace back +the evolution of the specification and corresponding model. +</p> +<p>Snapshots can be deleted in the Spec Explorer as needed. +</p> + <h3>Maximum JVM Heap Size</h3> <p> This is the value to which the <em>Maximum JVM heap size</em> parameter @@ -240,7 +250,7 @@ then the model will remain <A href="about-models.html#locking">locked</A>. </p> -<h3><A name="maximum-tail-length">Maximum Tail Length of Trace Explorer States</A></h3> +<h3><A name="maximum-tail-length">Default number of states shown in error traces</A></h3> <p> The maximum number of states of a trace that is displayed in the Trace Explorer when @@ -340,4 +350,4 @@ You can help make that happen by <a href="../trouble/trouble.html#reporting">rep </hr> </body> -</html> \ No newline at end of file +</html> diff --git a/org.lamport.tla.toolbox.doc/html/model/model-values.html b/org.lamport.tla.toolbox.doc/html/model/model-values.html index 46323a970cce566ce8ced051daf294439236d444..168d36d29c415135a07f9cc99be23dff72b340fc 100644 --- a/org.lamport.tla.toolbox.doc/html/model/model-values.html +++ b/org.lamport.tla.toolbox.doc/html/model/model-values.html @@ -1,229 +1,237 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> -<!-- This is file org.lamport.tla.toobox.doc/html/mode/model-values.html --> - - -<html> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <LINK href="../style.css" rel="stylesheet" type="text/css"> - -<title>Model Values and Symmetry</title> -</head> -<!-- a comment here --> - -<body> -<h1>Model Values and Symmetry</h1> - -<pre> -Contents - <a href="#values">Ordinary Model Values</a> - <a href="#typed-values">Typed Model Values</a> - <a href="#using">Using Model Values</a> - <a href="#symmetry">Symmetry</a> -</pre> - - <h2><a name="values">Ordinary Model Values</a></h2> - <P> -A model must specify the values of all declared constants. -You will sometimes want to let a constant equal an unspecified value or a finite -set of unspecified values. For example, the spec might have a -constant <code>Proc</code> whose value -represents a set of processes. -You could just let a process be a number, substituting an ordinary value - like <code>{1, 2, 3}</code> -for <code>Proc</code> . However, a better way is to represent a process by a TLC -<em>model value</em>. A model value is an unspecified value -that TLC considers to be unequal to any value that you can express -in TLA<sup>+</sup>. You can substitute the - set <code>{p1, p2, p3}</code> of three model values -for <code>Proc</code> . If by mistake you write an expression -like <code>p+1</code> where the value of <code>p</code> is -a process, TLC will report an error when it tries to evaluate that expression -because it knows that a process is a model value and thus not a number. -An important reason for -substituting a set of model values for <code>Proc</code> is -to let TLC take advantage of symmetry, as described <a href="#symmetry">below</a>. -</P> - -<P> -There is one particular TLA<sup>+</sup> specification idiom -that requires you to substitute a model value for a defined operator. -Here is a typical example of this idiom. -<pre> - NotANat == CHOOSE n : n \notin Nat -</pre> -It defines <code>NotANat</code> to be an arbitrary value -that is not a natural number. TLC cannot evaluate this definition because -it cannot evaluate the unbounded <code>CHOOSE</code> expression. -To allow TLC to handle the spec, you need to substitute a model value -for <code>NotANat</code> . The best model value to substitute for it -is one named <code>NotANat</code> . You can do this with the -<a href="advanced-page.html#override">Definition Override</a> section of -the <a href="advanced-page.html">Advanced Options Page</a>. -</P> - - - -<p> -There are some uses of model values that -can't be specified with the <a href="overview-page.html#what-is-model">What is the model?</a> -section of the <A href="overview-page.html">Model Overview Page</A>. -If you encounter such a problem, you can declare your own set of model values -with the <A href="advanced-page.html#additional">Additional Definitions</a> section -of the <A href="advanced-page.html"> Advanced Options Page</A> and then use them -as ordinary values in expressions of the model. -</p> - -<h2><a name="typed-values">Typed Model Values</a></h2> - -<p> -Suppose that, by mistake, you write the -expression <code>p=2</code> where <code>p</code> is -a process. If you substitute a set of ordinary model values for the set -of processes, TLC will simply obtain the value <code>FALSE</code> -if it evalutes this expression, since it considers an ordinary model value -to be different from any other value. To allow TLC to detect this kind of error, -you can use a set of <em>typed</em> model values. TLC considers a typed model value -to be unequal to any other model value of the same type. However, it produces an -error when evaluating an expression requires it to determine if a typed model value -is equal to any value other than a model value of the same type or an ordinary model value. -</p> - -<p> -A model-value type consists of -a single letter, so there are 52 different types because <code>a</code> -and <code>A</code> are different types. -(TLC actually accepts digits and underscore as types; don't use them.) -A model value has -type <code>T</code> if and only if its name begins with the two -characters <code>T_</code> . -</p> - -<h2><a name="using">Using Model Values</a></h2> - -<p> -A model value declared in the model can be used as an ordinary value in any expression that -is part of the model's specification. For example, suppose your spec has a -constant <code>Proc</code> that represents a set of processes -and a constant <code>Leader</code> that is some element -of <code>Proc</code> . You can substitute the -set <code>{p1, p2, p3}</code> of model values -for <code>Proc</code> using the <em>Set of model values</em> -option of the <a href="overview-page.html#what-is-model">What is the model?</a> -section of the <A href="overview-page.html">Model Overview Page</A>. You -can then -substitute <code>p1</code> -for <code>Leader</code> using the <em>Ordinary assignment</em> -option of that section. -</p> - -<p> -Model values can be declared in the following places: -<ul> -<li>When assigning a value to a constant parameter in the -<a href="overview-page.html#what-is-model">What is the model?</a> section of -the <em>Model Overview Page</em>, by choosing the <em>Model value</em> -or <em>Set of model values</em> option.</li> -<li>When overriding a definition in the <a href="advanced-page.html#override">Definition override</a> -section of the <em>Advanced Options Page</em>, by choosing the <em>Model value</em> option. -</li> - -<li> -In the <a href="advanced-page.html#model">Model Values</a> section of the <em>Advanced Options Page</em>. -</li> -</ul> - -</p> - - -<h2><a name="symmetry">Symmetry</a></h2> -<p> - -Consider a specification of a memory system containing a declared -constant <code>Val</code> that represents the set of -possible values of a memory register. The set <code>Val</code> of values is probably a -<em>symmetry set</em> for the memory system's behavior specification, -meaning that permuting -the elements in the set of values does not change whether or not -a behavior satisfies that behavior spec. TLC can -take advantage of this to speed up its checking. - -Suppose we substitute a -set <code>{v1, v2, v3}</code> of model values -for <code>Val</code> . We can use -the -<em>Symmetry set</em> -option of the <a href="overview-page.html#what-is-model">What is the model?</a> -section of the <A href="overview-page.html">Model Overview Page</A> -to declare this set of model values to be a symmetry set of the behavior spec. -This will reduce the number of reachable states that TLC has to examine -by up to 3!, or 6. -</p> - -<p> -You can declare more than one set of model values to be a symmetry set. -However, the union of all the symmetry sets cannot contain two -typed model values with different types. -</p> -<p> -TLC does not check if a set you declare to be a symmetry set really -is one. If you declare a set to be a symmetry set and it isn't, then -TLC can fail to find an error -that it otherwise would find. - -An expression is <i>symmetric</i> for a set <code>S</code> if and only if -interchanging any two values of <code>S</code> does not -change the value of the expression. The expression - <code>{{v1, v2}, {v1, v3}, {v2, v3}}</code> -is symmetric for the set - <code>{v1, v2, v3}</code> -- -for example, interchanging <code>v1</code> and <code>v3</code> -in this expression produces - <code>{{v3, v2}, {v3, v1}, {v2, v1}}</code>, -which is equal to the original expression. - -You should declare a set <code>S</code> of model values to be a symmetry set only if -the specification and all properties you are checking are symmetric for <code>S</code> -after the substitutions for constants and defined operators specified by the model -are made. - -For example, you should not declare - <code>{v1, v2, v3}</code> -to be a symmetry set if the model substitutes <code>v1</code> for some constant. - -The only TLA+ operator that can produce a non-symmetric expression when applied to -a symmetric expression is <code>CHOOSE</code>. For example, the expression - <pre> CHOOSE x \in {v1, v2, v3} : TRUE </pre> -is not symmetric for <code>{v1, v2, v3}</code>. -</p> - - -<p> -Symmetry sets should not be used when checking liveness properties. -Doing so can make TLC fail to find errors, or to report nonexistent errors. - -</p> -<!-- -<p> -Symmetry sets are used only in <a href="advanced-page.html#model-mode">model-checking mode</a>. -See Section 14.3.4 (page 245) of -<em><a href= -"http://research.microsoft.com/en-us/um/people/lamport/tla/book.html" target="_blank"> -Specifying Systems</a></em> for an explanation of symmetry and how TLC uses it. -</p> - --> -<hr> -<!-- delete rest of line to comment out -<dl> -<dt><b><font color=#0000c0>Subtopics</font></b></dt> -<dd> <A href=""> TOPIC </A></dd> -<dd> <A href=""> TOPIC </A></dd> -</dl> - --> -<!-- delete rest of line to comment out --> -<a href = "overview-page.html">↑ Model Overview Page</a> -<!-- --> -</hr> - -</body> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<!-- This is file org.lamport.tla.toobox.doc/html/mode/model-values.html --> + + +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <LINK href="../style.css" rel="stylesheet" type="text/css"> + +<title>Model Values and Symmetry</title> +</head> +<!-- a comment here --> + +<body> +<h1>Model Values and Symmetry</h1> + +<pre> +Contents + <a href="#values">Ordinary Model Values</a> + <a href="#typed-values">Typed Model Values</a> + <a href="#using">Using Model Values</a> + <a href="#symmetry">Symmetry</a> +</pre> + + <h2><a name="values">Ordinary Model Values</a></h2> + <P> +A model must specify the values of all declared constants. +You will sometimes want to let a constant equal an unspecified value or a finite +set of unspecified values. For example, the spec might have a +constant <code>Proc</code> whose value +represents a set of processes. +You could just let a process be a number, substituting an ordinary value + like <code>{1, 2, 3}</code> +for <code>Proc</code> . However, a better way is to represent a process by a TLC +<em>model value</em>. A model value is an unspecified value +that TLC considers to be unequal to any value that you can express +in TLA<sup>+</sup>. You can substitute the + set <code>{p1, p2, p3}</code> of three model values +for <code>Proc</code> . If by mistake you write an expression +like <code>p+1</code> where the value of <code>p</code> is +a process, TLC will report an error when it tries to evaluate that expression +because it knows that a process is a model value and thus not a number. +An important reason for +substituting a set of model values for <code>Proc</code> is +to let TLC take advantage of symmetry, as described <a href="#symmetry">below</a>. +</P> + +<P> +There is one particular TLA<sup>+</sup> specification idiom +that requires you to substitute a model value for a defined operator. +Here is a typical example of this idiom. +<pre> + NotANat == CHOOSE n : n \notin Nat +</pre> +It defines <code>NotANat</code> to be an arbitrary value +that is not a natural number. TLC cannot evaluate this definition because +it cannot evaluate the unbounded <code>CHOOSE</code> expression. +To allow TLC to handle the spec, you need to substitute a model value +for <code>NotANat</code> . The best model value to substitute for it +is one named <code>NotANat</code> . This is done in the +<a href="advanced-page.html#override">Definition Override</a> section of +the <a href="advanced-page.html">Advanced Options Page</a>. The Toolbox will +create the appropriate entry in that section when it creates a model +if it finds a definition having the precise +syntax above or the syntax +<pre> + NotANat == CHOOSE n : ~(n \in Nat) +</pre> +where <code>Nat</code> can be any expression, and <code>NotANat</code> and <code>n</code> +can be any identifiers. +</P> + + + +<p> +There are some uses of model values that +can't be specified with the <a href="overview-page.html#what-is-model">What is the model?</a> +section of the <A href="overview-page.html">Model Overview Page</A>. +If you encounter such a problem, you can declare your own set of model values +with the <A href="advanced-page.html#additional">Additional Definitions</a> section +of the <A href="advanced-page.html"> Advanced Options Page</A> and then use them +as ordinary values in expressions of the model. +</p> + +<h2><a name="typed-values">Typed Model Values</a></h2> + +<p> +Suppose that, by mistake, you write the +expression <code>p=2</code> where <code>p</code> is +a process. If you substitute a set of ordinary model values for the set +of processes, TLC will simply obtain the value <code>FALSE</code> +if it evalutes this expression, since it considers an ordinary model value +to be different from any other value. To allow TLC to detect this kind of error, +you can use a set of <em>typed</em> model values. TLC considers a typed model value +to be unequal to any other model value of the same type. However, it produces an +error when evaluating an expression requires it to determine if a typed model value +is equal to any value other than a model value of the same type or an ordinary model value. +</p> + +<p> +A model-value type consists of +a single letter, so there are 52 different types because <code>a</code> +and <code>A</code> are different types. +(TLC actually accepts digits and underscore as types; don't use them.) +A model value has +type <code>T</code> if and only if its name begins with the two +characters <code>T_</code> . +</p> + +<h2><a name="using">Using Model Values</a></h2> + +<p> +A model value declared in the model can be used as an ordinary value in any expression that +is part of the model's specification. For example, suppose your spec has a +constant <code>Proc</code> that represents a set of processes +and a constant <code>Leader</code> that is some element +of <code>Proc</code> . You can substitute the +set <code>{p1, p2, p3}</code> of model values +for <code>Proc</code> using the <em>Set of model values</em> +option of the <a href="overview-page.html#what-is-model">What is the model?</a> +section of the <A href="overview-page.html">Model Overview Page</A>. You +can then +substitute <code>p1</code> +for <code>Leader</code> using the <em>Ordinary assignment</em> +option of that section. +</p> + +<p> +Model values can be declared in the following places: +<ul> +<li>When assigning a value to a constant parameter in the +<a href="overview-page.html#what-is-model">What is the model?</a> section of +the <em>Model Overview Page</em>, by choosing the <em>Model value</em> +or <em>Set of model values</em> option.</li> +<li>When overriding a definition in the <a href="advanced-page.html#override">Definition override</a> +section of the <em>Advanced Options Page</em>, by choosing the <em>Model value</em> option. +</li> + +<li> +In the <a href="advanced-page.html#model">Model Values</a> section of the <em>Advanced Options Page</em>. +</li> +</ul> + +</p> + + +<h2><a name="symmetry">Symmetry</a></h2> +<p> + +Consider a specification of a memory system containing a declared +constant <code>Val</code> that represents the set of +possible values of a memory register. The set <code>Val</code> of values is probably a +<em>symmetry set</em> for the memory system's behavior specification, +meaning that permuting +the elements in the set of values does not change whether or not +a behavior satisfies that behavior spec. TLC can +take advantage of this to speed up its checking. + +Suppose we substitute a +set <code>{v1, v2, v3}</code> of model values +for <code>Val</code> . We can use +the +<em>Symmetry set</em> +option of the <a href="overview-page.html#what-is-model">What is the model?</a> +section of the <A href="overview-page.html">Model Overview Page</A> +to declare this set of model values to be a symmetry set of the behavior spec. +This will reduce the number of reachable states that TLC has to examine +by up to 3!, or 6. +</p> + +<p> +You can declare more than one set of model values to be a symmetry set. +However, the union of all the symmetry sets cannot contain two +typed model values with different types. +</p> +<p> +TLC does not check if a set you declare to be a symmetry set really +is one. If you declare a set to be a symmetry set and it isn't, then +TLC can fail to find an error +that it otherwise would find. + +An expression is <i>symmetric</i> for a set <code>S</code> if and only if +interchanging any two values of <code>S</code> does not +change the value of the expression. The expression + <code>{{v1, v2}, {v1, v3}, {v2, v3}}</code> +is symmetric for the set + <code>{v1, v2, v3}</code> -- +for example, interchanging <code>v1</code> and <code>v3</code> +in this expression produces + <code>{{v3, v2}, {v3, v1}, {v2, v1}}</code>, +which is equal to the original expression. + +You should declare a set <code>S</code> of model values to be a symmetry set only if +the specification and all properties you are checking are symmetric for <code>S</code> +after the substitutions for constants and defined operators specified by the model +are made. + +For example, you should not declare + <code>{v1, v2, v3}</code> +to be a symmetry set if the model substitutes <code>v1</code> for some constant. + +The only TLA+ operator that can produce a non-symmetric expression when applied to +a symmetric expression is <code>CHOOSE</code>. For example, the expression + <pre> CHOOSE x \in {v1, v2, v3} : TRUE </pre> +is not symmetric for <code>{v1, v2, v3}</code>. +</p> + + +<p> +Symmetry sets should not be used when checking liveness properties. +Doing so can make TLC fail to find errors, or to report nonexistent errors. + +</p> +<!-- +<p> +Symmetry sets are used only in <a href="advanced-page.html#model-mode">model-checking mode</a>. +See Section 14.3.4 (page 245) of +<em><a href= +"http://research.microsoft.com/en-us/um/people/lamport/tla/book.html" target="_blank"> +Specifying Systems</a></em> for an explanation of symmetry and how TLC uses it. +</p> + --> +<hr> +<!-- delete rest of line to comment out +<dl> +<dt><b><font color=#0000c0>Subtopics</font></b></dt> +<dd> <A href=""> TOPIC </A></dd> +<dd> <A href=""> TOPIC </A></dd> +</dl> + --> +<!-- delete rest of line to comment out --> +<a href = "overview-page.html">↑ Model Overview Page</a> +<!-- --> +</hr> + +</body> </html> \ No newline at end of file diff --git a/org.lamport.tla.toolbox.doc/html/model/results-page.html b/org.lamport.tla.toolbox.doc/html/model/results-page.html index 4eb1bbe1299981a2e10faee5faadaf335af1e17d..d39886a96497462976f9f25ed107abca9d14bfdf 100644 --- a/org.lamport.tla.toolbox.doc/html/model/results-page.html +++ b/org.lamport.tla.toolbox.doc/html/model/results-page.html @@ -31,7 +31,11 @@ the spec and open another one, or if you close the Toolbox, it will be there the open the spec and this model. The information is lost only if you run TLC on the model again or delete the model or the entire spec. So, don't re-run TLC on this model until you are sure you're through examining the results of running it the last time. - Remember that you can create a clone of this model to run TLC on. (See the + </p><p>Note that you can set the Toolbox to automatically snapshot (clone) the model + when you re-run it. Please click the corresponding checkbox on the TLC Model Checker + preference page on the File/Preferences menu. A model snapshot appears in the spec explorer + tree as a child of its (parent) model. The snapshot's name indicates the date and time of its creation. + Alternatively, you can manually create a clone of this model to run TLC on. (See the <a href="about-models.html"><em>Models</em></a> help page.) </P> @@ -91,15 +95,16 @@ it should be, so the spec cannot reach states that represent reachable states of the actual system being specified. This kind of error leads to a violation of desired liveness properties, but does not cause any violation of safety. The coverage statistics give you a way of catching such an error even if you're not -checking liveness. +checking liveness. </p> <p>Coverage shows the number of times each sub-action of the next-state relation has been used to compute a successor state. Don't worry about how TLC decides what a sub-action is; just look at the values reported and the corresponding parts of -the specification to which they refer. A sub-action that is executed zero times usually -indicates an error in the spec. Left-clicking on an entry takes you to the indicated +the specification to which they refer. A sub-action that is executed zero times +- emphasized by yellow background coloring of the table row - usually indicates an +error in the spec. Left-clicking on an entry takes you to the indicated location. </p> @@ -155,4 +160,4 @@ the <a href="executing-tlc.html#console">TLC Console View</a>. </hr> </body> -</html> \ No newline at end of file +</html> diff --git a/org.lamport.tla.toolbox.doc/html/spec/pretty-printing.html b/org.lamport.tla.toolbox.doc/html/spec/pretty-printing.html index c790e83340d370907f1c9a3d06e46f8bb72ad93c..27b090ce4c394ed7fe21d43fc6e80ff6cfb33c97 100644 --- a/org.lamport.tla.toolbox.doc/html/spec/pretty-printing.html +++ b/org.lamport.tla.toolbox.doc/html/spec/pretty-printing.html @@ -82,6 +82,12 @@ Use the <samp>+</samp> and <samp>-</samp> keys to zoom in and out. The built-in viewer option is not offered on Mac OS X. +<h4>Regenerate pretty-printed PDF of spec save</h4> + +When checked, the Toolbox automatically updates the pretty-printed version of the module every time +its corresponding editor is saved. +This is especially useful in presentations and trainings when the spec editor and PDF viewer +are arranged side by side to interactively build-up the spec but show attendees the pretty-printed version. <h4>Shade comments</h4> Comments are shaded in gray. This is the default. The darkness of the shading is specified @@ -104,6 +110,14 @@ not to be shaded. (The code's comments are still shaded.) Number lines in the PDF output. (This is independent of the line-numbering option for the module editor.) The default is unnumbered lines. +<h4>Specify dot command</h4> + +This is the command with which the Toolbox calls <samp>dot</samp> +to produce state graph visualizations. The default value <samp>dot</samp> +should work if the <samp>dot</samp> program is installed on your +computer. However, if it is not properly installed, you may have to prefix it with the path name +of the program's directory. + <h4>Specify pdflatex command</h4> This is the command with which the Toolbox calls <samp>pdflatex</samp> @@ -129,4 +143,4 @@ See the <b>Shade comments</b> option above. </hr> </body> -</html> \ No newline at end of file +</html> diff --git a/org.lamport.tla.toolbox.doc/html/trouble/trouble.html b/org.lamport.tla.toolbox.doc/html/trouble/trouble.html index 80466bd936aa86edac39c47050dd2d70cde57553..72fafe755fd72fe2ff0f44ed39d35bba4116375f 100644 --- a/org.lamport.tla.toolbox.doc/html/trouble/trouble.html +++ b/org.lamport.tla.toolbox.doc/html/trouble/trouble.html @@ -143,16 +143,13 @@ When reporting a bug, please include all details. It will be helpful if you can include the relevant part of the Toolbox's log, which is the file named <samp>.log</samp>. -On Windows and Linux, the <samp>.log</samp> file is in -the folder <samp>workspace/.metadata</samp> . +The <samp>.log</samp> file is in the + <samp>.tlaplus/.metadata/</samp> folder in your home +directory. -That folder is in the same folder as the executable file with which -you run the Toolbox. - -On the Mac, it is in the folder -<samp>toolbox.app/Contents/MacOS/workspace/.metadata</samp>, -where <samp>toolbox.app</samp> is the folder that you -execute to run the Toolbox. +On Windows, you can find out where your home directory is located, by +looking at the <samp>%HOMEPATH%</samp> environment variable. On +Linux and Mac, check the $HOME variable. (The log contains enough timestamped entries for you to figure out what part of it relates to the problem you are reporting.) diff --git a/org.lamport.tla.toolbox.doc/html/update/update-preferences.html b/org.lamport.tla.toolbox.doc/html/update/update-preferences.html index 5899073120bbca0bd6a5d72acc7a8d7451ff730a..ed6a6a28598e7e8f3a4154a685835f26a6c2894c 100644 --- a/org.lamport.tla.toolbox.doc/html/update/update-preferences.html +++ b/org.lamport.tla.toolbox.doc/html/update/update-preferences.html @@ -22,7 +22,11 @@ are scheduled and performed from the Checking the box at the top of the page enables automatic searching for and notification of available updates. -The rest of the preference page then allows you can schedule the search and notification. +<p> +You can also opt-in to receive experimental features of the TLA Toolbox and TLC. Note though, that +experimental features might not work as intended and can lead to bogus behavior. Not for the faint-hearted.</p> + +The rest of the preference page then allows you to schedule the search and notification. </p> <h3>Update schedule</h3> <p>You can choose among the following @@ -30,8 +34,8 @@ schedules for checking for updates:</p> <ul> <li>No automatic search</li> <li>On each startup (default)</li> - <li>Every day at a specific time (e.g., 3:00PM) *</li> - <li>On a scheduled day of the week at a specific time (e.g., Monday 8:00AM) *</li> + <li>Every day at a specific time (e.g., 3:00PM)</li> + <li>On a scheduled day of the week at a specific time (e.g., Monday 8:00AM)</li> </ul> <p>If the Toolbox is not running at the scheduled time, the search will be done when the Toolbox is next started. diff --git a/org.lamport.tla.toolbox.doc/pdfs/SRC-TN-1997-006A.pdf b/org.lamport.tla.toolbox.doc/pdfs/SRC-TN-1997-006A.pdf new file mode 100644 index 0000000000000000000000000000000000000000..75e9f60a6f84b80c404b3e8fe579b59f8b6d69f4 Binary files /dev/null and b/org.lamport.tla.toolbox.doc/pdfs/SRC-TN-1997-006A.pdf differ diff --git a/org.lamport.tla.toolbox.doc/pdfs/book-02-08-08.pdf b/org.lamport.tla.toolbox.doc/pdfs/book-02-08-08.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1917deeeb22a18d7e83a797ca6d5421077a8e856 Binary files /dev/null and b/org.lamport.tla.toolbox.doc/pdfs/book-02-08-08.pdf differ diff --git a/org.lamport.tla.toolbox.doc/pdfs/c-manual.pdf b/org.lamport.tla.toolbox.doc/pdfs/c-manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1e10bbca317ef227e54e7d3117907dd7a3f8c8f9 Binary files /dev/null and b/org.lamport.tla.toolbox.doc/pdfs/c-manual.pdf differ diff --git a/org.lamport.tla.toolbox.doc/pdfs/summary-standalone.pdf b/org.lamport.tla.toolbox.doc/pdfs/summary-standalone.pdf new file mode 100644 index 0000000000000000000000000000000000000000..215983c9bca4c59ea86884825f23708b2d4e983a Binary files /dev/null and b/org.lamport.tla.toolbox.doc/pdfs/summary-standalone.pdf differ diff --git a/org.lamport.tla.toolbox.doc/plugin.xml b/org.lamport.tla.toolbox.doc/plugin.xml index 91872130326b1985764916483044c2a2bbcfe674..8ce134611eb40298ac32e6f8cec01bc624c33dea 100644 --- a/org.lamport.tla.toolbox.doc/plugin.xml +++ b/org.lamport.tla.toolbox.doc/plugin.xml @@ -24,5 +24,207 @@ file="tocsamples.xml"> </toc> </extension> + <extension + point="org.eclipse.ui.commands"> + <command + defaultHandler="org.lamport.tla.toolbox.doc.handler.HelpContentsHandler" + id="org.lamport.tla.toolbox.doc.contents" + name="Table of Contents"> + </command> + <command + defaultHandler="org.lamport.tla.toolbox.doc.handler.HelpPDFHandler" + id="org.lamport.tla.toolbox.doc.pdf" + name="Help located in a local PDF"> + <commandParameter + id="org.lamport.tla.toolbox.doc.pdf.file" + name="pdf" + optional="false"> + </commandParameter> + <commandParameter + id="org.lamport.tla.toolbox.doc.pdf.name" + name="name" + optional="false"> + </commandParameter> + </command> + <command + defaultHandler="org.lamport.tla.toolbox.doc.handler.HelpURLHandler" + id="org.lamport.tla.toolbox.doc.url" + name="Help located at a URL"> + <commandParameter + id="org.lamport.tla.toolbox.doc.url.name" + name="name" + optional="false"> + </commandParameter> + </command> + </extension> + <extension + point="org.eclipse.ui.menus"> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?before=toolbox.menuItem.dynamicHelp"> + <command + commandId="org.lamport.tla.toolbox.doc.contents" + id="toolbox.menuItem.contentsHelp" + label="Table of Contents" + mnemonic="T" + mode="FORCE_TEXT" + style="push" + tooltip="Opens Table of Contents"> + </command> + </menuContribution> + <!-- The Operators of TLA+ --> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?after=toolbox.command.help.tlaplus"> + <command + commandId="org.lamport.tla.toolbox.doc.pdf" + label="The Operators of TLA+" + mnemonic="O" + mode="FORCE_TEXT" + style="push" + tooltip="Opens the paper "The Operators of TLA+""> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.file" + value="SRC-TN-1997-006A.pdf"> + </parameter> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.name" + value="Operators of TLA+"> + </parameter> + </command> + </menuContribution> + <!-- The TLA+ Cheat Sheet: --> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?after=toolbox.command.help.tlaplus"> + <command + commandId="org.lamport.tla.toolbox.doc.pdf" + label="The TLA+ Cheat Sheet" + mnemonic="C" + mode="FORCE_TEXT" + style="push" + tooltip="Opens the paper "Summary of TLA+""> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.file" + value="summary-standalone.pdf"> + </parameter> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.name" + value="TLA+ Cheat Sheet"> + </parameter> + </command> + </menuContribution> + <!-- C PlusCal Manual --> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?after=toolbox.command.help.tlaplus"> + <command + commandId="org.lamport.tla.toolbox.doc.pdf" + label="PlusCal User Manual" + mnemonic="P" + mode="FORCE_TEXT" + style="push" + tooltip="Opens the paper "A PlusCal User’s Manual - C-Syntax""> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.file" + value="c-manual.pdf"> + </parameter> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.name" + value="PlusCal Manual"> + </parameter> + </command> + </menuContribution> + <!-- Specifying Systems --> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?after=toolbox.command.help.tlaplus"> + <command + commandId="org.lamport.tla.toolbox.doc.pdf" + label="Specifying Systems" + mnemonic="S" + mode="FORCE_TEXT" + style="push" + tooltip="Opens the book "Specifying Systems""> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.file" + value="book-02-08-08.pdf"> + </parameter> + <parameter + name="org.lamport.tla.toolbox.doc.pdf.name" + value="Specifying Systems"> + </parameter> + </command> + </menuContribution> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?before=toolbox.command.about"> + <command + commandId="org.lamport.tla.toolbox.doc.url" + label="TLA+ Video Course" + mnemonic="V" + mode="FORCE_TEXT" + style="push"> + <parameter + name="org.lamport.tla.toolbox.doc.url.name" + value="http://lamport.azurewebsites.net/video/videos.html"> + </parameter> + </command> + </menuContribution> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?before=toolbox.command.about"> + <command + commandId="org.lamport.tla.toolbox.doc.url" + label="TLA+ Examples" + mnemonic="E" + mode="FORCE_TEXT" + style="push"> + <parameter + name="org.lamport.tla.toolbox.doc.url.name" + value="https://github.com/tlaplus/Examples"> + </parameter> + </command> + </menuContribution> + <menuContribution + allPopups="false" + locationURI="menu:toolbox.menu.help?before=toolbox.command.about"> + <menu id="community" label="Community" mnemonic="Y"> + <command + commandId="org.lamport.tla.toolbox.doc.url" + label="TLA+ user group" + mnemonic="V" + mode="FORCE_TEXT" + style="push"> + <parameter + name="org.lamport.tla.toolbox.doc.url.name" + value="https://groups.google.com/forum/#!forum/tlaplus"> + </parameter> + </command> + <command + commandId="org.lamport.tla.toolbox.doc.url" + label="TLA+ reddit" + mnemonic="V" + mode="FORCE_TEXT" + style="push"> + <parameter + name="org.lamport.tla.toolbox.doc.url.name" + value="https://www.reddit.com/r/tlaplus/"> + </parameter> + </command> + <command + commandId="org.lamport.tla.toolbox.doc.url" + label="Learn TLA" + mnemonic="V" + mode="FORCE_TEXT" + style="push"> + <parameter + name="org.lamport.tla.toolbox.doc.url.name" + value="https://learntla.com/"> + </parameter> + </command> + </menu> + </menuContribution> + </extension> </plugin> diff --git a/org.lamport.tla.toolbox.doc/pom.xml b/org.lamport.tla.toolbox.doc/pom.xml index e4f890e7189b6a21c1c20ce3e3b1ec56922b395a..0f81f09d0326469a6d0d3f72759294acb3c1c633 100644 --- a/org.lamport.tla.toolbox.doc/pom.xml +++ b/org.lamport.tla.toolbox.doc/pom.xml @@ -11,7 +11,7 @@ </parent> <groupId>tlatoolbox</groupId> <artifactId>org.lamport.tla.toolbox.doc</artifactId> - <version>1.5.3-SNAPSHOT</version> + <version>1.5.4-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> <properties> <!-- Do not include non-code project in Sonar reporting. --> diff --git a/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/HelpActivator.java b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/HelpActivator.java new file mode 100644 index 0000000000000000000000000000000000000000..6d0b625e6bc124b3f8df5c3b8aaf1874a4b2914d --- /dev/null +++ b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/HelpActivator.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.doc; + +import org.lamport.tla.toolbox.AbstractTLCActivator; +import org.osgi.framework.BundleContext; + +public class HelpActivator extends AbstractTLCActivator { + + // The plug-in ID + public static final String PLUGIN_ID = "org.lamport.tla.toolbox.doc"; + private static HelpActivator plugin; + + public HelpActivator() { + super(PLUGIN_ID); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + } + + public static HelpActivator getDefault() { + return plugin; + } +} diff --git a/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpContentsHandler.java b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpContentsHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..8731022f16d9a2c2568d9aa39d2da155c047eeeb --- /dev/null +++ b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpContentsHandler.java @@ -0,0 +1,47 @@ +package org.lamport.tla.toolbox.doc.handler; + +import java.lang.reflect.Field; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.help.ui.internal.views.HelpView; +import org.eclipse.help.ui.internal.views.ReusableHelpPart; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; + +@SuppressWarnings("restriction") +public class HelpContentsHandler extends AbstractHandler implements IHandler { + + public Object execute(ExecutionEvent event) throws ExecutionException { + BusyIndicator.showWhile(null, new Runnable() { + public void run() { + final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + try { + final HelpView helpView = (HelpView) window.getActivePage().showView("org.eclipse.help.ui.HelpView", + null, IWorkbenchPage.VIEW_ACTIVATE); + + final Field f = HelpView.class.getDeclaredField("reusableHelpPart"); + f.setAccessible(true); + final ReusableHelpPart helpPart = (ReusableHelpPart) f.get(helpView); + helpPart.showPage(ReusableHelpPart.HV_ALL_TOPICS_PAGE, true); + } catch (final NoSuchFieldException e) { + e.printStackTrace(); + } catch (final SecurityException e) { + e.printStackTrace(); + } catch (final IllegalArgumentException e) { + e.printStackTrace(); + } catch (final IllegalAccessException e) { + e.printStackTrace(); + } catch (final PartInitException e) { + e.printStackTrace(); + } + } + }); + return null; + } +} diff --git a/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpPDFHandler.java b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpPDFHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..ca322aec9aa577e27a346e1fa7bca942fb9b563d --- /dev/null +++ b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpPDFHandler.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.doc.handler; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Platform; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.PartInitException; +import org.lamport.tla.toolbox.doc.HelpActivator; +import org.lamport.tla.toolbox.ui.view.PDFBrowser; +import org.lamport.tla.toolbox.util.UIHelper; +import org.osgi.framework.Bundle; + +public class HelpPDFHandler extends AbstractHandler implements IHandler { + + public Object execute(ExecutionEvent event) throws ExecutionException { + final String pdf = event.getParameter("org.lamport.tla.toolbox.doc.pdf.file"); + final String name = event.getParameter("org.lamport.tla.toolbox.doc.pdf.name"); + + // For historical reasons this preference is found in the tlatex bundle. Thus, + // we read the value from there, but don't refer to the corresponding string + // constants to not introduce a plugin dependency. + // org.lamport.tla.toolbox.tool.tla2tex.TLA2TeXActivator.PLUGIN_ID + // org.lamport.tla.toolbox.tool.tla2tex.preference.ITLA2TeXPreferenceConstants.EMBEDDED_VIEWER + final boolean useEmbeddedViewer = Platform.getPreferencesService() + .getBoolean("org.lamport.tla.toolbox.tool.tla2tex", "embeddedViewer", false, null); + + // Show a sandglass while loading (large) pdfs. + BusyIndicator.showWhile(Display.getCurrent(), new Runnable() { + public void run() { + try { + final File pdfFile = getDocFile("/pdfs/" + pdf); + if (useEmbeddedViewer) { + UIHelper.openEditorUnchecked( + // Referencing de.vonloesch... + // creates an _implicit_ + // dependency which is not made + // explicit in the bundle's + // Manifest + "de.vonloesch.pdf4eclipse.editors.PDFEditor", pdfFile, name, false); + } else { + final IViewPart view = UIHelper.openViewNoFocus(PDFBrowser.ID, name); + ((PDFBrowser) view).setInput(name, pdfFile.toURI().toURL().toString()); + } + } catch (PartInitException e) { + HelpActivator.getDefault().logError(e.getMessage(), e); + } catch (IOException e) { + HelpActivator.getDefault().logError(e.getMessage(), e); + } catch (URISyntaxException e) { + HelpActivator.getDefault().logError(e.getMessage(), e); + } + } + }); + return null; + } + + private File getDocFile(final String bundleRelativePath) throws IOException, URISyntaxException { + final Bundle bundle = Platform.getBundle(HelpActivator.PLUGIN_ID); + // Do not call URL#toURI. The call throws an exception due to invalid chars on macOS + // when the Toolbox is installed in (its default location) /Applications/TLA+ + // Toolbox.app/... + return new File(FileLocator.resolve(bundle.getEntry(bundleRelativePath)).getFile()); + } +} diff --git a/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpURLHandler.java b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpURLHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..05ccce35cbf33cc5694690f630b65b6dde67fcae --- /dev/null +++ b/org.lamport.tla.toolbox.doc/src/org/lamport/tla/toolbox/doc/handler/HelpURLHandler.java @@ -0,0 +1,26 @@ +package org.lamport.tla.toolbox.doc.handler; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; + +public class HelpURLHandler extends AbstractHandler implements IHandler { + + public Object execute(final ExecutionEvent event) throws ExecutionException { + try { + final URL url = new URL(event.getParameter("org.lamport.tla.toolbox.doc.url.name")); + PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url); + } catch (final MalformedURLException e) { + e.printStackTrace(); + } catch (final PartInitException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/org.lamport.tla.toolbox.feature.standalone/feature.xml b/org.lamport.tla.toolbox.feature.standalone/feature.xml index 1793f1354e5a4c31e03e71b99d10afd778458676..70b72b5268e83a1dfb121a29bba70a6321129827 100644 --- a/org.lamport.tla.toolbox.feature.standalone/feature.xml +++ b/org.lamport.tla.toolbox.feature.standalone/feature.xml @@ -208,4 +208,11 @@ version="0.0.0" unpack="false"/> + <plugin + id="org.eclipse.ui.intro.quicklinks" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + </feature> diff --git a/org.lamport.tla.toolbox.jclouds/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.jclouds/META-INF/MANIFEST.MF index 27e616b51954f62089c043cd881971f3347a5d29..94db04f77392ef8f82baba987adac31b52249c3e 100644 --- a/org.lamport.tla.toolbox.jclouds/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.jclouds/META-INF/MANIFEST.MF @@ -13,11 +13,10 @@ Require-Bundle: org.eclipse.core.runtime, jclouds-sshj;bundle-version="1.7.3", com.google.guava;bundle-version="15.0.0", com.google.inject;bundle-version="3.0.0", - org.apache.commons.io;bundle-version="2.0.1" + org.apache.commons.io;bundle-version="2.0.1", + org.lamport.tlatools;bundle-version="1.0.0" DynamicImport-Package: * Bundle-Activator: org.lamport.tla.toolbox.jcloud.JCloudActivator Import-Package: org.jclouds.aws.ec2.reference;version="1.7.3", org.jclouds.ec2.reference;version="1.7.3", - org.lamport.tla.toolbox.tool.tlc.job;resolution:=optional, - tlc2.tool.distributed, - tlc2.tool.distributed.fp + org.lamport.tla.toolbox.tool.tlc.job;resolution:=optional diff --git a/org.lamport.tla.toolbox.jclouds/plugin.xml b/org.lamport.tla.toolbox.jclouds/plugin.xml index cb5d9882785b4ba872d103359986da2cfa42958a..d8daf8f1b06056a384b61c0965f72f83ec926b06 100644 --- a/org.lamport.tla.toolbox.jclouds/plugin.xml +++ b/org.lamport.tla.toolbox.jclouds/plugin.xml @@ -8,6 +8,15 @@ </factory> </extension> + <extension + id="org.lamport.tla.toolbox.jclouds.application" + point="org.eclipse.core.runtime.applications"> + <application> + <run + class="org.lamport.tla.toolbox.jcloud.Application"> + </run> + </application> + </extension> <!-- diff --git a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/Application.java b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/Application.java new file mode 100644 index 0000000000000000000000000000000000000000..dfa4e6baadbff97e82fd45b53469bb53a0977bb7 --- /dev/null +++ b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/Application.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.jcloud; + +import java.io.File; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Properties; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.equinox.app.IApplication; +import org.eclipse.equinox.app.IApplicationContext; +import org.lamport.tla.toolbox.tool.tlc.job.TLCJobFactory; + +import tlc2.TLCGlobals; + +public class Application implements IApplication { + + /* (non-Javadoc) + * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext) + */ + public Object start(IApplicationContext context) throws Exception { + Object argObject = context.getArguments().get( + IApplicationContext.APPLICATION_ARGS); + if (argObject == null || !(argObject instanceof String[]) || ((String[]) argObject).length < 1) { + //TODO print usage + System.exit(1); + } + + final String[] args = (String[]) argObject; + final String modelDirectory = args[0]; + + final Properties props = new Properties(); + props.put(TLCJobFactory.MAIN_CLASS, tlc2.TLC.class.getName()); + + // Optional parameters + final String cloud = args.length >= 2 ? args[1] : "aws-ec2"; + + if (args.length >= 3) { + props.put(TLCJobFactory.MODEL_NAME, args[2]); + } + if (args.length >= 4) { + props.put(TLCJobFactory.SPEC_NAME, args[3]); + } + if (args.length >= 5) { + props.put(TLCJobFactory.MAIL_ADDRESS, args[4]); + } + + // The parameters below are the only one currently useful with CloudDistributedTLC + final StringBuffer tlcParams = new StringBuffer(); + + // fp seed offset (decrease by one to map from [1, 64] interval to [0, 63] array address + final int fpSeedOffset = 1; + tlcParams.append("-fp "); + tlcParams.append(String.valueOf(fpSeedOffset - 1)); + tlcParams.append(" "); + + int maxSetSize = TLCGlobals.setBound; + if (maxSetSize != TLCGlobals.setBound) { + tlcParams.append("-maxSetSize "); + tlcParams.append(String.valueOf(maxSetSize)); + tlcParams.append(" "); + } + + boolean checkDeadlock = false; + if (!checkDeadlock) { + tlcParams.append("-deadlock"); + } + + final TLCJobFactory factory = new CloudTLCJobFactory(); + final CloudDistributedTLCJob job = (CloudDistributedTLCJob) factory.getTLCJob(cloud, new File(modelDirectory), 1, props, tlcParams.toString()); + job.setIsCLI(true); + job.setDoJfr(true); + final IStatus status = job.run(new MyProgressMonitor(9)); + // Show error message if any such as invalid credentials. + if (status.getSeverity() == IStatus.ERROR) { + System.err.println(status.getMessage()); + // Signal unsuccessful execution. + return new Integer(1); + } + + return IApplication.EXIT_OK; + } + + /* (non-Javadoc) + * @see org.eclipse.equinox.app.IApplication#stop() + */ + public void stop() { + } + + private static class MyProgressMonitor implements IProgressMonitor { + private final DateFormat formatter = new SimpleDateFormat( "YYYY-MM-dd HH:mm:ss.SSS" ); + private final int totalSteps; + private int steps = 1; + + public MyProgressMonitor(int totalSteps) { + this.totalSteps = totalSteps; + } + + public void subTask(String str) { + System.out.printf("%s (%s of %s): %s\n", formatter.format(new Date()), Integer.toString(steps), + Integer.toString(totalSteps), str); + steps++; + } + + public void beginTask(String str, int totalWork) { + subTask(str); + } + + public void done() {} + + public void internalWorked(double work) {} + + public boolean isCanceled() {return false;} + + public void setCanceled(boolean value) {} + + public void setTaskName(String name) {} + + public void worked(int work) {} + } +} diff --git a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureCloudTLCInstanceParameters.java b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureCloudTLCInstanceParameters.java index 6ccd2fee6c43311c3dc5ffd67a7a2bddaced3a88..5bc266f4cc5b0202d1f6283486303c208ca09ef3 100644 --- a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureCloudTLCInstanceParameters.java +++ b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureCloudTLCInstanceParameters.java @@ -47,7 +47,8 @@ public class AzureCloudTLCInstanceParameters extends CloudTLCInstanceParameters if (numberOfWorkers == 1) { return getJavaWorkerVMArgs(); } - return "-Xmx96G -Xms96G"; + // See org.lamport.tla.toolbox.tool.tlc.job.TLCProcessJob.getAdditionalVMArgs() + return "--add-modules=java.activation -XX:+IgnoreUnrecognizedVMOptions -Xmx96G -Xms96G"; } /* (non-Javadoc) @@ -55,7 +56,8 @@ public class AzureCloudTLCInstanceParameters extends CloudTLCInstanceParameters */ @Override public String getJavaWorkerVMArgs() { - return "-Xmx32G -Xms32G -XX:MaxDirectMemorySize=64g"; + // See org.lamport.tla.toolbox.tool.tlc.job.TLCProcessJob.getAdditionalVMArgs() + return "--add-modules=java.activation -XX:+IgnoreUnrecognizedVMOptions -Xmx32G -Xms32G -XX:MaxDirectMemorySize=64g"; } /* (non-Javadoc) @@ -81,13 +83,22 @@ public class AzureCloudTLCInstanceParameters extends CloudTLCInstanceParameters return "azurecompute"; } + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.jcloud.CloudTLCInstanceParameters#getRegion() + */ + @Override + public String getRegion() { + return "us-east"; + } + /* (non-Javadoc) * @see org.lamport.tla.toolbox.jcloud.CloudTLCInstanceParameters#getImageId() */ @Override public String getImageId() { - // azure vm image list |grep "Canonical" - return "b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_4-LTS-amd64-server-20160222-en-us-30GB"; + // 'azure vm image list eastus canonical' (manually lookup image release date from output) + // With azure-cli v2 (based on Python) extract date from 'az vm image list --all --publisher Canonical'. + return "b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-16_04-LTS-amd64-server-20170919-en-us-30GB"; } /* (non-Javadoc) @@ -151,4 +162,81 @@ public class AzureCloudTLCInstanceParameters extends CloudTLCInstanceParameters public void mungeBuilder(ContextBuilder builder) { builder.endpoint("https://management.core.windows.net/" + getSubscriptionId()); } + + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.jcloud.CloudTLCInstanceParameters#getExtraRepositories() + */ + @Override + public String getExtraRepositories() { + return "echo \"deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ wheezy main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list && apt-key adv --keyserver packages.microsoft.com --recv-keys 417A0893"; + } + + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.jcloud.CloudTLCInstanceParameters#getExtraPackages() + */ + @Override + public String getExtraPackages() { + // https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest#install-on-debianubuntu-with-apt-get + // see getExtraRepositories too. + return "azure-cli"; + } + + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.jcloud.CloudTLCInstanceParameters#getHostnameSetup() + */ + @Override + public String getHostnameSetup() { + // Append ".cloudapp.net" to automatically set hostname and add a mapping from + // public ip (obtained via third party service ifconfig.co) to hostname in + // /etc/hosts. Results in FQDN being used my MailSender and thus less likely + // to be classified or rejected as spam. + // The suffix ".cloudapp.net" is something that might change on the Azure end in + // the future. It will then break this statement (suffix can be found in portal). + // It would also be nice for Azure to offer a public API to query the hostname + // (similar to EC2CloudTLCInstanceParameters#getHostnameSetup. + return "hostname \"$(hostname).cloudapp.net\" && echo \"$(curl -s ifconfig.co) $(hostname)\" >> /etc/hosts"; + } + + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.jcloud.CloudTLCInstanceParameters#getCleanup() + */ + @Override + public String getCloudAPIShutdown() { + final String servicePrincipal = System.getenv("AZURE_COMPUTE_SERVICE_PRINCIPAL"); + final String password = System.getenv("AZURE_COMPUTE_SERVICE_PRINCIPAL_PASSWORD"); + final String tenant = System.getenv("AZURE_COMPUTE_SERVICE_PRINCIPAL_TENANT"); + if (servicePrincipal == null || password == null || tenant == null) { + // Missing credentials. + return super.getCloudAPIShutdown(); + } + // What we try to accomplish is to purge the complete Azure Resource Group (a RG + // combines all Azure resources associated with the VM (storage, networking, + // ips, ...). + // The way we do it, is to use the azure CLI to deploy the template in + // /tmp/rg.json created by the printf statement under the same resource group + // identify we wish to purge. The trick is, that the template defines no + // resources whatsoever. This effectively purges the old resources in the + // resource group. Idea taken from + // http://www.codeisahighway.com/effective-ways-to-delete-resources-in-a-resource-group-on-azure/ + // bash/screen/ssh apparently fiddle with quotes and which is why the json is base64 encoded + // and decoded on the instance: + // { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, "variables": {}, "resources": [], "outputs": {} } | base64 + // + // Unfortunately, the azure CLI needs credentials to talk to the Azure API. For + // that, one manually creates a service principal once as described in + // https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest + // and uses it to log into Azure as described in + // https://docs.microsoft.com/en-us/cli/azure/authenticate-azure-cli?view=azure-cli-latest. + // Using AZURE_COMPUTE_CREDENTIALS and AZURE_COMPUTE_IDENTITY to login with azure CLI + // would trigger a two-factor auth for Microsoft FTEs. Not something we want. + // + // An alternative might be to use an auth.properties file, but this doesn't seem + // supported by azure CLI yet. Read "File based authentication" at + // https://docs.microsoft.com/en-us/java/azure/java-sdk-azure-authenticate#mgmt-file + return "echo eyAiJHNjaGVtYSI6ICJodHRwczovL3NjaGVtYS5tYW5hZ2VtZW50LmF6dXJlLmNvbS9zY2hlbWFzLzIwMTUtMDEtMDEvZGVwbG95bWVudFRlbXBsYXRlLmpzb24jIiwgImNvbnRlbnRWZXJzaW9uIjogIjEuMC4wLjAiLCAicGFyYW1ldGVycyI6IHt9LCAidmFyaWFibGVzIjoge30sICJyZXNvdXJjZXMiOiBbXSwgIm91dHB1dHMiOiB7fSB9Cg== | base64 -d > /tmp/rg.json" + + " && " + "az login --service-principal -u \"" + servicePrincipal + "\" -p " + password + " --tenant " + // $(hostname -s) only works iff hostname is correctly setup with getHostnameSetup() above. + + tenant + " && " + "az group deployment create --resource-group $(hostname -s)" + + " --template-file /tmp/rg.json --mode Complete"; + } } diff --git a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudDistributedTLCJob.java b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudDistributedTLCJob.java index 908dcbefd9f95784256e5a4743583596c5720614..b66caf888dff68e00142d56b4212d75fdb085688 100644 --- a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudDistributedTLCJob.java +++ b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudDistributedTLCJob.java @@ -32,9 +32,11 @@ import static org.jclouds.compute.predicates.NodePredicates.inGroup; import static org.jclouds.scriptbuilder.domain.Statements.exec; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.NoSuchElementException; import java.util.Properties; import java.util.Set; @@ -49,14 +51,17 @@ import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.RunNodesException; import org.jclouds.compute.RunScriptOnNodesException; +import org.jclouds.compute.domain.ExecChannel; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.io.Payload; +import org.jclouds.logging.config.ConsoleLoggingModule; import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.rest.AuthorizationException; import org.jclouds.scriptbuilder.statements.login.AdminAccess; import org.jclouds.ssh.SshClient; +import org.jclouds.ssh.SshException; import org.jclouds.sshj.config.SshjSshClientModule; import org.lamport.tla.toolbox.tool.tlc.job.ITLCJobStatus; import org.lamport.tla.toolbox.tool.tlc.job.TLCJobFactory; @@ -65,6 +70,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.io.ByteStreams; import com.google.inject.AbstractModule; /* @@ -87,6 +93,8 @@ public class CloudDistributedTLCJob extends Job { private final int nodes; private final Properties props; private final CloudTLCInstanceParameters params; + private boolean isCLI = false; + private boolean doJfr = false; public CloudDistributedTLCJob(String aName, File aModelFolder, int numberOfWorkers, final Properties properties, CloudTLCInstanceParameters params) { @@ -128,8 +136,8 @@ public class CloudDistributedTLCJob extends Job { // Create compute environment in the cloud and inject an ssh // implementation. ssh is our means of communicating with the node. - final Iterable<AbstractModule> modules = ImmutableSet - .<AbstractModule> of(new SshjSshClientModule(), new SLF4JLoggingModule()); + final Iterable<AbstractModule> modules = ImmutableSet.<AbstractModule>of(new SshjSshClientModule(), + isCLI ? new ConsoleLoggingModule() : new SLF4JLoggingModule()); final ContextBuilder builder = ContextBuilder .newBuilder(params.getCloudProvider()) @@ -164,7 +172,8 @@ public class CloudDistributedTLCJob extends Job { templateBuilder.hardwareId(params.getHardwareId()); // Everything configured, now launch node - monitor.subTask("Starting " + nodes + " instance(s)."); + monitor.subTask(String.format("Starting %s %s instance%s in region %s.", nodes > 1 ? nodes : "a", + params.getHardwareId(), nodes > 1 ? "s" : "", params.getRegion())); final Set<? extends NodeMetadata> createNodesInGroup; createNodesInGroup = compute.createNodesInGroup(groupNameUUID, nodes, templateBuilder.build()); @@ -196,6 +205,16 @@ public class CloudDistributedTLCJob extends Job { // Don't want dpkg to require user interaction. + "export DEBIAN_FRONTEND=noninteractive" + " && " + + params.getHostnameSetup() + + " && " + // Oracle Java 8 + + "add-apt-repository ppa:webupd8team/java -y && " + // Accept license before apt (dpkg) tries to present it to us (which fails due to 'noninteractive' mode below) + // see http://stackoverflow.com/a/19391042 + + "echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections && " + + "echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections && " + + params.getExtraRepositories() + + " && " // Update Ubuntu's package index. The public/remote // package mirrors might have updated. Without // update, we might try to install outdated packages @@ -234,18 +253,11 @@ public class CloudDistributedTLCJob extends Job { // worker tla2tools.jar (strip spec) and // unattended-upgrades makes sure the instance // is up-to-date security-wise. - + "apt-get install --no-install-recommends mdadm e2fsprogs screen zip unattended-upgrades -y" + + "apt-get install --no-install-recommends mdadm e2fsprogs screen zip unattended-upgrades oracle-java8-installer oracle-java8-set-default " + + params.getExtraPackages() + " -y" + " && " - // Create a raid0 out of the two instance store - // disks and optimize its fs towards performance - // by sacrificing data durability. - + "umount /mnt && " - + "/usr/bin/yes|/sbin/mdadm --create --force --auto=yes /dev/md0 --level=0 --raid-devices=2 --assume-clean --name=tlaplus /dev/xvdb /dev/xvdc && " - + "/sbin/mdadm --detail --scan >> /etc/mdadm/mdadm.conf && " - + "sed -i '\\?^/dev/xvdb?d' /etc/fstab && " - + "echo \"/dev/md127 /mnt ext4 defaults 0 0\" >> /etc/fstab && " - + "/sbin/mkfs.ext4 -O ^has_journal /dev/md0 && " - + "mount /dev/md0 /mnt" + // Delegate file system tuning to cloud specific code. + + params.getOSFilesystemTuning() // Install Oracle Java8. It supports Java Mission // Control, an honest profiler. But first, // automatically accept the Oracle license because @@ -263,7 +275,8 @@ public class CloudDistributedTLCJob extends Job { + " && " + "mkdir -p /mnt/tlc/ && chmod 777 /mnt/tlc/ && " + "ln -s /mnt/tlc/MC.out /var/www/html/MC.out && " - + "ln -s /mnt/tlc/MC.err /var/www/html/MC.err"), + + "ln -s /mnt/tlc/MC.err /var/www/html/MC.err && " + + "ln -s /mnt/tlc/tlc.jfr /var/www/html/tlc.jfr"), new TemplateOptions().runAsRoot(true).wrapInInitScript( false)); monitor.worked(10); @@ -284,6 +297,7 @@ public class CloudDistributedTLCJob extends Job { // Choose one of the nodes to be the master and create an // identifying predicate. final NodeMetadata master = Iterables.getLast(createNodesInGroup); + final String hostname = Iterables.getOnlyElement(master.getPublicAddresses()); // master.getHostname() only returns internal name // Copy tlatools.jar to _one_ remote host (do not exhaust upload of // the machine running the toolbox). @@ -291,142 +305,178 @@ public class CloudDistributedTLCJob extends Job { // available on the master's webserver for the clients to download. // On the other hand this means we are making the spec // world-readable. It is cloud-readable already through the RMI api. - monitor.subTask("Copying tla2tools.jar to master node"); + monitor.subTask("Copying tla2tools.jar to master node at " + hostname); SshClient sshClient = context.utils().sshForNode().apply(master); sshClient.put("/tmp/tla2tools.jar", jarPayLoad); - sshClient.disconnect(); monitor.worked(10); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } - // Run model checker master on master - monitor.subTask("Starting TLC model checker process on the master node (in background)"); - // The predicate will be applied to ALL instances owned by the - // cloud account (ie AWS), even the ones in different regions - // completely unrelated to TLC. - final Predicate<NodeMetadata> isMaster = new Predicate<NodeMetadata>() { - private final String masterHostname = master.getHostname(); - public boolean apply(NodeMetadata nodeMetadata) { - // hostname can be null if instance is terminated. - final String hostname = nodeMetadata.getHostname(); - return masterHostname.equals(hostname); - }; - }; - compute.runScriptOnNodesMatching( - isMaster, - exec(" cd /mnt/tlc/ && " - // Execute TLC (java) process inside screen - // and shutdown on TLC's completion. But - // detach from screen directly. Name screen - // session "tlc". - // (see http://stackoverflow.com/a/10126799) - + "screen -dm -S tlc bash -c \" " - // This requires a modified version where all parameters and - // all spec modules are stored in files in a model/ folder - // inside of the jar. - // This is done in anticipation of other cloud providers - // where one cannot easily pass in parameters on the command - // line because there is no command line. - + "java " - + params.getJavaVMArgs() + " " - // Write all tmp files to the ephemeral instance - // storage which is expected to have a higher IOPS - // compared to non-local storage. - + "-Djava.io.tmpdir=/mnt/tlc/ " - // These properties cannot be "backed" into - // the payload jar as java itself does not - // support this. - // It might be able to read the properties from - // the config file with 'com.sun.management.config.file=path', - // but I haven't tried if the path can point into the jar. - + "-Dcom.sun.management.jmxremote " - + "-Dcom.sun.management.jmxremote.port=5400 " - + "-Dcom.sun.management.jmxremote.ssl=false " - + "-Dcom.sun.management.jmxremote.authenticate=false " - // TLC tuning options - + params.getJavaSystemProperties() + " " - + "-jar /tmp/tla2tools.jar " - + params.getTLCParameters() + " " - + "&& " - // Let the machine power down immediately after - // finishing model checking to cut costs. However, - // do not shut down (hence "&&") when TLC finished - // with an error. - // It uses "sudo" because the script is explicitly - // run as a user. No need to run the TLC process as - // root. - + "sudo shutdown -h now" - + "\""), // closing opening '"' of screen/bash -c - new TemplateOptions().runAsRoot(false).wrapInInitScript( - true).blockOnComplete(false).blockUntilRunning(false)); - monitor.worked(5); - final long tlcStartUp = System.currentTimeMillis(); - - if (nodes > 1) { - // copy the tla2tools.jar to the root of the master's webserver - // to make it available to workers. However, strip the spec - // (*.tla/*.cfg/...) from the jar file to not share the spec - // with the world. - monitor.subTask("Make TLC code available to all worker node(s)"); - compute.runScriptOnNodesMatching( - isMaster, - exec("cp /tmp/tla2tools.jar /var/www/html/tla2tools.jar && " - + "zip -d /var/www/html/tla2tools.jar model/*.tla model/*.cfg model/generated.properties"), - new TemplateOptions().runAsRoot(true).wrapInInitScript( - false)); - monitor.worked(10); - if (monitor.isCanceled()) { - return Status.CANCEL_STATUS; + final String tlcMasterCommand = " cd /mnt/tlc/ && " + // Execute TLC (java) process inside screen + // and shutdown on TLC's completion. But + // detach from screen directly. Name screen + // session "tlc". + // (see http://stackoverflow.com/a/10126799) + + (isCLI ? "" : "screen -dm -S tlc bash -c \" ") + // This requires a modified version where all parameters and + // all spec modules are stored in files in a model/ folder + // inside of the jar. + // This is done in anticipation of other cloud providers + // where one cannot easily pass in parameters on the command + // line because there is no command line. + + "java " + + params.getJavaVMArgs() + " " + + (doJfr ? params.getFlightRecording() + " " : "") + // Write all tmp files to the ephemeral instance + // storage which is expected to have a higher IOPS + // compared to non-local storage. + + "-Djava.io.tmpdir=/mnt/tlc/ " + // These properties cannot be "backed" into + // the payload jar as java itself does not + // support this. + // It might be able to read the properties from + // the config file with 'com.sun.management.config.file=path', + // but I haven't tried if the path can point into the jar. + + "-Dcom.sun.management.jmxremote " + + "-Dcom.sun.management.jmxremote.port=5400 " + + "-Dcom.sun.management.jmxremote.ssl=false " + + "-Dcom.sun.management.jmxremote.authenticate=false " + // TLC tuning options + + params.getJavaSystemProperties() + " " + + "-jar /tmp/tla2tools.jar " + + params.getTLCParameters() + " " + + "&& " + // Run any cloud specific cleanup tasks. + // When CloudDistributedTLCJob runs in synchronous CLI mode (isCLI), it will destroy + // the VMs (nodes) via the jclouds API. No need to deallocate nodes + // via special logic. + + (isCLI ? "/bin/true" : params.getCloudAPIShutdown()) + + " && " + // Let the machine power down immediately after + // finishing model checking to cut costs. However, + // do not shut down (hence "&&") when TLC finished + // with an error. + // It uses "sudo" because the script is explicitly + // run as a user. No need to run the TLC process as + // root. + + "sudo shutdown -h " + (isCLI ? "+10" : "now") + + (isCLI ? "" : "\""); // closing opening '"' of screen/bash -c + if (isCLI) { + monitor.subTask("Starting TLC model checker process"); + // Execute command via ssh instead of as a script to get access to the TLC + // processes' stdout and stderr. + //TODO Better handle error case. + ExecChannel channel = sshClient.execChannel(tlcMasterCommand); + // Send remote TLC's stdout to local stdout (this throws a TransportException + // unless shutdown is postponed by a few minutes above). + ByteStreams.copy(channel.getOutput(), System.out); + if (doJfr) { + // Get Java Flight Recording from remote machine and save if to a local file in + // the current working directory. We call "cat" because sftclient#get fails with + // the old net.schmizz.sshj and an update to the newer com.hierynomus seems + // awful lot of work. + channel = sshClient.execChannel("cat /mnt/tlc/tlc.jfr"); + final String cwd = Paths.get(".").toAbsolutePath().normalize().toString() + File.separator; + ByteStreams.copy(channel.getOutput(), new FileOutputStream(new File(cwd + "tlc.jfr"))); } - + // Finally close the ssh connection. + sshClient.disconnect(); + monitor.subTask("TLC model checker process finished"); + // Eagerly destroy the instance after we pulled the tlc.jfr file from it. No + // point in waiting for shutdown -h +10 to shutdown the instance. + destroyNodes(context, groupNameUUID); + } else { + sshClient.disconnect(); + + // Run model checker master on master + monitor.subTask("Starting TLC model checker process on the master node (in background)"); // The predicate will be applied to ALL instances owned by the - // AWS account, even the ones in different regions completely - // unrelated to TLC. - final Predicate<NodeMetadata> onWorkers = new Predicate<NodeMetadata>() { - // Remove the master from the set of our nodes. - private final Iterable<? extends NodeMetadata> workers = Iterables.filter(createNodesInGroup, new Predicate<NodeMetadata>() { - private final String masterHostname = master.getHostname(); - public boolean apply(NodeMetadata nodeMetadata) { - // nodeMetadata.getHostname is null for terminated hosts. - return !masterHostname.equals(nodeMetadata.getHostname()); - }; - }); + // cloud account (ie AWS), even the ones in different regions + // completely unrelated to TLC. + final Predicate<NodeMetadata> isMaster = new Predicate<NodeMetadata>() { + private final String masterHostname = master.getHostname(); public boolean apply(NodeMetadata nodeMetadata) { - return Iterables.contains(workers, nodeMetadata); + // hostname can be null if instance is terminated. + final String hostname = nodeMetadata.getHostname(); + return masterHostname.equals(hostname); }; }; + compute.runScriptOnNodesMatching(isMaster, exec(tlcMasterCommand), new TemplateOptions().runAsRoot(false) + .wrapInInitScript(true).blockOnComplete(false).blockUntilRunning(false)); + monitor.worked(5); + final long tlcStartUp = System.currentTimeMillis(); + + if (nodes > 1) { + // copy the tla2tools.jar to the root of the master's webserver + // to make it available to workers. However, strip the spec + // (*.tla/*.cfg/...) from the jar file to not share the spec + // with the world. + monitor.subTask("Make TLC code available to all worker node(s)"); + compute.runScriptOnNodesMatching( + isMaster, + exec("cp /tmp/tla2tools.jar /var/www/html/tla2tools.jar && " + + "zip -d /var/www/html/tla2tools.jar model/*.tla model/*.cfg model/generated.properties"), + new TemplateOptions().runAsRoot(true).wrapInInitScript( + false)); + monitor.worked(10); + if (monitor.isCanceled()) { + return Status.CANCEL_STATUS; + } + + // The predicate will be applied to ALL instances owned by the + // AWS account, even the ones in different regions completely + // unrelated to TLC. + final Predicate<NodeMetadata> onWorkers = new Predicate<NodeMetadata>() { + // Remove the master from the set of our nodes. + private final Iterable<? extends NodeMetadata> workers = Iterables.filter(createNodesInGroup, new Predicate<NodeMetadata>() { + private final String masterHostname = master.getHostname(); + public boolean apply(NodeMetadata nodeMetadata) { + // nodeMetadata.getHostname is null for terminated hosts. + return !masterHostname.equals(nodeMetadata.getHostname()); + }; + }); + public boolean apply(NodeMetadata nodeMetadata) { + return Iterables.contains(workers, nodeMetadata); + }; + }; - // see master startup for comments - monitor.subTask("Starting TLC workers on the remaining node(s) (in background)"); - final String hostname = Iterables.getOnlyElement(master.getPrivateAddresses()); - compute.runScriptOnNodesMatching( - onWorkers, - exec("cd /mnt/tlc/ && " - + "wget http://" + hostname + "/tla2tools.jar && " - + "screen -dm -S tlc bash -c \" " - + "java " - + params.getJavaWorkerVMArgs() + " " - + "-Djava.io.tmpdir=/mnt/tlc/ " - + "-Dcom.sun.management.jmxremote " - + "-Dcom.sun.management.jmxremote.port=5400 " - + "-Dcom.sun.management.jmxremote.ssl=false " - + "-Dcom.sun.management.jmxremote.authenticate=false " - + params.getJavaWorkerSystemProperties() + " " - + "-cp /mnt/tlc/tla2tools.jar " - + params.getTLCWorkerParameters() + " " - + hostname + " " // Use host's internal ip due to firewall reasons. - + "&& " - // Terminate regardless of TLCWorker process - // exit value. E.g. TLCWorker can terminate due - // to a NoRouteToHostException when the master - // shut down caused by a violation among the - // init states. - + "sudo shutdown -h now" - + "\""), - new TemplateOptions().runAsRoot(false).wrapInInitScript( - true).blockOnComplete(false).blockUntilRunning(false)); - monitor.worked(10); + // see master startup for comments + monitor.subTask("Starting TLC workers on the remaining node(s) (in background)"); + final String privateHostname = Iterables.getOnlyElement(master.getPrivateAddresses()); + compute.runScriptOnNodesMatching( + onWorkers, + exec("cd /mnt/tlc/ && " + + "wget http://" + privateHostname + "/tla2tools.jar && " + + "screen -dm -S tlc bash -c \" " + + "java " + + params.getJavaWorkerVMArgs() + " " + + "-Djava.io.tmpdir=/mnt/tlc/ " + + "-Dcom.sun.management.jmxremote " + + "-Dcom.sun.management.jmxremote.port=5400 " + + "-Dcom.sun.management.jmxremote.ssl=false " + + "-Dcom.sun.management.jmxremote.authenticate=false " + + params.getJavaWorkerSystemProperties() + " " + + "-cp /mnt/tlc/tla2tools.jar " + + params.getTLCWorkerParameters() + " " + + privateHostname + " " // Use host's internal ip due to firewall reasons. + + "&& " + // Terminate regardless of TLCWorker process + // exit value. E.g. TLCWorker can terminate due + // to a NoRouteToHostException when the master + // shut down caused by a violation among the + // init states. + // Run any cloud specific cleanup tasks. + + params.getCloudAPIShutdown() + + " && " + + "sudo shutdown -h now" + + "\""), + new TemplateOptions().runAsRoot(false).wrapInInitScript( + true).blockOnComplete(false).blockUntilRunning(false)); + monitor.worked(10); + } + } // Print runtimes of various work items. @@ -436,7 +486,6 @@ public class CloudDistributedTLCJob extends Job { // Communicate result to user monitor.done(); - final String hostname = Iterables.getOnlyElement(master.getPublicAddresses()); // master.getHostname() only returns internal name return new CloudStatus( Status.OK, "org.lamport.tla.toolbox.jcloud", @@ -447,7 +496,7 @@ public class CloudDistributedTLCJob extends Job { hostname, props.get("result.mail.address")), null, new URL( "http://" + hostname + "/munin/")); - } catch (RunNodesException|IOException|RunScriptOnNodesException|NoSuchElementException|AuthorizationException e) { + } catch (RunNodesException|IOException|RunScriptOnNodesException|NoSuchElementException|AuthorizationException|SshException e) { e.printStackTrace(); if (context != null) { destroyNodes(context, groupNameUUID); @@ -466,6 +515,14 @@ public class CloudDistributedTLCJob extends Job { } } } + + public void setIsCLI(boolean cli) { + this.isCLI = cli; + } + + public void setDoJfr(boolean doIt) { + this.doJfr = doIt; + } private static void destroyNodes(final ComputeServiceContext ctx, final String groupname) { // Destroy all workers identified by the given group diff --git a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudTLCInstanceParameters.java b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudTLCInstanceParameters.java index 9f68746e653326192d0e348865aae61277ab9b5a..7cbba7e80648da18bde2b77319612d5c7b21ba2f 100644 --- a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudTLCInstanceParameters.java +++ b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/CloudTLCInstanceParameters.java @@ -74,11 +74,13 @@ public abstract class CloudTLCInstanceParameters { if (numberOfWorkers == 1) { return getJavaWorkerVMArgs(); } - return "-Xmx56G -Xms56G"; + // See org.lamport.tla.toolbox.tool.tlc.job.TLCProcessJob.getAdditionalVMArgs() + return "--add-modules=java.activation -XX:+IgnoreUnrecognizedVMOptions -Xmx56G -Xms56G"; } public String getJavaWorkerVMArgs() { - return "-Xmx24G -Xms24G -XX:MaxDirectMemorySize=32g"; + // See org.lamport.tla.toolbox.tool.tlc.job.TLCProcessJob.getAdditionalVMArgs() + return "--add-modules=java.activation -XX:+IgnoreUnrecognizedVMOptions -Xmx24G -Xms24G -XX:MaxDirectMemorySize=32g"; } // tlc parameters @@ -102,6 +104,8 @@ public abstract class CloudTLCInstanceParameters { public abstract String getCloudProvider(); + public abstract String getRegion(); + public abstract String getImageId(); public abstract String getHardwareId(); @@ -119,4 +123,32 @@ public abstract class CloudTLCInstanceParameters { public void mungeBuilder(ContextBuilder builder) { // Nothing to be done here } + + public String getOSFilesystemTuning() { + return "/bin/true"; // no-op, because concat with && ... && in CDTJ. + } + + public String getFlightRecording() { + return "-XX:+UnlockCommercialFeatures " + + "-XX:+FlightRecorder " + + "-XX:+UnlockDiagnosticVMOptions " + + "-XX:+DebugNonSafepoints " + + "-XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/mnt/tlc,dumponexit=true,dumponexitpath=/mnt/tlc/tlc.jfr,maxage=12h"; + } + + public String getHostnameSetup() { + return "/bin/true"; // no-op, because concat with && ... && in CDTJ. + } + + public String getCloudAPIShutdown() { + return "/bin/true"; // no-op, because concat with && ... && in CDTJ. + } + + public String getExtraRepositories() { + return "/bin/true"; // no-op, because concat with && ... && in CDTJ. + } + + public String getExtraPackages() { + return ""; // no-op, because concat with && ... && in CDTJ. + } } diff --git a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/EC2CloudTLCInstanceParameters.java b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/EC2CloudTLCInstanceParameters.java index c72bb0b09b412005c4a1c4fc3e12831daf3d1efc..b1ec2c097f8f0fb446cc2eb39db5c0ab492e42f3 100644 --- a/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/EC2CloudTLCInstanceParameters.java +++ b/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/EC2CloudTLCInstanceParameters.java @@ -51,15 +51,15 @@ public class EC2CloudTLCInstanceParameters extends CloudTLCInstanceParameters { @Override public String getImageId() { - // Ubuntu 64bit 14.04.4 Trusty paravirtual/instance-store release - // https://cloud-images.ubuntu.com/releases/14.04/release/ - // or http://cloud-images.ubuntu.com/locator/ec2/ + // Ubuntu 64bit 16.04 Xenial + // http://cloud-images.ubuntu.com/locator/ec2/ // See http://aws.amazon.com/amazon-linux-ami/instance-type-matrix/ // for paravirtual vs. hvm - return getRegion() + "/ami-2ae6633c"; // "xenial,amd64,hvm:instance-store" + return getRegion() + "/ami-84a048fe"; // "xenial,amd64,hvm:instance-store" } - private String getRegion() { + @Override + public String getRegion() { return "us-east-1"; } @@ -109,4 +109,29 @@ public class EC2CloudTLCInstanceParameters extends CloudTLCInstanceParameters { // (i.e. South America). properties.setProperty(LocationConstants.PROPERTY_REGIONS, getRegion()); } + + /* (non-Javadoc) + * @see org.lamport.tla.toolbox.jcloud.CloudTLCInstanceParameters#getOSFilesystemTuning() + */ + @Override + public String getOSFilesystemTuning() { + // Create a raid0 out of the two instance store + // disks and optimize its fs towards performance + // by sacrificing data durability. + return "umount /mnt && " + + "/usr/bin/yes|/sbin/mdadm --create --force --auto=yes /dev/md0 --level=0 --raid-devices=2 --assume-clean --name=tlaplus /dev/xvdb /dev/xvdc && " + + "/sbin/mdadm --detail --scan >> /etc/mdadm/mdadm.conf && " + + "sed -i '\\?^/dev/xvdb?d' /etc/fstab && " + + "echo \"/dev/md127 /mnt ext4 defaults 0 0\" >> /etc/fstab && " + + "/sbin/mkfs.ext4 -O ^has_journal /dev/md0 && " + + "mount /dev/md0 /mnt"; + } + + @Override + public String getHostnameSetup() { + // Lookup public ipv4 hostname and configure /etc/hosts accordingly. Otherwise, + // MailSender uses the internal name which increases likelihood of email being + // classified/rejected as spam. + return "echo \"$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4) $(curl -s http://169.254.169.254/latest/meta-data/public-hostname)\" >> /etc/hosts && hostname $(curl -s http://169.254.169.254/latest/meta-data/public-hostname)"; + } } diff --git a/org.lamport.tla.toolbox.product.product/TLAToolbox.target b/org.lamport.tla.toolbox.product.product/TLAToolbox.target index a9e9b8cf537e6a97c1ecf980da553cc9320c803f..52b6e3ce81b05d03170815220ea0822a85395c09 100644 --- a/org.lamport.tla.toolbox.product.product/TLAToolbox.target +++ b/org.lamport.tla.toolbox.product.product/TLAToolbox.target @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<?pde version="3.8"?><target name="TLA+ Toolbox" sequenceNumber="74"> +<?pde version="3.8"?><target name="TLA+ Toolbox" sequenceNumber="79"> <locations> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> -<unit id="org.eclipse.contribution.weaving.feature.group" version="2.2.4.e45x-20150206-1600"/> -<unit id="org.aspectj.feature.group" version="1.8.5.20150128171000"/> -<repository location="http://download.eclipse.org/tools/ajdt/mars/update/" id="eclipse-ajdt"/> +<unit id="org.eclipse.contribution.weaving.feature.group" version="2.2.4.201704242114"/> +<unit id="org.eclipse.ajdt.feature.group" version="2.2.4.201704242114"/> +<repository location="http://download.eclipse.org/tools/ajdt/47/dev/update/ajdt-e47-2.2.4.201704242114/" id="eclipse-ajdt"/> </location> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> @@ -19,36 +19,36 @@ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20140114142710/repository/" id="eclipse-orbit"/> </location> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> -<unit id="org.eclipse.swtbot.ide.feature.group" version="2.2.1.201402241301"/> -<unit id="org.eclipse.swtbot.eclipse.test.junit.feature.group" version="2.2.1.201402241301"/> -<unit id="org.eclipse.swtbot.feature.group" version="2.2.1.201402241301"/> -<unit id="org.eclipse.swtbot.eclipse.feature.group" version="2.2.1.201402241301"/> -<repository location="http://download.eclipse.org/technology/swtbot/releases/2.2.1/" id="eclipse-swtbot"/> +<unit id="org.eclipse.swtbot.ide.feature.group" version="2.6.0.201706141832"/> +<unit id="org.eclipse.swtbot.eclipse.test.junit.feature.group" version="2.6.0.201706141832"/> +<unit id="org.eclipse.swtbot.feature.group" version="2.6.0.201706141832"/> +<unit id="org.eclipse.swtbot.eclipse.feature.group" version="2.6.0.201706141832"/> +<repository location="http://download.eclipse.org/technology/swtbot/releases/2.6.0/" id="eclipse-swtbot"/> </location> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> <unit id="org.apache.jclouds.feature.feature.group" version="0.0.0"/> <repository location="http://lemmy.github.com/jclouds2p2/"/> </location> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> -<unit id="org.eclipse.rcp.id" version="4.5.2.M20160212-1500"/> -<unit id="org.eclipse.help.feature.group" version="2.1.2.v20160212-1500"/> -<unit id="org.eclipse.equinox.core.sdk.feature.group" version="3.11.2.v20160128-1406"/> -<unit id="org.eclipse.rcp.sdk.id" version="4.5.2.M20160212-1500"/> -<unit id="org.eclipse.platform.ide" version="4.5.2.M20160212-1500"/> -<unit id="org.eclipse.rcp.configuration.feature.group" version="1.0.102.v20160212-1500"/> -<unit id="org.eclipse.emf.ecore.feature.group" version="2.11.2.v20160208-0816"/> -<unit id="org.eclipse.equinox.executable" version="3.6.200.v20150602-1417"/> -<unit id="org.eclipse.ecf.core.feature.feature.group" version="1.2.0.v20151130-0157"/> -<unit id="org.eclipse.e4.rcp.feature.group" version="1.4.1.v20160212-1350"/> -<unit id="org.eclipse.platform.sdk" version="4.5.2.M20160212-1500"/> -<unit id="org.eclipse.emf.common.feature.group" version="2.11.1.v20160208-0816"/> -<unit id="org.eclipse.equinox.sdk.feature.group" version="3.11.2.v20160202-2102"/> -<unit id="org.eclipse.core.runtime.feature.feature.group" version="1.1.102.v20160128-1558"/> -<unit id="org.eclipse.sdk.ide" version="4.5.2.M20160212-1500"/> -<repository location="http://download.eclipse.org/eclipse/updates/4.5/" id="eclipse"/> +<unit id="org.eclipse.rcp.id" version="4.7.0.I20170612-0950"/> +<unit id="org.eclipse.help.feature.group" version="2.2.100.v20170612-0950"/> +<unit id="org.eclipse.equinox.core.sdk.feature.group" version="3.13.0.v20170516-1513"/> +<unit id="org.eclipse.rcp.sdk.id" version="4.7.0.I20170612-0950"/> +<unit id="org.eclipse.platform.ide" version="4.7.0.I20170612-0950"/> +<unit id="org.eclipse.rcp.configuration.feature.group" version="1.1.0.v20170612-0950"/> +<unit id="org.eclipse.emf.ecore.feature.group" version="2.13.0.v20170609-0707"/> +<unit id="org.eclipse.equinox.executable" version="3.7.0.v20170531-1133"/> +<unit id="org.eclipse.ecf.core.feature.feature.group" version="1.4.0.v20170516-2248"/> +<unit id="org.eclipse.e4.rcp.feature.group" version="1.6.0.v20170612-1255"/> +<unit id="org.eclipse.platform.sdk" version="4.7.0.I20170612-0950"/> +<unit id="org.eclipse.emf.common.feature.group" version="2.13.0.v20170609-0707"/> +<unit id="org.eclipse.equinox.sdk.feature.group" version="3.13.0.v20170531-1133"/> +<unit id="org.eclipse.core.runtime.feature.feature.group" version="1.2.0.v20170518-1049"/> +<unit id="org.eclipse.sdk.ide" version="4.7.0.I20170612-0950"/> +<repository location="http://download.eclipse.org/eclipse/updates/4.7/"/> </location> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> -<unit id="de.vonloesch.pdf4eclipse.feature.feature.group" version="1.1.0.201704071204"/> +<unit id="de.vonloesch.pdf4eclipse.feature.feature.group" version="0.0.0"/> <repository location="http://lemmy.github.com/Pdf4Eclipse/"/> </location> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> diff --git a/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product b/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product index c179ab99520b4d5425d0149eaf2273486533b0c7..f608beb1b6ac6648921b7bbf425006ac7e304c5b 100644 --- a/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product +++ b/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product @@ -1,18 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> <?pde version="3.5"?> -<product name="TLA+ Toolbox" uid="org.lamport.tla.toolbox.product.product" id="org.lamport.tla.toolbox.product.standalone.product" application="org.lamport.tla.toolbox.application" version="1.5.3" useFeatures="true" includeLaunchers="true"> +<product name="TLA+ Toolbox" uid="org.lamport.tla.toolbox.product.product" id="org.lamport.tla.toolbox.product.standalone.product" application="org.lamport.tla.toolbox.application" version="1.5.4" useFeatures="true" includeLaunchers="true"> <aboutInfo> <image path="/org.lamport.tla.toolbox.product.standalone/images/splash_small.png"/> <text> TLA+ Toolbox provides a user interface for TLA+ Tools. -This is Version 1.5.3 of 14 April 2017 and includes: - - SANY Version 2.1 of 10 February 2016 - - TLC Version 2.09 of 10 March 2017 +This is Version 1.5.4 of 06 October 2017 and includes: + - SANY Version 2.1 of 23 July 2017 + - TLC Version 2.10 of 28 September 2017 - PlusCal Version 1.8 of 07 December 2015 - - TLATeX Version 1.0 of 04 August 2015 + - TLATeX Version 1.0 of 20 September 2017 Don't forget to click on help. You can learn about features that you never knew about or have forgotten. @@ -31,9 +31,13 @@ openFile </programArgs> <programArgsLin>--launcher.GTK_version 2 </programArgsLin> - <vmArgs>-Xmx1000m + <vmArgs>--add-modules=java.se.ee +-XX:+IgnoreUnrecognizedVMOptions +-Xmx1000m -Dorg.eclipse.equinox.http.jetty.http.port=10996 -Dosgi.splashPath=platform:/base/ +-Dosgi.requiredJavaVersion=1.8 +-Dosgi.instance.area.default=@user.home/.tlaplus/ </vmArgs> <vmArgsMac>-XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts </vmArgsMac> @@ -41,8 +45,8 @@ openFile <windowImages i16="/org.lamport.tla.toolbox/icons/full/etool16/tla_launch_check_wiz_16.png" i32="/org.lamport.tla.toolbox/icons/full/etool16/tla_launch_check_wiz_32.png" i48="/org.lamport.tla.toolbox/icons/full/etool16/tla_launch_check_wiz_48.png" i64="/org.lamport.tla.toolbox/icons/full/etool16/tla_launch_check_wiz_64.png" i128="/org.lamport.tla.toolbox/icons/full/etool16/tla_launch_check_wiz_128.png" i256="/org.lamport.tla.toolbox/icons/full/etool16/tla_launch_check_wiz_256.png"/> + <launcher name="toolbox"> - <solaris/> <win useIco="false"> <bmp/> </win> @@ -51,12 +55,11 @@ openFile <intro introId="org.lamport.tla.toolbox.product.standalone.intro"/> <vm> - <linux include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7</linux> - <macos include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7</macos> - <windows include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7</windows> + <linux include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8</linux> + <macos include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8</macos> + <windows include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8</windows> </vm> - <plugins> </plugins> @@ -77,11 +80,12 @@ openFile <plugin id="org.eclipse.equinox.http.registry" autoStart="true" startLevel="3" /> <plugin id="org.lamport.tla.toolbox.jclouds" autoStart="true" startLevel="4" /> <plugin id="sts" autoStart="true" startLevel="4" /> - <property name="eclipse.buildId" value="1.5.3" /> + <property name="eclipse.buildId" value="1.5.4" /> </configurations> <repositories> <repository location="http://lamport.org/tlatoolbox/toolboxUpdate/" enabled="true" /> + <repository location="http://lamport.org/tlatoolbox/ci/toolboxUpdate/" enabled="false" /> </repositories> <preferencesInfo> diff --git a/org.lamport.tla.toolbox.product.product/pom.xml b/org.lamport.tla.toolbox.product.product/pom.xml index 110cb785e85e8be60a46b9db09e1495dedb118be..309be7b6fdc959493bc5c1bb86ed6229536b0418 100644 --- a/org.lamport.tla.toolbox.product.product/pom.xml +++ b/org.lamport.tla.toolbox.product.product/pom.xml @@ -19,7 +19,9 @@ <!-- Align product.version with the version in org.lamport.tla.toolbox.product.product.product product.version. --> - <product.version>1.5.3</product.version> + <product.version>1.5.4</product.version> + <!-- Format build timestamp to adhere to the Debian package guidelines --> + <maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format> <product.build>${maven.build.timestamp}</product.build> <!-- Do not include non-code project in Sonar reporting. --> <sonar.skip>true</sonar.skip> diff --git a/org.lamport.tla.toolbox.product.product/src/deb/control/postinst b/org.lamport.tla.toolbox.product.product/src/deb/control/postinst index 0dad5138297a00d8770ef64bd51ac97353766ded..d39accb3f3b04d9b755e4f8c3985eb48a4710977 100644 --- a/org.lamport.tla.toolbox.product.product/src/deb/control/postinst +++ b/org.lamport.tla.toolbox.product.product/src/deb/control/postinst @@ -6,20 +6,6 @@ chmod 755 /opt/TLA+Toolbox/toolbox ## Update mime database for x-tla type. /usr/bin/update-mime-database /usr/share/mime -## Place Toolbox's workspace directory -## in her home directory to support -## multi-user installation and not -## require user write access to -## /opt/TLA+Toolbox/. Also makes -## replacing the Toolbox less error -## prone when TLA+Toolbox is never -## touched except for dpkg. -## Having a fixed workspace location -## is also better than creating a -## workspace directory in ever directory -## from which the Toolbox is launched. -echo "-Dosgi.instance.area.default=@user.home/.tlaplus/" >> /opt/TLA+Toolbox/toolbox.ini - #- Apt repo #-- Jenkins Plugin #-- https://github.com/theoweiss/apt-repo diff --git a/org.lamport.tla.toolbox.product.standalone/.classpath b/org.lamport.tla.toolbox.product.standalone/.classpath index 731ea5fad3f73a3a6dfd0bcf5ae3431740bb69cb..098194ca4b7d8f45177f94e735506ae3a26b5c94 100644 --- a/org.lamport.tla.toolbox.product.standalone/.classpath +++ b/org.lamport.tla.toolbox.product.standalone/.classpath @@ -1,7 +1,7 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/> - <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="src" path="src"/> - <classpathentry kind="output" path="bin"/> -</classpath> +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.lamport.tla.toolbox.product.standalone/.settings/org.eclipse.jdt.core.prefs b/org.lamport.tla.toolbox.product.standalone/.settings/org.eclipse.jdt.core.prefs index d4a596f3fef58cd43d4542a93f1ca9194232d75e..838bd9d69424290f7e947b867a3b4381e756dad8 100644 --- a/org.lamport.tla.toolbox.product.standalone/.settings/org.eclipse.jdt.core.prefs +++ b/org.lamport.tla.toolbox.product.standalone/.settings/org.eclipse.jdt.core.prefs @@ -1,12 +1,11 @@ -#Tue Dec 02 21:58:22 CET 2008 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning -org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning -org.eclipse.jdt.core.compiler.source=1.3 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF index 5ff9affd3ff13e08902faaa4c77137a9366a4531..7e966e7a4e3774e2ec852f7e6e49dbb537cd7e49 100644 --- a/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.product.standalone/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: TLA+ Toolbox Standalone Product Bundle-SymbolicName: org.lamport.tla.toolbox.product.standalone;singleton:=true Bundle-Version: 1.0.0.qualifier Bundle-Vendor: Simon Zambrovski, Leslie Lamport -Bundle-RequiredExecutionEnvironment: J2SE-1.4 +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Require-Bundle: org.eclipse.ui, org.eclipse.equinox.app, org.eclipse.core.resources, @@ -13,8 +13,14 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ui.forms, org.eclipse.ui.ide;bundle-version="3.4.1", org.eclipse.swt, - org.eclipse.equinox.p2.ui.sdk.scheduler;bundle-version="1.0.0" + org.eclipse.equinox.p2.ui.sdk.scheduler;bundle-version="1.0.0", + org.eclipse.equinox.p2.ui;bundle-version="2.4.0", + org.eclipse.equinox.p2.ui.sdk;bundle-version="1.0.0", + org.eclipse.equinox.p2.operations;bundle-version="2.4.0", + org.eclipse.equinox.p2.repository;bundle-version="2.3.0", + org.eclipse.equinox.p2.metadata;bundle-version="2.3.0" Bundle-ActivationPolicy: lazy Bundle-Activator: org.lamport.tla.toolbox.StandaloneActivator Export-Package: org.lamport.tla.toolbox.lifecycle, org.lamport.tla.toolbox.ui.intro +Import-Package: org.eclipse.equinox.p2.core diff --git a/org.lamport.tla.toolbox.product.standalone/plugin.xml b/org.lamport.tla.toolbox.product.standalone/plugin.xml index 4c4d8dbd0cb940bcfdab73cf7903311d3fee6f0d..239b8d7cc281d7dea969e76b33ae06b6ea312815 100644 --- a/org.lamport.tla.toolbox.product.standalone/plugin.xml +++ b/org.lamport.tla.toolbox.product.standalone/plugin.xml @@ -30,7 +30,7 @@ </property> <property name="aboutText" - value="TLA+ Toolbox provides a user interface for TLA+ Tools. 

This is Version 1.5.3 of 14 April 2017 and includes:
 - SANY Version 2.1 of 10 February 2016
 - TLC Version 2.09 of 10 March 2017
 - PlusCal Version 1.8 of 07 December 2015
 - TLATeX Version 1.0 of 04 August 2015

Don't forget to click on help. You can learn about features that you never knew about or have forgotten.

Please send us reports of problems or suggestions; see https://groups.google.com/d/forum/tlaplus ."> + value="TLA+ Toolbox provides a user interface for TLA+ Tools. 

This is Version 1.5.4 of 06 October 2017 and includes:
 - SANY Version 2.1 of 23 July 2017
 - TLC Version 2.10 of 28 September 2017
 - PlusCal Version 1.8 of 07 December 2015
 - TLATeX Version 1.0 of 20 September 2017

Don't forget to click on help. You can learn about features that you never knew about or have forgotten.

Please send us reports of problems or suggestions; see https://groups.google.com/d/forum/tlaplus ."> </property> <property name="aboutImage" @@ -92,11 +92,21 @@ <extension point="org.eclipse.ui.preferencePages"> <page - class="org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdatesPreferencePage" + class="org.lamport.tla.toolbox.preferences.AutomaticUpdatesPreferencePage" id="org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdatesPreferencePage" name="Automatic Update"> <keywordReference id="org.eclipse.equinox.p2.ui.sdk.updates.general"/> </page> + <!-- Activate preference page below if Toolbox preferences should include + generic p2 repository editor (see Window > Preferences > + Install/Update > Available Sofware Sites in Eclipse. + <page + class="org.eclipse.equinox.p2.ui.RepositoryManipulationPage" + id="org.eclipse.equinox.internal.p2.ui.sdk.SitesPreferencePage" + name="Available Software Sites"> + <keywordReference id="org.eclipse.equinox.p2.ui.sdk.updates.general"/> + </page> + --> </extension> <!-- Navogator --> diff --git a/org.lamport.tla.toolbox.product.standalone/plugin_customization.ini b/org.lamport.tla.toolbox.product.standalone/plugin_customization.ini index 06343747aaad57b0e539af0ec9014ee50e6c97a0..153a0ab2e4d0ce888a11bd29b902426a96c82243 100644 --- a/org.lamport.tla.toolbox.product.standalone/plugin_customization.ini +++ b/org.lamport.tla.toolbox.product.standalone/plugin_customization.ini @@ -5,7 +5,7 @@ # org.eclipse.ui/EDITOR_TAB_POSITION=1024 org.eclipse.ui/SHOW_PROGRESS_ON_STARTUP=true -org.eclipse.ui/SHOW_MEMORY_MONITOR=true +org.eclipse.ui/SHOW_MEMORY_MONITOR=false # org.eclipse.ui/LOCK_TRIM=true org.eclipse.ui/initialFastViewBarLocation=left org.eclipse.ui/disableNewFastView=true diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java index 57d6bef6c26858a8d7a9098b285f75a0cbfaf54a..fffde044e54a2718bf9494ba18513c2758b8e37c 100644 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/Application.java @@ -3,14 +3,26 @@ package org.lamport.tla.toolbox; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; +import java.util.List; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; @@ -23,12 +35,13 @@ import org.eclipse.ui.internal.ide.ChooseWorkspaceData; * * @author Simon Zambrovski */ +@SuppressWarnings("restriction") public class Application implements IApplication { /* (non-Javadoc) * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext) */ - public Object start(IApplicationContext context) { + public Object start(IApplicationContext context) throws InvocationTargetException, InterruptedException { Object argObject = context.getArguments().get( IApplicationContext.APPLICATION_ARGS); if (argObject != null && argObject instanceof String[]) { @@ -46,10 +59,57 @@ public class Application implements IApplication { } } + final Location instanceLocation = Platform.getInstanceLocation(); + + // Check if the workspace has already been migrated to the new canonical + // location starting with Toolbox 1.5.4. If it hasn't, we lookup the + // previous location and copy its content to the canonical location + // (unless for whatever reason the user decides to start fresh). + // With a canonical location the cwd no longer matters from which the + // Toolbox launcher is executed. + if (!new File(instanceLocation.getURL().getFile() + File.separator + ".metadata" + File.separator).exists()) { + final String previousInstanceLoc = getPreviousInstanceLocation(instanceLocation); + if (previousInstanceLoc != null) { + final String instanceLoc = instanceLocation.getURL().toExternalForm(); + if(!instanceLoc.equals(previousInstanceLoc)) { + // Empty the recent workspaces to not show the dialog again. + // Only clear it when the previous and current instance + // location differ. This is the case when a user updates the + // Toolbox and hits the restart button at the end of update. + // This causes the Toolbox to restart with the previous + // install location again. + clearPreviousInstanceLocation(instanceLocation); + + // Open dialog and ask user to either import (migrate) the old workspace or create a fresh workspace. + final MessageDialog md = new MessageDialog(PlatformUI.createDisplay().getActiveShell(), "Migrate Toolbox files.", + null, + "Previously, your Toolbox used a different location to store its list of specifications and preferences. " + + "Starting with Toolbox release 1.5.4, the Toolbox keeps this data in a canonical location.\n\n" + + "If you do not let the Toolbox migrate this data, " + + "it will come up with default preferences and an empty spec explorer. " + + "You will have to manually configure the preferences and import your existing specifications.\n\n" + + "Click \"Migrate\" if you want the Toolbox to migrate the data now and continue Toolbox start-up.\n\n" + + "Click \"Start Fresh\" to continue Toolbox start-up and to manually import your specifications.", + MessageDialog.QUESTION, new String[] {"Migrate", "Start Fresh"}, 0); + + if (md.open() == 0) { + final ProgressMonitorDialog pmd = new ProgressMonitorDialog(Display.getCurrent().getActiveShell()); + pmd.run(true, false, new IRunnableWithProgress() { + public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + monitor.beginTask("Migrating Toolbox metadata to new location.", IProgressMonitor.UNKNOWN); + + copyRecurively(toPath(previousInstanceLoc, ".metadata"), toPath(instanceLoc), monitor); + monitor.done(); + } + }); + } + } + } + } + // The call to getStateLocation makes sure the instance location gets // initialized before we call .lock on it. StandaloneActivator.getDefault().getStateLocation(); - final Location instanceLocation = Platform.getInstanceLocation(); // Only allow a single Toolbox instance per workspace to prevent data // corruption in the workspace files. try { @@ -76,18 +136,6 @@ public class Application implements IApplication { System.exit(0); } - // CWD is Eclipse infrastructure which stores the location of the - // current workspace in a (text) file in the configuration area (Toolbox - // installation directory). With version 1.5.4 of the Toolbox, we will - // use this information to migrate all workspaces to @user.home/.tlaplus. - final ChooseWorkspaceData launchData = new ChooseWorkspaceData(instanceLocation.getDefault()); - final Set s = new HashSet(Arrays.asList(launchData.getRecentWorkspaces())); - // Remove null if recent workspaces above was empty (e.g. upon first toolbox startup). - s.add(instanceLocation.getURL().toExternalForm()); - s.remove(null); - launchData.setRecentWorkspaces((String[]) s.toArray(new String[s.size()])); - launchData.writePersistedData(); - Display display = PlatformUI.createDisplay(); try { int returnCode = PlatformUI.createAndRunWorkbench(display, @@ -101,6 +149,84 @@ public class Application implements IApplication { } } + private static Path toPath(final String prefix) { + return toPath(prefix, ""); + } + + // Converts our special path string coming from the BasicLocation to a valid + // path for java.nio.file.Path. + private static Path toPath(final String prefix, final String suffix) { + if (prefix.replaceFirst("file:", "").contains(":")) { + // Windows path, e.g. file:/C:/... + return Paths.get(prefix.replaceFirst("file:/", ""), suffix); + } + return Paths.get(prefix.replaceFirst("file:", ""), suffix); + } + + private static void clearPreviousInstanceLocation(final Location instanceLocation) { + final ChooseWorkspaceData launchData = new ChooseWorkspaceData(instanceLocation.getDefault()); + launchData.setRecentWorkspaces(new String[0]); + launchData.writePersistedData(); + } + + private static String getPreviousInstanceLocation(final Location instanceLocation) { + // CWD is Eclipse infrastructure which stores the location of the + // current workspace in a (text) file in the configuration area (Toolbox + // installation directory) in 1.5.3. With version 1.5.4 of the Toolbox, we will + // use this information below to migrate all workspaces to @user.home/.tlaplus. + final ChooseWorkspaceData launchData = new ChooseWorkspaceData(instanceLocation.getDefault()); + final List<String> recentWorkspaces = Arrays.asList(launchData.getRecentWorkspaces()); + if (!recentWorkspaces.isEmpty()) { + // Get the first non-null workspace. It is the most recently used one. + for(int i = 0; i < recentWorkspaces.size(); i++) { + if (recentWorkspaces.get(i) != null) { + return recentWorkspaces.get(i); + } + } + } + return null; + } + + // https://docs.oracle.com/javase/tutorial/displayCode.html?code=https://docs.oracle.com/javase/tutorial/essential/io/examples/Copy.java + private static void copyRecurively(final Path src, final Path dst, final IProgressMonitor monitor) + throws InvocationTargetException { + try { + dst.toFile().mkdir(); + final Path target = dst.resolve(src.getFileName()); + Files.walkFileTree(src, new FileVisitor<Path>() { + public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) + throws IOException { + try { + Files.copy(dir, target.resolve(src.relativize(dir)), StandardCopyOption.COPY_ATTRIBUTES); + } catch (final FileAlreadyExistsException e) { + // ignore + } + monitor.worked(1); + return FileVisitResult.CONTINUE; + } + + public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { + Files.copy(file, target.resolve(src.relativize(file)), StandardCopyOption.COPY_ATTRIBUTES); + monitor.worked(1); + return FileVisitResult.CONTINUE; + } + + public FileVisitResult visitFileFailed(final Path file, final IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { + final FileTime time = Files.getLastModifiedTime(dir); + Files.setLastModifiedTime(target.resolve(src.relativize(dir)), time); + monitor.worked(1); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + throw new InvocationTargetException(e); + } + } + /* (non-Javadoc) * @see org.eclipse.equinox.app.IApplication#stop() */ diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java index 7a49aa72ce8f7c6dc8e827ef44c23c6590fd543e..c3bfe9203483632ad607fdb4848a4f789a5649ca 100644 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ApplicationWorkbenchWindowAdvisor.java @@ -55,7 +55,7 @@ public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor final IPreferenceNode[] rootSubNodes = preferenceManager.getRootSubNodes(); // @see Bug #191 in general/bugzilla/index.html - final List filters = new ArrayList(); + final List<String> filters = new ArrayList<String>(); filters.add("org.eclipse.compare"); // The following three preferences are shown because the Toolbox uses // the local history feature provided by o.e.team.ui @@ -71,9 +71,9 @@ public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor filters.add("com.abstratt.graphviz.ui"); // Clean the preferences - final List elements = preferenceManager.getElements(PreferenceManager.POST_ORDER); - for (Iterator iterator = elements.iterator(); iterator.hasNext();) { - final Object elem = (Object) iterator.next(); + final List<IPreferenceNode> elements = preferenceManager.getElements(PreferenceManager.POST_ORDER); + for (Iterator<IPreferenceNode> iterator = elements.iterator(); iterator.hasNext();) { + final IPreferenceNode elem = iterator.next(); if (elem instanceof IPluginContribution) { final IPluginContribution aPluginContribution = (IPluginContribution) elem; if (filters.contains(aPluginContribution.getPluginId())) { diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/preferences/AutomaticUpdatesPreferencePage.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/preferences/AutomaticUpdatesPreferencePage.java new file mode 100644 index 0000000000000000000000000000000000000000..53774ab236c1fbd9252bbaf85f7ca2c82f8f6eb7 --- /dev/null +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/preferences/AutomaticUpdatesPreferencePage.java @@ -0,0 +1,373 @@ +/******************************************************************************* + * Copyright (c) 2007, 2016 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Johannes Michler <orgler@gmail.com> - Bug 321568 - [ui] Preference for automatic-update-reminder doesn't work in multilanguage-environments + * Christian Georgi <christian.georgi@sap.com> - Bug 432887 - Setting to show update wizard w/o notification popup + * Mikael Barbero (Eclipse Foundation) - Bug 498116 + *******************************************************************************/ +package org.lamport.tla.toolbox.preferences; + +import java.net.URI; +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import org.eclipse.core.runtime.IProduct; +import org.eclipse.core.runtime.Platform; +import org.eclipse.equinox.internal.p2.ui.ProvUI; +import org.eclipse.equinox.internal.p2.ui.ProvUIActivator; +import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdateMessages; +import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdatePlugin; +import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdateScheduler; +import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdatesPopup; +import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.IAutomaticUpdaterHelpContextIds; +import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.LastAutoCheckForUpdateMemo; +import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.PreferenceConstants; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; +import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; +import org.eclipse.equinox.p2.ui.ProvisioningUI; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PlatformUI; + +/** + * Preference page for automated updates. + * + * @since 3.4 + * + */ +public class AutomaticUpdatesPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + private Button enabledCheck; + private Button enabledBeta; + private Button showUpdateWizard; + private Button onStartupRadio, onFuzzyScheduleRadio; + private Combo fuzzyRecurrenceCombo; + private Button searchOnlyRadio, searchAndDownloadRadio; + private Button remindOnceRadio, remindScheduleRadio; + private Combo remindElapseCombo; + private Group updateScheduleGroup, downloadGroup, remindGroup; + + private URI uri; + private IMetadataRepositoryManager metadataRepositoryManager; + private IArtifactRepositoryManager artifactRepositoryManager; + + public void init(IWorkbench workbench) { + final ProvisioningUI ui = ProvUIActivator.getDefault().getProvisioningUI(); + uri = URI.create("http://lamport.org/tlatoolbox/ci/toolboxUpdate/"); + artifactRepositoryManager = ProvUI.getArtifactRepositoryManager(ui.getSession()); + metadataRepositoryManager = ProvUI.getMetadataRepositoryManager(ui.getSession()); + } + + protected Control createContents(Composite parent) { + PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IAutomaticUpdaterHelpContextIds.AUTOMATIC_UPDATES_PREFERENCE_PAGE); + + Composite container = new Composite(parent, SWT.NULL); + GridLayout layout = new GridLayout(); + layout.marginWidth = layout.marginHeight = 0; + container.setLayout(layout); + + enabledCheck = new Button(container, SWT.CHECK); + enabledCheck.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_findUpdates); + + createSpacer(container, 1); + + container = new Composite(parent, SWT.NULL); + layout = new GridLayout(); + layout.marginWidth = layout.marginHeight = 0; + container.setLayout(layout); + + enabledBeta = new Button(container, SWT.CHECK); + enabledBeta.setText("Receive experimental features for the TLA Toolbox."); + + createSpacer(container, 1); + + updateScheduleGroup = new Group(container, SWT.NONE); + updateScheduleGroup.setText(NLS.bind(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_UpdateSchedule, lastCheckForUpdateDateString())); + layout = new GridLayout(); + layout.numColumns = 3; + updateScheduleGroup.setLayout(layout); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + updateScheduleGroup.setLayoutData(gd); + + onStartupRadio = new Button(updateScheduleGroup, SWT.RADIO); + IProduct product = Platform.getProduct(); + String productName = product != null && product.getName() != null ? product.getName() : AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_GenericProductName; + onStartupRadio.setText(NLS.bind(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_findOnStart, productName)); + gd = new GridData(); + gd.horizontalSpan = 3; + onStartupRadio.setLayoutData(gd); + onStartupRadio.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + onFuzzyScheduleRadio = new Button(updateScheduleGroup, SWT.RADIO); + onFuzzyScheduleRadio.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_findOnSchedule); + gd = new GridData(); + gd.horizontalSpan = 3; + onFuzzyScheduleRadio.setLayoutData(gd); + onFuzzyScheduleRadio.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + fuzzyRecurrenceCombo = new Combo(updateScheduleGroup, SWT.READ_ONLY); + fuzzyRecurrenceCombo.setItems(AutomaticUpdateScheduler.FUZZY_RECURRENCE); + gd = new GridData(); + gd.widthHint = 200; + gd.horizontalIndent = 30; + gd.horizontalSpan = 3; + fuzzyRecurrenceCombo.setLayoutData(gd); + + createSpacer(container, 1); + + downloadGroup = new Group(container, SWT.NONE); + downloadGroup.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_downloadOptions); + layout = new GridLayout(); + layout.numColumns = 3; + downloadGroup.setLayout(layout); + gd = new GridData(GridData.FILL_HORIZONTAL); + downloadGroup.setLayoutData(gd); + + searchOnlyRadio = new Button(downloadGroup, SWT.RADIO); + searchOnlyRadio.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_searchAndNotify); + gd = new GridData(); + gd.horizontalSpan = 3; + searchOnlyRadio.setLayoutData(gd); + searchOnlyRadio.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + searchAndDownloadRadio = new Button(downloadGroup, SWT.RADIO); + searchAndDownloadRadio.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_downloadAndNotify); + gd = new GridData(); + gd.horizontalSpan = 3; + searchAndDownloadRadio.setLayoutData(gd); + searchAndDownloadRadio.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + createSpacer(container, 1); + + remindGroup = new Group(container, SWT.NONE); + remindGroup.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_RemindGroup); + layout = new GridLayout(); + layout.numColumns = 3; + remindGroup.setLayout(layout); + gd = new GridData(GridData.FILL_HORIZONTAL); + remindGroup.setLayoutData(gd); + + remindOnceRadio = new Button(remindGroup, SWT.RADIO); + remindOnceRadio.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_RemindOnce); + gd = new GridData(); + gd.horizontalSpan = 3; + remindOnceRadio.setLayoutData(gd); + remindOnceRadio.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + remindScheduleRadio = new Button(remindGroup, SWT.RADIO); + remindScheduleRadio.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_RemindSchedule); + gd = new GridData(); + gd.horizontalSpan = 3; + remindScheduleRadio.setLayoutData(gd); + remindScheduleRadio.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + remindElapseCombo = new Combo(remindGroup, SWT.READ_ONLY); + remindElapseCombo.setItems(AutomaticUpdatesPopup.ELAPSED_LOCALIZED_STRINGS); + + gd = new GridData(); + gd.widthHint = 200; + gd.horizontalIndent = 30; + gd.horizontalSpan = 3; + remindElapseCombo.setLayoutData(gd); + + showUpdateWizard = new Button(remindGroup, SWT.CHECK); + showUpdateWizard.setText(AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_directlyShowUpdateWizard); + GridDataFactory.fillDefaults().span(3, 1).grab(true, false).applyTo(showUpdateWizard); + + initialize(); + + enabledCheck.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + enabledBeta.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + pageChanged(); + } + }); + + Dialog.applyDialogFont(container); + return container; + } + + private static String lastCheckForUpdateDateString() { + Date lastCheckDate = new LastAutoCheckForUpdateMemo(AutomaticUpdatePlugin.getDefault().getAgentLocation()).read(); + if (lastCheckDate == null) { + return AutomaticUpdateMessages.AutomaticUpdatesPreferencePage_never; + } + + DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, Locale.getDefault()); + return formatter.format(lastCheckDate); + } + + protected void createSpacer(Composite composite, int columnSpan) { + Label label = new Label(composite, SWT.NONE); + GridData gd = new GridData(); + gd.horizontalSpan = columnSpan; + label.setLayoutData(gd); + } + + private void initialize() { + IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore(); + enabledCheck.setSelection(pref.getBoolean(PreferenceConstants.PREF_AUTO_UPDATE_ENABLED)); + enabledBeta.setSelection(artifactRepositoryManager.isEnabled(uri)); + setSchedule(pref.getString(PreferenceConstants.PREF_AUTO_UPDATE_SCHEDULE)); + + fuzzyRecurrenceCombo.setText(AutomaticUpdateScheduler.FUZZY_RECURRENCE[getFuzzyRecurrence(pref, false)]); + + remindScheduleRadio.setSelection(pref.getBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE)); + remindOnceRadio.setSelection(!pref.getBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE)); + remindElapseCombo.setText(AutomaticUpdatesPopup.getElapsedTimeString(pref.getString(PreferenceConstants.PREF_REMIND_ELAPSED))); + searchOnlyRadio.setSelection(!pref.getBoolean(PreferenceConstants.PREF_DOWNLOAD_ONLY)); + searchAndDownloadRadio.setSelection(pref.getBoolean(PreferenceConstants.PREF_DOWNLOAD_ONLY)); + showUpdateWizard.setSelection(pref.getBoolean(PreferenceConstants.PREF_SHOW_UPDATE_WIZARD)); + + pageChanged(); + } + + private void setSchedule(String value) { + if (value.equals(PreferenceConstants.PREF_UPDATE_ON_STARTUP)) { + onStartupRadio.setSelection(true); + } else { + onFuzzyScheduleRadio.setSelection(true); + } + } + + void pageChanged() { + boolean master = enabledCheck.getSelection(); + updateScheduleGroup.setEnabled(master); + onStartupRadio.setEnabled(master); + onFuzzyScheduleRadio.setEnabled(master); + fuzzyRecurrenceCombo.setEnabled(master && onFuzzyScheduleRadio.getSelection()); + downloadGroup.setEnabled(master); + searchOnlyRadio.setEnabled(master); + searchAndDownloadRadio.setEnabled(master); + remindGroup.setEnabled(master); + remindScheduleRadio.setEnabled(master); + remindOnceRadio.setEnabled(master); + remindElapseCombo.setEnabled(master && remindScheduleRadio.getSelection()); + showUpdateWizard.setEnabled(master); + + boolean beta = enabledBeta.getSelection(); + artifactRepositoryManager.setEnabled(uri, beta); + metadataRepositoryManager.setEnabled(uri, beta); + } + + protected void performDefaults() { + super.performDefaults(); + IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore(); + enabledCheck.setSelection(pref.getDefaultBoolean(PreferenceConstants.PREF_AUTO_UPDATE_ENABLED)); + + enabledBeta.setSelection(false); + artifactRepositoryManager.setEnabled(uri, false); + metadataRepositoryManager.setEnabled(uri, false); + + setSchedule(pref.getDefaultString(PreferenceConstants.PREF_AUTO_UPDATE_SCHEDULE)); + + remindOnceRadio.setSelection(!pref.getDefaultBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE)); + remindScheduleRadio.setSelection(pref.getDefaultBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE)); + remindElapseCombo.setText(AutomaticUpdatesPopup.getElapsedTimeString(pref.getDefaultString(PreferenceConstants.PREF_REMIND_ELAPSED))); + + searchOnlyRadio.setSelection(!pref.getDefaultBoolean(PreferenceConstants.PREF_DOWNLOAD_ONLY)); + searchAndDownloadRadio.setSelection(pref.getDefaultBoolean(PreferenceConstants.PREF_DOWNLOAD_ONLY)); + + showUpdateWizard.setSelection(pref.getDefaultBoolean(PreferenceConstants.PREF_SHOW_UPDATE_WIZARD)); + + pageChanged(); + } + + /** + * Method declared on IPreferencePage. Subclasses should override + */ + public boolean performOk() { + IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore(); + pref.setValue(PreferenceConstants.PREF_AUTO_UPDATE_ENABLED, enabledCheck.getSelection()); + + artifactRepositoryManager.setEnabled(uri, enabledBeta.getSelection()); + metadataRepositoryManager.setEnabled(uri, enabledBeta.getSelection()); + + if (onStartupRadio.getSelection()) { + pref.setValue(PreferenceConstants.PREF_AUTO_UPDATE_SCHEDULE, PreferenceConstants.PREF_UPDATE_ON_STARTUP); + } else if (onFuzzyScheduleRadio.getSelection()) { + pref.setValue(PreferenceConstants.PREF_AUTO_UPDATE_SCHEDULE, PreferenceConstants.PREF_UPDATE_ON_FUZZY_SCHEDULE); + new LastAutoCheckForUpdateMemo(AutomaticUpdatePlugin.getDefault().getAgentLocation()).readAndStoreIfAbsent(Calendar.getInstance().getTime()); + } else { + pref.setValue(PreferenceConstants.PREF_AUTO_UPDATE_SCHEDULE, PreferenceConstants.PREF_UPDATE_ON_SCHEDULE); + } + + if (remindScheduleRadio.getSelection()) { + pref.setValue(PreferenceConstants.PREF_REMIND_SCHEDULE, true); + pref.setValue(PreferenceConstants.PREF_REMIND_ELAPSED, AutomaticUpdatesPopup.ELAPSED_VALUES[remindElapseCombo.getSelectionIndex()]); + } else { + pref.setValue(PreferenceConstants.PREF_REMIND_SCHEDULE, false); + } + + pref.setValue(AutomaticUpdateScheduler.P_FUZZY_RECURRENCE, fuzzyRecurrenceCombo.getText()); + + pref.setValue(PreferenceConstants.PREF_DOWNLOAD_ONLY, searchAndDownloadRadio.getSelection()); + + pref.setValue(PreferenceConstants.PREF_SHOW_UPDATE_WIZARD, showUpdateWizard.getSelection()); + + AutomaticUpdatePlugin.getDefault().savePreferences(); + AutomaticUpdatePlugin.getDefault().getScheduler().rescheduleUpdate(); + return true; + } + + private int getFuzzyRecurrence(IPreferenceStore pref, boolean useDefault) { + String day = useDefault ? pref.getDefaultString(AutomaticUpdateScheduler.P_FUZZY_RECURRENCE) : pref.getString(AutomaticUpdateScheduler.P_FUZZY_RECURRENCE); + for (int i = 0; i < AutomaticUpdateScheduler.FUZZY_RECURRENCE.length; i++) + if (AutomaticUpdateScheduler.FUZZY_RECURRENCE[i].equals(day)) + return i; + return 0; + } +} diff --git a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java index c73604876bedbf1142766483ad593f4113b6c782..a1e55c43b35dfd33664e0b2b4a26e9880fd71bf5 100644 --- a/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java +++ b/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java @@ -175,7 +175,7 @@ public class ToolboxIntroPart extends IntroPart implements IIntroPart { final Label lblVersion = new Label(outerContainer, SWT.WRAP); lblVersion.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); - lblVersion.setText("Version 1.5.3 of 14 April 2017"); + lblVersion.setText("Version 1.5.4 of 06 October 2017"); lblVersion.setBackground(backgroundColor); } diff --git a/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ui/util/ProverHelper.java b/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ui/util/ProverHelper.java index 98f6463d83b4d6c0fdc7b7294fe7bfe38f3be203..2f095b17f9a834445aab17275d52a13f515919a5 100644 --- a/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ui/util/ProverHelper.java +++ b/org.lamport.tla.toolbox.tool.prover/src/org/lamport/tla/toolbox/tool/prover/ui/util/ProverHelper.java @@ -44,6 +44,7 @@ import org.lamport.tla.toolbox.tool.prover.ui.preference.ProverSecondPreferenceP import org.lamport.tla.toolbox.tool.prover.ui.view.ObligationsView; import org.lamport.tla.toolbox.ui.dialog.InformationDialog; import org.lamport.tla.toolbox.util.AdapterFactory; +import org.lamport.tla.toolbox.util.LegacyFileDocumentProvider; import org.lamport.tla.toolbox.util.ResourceHelper; import org.lamport.tla.toolbox.util.UIHelper; @@ -1155,7 +1156,19 @@ public class ProverHelper * the finally block of the following try block to avoid a memory leak. */ FileEditorInput fileEditorInput = new FileEditorInput((IFile) module); - FileDocumentProvider fileDocumentProvider = new FileDocumentProvider(); + // Fix deadlock introduced with upgrade to Eclipse Oxygen framework/platform. + // FileDocumentProvider has changed an no longer checks if the IFile module is + // synced. This results in a refresh Job with a lock on the file which deadlocks + // because the ProverJob locked the entire workspace (including the file). At + // the same time, the ProverJob waits for + // org.lamport.tla.toolbox.tool.prover.ui.output.TagBasedTLAPMOutputIncrementalParser.appendText(String) + // to finish. Its implementation calls this method. In other words, the + // ProverJob locks the workspace and triggers a (workspace) Job to refresh the + // module. The refresh Job and the ProverJob deadlock. + // Alternatively, it's possible to acquire/create the IDocument in the ProverJob + // and pass it to the TagBasedTLAPMOutputIncrementalParser. However, I'm not + // sure if this causes problems if a proof spans across multiple files. + final FileDocumentProvider fileDocumentProvider = new LegacyFileDocumentProvider(); try { diff --git a/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF index 3d23cbaf1d78a632f214b094ff7b47cb7a92aa93..f7b581382b7a7ece789c2be45bf96dbde134ac62 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF @@ -12,7 +12,8 @@ Require-Bundle: org.eclipse.ui, Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-Vendor: Daniel Ricketts -Import-Package: org.eclipse.core.resources, +Import-Package: com.abstratt.graphviz, + org.eclipse.core.resources, org.eclipse.e4.core.services.events, org.eclipse.ui, org.eclipse.ui.forms.editor, diff --git a/org.lamport.tla.toolbox.tool.tla2tex/plugin.xml b/org.lamport.tla.toolbox.tool.tla2tex/plugin.xml index 4cf55630046678c002574b4e8d772cba871ad34f..902f64af3ffcba984f1168ab0148bd2fb2114e1b 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/plugin.xml +++ b/org.lamport.tla.toolbox.tool.tla2tex/plugin.xml @@ -64,14 +64,6 @@ </extension> <extension point="org.eclipse.ui.views"> - <view - allowMultiple="true" - class="org.lamport.tla.toolbox.tool.tla2tex.view.PDFBrowser" - icon="icons/document-pdf.png" - id="org.lamport.tla.toolbox.tool.tla2tex.PDFBrowser" - name="PDFBrowser" - restorable="false"> - </view> </extension> <extension point="org.eclipse.ui.bindings"> diff --git a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/TLA2TeXActivator.java b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/TLA2TeXActivator.java index cba5ce81f3fc52e740942f9cacf715d40b9f4c23..c0b52d207fdf0b8a23b0cb116fea9ed2f3c5a85e 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/TLA2TeXActivator.java +++ b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/TLA2TeXActivator.java @@ -1,6 +1,12 @@ package org.lamport.tla.toolbox.tool.tla2tex; +import com.abstratt.graphviz.GraphVizActivator; +import com.abstratt.graphviz.GraphVizActivator.DotMethod; + +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.lamport.tla.toolbox.AbstractTLCActivator; +import org.lamport.tla.toolbox.tool.tla2tex.preference.ITLA2TeXPreferenceConstants; import org.osgi.framework.BundleContext; /** @@ -15,6 +21,36 @@ public class TLA2TeXActivator extends AbstractTLCActivator // The shared instance private static TLA2TeXActivator plugin; + // Listen to preference changes in the TLA2TexPreferencePage. If the value for + // dot command changes, we send the update to the GraphViz preference store. + // GraphViz reads the value from its own store instead of ours. + // Alternatively, we could instantiate GraphViz's own preference page, which + // writes to GraphViz's preference store. However, we don't want to clutter + // the Toolbox's preference with a dedicate GraphViz page. + // <page + // category="toolbox.ui.preferences.GeneralPreferencePage" + // class="com.abstratt.graphviz.ui.GraphVizPreferencePage" + // id="toolbox.ui.preferences.StateGraphPreferences" + // name="State Graph"> + // </page> + private IPropertyChangeListener listener = new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + if (ITLA2TeXPreferenceConstants.DOT_COMMAND.equals(event.getProperty())) { + final String dotCommand = (String) event.getNewValue(); + if ("dot".equals(dotCommand)) { + // Setting it to "dot" implies auto lookup. + TLA2TeXActivator.this.logInfo("dot command set to automatic lookup."); + GraphVizActivator.getInstance().setDotSearchMethod(DotMethod.AUTO); + } else { + // Explicit path is given. + TLA2TeXActivator.this.logInfo("dot command set to: " + dotCommand); + GraphVizActivator.getInstance().setDotSearchMethod(DotMethod.MANUAL); + GraphVizActivator.getInstance().setManualDotPath(dotCommand); + } + } + } + }; + /** * The constructor */ @@ -31,6 +67,8 @@ public class TLA2TeXActivator extends AbstractTLCActivator { super.start(context); plugin = this; + + getPreferenceStore().addPropertyChangeListener(listener); } /* diff --git a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/handler/StandalonePDFViewerRunnable.java b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/handler/StandalonePDFViewerRunnable.java index 62df9daa8cb05f648a9807987928c68cb2395885..c6e320d9a295980be19aeeef53681bf5c2589060 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/handler/StandalonePDFViewerRunnable.java +++ b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/handler/StandalonePDFViewerRunnable.java @@ -3,7 +3,7 @@ package org.lamport.tla.toolbox.tool.tla2tex.handler; import org.eclipse.core.resources.IResource; import org.eclipse.ui.IWorkbenchPartSite; -import org.lamport.tla.toolbox.tool.tla2tex.view.PDFBrowser; +import org.lamport.tla.toolbox.ui.view.PDFBrowser; import org.lamport.tla.toolbox.util.UIHelper; public class StandalonePDFViewerRunnable extends AbstractPDFViewerRunnable { diff --git a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/ITLA2TeXPreferenceConstants.java b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/ITLA2TeXPreferenceConstants.java index ab052e4c412a3f37dc79d51d7f78785baf7a7b4a..d0ab35515f2c5e708cc94a34b8463bb1175e7822 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/ITLA2TeXPreferenceConstants.java +++ b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/ITLA2TeXPreferenceConstants.java @@ -42,4 +42,9 @@ public interface ITLA2TeXPreferenceConstants * True if embedded viewer is to be used */ public static final String EMBEDDED_VIEWER = "embeddedViewer"; + + /** + * Specify the full qualified path to GraphViz's dot executable. + */ + public static final String DOT_COMMAND = "dotCommand"; } diff --git a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferenceInitializer.java b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferenceInitializer.java index dadccf2c01f1e44b691831dfac6a403edc64cd6e..daf862690193274250d7792264948f11f5460a7a 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferenceInitializer.java +++ b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferenceInitializer.java @@ -9,7 +9,6 @@ import org.lamport.tla.toolbox.tool.tla2tex.TLA2TeXActivator; * Gives default preferences for TLA2TeX * * @author Daniel Ricketts - * @version $Id$ */ public class TLA2TeXPreferenceInitializer extends AbstractPreferenceInitializer { @@ -19,6 +18,7 @@ public class TLA2TeXPreferenceInitializer extends AbstractPreferenceInitializer store.setDefault(ITLA2TeXPreferenceConstants.SHADE_COMMENTS, true); store.setDefault(ITLA2TeXPreferenceConstants.NO_PCAL_SHADE, false); store.setDefault(ITLA2TeXPreferenceConstants.NUMBER_LINES, false); + store.setDefault(ITLA2TeXPreferenceConstants.DOT_COMMAND, "dot"); store.setDefault(ITLA2TeXPreferenceConstants.LATEX_COMMAND, "pdflatex"); store.setDefault(ITLA2TeXPreferenceConstants.GRAY_LEVEL, "0.85"); if (Platform.getOS().equals(Platform.OS_MACOSX)) { diff --git a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferencePage.java b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferencePage.java index 2282f3d5d16db8778ae499f17a3c083250f26284..479777961943a0bbd992c03f5c074bc175dc3c1b 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferencePage.java +++ b/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/preference/TLA2TeXPreferencePage.java @@ -56,6 +56,9 @@ public class TLA2TeXPreferencePage extends FieldEditorPreferencePage implements addField(new BooleanFieldEditor( ITLA2TeXPreferenceConstants.NUMBER_LINES, "&Number lines", getFieldEditorParent())); + addField(new StringFieldEditor( + ITLA2TeXPreferenceConstants.DOT_COMMAND, + "&Specify dot command", getFieldEditorParent())); addField(new StringFieldEditor( ITLA2TeXPreferenceConstants.LATEX_COMMAND, "&Specify pdflatex command", getFieldEditorParent())); diff --git a/org.lamport.tla.toolbox.tool.tlc.ui.test/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCStateTest.java b/org.lamport.tla.toolbox.tool.tlc.ui.test/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCStateTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eddf77b693a9932ba48bf2aed4d8ca913f46c4da --- /dev/null +++ b/org.lamport.tla.toolbox.tool.tlc.ui.test/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCStateTest.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.tool.tlc.output.data; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; + +import tla2sany.st.Location; +import util.UniqueString; + +public class TLCStateTest { + + @Test + public void testInitNQSpec() { + final String input = "1: <Initial predicate>\n" + + "/\\ adding = (e1 :> NotAnElement @@ e2 :> NotAnElement)\n" + + "/\\ deq = (d1 :> v1)\n" + + "/\\ enq = (e1 :> Done @@ e2 :> Done)\n" + + "/\\ after = <<>>"; + + final TLCState state = TLCState.parseState(input, "Model_1"); + assertTrue(state.isInitialState()); + assertFalse(state.isBackToState()); + assertFalse(state.isStuttering()); + + assertEquals(1, state.getStateNumber()); + + final List<TLCVariable> variables = state.getVariablesAsList(); + variables.contains( + new TLCVariable("adding", TLCVariableValue.parseValue("(e1 :> NotAnElement @@ e2 :> NotAnElement)"))); + variables.contains( + new TLCVariable("deq", TLCVariableValue.parseValue("(d1 :> v1)"))); + variables.contains( + new TLCVariable("enq", TLCVariableValue.parseValue("(e1 :> Done @@ e2 :> Done)"))); + variables.contains( + new TLCVariable("after", TLCVariableValue.parseValue("<<>>"))); + } + + public void testInitNodeBackup() { + final String input = "1: <Initial predicate>\n" + + "/\\ IsNodeUp = (n1 :> TRUE @@ n2 :> TRUE @@ n3 :> TRUE)\n" + + "/\\ NetworkPath = ( <<n1, n1>> :> TRUE @@\n" + + " <<n1, n2>> :> TRUE @@\n" + + " <<n1, n3>> :> TRUE @@\n" + + " <<n2, n1>> :> TRUE @@\n" + + " <<n2, n2>> :> TRUE @@\n" + + " <<n2, n3>> :> TRUE @@\n" + + " <<n3, n1>> :> TRUE @@\n" + + " <<n3, n2>> :> TRUE @@\n" + + " <<n3, n3>> :> TRUE )\n" + + "/\\ LockTimeout = FALSE\n" + + "/\\ BackupLock = None\n" + + "/\\ IsTakingBackup = (n1 :> FALSE @@ n2 :> FALSE @@ n3 :> FALSE)\n" + + "/\\ Leader = None"; + + final TLCState state = TLCState.parseState(input, "Model_1"); + assertTrue(state.isInitialState()); + assertFalse(state.isBackToState()); + assertFalse(state.isStuttering()); + + assertEquals(1, state.getStateNumber()); + + final List<TLCVariable> variables = state.getVariablesAsList(); + variables.contains( + new TLCVariable("IsNodeUp", TLCVariableValue.parseValue("(n1 :> TRUE @@ n2 :> TRUE @@ n3 :> TRUE)"))); + variables.contains( + new TLCVariable("LockTimeout", TLCVariableValue.parseValue("FALSE"))); + variables.contains( + new TLCVariable("BackupLock", TLCVariableValue.parseValue("None"))); + variables.contains( + new TLCVariable("IsTakingBackup", TLCVariableValue.parseValue("(n1 :> FALSE @@ n2 :> FALSE @@ n3 :> FALSE)"))); + } + + @Test + public void testStuttering() { + final TLCState state = TLCState.parseState("2: Stuttering", "Model_1"); + assertFalse(state.isBackToState()); + assertFalse(state.isInitialState()); + assertTrue(state.isStuttering()); + + assertEquals(2, state.getStateNumber()); + + assertEquals(0, state.getVariablesAsList().size()); + } + + @Test + public void testBackToState() { + final TLCState state = TLCState.parseState( + "3: Back to state: <Action line 102, col 16 to line 104, col 50 of module NQSpec>", "Model_1"); + assertTrue(state.isBackToState()); + assertFalse(state.isInitialState()); + assertFalse(state.isStuttering()); + + assertEquals(3, state.getStateNumber()); + + assertEquals(0, state.getVariablesAsList().size()); + + assertEquals(state.getModuleLocation(), new Location(UniqueString.uniqueStringOf("NQSpec"), 102, 16, 104, 50)); + } + + @Test + public void testStateNQSpec() { + final String input = "2: <Action line 88, col 16 to line 94, col 31 of module NQSpec>\n"+ + "/\\ adding = (e1 :> [data |-> v2, id |-> i2] @@ e2 :> NotAnElement)\n"+ + "/\\ deq = (d1 :> v1)\n"+ + "/\\ enq = (e1 :> v2 @@ e2 :> Done)\n"+ + "/\\ after = ([data |-> v2, id |-> i2] :> {})"; + + final TLCState state = TLCState.parseState(input, "Model_1"); + assertFalse(state.isInitialState()); + assertFalse(state.isBackToState()); + assertFalse(state.isStuttering()); + + assertEquals(2, state.getStateNumber()); + + final List<TLCVariable> variables = state.getVariablesAsList(); + variables.contains( + new TLCVariable("adding", TLCVariableValue.parseValue("(e1 :> [data |-> v2, id |-> i2] @@ e2 :> NotAnElement)"))); + variables.contains( + new TLCVariable("deq", TLCVariableValue.parseValue("(d1 :> v1)"))); + variables.contains( + new TLCVariable("enq", TLCVariableValue.parseValue("(e1 :> v2 @@ e2 :> Done)"))); + variables.contains( + new TLCVariable("after", TLCVariableValue.parseValue("([data |-> v2, id |-> i2] :> {})"))); + + assertEquals(state.getModuleLocation(), new Location(UniqueString.uniqueStringOf("NQSpec"), 88, 16, 94, 31)); + } + + @Test + public void testStateNodeBackup() { + final String input = "2: <Action line 138, col 24 to line 138, col 37 of module NodeBackup>\n" + + "/\\ IsNodeUp = (n1 :> TRUE @@ n2 :> TRUE @@ n3 :> TRUE)\n" + + "/\\ NetworkPath = ( <<n1, n1>> :> TRUE @@\n" + + " <<n1, n2>> :> TRUE @@\n" + + " <<n1, n3>> :> TRUE @@\n" + + " <<n2, n1>> :> TRUE @@\n" + + " <<n2, n2>> :> TRUE @@\n" + + " <<n2, n3>> :> TRUE @@\n" + + " <<n3, n1>> :> TRUE @@\n" + + " <<n3, n2>> :> TRUE @@\n" + + " <<n3, n3>> :> TRUE )\n" + + "/\\ LockTimeout = FALSE\n" + + "/\\ BackupLock = None\n" + + "/\\ IsTakingBackup = (n1 :> FALSE @@ n2 :> FALSE @@ n3 :> FALSE)\n" + + "/\\ Leader = n1"; + + final TLCState state = TLCState.parseState(input, "Model_1"); + assertFalse(state.isInitialState()); + assertFalse(state.isBackToState()); + assertFalse(state.isStuttering()); + + assertEquals(2, state.getStateNumber()); + + final List<TLCVariable> variables = state.getVariablesAsList(); + variables.contains( + new TLCVariable("IsNodeUp", TLCVariableValue.parseValue("(n1 :> TRUE @@ n2 :> TRUE @@ n3 :> TRUE)"))); + variables.contains( + new TLCVariable("LockTimeout", TLCVariableValue.parseValue("FALSE"))); + variables.contains( + new TLCVariable("BackupLock", TLCVariableValue.parseValue("None"))); + variables.contains( + new TLCVariable("IsTakingBackup", TLCVariableValue.parseValue("(n1 :> FALSE @@ n2 :> FALSE @@ n3 :> FALSE)"))); + + assertEquals(state.getModuleLocation(), new Location(UniqueString.uniqueStringOf("NodeBackup"), 138, 24, 138, 37)); + } +} diff --git a/org.lamport.tla.toolbox.tool.tlc.ui.test/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorViewTest.java b/org.lamport.tla.toolbox.tool.tlc.ui.test/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorViewTest.java index fb228942234f5e2ddd5a4eb35c9a90e929fe20ce..62e4abed7f9a02c8c3ad1d9e5ab214f1ffae4a77 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui.test/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorViewTest.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui.test/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorViewTest.java @@ -26,10 +26,13 @@ package org.lamport.tla.toolbox.tool.tlc.ui.view; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.easymock.EasyMock; import org.eclipse.core.resources.IFile; @@ -38,6 +41,10 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.TreeItem; +import org.junit.Assume; import org.junit.Ignore; import org.junit.Test; import org.lamport.tla.toolbox.tool.tlc.model.Model; @@ -114,4 +121,378 @@ public class TLCErrorViewTest { super(launchConfig); } } + + @Test + public void testColoring() { + UIHelper.runUISync(new Runnable() { + public void run() { + try { + doTestColoring(); + } catch (CoreException e) { + e.printStackTrace(); + } + } + }); + } + + private void doTestColoring() throws CoreException { + /* + * Create view and check that it has been created successfully. + */ + final TLCErrorView view = (TLCErrorView) UIHelper.openView(TLCErrorView.ID); + final TreeViewer viewer = view.getViewer(); + Assume.assumeNotNull(viewer); + Assume.assumeNotNull(viewer.getTree()); + + /* + * Create dummy error trace + */ + final TLCError error = new TLCError(); + final boolean sortOrder = false; + + String str = "1: <Initial predicate>\n" + + "/\\ sq = <<>>\n" + + "/\\ sqn = <<{}>>\n" + + "/\\ i = 1\n" + + "/\\ s = {5}\n" + + "/\\ x = (a :> 0 @@ b :> 0 @@ c :> 0)\n" + + "/\\ y = <<0, 0, 0>>\n" + + "/\\ z = [a |-> 0, b |-> 0, c |-> 0]"; + error.addState(TLCState.parseState(str, "testColoring"), sortOrder); + + str = "2: <Action line 22, col 12 to line 29, col 33 of module TraceExplorerColoring>\n" + + "/\\ sq = <<1>>\n" + + "/\\ sqn = <<{},{5}>>\n" + + "/\\ i = 2\n" + + "/\\ s = {5, 6}\n" + + "/\\ x = (a :> 42 @@ b :> 0 @@ c :> 0)\n" + + "/\\ y = <<42, 0, 0>>\n" + + "/\\ z = [a |-> 42, b |-> 0, c |-> 0]"; + error.addState(TLCState.parseState(str, "testColoring"), sortOrder); + + str = "3: <Action line 22, col 12 to line 29, col 33 of module TraceExplorerColoring>\n" + + "/\\ sq = <<1, 2>>\n" + + "/\\ sqn = <<{}, {5}, {5, 6}>>\n" + + "/\\ i = 3\n" + + "/\\ s = {5, 6, 7}\n" + + "/\\ x = (a :> 42 @@ b :> 42 @@ c :> 0)\n" + + "/\\ y = <<42, 42, 0>>\n" + + "/\\ z = [a |-> 42, b |-> 42, c |-> 0]"; + error.addState(TLCState.parseState(str, "testColoring"), sortOrder); + + str = "4: <Action line 22, col 12 to line 29, col 33 of module TraceExplorerColoring>\n" + + "/\\ sq = <<1, 2, 3>>\n" + + "/\\ sqn = <<{}, {5}, {5, 6}, {5, 6, 7}>>\n" + + "/\\ i = 4\n" + + "/\\ s = {5, 6, 7, 8}\n" + + "/\\ x = (a :> 42 @@ b :> 42 @@ c :> 42)\n" + + "/\\ y = <<42, 42, 42>>\n" + + "/\\ z = [a |-> 42, b |-> 42, c |-> 42]"; + error.addState(TLCState.parseState(str, "testColoring"), sortOrder); + + str = "5: <Action line 30, col 12 to line 37, col 29 of module TraceExplorerColoring>\n" + + "/\\ sq = <<2, 3>>\n" + + "/\\ sqn = <<{5}, {5, 6}, {5, 6, 7}>>\n" + + "/\\ i = 5\n" + + "/\\ s = {}\n" + + "/\\ x = (a :> 42 @@ b :> 42 @@ c :> 42)\n" + + "/\\ y = <<42, 42, 42>>\n" + + "/\\ z = [a |-> 42, b |-> 42, c |-> 42]"; + error.addState(TLCState.parseState(str, "testColoring"), sortOrder); + + /* + * Feed error trace to view. + */ + view.setTraceInput(error); + + /* + * Expand all items to force coloring (expect test to fail otherwise). + */ + viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); + viewer.expandAll(); + + /* + * Bring unordered list of treeitems into the actual error trace order. + */ + final Map<Integer, TreeItem> items = new HashMap<Integer, TreeItem>(); + final TreeItem[] anItems = viewer.getTree().getItems(); + for (int i = 0; i < anItems.length; i++) { + final TreeItem item = anItems[i]; + final int idx = Integer.parseInt(item.getText(1).subSequence(13, 14).toString()); + items.put(idx, item); + } + + /* + * Check assertions. + */ + assertEquals(error.getTraceSize(), items.size()); + + // 1. state (no coloring) + TreeItem action = items.get(1); + assertTrue(noneOf(action.getBackground())); + assertTrue(noneOf(action.getBackground(1))); + + TreeItem[] variables = action.getItems(); + assertEquals(7, variables.length); + + for (TreeItem variable : variables) { + assertTrue(noneOf(variable.getBackground())); + assertTrue(noneOf(variable.getBackground(1))); + TreeItem[] values = variable.getItems(); + for (TreeItem value : values) { + assertTrue(noneOf(value.getBackground())); + assertTrue(noneOf(value.getBackground(1))); + } + } + + final Color deleted = TLCUIActivator.getDefault().getDeletedColor(); + final Color changed = TLCUIActivator.getDefault().getChangedColor(); + final Color added = TLCUIActivator.getDefault().getAddedColor(); + + /* + * 2. state + */ + action = items.get(2); + assertEquals(7, action.getItemCount()); + assertTrue(noneOf(action.getBackground())); + assertTrue(noneOf(action.getBackground(1))); + + // Variable "i" + TreeItem var = action.getItem(0); + assertEquals(changed, var.getBackground(1)); + + // Variable "s" + var = action.getItem(1); + assertEquals(changed, var.getBackground(1)); + assertEquals(2, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertEquals(added, var.getItem(1).getBackground(1)); + + // Variable "sq" + var = action.getItem(2); + assertEquals(changed, var.getBackground(1)); + assertEquals(1, var.getItemCount()); + assertEquals(added, var.getItem(0).getBackground(1)); + + // Variable "sqn" + var = action.getItem(3); + assertEquals(changed, var.getBackground(1)); + assertEquals(2, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertEquals(added, var.getItem(1).getBackground(1)); + + // Variable "x" + var = action.getItem(4); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertEquals(changed, var.getItem(0).getBackground(1)); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + // Variable "y" + var = action.getItem(5); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertEquals(changed, var.getItem(0).getBackground(1)); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + // Variable "z" + var = action.getItem(6); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertEquals(changed, var.getItem(0).getBackground(1)); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + /* + * 3. state + */ + action = items.get(3); + assertEquals(7, action.getItemCount()); + assertTrue(noneOf(action.getBackground())); + assertTrue(noneOf(action.getBackground(1))); + + // Variable "i" + var = action.getItem(0); + assertEquals(changed, var.getBackground(1)); + + // Variable "s" + var = action.getItem(1); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertEquals(added, var.getItem(2).getBackground(1)); + + // Variable "sq" + var = action.getItem(2); + assertEquals(changed, var.getBackground(1)); + assertEquals(2, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertEquals(added, var.getItem(1).getBackground(1)); + + // Variable "sqn" + var = action.getItem(3); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertEquals(added, var.getItem(2).getBackground(1)); + + // Variable "x" + var = action.getItem(4); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertEquals(changed, var.getItem(1).getBackground(1)); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + // Variable "y" + var = action.getItem(5); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertEquals(changed, var.getItem(1).getBackground(1)); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + // Variable "z" + var = action.getItem(6); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertEquals(changed, var.getItem(1).getBackground(1)); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + /* + * 4. state + */ + action = items.get(4); + assertEquals(7, action.getItemCount()); + assertTrue(noneOf(action.getBackground())); + assertTrue(noneOf(action.getBackground(1))); + + // Variable "i" + var = action.getItem(0); + assertEquals(changed, var.getBackground(1)); + + // Variable "s" + var = action.getItem(1); + assertEquals(changed, var.getBackground(1)); + assertEquals(4, var.getItemCount()); + assertEquals(deleted, var.getItem(0).getBackground(1)); + assertEquals(deleted, var.getItem(1).getBackground(1)); + assertEquals(deleted, var.getItem(2).getBackground(1)); + assertEquals(added, var.getItem(3).getBackground(1)); + + // Variable "sq" + var = action.getItem(2); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertEquals(deleted, var.getItem(0).getBackground(1)); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertEquals(added, var.getItem(2).getBackground(1)); + + // Variable "sqn" + var = action.getItem(3); + assertEquals(changed, var.getBackground(1)); + assertEquals(4, var.getItemCount()); + assertEquals(deleted, var.getItem(0).getBackground(1)); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + assertEquals(added, var.getItem(3).getBackground(1)); + + // Variable "x" + var = action.getItem(4); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertEquals(changed, var.getItem(2).getBackground(1)); + + // Variable "y" + var = action.getItem(5); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertEquals(changed, var.getItem(2).getBackground(1)); + + // Variable "z" + var = action.getItem(6); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertEquals(changed, var.getItem(2).getBackground(1)); + + /* + * Final state + */ + action = items.get(5); + assertEquals(7, action.getItemCount()); + assertTrue(noneOf(action.getBackground())); + assertTrue(noneOf(action.getBackground(1))); + + // Variable "i" + var = action.getItem(0); + assertEquals(changed, var.getBackground(1)); + + // Variable "s" + var = action.getItem(1); + assertEquals(changed, var.getBackground(1)); + assertEquals(0, var.getItemCount()); + + // Variable "sq" + var = action.getItem(2); + assertEquals(changed, var.getBackground(1)); + assertEquals(2, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + + // Variable "sqn" + var = action.getItem(3); + assertEquals(changed, var.getBackground(1)); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + // Variable "x" + var = action.getItem(4); + assertTrue(noneOf(var.getBackground(1))); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + // Variable "y" + var = action.getItem(5); + assertTrue(noneOf(var.getBackground(1))); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + + // Variable "z" + var = action.getItem(6); + assertTrue(noneOf(var.getBackground(1))); + assertEquals(3, var.getItemCount()); + assertTrue(noneOf(var.getItem(0).getBackground(1))); + assertTrue(noneOf(var.getItem(1).getBackground(1))); + assertTrue(noneOf(var.getItem(2).getBackground(1))); + } + + private boolean noneOf(Color c) { + if (c.equals(TLCUIActivator.getDefault().getDeletedColor())) { + return false; + } + if (c.equals(TLCUIActivator.getDefault().getChangedColor())) { + return false; + } + if (c.equals(TLCUIActivator.getDefault().getAddedColor())) { + return false; + } + return true; + } } diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.tool.tlc.ui/META-INF/MANIFEST.MF index 9dbec0b89e3aba4254c8b75dc9358c4ecf64be09..f30093583fc2492dcd09da2d03e6f1d1db25fd75 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.tool.tlc.ui/META-INF/MANIFEST.MF @@ -17,7 +17,9 @@ Require-Bundle: org.eclipse.core.runtime, org.lamport.tla.toolbox, org.eclipse.ui.navigator;bundle-version="3.4.0", org.eclipse.core.expressions;bundle-version="3.4.100", - javax.mail;bundle-version="1.4.0" + javax.mail;bundle-version="1.4.0", + com.abstratt.graphviz;bundle-version="2.1.201501", + com.abstratt.graphviz.ui;bundle-version="2.1.201501" Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy Import-Package: org.lamport.tla.toolbox.editor.basic diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/icons/full/model_no_error.gif b/org.lamport.tla.toolbox.tool.tlc.ui/icons/full/model_no_error.gif new file mode 100644 index 0000000000000000000000000000000000000000..38cbe8ab8cacadc9278e48fe25e6a2218442b0fa Binary files /dev/null and b/org.lamport.tla.toolbox.tool.tlc.ui/icons/full/model_no_error.gif differ diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/icons/full/model_with_error.gif b/org.lamport.tla.toolbox.tool.tlc.ui/icons/full/model_with_error.gif new file mode 100644 index 0000000000000000000000000000000000000000..9bcaad767c22f2db45de0e314eb9c93abc5dc71a Binary files /dev/null and b/org.lamport.tla.toolbox.tool.tlc.ui/icons/full/model_with_error.gif differ diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/plugin.xml b/org.lamport.tla.toolbox.tool.tlc.ui/plugin.xml index 775e4cefa2ab17fc0ffdc27be05160184a02d393..d1d1faaf92d1e32ffa5caa9a9e58f67852df7064 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/plugin.xml +++ b/org.lamport.tla.toolbox.tool.tlc.ui/plugin.xml @@ -67,6 +67,11 @@ name="modelLaunchName" optional="false"> </commandParameter> + <commandParameter + id="toolbox.tool.tlc.commands.model.open.param.expand.properties" + name="expandPropertiesSection" + optional="true"> + </commandParameter> </command> <command categoryId="toolbox.tool.tlc.commands.category" diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/NewModelHandler.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/NewModelHandler.java index a1fe39ad436084ff1c1d46e034c47ab6264867cb..b61323501b7442fd156f571600ac3047b862ce3d 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/NewModelHandler.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/NewModelHandler.java @@ -3,6 +3,7 @@ package org.lamport.tla.toolbox.tool.tlc.handlers; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Vector; import org.eclipse.core.commands.AbstractHandler; @@ -269,8 +270,7 @@ public class NewModelHandler extends AbstractHandler implements IModelConfigurat IModelConfigurationDefaults.MODEL_BEHAVIOR_TYPE_SPEC_CLOSED); if (foundTermination) { - Vector vec = new Vector(); - launchCopy.setAttribute(MODEL_PROPERTIES_EXPAND, "set"); + Vector<String> vec = new Vector<String>(); vec.add((checkTermination ? "1" : "0") + "Termination"); // The first character should be 1 or 0 depending // on whether or not the box enabling the property should be checked. @@ -374,8 +374,12 @@ public class NewModelHandler extends AbstractHandler implements IModelConfigurat ILaunchConfiguration launchSaved = launchCopy.doSave(); // create parameters for the handler - HashMap parameters = new HashMap(); + final Map<String, String> parameters = new HashMap<String, String>(); parameters.put(OpenModelHandler.PARAM_MODEL_NAME, modelName); + + if (foundSpec && foundTermination) { + parameters.put(OpenModelHandler.PARAM_EXPAND_PROPERTIES, "expand"); + } // runs the command and opens the module [should be model?] in the editor // diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/OpenModelHandler.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/OpenModelHandler.java index 02aaabb35532a8b570d761e805d07fb3f4c7e90d..fc52e496e53c2c9856c3c1f427f87c90caf905f0 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/OpenModelHandler.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/OpenModelHandler.java @@ -8,7 +8,7 @@ import org.lamport.tla.toolbox.tool.ToolboxHandle; import org.lamport.tla.toolbox.tool.tlc.launch.IConfigurationConstants; import org.lamport.tla.toolbox.tool.tlc.model.Model; import org.lamport.tla.toolbox.tool.tlc.model.TLCSpec; -import org.lamport.tla.toolbox.tool.tlc.ui.TLCUIActivator; +import org.lamport.tla.toolbox.tool.tlc.ui.editor.ModelEditor; import org.lamport.tla.toolbox.util.UIHelper; /** @@ -20,6 +20,7 @@ import org.lamport.tla.toolbox.util.UIHelper; public class OpenModelHandler extends AbstractHandler implements IConfigurationConstants { public static final String COMMAND_ID = "toolbox.tool.tlc.commands.model.open"; public static final String PARAM_MODEL_NAME = "toolbox.tool.tlc.commands.model.open.param"; + public static final String PARAM_EXPAND_PROPERTIES = "toolbox.tool.tlc.commands.model.open.param.expand.properties"; public static final String EDITOR_ID = "org.lamport.tla.toolbox.tool.tlc.ui.editor.ModelEditor"; @@ -29,8 +30,7 @@ public class OpenModelHandler extends AbstractHandler implements IConfigurationC public Object execute(final ExecutionEvent event) throws ExecutionException { // The non-qualified model name (no spec prefix) // The ModelHelper associates it implicitly with the current spec - final String modelName = event.getParameter((String) PARAM_MODEL_NAME); - TLCUIActivator.getDefault().logDebug("Open handler invoked on " + modelName); + final String modelName = event.getParameter(PARAM_MODEL_NAME); final Model model = ToolboxHandle.getCurrentSpec().getAdapter(TLCSpec.class).getModel(modelName); final IFile launchFile = model.getLaunchConfiguration().getFile(); @@ -40,10 +40,11 @@ public class OpenModelHandler extends AbstractHandler implements IConfigurationC + " does not exist. Try restarting the Toolbox and if that does not help, delete the model from the Spec Explorer."); } - UIHelper.openEditor(EDITOR_ID, launchFile); - - TLCUIActivator.getDefault().logDebug("Finished open handler"); - + final ModelEditor modelEditor = (ModelEditor) UIHelper.openEditor(EDITOR_ID, launchFile); + if ("expand".equals(event.getParameter(PARAM_EXPAND_PROPERTIES))) { + modelEditor.expandPropertiesSection(); + } + return null; } } diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/RenameModelHandlerDelegate.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/RenameModelHandlerDelegate.java index 3ef525cb9a1695cd751a95f73f05cd1a022df783..aac3c051ace7ad503f15fd62fb3be6015795e622 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/RenameModelHandlerDelegate.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/handlers/RenameModelHandlerDelegate.java @@ -54,6 +54,12 @@ public class RenameModelHandlerDelegate extends AbstractHandler implements IHand + ", because it is being model checked or is locked."); return null; } + if (model.isSnapshot()) { + MessageDialog.openError(UIHelper.getShellProvider().getShell(), "Could not rename model", + "Could not rename the model " + model.getName() + + ", because it is a snapshot."); + return null; + } // b) open dialog prompting for new model name final IInputValidator modelNameInputValidator = new ModelNameValidator(model.getSpec()); diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ITLCModelLaunchDataPresenter.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ITLCModelLaunchDataPresenter.java index 1f39469f7529525fd0b33f8ed97ba6f4df585335..46c311088edcd64cef7385615e569cd8e8c8ab02 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ITLCModelLaunchDataPresenter.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ITLCModelLaunchDataPresenter.java @@ -21,10 +21,11 @@ public interface ITLCModelLaunchDataPresenter public final static int FINGERPRINT_COLLISION_PROBABILITY = 2048; public static final int DISTRIBUTED_SERVER_RUNNING = 4069; public static final int DISTRIBUTED_WORKER_REGISTERED = 8192; + public static final int TLC_MODE = DISTRIBUTED_WORKER_REGISTERED << 1; public final static int[] ALL_FIELDS = { USER_OUTPUT, PROGRESS_OUTPUT, START_TIME, END_TIME, LAST_CHECKPOINT_TIME, COVERAGE_TIME, COVERAGE, PROGRESS, ERRORS, CONST_EXPR_EVAL_OUTPUT, FINGERPRINT_COLLISION_PROBABILITY, - DISTRIBUTED_SERVER_RUNNING, DISTRIBUTED_WORKER_REGISTERED }; + DISTRIBUTED_SERVER_RUNNING, DISTRIBUTED_WORKER_REGISTERED, TLC_MODE }; /** * Inform the presenter about the data changes diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java index 1747820f74da06fdd525563536985740561af73f..b31380b85bccdb73c934330e5e5c8c65802eb69e 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java @@ -52,6 +52,7 @@ import org.eclipse.ui.editors.text.FileDocumentProvider; import org.eclipse.ui.part.FileEditorInput; import org.lamport.tla.toolbox.Activator; import org.lamport.tla.toolbox.tool.tlc.launch.IModelConfigurationConstants; +import org.lamport.tla.toolbox.tool.tlc.model.Assignment; import org.lamport.tla.toolbox.tool.tlc.model.Formula; import org.lamport.tla.toolbox.tool.tlc.model.Model; import org.lamport.tla.toolbox.tool.tlc.model.ModelWriter; @@ -105,6 +106,8 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener protected long startTimestamp; // end time protected long finishTimestamp; + // tlc mode (BFS|DFS|Simu) + protected String tlcMode; // last checkpoint time protected long lastCheckpointTimeStamp; // coverage at @@ -115,6 +118,8 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener protected String fingerprintCollisionProbability; // coverage items protected List<CoverageInformationItem> coverageInfo; + // One of the coverage infos indicate zero coverage. + protected boolean zeroCoverage = false; // progress information protected List<StateSpaceInformationItem> progressInformation; @@ -188,6 +193,7 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener startTime = 0; startTimestamp = Long.MIN_VALUE; finishTimestamp = Long.MIN_VALUE; + tlcMode = ""; lastCheckpointTimeStamp = Long.MIN_VALUE; coverageTimestamp = ""; setCurrentStatus(NOT_RUNNING); @@ -298,7 +304,13 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener case EC.TLC_BEHAVIOR_UP_TO_THIS_POINT: case EC.TLC_COUNTER_EXAMPLE: break; - + + // send to progress output + case EC.TLC_FEATURE_UNSUPPORTED: + case EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY: + setDocumentText(this.progressOutput, outputMessage, true); + break; + // usual errors default: if (this.lastDetectedError != null) @@ -334,8 +346,6 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener // Progress information case EC.TLC_VERSION: case EC.TLC_SANY_START: - case EC.TLC_MODE_MC: - case EC.TLC_MODE_SIMU: case EC.TLC_SANY_END: // case EC.TLC_SUCCESS: case EC.TLC_PROGRESS_START_STATS_DFID: @@ -344,10 +354,26 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener case EC.TLC_STATS_DFID: case EC.TLC_STATS_SIMU: case EC.TLC_SEARCH_DEPTH: + case EC.TLC_STATE_GRAPH_OUTDEGREE: case EC.TLC_LIVE_IMPLIED: case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED: setDocumentText(this.progressOutput, outputMessage, true); break; + case EC.TLC_MODE_MC: + this.tlcMode = "Breadth-first search"; + informPresenter(ITLCModelLaunchDataPresenter.TLC_MODE); + setDocumentText(this.progressOutput, outputMessage, true); + break; + case EC.TLC_MODE_MC_DFS: + this.tlcMode = "Depth-first search"; + informPresenter(ITLCModelLaunchDataPresenter.TLC_MODE); + setDocumentText(this.progressOutput, outputMessage, true); + break; + case EC.TLC_MODE_SIMU: + this.tlcMode = "Simulation"; + informPresenter(ITLCModelLaunchDataPresenter.TLC_MODE); + setDocumentText(this.progressOutput, outputMessage, true); + break; case EC.TLC_SUCCESS: this.setFingerprintCollisionProbability(extractCollisionProbability(outputMessage)); informPresenter(ITLCModelLaunchDataPresenter.FINGERPRINT_COLLISION_PROBABILITY); @@ -436,6 +462,9 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener { // only add coverage of the spec files this.coverageInfo.add(item); + if (item.getCount() == 0) { + this.zeroCoverage = true; + } informPresenter(ITLCModelLaunchDataPresenter.COVERAGE); } break; @@ -679,12 +708,16 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener .getAttribute(attributeName, new ArrayList<String>(0)); int attributeNumber = (attributeIndex != null) ? attributeIndex.intValue() : 0; - if (IModelConfigurationConstants.MODEL_PARAMETER_CONSTANTS.equals(attributeName) - || IModelConfigurationConstants.MODEL_PARAMETER_CONSTANTS - .equals(attributeName)) + if (IModelConfigurationConstants.MODEL_PARAMETER_CONSTANTS.equals(attributeName)) { - // List valueList = ModelHelper.deserializeAssignmentList(attributeValue); - idReplacement = "'LL claims this should not happen. See Bug in TLCModelLaunchDataProvider.'"; + // MK 07/25/2017: Correctly show error when constant is assigned a non-constant. + final List<Assignment> valueList = ModelHelper.deserializeAssignmentList(attributeValue); + if (valueList.size() >= (attributeNumber + 1)) { + final Assignment assignment = valueList.get(attributeNumber); + idReplacement = assignment.getRight(); + } else { + idReplacement = "'LL claims this should not happen. See Bug in TLCModelLaunchDataProvider.'"; + } } else { // invariants and properties @@ -902,6 +935,10 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener { return finishTimestamp; } + + public String getTLCMode() { + return tlcMode; + } public String getCoverageTimestamp() { @@ -923,6 +960,10 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener this.coverageInfo = coverageInfo; } + public boolean hasZeroCoverage() { + return this.zeroCoverage; + } + public List<StateSpaceInformationItem> getProgressInformation() { return progressInformation; diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCSequenceVariableValue.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCSequenceVariableValue.java index 727aed0db9a2b1e2a71c3641a0af0dd2828d159f..02825d5b948ed26b15af660c301fc9b3597c3c0a 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCSequenceVariableValue.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCSequenceVariableValue.java @@ -164,5 +164,23 @@ public class TLCSequenceVariableValue extends TLCVariableValue implements TLCMul setFcnElementArrayDiffInfo(firstElts, secondElts); return; } + + /* + * There are four cases: isPrefix and firstShorter : we mark end of + * longer (= second) as added. isPrefix and !firstShorter : we mark + * end of longer (= first) as deleted. isSuffix and firstShorter : + * we mark beginning of longer (=second) as added. isSuffix and + * !firstShorter : we mark beginning of longer (=first) as deleted. + */ + final int firstEltToMark = (isPrefix) ? shorter.length : 0; + + for (int i = 0; i < longer.length - shorter.length; i++) { + final TLCVariableValue value = (TLCVariableValue) longer[i + firstEltToMark].value; + if (firstShorter) { + value.setAdded(); + } else { + value.setDeleted(); + } + } } } diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java index dd5e6db313dc0415b9c7d5526355f64ba777af6b..d721001590c80880ec526251173afafa22c0f5b9 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java @@ -43,10 +43,10 @@ public class TLCState implements IModuleLocatable { private static final String COLON = ":"; private static final String CR = "\n"; - private static final String STUTTERING = "Stuttering"; + private static final String STUTTERING = " Stuttering"; // See tlc2.output.MP private static final String AND = "/\\"; private static final String EQ = " = "; - private static final String BACK_TO_STATE = "Back"; + private static final String BACK_TO_STATE = " Back to state"; // See tlc2.output.MP /** * A factory for stuttering states @@ -91,14 +91,14 @@ public class TLCState implements IModuleLocatable int number = Integer.parseInt(input.substring(0, index)); String label = input.substring(index + 1, index2); - if (label.indexOf(STUTTERING) != -1) + if (label.indexOf(STUTTERING) == 0) { return STUTTERING_STATE(number, modelName); - } else if (label.indexOf(BACK_TO_STATE) != -1) + } else if (label.indexOf(BACK_TO_STATE) == 0) { final TLCState state = BACK_TO_STATE(number, modelName); // See in MP.java case for EC.TLC_BACK_TO_STATE - state.setLocation(Location.parseLocation(label.substring(" Back to State: ".length(), label.length()))); + state.setLocation(Location.parseLocation(label.substring((BACK_TO_STATE + ": ").length(), label.length()))); return state; } else { diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCVariableValue.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCVariableValue.java index 5b24b31f52c2a418d22d088cec6dd57c37734cc6..73042280cc0825359f027bf50cda1a7476da5f1b 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCVariableValue.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCVariableValue.java @@ -597,6 +597,7 @@ public abstract class TLCVariableValue TLCVariableValue first = (TLCVariableValue) firstElts[i].getValue(); TLCVariableValue second = (TLCVariableValue) secondElts[j].getValue(); if (!first.toSimpleString().equals(second.toSimpleString())) { + secondElts[i].setChanged(); second.setChanged(); if (first.getClass().equals(second.getClass())) { // Only diff objects of identical types @@ -622,6 +623,7 @@ public abstract class TLCVariableValue } if (notfound) { secondElts[i].setAdded(); + ((TLCVariableValue) secondElts[i].getValue()).setAdded(); } } } diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TraceExplorerDataProvider.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TraceExplorerDataProvider.java index 0bc19b53d5ec2307ee48235cd0af77437fa8d045..0cc5b4b080d500e68604a6abd1b705d6c6a161cd 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TraceExplorerDataProvider.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TraceExplorerDataProvider.java @@ -28,6 +28,7 @@ import org.lamport.tla.toolbox.tool.tlc.output.source.TLCRegionContainer; import org.lamport.tla.toolbox.tool.tlc.traceexplorer.TraceExplorerHelper; import org.lamport.tla.toolbox.tool.tlc.ui.TLCUIActivator; import org.lamport.tla.toolbox.tool.tlc.ui.view.TLCErrorView; +import org.lamport.tla.toolbox.util.LegacyFileDocumentProvider; import org.lamport.tla.toolbox.util.UIHelper; import tlc2.output.EC; @@ -108,7 +109,12 @@ public class TraceExplorerDataProvider extends TLCModelLaunchDataProvider */ IFile teFile = getModel().getTraceExplorerTLAFile(); FileEditorInput teFileEditorInput = new FileEditorInput((IFile) teFile); - FileDocumentProvider teFileDocumentProvider = new FileDocumentProvider(); + // Use LegacyFileDocumentProvider to fix race condition which causes Trace + // Explorer Exploration to label the expression as __trace_var_XXXXXXXX instead + // of the actual expression. The broken label is accompanied by an exception: + // java.lang.IllegalArgumentException: Attempted to beginRule: F/DijkstraMutex/Model_1, + // does not match outer scope rule: org.lamport.tla.toolbox.tool.tlc.launch.TLCModelLaunchDelegate$MutexRule@1e6cad2d + FileDocumentProvider teFileDocumentProvider = new LegacyFileDocumentProvider(); try { diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/source/TLCOutputSourceRegistry.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/source/TLCOutputSourceRegistry.java index d0ce4dc5291efa977afed4cafcb67f6648b17966..f5ee62caf36c8a5f6ab32b5a3863e6a58be06f13 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/source/TLCOutputSourceRegistry.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/source/TLCOutputSourceRegistry.java @@ -1,8 +1,9 @@ package org.lamport.tla.toolbox.tool.tlc.output.source; import java.io.IOException; -import java.util.Enumeration; -import java.util.Hashtable; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.WorkspaceJob; @@ -55,15 +56,15 @@ import org.lamport.tla.toolbox.tool.tlc.ui.TLCUIActivator; */ public class TLCOutputSourceRegistry { - private static final boolean DO_DEBUG = true; + private static final boolean DO_DEBUG = Boolean.getBoolean(TLCOutputSourceRegistry.class.getName() + ".debug"); // instance for output sources from model checking private static TLCOutputSourceRegistry modelCheckInstance; // instance for output sources from trace exploration private static TLCOutputSourceRegistry traceExploreInstance; // container for sources, hashed by the source name - private Hashtable<Model, ITLCOutputSource> sources; + private Map<Model, ITLCOutputSource> sources; // container for data providers, hashed by the process name - private Hashtable<Model, TLCModelLaunchDataProvider> providers; + private Map<Model, TLCModelLaunchDataProvider> providers; // flag indicating if this is a trace explorer instance // true indicates that this is a trace explorer instance private boolean isTraceExploreInstance; @@ -119,9 +120,8 @@ public class TLCOutputSourceRegistry */ private synchronized void removeTLCStatusSource(Model model) { - String name = model.getLaunchConfiguration().getFile().getName(); - this.sources.remove(name); - this.providers.remove(name); + this.sources.remove(model); + this.providers.remove(model); printStats(); } @@ -258,8 +258,8 @@ public class TLCOutputSourceRegistry */ private TLCOutputSourceRegistry() { - this.sources = new Hashtable<Model, ITLCOutputSource>(); - this.providers = new Hashtable<Model, TLCModelLaunchDataProvider>(); + this.sources = new HashMap<Model, ITLCOutputSource>(); + this.providers = new HashMap<Model, TLCModelLaunchDataProvider>(); } /** @@ -303,15 +303,12 @@ public class TLCOutputSourceRegistry } TLCUIActivator.getDefault().logDebug("TLCOutputSourceRegistry for " + type + " maintains " + sources.size() + " sources."); - Enumeration<Model> keys = sources.keys(); - while (keys.hasMoreElements()) - { - Model model = keys.nextElement(); - ITLCOutputSource source = sources.get(model); - TLCUIActivator.getDefault().logDebug("The source " + model.getName() + " has " + source.getSourcePrio() + " prio and " - + source.getListeners().length + " listeners"); - - } + Set<Model> keySet = sources.keySet(); + for (Model model : keySet) { + ITLCOutputSource source = sources.get(model); + TLCUIActivator.getDefault().logDebug("The source " + model.getName() + " has " + source.getSourcePrio() + " prio and " + + source.getListeners().length + " listeners"); + } } } } diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/dialog/TLAFilteredItemsSelectionDialog.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/dialog/TLAFilteredItemsSelectionDialog.java index 01a5cbfd9b2d2ddcd52ce11723e22c837297bf5f..021e19b0c0b8dae16c5912fc8ecd3a346930cdbc 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/dialog/TLAFilteredItemsSelectionDialog.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/dialog/TLAFilteredItemsSelectionDialog.java @@ -463,9 +463,9 @@ public class TLAFilteredItemsSelectionDialog extends FilteredItemsSelectionDialo } if (element instanceof Spec) { if (Activator.getSpecManager().isSpecLoaded((Spec) element)) { - return PlatformUI.getWorkbench().getSharedImages().getImage(SharedImages.IMG_OBJ_PROJECT); + return Activator.getDefault().getImageRegistry().get(Activator.IMG_SPEC_OPEN); } - return PlatformUI.getWorkbench().getSharedImages().getImage(SharedImages.IMG_OBJ_PROJECT_CLOSED); + return Activator.getDefault().getImageRegistry().get(Activator.IMG_SPEC_CLOSED); } else if (element instanceof Module) { return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE); } else if (element instanceof Model) { diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java index daf515f1a40aff099818c0106b16e1e51300f71e..e5557023b3a2f9e7397055a49aa070886c3d240d 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java @@ -1,9 +1,13 @@ package org.lamport.tla.toolbox.tool.tlc.ui.editor; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.text.SimpleDateFormat; + import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; @@ -11,10 +15,15 @@ import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.IMessageProvider; @@ -28,6 +37,8 @@ import org.eclipse.swt.custom.CTabFolder2Listener; import org.eclipse.swt.custom.CTabFolderEvent; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; @@ -43,9 +54,11 @@ import org.eclipse.ui.texteditor.ITextEditor; import org.lamport.tla.toolbox.Activator; import org.lamport.tla.toolbox.spec.Spec; import org.lamport.tla.toolbox.spec.parser.IParseConstants; +import org.lamport.tla.toolbox.tool.tlc.TLCActivator; import org.lamport.tla.toolbox.tool.tlc.launch.IModelConfigurationDefaults; import org.lamport.tla.toolbox.tool.tlc.launch.TLCModelLaunchDelegate; import org.lamport.tla.toolbox.tool.tlc.model.Model; +import org.lamport.tla.toolbox.tool.tlc.model.Model.StateChangeListener.ChangeEvent.State; import org.lamport.tla.toolbox.tool.tlc.model.TLCModelFactory; import org.lamport.tla.toolbox.tool.tlc.output.data.TLCModelLaunchDataProvider; import org.lamport.tla.toolbox.tool.tlc.output.source.TLCOutputSourceRegistry; @@ -61,9 +74,12 @@ import org.lamport.tla.toolbox.tool.tlc.ui.view.TLCErrorView; import org.lamport.tla.toolbox.tool.tlc.util.ChangedSpecModulesGatheringDeltaVisitor; import org.lamport.tla.toolbox.tool.tlc.util.ModelHelper; import org.lamport.tla.toolbox.ui.handler.OpenSpecHandler; +import org.lamport.tla.toolbox.ui.view.PDFBrowserEditor; import org.lamport.tla.toolbox.util.ResourceHelper; import org.lamport.tla.toolbox.util.UIHelper; +import com.abstratt.graphviz.GraphViz; + import tla2sany.semantic.ModuleNode; /** @@ -72,6 +88,7 @@ import tla2sany.semantic.ModuleNode; */ public class ModelEditor extends FormEditor { + private static final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd,yyyy HH:mm:ss"); /** * Editor ID @@ -83,8 +100,40 @@ public class ModelEditor extends FormEditor */ // helper to resolve semantic matches of words private SemanticHelper helper; - // reacts on model changes - private IResourceChangeListener modelFileChangeListener; + private final Model.StateChangeListener modelStateListener = new Model.StateChangeListener() { + @Override + public boolean handleChange(final ChangeEvent event) { + if (event.getState().in(State.NOT_RUNNING, State.RUNNING)) { + UIHelper.runUIAsync(new Runnable() { + public void run() { + for (int i = 0; i < getPageCount(); i++) { + final Object object = pages.get(i); + if (object instanceof BasicFormPage) { + final BasicFormPage bfp = (BasicFormPage) object; + bfp.refresh(); + } + } + if (event.getState().in(State.RUNNING)) { + // Switch to Result Page (put on top) of model editor stack. A user wants to see + // the status of a model run she has just started. + ModelEditor.this.showResultPage(); + } + if (event.getState().in(State.NOT_RUNNING)) { + // Model checking finished, lets open state graph if any. + if (event.getModel().hasStateGraphDump()) { + try { + ModelEditor.this.addOrUpdateStateGraphEditor(event.getModel().getStateGraphDump()); + } catch (CoreException e) { + TLCUIActivator.getDefault().logError("Error initializing editor", e); + } + } + } + } + }); + } + return false; + } + }; /** * This runnable is responsible for the validation of the pages. @@ -223,61 +272,13 @@ public class ModelEditor extends FormEditor model = TLCModelFactory.getBy(finput.getFile()); - /* - * Install a resource change listener on the file opened which react - * on marker changes - */ - // construct the listener - modelFileChangeListener = new IResourceChangeListener() { - public void resourceChanged(IResourceChangeEvent event) { - // get the marker changes - IMarkerDelta[] markerChanges = event.findMarkerDeltas(Model.TLC_MODEL_IN_USE_MARKER, false); - - // usually this list has at most one element - for (int i = 0; i < markerChanges.length; i++) { - if (getFileEditorInput().getFile().equals(markerChanges[i].getResource())) { - UIHelper.runUIAsync( - /* - * If the model file is changed, refresh the - * changes in the editor if the model is in use, - * activate the third page - */ - new Runnable() { - public void run() { - // update the pages - for (int i = 0; i < getPageCount(); i++) { - /* - * Note that all pages are not necessarily - * instances of BasicFormPage. Some are read - * only editors showing saved versions of - * modules. - */ - if (pages.get(i) instanceof BasicFormPage) { - BasicFormPage page = (BasicFormPage) pages.get(i); - ((BasicFormPage) page).refresh(); - } - } - - if (model.isRunning()) { - showResultPage(); - } - // evtl. add more graphical sugar here, - // like changing the model icon, - // changing the editor title (part name) - } - }); - // It's sufficient to only update the UI once - return; - } - } - } - }; - - // add to the workspace root - ResourcesPlugin.getWorkspace().addResourceChangeListener(modelFileChangeListener, IResourceChangeEvent.POST_CHANGE); - // setContentDescription(path.toString()); - this.setPartName(model.getName()); + if (model.isSnapshot()) { + final String date = sdf.format(model.getSnapshotTimeStamp()); + this.setPartName(model.getSnapshotFor().getName() + " (" + date + ")"); + } else { + this.setPartName(model.getName()); + } this.setTitleToolTip(model.getFile().getLocation().toOSString()); // add a listener that will update the tlc error view when a model editor @@ -305,6 +306,8 @@ public class ModelEditor extends FormEditor addPageChangedListener(pageChangedListener); } }); + + model.add(modelStateListener); } /** @@ -324,7 +327,7 @@ public class ModelEditor extends FormEditor navigationHistory.markLocation((IEditorPart) event .getSelectedPage()); } - };; + }; /** * @see org.eclipse.ui.forms.editor.FormEditor#dispose() @@ -334,7 +337,7 @@ public class ModelEditor extends FormEditor // TLCUIActivator.getDefault().logDebug("entering ModelEditor#dispose()"); // remove the listeners ResourcesPlugin.getWorkspace().removeResourceChangeListener(workspaceResourceChangeListener); - ResourcesPlugin.getWorkspace().removeResourceChangeListener(modelFileChangeListener); + model.remove(modelStateListener); super.dispose(); @@ -541,21 +544,132 @@ public class ModelEditor extends FormEditor // run the validation UIHelper.runUIAsync(validateRunable); - } catch (PartInitException e) + + ModuleNode rootModule = SemanticHelper.getRootModuleNode(); + if (rootModule != null && rootModule.getVariableDecls().length == 0 + && rootModule.getConstantDecls().length == 0) + { + showResultPage(); + } + + if (model.hasStateGraphDump()) { + addOrUpdateStateGraphEditor(model.getStateGraphDump()); + } + } catch (CoreException e) { TLCUIActivator.getDefault().logError("Error initializing editor", e); } - ModuleNode rootModule = SemanticHelper.getRootModuleNode(); - if (rootModule != null && rootModule.getVariableDecls().length == 0 - && rootModule.getConstantDecls().length == 0) - { - showResultPage(); - } - // TLCUIActivator.getDefault().logDebug("leaving ModelEditor#addPages()"); } + + public void addOrUpdateStateGraphEditor(final IFile stateGraphDotDump) throws CoreException { + // For historical reasons this preference is found in the tlatex bundle. Thus, + // we read the value from there, but don't refer to the corresponding string + // constants to not introduce a plugin dependency. + // org.lamport.tla.toolbox.tool.tla2tex.TLA2TeXActivator.PLUGIN_ID + // org.lamport.tla.toolbox.tool.tla2tex.preference.ITLA2TeXPreferenceConstants.EMBEDDED_VIEWER + final boolean useEmbeddedViewer = Platform.getPreferencesService() + .getBoolean("org.lamport.tla.toolbox.tool.tla2tex", "embeddedViewer", false, null); + + final IEditorPart findEditor; + if (useEmbeddedViewer) { + // Try to get hold of the editor instance without opening it yet. Opening is + // triggered by calling addPage. + findEditor = UIHelper.findEditor("de.vonloesch.pdf4eclipse.editors.PDFEditor"); + } else { + findEditor = UIHelper.findEditor(PDFBrowserEditor.ID); + } + + // Load a previously generated pdf file. + final IFile file = model.getFolder().getFile(model.getName() + ".pdf"); + if (file.exists()) { + addPage(findEditor, new FileEditorInput(file)); + return; + } + // Generating a PDF from the dot file can be a time consuming task. Thus, wrap + // the generation inside a background job for processing. When done, the job will join the + // main thread and update the UI (create the multipage editor page that renders + // the pdf). Errors (IStatus) are handled by the Job framework and trigger a dialog, unless + // errors occur inside the UI runnable. There we handle errors manually. + final Job j = new WorkspaceJob("Generating State Graph Visualization...") { + @Override + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + try { + // Generate PDF (this process runs with a timeout of one minute which is why we + // don't provide a mechanism to cancel it. + final byte[] load = GraphViz.load(new FileInputStream(stateGraphDotDump.getLocation().toFile()), + "pdf", 0, 0); + // Write byte[] into IFile file + file.create(new ByteArrayInputStream(load), IResource.NONE, null); + UIHelper.runUISync(new Runnable() { + @Override + public void run() { + try { + addPage(findEditor, new FileEditorInput(file)); + } catch (PartInitException e) { + final Shell shell = Display.getDefault().getActiveShell(); + MessageDialog.openError(shell == null ? new Shell() : shell, + "Opening state graph visualization failed.", + "Opening state graph visualization failed: " + e.getMessage()); + } catch (OutOfMemoryError e) { + final Shell shell = Display.getDefault().getActiveShell(); + MessageDialog.openError(shell == null ? new Shell() : shell, "Opening state graph visualization ran out of memory.", + "Opening state graph visualization ran out of memory. The state graph is likely too large. " + + "Try using a standalone PDF viewer if the Toolbox is currently set to use the built-in one."); + } + } + }); + } catch (CoreException e) { + // If generation failed to generate a pdf, inform the user by raising a dialog. + // Reason of failure can be 1) input too large 2) incorrect dot path 3) ... + return shortenStatusMessage(e.getStatus()); + } catch (FileNotFoundException notExpectedTohappen) { + // We don't expect this to happen, because addOrUpdateStateGraphEditor gets + // called with a valid file. + return new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, notExpectedTohappen.getMessage(), + notExpectedTohappen); + } + return Status.OK_STATUS; + } + }; + j.setUser(true); + j.setPriority(Job.LONG); + j.schedule(); + } + + // Shorten message to 1024 chars in case GraphViz attached the complete dot + // input which can be huge. + // https://github.com/abstratt/eclipsegraphviz/issues/8 + private static IStatus shortenStatusMessage(IStatus status) { + if (status.isMultiStatus()) { + final IStatus[] convertedChildren = new Status[status.getChildren().length]; + // convert nested status objects. + final IStatus[] children = status.getChildren(); + for (int i = 0; i < children.length; i++) { + final IStatus child = children[i]; + convertedChildren[i] = new Status(child.getSeverity(), child.getPlugin(), child.getCode(), + substring(child.getMessage()), + child.getException()); + } + return new MultiStatus(status.getPlugin(), status.getCode(), convertedChildren, + substring(status.getMessage()), + status.getException()); + } else { + return new Status(status.getSeverity(), status.getPlugin(), status.getCode(), + substring(status.getMessage()), + status.getException()); + } + } + + private static String substring(String in) { + if (in.length() > 1024) { + return in.substring(0, 1024) + "... (" + (in.length() - 1024) + " chars omitted)"; + } + return in; + } + /* --------------------------------------------------------------------- */ public void launchModel(final String mode, final boolean userPased) { @@ -572,6 +686,15 @@ public class ModelEditor extends FormEditor * @throws CoreException */ public void launchModel(final String mode, final boolean userPased, final IProgressMonitor monitor) { + if (model.isSnapshot()) { + final boolean launchSnapshot = MessageDialog.openConfirm(getSite().getShell(), "Model is a snapshot", + "The model which is about to launch is a snapshot of another model. " + + "Beware that no snapshots of snapshots are taken. " + + "Click the \"OK\" button to launch the snapshot anyway."); + if (!launchSnapshot) { + return; + } + } final IWorkspace workspace = ResourcesPlugin.getWorkspace(); try { workspace.run(new IWorkspaceRunnable() { @@ -718,11 +841,11 @@ public class ModelEditor extends FormEditor */ for (int i = 0; i < getPageCount(); i++) { /* - * The normal form pages (main model page, advanced - * options, results) are not text editors. We leave - * those pages but remove all text editors. + * The normal form pages (main model page, advanced options, results) are remain + * open, all other pages get closed i.e. Saved Module Editor and State Graph + * editor. */ - if (pages.get(i) instanceof ITextEditor) { + if (!(pages.get(i) instanceof BasicFormPage)) { removePage(i); } } @@ -738,7 +861,7 @@ public class ModelEditor extends FormEditor } } } - }, monitor); + }, workspace.getRoot(), IWorkspace.AVOID_UPDATE, monitor); } catch (CoreException e) { TLCUIActivator.getDefault().logError( "Error launching the configuration " + model.getName(), e); @@ -1019,6 +1142,20 @@ public class ModelEditor extends FormEditor } } } + + /** + * Expands the properties section on the main model editor page. + */ + public void expandPropertiesSection() { + for (int i = 0; i < pagesToAdd.length; i++) { + final BasicFormPage basicFormPage = pagesToAdd[i]; + if (basicFormPage instanceof MainModelPage) { + final MainModelPage mainPage = (MainModelPage) basicFormPage; + mainPage.expandPropertiesSection(); + return; + } + } + } // TODO remove public void setUpPage(BasicFormPage newPage, int index) @@ -1140,7 +1277,9 @@ public class ModelEditor extends FormEditor } // setPageImage(pageIndex, image); - } + } else if (input instanceof FileEditorInput && "pdf".equals(((FileEditorInput) input).getFile().getFileExtension())) { + setPageText(index, "State Graph"); + } } /** diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/AdvancedModelPage.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/AdvancedModelPage.java index 33e17eab3f0012bdeaee2e41e0cc4b8835f400d6..207b803ade6d217a0cccd70df54e41b1f7c35e80 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/AdvancedModelPage.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/AdvancedModelPage.java @@ -66,6 +66,8 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo private Button dfidOption; private Button mcOption; private Button simulationOption; + private Button deferLiveness; + private Button visualizeStateGraph; private Text dfidDepthText; private Text simuDepthText; private Text simuSeedText; @@ -174,6 +176,9 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo simuSeedText.setText(""); } + // Defer Liveness + deferLiveness.setSelection(getModel().getAttribute(LAUNCH_DEFER_LIVENESS, LAUNCH_DEFER_LIVENESS_DEFAULT)); + // fp index final int fpIndex = getModel().getAttribute(LAUNCH_FP_INDEX, LAUNCH_FP_INDEX_DEFAULT); fpIndexSpinner.setSelection(fpIndex); @@ -188,6 +193,9 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo ITLCPreferenceConstants.I_TLC_MAXSETSIZE_DEFAULT); maxSetSize.setSelection(getModel().getAttribute(LAUNCH_MAXSETSIZE, defaultMaxSetSize)); + // visualize state graph + visualizeStateGraph.setSelection(getModel().getAttribute(LAUNCH_VISUALIZE_STATEGRAPH, LAUNCH_VISUALIZE_STATEGRAPH_DEFAULT)); + // Extra JVM arguments and system properties final String vmArgs = getModel().getAttribute(LAUNCH_JVM_ARGS, LAUNCH_JVM_ARGS_DEFAULT); this.extraVMArgumentsText.setText(vmArgs); @@ -234,6 +242,9 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo // simulation seed getModel().setAttribute(LAUNCH_SIMU_ARIL, simuAril); + // Defer Liveness + getModel().setAttribute(LAUNCH_DEFER_LIVENESS, deferLiveness.getSelection()); + // FP Seed index getModel().setAttribute(LAUNCH_FP_INDEX, fpIndexSpinner.getSelection()); @@ -242,6 +253,9 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo // fpBits getModel().setAttribute(LAUNCH_MAXSETSIZE, maxSetSize.getSelection()); + + // Visualize State Graph + getModel().setAttribute(LAUNCH_VISUALIZE_STATEGRAPH, visualizeStateGraph.getSelection()); // definitions List<String> definitions = FormHelper.getSerializedInput(definitionsTable); @@ -768,9 +782,11 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo maxSetSize.addModifyListener(launchListener); dfidDepthText.addModifyListener(launchListener); simulationOption.addSelectionListener(launchListener); + deferLiveness.addSelectionListener(launchListener); dfidOption.addSelectionListener(launchListener); mcOption.addSelectionListener(launchListener); viewSource.addTextListener(launchListener); + visualizeStateGraph.addSelectionListener(launchListener); extraTLCParametersText.addModifyListener(launchListener); extraVMArgumentsText.addModifyListener(launchListener); @@ -909,7 +925,25 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo toolkit.createSeparator(area, SWT.HORIZONTAL); // add empty composite to make the two column grid layout happy toolkit.createComposite(area); - + + // label deferred liveness checking + final String deferLivenessHelp = "Defer verification of temporal properties (liveness) to the end of model checking" + + " to reduce overall model checking time. Liveness violations will be found late compared to invariant " + + "violations. In other words check liveness only once on the complete state space."; + Label deferLivenessLabel = toolkit.createLabel(area, "Verify temporal properties upon termination only:"); + gd = new GridData(); + gd.horizontalIndent = 0; + deferLivenessLabel.setLayoutData(gd); + deferLivenessLabel.setToolTipText(deferLivenessHelp); + + deferLiveness = toolkit.createButton(area, "", SWT.CHECK); + gd = new GridData(); + gd.widthHint = 200; + gd.verticalIndent = 20; + gd.horizontalIndent = 0; + deferLiveness.addFocusListener(focusListener); + deferLiveness.setToolTipText(deferLivenessHelp); + // label fp Label fpLabel = toolkit.createLabel(area, "Fingerprint seed index:"); gd = new GridData(); @@ -977,6 +1011,23 @@ public class AdvancedModelPage extends BasicFormPage implements IConfigurationCo int defaultMaxSetSize = TLCUIActivator.getDefault().getPreferenceStore().getInt( ITLCPreferenceConstants.I_TLC_MAXSETSIZE_DEFAULT); maxSetSize.setSelection(defaultMaxSetSize); + + // Visualize State Graph with GraphViz (dot) + final String visualizeStateGraphHelp = "Draw the state graph after completion of model checking provided the " + + "state graph is sufficiently small (cannot handle more than a few dozen states and slows down model checking)."; + Label visualizeStateGraphLabel = toolkit.createLabel(area, "Visualize state graph after completion of model checking:"); + gd = new GridData(); + gd.horizontalIndent = 0; + visualizeStateGraphLabel.setLayoutData(gd); + visualizeStateGraphLabel.setToolTipText(visualizeStateGraphHelp); + + visualizeStateGraph = toolkit.createButton(area, "", SWT.CHECK); + gd = new GridData(); + gd.widthHint = 200; + gd.verticalIndent = 20; + gd.horizontalIndent = 0; + visualizeStateGraph.addFocusListener(focusListener); + visualizeStateGraph.setToolTipText(visualizeStateGraphHelp); // Extra/Additional VM arguments and system properties toolkit.createLabel(area, "JVM arguments:"); diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/MainModelPage.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/MainModelPage.java index e51b874c6552e3538ac73d751edf90bd0d5964d3..a2f18dc31378b6f98b57e6666f81a6b74d343164 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/MainModelPage.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/MainModelPage.java @@ -14,8 +14,6 @@ import java.util.Enumeration; import java.util.List; import java.util.Vector; -import javax.mail.internet.AddressException; - import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; @@ -25,6 +23,7 @@ import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; @@ -220,7 +219,7 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta // 2^((Year-1993)/ 2)+2) // (1993 as base results from a statistic of windows OS memory requirements) final int currentYear = Calendar.getInstance().get(Calendar.YEAR); - double estimateSoftwareBloatInMBytes = Math.pow(2, ((currentYear - 1993) / 2) + 2); + double estimateSoftwareBloatInMBytes = Math.pow(2, ((currentYear - 1993) / 3) + 3.5); // 2.) Optimal range x[s] = lowerLimit * 2d; @@ -380,7 +379,8 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta getLookupHelper().resetModelNames(this); // constants in the table - List<Assignment> constants = (List<Assignment>) constantTable.getInput(); + @SuppressWarnings("unchecked") + List<Assignment> constants = (List<Assignment>) constantTable.getInput(); // merge constants with currently defined in the specobj, if any if (rootModuleNode != null) { @@ -433,6 +433,15 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta if (constant.isSymmetricalSet()) { + if (((CheckboxTableViewer) propertiesTable).getCheckedElements().length > 0) { + modelEditor.addErrorMessage(constant.getLabel(), constant.getLabel() + + " declared to be symmetric. Liveness checking under symmetry might fail to find a violation.", + this.getId(), IMessageProvider.WARNING, + UIHelper.getWidget(dm.getAttributeControl(MODEL_PARAMETER_CONSTANTS))); + expandSection(dm.getSectionForAttribute(MODEL_PARAMETER_CONSTANTS)); + expandSection(dm.getSectionForAttribute(MODEL_CORRECTNESS_PROPERTIES)); + } + boolean hasTwoTypes = false; // set true if this symmetry set has two differently-typed model // values. String typeString = null; // set to the type of the first typed model value in this symmetry @@ -1112,25 +1121,8 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta dm.bindAttribute(MODEL_CORRECTNESS_INVARIANTS, invariantsTable, invariantsPart); // Properties - - // The following code added by LL on 29 May 2010 to expand the Property section - // and reset the MODEL_PROPERTIES_EXPAND property to "" if that property has - // been set to a non-"" value. - int propFlags = sectionFlags; - try - { - if (!((String) getModel().getAttribute(MODEL_PROPERTIES_EXPAND, "")).equals("")) { - propFlags = propFlags | Section.EXPANDED; - getModel().setAttribute(MODEL_PROPERTIES_EXPAND, ""); - } - } catch (CoreException e) - { - // I don't know why such an exception might occur, but there's no - // great harm if it does. LL - e.printStackTrace(); - } ValidateableTableSectionPart propertiesPart = new ValidateableTableSectionPart(toBeCheckedArea, "Properties", - "Temporal formulas true for every possible behavior.", toolkit, propFlags, this, + "Temporal formulas true for every possible behavior.", toolkit, sectionFlags, this, SEC_WHAT_TO_CHECK_PROPERTIES); managedForm.addPart(propertiesPart); propertiesTable = propertiesPart.getTableViewer(); @@ -1578,7 +1570,7 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta distributedNodesCountSpinner.setMinimum(1); distributedNodesCountSpinner.setMaximum(64); // Haven't really tested this many distributed fpsets distributedNodesCountSpinner.setPageIncrement(1); - distributedNodesCountSpinner.setToolTipText( + distributedNodesCountSpinner.setToolTipText( "Determines how many compute nodes/VMs will be launched. More VMs means faster results and higher costs."); distributedNodesCountSpinner.setSelection(IConfigurationDefaults.LAUNCH_DISTRIBUTED_NODES_COUNT_DEFAULT); @@ -1590,14 +1582,17 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta final Composite resultAddress = new Composite(jcloudsOptions, SWT.NONE) ; layout = new GridLayout(2, true); resultAddress.setLayout(layout); + final String resultAddressTooltip = "A list (comma-separated) of one to N email addresses to send the model checking result to."; + resultAddress.setToolTipText(resultAddressTooltip); gd = new GridData(); gd.horizontalSpan = 2; resultAddress.setLayoutData(gd); - toolkit.createLabel(resultAddress, "Result mailto address:"); + toolkit.createLabel(resultAddress, "Result mailto addresses:"); resultMailAddressText = toolkit.createText(resultAddress, "", SWT.BORDER); - resultMailAddressText.setMessage("my-name@my-domain.org"); // hint + resultMailAddressText.setMessage("my-name@my-domain.org,alternative-name@alternative-domain.org"); // hint + resultMailAddressText.setToolTipText(resultAddressTooltip); resultMailAddressText.addKeyListener(new KeyAdapter() { private final ModelEditor modelEditor = (ModelEditor) getEditor(); @@ -1611,9 +1606,9 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta public void keyReleased(KeyEvent e) { super.keyReleased(e); try { - String text = resultMailAddressText.getText(); - new javax.mail.internet.InternetAddress(text, true); - } catch (AddressException exp) { + final String text = resultMailAddressText.getText(); + javax.mail.internet.InternetAddress.parse(text, true); + } catch (javax.mail.internet.AddressException exp) { modelEditor.addErrorMessage("emailAddressInvalid", "Invalid email address", getId(), IMessageProvider.ERROR, resultMailAddressText); @@ -1702,6 +1697,14 @@ public class MainModelPage extends BasicFormPage implements IConfigurationConsta distributedOptions.layout(); } + /** + * Expands the properties table. + */ + public void expandPropertiesSection() { + final SectionPart section = getDataBindingManager().getSection(SEC_WHAT_TO_CHECK_PROPERTIES); + section.getSection().setExpanded(true); + } + /** * On a refresh, the checkpoint information is re-read */ diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/ResultPage.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/ResultPage.java index 594622edf4e261c4636050e064fc500dd542a176..6c48bf3c8651fef488438bf7007c8ea4311e616a 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/ResultPage.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/ResultPage.java @@ -109,6 +109,7 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres // method. private long startTime = 0; private Text finishTimestampText; + private Text tlcModeText; private Text lastCheckpointTimeText; private Text coverageTimestampText; private Text currentStatusText; @@ -135,6 +136,7 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres }; private IMarker incompleteStateExploration; + private IMarker zeroCoverage; /** * Constructor for the page @@ -205,6 +207,8 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres ResultPage.this.finishTimestampText.setToolTipText(sdf.format(new Date(elapsedTime))); ResultPage.this.startTimestampText.setToolTipText(sdf.format(new Date(elapsedTime))); break; + case TLC_MODE: + ResultPage.this.tlcModeText.setText(dataProvider.getTLCMode()); case LAST_CHECKPOINT_TIME: long lastCheckpointTimeStamp = dataProvider.getLastCheckpointTimeStamp(); if(lastCheckpointTimeStamp > 0) { @@ -223,7 +227,26 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres ResultPage.this.coverageTimestampText.setText(dataProvider.getCoverageTimestamp()); break; case COVERAGE: - ResultPage.this.coverage.setInput(dataProvider.getCoverageInfo()); + final List<CoverageInformationItem> coverageInfo = dataProvider.getCoverageInfo(); + ResultPage.this.coverage.setInput(coverageInfo); + if (dataProvider.isDone() && coverageInfo.size() > 0) { + if (dataProvider.hasZeroCoverage()) { + if (zeroCoverage == null) { + final Hashtable<String, Object> marker = ModelHelper.createMarkerDescription( + "Coverage is zero for one or more modules.", IMarker.SEVERITY_WARNING); + marker.put(ModelHelper.TLC_MODEL_ERROR_MARKER_ATTRIBUTE_PAGE, 2); + zeroCoverage = getModel().setMarker(marker, ModelHelper.TLC_MODEL_ERROR_MARKER_TLC); + } + } else if (zeroCoverage != null) { + try { + zeroCoverage.delete(); + ResultPage.this.resetMessage(RESULT_PAGE_PROBLEM); + zeroCoverage = null; + } catch (CoreException e) { + TLCUIActivator.getDefault().logError(e.getMessage(), e); + } + } + } break; case PROGRESS: ResultPage.this.stateSpace.setInput(dataProvider.getProgressInformation()); @@ -357,6 +380,7 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres this.startTimestampText.setText(""); this.startTime = 0; this.finishTimestampText.setText(""); + this.tlcModeText.setText(""); this.lastCheckpointTimeText.setText(""); this.currentStatusText.setText(TLCModelLaunchDataProvider.NOT_RUNNING); this.errorStatusHyperLink.setText(TLCModelLaunchDataProvider.NO_ERRORS); @@ -392,6 +416,11 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres incompleteStateExploration.delete(); incompleteStateExploration = null; } + + if (zeroCoverage != null) { + zeroCoverage.delete(); + zeroCoverage = null; + } JFaceResources.getFontRegistry().removeListener(fontChangeListener); @@ -461,7 +490,10 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres // elapsed time this.finishTimestampText = FormHelper.createTextLeft("End time:", statusComposite, toolkit); this.finishTimestampText.setEditable(false); - // last checkpoint time + // elapsed time + this.tlcModeText = FormHelper.createTextLeft("TLC mode:", statusComposite, toolkit); + this.tlcModeText.setEditable(false); + // last checkpoint time this.lastCheckpointTimeText = FormHelper.createTextLeft("Last checkpoint time:", statusComposite, toolkit); this.lastCheckpointTimeText.setEditable(false); // current status @@ -966,7 +998,7 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres /** * Provides labels for the coverage table */ - static class CoverageLabelProvider extends LabelProvider implements ITableLabelProvider + static class CoverageLabelProvider extends LabelProvider implements ITableLabelProvider, ITableColorProvider { public final static String[] columnTitles = new String[] { "Module", "Location", "Count" }; public final static int[] columnWidths = { 80, 200, 80 }; @@ -1018,6 +1050,17 @@ public class ResultPage extends BasicFormPage implements ITLCModelLaunchDataPres return null; } + public Color getForeground(Object element, int columnIndex) { + return null; // Use default color + } + + public Color getBackground(Object element, int columnIndex) { + final CoverageInformationItem cii = (CoverageInformationItem) element; + if (cii.getCount() == 0) { + return TLCUIActivator.getColor(SWT.COLOR_YELLOW); + } + return null; + } } /** diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/part/ValidateableTableSectionPart.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/part/ValidateableTableSectionPart.java index 2eea33802a95329aeb870c8a0f72db9924f455e2..fd76feeb7578fa47a37e47fcff347351fbb98de7 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/part/ValidateableTableSectionPart.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/part/ValidateableTableSectionPart.java @@ -2,6 +2,7 @@ package org.lamport.tla.toolbox.tool.tlc.ui.editor.part; import java.util.Vector; +import org.eclipse.core.runtime.Platform; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.DoubleClickEvent; @@ -29,6 +30,7 @@ import org.eclipse.ui.forms.widgets.Section; import org.lamport.tla.toolbox.tool.tlc.model.Formula; import org.lamport.tla.toolbox.tool.tlc.ui.editor.page.BasicFormPage; import org.lamport.tla.toolbox.tool.tlc.ui.editor.provider.FormulaContentProvider; +import org.lamport.tla.toolbox.tool.tlc.ui.editor.provider.FormulaLabelProvider; import org.lamport.tla.toolbox.tool.tlc.ui.util.FormHelper; import org.lamport.tla.toolbox.tool.tlc.ui.wizard.FormulaWizard; @@ -170,6 +172,13 @@ public class ValidateableTableSectionPart extends SectionPart implements IValida { // create CheckboxTableViewer tableViewer = new CheckboxTableViewer(table); + if (Platform.WS_GTK.equals(Platform.getWS())) { + // Ideally, the invariants and properties viewer would show the formula with a + // fixed-width font (monospace). However, this has issues on Mac and Windows + // (does not handle multi-line formula). Hence, only use a monospaced font + // on GTK (Linux) where the behaviour is fine. + tableViewer.setLabelProvider(new FormulaLabelProvider()); + } // represent formulas in the view tableViewer.setContentProvider(new FormulaContentProvider()); // on changed selection change button enablement diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/provider/FormulaLabelProvider.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/provider/FormulaLabelProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..4b48f92d47917fa1bd76d691563781db0e2695c2 --- /dev/null +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/provider/FormulaLabelProvider.java @@ -0,0 +1,27 @@ +package org.lamport.tla.toolbox.tool.tlc.ui.editor.provider; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ITableFontProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; + +public class FormulaLabelProvider extends LabelProvider implements ITableLabelProvider, ITableFontProvider { + + @Override + public Font getFont(Object element, int columnIndex) { + // Regardless of the element and its position, we want to use a monospaced font. + return JFaceResources.getFont(JFaceResources.TEXT_FONT); + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + return this.getImage(element); + } + + @Override + public String getColumnText(Object element, int columnIndex) { + return this.getText(element); + } +} diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelContentProvider.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelContentProvider.java index b57be03e436536c3bd7652d09d0897a2c330eff3..7bb15da476d43889877f245ee1ba94c6a6a2869d 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelContentProvider.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelContentProvider.java @@ -30,12 +30,17 @@ import java.util.HashMap; import java.util.Map; import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.ui.navigator.CommonViewer; import org.lamport.tla.toolbox.spec.Spec; import org.lamport.tla.toolbox.tool.ToolboxHandle; import org.lamport.tla.toolbox.tool.tlc.model.Model; +import org.lamport.tla.toolbox.tool.tlc.model.Model.StateChangeListener; +import org.lamport.tla.toolbox.tool.tlc.model.Model.StateChangeListener.ChangeEvent.State; import org.lamport.tla.toolbox.tool.tlc.model.TLCSpec; import org.lamport.tla.toolbox.ui.provider.IGroup; +import org.lamport.tla.toolbox.util.UIHelper; /** * Provides information about TLC models (launch configurations) in the current @@ -47,24 +52,56 @@ public class ModelContentProvider implements ITreeContentProvider { private static final Object[] EMPTY_ARRAY = new Object[0]; private final Map<Model, Group> reverse = new HashMap<Model, Group>(); + private final MyStateChangeListener modelChangeListener = new MyStateChangeListener() { + @Override + public boolean handleChange(final ChangeEvent event) { + if (event.getState() == State.DELETED) { + UIHelper.runUISync(new Runnable() { + @Override + public void run() { + final Model snapshot = event.getModel(); + final Model parent = snapshot.getSnapshotFor(); + // The CommonViewer is stupid in that it accesses an element (snapshot) even + // after it has been removed in order to update the viewer's current selection. + // Since we have to prevent this access to avoid a null pointer, we explicitly + // reset the selection. + getViewer().setSelection(new StructuredSelection(parent)); + // ...still remove the element from the tree. + getViewer().remove(parent, new Object[] {snapshot}); + } + }); + } + return true; + } + }; public Object[] getChildren(final Object parentElement) { if (parentElement instanceof Spec) { final Spec currentSpec = (Spec) parentElement; // only get models of the current spec if (ToolboxHandle.getCurrentSpec() == parentElement) { - final Collection<Model> models = currentSpec.getAdapter(TLCSpec.class).getModels().values(); + final Collection<Model> models = currentSpec.getAdapter(TLCSpec.class).getModels(".*_SnapShot_[0-9]*$", false).values(); return new Group[] {new Group((Spec) parentElement, models.toArray(new Model[models.size()]))}; } } else if (parentElement instanceof Group) { return ((Group) parentElement).getModels(); + } else if (parentElement instanceof Model) { + final Collection<Model> snapshots = ((Model) parentElement).getSnapshots(); + for (final Model model : snapshots) { + model.add(modelChangeListener); + } + return snapshots.toArray(new Model[snapshots.size()]); } return EMPTY_ARRAY; } public Object getParent(final Object element) { if (element instanceof Model) { - return ((Model) element).getSpec(); + final Model model = (Model) element; + if (model.isSnapshot()) { + return model.getSnapshotFor(); + } + return model.getSpec(); } return null; } @@ -72,6 +109,8 @@ public class ModelContentProvider implements ITreeContentProvider { public boolean hasChildren(final Object element) { if (element instanceof Group) { return ((Group) element).getModels().length > 0; + } else if (element instanceof Model) { + return ((Model) element).hasSnapshots(); } /* * Models are shown for the current spec only @@ -88,7 +127,20 @@ public class ModelContentProvider implements ITreeContentProvider { } public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { - // nothing to do + this.modelChangeListener.setViewer((CommonViewer) viewer); + } + + private static class MyStateChangeListener extends StateChangeListener { + + private CommonViewer viewer; + + public void setViewer(CommonViewer viewer) { + this.viewer = viewer; + } + + public CommonViewer getViewer() { + return viewer; + } } public static final class Group implements IGroup { diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelLabelProvider.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelLabelProvider.java index f4118074c7b1e3e5b550855461b6275aef105913..8d185b2cbe1a24bbb19b51940f3683a2335e6500 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelLabelProvider.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/modelexplorer/ModelLabelProvider.java @@ -25,6 +25,8 @@ ******************************************************************************/ package org.lamport.tla.toolbox.tool.tlc.ui.modelexplorer; +import java.text.SimpleDateFormat; + import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.navigator.IDescriptionProvider; @@ -36,14 +38,28 @@ import org.lamport.tla.toolbox.tool.tlc.ui.modelexplorer.ModelContentProvider.Gr * Provides labels for the TLC models */ public class ModelLabelProvider extends LabelProvider implements IDescriptionProvider { - private Image image = TLCUIActivator.getImageDescriptor("/icons/full/choice_sc_obj.gif").createImage(); + private static final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd,yyyy HH:mm:ss"); + private Image modelImage = TLCUIActivator.getImageDescriptor("/icons/full/choice_sc_obj.gif").createImage(); + private Image modelNoError = TLCUIActivator.getImageDescriptor("/icons/full/model_no_error.gif").createImage(); + private Image modelWithError = TLCUIActivator.getImageDescriptor("/icons/full/model_with_error.gif").createImage(); + /** * Retrieves model's image */ public Image getImage(final Object element) { - if (element instanceof Model || element instanceof Group) { - return image; + if (element instanceof Group) { + return modelImage; + } else if (element instanceof Model) { + final Model model = (Model) element; + if (model.isSnapshot()) { + if (model.hasError()) { + return modelWithError; + } + return modelNoError; + } else { + return modelImage; + } } return super.getImage(element); } @@ -54,6 +70,9 @@ public class ModelLabelProvider extends LabelProvider implements IDescriptionPro public String getText(final Object element) { if (element instanceof Model) { final Model model = (Model) element; + if (model.isSnapshot()) { + return sdf.format(model.getSnapshotTimeStamp()); + } final String modelName = model.getName(); if (model.isStale()) { return modelName + " [ crashed ]"; @@ -87,10 +106,18 @@ public class ModelLabelProvider extends LabelProvider implements IDescriptionPro * Dispose the image */ public void dispose() { - if (image != null) { - image.dispose(); + if (modelImage != null) { + modelImage.dispose(); + } + modelImage = null; + if (modelNoError != null) { + modelNoError.dispose(); + } + modelNoError = null; + if (modelWithError != null) { + modelWithError.dispose(); } - image = null; + modelWithError = null; super.dispose(); } } diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferenceInitializer.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferenceInitializer.java index 8e4aef0ec643bd4663acd1f870176e9247d69930..e3e512a92b4ac995f20c645463ecb46a2cbdb776 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferenceInitializer.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferenceInitializer.java @@ -2,6 +2,7 @@ package org.lamport.tla.toolbox.tool.tlc.ui.preference; import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; import org.eclipse.jface.preference.IPreferenceStore; +import org.lamport.tla.toolbox.tool.tlc.TLCActivator; import org.lamport.tla.toolbox.tool.tlc.launch.IModelConfigurationDefaults; import org.lamport.tla.toolbox.tool.tlc.ui.TLCUIActivator; @@ -26,6 +27,7 @@ public class TLCPreferenceInitializer extends AbstractPreferenceInitializer store.setDefault(ITLCPreferenceConstants.I_TLC_TRACE_MAX_SHOW_ERRORS, 10000); store.setDefault(ITLCPreferenceConstants.I_TLC_POPUP_ERRORS, true); store.setDefault(ITLCPreferenceConstants.I_TLC_REVALIDATE_ON_MODIFY, true); + store.setDefault(TLCActivator.I_TLC_SNAPSHOT_PREFERENCE, true); store.setDefault(ITLCPreferenceConstants.I_TLC_MAXIMUM_HEAP_SIZE_DEFAULT, MAX_HEAP_SIZE_DEFAULT); store.setDefault(ITLCPreferenceConstants.I_TLC_MAXSETSIZE_DEFAULT, TLCGlobals.setBound); store.setDefault(ITLCPreferenceConstants.I_TLC_FPBITS_DEFAULT, 1); diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferencePage.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferencePage.java index 5c93ea3b899e7fedf9d0c8670bb8639b6a303de8..99d34cfec051d572a5602910f1e3261ae84b9da3 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferencePage.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/preference/TLCPreferencePage.java @@ -2,11 +2,15 @@ package org.lamport.tla.toolbox.tool.tlc.ui.preference; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; +import org.lamport.tla.toolbox.tool.tlc.TLCActivator; import org.lamport.tla.toolbox.tool.tlc.ui.TLCUIActivator; import org.lamport.tla.toolbox.util.IHelpConstants; import org.lamport.tla.toolbox.util.UIHelper; @@ -24,7 +28,17 @@ public class TLCPreferencePage extends FieldEditorPreferencePage implements IWor public TLCPreferencePage() { super(GRID); - setPreferenceStore(TLCUIActivator.getDefault().getPreferenceStore()); + // Copy preference value to non-ui plugin. + TLCUIActivator.getDefault().getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + final IPreferenceStore store = TLCActivator.getDefault().getPreferenceStore(); + if (TLCActivator.I_TLC_SNAPSHOT_PREFERENCE.equals(event.getProperty())) { + store.setValue(TLCActivator.I_TLC_SNAPSHOT_PREFERENCE, (boolean) event.getNewValue()); + } + } + }); + setPreferenceStore(TLCUIActivator.getDefault().getPreferenceStore()); setDescription("TLC Model Checker preferences"); } @@ -45,6 +59,8 @@ public class TLCPreferencePage extends FieldEditorPreferencePage implements IWor addField(new BooleanFieldEditor(ITLCPreferenceConstants.I_TLC_REVALIDATE_ON_MODIFY, "&Re-validate model on save", getFieldEditorParent())); + addField(new BooleanFieldEditor(TLCActivator.I_TLC_SNAPSHOT_PREFERENCE, + "Take &snapshot of model after completion of model checking", getFieldEditorParent())); // addField(new BooleanFieldEditor(ITLCPreferenceConstants.I_TLC_DELETE_PREVIOUS_FILES, // "&Automatically delete unused data from previous model run", getFieldEditorParent())); addField(new IntegerFieldEditor(ITLCPreferenceConstants.I_TLC_MAXIMUM_HEAP_SIZE_DEFAULT, diff --git a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java index 91af61d3a973439325d07c69fc942d6f10c1675b..1cc678e03ae5f7aa28dea45b73263e694e2a8327 100644 --- a/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java +++ b/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java @@ -1,5 +1,6 @@ package org.lamport.tla.toolbox.tool.tlc.ui.view; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -97,7 +98,8 @@ import tlc2.output.MP; */ public class TLCErrorView extends ViewPart { - + private static final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd,yyyy HH:mm:ss"); + private static final String INNER_WEIGHTS_KEY = "INNER_WEIGHTS_KEY"; private static final String OUTER_WEIGHTS_KEY = "OUTER_WEIGHTS_KEY"; @@ -162,7 +164,7 @@ public class TLCErrorView extends ViewPart * @param problems * a list of {@link TLCError} objects representing the errors. */ - protected void fill(String modelName, List<TLCError> problems, final List<String> serializedInput) + protected void fill(Model model, List<TLCError> problems, final List<String> serializedInput) { /* * Fill the trace explorer expression table with expressions saved in @@ -225,7 +227,12 @@ public class TLCErrorView extends ViewPart this.setTraceInput(trace); traceExplorerComposite.changeExploreEnablement(true); } - this.form.setText(modelName); + if (model.isSnapshot()) { + final String date = sdf.format(model.getSnapshotTimeStamp()); + this.form.setText(model.getSnapshotFor().getName() + " (" + date + ")"); + } else { + this.form.setText(model.getName()); + } } else { @@ -664,7 +671,6 @@ public class TLCErrorView extends ViewPart * a list of {@link TLCError} */ public static void updateErrorView(Model model) { - System.out.println(model); updateErrorView(model, true); } @@ -706,7 +712,7 @@ public class TLCErrorView extends ViewPart final List<String> serializedInput = model.getTraceExplorerExpressions(); // fill the name and the errors - errorView.fill(provider.getModel().getName(), provider.getErrors(), + errorView.fill(provider.getModel(), provider.getErrors(), serializedInput); if (provider.getErrors().size() == 0) { @@ -1169,13 +1175,16 @@ public class TLCErrorView extends ViewPart return TLCUIActivator.getDefault().getChangedColor(); } } else if (value.isAdded()) { + // Added takes precedence over deleted. E.g. a value can be + // added to a set in this state and be removed in the next + // state. return TLCUIActivator.getDefault().getAddedColor(); } else if (value.isDeleted()) { return TLCUIActivator.getDefault().getDeletedColor(); } } else if (coloring && element instanceof TLCState) { // Assign a color to each location to make actions in the error - // viewer easier distinguishable. + // viewer more easily distinguishable. final TLCState state = (TLCState) element; Location moduleLocation = state.getModuleLocation(); if (moduleLocation == null) { @@ -1329,4 +1338,8 @@ public class TLCErrorView extends ViewPart public void setOriginalTraceShown(boolean b) { this.model.setOriginalTraceShown(b); } + + TreeViewer getViewer() { + return variableViewer; + } } diff --git a/org.lamport.tla.toolbox.tool.tlc/.classpath b/org.lamport.tla.toolbox.tool.tlc/.classpath index 64c5e31b7a264082f4c1dfdabb8097de820e66ce..098194ca4b7d8f45177f94e735506ae3a26b5c94 100644 --- a/org.lamport.tla.toolbox.tool.tlc/.classpath +++ b/org.lamport.tla.toolbox.tool.tlc/.classpath @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="src" path="src"/> <classpathentry kind="output" path="bin"/> diff --git a/org.lamport.tla.toolbox.tool.tlc/.settings/org.eclipse.jdt.core.prefs b/org.lamport.tla.toolbox.tool.tlc/.settings/org.eclipse.jdt.core.prefs index d0ff54b48624d5a1350faae4e9430e814fa3f7be..f42de363afaae68bbd968318f1d331877f5514fc 100644 --- a/org.lamport.tla.toolbox.tool.tlc/.settings/org.eclipse.jdt.core.prefs +++ b/org.lamport.tla.toolbox.tool.tlc/.settings/org.eclipse.jdt.core.prefs @@ -1,8 +1,7 @@ -#Thu Jul 07 10:25:34 CEST 2011 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF b/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF index ee1f9989da0baceb08f815b0d7fc78616dfe026a..0340f59fc66badf0b5ef43fac6bd685e264bac25 100644 --- a/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF +++ b/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF @@ -13,10 +13,12 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.expressions;bundle-version="3.4.100", org.eclipse.ui.editors;bundle-version="3.5.0", org.eclipse.jface.text;bundle-version="3.5.0", - org.lamport.tla.toolbox.product.standalone -Bundle-RequiredExecutionEnvironment: J2SE-1.5 + org.lamport.tla.toolbox.product.standalone, + org.apache.commons.io;bundle-version="2.0.1" +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy -Export-Package: org.lamport.tla.toolbox.tool.tlc.job, +Export-Package: org.lamport.tla.toolbox.tool.tlc, + org.lamport.tla.toolbox.tool.tlc.job, org.lamport.tla.toolbox.tool.tlc.launch, org.lamport.tla.toolbox.tool.tlc.model, org.lamport.tla.toolbox.tool.tlc.output, diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCActivator.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCActivator.java index a5ad0e7e329635f1751458e0efca858b20a70b72..81011a59786b973f60dfc015464b94ee52d9a6f4 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCActivator.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/TLCActivator.java @@ -8,7 +8,12 @@ import org.osgi.framework.BundleContext; * The activator class controls the plug-in life cycle */ public class TLCActivator extends AbstractUIPlugin { - + + /** + * Take a model snapshot when model checking finishes to create a history of model runs. + */ + public static final String I_TLC_SNAPSHOT_PREFERENCE = "takeModelSnapshot"; + // The plug-in ID public static final String PLUGIN_ID = "org.lamport.tla.toolbox.tool.tlc"; @@ -29,6 +34,7 @@ public class TLCActivator extends AbstractUIPlugin { public void start(BundleContext context) throws Exception { super.start(context); plugin = this; + plugin.getPreferenceStore().setDefault(I_TLC_SNAPSHOT_PREFERENCE, true); } /* diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java index 4276011cb73b84d113cad3da9b0674a433d96c95..232602e963e4fd26b8c7cd3099cef70dbcdf1471 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java @@ -196,6 +196,13 @@ public abstract class TLCJob extends AbstractJob implements IModelConfigurationC } } + // Defer liveness checking + final boolean deferLiveness = launch.getLaunchConfiguration().getAttribute(LAUNCH_DEFER_LIVENESS, false); + if (deferLiveness) { + arguments.add("-lncheck"); + arguments.add("final"); + } + // fpBits int fpBits = launch.getLaunchConfiguration().getAttribute(LAUNCH_FPBITS, -1); if(fpBits >= 0) { @@ -217,6 +224,16 @@ public abstract class TLCJob extends AbstractJob implements IModelConfigurationC arguments.add(String.valueOf(maxSetSize)); } + // Visualize state graph + final boolean visualizeStateGraph = launch.getLaunchConfiguration().getAttribute(LAUNCH_VISUALIZE_STATEGRAPH, false); + if (visualizeStateGraph && hasSpec) { + // Visualize state graph when requested and a behavior spec is given. A behavior + // spec is required for TLC to create states. + arguments.add("-dump"); + arguments.add("dot"); + arguments.add(modelName); + } + arguments.add("-config"); arguments.add(cfgFile.getName()); // configuration file diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCProcessJob.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCProcessJob.java index 6f53bdbddfc3004d5bb6f4808502519a9dde0ed5..93a04490e534d84b530dcdc67bb02a3841904ec8 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCProcessJob.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCProcessJob.java @@ -31,6 +31,7 @@ import org.lamport.tla.toolbox.util.ResourceHelper; import tlc2.TLC; import tlc2.tool.fp.FPSetFactory; +import tlc2.tool.fp.OffHeapDiskFPSet; import util.TLCRuntime; /** @@ -101,13 +102,38 @@ public class TLCProcessJob extends TLCJob // get max heap size as fraction from model editor final double maxHeapSize = launch.getLaunchConfiguration().getAttribute(LAUNCH_MAX_HEAP_SIZE, 50) / 100d; final TLCRuntime instance = TLCRuntime.getInstance(); - final long absolutePhysicalSystemMemory = instance.getAbsolutePhysicalSystemMemory(maxHeapSize); - - // Set class name of FPSet (added by MAK on 07/31/2012) - final String clazz = launch.getLaunchConfiguration().getAttribute(LAUNCH_FPSET_IMPL, - FPSetFactory.getImplementationDefault()); - vmArgs.add("-D" + FPSetFactory.IMPL_PROPERTY + "=" + clazz); - vmArgs.add(FPSetFactory.getVMArguments(clazz, absolutePhysicalSystemMemory)); + long absolutePhysicalSystemMemory = instance.getAbsolutePhysicalSystemMemory(maxHeapSize); + + // Get class name of the user selected FPSet. If it is unset, try and load the + // OffHeapDiskFPSet (which might not be supported). In unsupported, revert to + // TLC's default set. + // If the user happened to select OffHeapDiskFPSet, the -XX:MaxDirectMemorySize + // is set by getVMArguments. + final String clazz = launch.getLaunchConfiguration().getAttribute(LAUNCH_FPSET_IMPL, (String) null); + if (clazz == null || clazz.equals(OffHeapDiskFPSet.class.getName())) { + if (OffHeapDiskFPSet.isSupported()) { + // ...good, can use lock-free/scalabe fpset, but need to divide assigned memory + // into JVM heap and non-heap/off-heap memory. Let's assign 2/3 to the off-heap + // FPSet. + final long offheapMemory = (long) (absolutePhysicalSystemMemory * 0.6666d); + vmArgs.add(FPSetFactory.getVMArguments(OffHeapDiskFPSet.class.getName(), offheapMemory)); + + // Rest goes to the heap (state queue/...). + vmArgs.add(FPSetFactory.getVMArguments(FPSetFactory.getImplementationDefault(), + absolutePhysicalSystemMemory - offheapMemory)); + + vmArgs.add("-D" + FPSetFactory.IMPL_PROPERTY + "=" + OffHeapDiskFPSet.class.getName()); + } else { + // Off-heap fpset couldn't be loaded. Use default fpset and assign all memory to + // heap memory. + vmArgs.add(FPSetFactory.getVMArguments(FPSetFactory.getImplementationDefault(), + absolutePhysicalSystemMemory)); + vmArgs.add("-D" + FPSetFactory.IMPL_PROPERTY + "=" + FPSetFactory.getImplementationDefault()); + } + } else { + vmArgs.add(FPSetFactory.getVMArguments(clazz, absolutePhysicalSystemMemory)); + vmArgs.add("-D" + FPSetFactory.IMPL_PROPERTY + "=" + clazz); + } // add remaining VM args vmArgs.addAll(getAdditionalVMArgs()); @@ -285,6 +311,17 @@ public class TLCProcessJob extends TLCJob protected List<String> getAdditionalVMArgs() throws CoreException { final List<String> result = new ArrayList<String>(0); + /* + * Allow access to the java.activation module on Java9 + * https://bugs.eclipse.org/493761 + * http://download.java.net/java/jdk9/docs/api/java.activation-summary.html + * + * This is needed by MailSender.java. + */ + result.add("--add-modules=java.activation"); + // Have < Java9 ignore --add-modules=java.activation flag + result.add("-XX:+IgnoreUnrecognizedVMOptions"); + // Library Path final Spec spec = Activator.getSpecManager().getSpecByName(specName); final String tlaLibraryPathAsVMArg = spec.getTLALibraryPathAsVMArg(); diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java index 0090fad7b5c03f41843f3677a6c5697b238b5527..7302a70b2caf3e049d8350b6448db6ad056be6ed 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java @@ -90,6 +90,15 @@ public interface IConfigurationConstants */ public static final String LAUNCH_FP_INDEX = "fpIndex"; + /** + * Defers verification of liveness properties upon the final stage of model + * checking (upon termination). + */ + public static final String LAUNCH_DEFER_LIVENESS = "deferLiveness"; + /** + * Visualize state graph after model checking with GraphViz. + */ + public static final String LAUNCH_VISUALIZE_STATEGRAPH = "visualizeStateGraph"; /** * Run from the checkpoint */ diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java index 53832731ef1d7d24ea5127b4bee70d5a6e7b5566..2b1697fcb8a5aa45758d6b02ea840f4422e3ea3c 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java @@ -93,7 +93,14 @@ public interface IConfigurationDefaults * Default fp seed is 1 meaning the first elem in the list */ public static final int LAUNCH_FP_INDEX_DEFAULT = 1; - + /** + * Default fp seed is 1 meaning the first elem in the list + */ + public static final boolean LAUNCH_DEFER_LIVENESS_DEFAULT = false; + /** + * Do not visualize state graph by default. Most state graphs are too large. + */ + public static final boolean LAUNCH_VISUALIZE_STATEGRAPH_DEFAULT = false; /** * Do not recover from checkpoints by defualt */ diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java index db1493dfc1f3b0cd2d5582f802a3109bbd56d2e3..ea23d94cba836a527d9647ee91b97f66f8f0633f 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java @@ -13,8 +13,6 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.Assert; @@ -30,7 +28,6 @@ import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; -import org.eclipse.core.runtime.jobs.MultiRule; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; @@ -89,10 +86,6 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen // Mutex rule for the following jobs to run after each other protected MutexRule mutexRule = new MutexRule(); - protected String specName = null; - protected String modelName = null; - protected String specRootFilename = null; - /** * Configuration type */ @@ -148,7 +141,7 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen IStatus.ERROR, TLCActivator.PLUGIN_ID, "The running attribute for " - + modelName + + model.getName() + " has been set to true or that model is locked. " + "Another TLC is possible running on the same model, has been terminated non-gracefully " + "or you tried to run TLC on a locked model. Running TLC on a locked model is not possible.")); @@ -158,18 +151,6 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen try { monitor.beginTask("Reading model parameters", 1); - - // name of the specification - specName = config.getAttribute(SPEC_NAME, EMPTY_STRING); - - // model name - modelName = config.getAttribute(MODEL_NAME, EMPTY_STRING); - - // root file name - specRootFilename = ToolboxHandle.getRootModule(config.getFile().getProject()).getLocation().toOSString(); - // specRootFilename = config.getAttribute(SPEC_ROOT_FILE, - // EMPTY_STRING); - } finally { // finish the monitor @@ -205,25 +186,25 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen monitor.subTask("Creating directories"); // retrieve the project containing the specification - IProject project = ResourceHelper.getProject(specName); + final IProject project = model.getSpec().getProject(); if (project == null) { // project could not be found throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, - "Error accessing the spec project " + specName)); + "Error accessing the spec project " + model.getSpec().getName())); } // retrieve the root file - IFile specRootFile = ResourceHelper.getLinkedFile(project, specRootFilename, false); + final IFile specRootFile = model.getSpec().getRootFile(); if (specRootFile == null) { // root module file not found throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, - "Error accessing the root module " + specRootFilename)); + "Error accessing the root module " + model.getSpec().getRootFilename())); } // retrieve the model folder - IFolder modelFolder = project.getFolder(modelName); + final IFolder modelFolder = model.getFolder(); IPath targetFolderPath = modelFolder.getProjectRelativePath().addTrailingSeparator(); // create the handles: MC.tla, MC.cfg and MC.out @@ -247,55 +228,46 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen final boolean recover = config.getAttribute(LAUNCH_RECOVER, LAUNCH_RECOVER_DEFAULT); final IResource[] checkpoints = config.getAdapter(Model.class).getCheckpoints(false); - ISchedulingRule deleteRule = ResourceHelper.getDeleteRule(members); - // delete files - ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { - - public void run(IProgressMonitor monitor) throws CoreException + boolean checkFiles = recover; + monitor.beginTask("Deleting files", members.length); + // delete the members of the target + // directory + for (int i = 0; i < members.length; i++) + { + if (checkFiles) { - boolean checkFiles = recover; - monitor.beginTask("Deleting files", members.length); - // delete the members of the target - // directory - for (int i = 0; i < members.length; i++) + if (checkpoints.length > 0 && checkpoints[0].equals(members[i])) { - if (checkFiles) - { - if (checkpoints.length > 0 && checkpoints[0].equals(members[i])) - { - // we found the recovery - // directory and didn't delete - // it - checkFiles = false; - continue; - } - } else + // we found the recovery + // directory and didn't delete + // it + checkFiles = false; + continue; + } + } else + { + // delete file + // either non-recovery mode + // or the recovery directory already + // skipped + try + { + if (!members[i].getName().equals(ModelHelper.TE_TRACE_SOURCE)) { - // delete file - // either non-recovery mode - // or the recovery directory already - // skipped - try - { - if (!members[i].getName().equals(ModelHelper.TE_TRACE_SOURCE)) - { - members[i].delete(IResource.FORCE, new SubProgressMonitor(monitor, 1)); - } - } catch (CoreException e) - { - // catch the exception if - // deletion failed, and just - // ignore this fact - // FIXME this should be fixed at - // some later point in time - TLCActivator.logError("Error deleting a file " + members[i].getLocation(), e); - } + members[i].delete(IResource.FORCE, new SubProgressMonitor(monitor, 1)); } + } catch (CoreException e) + { + // catch the exception if + // deletion failed, and just + // ignore this fact + // FIXME this should be fixed at + // some later point in time + TLCActivator.logError("Error deleting a file " + members[i].getLocation(), e); } - monitor.done(); } - }, deleteRule, IWorkspace.AVOID_UPDATE, new SubProgressMonitor(monitor, STEP)); + } } } else { @@ -316,7 +288,7 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen if (specRootFileCopy == null) { throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, "Error copying " - + specRootFilename + " into " + targetFolderPath.toOSString())); + + model.getSpec().getRootFilename() + " into " + targetFolderPath.toOSString())); } // Copy the spec's root file userModule override if any. @@ -370,29 +342,16 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen } } - // get the scheduling rule - ISchedulingRule fileRule = MultiRule.combine(ResourceHelper.getModifyRule(files), ResourceHelper - .getCreateRule(files)); - // create files - ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { - public void run(IProgressMonitor monitor) throws CoreException - { - for (int i = 0; i < files.length; i++) - { - if (files[i].exists()) - { - files[i].setContents(new ByteArrayInputStream("".getBytes()), IResource.DERIVED - | IResource.FORCE, new SubProgressMonitor(monitor, 1)); - } else - { - files[i].create(new ByteArrayInputStream("".getBytes()), IResource.DERIVED - | IResource.FORCE, new SubProgressMonitor(monitor, 1)); - } - } - } - - }, fileRule, IWorkspace.AVOID_UPDATE, new SubProgressMonitor(monitor, STEP)); + for (int i = 0; i < files.length; i++) { + if (files[i].exists()) { + files[i].setContents(new ByteArrayInputStream("".getBytes()), IResource.DERIVED | IResource.FORCE, + new SubProgressMonitor(monitor, 1)); + } else { + files[i].create(new ByteArrayInputStream("".getBytes()), IResource.DERIVED | IResource.FORCE, + new SubProgressMonitor(monitor, 1)); + } + } monitor.worked(STEP); monitor.subTask("Creating contents"); @@ -400,7 +359,7 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen ModelWriter writer = new ModelWriter(); // add the MODULE beginning and EXTENDS statement - writer.addPrimer(ModelHelper.MC_MODEL_NAME, ResourceHelper.getModuleName(specRootFilename)); + writer.addPrimer(ModelHelper.MC_MODEL_NAME, model.getSpec().getRootModuleName()); // Sets constants to a Vector of the substitutions for the CONSTANT substitutions List constants = ModelHelper.deserializeAssignmentList(config.getAttribute(MODEL_PARAMETER_CONSTANTS, @@ -578,10 +537,7 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen monitor.beginTask("Verifying model files", 4); final Model model = configuration.getAdapter(Model.class); - - IProject project = ResourceHelper.getProject(specName); - IFolder launchDir = project.getFolder(modelName); - IFile rootModule = launchDir.getFile(ModelHelper.FILE_TLA); + final IFile rootModule = model.getTLAFile(); monitor.worked(1); // parse the MC file @@ -694,22 +650,23 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, "Unsupported launch mode " + mode)); } + final Model model = config.getAdapter(Model.class); // retrieve the project containing the specification - IProject project = ResourceHelper.getProject(specName); + final IProject project = model.getSpec().getProject(); if (project == null) { // project could not be found throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, - "Error accessing the spec project " + specName)); + "Error accessing the spec project " + model.getSpec().getName())); } // setup the running flag // from this point any termination of the run must reset the // flag - config.getAdapter(Model.class).setRunning(true); + model.setRunning(true); // set the model to have the original trace shown - config.getAdapter(Model.class).setOriginalTraceShown(true); + model.setOriginalTraceShown(true); // number of workers int numberOfWorkers = config.getAttribute(LAUNCH_NUMBER_OF_WORKERS, LAUNCH_NUMBER_OF_WORKERS_DEFAULT); @@ -734,17 +691,17 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen // TLC job Job job = null; if("off".equalsIgnoreCase(cloud)) { - job = new TLCProcessJob(specName, modelName, launch, numberOfWorkers); + job = new TLCProcessJob(model.getSpec().getName(), model.getName(), launch, numberOfWorkers); // The TLC job itself does not do any file IO job.setRule(mutexRule); } else { if ("ad hoc".equalsIgnoreCase(cloud)) { - job = new DistributedTLCJob(specName, modelName, launch, numberOfWorkers); + job = new DistributedTLCJob(model.getSpec().getName(), model.getName(), launch, numberOfWorkers); job.setRule(mutexRule); } else { numberOfWorkers = config.getAttribute(LAUNCH_DISTRIBUTED_NODES_COUNT, LAUNCH_DISTRIBUTED_NODES_COUNT_DEFAULT); //final IProject iproject = ResourceHelper.getProject(specName); - final IFolder launchDir = project.getFolder(modelName); + final IFolder launchDir = model.getFolder(); final File file = launchDir.getRawLocation().makeAbsolute().toFile(); final BundleContext bundleContext = FrameworkUtil.getBundle( @@ -764,7 +721,6 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen props.put(TLCJobFactory.MAIN_CLASS, tlc2.TLC.class.getName()); // Add model and spec name to properties to make the model // checker run easily identifiable in the result email. - final Model model = config.getAdapter(Model.class); props.put(TLCJobFactory.MODEL_NAME, model.getName()); props.put(TLCJobFactory.SPEC_NAME, model.getSpec().getName()); if (numberOfWorkers > 1) { @@ -989,14 +945,14 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen * for refreshing the model folder, and schedule that wrapping job * to be run later. */ - IProject project = ResourceHelper.getProject(specName); - IFolder modelFolder = project.getFolder(modelName); + final IFolder modelFolder = model.getFolder(); WorkspaceJob refreshJob = new WorkspaceJob("") { public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { model.getCheckpoints(true); - return Status.OK_STATUS; + + return Status.OK_STATUS; } }; refreshJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().refreshRule(modelFolder)); @@ -1026,7 +982,29 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate implemen } } model.setRunning(false); - } + + /* + * Lastly, take a snapshot of the finished spec. This provides a history of model checker runs. + * Technically, snapshots are just regular models. However, their name has a specially crafted + * suffix to identify it as a snapshot. The model for which it is a snapshot is identified by the + * prefix of the snapshot's model name. + */ + final boolean takeSnapshot = TLCActivator.getDefault().getPreferenceStore() + .getBoolean(TLCActivator.I_TLC_SNAPSHOT_PREFERENCE); + if (!takeSnapshot || model.isSnapshot()) { + return; + } + refreshJob = new WorkspaceJob("Taking snapshot of " + model.getName() + "...") { + public IStatus runInWorkspace(final IProgressMonitor monitor) throws CoreException { + monitor.beginTask("Taking snapshot of " + model.getName() + "...", 1); + model.snapshot(); + monitor.done(); + return Status.OK_STATUS; + } + }; + refreshJob.setRule(model.getSpec().getProject()); + refreshJob.schedule(); + } } /** diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java index a355826768aa070d7ca77542d4d21c94e9d65710..bd84e66712f9228a2576cb30877fb108a9793cbe 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java @@ -60,6 +60,7 @@ import org.lamport.tla.toolbox.tool.tlc.job.TraceExplorerJob; import org.lamport.tla.toolbox.tool.tlc.model.Assignment; import org.lamport.tla.toolbox.tool.tlc.model.Model; import org.lamport.tla.toolbox.tool.tlc.model.ModelWriter; +import org.lamport.tla.toolbox.tool.tlc.model.TLCSpec; import org.lamport.tla.toolbox.tool.tlc.model.TypedSet; import org.lamport.tla.toolbox.tool.tlc.traceexplorer.SimpleTLCState; import org.lamport.tla.toolbox.tool.tlc.util.ModelHelper; @@ -370,12 +371,12 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa monitor.subTask("Copying files"); // retrieve the root file - IFile specRootFile = ResourceHelper.getLinkedFile(project, specRootFilename, false); + final IFile specRootFile = model.getSpec().getRootFile(); if (specRootFile == null) { // root module file not found throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, - "Error accessing the root module " + specRootFilename)); + "Error accessing the root module " + model.getSpec().getRootFilename())); } // copy @@ -388,7 +389,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa if (specRootFileCopy == null) { throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, "Error copying " - + specRootFilename + " into " + targetFolderPath.toOSString())); + + model.getSpec().getRootFilename() + " into " + targetFolderPath.toOSString())); } ModelHelper.copyExtendedModuleFiles(specRootFile, targetFolderPath, monitor, STEP, project); @@ -420,7 +421,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa ModelWriter writer = new ModelWriter(); // add extend primer - writer.addPrimer(ModelHelper.TE_MODEL_NAME, ResourceHelper.getModuleName(specRootFilename)); + writer.addPrimer(ModelHelper.TE_MODEL_NAME, ResourceHelper.getModuleName(model.getSpec().getRootFilename())); writeModelInfo(config, writer); @@ -475,9 +476,8 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa { monitor.beginTask("Verifying model files", 4); - IProject project = ResourceHelper.getProject(specName); - IFolder launchDir = project.getFolder(modelName); - IFile rootModule = launchDir.getFile(ModelHelper.TE_FILE_TLA); + final Model model = configuration.getAdapter(Model.class); + final IFile rootModule = model.getTEFile(); monitor.worked(1); // parse the TE.tla file @@ -619,7 +619,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa writer.addTraceExplorerExpressionInfoComments(traceExpressionData); // add extend primer - writer.addPrimer(ModelHelper.TE_MODEL_NAME, ResourceHelper.getModuleName(specRootFilename)); + writer.addPrimer(ModelHelper.TE_MODEL_NAME, ResourceHelper.getModuleName(model.getSpec().getRootFilename())); // write constants, model values, new definitions, definition overrides writeModelInfo(configuration, writer); @@ -652,7 +652,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa writer.writeFiles(tlaFile, cfgFile, monitor); // retrieve the model folder - IFolder modelFolder = project.getFolder(modelName); + IFolder modelFolder = model.getFolder(); // refresh the model folder modelFolder.refreshLocal(IResource.DEPTH_ONE, new SubProgressMonitor(monitor, 100)); @@ -675,15 +675,17 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa } // retrieve the project containing the specification - IProject project = ResourceHelper.getProject(specName); + final Model model = configuration.getAdapter(Model.class); + final TLCSpec spec = model.getSpec(); + final IProject project = spec.getProject(); if (project == null) { // project could not be found throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, - "Error accessing the spec project " + specName)); + "Error accessing the spec project " + spec.getName())); } - TLCJob tlcjob = new TraceExplorerJob(specName, modelName, launch, 1); + TLCJob tlcjob = new TraceExplorerJob(spec.getName(), model.getName(), launch, 1); tlcjob.setPriority(Job.SHORT); tlcjob.setUser(true); // The TLC job itself does not do any file IO diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java index 0119edca86ade3bf3b2d736020c3f25fe7abaec8..183ac571ba5cd96ad7bca7428fe335f1331c1cfa 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java @@ -26,15 +26,23 @@ package org.lamport.tla.toolbox.tool.tlc.model; +import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Vector; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.DirectoryFileFilter; +import org.apache.commons.io.filefilter.NotFileFilter; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; @@ -46,8 +54,11 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.debug.core.DebugPlugin; @@ -67,6 +78,8 @@ import org.lamport.tla.toolbox.tool.tlc.TLCActivator; import org.lamport.tla.toolbox.tool.tlc.launch.IConfigurationConstants; import org.lamport.tla.toolbox.tool.tlc.launch.IModelConfigurationConstants; import org.lamport.tla.toolbox.tool.tlc.launch.IModelConfigurationDefaults; +import org.lamport.tla.toolbox.tool.tlc.model.Model.StateChangeListener.ChangeEvent; +import org.lamport.tla.toolbox.tool.tlc.model.Model.StateChangeListener.ChangeEvent.State; import org.lamport.tla.toolbox.tool.tlc.traceexplorer.SimpleTLCState; import org.lamport.tla.toolbox.tool.tlc.util.ModelHelper; import org.lamport.tla.toolbox.util.ResourceHelper; @@ -77,8 +90,7 @@ import tlc2.output.MP; * This class represents a Toolbox Model that can be executed by TLC. */ public class Model implements IModelConfigurationConstants, IAdaptable { - - /** + /** * Marker indicating an error in the model */ private static final String TLC_MODEL_ERROR_MARKER = "org.lamport.tla.toolbox.tlc.modelErrorMarker"; @@ -90,15 +102,11 @@ public class Model implements IModelConfigurationConstants, IAdaptable { /** * marker on .launch file with boolean attribute modelIsRunning */ - public static final String TLC_MODEL_IN_USE_MARKER = "org.lamport.tla.toolbox.tlc.modelMarker"; + private static final String TLC_MODEL_IN_USE_MARKER = "org.lamport.tla.toolbox.tlc.modelMarker"; /** * marker on .launch file, binary semantics */ private static final String TLC_CRASHED_MARKER = "org.lamport.tla.toolbox.tlc.crashedModelMarker"; - /** - * model is being run - */ - private static final String MODEL_IS_RUNNING = "modelIsRunning"; /** * model is locked by a user lock */ @@ -130,7 +138,57 @@ public class Model implements IModelConfigurationConstants, IAdaptable { public static Model getByName(final String modelName) { return TLCModelFactory.getByName(modelName); } + + /** + * A {@link StateChangeListener} is notified when the running state of the model + * changes. There is no guarantee as to which thread is being used to send the + * notification. A {@link StateChangeListener} has subscribe and unsubscribe + * via {@link Model#add(StateChangeListener)} and {@link Model#remove(StateChangeListener)}. + */ + public static class StateChangeListener { + + public static class ChangeEvent { + + public enum State { + RUNNING, NOT_RUNNING, DELETED; + + public boolean in(State ... states) { + for (State state : states) { + if (state == this) { + return true; + } + } + return false; + } + } + + private final State state; + private final Model model; + + private ChangeEvent(Model model, State state) { + this.model = model; + this.state = state; + } + public State getState() { + return state; + } + + public Model getModel() { + return model; + } + } + + /** + * @return true iff the listener should be unsubscribed from receiving future + * events after it handled the event. + */ + public boolean handleChange(ChangeEvent event) { + return false; + } + } + + private final Set<StateChangeListener> listeners = new CopyOnWriteArraySet<StateChangeListener>(); private TLCSpec spec; private ILaunchConfiguration launchConfig; @@ -164,6 +222,23 @@ public class Model implements IModelConfigurationConstants, IAdaptable { this.launchConfig = launchConfig; } + public boolean add(StateChangeListener stateChangeListener) { + return this.listeners.add(stateChangeListener); + } + + public boolean remove(StateChangeListener stateChangeListener) { + return this.listeners.remove(stateChangeListener); + } + + private void notifyListener(final StateChangeListener.ChangeEvent event) { + for (StateChangeListener scl : listeners) { + if (scl.handleChange(event)) { + // Listener wants to be deregistered as a listener. + listeners.remove(scl); + } + } + } + public TLCSpec getSpec() { if (this.spec == null) { final String launchName = this.launchConfig.getName(); @@ -191,7 +266,13 @@ public class Model implements IModelConfigurationConstants, IAdaptable { } public void rename(String newModelName) { + final Collection<Model> snapshots = getSnapshots(); + renameLaunch(getSpec(), sanitizeName(newModelName)); + + for (Model snapshot : snapshots) { + snapshot.rename(newModelName + snapshot.getSnapshotSuffix()); + } } /** @@ -199,10 +280,16 @@ public class Model implements IModelConfigurationConstants, IAdaptable { * @param newSpecName */ void specRename(final Spec newSpec) { + final Collection<Model> snapshots = getSnapshots(); + renameLaunch(newSpec, getName()); // The spec's name has changed. Force a re-lookup of the instance with // the updated name. this.spec = null; + + for (Model snapshot : snapshots) { + snapshot.specRename(newSpec);; + } } private void renameLaunch(final Spec newSpec, String newModelName) { @@ -261,7 +348,6 @@ public class Model implements IModelConfigurationConstants, IAdaptable { } public boolean isRunning() { - final boolean marker = isMarkerSet(TLC_MODEL_IN_USE_MARKER, MODEL_IS_RUNNING); final ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); final ILaunch[] launches = launchManager.getLaunches(); for (int i = 0; i < launches.length; i++) { @@ -269,31 +355,20 @@ public class Model implements IModelConfigurationConstants, IAdaptable { if (launch.getLaunchConfiguration() != null && launch.getLaunchConfiguration().contentsEqual(this.launchConfig)) { if (!launch.isTerminated()) { - if (marker != true) { - TLCActivator.logInfo("Model state out-of-sync. Close and reopen spec to sync."); - } return true; } } } - if (marker != false) { - TLCActivator.logInfo("Model state out-of-sync. Close and reopen spec to sync."); - } return false; } public void setRunning(boolean isRunning) { if (isRunning) { - // Clear the stale/crashed marker. After all, the model is obviously + // Clear the crashed marker. After all, the model is obviously // running now. recover(); } - - // There marker is set regardless of the actual isRunning() state - // returned by the ILaunchManager above. This is, because the marker - // mechanism is used to send "events" to the Toolbox's UI to update the - // model editor. - setMarker(TLC_MODEL_IN_USE_MARKER, MODEL_IS_RUNNING, isRunning); + notifyListener(new StateChangeListener.ChangeEvent(this, isRunning ? State.RUNNING : State.NOT_RUNNING)); } public boolean isLocked() { @@ -334,7 +409,116 @@ public class Model implements IModelConfigurationConstants, IAdaptable { } } - /** + /* + * Snapshot related methods. + */ + + private static final String SNAP_SHOT = "_SnapShot_"; + static final String SNAPSHOT_REGEXP = SNAP_SHOT + "[0-9]*$"; + + private String getSnapshotSuffix() { + if (isSnapshot()) { + final int idx = getName().lastIndexOf(SNAP_SHOT); + return getName().substring(idx, getName().length()); + } + return ""; + } + + public long getSnapshotTimeStamp() { + final int idx = getName().lastIndexOf(SNAP_SHOT) + 10; + return Long.valueOf(getName().substring(idx, getName().length())); + } + + public Collection<Model> getSnapshots() { + return getSpec().getModels(getName() + SNAPSHOT_REGEXP, true).values(); + } + + public boolean isSnapshot() { + return getName().matches(".*" + SNAPSHOT_REGEXP); + } + + public boolean hasSnapshots() { + return !getSpec().getModels(getName() + SNAPSHOT_REGEXP, true).isEmpty(); + } + + public Model getSnapshotFor() { + return getSpec().getModel(getName().replaceFirst(SNAPSHOT_REGEXP, "")); + } + + public Model snapshot() throws CoreException { + // Create a copy of the underlying launch configuration. + final Model snapshot = copy(getName() + SNAP_SHOT + System.currentTimeMillis()); + + // Snapshot the model's markers as well (e.g. the information about errors, see hasErrors()). + final IMarker[] markers = getMarkers(); + for (IMarker iMarker : markers) { + snapshot.setMarker(iMarker.getAttributes(), iMarker.getType()); + } + + // Set the snapshot to be locked? Do we want the user to be able to run it again? +// snapshot.setLocked(true); + + /* + * Snapshot (copy) the model folder which include the TLC output as well as the version + * of the spec and module overwrites with which TLC ran. + */ + final IPath snapshotPath = getSpec().getProject().getFolder(snapshot.getName()).getLocation(); + final IPath modelFolder = getSpec().getProject().getFolder(this.getName()).getLocation(); + // Use non-Eclipse API instead of modelFolder.copy(snapshotFolder, false, + // monitor which supports a non-recursive copy. A recursive copy includes the + // states/ directory leftover from TLC which waste quite some space and might + // take some time to copy. + try { + FileUtils.copyDirectory(modelFolder.toFile(), snapshotPath.toFile(), + new NotFileFilter(DirectoryFileFilter.DIRECTORY)); + + // Rename .dot file name because hasStateGraphDump checks if a .dot file exists + // that matches the name of the model. + if (hasStateGraphDump()) { + final IPath oldDotFile = getSpec().getProject() + .getFolder(snapshot.getName() + File.separator + this.getName() + ".dot").getLocation(); + final IPath newDotFile = getSpec().getProject() + .getFolder(snapshot.getName() + File.separator + snapshot.getName() + ".dot").getLocation(); + FileUtils.moveFile(oldDotFile.toFile(), newDotFile.toFile()); + } + // Refresh the snapshot folder after having copied files without using the + // Eclipse resource API. Otherwise, the resource API does not see the files + // which e.g. results in an incomplete model deletion or hasStateGraphDump + // incorrectly returning false. + snapshot.getFolder().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + } catch (IOException e) { + throw new CoreException(new Status(Status.ERROR, TLCActivator.PLUGIN_ID, e.getMessage(), e)); + } + + return snapshot; + } + + /* + * End of snapshot related methods. + */ + + /* + * State Graph dump in dot file format (GraphViz). + */ + + private static final String DOT_FILE_EXT = ".dot"; + + public boolean hasStateGraphDump() { + final String name = getName().concat(DOT_FILE_EXT); + final IFile file = getFolder().getFile(name); + return file.exists(); + } + + public IFile getStateGraphDump() { + final String name = getName().concat(DOT_FILE_EXT); + return getFolder().getFile(name); + } + + /* + * End of state graph dump related methods. + */ + + /** * Returns whether the original trace or the trace with trace explorer expressions from the * most recent run of the trace explorer for the model should be shown in the TLC error view. * @@ -364,6 +548,10 @@ public class Model implements IModelConfigurationConstants, IAdaptable { setMarker(TRACE_EXPLORER_MARKER, IS_ORIGINAL_TRACE_SHOWN, isOriginalTraceShown); } + public boolean hasError() { + return getMarkers().length > 0; + } + private boolean isMarkerSet(String markerType, final String attributeName) { // marker final IFile resource = getFile(); @@ -484,6 +672,7 @@ public class Model implements IModelConfigurationConstants, IAdaptable { } public void delete(IProgressMonitor monitor) throws CoreException { + notifyListener(new ChangeEvent(this, State.DELETED)); final IResource[] members; @@ -550,6 +739,17 @@ public class Model implements IModelConfigurationConstants, IAdaptable { return getFile(ModelHelper.FILE_TLA); } + /** + * Retrieves the TE file that is being used by the trace explorer. + * + * @param config + * configuration representing the model + * @return a file handle or <code>null</code> + */ + public IFile getTEFile() { + return getFile(ModelHelper.TE_FILE_TLA); + } + public IFile getOutputLogFile() { return getOutputLogFile(false); } @@ -572,7 +772,7 @@ public class Model implements IModelConfigurationConstants, IAdaptable { } /** - * Retrives the TLA file used by the trace explorer + * Retrieves the TLA file used by the trace explorer * @return a file handle or <code>null</code> */ public IFile getTraceExplorerTLAFile() { diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCSpec.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCSpec.java index 1b0a6beb791e47a00defa845f874aac78cea2361..d7366460cb435941f56dccd646d80de4a332d4eb 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCSpec.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCSpec.java @@ -61,9 +61,14 @@ public class TLCSpec extends Spec { } /** - * @return All {@link Model}s associated with the given {@link Spec} + * @return All {@link Model}s associated with the given {@link Spec} excluding + * snapshots of models. */ public Map<String, Model> getModels() { + return getModels(".*" + Model.SNAPSHOT_REGEXP, false); + } + + public Map<String, Model> getModels(final String regexp, final boolean include) { final ILaunchConfiguration[] launchConfigurations = getAllLaunchConfigurations(); final Map<String, Model> res = new HashMap<String, Model>(); @@ -73,6 +78,9 @@ public class TLCSpec extends Spec { final ILaunchConfiguration aConfiguration = launchConfigurations[i]; if (location.isPrefixOf(aConfiguration.getFile().getLocation())) { final Model model = aConfiguration.getAdapter(Model.class); + if (model.getName().matches(regexp) != include) { + continue; + } res.put(model.getName(), model); } } @@ -93,7 +101,9 @@ public class TLCSpec extends Spec { } public Model getModel(final String modelName) { - return getModels().get(Model.sanitizeName(modelName)); + final String sanitizedName = Model.sanitizeName(modelName); + final Map<String, Model> models = getModels(sanitizedName, true); + return models.get(sanitizedName); } public void rename(final Spec aNewSpec) { diff --git a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/SimpleTLCState.java b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/SimpleTLCState.java index 6950125822653eb5e5a5bd3f89393d2d341683a3..2c77b45b1935a5f680c0644b21a26c0251478b0f 100644 --- a/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/SimpleTLCState.java +++ b/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/SimpleTLCState.java @@ -24,8 +24,8 @@ public class SimpleTLCState private static final String COLON = ":"; private static final String CR = "\n"; - private static final String STUTTERING = "Stuttering"; - private static final String BACK_TO_STATE = "Back"; + private static final String STUTTERING = " Stuttering"; // See tlc2.output.MP + private static final String BACK_TO_STATE = " Back to state"; // See tlc2.output.MP private static final String AND = "/\\"; private static final String EQ = " = "; @@ -78,8 +78,8 @@ public class SimpleTLCState int stateNumber = Integer.parseInt(stateInputString.substring(0, index).trim()); String label = stateInputString.substring(index + 1, index2); - boolean isStuttering = label.indexOf(STUTTERING) != -1; - boolean isBackToState = label.indexOf(BACK_TO_STATE) != -1; + boolean isStuttering = label.indexOf(STUTTERING) == 0; + boolean isBackToState = label.indexOf(BACK_TO_STATE) == 0; SimpleTLCVariable[] vars = null; diff --git a/org.lamport.tla.toolbox/icons/document-pdf.png b/org.lamport.tla.toolbox/icons/document-pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..c68ab66ca23e371d9a42fe97132b3b2707cc3271 Binary files /dev/null and b/org.lamport.tla.toolbox/icons/document-pdf.png differ diff --git a/org.lamport.tla.toolbox/icons/full/obj16/cprj_obj.png b/org.lamport.tla.toolbox/icons/full/obj16/cprj_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..812e7839e6d86e1434e9f183d6be01eb765a2f43 Binary files /dev/null and b/org.lamport.tla.toolbox/icons/full/obj16/cprj_obj.png differ diff --git a/org.lamport.tla.toolbox/icons/full/obj16/cprj_obj@2x.png b/org.lamport.tla.toolbox/icons/full/obj16/cprj_obj@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..17910eb569f7a7543831f2acce46861d76cb02ab Binary files /dev/null and b/org.lamport.tla.toolbox/icons/full/obj16/cprj_obj@2x.png differ diff --git a/org.lamport.tla.toolbox/icons/full/obj16/prj_obj.png b/org.lamport.tla.toolbox/icons/full/obj16/prj_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..be0b3507bfd378440fa5b9728a475ac695cb546f Binary files /dev/null and b/org.lamport.tla.toolbox/icons/full/obj16/prj_obj.png differ diff --git a/org.lamport.tla.toolbox/icons/full/obj16/prj_obj@2x.png b/org.lamport.tla.toolbox/icons/full/obj16/prj_obj@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..543bf8eaf22b482fd7b40040419288a4b2020f84 Binary files /dev/null and b/org.lamport.tla.toolbox/icons/full/obj16/prj_obj@2x.png differ diff --git a/org.lamport.tla.toolbox/plugin.xml b/org.lamport.tla.toolbox/plugin.xml index 8336e07b903edbc0c64d6044d1c75dbe71ba893c..ca9051143a645461a643ff46ec8680e6e7881f3b 100644 --- a/org.lamport.tla.toolbox/plugin.xml +++ b/org.lamport.tla.toolbox/plugin.xml @@ -122,6 +122,24 @@ name="Welcome View" restorable="true"> </view> + <view + allowMultiple="true" + class="org.lamport.tla.toolbox.ui.view.PDFBrowser" + icon="icons/document-pdf.png" + id="org.lamport.tla.toolbox.PDFBrowser" + name="PDFBrowser" + restorable="false"> + </view> + </extension> + <extension + point="org.eclipse.ui.editors"> + <editor + class="org.lamport.tla.toolbox.ui.view.PDFBrowserEditor" + extensions="pdf" + icon="icons/document-pdf.png" + id="org.lamport.tla.toolbox.PDFBrowserEditor" + name="PDF Editor"> + </editor> </extension> <!-- --> @@ -633,6 +651,14 @@ style="push" tooltip="Opens dynamic help"> </command> + <separator + name="toolbox.command.help.tlaplus" + visible="true"> + </separator> + <separator + name="toolbox.command.about" + visible="true"> + </separator> <command commandId="org.eclipse.ui.help.aboutAction" id="toolbox.menuItem.about" diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java index 07ab418bb807d04a8b52d8e65c0e1dc001a97c2b..09202d16b5533f07c40dfc858f6fea50bf4ed9a5 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/Activator.java @@ -11,8 +11,10 @@ import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.resource.ImageDescriptor; import org.lamport.tla.toolbox.spec.Spec; import org.lamport.tla.toolbox.spec.manager.WorkspaceSpecManager; import org.lamport.tla.toolbox.spec.nature.TLAParsingBuilder.OutOfBuildSpecModulesGatheringDeltaVisitor; @@ -29,6 +31,9 @@ public class Activator extends AbstractTLCActivator // The plug-in ID public static final String PLUGIN_ID = "org.lamport.tla.toolbox"; + + public static final String IMG_SPEC_OPEN = "img_spec_open"; + public static final String IMG_SPEC_CLOSED = "img_spec_closed"; // The shared instance private static Activator plugin; @@ -48,6 +53,14 @@ public class Activator extends AbstractTLCActivator { super.start(context); plugin = this; + + // Eclipse Neon has started to use PNG instead of GIF icons to support display + // with high dpi counts. Load two pngs which we used to get from the Eclipse + // Foundation but now have to provide ourself. + getImageRegistry().put(IMG_SPEC_OPEN, ImageDescriptor.createFromURL( + Platform.getBundle(org.lamport.tla.toolbox.Activator.PLUGIN_ID).getEntry("/icons/full/obj16/prj_obj.png"))); + getImageRegistry().put(IMG_SPEC_CLOSED, ImageDescriptor.createFromURL( + Platform.getBundle(org.lamport.tla.toolbox.Activator.PLUGIN_ID).getEntry("/icons/full/obj16/cprj_obj.png"))); // register the listeners IWorkspace workspace = ResourcesPlugin.getWorkspace(); diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/Spec.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/Spec.java index 21ce075599af6e01e1075ddc66f1c475c7c85626..0461895e93e72b66e8de8d9deec4fb70213ac3d6 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/Spec.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/Spec.java @@ -345,6 +345,22 @@ public class Spec implements IAdaptable { return this.specObj; } + /** + * @return The logical name of the root module. E.g. if the file is named + * "DieHard.tla", the root module name is "DieHard". The root module + * name reflects the name declard in the MODULE statement inside the + * .tla file. + * @see SpecObj#getName() + */ + public String getRootModuleName() { + // Can't use this.specObj#getName because specObj can be null. + + // ThefileExtension will be tla. + final String fileExtension = this.rootFile.getFileExtension(); + // Strip off ".tla" from the end of the string. + return this.rootFile.getName().replaceFirst("." + fileExtension + "$", ""); + } + /** * Returns the SpecObj only on valid status */ diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/manager/WorkspaceSpecManager.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/manager/WorkspaceSpecManager.java index 48de9e543a86abca5619ef786bcee084a33a6ba8..78b9ed0d135add00a13ad06f6c31f60b51840b36 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/manager/WorkspaceSpecManager.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/manager/WorkspaceSpecManager.java @@ -207,6 +207,7 @@ public class WorkspaceSpecManager extends GenericSelectionProvider implements IS } } catch (Exception e) { + e.printStackTrace(); // Just ignore the exception and pray. } } diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java index 289452a8f568a4b3022de615acbcd52551fa01d3..b5a114d9015e44da9d8c6f3f15f5615faf940c80 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java @@ -518,6 +518,19 @@ public class ModuleParserLauncher result.addMarker(new TLAMarkerInformationHolder(module, module.getName(), IMarker.SEVERITY_ERROR, coordinates, message)); } // if + else if (message.indexOf("Lexical {error: EOF reached, possibly open comment starting around line ") != -1) + { + // Handles tla2sany.parser.TLAplusParser.parse() EOF due to + // dangling multi-line comment. Without it, the model fails + // to launch and gets stuck in model-checking mode from + // which only a Toolbox restart can recover. + final int beginLine = Integer.parseInt(message.substring( + "Lexical {error: EOF reached, possibly open comment starting around line ".length())); + // coordinates of the error + coordinates = new int[] { beginLine, 0, beginLine, 0 }; + result.addMarker(new TLAMarkerInformationHolder(module, module.getName(), IMarker.SEVERITY_ERROR, + coordinates, message)); + } else { diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/provider/ToolboxLabelProvider.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/provider/ToolboxLabelProvider.java index 1dd68140c18fdf64d43f30f0209623dd87faf070..4625647417aec6e1d768b95f47406b0b5078acbf 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/provider/ToolboxLabelProvider.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/provider/ToolboxLabelProvider.java @@ -31,7 +31,6 @@ import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.ide.IDE.SharedImages; import org.eclipse.ui.navigator.IDescriptionProvider; import org.lamport.tla.toolbox.Activator; import org.lamport.tla.toolbox.spec.Module; @@ -72,9 +71,9 @@ public class ToolboxLabelProvider extends LabelProvider implements ILabelProvide } if (element instanceof Spec) { if (Activator.getSpecManager().isSpecLoaded((Spec) element)) { - return PlatformUI.getWorkbench().getSharedImages().getImage(SharedImages.IMG_OBJ_PROJECT); + return Activator.getDefault().getImageRegistry().get(Activator.IMG_SPEC_OPEN); } - return PlatformUI.getWorkbench().getSharedImages().getImage(SharedImages.IMG_OBJ_PROJECT_CLOSED); + return Activator.getDefault().getImageRegistry().get(Activator.IMG_SPEC_CLOSED); } else if (element instanceof Module) { return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE); } else if (element instanceof Group) { diff --git a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/view/PDFBrowser.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/PDFBrowser.java similarity index 94% rename from org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/view/PDFBrowser.java rename to org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/PDFBrowser.java index 931347d5426663021a0f7e2acfca0633872f5524..4c8e7380536b25ea0e80f92c37323611e19d145a 100644 --- a/org.lamport.tla.toolbox.tool.tla2tex/src/org/lamport/tla/toolbox/tool/tla2tex/view/PDFBrowser.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/PDFBrowser.java @@ -23,7 +23,7 @@ * Contributors: * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ -package org.lamport.tla.toolbox.tool.tla2tex.view; +package org.lamport.tla.toolbox.ui.view; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; @@ -33,7 +33,7 @@ import org.eclipse.ui.part.ViewPart; public class PDFBrowser extends ViewPart { - public static final String ID = "org.lamport.tla.toolbox.tool.tla2tex.PDFBrowser"; + public static final String ID = "org.lamport.tla.toolbox.PDFBrowser"; private Browser browser; diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/PDFBrowserEditor.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/PDFBrowserEditor.java new file mode 100644 index 0000000000000000000000000000000000000000..ef7a5c8b64304b70905eb95fd21c341400af9248 --- /dev/null +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/view/PDFBrowserEditor.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.ui.view; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.part.EditorPart; + +public class PDFBrowserEditor extends EditorPart { + + public static final String ID = "org.lamport.tla.toolbox.PDFBrowserEditor"; + + private Browser browser; + + @Override + public void init(IEditorSite site, IEditorInput input) throws PartInitException { + setSite(site); + setInput(input); + this.setPartName(input.getName()); + if (browser != null && input instanceof IFileEditorInput) { + setFileInput((IFileEditorInput) input); + } + } + + @Override + public void createPartControl(Composite parent) { + final Composite body = new Composite(parent, SWT.NONE); + body.setLayout(new FillLayout()); + this.browser = new Browser(body, SWT.NONE); + if (getEditorInput() instanceof IFileEditorInput) { + setFileInput((IFileEditorInput) getEditorInput()); + } else { + this.browser.setText("<html><body></body></html>"); + } + } + + private void setFileInput(IFileEditorInput input) { + this.browser.setUrl(input.getFile().getLocationURI().toASCIIString()); + } + + @Override + public void setFocus() { + this.browser.getParent().setFocus(); + } + + //** Methods below not relevant because PDF editor is read-only. **// + + @Override + public boolean isDirty() { + return false; + } + + @Override + public void doSave(IProgressMonitor monitor) { + // unsupported + } + + @Override + public void doSaveAs() { + // unsupported + } + + @Override + public boolean isSaveAsAllowed() { + return false; + } +} diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/wizard/NewSpecWizardPage.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/wizard/NewSpecWizardPage.java index 2cc87b0b4de7ac9c80470b1ba7da332e8343f393..beb3e2c18426998c3f07d1726591ca3f088da236 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/wizard/NewSpecWizardPage.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/ui/wizard/NewSpecWizardPage.java @@ -280,6 +280,7 @@ public class NewSpecWizardPage extends WizardPage final String canonicalPath = f.getCanonicalPath(); if (!rootfilePath.equals(canonicalPath)) { rootfilePath = canonicalPath; + this.fileText.setText(rootfilePath); } } catch (IOException e) { e.printStackTrace(); diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/HelpButton.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/HelpButton.java index 080070ecb7c9a2520df82fc16bb86ea622377db5..701383fc2a39e0f1643c33eedc95ead261180ca6 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/HelpButton.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/HelpButton.java @@ -100,7 +100,6 @@ public class HelpButton { public static class HelpButtonListener extends SelectionAdapter implements SelectionListener { -// String file; String url; Composite shell; @@ -133,9 +132,7 @@ public class HelpButton { URL fileURL = bundle.getEntry("html/" + fileName); File theFile = null; try { - theFile = new File(FileLocator.resolve(fileURL).toURI()); - } catch (URISyntaxException e1) { - e1.printStackTrace(); + theFile = new File(FileLocator.resolve(fileURL).getFile()); } catch (IOException e1) { e1.printStackTrace(); } diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/LegacyFileDocumentProvider.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/LegacyFileDocumentProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..6fa5ae894c1bd41dd5141518c64bed09a67fee03 --- /dev/null +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/LegacyFileDocumentProvider.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package org.lamport.tla.toolbox.util; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.ui.editors.text.FileDocumentProvider; + +public class LegacyFileDocumentProvider extends FileDocumentProvider { + + // Overrides the implementation of refreshFile introduced with Bug 482354 - "SVN + // checkout deadlocks Eclipse" with corresponding commit + // Id742d98403cc546fad4a21d25eb18ab7bef48776 (eclipse git repository). + // Use this implementation to check if the file is in sync before a refresh + // operation is executed. + protected void refreshFile(IFile file, IProgressMonitor monitor) throws CoreException { + try { + file.refreshLocal(IResource.DEPTH_INFINITE, monitor); + } catch (OperationCanceledException notExpectedToHappen) { + notExpectedToHappen.printStackTrace(); + } + } +} diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/TLAMarkerHelper.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/TLAMarkerHelper.java index 0aee673ab78e9dd495b6f3023daf2fa35f0a8295..ff0f524fe0e7193a3a3a517dc168d20f8faefb58 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/TLAMarkerHelper.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/TLAMarkerHelper.java @@ -159,7 +159,7 @@ public class TLAMarkerHelper // since we know that the editor uses file based editor representation FileEditorInput fileEditorInput = new FileEditorInput((IFile) resource); - FileDocumentProvider fileDocumentProvider = new FileDocumentProvider(); + FileDocumentProvider fileDocumentProvider = new LegacyFileDocumentProvider(); try { fileDocumentProvider.connect(fileEditorInput); diff --git a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/UIHelper.java b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/UIHelper.java index 0af3b03517f75f4f99453166885499ff8fa1ede5..404063756b9a353ac2306e2e55785625e1d5a266 100644 --- a/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/UIHelper.java +++ b/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/UIHelper.java @@ -1,5 +1,6 @@ package org.lamport.tla.toolbox.util; +import java.io.File; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -11,11 +12,16 @@ import org.eclipse.core.commands.NotEnabledException; import org.eclipse.core.commands.NotHandledException; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.core.commands.common.NotDefinedException; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.filesystem.IFileSystem; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.e4.core.contexts.IEclipseContext; @@ -65,6 +71,7 @@ import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.editors.text.FileDocumentProvider; import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.ide.FileStoreEditorInput; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.part.MultiPageEditorPart; import org.eclipse.ui.texteditor.ITextEditor; @@ -325,6 +332,26 @@ public class UIHelper { public static String getActivePerspectiveId() { return UIHelper.getActivePage().getPerspective().getId(); } + + /** + * @return Returns the {@link IEditorPart} for the given editor id without + * opening the editor or null if no editor with the given id exists. + * Contrary to the Eclipse facilities to find and editor, this method + * does not initialize/render/configure the editor instance. It is left + * to the callee to properly initialize the editor instance by e.g. + * adding it to a MultipageEditor. + * @throws CoreException + */ + public static IEditorPart findEditor(final String editorId) throws CoreException { + final IExtensionRegistry registry = Platform.getExtensionRegistry(); + final IConfigurationElement[] elements = registry.getConfigurationElementsFor(PlatformUI.PLUGIN_ID, "editors"); + for (IConfigurationElement ice : elements) { + if (editorId.equals(ice.getAttribute("id"))) { + return (IEditorPart) ice.createExecutableExtension("class"); + } + } + return null; + } /** * Convenience method to reduce client dependencies @@ -381,7 +408,25 @@ public class UIHelper { public static IEditorPart openEditorUnchecked(String editorId, IFile file, boolean activate) throws PartInitException { return openEditorUnchecked(editorId, new FileEditorInput(file), activate); } - + + /** + * @see UIHelper#openEditorUnchecked(String, File, String, boolean) + */ + public static IEditorPart openEditorUnchecked(String editorId, File file, final String name) throws PartInitException { + return openEditorUnchecked(editorId, file, name, true); + } + + /** + * @param file The file to open + * @param name A human readable name for the input file. If the file name is to be used, pass {@link File#getName()} + * @see UIHelper#openEditorUnchecked(String, IEditorInput, boolean) + */ + public static IEditorPart openEditorUnchecked(final String editorId, final File file, final String name, final boolean activate) throws PartInitException { + final IFileSystem localFileSystem = EFS.getLocalFileSystem(); + final IFileStore fromLocalFile = localFileSystem.fromLocalFile(file); + return openEditorUnchecked(editorId, new NamedFileStoreEditorInput(fromLocalFile, name), activate); + } + public static IEditorPart openEditorUnchecked(String editorId, IEditorInput input) throws PartInitException { return openEditorUnchecked(editorId, input, true); } @@ -1238,6 +1283,21 @@ public class UIHelper { } } + private static class NamedFileStoreEditorInput extends FileStoreEditorInput { + + private final String name; + + public NamedFileStoreEditorInput(final IFileStore fileStore, final String name) { + super(fileStore); + this.name = name; + } + + @Override + public String getName() { + return name; + } + } + /** * Parts (Editors and Views) can be stacked if the user drags them on top of * each other. This method returns true, if the given part (first parameter) diff --git a/pom.xml b/pom.xml index 1a0a4d30958ad86e9ccb917eba32a1091f5ae73d..5d0673f2f11b0ae74c5dac57558c5c1ac901134c 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ <!-- http://maven.apache.org/general.html#encoding-warning --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <tycho-version>0.26.0</tycho-version> + <tycho-version>1.0.0</tycho-version> <!-- no default here --> <tycho.test.vm.argline>-Xmx500m -Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=n</tycho.test.vm.argline> @@ -130,8 +130,8 @@ <artifactId>tycho-compiler-plugin</artifactId> <version>${tycho-version}</version> <configuration> - <source>1.5</source> - <target>1.5</target> + <source>1.7</source> + <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> @@ -191,13 +191,13 @@ </artifact> </target> - <!-- Need to specify miminum Java version. This defines what + <!-- Need to specify mininum Java version. This defines what java.* packages are available during dependency resolution. The default is Java 1.4 which e.g. does not come with "java.security.sasl", a package that is indirectly referenced by the toolbox (indirectly via org.apache.mina.core). see http://dev.eclipse.org/mhonarc/lists/cbi-dev/msg00166.html --> - <executionEnvironment>JavaSE-1.7</executionEnvironment> + <executionEnvironment>JavaSE-1.8</executionEnvironment> <!-- configure the p2 target environments for multi-platform build --> <environments> diff --git a/tlatools/.classpath b/tlatools/.classpath index 5df2c0e31ace0682e9abd3d638adafa4534c24fa..1fe074bee9d05529b82884cadf03067bd5110a4e 100644 --- a/tlatools/.classpath +++ b/tlatools/.classpath @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="src" path="src"/> <classpathentry excluding="tlc2/tool/WorkerMonitorAspect.aj|tlc2/tool/distributed/TLCServerMonitorAspect.aj|tlc2/tool/distributed/RMIMethodMonitorAspect.aj|tlc2/tool/distributed/RMIMethodMonitorAspect.aj|tlc2/tool/distributed/TLCServerMonitorAspect.aj" kind="src" path="src-aj"/> diff --git a/tlatools/.settings/org.eclipse.jdt.core.prefs b/tlatools/.settings/org.eclipse.jdt.core.prefs index 812fbdb54710711e928854c8b1a4473c53289b0c..deac336a07ad19e9db9791516f09fedd665afdd9 100644 --- a/tlatools/.settings/org.eclipse.jdt.core.prefs +++ b/tlatools/.settings/org.eclipse.jdt.core.prefs @@ -6,9 +6,9 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -96,4 +96,4 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/tlatools/META-INF/MANIFEST.MF b/tlatools/META-INF/MANIFEST.MF index 92192777b4ce1d8187176261d0266bb71ba4472b..8b455ca1cf02ceee0239f9f4f0193cf02d6e2779 100644 --- a/tlatools/META-INF/MANIFEST.MF +++ b/tlatools/META-INF/MANIFEST.MF @@ -35,10 +35,10 @@ Export-Package: pcal, tlc2.value, util Bundle-Vendor: Leslie Lamport, Markus Alexander Kuppe -Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Eclipse-BundleShape: dir Require-Bundle: org.aspectj.runtime;bundle-version="1.6.0";resolution:=optional;x-installation:=greedy, - org.junit;bundle-version="3.8.0";resolution:=optional;x-installation:=greedy, + org.junit;bundle-version="4.12.0";resolution:=optional;x-installation:=greedy, javax.mail;bundle-version="1.4.0";resolution:=optional, javax.activation;bundle-version="1.1.0";resolution:=optional, org.easymock;bundle-version="2.4.0";resolution:=optional diff --git a/tlatools/customBuild.xml b/tlatools/customBuild.xml index b5b906c5a1ece0d48de9e8a118d37a180ae83081..eca525a62ab7ff4dc0f0547f5a6fe95afcf8aa97 100644 --- a/tlatools/customBuild.xml +++ b/tlatools/customBuild.xml @@ -118,7 +118,7 @@ </echo> <!-- compile --> <mkdir dir="${class.dir}" /> - <javac includeantruntime="false" srcdir="${src.dir}" destdir="${class.dir}" debug="true" verbose="false" source="1.5" target="1.5"> + <javac includeantruntime="false" srcdir="${src.dir}" destdir="${class.dir}" debug="true" verbose="false" source="1.8" target="1.8"> <!-- compilerarg value="-Xlint:deprecation"/--> <classpath refid="project.classpath" /> <classpath> @@ -147,7 +147,7 @@ ==================================================================== </echo> <!-- compile aspectj related class files --> - <iajc destdir="${class.dir}" sourceRoots="${src-aj.dir}" debug="true" source="1.5" target="1.5"> + <iajc destdir="${class.dir}" sourceRoots="${src-aj.dir}" debug="true" source="1.8" target="1.8"> <classpath refid="project.classpath" /> <classpath> <pathelement location="lib/aspectjrt-1.8.5.jar" /> @@ -223,7 +223,7 @@ <target name="compile-test" unless="test.skip"> <!-- compile unit tests --> <mkdir dir="${test.class.dir}" /> - <javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false"> + <javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false" source="1.8" target="1.8"> <classpath refid="project.classpath" /> <classpath> <pathelement location="lib/junit-4.12.jar" /> @@ -253,9 +253,7 @@ <junit printsummary="yes" haltonfailure="${test.halt}" haltonerror="${test.halt}" forkmode="perTest" fork="yes"> <!-- enable all assertions --> <jvmarg value="-ea"/> - <!-- Uncomment to run TLC with alternative fingerprintset impl. See sysproperty below too. - <jvmarg value="-XX:MaxDirectMemorySize=32m"/> - --> + <jvmarg value="-XX:MaxDirectMemorySize=512k"/> <!-- Uncomment to open a debug port in each forked VM to remote attach your Eclipse at port 1044. <jvmarg value="-Xdebug" /> <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044" /> @@ -275,9 +273,7 @@ <!-- Pass the base path of the tlatools project to unit tests in case they need it to locate TLA+ specs or configs --> <sysproperty key="basedir" value="${basedir}/"/> - <!-- Uncomment to run TLC with alternative fingerprintset impl. See jvmarg above too. <sysproperty key="tlc2.tool.fp.FPSet.impl" value="tlc2.tool.fp.OffHeapDiskFPSet"/> - --> <sysproperty key="util.FileUtil.milliseconds" value="true"/> <sysproperty key="tlc2.tool.distributed.TLCWorker.threadCount" value="4"/> <batchtest fork="yes" todir="${test.reports}"> @@ -292,6 +288,7 @@ <exclude name="**/SuccessfulSimulationTestCase.java" /> <exclude name="**/AbstractExampleTestCase.java" /> <exclude name="**/TestMPRecorder.java" /> + <exclude name="**/TestPrintStream.java" /> <exclude name="**/Abstract*Test.java" /> <exclude name="**/TestDriver.java" /> <exclude name="**/TestDriver2.java" /> @@ -339,6 +336,7 @@ <exclude name="**/SuccessfulSimulationTestCase.java" /> <exclude name="**/AbstractExampleTestCase.java" /> <exclude name="**/TestMPRecorder.java" /> + <exclude name="**/TestPrintStream.java" /> <exclude name="**/Abstract*Test.java" /> <exclude name="**/TestDriver.java" /> <exclude name="**/TestDriver2.java" /> diff --git a/tlatools/lib/javax.mail.jar b/tlatools/lib/javax.mail.jar index 0c5ac3967e2fb98b74f86b41de47666f9bd2758d..dd06a6a0965a3439620391bcf4b166316e903e88 100644 Binary files a/tlatools/lib/javax.mail.jar and b/tlatools/lib/javax.mail.jar differ diff --git a/tlatools/logging.properties b/tlatools/logging.properties index d256cb507341669daa226bc0c55e91b36ee16d92..f4fb74c8d53ff25375ece6a261db66300aa69a1f 100644 --- a/tlatools/logging.properties +++ b/tlatools/logging.properties @@ -54,4 +54,5 @@ java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter ############################################################ #tlc2.tool.level = ALL +#tlc2.tool.fp.level = ALL #tlc2.tool.liveness.level = ALL diff --git a/tlatools/src/tla2sany/parser/BracketStack.java b/tlatools/src/tla2sany/parser/BracketStack.java index 74a8713cc6e1204476b208e60b96640b00ade945..8ace9df9febc740f5fc32094e3c7f20c0a93a73c 100644 --- a/tlatools/src/tla2sany/parser/BracketStack.java +++ b/tlatools/src/tla2sany/parser/BracketStack.java @@ -132,8 +132,8 @@ public class BracketStack implements //LogCategories, Offset is the end column of the token ...\/ ou .../\ on utilise - 1 pour comparer au de'but de la partie significative du symbole. - De cette manire, le comportement ne change pas si - on utilise uniquement la forme non prfixe des + De cette manire, le comportement ne change pas si + on utilise uniquement la forme non prafixe des symboles */ } } diff --git a/tlatools/src/tla2sany/semantic/APSubstInNode.java b/tlatools/src/tla2sany/semantic/APSubstInNode.java index e14ac1582babfe825512c878dea78f77e369bd71..f8af1db14f9bf1adb4ed13681d5b42f25f48e0f1 100644 --- a/tlatools/src/tla2sany/semantic/APSubstInNode.java +++ b/tlatools/src/tla2sany/semantic/APSubstInNode.java @@ -462,8 +462,3 @@ public class APSubstInNode extends LevelNode { return ret; } } - - - - - diff --git a/tlatools/src/tla2sany/semantic/AssumeNode.java b/tlatools/src/tla2sany/semantic/AssumeNode.java index 961dacea486bbe3b894dc8ef743074a7a4170b4b..6077e82931eb3b86c9f84d74dc8799866b49ce95 100644 --- a/tlatools/src/tla2sany/semantic/AssumeNode.java +++ b/tlatools/src/tla2sany/semantic/AssumeNode.java @@ -12,6 +12,7 @@ import tla2sany.xml.XMLExportable; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; /** * This class represents an assumption about the constants in a module. @@ -182,17 +183,70 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn, if (assumeExpr != null) {assumeExpr.walkGraph(semNodesTable);} ; } - public Element export(Document doc, tla2sany.xml.SymbolContext context) { - if (getDef() == null) - // we export the definition of the assumption - return super.export(doc,context); - else - // we export its name only, named assumptions will be exported through the ThmOrAss.. - return getDef().export(doc,context); + /* MR: This is the same as SymbolNode.exportDefinition. Exports the actual theorem content, not only a reference. + */ + public Element exportDefinition(Document doc, tla2sany.xml.SymbolContext context) { + if (!context.isTop_level_entry()) + throw new IllegalArgumentException("Exporting theorem ref "+getNodeRef()+" twice!"); + context.resetTop_level_entry(); + try { + Element e = getLevelElement(doc, context); + // level + try { + Element l = appendText(doc,"level",Integer.toString(getLevel())); + e.insertBefore(l,e.getFirstChild()); + } catch (RuntimeException ee) { + // not sure it is legal for a LevelNode not to have level, debug it! + } + //location + try { + Element loc = getLocationElement(doc); + e.insertBefore(loc,e.getFirstChild()); + } catch (RuntimeException ee) { + // do nothing if no location + } + return e; + } catch (RuntimeException ee) { + System.err.println("failed for node.toString(): " + toString() + "\n with error "); + ee.printStackTrace(); + throw ee; + } } + + protected String getNodeRef() { + return "AssumeNodeRef"; + } + +// public Element export(Document doc, tla2sany.xml.SymbolContext context) { +// if (getDef() == null) +// // we export the definition of the assumption +// return super.export(doc,context); +// else +// // we export its name only, named assumptions will be exported through the ThmOrAss.. +// return getDef().export(doc,context); +// } + protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { Element e = doc.createElement("AssumeNode"); - e.appendChild(getAssume().export(doc,context)); + if (def != null) { + //if there is a definition, export it too + Node d = doc.createElement("definition"); + d.appendChild(def.export(doc, context)); + e.appendChild(d); + assert( def.getBody() == this.assumeExpr ); //make sure theorem and definition body agree before export + } + Node n = doc.createElement("body"); + n.appendChild(getAssume().export(doc,context)); + e.appendChild(n); + return e; + } + + /* overrides LevelNode.export and exports a UID reference instad of the full version*/ + public Element export(Document doc, tla2sany.xml.SymbolContext context) { + // first add symbol to context + context.put(this, doc); + Element e = doc.createElement(getNodeRef()); + e.appendChild(appendText(doc,"UID",Integer.toString(myUID))); return e; } } diff --git a/tlatools/src/tla2sany/semantic/InstanceNode.java b/tlatools/src/tla2sany/semantic/InstanceNode.java index e65037bd15c82e0a57074a78e74822dba31618a9..85425ae746e023758f25176d716d619773022651 100644 --- a/tlatools/src/tla2sany/semantic/InstanceNode.java +++ b/tlatools/src/tla2sany/semantic/InstanceNode.java @@ -449,7 +449,8 @@ public class InstanceNode extends LevelNode { } Element ret = doc.createElement("InstanceNode"); - ret.appendChild(appendText(doc,"uniquename",name.toString())); + if (name != null) ret.appendChild(appendText(doc,"uniquename",name.toString())); + ret.appendChild(appendText(doc, "module", module.getName().toString() )); ret.appendChild(sbts); ret.appendChild(prms); return ret; diff --git a/tlatools/src/tla2sany/semantic/LevelNode.java b/tlatools/src/tla2sany/semantic/LevelNode.java index 5e6caf692d861dbe2f5cbf3232ab3b833283eb30..986dfbbdef5a679017917ec7228ef7132853d5b5 100644 --- a/tlatools/src/tla2sany/semantic/LevelNode.java +++ b/tlatools/src/tla2sany/semantic/LevelNode.java @@ -607,6 +607,28 @@ public int levelChecked = 0 ; // (* any reasonable case where this will disallow a level-correct expression *) // (* that a user is likely to write. *) // (* *) +// (* The decision to simplify the bookkeeping results in the following, *) +// (* somewhat less unlikely problem. With the definitions *) +// (* *) +// (* ApplyToPrime(Op(_)) == Op(x') *) +// (* EqualsNoPrime(a) == x *) +// (* *) +// (* the expression ApplyToPrime(EqualsNoPrime)' , which equals x', is *) +// (* considered to be illegal. This is because the algorithm to compute the *) +// (* level makes the assumption that ApplyToPrime will always be applied to *) +// (* operators Op for which the level of Op(exp) depends on the level of *) +// (* exp. Hence, SANY's algorithm gives ApplyToPrime(Op) a level of at *) +// (* least 2 (primed expression) for any operator Op. A slightly more *) +// (* realistic example can be constructed by modifying ApplyToPrime a bit *) +// (* and applying it to ENABLED. *) +// (* TLC warns users about this bug if it reports an invariant to be *) +// (* level-incorrect in tlc2.tool.Spec.processConfigInvariants() with error *) +// (* code tlc2.output.EC.TLC_INVARIANT_VIOLATED_LEVEL. *) +// (* A corresponding test can be found in test52. Its invariant "Invariant" *) +// (* covers the ENABLED scenario. However, the invariant remains disabled *) +// (* for as long as this bug is open. The invariant Invariant can be *) +// (* re-enabled in test52.cfg once this bug is closed. *) +// (* *) // (* To compute the values of op.level, op.weights, and op.minMaxLevel for *) // (* an OpDefNode op corresponding to a definition, a level-checking *) // (* algorithm needs to keep track of the constraints on its formal *) diff --git a/tlatools/src/tla2sany/semantic/ModuleNode.java b/tlatools/src/tla2sany/semantic/ModuleNode.java index 32173854f8d7de3e902961b30d34014652e700b0..8c7b6fedacdb53095df8f604584a78abc6265fa2 100644 --- a/tlatools/src/tla2sany/semantic/ModuleNode.java +++ b/tlatools/src/tla2sany/semantic/ModuleNode.java @@ -1155,29 +1155,30 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, SemanticNode[] nodes = null; // constants - Element constants = doc.createElement("constants"); + //Element constants = doc.createElement("constants"); nodes = getConstantDecls(); for (int i=0; i<nodes.length; i++) { - constants.appendChild(nodes[i].export(doc,context)); + ret.appendChild(nodes[i].export(doc,context)); } - ret.appendChild(constants); + //ret.appendChild(constants); // variables - Element variables = doc.createElement("variables"); + //Element variables = doc.createElement("variables"); nodes = getVariableDecls(); for (int i=0; i<nodes.length; i++) { - variables.appendChild(nodes[i].export(doc,context)); + ret.appendChild(nodes[i].export(doc,context)); } - ret.appendChild(variables); + //ret.appendChild(variables); //operators - Element operators = doc.createElement("definitions"); + //Element operators = doc.createElement("definitions"); nodes = getOpDefs(); for (int i=0; i<nodes.length; i++) { - operators.appendChild(nodes[i].export(doc,context)); //was with true to expand operators + ret.appendChild(nodes[i].export(doc,context)); //was with true to expand operators } - ret.appendChild(operators); + //ret.appendChild(operators); + /* //assumptions Element assums = doc.createElement("assumptions"); nodes = getAssumptions(); @@ -1193,6 +1194,12 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, thms.appendChild(nodes[i].export(doc,context)); } ret.appendChild(thms); + */ + + nodes = getTopLevel(); + for (int i=0; i<nodes.length; i++) { + ret.appendChild(nodes[i].export(doc,context)); + } return ret; } diff --git a/tlatools/src/tla2sany/semantic/OpArgNode.java b/tlatools/src/tla2sany/semantic/OpArgNode.java index a38ff91181dd4d8b2fd338e6014a6128834d9bf0..2fc49eaab6ac23e00e36b9e6d873ac58b22f5b08 100644 --- a/tlatools/src/tla2sany/semantic/OpArgNode.java +++ b/tlatools/src/tla2sany/semantic/OpArgNode.java @@ -138,7 +138,8 @@ public class OpArgNode extends ExprOrOpArgNode { protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { Element e = doc.createElement("OpArgNode"); Element n = doc.createElement("argument"); - Element ope = op.getSymbolElement(doc, context); + //Element ope = op.getSymbolElement(doc, context); + Element ope = op.export(doc, context); n.appendChild(ope); e.appendChild(n); diff --git a/tlatools/src/tla2sany/semantic/OpDefNode.java b/tlatools/src/tla2sany/semantic/OpDefNode.java index 59ee96d97099dc368e031025ba53c81b40ea34f2..3966402f9abcff8d0878b71df1092dd58a31bbae 100644 --- a/tlatools/src/tla2sany/semantic/OpDefNode.java +++ b/tlatools/src/tla2sany/semantic/OpDefNode.java @@ -620,11 +620,52 @@ public class OpDefNode extends OpDefOrDeclNode /** * This method tests whether an operand is a legal instance of an - * operator being passed as argument to another operator + * operator being passed as argument to another operator. + * + * Bug discovered in July 2017: This method was testing if the + * arg is an operator argument that may be used as parameter + * number i of the operator described by this OpDefNode. For example, + * if + * + * D(Op(_)) == Op(42) + * + * then the argument of D must be an operator that takes a single + * expression as an argument. However, this method returned true + * if the argument is an operator like D, that takes a single + * OPERATOR as an argument. LL rewrote the code on 22 July 2017 + * to catch this case. + * + * @see IllegalOperatorTest */ private boolean matchingOpArgOperand (ExprOrOpArgNode arg, int i) { - return ((arg instanceof OpArgNode) && - params[i].getArity() == ((OpArgNode)arg).getArity()); + // Set result to true iff arg is an operator argument of the + // correct arity. + boolean result = (arg instanceof OpArgNode) && + (params[i].getArity() == ((OpArgNode)arg).getArity()) ; + + // We now must check that arg does not expect an operator as any + // of its arguments. The comments in OpArgNode.java state that + // OpArgNode.op is an OpDefNode, OpDeclNode, or FormalParamNode. Of + // those, only an OpDefNode represents an operator that can have + // an argument that is an operator. Note that an argument j of an + // OpDefNode is an operator iff its arity is 0. + if (result) { + OpArgNode argOpArg = (OpArgNode) arg ; + if (argOpArg.getOp() instanceof OpDefNode) { + OpDefNode opdefArg = (OpDefNode) argOpArg.getOp() ; + for (int j = 0; j < opdefArg.getArity() ; j++) { + if (opdefArg.getParams()[j].getArity() > 0) { + result = false ; + } + } + } + } + + return result ; + + // Code before 22 July 2017 change. + // return ((arg instanceof OpArgNode) && + // params[i].getArity() == ((OpArgNode)arg).getArity()); } /* This method shortens the match() method right after it */ @@ -1325,4 +1366,4 @@ public class OpDefNode extends OpDefOrDeclNode } return ret; } -} +} \ No newline at end of file diff --git a/tlatools/src/tla2sany/semantic/SymbolNode.java b/tlatools/src/tla2sany/semantic/SymbolNode.java index 52b914760d3c06459b7e73ff72cce4dd0ec46b90..82e7b074b918422d8cbae9684de9122342462b33 100644 --- a/tlatools/src/tla2sany/semantic/SymbolNode.java +++ b/tlatools/src/tla2sany/semantic/SymbolNode.java @@ -127,6 +127,9 @@ public abstract class SymbolNode extends LevelNode { * we need to add location and level information here. */ public Element exportDefinition(Document doc, tla2sany.xml.SymbolContext context) { + if (!context.isTop_level_entry()) + throw new IllegalArgumentException("Exporting definition "+getName()+" ref "+getNodeRef()+" twice!"); + context.resetTop_level_entry(); try { Element e = getSymbolElement(doc, context); // level diff --git a/tlatools/src/tla2sany/semantic/TheoremNode.java b/tlatools/src/tla2sany/semantic/TheoremNode.java index b90eae5dca23294c996ff1ef4c844b2e9ffd564d..a9373f030a3d047bf43e6c2a3f9cddad42cf133b 100644 --- a/tlatools/src/tla2sany/semantic/TheoremNode.java +++ b/tlatools/src/tla2sany/semantic/TheoremNode.java @@ -21,6 +21,7 @@ import util.UniqueString; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; /** * This class represents a theorem @@ -70,6 +71,10 @@ public class TheoremNode extends LevelNode { this.def = opd; this.proof = pf; if (opd != null) opd.thmOrAssump = this; + + // make sure that definition and statemtent agree + if (def != null) + assert(def.getBody() == theoremExprOrAssumeProve); } /* Returns the statement of the theorem */ @@ -370,23 +375,77 @@ public final boolean levelCheck(int iter) { if (proof != null) {proof.walkGraph(semNodesTable);} ; } + /* MR: this does not do anything public Element export(Document doc, tla2sany.xml.SymbolContext context) { Element e = super.export(doc, context); return e; } + */ + + /* MR: This is the same as SymbolNode.exportDefinition. Exports the actual theorem content, not only a reference. + */ + public Element exportDefinition(Document doc, tla2sany.xml.SymbolContext context) { + //makes sure that the we are creating an entry in the database + if (!context.isTop_level_entry()) + throw new IllegalArgumentException("Exporting theorem ref "+getNodeRef()+" twice!"); + context.resetTop_level_entry(); + + try { + Element e = getLevelElement(doc, context); + // level + try { + Element l = appendText(doc,"level",Integer.toString(getLevel())); + e.insertBefore(l,e.getFirstChild()); + } catch (RuntimeException ee) { + // not sure it is legal for a LevelNode not to have level, debug it! + } + //location + try { + Element loc = getLocationElement(doc); + e.insertBefore(loc,e.getFirstChild()); + } catch (RuntimeException ee) { + // do nothing if no location + } + return e; + } catch (RuntimeException ee) { + System.err.println("failed for node.toString(): " + toString() + "\n with error "); + ee.printStackTrace(); + throw ee; + } + } + + protected String getNodeRef() { + return "TheoremNodeRef"; + } protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { Element e = doc.createElement("TheoremNode"); - //e.appendChild(appendText(doc,"uniquename",getName().toString())); - if (getDef() != null) { - //if there is a definition, export its name too - e.appendChild(appendText(doc, "uniquename",getDef().getName().toString())); + + //the theorem name is now contained in the definition, if it exists + Node n = doc.createElement("body"); + if (def != null) { + //if there is a definition, export it too + Node d = doc.createElement("definition"); + d.appendChild(def.export(doc, context)); + e.appendChild(d); + assert( def.getBody() == getTheorem() ); //make sure theorem and definition body agree before export } - e.appendChild(getTheorem().export(doc,context)); + n.appendChild(getTheorem().export(doc,context)); + e.appendChild( n ); + if (getProof() != null) e.appendChild(getProof().export(doc,context)); if (isSuffices()) e.appendChild(doc.createElement("suffices")); return e; } + + /* overrides LevelNode.export and exports a UID reference instad of the full version*/ + public Element export(Document doc, tla2sany.xml.SymbolContext context) { + // first add symbol to context + context.put(this, doc); + Element e = doc.createElement(getNodeRef()); + e.appendChild(appendText(doc,"UID",Integer.toString(myUID))); + return e; + } } diff --git a/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java b/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java index 80021c07b027d8e5d6d191635817268b51944e16..9df5b8be650ffd0a21d0e02f0e89b79cbc34ec35 100644 --- a/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java +++ b/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java @@ -628,39 +628,40 @@ public class ThmOrAssumpDefNode extends SymbolNode } /** - * most of the information in this class is a duplication of the information - * in the TheoremNode. * - * We care to export only the name of the theorem (CHECK what happens when instantiated). */ protected String getNodeRef() { if (theorem) { assert(thmOrAssump instanceof TheoremNode); - return "TheoremNodeRef"; + return "TheoremDefRef"; } else { assert(thmOrAssump instanceof AssumeNode); - return "AssumeNodeRef"; + return "AssumeDefRef"; } } - /* appending the name is handled in Theorem.export to prevent cyclic dependencies, this is only left for documentation - public Element export(Document doc, tla2sany.xml.SymbolContext context) { - Element e = getSymbolElement(doc, context); - Node n = e.appendChild(appendText(doc, "uniquename",name.toString())); - e.appendChild(n); + protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { + assert(this.body != null); //A theorem or assumption definition without a body does not make sense. + Element e = null; + if (theorem) { + e = doc.createElement("TheoremDefNode"); + } + else { + e = doc.createElement("AssumeDef"); + } + + e.appendChild(appendText(doc, "uniquename", getName().toString() )); + e.appendChild(body.export(doc, context)); return e; - }*/ + } - protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { - // since this element doesnt seem to contain any additional information - // over theorems or assumptions, we just refer to them - //actually it does: the name is not known in a theorem - /*TODO: for now we just return an invalid element if thmOrAssump is null. if there are some valid calls with null after - bug-fixing, change the Dummy solution */ - if (thmOrAssump != null) return thmOrAssump.getLevelElement(doc,context); - if (body != null) return body.getLevelElement(doc, context); - - return doc.createElement("DummyThmOrAssumpDefNode"); //if the body element is null, export something invalid + /* overrides LevelNode.export and exports a UID reference instad of the full version*/ + public Element export(Document doc, tla2sany.xml.SymbolContext context) { + // first add symbol to context + context.put(this, doc); + Element e = doc.createElement(getNodeRef()); + e.appendChild(appendText(doc,"UID",Integer.toString(myUID))); + return e; } } diff --git a/tlatools/src/tla2sany/xml/SymbolContext.java b/tlatools/src/tla2sany/xml/SymbolContext.java index 7af4047bac18a73858cd025d78b9698a4a937afc..57d900392eafb02590b53d1840e91b8eeebc398a 100644 --- a/tlatools/src/tla2sany/xml/SymbolContext.java +++ b/tlatools/src/tla2sany/xml/SymbolContext.java @@ -1,6 +1,8 @@ package tla2sany.xml; import tla2sany.semantic.SymbolNode; +import tla2sany.semantic.TheoremNode; +import tla2sany.semantic.AssumeNode; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -16,6 +18,8 @@ import org.w3c.dom.Node; public class SymbolContext { private java.util.Map<Integer,Element> context; private java.util.Set<Integer> keys; // we need this set since the generated element might spawn new keys + private boolean top_level_entry; // used to detect if a symbol is exported twice. + // only set in put() and reset in SymbolNode.getDefinitionElement // flags list public static final int OTHER_BUG = 0; @@ -28,6 +32,7 @@ public class SymbolContext { context = new java.util.HashMap<Integer,Element>(); keys = new java.util.HashSet<Integer>(); flagArray = new boolean[1]; + top_level_entry = false; } // copy concstructor @@ -35,6 +40,7 @@ public class SymbolContext { context = other.context; keys = other.keys; flagArray = other.flagArray; + top_level_entry = other.top_level_entry; } public void setFlag(int flag) { @@ -50,6 +56,27 @@ public class SymbolContext { if (!keys.contains(k)) { // first add the key as it might be mentioned again inside the definition keys.add(k); + setTop_level_entry(); + context.put(k,nd.exportDefinition(doc,this)); + } + } + + public void put(TheoremNode nd, Document doc) { + Integer k = new Integer(nd.myUID); + if (!keys.contains(k)) { + // first add the key as it might be mentioned again inside the definition + keys.add(k); + setTop_level_entry(); + context.put(k,nd.exportDefinition(doc,this)); + } + } + + public void put(AssumeNode nd, Document doc) { + Integer k = new Integer(nd.myUID); + if (!keys.contains(k)) { + // first add the key as it might be mentioned again inside the definition + keys.add(k); + setTop_level_entry(); context.put(k,nd.exportDefinition(doc,this)); } } @@ -66,4 +93,13 @@ public class SymbolContext { } return ret; } + + public int getContextSize() { + return context.size(); + } + + public boolean isTop_level_entry() { return top_level_entry; } + public void setTop_level_entry() { top_level_entry = true; } + public void resetTop_level_entry() { top_level_entry = false; } + } diff --git a/tlatools/src/tla2sany/xml/XMLExporter.java b/tlatools/src/tla2sany/xml/XMLExporter.java index 300ad98b2ed26f0ec2c4466ab77033932247334a..6a1ba8ec40e469ae1210598af9ba70c2d27882fe 100644 --- a/tlatools/src/tla2sany/xml/XMLExporter.java +++ b/tlatools/src/tla2sany/xml/XMLExporter.java @@ -141,6 +141,7 @@ public class XMLExporter { } } else { ToolIO.out.println("Cannot find the specified file " + tla_name + "."); + return; } @@ -165,7 +166,8 @@ public class XMLExporter { //rootElement.appendChild(e); ModuleNode[] externalModules = table.getModuleNodes(); for (int j = 0; j < externalModules.length; j++) { - Element ext_e = externalModules[j].exportDefinition(doc, context); + //Element ext_e = externalModules[j].exportDefinition(doc, context); + Element ext_e = externalModules[j].export(doc, context); rootElement.appendChild(ext_e); } @@ -208,6 +210,7 @@ public class XMLExporter { } catch (SAXException se) { throw new XMLExportingException("failed to validate XML", se); } + } static void insertRootName(Document doc, Element rootElement, SpecObj spec) { diff --git a/tlatools/src/tla2sany/xml/sany.xsd b/tlatools/src/tla2sany/xml/sany.xsd index 4a8137793d8ea262dcf2e54181a9eb8bd94c6513..09d9687e8f58fa01890b09fead54018c40f26957 100644 --- a/tlatools/src/tla2sany/xml/sany.xsd +++ b/tlatools/src/tla2sany/xml/sany.xsd @@ -13,16 +13,19 @@ <xs:element name="RootModule" type="xs:string"/> <xs:element ref="context"/> <xs:sequence maxOccurs="unbounded"> - <xs:element ref="ModuleNode"/> + <xs:choice> + <xs:element ref="ModuleNode"/> + <xs:element ref="ModuleNodeRef"/> + </xs:choice> </xs:sequence> </xs:sequence> </xs:complexType> </xs:element> - + <!-- contextes and symbols references --> <!-- everything which has a uniquename is defined only once, here, and is referred by its name. this element resides within a certain scope and affects only this scope --> - <xs:element name="context"> + <xs:element name="context"> <xs:complexType> <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="entry"> @@ -37,6 +40,8 @@ <xs:element ref="TheoremNode"/> <xs:element ref="AssumeNode"/> <xs:element ref="APSubstInNode"/> + <xs:element ref="TheoremDefNode"/> + <xs:element ref="AssumeDef"/> </xs:choice> </xs:sequence> </xs:complexType> @@ -44,12 +49,14 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:group name="OpDefNodeRef"> <xs:choice> <xs:element ref="ModuleInstanceKindRef"/> <xs:element ref="UserDefinedOpKindRef"/> <xs:element ref="BuiltInKindRef"/> + <xs:element ref="TheoremDefRef"/> + <xs:element ref="AssumeDefRef"/> </xs:choice> </xs:group> <xs:element name="ModuleInstanceKindRef"> @@ -73,7 +80,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="FormalParamNodeRef"> <xs:complexType> <xs:sequence> @@ -81,7 +88,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="ModuleNodeRef"> <xs:complexType> <xs:sequence> @@ -89,7 +96,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="TheoremNodeRef"> <xs:complexType> <xs:sequence> @@ -97,7 +104,15 @@ </xs:sequence> </xs:complexType> </xs:element> - + + <xs:element name="TheoremDefRef"> + <xs:complexType> + <xs:sequence> + <xs:element ref="UID"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="AssumeNodeRef"> <xs:complexType> <xs:sequence> @@ -105,7 +120,15 @@ </xs:sequence> </xs:complexType> </xs:element> - + + <xs:element name="AssumeDefRef"> + <xs:complexType> + <xs:sequence> + <xs:element ref="UID"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="OpDeclNodeRef"> <xs:complexType> <xs:sequence> @@ -113,7 +136,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- A level node is a wrapper around all other nodes containing location and level information --> <xs:group name="node"> <xs:sequence> @@ -121,7 +144,7 @@ <xs:element minOccurs="0" ref="level"/> </xs:sequence> </xs:group> - + <!-- ExprNode is a grouping of the following nodes --> <xs:group name="ExprNode"> <xs:choice> @@ -133,9 +156,11 @@ <xs:element ref="OpApplNode"/> <xs:element ref="StringNode"/> <xs:element ref="SubstInNode"/> + <xs:element ref="TheoremDefRef"/> + <xs:element ref="AssumeDefRef"/> </xs:choice> </xs:group> - + <!-- Location --> <xs:element name="location"> <xs:complexType> @@ -165,7 +190,7 @@ <xs:element name="filename" type="xs:string"/> <xs:element name="begin" type="xs:integer"/> <xs:element name="end" type="xs:integer"/> - + <!-- Level --> <xs:element name="level"> <xs:simpleType> @@ -173,16 +198,16 @@ <xs:minInclusive value="0"/> <xs:maxInclusive value="3"/> <!-- - ActionLevel = 2 - ConstantLevel = 0 - TemporalLevel = 3 - VariableLevel = 1 + ConstantLevel = 0 (Constant) + VariableLevel = 1 (State) + ActionLevel = 2 (Transition) + TemporalLevel = 3 (Temporal) --> </xs:restriction> </xs:simpleType> </xs:element> - - + + <!-- Instantiations and substitutions --> <!-- Operator substitution? --> <!-- A list of substitutions and the expression they apply to --> @@ -200,28 +225,15 @@ <xs:element name="body"> <xs:complexType> <xs:choice> - <xs:element ref="APSubstInNode"/> - <xs:element ref="AssumeProveNode"/> - <xs:element ref="DefStepNode"/> <xs:group ref="ExprNode"/> - <xs:element ref="OpArgNode"/> - <xs:element ref="InstanceNode"/> - <xs:element ref="NewSymbNode"/> - <xs:group ref="ProofNode"/> - <xs:element ref="FormalParamNodeRef"/> - <xs:element ref="ModuleNodeRef"/> - <xs:element ref="OpDeclNodeRef"/> - <xs:group ref="OpDefNodeRef"/> - <xs:element ref="AssumeNodeRef"/> - <xs:element ref="TheoremNodeRef"/> - <xs:element ref="UseOrHideNode"/> + <xs:group ref="AssumeProveLike"/> </xs:choice> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> - + <!-- Expr substitution --> <!-- A list of substitutions and the expression they apply to --> <xs:element name="SubstInNode"> @@ -235,7 +247,7 @@ </xs:sequence> </xs:complexType> </xs:element> - <xs:element name="body"> + <xs:element name="body"> <xs:complexType> <xs:sequence> <xs:group ref="ExprNode"/> @@ -245,18 +257,21 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- Module substitution --> <!-- A list of substitutions, the name of the instance and list of instance params --> <xs:element name="InstanceNode"> <xs:complexType> <xs:sequence> <xs:group ref="node"/> - <xs:element ref="uniquename"/> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="1" ref="uniquename"/> + </xs:sequence> + <xs:element name="module" type="xs:string"/> <xs:element name="substs"> <xs:complexType> <xs:sequence> - <xs:element maxOccurs="unbounded" ref="Subst"/> + <xs:element minOccurs="0" maxOccurs="unbounded" ref="Subst"/> </xs:sequence> </xs:complexType> </xs:element> @@ -270,7 +285,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- Mapping from the OpDeclNode to either of the others --> <xs:element name="Subst"> <xs:complexType> @@ -283,34 +298,72 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- This is a unique name in the hierarchy --> <xs:element name="uniquename" type="xs:string"/> - + <!-- This is a unique ID in the system --> <xs:element name="UID" type="xs:integer"/> - + <!-- Assumptions and Theorems --> <!-- An assumption named by name and described by ExprNode --> <xs:element name="AssumeNode"> <xs:complexType> <xs:sequence> <xs:group ref="node"/> - <xs:group ref="ExprNode"/> + <xs:element minOccurs="0" name="definition"> + <xs:complexType> + <xs:choice> + <xs:element ref="AssumeDefRef"/> + </xs:choice> + </xs:complexType> + </xs:element> + <xs:element name="body"> <!-- Invariant: definition body is assumption body, if definition exists --> + <xs:complexType> + <xs:choice> + <xs:group ref="ExprNode"/> + <xs:element ref="AssumeDef"/> + </xs:choice> + </xs:complexType> + </xs:element> + <!-- reference to AssumeDef included in ExprNode --> </xs:sequence> </xs:complexType> </xs:element> - - <!-- Similar to assumption but may contain a proof. It can also refer to a proof step --> - <xs:element name="TheoremNode"> + + <xs:element name="AssumeDef"> <xs:complexType> <xs:sequence> <xs:group ref="node"/> - <xs:element minOccurs="0" ref="uniquename"/> + <xs:element name="uniquename" type="xs:string"/> <xs:choice> <xs:group ref="ExprNode"/> - <xs:element ref="AssumeProveNode"/> + <xs:group ref="AssumeProveLike"/> </xs:choice> + </xs:sequence> + </xs:complexType> + </xs:element> + + <!-- Similar to assumption but may contain a proof. It can also refer to a proof step --> + <xs:element name="TheoremNode"> + <xs:complexType> + <xs:sequence> + <xs:group ref="node"/> + <xs:element minOccurs="0" name="definition"> + <xs:complexType> + <xs:choice> + <xs:element ref="TheoremDefRef"/> + </xs:choice> + </xs:complexType> + </xs:element> + <xs:element name="body"> <!-- Invariant: definition body is theorem body, if definition exists --> + <xs:complexType> + <xs:choice> + <xs:group ref="ExprNode"/> + <xs:group ref="AssumeProveLike"/> + </xs:choice> + </xs:complexType> + </xs:element> <xs:group minOccurs="0" ref="ProofNode"/> <xs:element minOccurs="0" ref="suffices"/> </xs:sequence> @@ -319,37 +372,57 @@ <xs:element name="suffices"> <xs:complexType/> </xs:element> - + + <xs:element name="TheoremDefNode"> + <xs:complexType> + <xs:sequence> + <xs:group ref="node"/> + <xs:element name="uniquename" type="xs:string"/> + <xs:choice> + <xs:group ref="ExprNode"/> + <xs:group ref="AssumeProveLike"/> + </xs:choice> + </xs:sequence> + </xs:complexType> + </xs:element> + <!-- A non empty list of assumptions and a prove expressions with possible suffices and boxed --> <xs:element name="AssumeProveNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element name="assumes"> <xs:complexType> <xs:choice maxOccurs="unbounded"> - <xs:element ref="AssumeProveNode"/> + <xs:group ref="AssumeProveLike"/> <xs:group ref="ExprNode"/> <xs:element ref="NewSymbNode"/> </xs:choice> </xs:complexType> - </xs:element> + </xs:element> <xs:element name="prove"> - <xs:complexType> + <xs:complexType> <xs:group ref="ExprNode"/> </xs:complexType> - </xs:element> + </xs:element> <xs:element minOccurs="0" ref="suffices"/> <xs:element minOccurs="0" ref="boxed"/> </xs:sequence> </xs:complexType> </xs:element> - + + <xs:group name="AssumeProveLike"> + <xs:choice> + <xs:element ref="AssumeProveNode"/> + <xs:element ref="APSubstInNode"/> + </xs:choice> + </xs:group> + <!-- Signals a boxed scope (not clear if it is implemented in SANY) --> <xs:element name="boxed"> <xs:complexType/> </xs:element> - + <!-- Represents a new declaration + possible domain --> <xs:element name="NewSymbNode"> <xs:complexType> @@ -360,8 +433,8 @@ </xs:sequence> </xs:complexType> </xs:element> - - + + <!-- Operator definitions --> <!-- OpDefNode is classified according to its kinds (is NumberedProofStepKind relevant, since it is never referenced in by or use?) --> <xs:group name="OpDefNode"> @@ -371,7 +444,7 @@ <xs:element ref="BuiltInKind"/> </xs:choice> </xs:group> - + <!-- Reprensets the name of an instantiated module --> <xs:element name="ModuleInstanceKind"> <xs:complexType> @@ -381,12 +454,12 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- An operator and its definition, arity and list of arguments --> <xs:element name="UserDefinedOpKind"> <xs:complexType> - <xs:sequence> - <xs:group ref="node"/> + <xs:sequence> + <xs:group ref="node"/> <xs:element ref="uniquename"/> <xs:element ref="arity"/> <xs:element name="body"> @@ -405,13 +478,13 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="recursive"> <xs:complexType/> </xs:element> - + <xs:element name="arity" type="xs:integer"/> - + <xs:element name="leibnizparam"> <xs:complexType> <xs:sequence> @@ -420,16 +493,16 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="leibniz"> <xs:complexType/> </xs:element> - + <!-- This node represents all builtins, including quantifiers, set operatios, etc. --> <xs:element name="BuiltInKind"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element ref="uniquename"/> <xs:element ref="arity"/> <xs:element minOccurs="0" name="params"> @@ -447,12 +520,23 @@ <xs:complexType> <xs:sequence> <xs:group ref="node"/> - <xs:element ref="uniquename"/> - <xs:element ref="arity"/> + <xs:element name="argument"> + <xs:complexType> + <xs:choice> + <xs:element ref="FormalParamNodeRef"/> + <xs:element ref="ModuleNodeRef"/> + <xs:element ref="OpDeclNodeRef"/> + <xs:group ref="OpDefNodeRef"/> + <xs:element ref="TheoremNodeRef"/> + <xs:element ref="AssumeNodeRef"/> + <xs:element ref="APSubstInNode"/> + </xs:choice> + </xs:complexType> + </xs:element> </xs:sequence> </xs:complexType> </xs:element> - + <!-- Represents params of user definitions --> <xs:element name="FormalParamNode"> <xs:complexType> @@ -463,7 +547,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- Represents constants, variables, bound variables and new symbols --> <xs:element name="OpDeclNode"> <xs:complexType> @@ -484,12 +568,12 @@ <xs:enumeration value="28"/> <!--NewTemporalKind--> </xs:restriction> </xs:simpleType> - </xs:element> + </xs:element> </xs:sequence> </xs:complexType> </xs:element> - - + + <!-- Proofs --> <!-- The first three correspond to LeafProofNode, the last to NonLeafProofNode --> <xs:group name="ProofNode"> @@ -502,7 +586,7 @@ <xs:element name="steps"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:sequence maxOccurs="unbounded"> <xs:group ref="step"/> </xs:sequence> @@ -510,21 +594,21 @@ </xs:complexType> </xs:element> </xs:choice> - </xs:sequence> + </xs:sequence> </xs:group> - + <xs:element name="omitted"> <xs:complexType> <xs:group ref="node"/> </xs:complexType> </xs:element> - + <xs:element name="obvious"> <xs:complexType> <xs:group ref="node"/> </xs:complexType> </xs:element> - + <!-- Facts and Defs are defined to be of these kinds according to comments in the Java code, seems step names cannot be referenced? --> <xs:element name="by"> <xs:complexType> @@ -544,20 +628,20 @@ <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element ref="UserDefinedOpKindRef"/> <xs:element ref="ModuleInstanceKindRef"/> - <xs:element ref="TheoremNodeRef"/> - <xs:element ref="AssumeNodeRef"/> + <xs:element ref="TheoremDefRef"/> + <xs:element ref="AssumeDefRef"/> </xs:choice> </xs:complexType> - </xs:element> + </xs:element> <xs:element minOccurs="0" ref="only"/> </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="only"> <xs:complexType/> </xs:element> - + <!-- Steps are defined to be of this types according to comments in the Java code --> <xs:group name="step"> <xs:choice> @@ -568,7 +652,7 @@ <xs:element ref="TheoremNode"/> <!-- in case of unnamed theorem --> </xs:choice> </xs:group> - + <xs:element name="DefStepNode"> <xs:complexType> <xs:sequence> @@ -576,10 +660,10 @@ <xs:group maxOccurs="unbounded" ref="OpDefNodeRef"/> </xs:sequence> </xs:complexType> -<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) -when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> +<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) +when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> </xs:element> - + <xs:element name="UseOrHideNode"> <xs:complexType> <xs:sequence> @@ -605,19 +689,19 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:element> <xs:element minOccurs="0" ref="only"/> <xs:element minOccurs="0" ref="hide"/> -<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) -when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> +<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) +when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="hide"> <xs:complexType/> </xs:element> - + <xs:element name="stepnumber" type="xs:string"/> - - + + <!-- Expressions --> <!-- Occurrences of @ by supplying the innermost enclosing $Except and the innermost $Pair containing this @ --> <xs:element name="AtNode"> @@ -635,7 +719,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- A decimal number (read Java code and java API for more information)--> <xs:element name="DecimalNode"> <xs:complexType> @@ -645,7 +729,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- Represents a labelled expression nm(x,y): (ExprNode|AssumeProveNode) --> <xs:element name="LabelNode"> <xs:complexType> @@ -654,10 +738,10 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s <xs:element ref="uniquename"/> <xs:element ref="arity"/> <xs:element name="body"> - <xs:complexType> + <xs:complexType> <xs:choice> <xs:group ref="ExprNode"/> - <xs:element ref="AssumeProveNode"/> + <xs:group ref="AssumeProveLike"/> </xs:choice> </xs:complexType> </xs:element> @@ -671,14 +755,14 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- A list of definitions and the body --> <xs:element name="LetInNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element name="body"> - <xs:complexType> + <xs:complexType> <xs:group ref="ExprNode"/> </xs:complexType> </xs:element> @@ -696,7 +780,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- An integer number --> <xs:element name="NumeralNode"> <xs:complexType> @@ -706,7 +790,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- An string --> <xs:element name="StringNode"> <xs:complexType> @@ -716,12 +800,12 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- A general node for applications. FIrst the operator and then the operands --> <xs:element name="OpApplNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element name="operator"> <xs:complexType> <xs:choice> @@ -753,7 +837,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="unbound"> <xs:complexType> <xs:sequence> @@ -762,7 +846,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="bound"> <xs:complexType> <xs:sequence> @@ -777,52 +861,25 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s <xs:element name="tuple"> <xs:complexType/> </xs:element> - + <!-- Module(constants,variables,operators,assumptions,theorems) --> <xs:element name="ModuleNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element ref="uniquename"/> - <xs:element name="constants"> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" ref="OpDeclNodeRef"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="variables"> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" ref="OpDeclNodeRef"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="definitions"> - <xs:complexType> - <xs:sequence> - <xs:group minOccurs="0" maxOccurs="unbounded" ref="OpDefNodeRef"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="assumptions"> - <xs:complexType> - <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element ref="AssumeNodeRef"/> - <xs:element ref="AssumeNode"/> - </xs:choice> - </xs:complexType> - </xs:element> - <xs:element name="theorems"> - <xs:complexType> - <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element ref="TheoremNodeRef"/> - <xs:element ref="TheoremNode"/> - </xs:choice> - </xs:complexType> - </xs:element> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:choice> + <xs:element ref="OpDeclNodeRef"/> + <xs:group ref="OpDefNodeRef"/> + <xs:element ref="AssumeNodeRef"/> + <xs:element ref="InstanceNode"/> + <xs:element ref="UseOrHideNode"/> + <xs:element ref="TheoremNodeRef"/> + </xs:choice> + </xs:sequence> </xs:sequence> </xs:complexType> </xs:element> - + </xs:schema> diff --git a/tlatools/src/tla2tex/BuiltInSymbols.java b/tlatools/src/tla2tex/BuiltInSymbols.java index 55f5f07afb08e540e91bdf5f019cdc75f120aceb..83c3ccfdd4f8b952a91e6e52fd8d9587ed33a244 100644 --- a/tlatools/src/tla2tex/BuiltInSymbols.java +++ b/tlatools/src/tla2tex/BuiltInSymbols.java @@ -145,7 +145,7 @@ public final class BuiltInSymbols } ; public static boolean IsStringChar(char ch) - { return stringCharTable.containsKey("" + ch) ; + { return stringCharTable.containsKey(String.valueOf(ch)) ; } ; public static boolean CanPrecedeLabel(String str) { @@ -164,7 +164,7 @@ public final class BuiltInSymbols + "0123456789" ; int n = 0 ; while (n < legalChars.length()) - { stringCharTable.put("" + legalChars.charAt(n), nullString); + { stringCharTable.put(String.valueOf(legalChars.charAt(n)), nullString); n = n + 1 ; } } ; diff --git a/tlatools/src/tlc2/TLC.java b/tlatools/src/tlc2/TLC.java index 5366a3de3bbb220f93a48b179aaf350f87a73e1f..7c1d147c5a98924cf7b3009489b62ec8a41c5bf4 100644 --- a/tlatools/src/tlc2/TLC.java +++ b/tlatools/src/tlc2/TLC.java @@ -7,10 +7,8 @@ package tlc2; import java.io.BufferedWriter; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; -import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Enumeration; @@ -40,6 +38,7 @@ import util.FileUtil; import util.FilenameToStream; import util.MailSender; import util.SimpleFilenameToStream; +import util.TLCRuntime; import util.ToolIO; import util.UniqueString; @@ -171,6 +170,9 @@ public class TLC * Defaults to no coverage if not specified * o -continue: continue running even when invariant is violated * Defaults to stop at the first violation if not specified + * o -lncheck: Check liveness properties at different times + * of model checking. + * Defaults to false increasing the overall model checking time. * o -nowarning: disable all the warnings * Defaults to report warnings if not specified * o -fp num: use the num'th irreducible polynomial from the list @@ -191,7 +193,7 @@ public class TLC * default: 1000000 * */ - public static void main(String[] args) throws UnknownHostException, FileNotFoundException + public static void main(String[] args) throws Exception { TLC tlc = new TLC(); @@ -270,7 +272,7 @@ public class TLC } else if (args[index].equals("-gzip")) { index++; - TLCGlobals.useGZIP = false; + TLCGlobals.useGZIP = true; } else if (args[index].equals("-terse")) { index++; @@ -295,7 +297,19 @@ public class TLC { printUsage(); return false; - } else if (args[index].equals("-config")) + } else if (args[index].equals("-lncheck")) + { + index++; + if (index < args.length) + { + TLCGlobals.lnCheck = args[index].toLowerCase(); + index++; + } else + { + printErrorMsg("Error: expect a strategy such as final for -lncheck option."); + return false; + } + } else if (args[index].equals("-config")) { index++; if (index < args.length) @@ -757,6 +771,22 @@ public class TLC } FP64.Init(fpIndex); + + final TLCRuntime tlcRuntime = TLCRuntime.getInstance(); + final long offHeapMemory = tlcRuntime.getNonHeapPhysicalMemory() / 1024L / 1024L; + final String arch = tlcRuntime.getArchitecture().name(); + + final Runtime runtime = Runtime.getRuntime(); + final long heapMemory = runtime.maxMemory() / 1024L / 1024L; + final String cores = Integer.toString(runtime.availableProcessors()); + + final String vendor = System.getProperty("java.vendor"); + final String version = System.getProperty("java.version"); + + final String osName = System.getProperty("os.name"); + final String osVersion = System.getProperty("os.version"); + final String osArch = System.getProperty("os.arch"); + // Start checking: if (isSimulate) { @@ -770,7 +800,10 @@ public class TLC { rng.setSeed(seed, aril); } - MP.printMessage(EC.TLC_MODE_SIMU, String.valueOf(seed)); + MP.printMessage(EC.TLC_MODE_SIMU, + new String[] { String.valueOf(seed), String.valueOf(TLCGlobals.getNumWorkers()), + TLCGlobals.getNumWorkers() == 1 ? "" : "s", cores, osName, osVersion, osArch, vendor, + version, arch, Long.toString(heapMemory), Long.toString(offHeapMemory) }); Simulator simulator = new Simulator(mainFile, configFile, null, deadlock, traceDepth, traceNum, rng, seed, true, resolver, specObj); TLCGlobals.simulator = simulator; @@ -780,17 +813,20 @@ public class TLC simulator.simulate(); } else { - // model checking - MP.printMessage(EC.TLC_MODE_MC, new String[] { String.valueOf(TLCGlobals.getNumWorkers()), - TLCGlobals.getNumWorkers() == 1 ? "" : "s" }); - - AbstractChecker mc = null; + final String[] parameters = new String[] { String.valueOf(TLCGlobals.getNumWorkers()), + TLCGlobals.getNumWorkers() == 1 ? "" : "s", cores, osName, osVersion, osArch, vendor, + version, arch, Long.toString(heapMemory), Long.toString(offHeapMemory) }; + + // model checking + AbstractChecker mc = null; if (TLCGlobals.DFIDMax == -1) { + MP.printMessage(EC.TLC_MODE_MC, parameters); mc = new ModelChecker(mainFile, configFile, dumpFile, asDot, deadlock, fromChkpt, resolver, specObj, fpSetConfiguration); modelCheckerMXWrapper = new ModelCheckerMXWrapper((ModelChecker) mc, this); } else { + MP.printMessage(EC.TLC_MODE_MC_DFS, parameters); mc = new DFIDModelChecker(mainFile, configFile, dumpFile, asDot, deadlock, fromChkpt, true, resolver, specObj); } TLCGlobals.mainChecker = mc; @@ -830,8 +866,11 @@ public class TLC } } modelCheckerMXWrapper.unregister(); + // In tool mode print runtime in milliseconds, in non-tool mode print human + // readable runtime (days, hours, minutes, ...). + final long runtime = System.currentTimeMillis() - startTime; MP.printMessage(EC.TLC_FINISHED, - convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime)); + TLCGlobals.tool ? Long.toString(runtime) + "ms" : convertRuntimeToHumanReadable(runtime)); MP.flush(); } } diff --git a/tlatools/src/tlc2/TLCGlobals.java b/tlatools/src/tlc2/TLCGlobals.java index ac7fefb907e26338eaf8ee5266b7eb58c74005c8..1c05c17461d08af16d8f8108805fbccca03f78f6 100644 --- a/tlatools/src/tlc2/TLCGlobals.java +++ b/tlatools/src/tlc2/TLCGlobals.java @@ -22,7 +22,7 @@ public class TLCGlobals { // The current version of TLC - public static String versionOfTLC = "Version 2.09 of 10 March 2017"; + public static String versionOfTLC = "Version 2.10 of 28 September 2017"; // The bound for set enumeration, used for pretty printing public static int enumBound = 2000; @@ -47,6 +47,12 @@ public class TLCGlobals * (see ILiveCheck#addNextState) and thus part of safety checking.. */ public static double livenessRatio = 0.2d; + + public static String lnCheck = "default"; + + public static boolean doLiveness() { + return !lnCheck.equals("final"); + } public synchronized static void setNumWorkers(int n) { @@ -148,7 +154,7 @@ public class TLCGlobals public static boolean useView = false; // The flag to control if gzip is applied to Value input/output stream. - public static boolean useGZIP = true; + public static boolean useGZIP = false; // The tool id number for TLC2. public static int ToolId = FrontEnd.getToolId(); diff --git a/tlatools/src/tlc2/module/Integers.java b/tlatools/src/tlc2/module/Integers.java index 4bf47492a1a66a7d8d461a8928aadad44b267fbb..aa12d836990a6f06faabb69e6733d2fc618bff05 100644 --- a/tlatools/src/tlc2/module/Integers.java +++ b/tlatools/src/tlc2/module/Integers.java @@ -90,7 +90,7 @@ public class Integers extends UserObj implements ValueConstants if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<=", "integer", - Value.ppr(x.toString()) }); + Value.ppr(y.toString()) }); } return (((IntValue) x).val <= ((IntValue) y).val) ? ValTrue : ValFalse; @@ -106,7 +106,7 @@ public class Integers extends UserObj implements ValueConstants if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">", "integer", - Value.ppr(x.toString()) }); + Value.ppr(y.toString()) }); } return (((IntValue) x).val > ((IntValue) y).val) ? ValTrue : ValFalse; @@ -122,7 +122,7 @@ public class Integers extends UserObj implements ValueConstants if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">=", "integer", - Value.ppr(x.toString()) }); + Value.ppr(y.toString()) }); } return (((IntValue) x).val >= ((IntValue) y).val) ? ValTrue : ValFalse; diff --git a/tlatools/src/tlc2/module/Naturals.java b/tlatools/src/tlc2/module/Naturals.java index 14a912c800a06970e62c37c2fde2266be5a2f9ab..233c97abf52fb621a8dc6d91a2ba4ae7cf3d2de2 100644 --- a/tlatools/src/tlc2/module/Naturals.java +++ b/tlatools/src/tlc2/module/Naturals.java @@ -95,7 +95,7 @@ public class Naturals extends UserObj implements ValueConstants if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<", "integer", - Value.ppr(x.toString()) }); + Value.ppr(y.toString()) }); } return (((IntValue) x).val < ((IntValue) y).val) ? ValTrue : ValFalse; @@ -111,7 +111,7 @@ public class Naturals extends UserObj implements ValueConstants if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<=", "integer", - Value.ppr(x.toString()) }); + Value.ppr(y.toString()) }); } return (((IntValue) x).val <= ((IntValue) y).val) ? ValTrue : ValFalse; diff --git a/tlatools/src/tlc2/module/Sequences.java b/tlatools/src/tlc2/module/Sequences.java index 8f6d4c1ec3aafcb8d62932d8176a571df1f8a66a..58eb10489b556708d216a74f3bd413b7a2855771 100644 --- a/tlatools/src/tlc2/module/Sequences.java +++ b/tlatools/src/tlc2/module/Sequences.java @@ -300,12 +300,12 @@ public class Sequences extends UserObj implements ValueConstants if (beg < 1 || beg > len) { - throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "second", "SubSeq", + throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "second", "SubSeq", "first", Value.ppr(s.toString()), Value.ppr(m.toString()) }); } if (end < 1 || end > len) { - throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "third", "SubSeq", + throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "third", "SubSeq", "first", Value.ppr(s.toString()), Value.ppr(n.toString()) }); } diff --git a/tlatools/src/tlc2/output/EC.java b/tlatools/src/tlc2/output/EC.java index 4d72b1b668913b246f3e226535f74889300eec70..9af7acc06458b578801476e330aed3020afe529c 100644 --- a/tlatools/src/tlc2/output/EC.java +++ b/tlatools/src/tlc2/output/EC.java @@ -1,260 +1,267 @@ -package tlc2.output; - - -/** - * Interface containing the error code constants - * @author Simon Zambrovski - * @version $Id$ - */ -public interface EC -{ - // Check and CheckImpl - // check if the TLC option is the same for params - public static final int CHECK_FAILED_TO_CHECK = 3000; - public static final int CHECK_COULD_NOT_READ_TRACE = 3001; - - public static final int CHECK_PARAM_EXPECT_CONFIG_FILENAME = 3100; - public static final int CHECK_PARAM_USAGE = 3101; - public static final int CHECK_PARAM_MISSING_TLA_MODULE = 3102; - public static final int CHECK_PARAM_NEED_TO_SPECIFY_CONFIG_DIR = 3103; - public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED = 3104; - public static final int CHECK_PARAM_WORKER_NUMBER_TOO_SMALL = 3105; - public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED2 = 3106; - public static final int CHECK_PARAM_DEPTH_REQUIRED = 3107; - public static final int CHECK_PARAM_DEPTH_REQUIRED2 = 3108; - public static final int CHECK_PARAM_TRACE_REQUIRED = 3109; - public static final int CHECK_PARAM_COVREAGE_REQUIRED = 3110; - public static final int CHECK_PARAM_COVREAGE_REQUIRED2 = 3111; - public static final int CHECK_PARAM_COVREAGE_TOO_SMALL = 3112; - public static final int CHECK_PARAM_UNRECOGNIZED = 3113; - public static final int CHECK_PARAM_TOO_MANY_INPUT_FILES = 3114; - - - public final static int SANY_PARSER_CHECK_1 = 4000; - public final static int SANY_PARSER_CHECK_2 = 4001; - public final static int SANY_PARSER_CHECK_3 = 4002; - - public static final int UNKNOWN = -1; // TODO remove all these - public final static int UNIT_TEST = -123456; - /** - * A feature of TLA+/TLC is not supported by TLC's current mode. E.g. - * TLCGet/TLCSet operator cannot be used in distributed TLC. - */ - public static final int TLC_FEATURE_UNSUPPORTED = 2156; - - public static final int GENERAL = 1000; - public static final int SYSTEM_OUT_OF_MEMORY = 1001; - public static final int SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT = 1002; - public static final int SYSTEM_STACK_OVERFLOW = 1005; - - public static final int WRONG_COMMANDLINE_PARAMS_SIMULATOR = 1101; - public static final int WRONG_COMMANDLINE_PARAMS_TLC = 1102; - - public static final int TLC_PP_PARSING_VALUE = 2000; - public static final int TLC_PP_FORMATING_VALUE = 2001; - - public static final int TLC_METADIR_EXISTS = 2100; - public static final int TLC_METADIR_CAN_NOT_BE_CREATED = 2101; - public static final int TLC_INITIAL_STATE = 2102; - public static final int TLC_NESTED_EXPRESSION = 2103; - public static final int TLC_ASSUMPTION_FALSE = 2104; - public static final int TLC_ASSUMPTION_EVALUATION_ERROR = 2105; - public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL = 2106; - - public static final int TLC_INVARIANT_VIOLATED_INITIAL = 2107; - public static final int TLC_PROPERTY_VIOLATED_INITIAL = 2108; - public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT = 2109; - public static final int TLC_INVARIANT_VIOLATED_BEHAVIOR = 2110; - public static final int TLC_INVARIANT_EVALUATION_FAILED = 2111; - public static final int TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR = 2112; - public static final int TLC_ACTION_PROPERTY_EVALUATION_FAILED = 2113; - public static final int TLC_DEADLOCK_REACHED = 2114; - - public static final int TLC_STATES_AND_NO_NEXT_ACTION = 2115; - public static final int TLC_TEMPORAL_PROPERTY_VIOLATED = 2116; - public static final int TLC_FAILED_TO_RECOVER_NEXT = 2117; - public static final int TLC_NO_STATES_SATISFYING_INIT = 2118; - public static final int TLC_STRING_MODULE_NOT_FOUND = 2119; - - public static final int TLC_ERROR_STATE = 2120; - public static final int TLC_BEHAVIOR_UP_TO_THIS_POINT = 2121; - public static final int TLC_BACK_TO_STATE = 2122; // The "back to state" message from a liveness-error trace. - public static final int TLC_FAILED_TO_RECOVER_INIT = 2123; - public static final int TLC_REPORTER_DIED = 2124; - - public static final int SYSTEM_ERROR_READING_POOL = 2125; - public static final int SYSTEM_CHECKPOINT_RECOVERY_CORRUPT = 2126; - public static final int SYSTEM_ERROR_WRITING_POOL = 2127; - public static final int SYSTEM_ERROR_CLEANING_POOL = 2270; - public static final int SYSTEM_INDEX_ERROR = 2134; - public static final int SYSTEM_STREAM_EMPTY = 2135; - public static final int SYSTEM_FILE_NULL = 2137; - public static final int SYSTEM_INTERRUPTED = 2138; - public static final int SYSTEM_UNABLE_NOT_RENAME_FILE = 2160; - public static final int SYSTEM_DISK_IO_ERROR_FOR_FILE = 2161; - public static final int SYSTEM_METADIR_EXISTS = 2162; - public static final int SYSTEM_METADIR_CREATION_ERROR = 2163; - public static final int SYSTEM_UNABLE_TO_OPEN_FILE = 2167; - public static final int TLC_BUG = 2128; // TODO Bad description - - public static final int SYSTEM_DISKGRAPH_ACCESS = 2129; // TODO refactor - - public static final int TLC_AAAAAAA = 2130; - public static final int TLC_REGISTRY_INIT_ERROR = 2131; - public static final int TLC_CHOOSE_ARGUMENTS_WRONG = 2164; - public static final int TLC_CHOOSE_UPPER_BOUND = 2165; - - public static final int TLC_VALUE_ASSERT_FAILED = 2132; - public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE = 2154; - public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED = 2168; - - public static final int TLC_FP_NOT_IN_SET = 2133; - public static final int TLC_FP_VALUE_ALREADY_ON_DISK = 2166; - - public static final int TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT = 2159; - public static final int TLC_PARAMETER_MUST_BE_POSTFIX = 2136; - public static final int TLC_COULD_NOT_DETERMINE_SUBSCRIPT = 2139; - public static final int TLC_SUBSCRIPT_CONTAIN_NO_STATE_VAR = 2140; - public static final int TLC_WRONG_TUPLE_FIELD_NAME = 2141; - public static final int TLC_WRONG_RECORD_FIELD_NAME = 2142; - public static final int TLC_UNCHANGED_VARIABLE_CHANGED = 2143; - public static final int TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD = 2144; - - public static final int TLC_MODULE_TLCGET_UNDEFINED = 2145; - /** Attempted to compare %1% with the value\n%2% */ - public static final int TLC_MODULE_COMPARE_VALUE = 2155; - public static final int TLC_MODULE_CHECK_MEMBER_OF = 2158; - public static final int TLC_MODULE_TRANSITIVE_CLOSURE = 2157; - /** The %1% argument of %2% should be a %3%, but instead it is:<br>%4% */ - public static final int TLC_MODULE_ARGUMENT_ERROR = 2169; - /** Simon used an argument like "\bn apple" to TLC_MODULE_ARGUMENT_ERROR to turn - * an "a" into an "an". This doesn't work on the Toolbox's console. Hence, LL added - * the following message type on 21 May 2012. */ - public static final int TLC_MODULE_ARGUMENT_ERROR_AN = 2266; - public static final int TLC_ARGUMENT_MISMATCH = 2170; - public static final int TLC_PARSING_FAILED2 = 2171; - public static final int TLC_PARSING_FAILED = 3002; - public static final int TLC_TOO_MNY_POSSIBLE_STATES = 2172; - public static final int TLC_ERROR_REPLACING_MODULES = 2173; - public static final int SYSTEM_ERROR_READING_STATES = 2174; - public static final int SYSTEM_ERROR_WRITING_STATES = 2175; - public static final int TLC_MODULE_APPLYING_TO_WRONG_VALUE = 2176; - public static final int TLC_MODULE_BAG_UNION1 = 2177; - public static final int TLC_MODULE_OVERFLOW = 2178; - public static final int TLC_MODULE_DIVISION_BY_ZERO = 2179; - public static final int TLC_MODULE_NULL_POWER_NULL = 2180; - public static final int TLC_MODULE_COMPUTING_CARDINALITY = 2181; - public static final int TLC_MODULE_EVALUATING = 2182; - /** The %1% argument of %2% must be in the domain of its first argument:<br>%3%<br>, but instead it is<br>%4% */ - public static final int TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN = 2183; - public static final int TLC_MODULE_APPLY_EMPTY_SEQ = 2184; - - public static final int TLC_STARTING = 2185; - public static final int TLC_FINISHED = 2186; - - // distributed TLC - - public static final int TLC_DISTRIBUTED_SERVER_RUNNING = 7000; - public static final int TLC_DISTRIBUTED_WORKER_REGISTERED = TLC_DISTRIBUTED_SERVER_RUNNING + 1; - public static final int TLC_DISTRIBUTED_WORKER_DEREGISTERED = TLC_DISTRIBUTED_WORKER_REGISTERED + 1; - public static final int TLC_DISTRIBUTED_WORKER_STATS = TLC_DISTRIBUTED_WORKER_DEREGISTERED + 1; - public static final int TLC_DISTRIBUTED_SERVER_NOT_RUNNING = TLC_DISTRIBUTED_WORKER_STATS + 1; - public static final int TLC_DISTRIBUTED_VM_VERSION = TLC_DISTRIBUTED_SERVER_NOT_RUNNING + 1; - public static final int TLC_DISTRIBUTED_WORKER_LOST = TLC_DISTRIBUTED_VM_VERSION + 1; - public static final int TLC_DISTRIBUTED_EXCEED_BLOCKSIZE = TLC_DISTRIBUTED_WORKER_LOST + 1; - public static final int TLC_DISTRIBUTED_SERVER_FPSET_WAITING = TLC_DISTRIBUTED_EXCEED_BLOCKSIZE + 1; - public static final int TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED = TLC_DISTRIBUTED_SERVER_FPSET_WAITING + 1; - public static final int TLC_DISTRIBUTED_SERVER_FINISHED = TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED + 1; - - // errors during parsing of the model configuration - - public static final int CFG_ERROR_READING_FILE = 5001; - public static final int CFG_GENERAL = 5002; - public static final int CFG_MISSING_ID = 5003; - public static final int CFG_TWICE_KEYWORD = 5004; - public static final int CFG_EXPECT_ID = 5005; - public static final int CFG_EXPECTED_SYMBOL = 5006; - public static final int TLC_MODE_MC = 2187; - public static final int TLC_MODE_SIMU = 2188; - public static final int TLC_COMPUTING_INIT = 2189; - public static final int TLC_INIT_GENERATED1 = 2190; - public static final int TLC_INIT_GENERATED2 = 2191; - public static final int TLC_INIT_GENERATED3 = 2207; - public static final int TLC_INIT_GENERATED4 = 2208; - public static final int TLC_CHECKING_TEMPORAL_PROPS = 2192; - public static final int TLC_CHECKING_TEMPORAL_PROPS_END = 2267; - public static final int TLC_SUCCESS = 2193; - public static final int TLC_SEARCH_DEPTH = 2194; - public static final int TLC_CHECKPOINT_START = 2195; - public static final int TLC_CHECKPOINT_END = 2196; - public static final int TLC_CHECKPOINT_RECOVER_START = 2197; - public static final int TLC_CHECKPOINT_RECOVER_END = 2198; - public static final int TLC_STATS = 2199; - public static final int TLC_STATS_DFID = 2204; - public static final int TLC_STATS_SIMU = 2210; - public static final int TLC_PROGRESS_STATS = 2200; - public static final int TLC_COVERAGE_START = 2201; - public static final int TLC_COVERAGE_END = 2202; - public static final int TLC_CHECKPOINT_RECOVER_END_DFID = 2203; - public static final int TLC_PROGRESS_START_STATS_DFID = 2205; - public static final int TLC_PROGRESS_STATS_DFID = 2206; - public static final int TLC_PROGRESS_SIMU = 2209; - public static final int TLC_FP_COMPLETED = 2211; - - public static final int TLC_LIVE_IMPLIED = 2212; - public static final int TLC_LIVE_CANNOT_HANDLE_FORMULA = 2213; - public static final int TLC_LIVE_WRONG_FORMULA_FORMAT = 2214; - public static final int TLC_LIVE_ENCOUNTERED_ACTIONS = 2249; - public static final int TLC_LIVE_STATE_PREDICATE_NON_BOOL = 2250; - public static final int TLC_LIVE_CANNOT_EVAL_FORMULA = 2251; - public static final int TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE = 2252; - - - public static final int TLC_EXPECTED_VALUE = 2215; - public static final int TLC_EXPECTED_EXPRESSION = 2246; - public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING = 2247; - public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING2 = 2248; - - - // state printing - public static final int TLC_STATE_PRINT1 = 2216; - public static final int TLC_STATE_PRINT2 = 2217; - public static final int TLC_STATE_PRINT3 = 2218; // This seems to be a "Stuttering" message from a liveness-error trace - public static final int TLC_SANY_END = 2219; - public static final int TLC_SANY_START = 2220; - public static final int TLC_COVERAGE_VALUE = 2221; - - // config file errors - public static final int TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM = 2222; - public static final int TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID = 2223; - public static final int TLC_CONFIG_WRONG_SUBSTITUTION = 2224; - public static final int TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS = 2225; - public static final int TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC = 2226; - public static final int TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT = 2227; - public static final int TLC_CONFIG_ID_REQUIRES_NO_ARG = 2228; - public static final int TLC_CONFIG_SPECIFIED_NOT_DEFINED = 2229; - public static final int TLC_CONFIG_ID_HAS_VALUE = 2230; - public static final int TLC_CONFIG_MISSING_INIT = 2231; - public static final int TLC_CONFIG_MISSING_NEXT = 2232; - public static final int TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT = 2233; - public static final int TLC_CONFIG_OP_NO_ARGS = 2234; - public static final int TLC_CONFIG_OP_NOT_IN_SPEC = 2235; - public static final int TLC_CONFIG_OP_IS_EQUAL = 2236; - public static final int TLC_CONFIG_SPEC_IS_TRIVIAL = 2237; - public static final int TLC_CANT_HANDLE_SUBSCRIPT = 2238; - public static final int TLC_CANT_HANDLE_CONJUNCT = 2239; - public static final int TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS = 2240; - public static final int TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED = 2241; - public static final int TLC_CONFIG_OP_ARITY_INCONSISTENT = 2242; - public static final int TLC_CONFIG_NO_STATE_TYPE = 2243; - public static final int TLC_CANT_HANDLE_REAL_NUMBERS = 2244; - public static final int TLC_NO_MODULES = 2245; - - public static final int TLC_ENABLED_WRONG_FORMULA = 2260; - public static final int TLC_ENCOUNTERED_FORMULA_IN_PREDICATE = 2261; - public static final int TLC_VERSION = 2262; - public static final int TLC_USAGE = 2263; - public static final int TLC_COUNTER_EXAMPLE = 2264; - - public static final int TLC_INTEGER_TOO_BIG = 2265; -} +package tlc2.output; + + +/** + * Interface containing the error code constants + * @author Simon Zambrovski + * @version $Id$ + */ +public interface EC +{ + // Check and CheckImpl + // check if the TLC option is the same for params + public static final int CHECK_FAILED_TO_CHECK = 3000; + public static final int CHECK_COULD_NOT_READ_TRACE = 3001; + + public static final int CHECK_PARAM_EXPECT_CONFIG_FILENAME = 3100; + public static final int CHECK_PARAM_USAGE = 3101; + public static final int CHECK_PARAM_MISSING_TLA_MODULE = 3102; + public static final int CHECK_PARAM_NEED_TO_SPECIFY_CONFIG_DIR = 3103; + public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED = 3104; + public static final int CHECK_PARAM_WORKER_NUMBER_TOO_SMALL = 3105; + public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED2 = 3106; + public static final int CHECK_PARAM_DEPTH_REQUIRED = 3107; + public static final int CHECK_PARAM_DEPTH_REQUIRED2 = 3108; + public static final int CHECK_PARAM_TRACE_REQUIRED = 3109; + public static final int CHECK_PARAM_COVREAGE_REQUIRED = 3110; + public static final int CHECK_PARAM_COVREAGE_REQUIRED2 = 3111; + public static final int CHECK_PARAM_COVREAGE_TOO_SMALL = 3112; + public static final int CHECK_PARAM_UNRECOGNIZED = 3113; + public static final int CHECK_PARAM_TOO_MANY_INPUT_FILES = 3114; + + + public final static int SANY_PARSER_CHECK_1 = 4000; + public final static int SANY_PARSER_CHECK_2 = 4001; + public final static int SANY_PARSER_CHECK_3 = 4002; + + public static final int UNKNOWN = -1; // TODO remove all these + public final static int UNIT_TEST = -123456; + /** + * A feature of TLA+/TLC is not supported by TLC's current mode. E.g. + * TLCGet/TLCSet operator cannot be used in distributed TLC. + */ + public static final int TLC_FEATURE_UNSUPPORTED = 2156; + public static final int TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY = 2279; + + public static final int GENERAL = 1000; + public static final int SYSTEM_OUT_OF_MEMORY = 1001; + public static final int SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT = 1002; + public static final int SYSTEM_STACK_OVERFLOW = 1005; + + public static final int WRONG_COMMANDLINE_PARAMS_SIMULATOR = 1101; + public static final int WRONG_COMMANDLINE_PARAMS_TLC = 1102; + + public static final int TLC_PP_PARSING_VALUE = 2000; + public static final int TLC_PP_FORMATING_VALUE = 2001; + + public static final int TLC_METADIR_EXISTS = 2100; + public static final int TLC_METADIR_CAN_NOT_BE_CREATED = 2101; + public static final int TLC_INITIAL_STATE = 2102; + public static final int TLC_NESTED_EXPRESSION = 2103; + public static final int TLC_ASSUMPTION_FALSE = 2104; + public static final int TLC_ASSUMPTION_EVALUATION_ERROR = 2105; + public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL = 2106; + + public static final int TLC_INVARIANT_VIOLATED_INITIAL = 2107; + public static final int TLC_PROPERTY_VIOLATED_INITIAL = 2108; + public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT = 2109; + public static final int TLC_INVARIANT_VIOLATED_BEHAVIOR = 2110; + public static final int TLC_INVARIANT_EVALUATION_FAILED = 2111; + public static final int TLC_INVARIANT_VIOLATED_LEVEL = 2146; + public static final int TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR = 2112; + public static final int TLC_ACTION_PROPERTY_EVALUATION_FAILED = 2113; + public static final int TLC_DEADLOCK_REACHED = 2114; + + public static final int TLC_STATES_AND_NO_NEXT_ACTION = 2115; + public static final int TLC_TEMPORAL_PROPERTY_VIOLATED = 2116; + public static final int TLC_FAILED_TO_RECOVER_NEXT = 2117; + public static final int TLC_NO_STATES_SATISFYING_INIT = 2118; + public static final int TLC_STRING_MODULE_NOT_FOUND = 2119; + + public static final int TLC_ERROR_STATE = 2120; + public static final int TLC_BEHAVIOR_UP_TO_THIS_POINT = 2121; + public static final int TLC_BACK_TO_STATE = 2122; // The "back to state" message from a liveness-error trace. + public static final int TLC_FAILED_TO_RECOVER_INIT = 2123; + public static final int TLC_REPORTER_DIED = 2124; + + public static final int SYSTEM_ERROR_READING_POOL = 2125; + public static final int SYSTEM_CHECKPOINT_RECOVERY_CORRUPT = 2126; + public static final int SYSTEM_ERROR_WRITING_POOL = 2127; + public static final int SYSTEM_ERROR_CLEANING_POOL = 2270; + public static final int SYSTEM_INDEX_ERROR = 2134; + public static final int SYSTEM_STREAM_EMPTY = 2135; + public static final int SYSTEM_FILE_NULL = 2137; + public static final int SYSTEM_INTERRUPTED = 2138; + public static final int SYSTEM_UNABLE_NOT_RENAME_FILE = 2160; + public static final int SYSTEM_DISK_IO_ERROR_FOR_FILE = 2161; + public static final int SYSTEM_METADIR_EXISTS = 2162; + public static final int SYSTEM_METADIR_CREATION_ERROR = 2163; + public static final int SYSTEM_UNABLE_TO_OPEN_FILE = 2167; + public static final int TLC_BUG = 2128; // TODO Bad description + public static final int TLC_FINGERPRINT_EXCEPTION = 2147; + + public static final int SYSTEM_DISKGRAPH_ACCESS = 2129; // TODO refactor + + public static final int TLC_AAAAAAA = 2130; + public static final int TLC_REGISTRY_INIT_ERROR = 2131; + public static final int TLC_CHOOSE_ARGUMENTS_WRONG = 2164; + public static final int TLC_CHOOSE_UPPER_BOUND = 2165; + + public static final int TLC_VALUE_ASSERT_FAILED = 2132; + public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE = 2154; + public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED = 2168; + + public static final int TLC_FP_NOT_IN_SET = 2133; + public static final int TLC_FP_VALUE_ALREADY_ON_DISK = 2166; + + public static final int TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT = 2159; + public static final int TLC_PARAMETER_MUST_BE_POSTFIX = 2136; + public static final int TLC_COULD_NOT_DETERMINE_SUBSCRIPT = 2139; + public static final int TLC_SUBSCRIPT_CONTAIN_NO_STATE_VAR = 2140; + public static final int TLC_WRONG_TUPLE_FIELD_NAME = 2141; + public static final int TLC_WRONG_RECORD_FIELD_NAME = 2142; + public static final int TLC_UNCHANGED_VARIABLE_CHANGED = 2143; + public static final int TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD = 2144; + + public static final int TLC_MODULE_TLCGET_UNDEFINED = 2145; + /** Attempted to compare %1% with the value\n%2% */ + public static final int TLC_MODULE_COMPARE_VALUE = 2155; + public static final int TLC_MODULE_CHECK_MEMBER_OF = 2158; + public static final int TLC_MODULE_TRANSITIVE_CLOSURE = 2157; + /** The %1% argument of %2% should be a %3%, but instead it is:<br>%4% */ + public static final int TLC_MODULE_ARGUMENT_ERROR = 2169; + /** Simon used an argument like "\bn apple" to TLC_MODULE_ARGUMENT_ERROR to turn + * an "a" into an "an". This doesn't work on the Toolbox's console. Hence, LL added + * the following message type on 21 May 2012. */ + public static final int TLC_MODULE_ARGUMENT_ERROR_AN = 2266; + public static final int TLC_ARGUMENT_MISMATCH = 2170; + public static final int TLC_PARSING_FAILED2 = 2171; + public static final int TLC_PARSING_FAILED = 3002; + public static final int TLC_TOO_MNY_POSSIBLE_STATES = 2172; + public static final int TLC_ERROR_REPLACING_MODULES = 2173; + public static final int SYSTEM_ERROR_READING_STATES = 2174; + public static final int SYSTEM_ERROR_WRITING_STATES = 2175; + public static final int TLC_MODULE_APPLYING_TO_WRONG_VALUE = 2176; + public static final int TLC_MODULE_BAG_UNION1 = 2177; + public static final int TLC_MODULE_OVERFLOW = 2178; + public static final int TLC_MODULE_DIVISION_BY_ZERO = 2179; + public static final int TLC_MODULE_NULL_POWER_NULL = 2180; + public static final int TLC_MODULE_COMPUTING_CARDINALITY = 2181; + public static final int TLC_MODULE_EVALUATING = 2182; + /** The %1% argument of %2% must be in the domain of its first argument:<br>%3%<br>, but instead it is<br>%4% */ + public static final int TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN = 2183; + public static final int TLC_MODULE_APPLY_EMPTY_SEQ = 2184; + + public static final int TLC_STARTING = 2185; + public static final int TLC_FINISHED = 2186; + + // distributed TLC + + public static final int TLC_DISTRIBUTED_SERVER_RUNNING = 7000; + public static final int TLC_DISTRIBUTED_WORKER_REGISTERED = TLC_DISTRIBUTED_SERVER_RUNNING + 1; + public static final int TLC_DISTRIBUTED_WORKER_DEREGISTERED = TLC_DISTRIBUTED_WORKER_REGISTERED + 1; + public static final int TLC_DISTRIBUTED_WORKER_STATS = TLC_DISTRIBUTED_WORKER_DEREGISTERED + 1; + public static final int TLC_DISTRIBUTED_SERVER_NOT_RUNNING = TLC_DISTRIBUTED_WORKER_STATS + 1; + public static final int TLC_DISTRIBUTED_VM_VERSION = TLC_DISTRIBUTED_SERVER_NOT_RUNNING + 1; + public static final int TLC_DISTRIBUTED_WORKER_LOST = TLC_DISTRIBUTED_VM_VERSION + 1; + public static final int TLC_DISTRIBUTED_EXCEED_BLOCKSIZE = TLC_DISTRIBUTED_WORKER_LOST + 1; + public static final int TLC_DISTRIBUTED_SERVER_FPSET_WAITING = TLC_DISTRIBUTED_EXCEED_BLOCKSIZE + 1; + public static final int TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED = TLC_DISTRIBUTED_SERVER_FPSET_WAITING + 1; + public static final int TLC_DISTRIBUTED_SERVER_FINISHED = TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED + 1; + + // errors during parsing of the model configuration + + public static final int CFG_ERROR_READING_FILE = 5001; + public static final int CFG_GENERAL = 5002; + public static final int CFG_MISSING_ID = 5003; + public static final int CFG_TWICE_KEYWORD = 5004; + public static final int CFG_EXPECT_ID = 5005; + public static final int CFG_EXPECTED_SYMBOL = 5006; + public static final int TLC_MODE_MC = 2187; + public static final int TLC_MODE_MC_DFS = 2271; + public static final int TLC_MODE_SIMU = 2188; + public static final int TLC_COMPUTING_INIT = 2189; + public static final int TLC_INIT_GENERATED1 = 2190; + public static final int TLC_INIT_GENERATED2 = 2191; + public static final int TLC_INIT_GENERATED3 = 2207; + public static final int TLC_INIT_GENERATED4 = 2208; + public static final int TLC_CHECKING_TEMPORAL_PROPS = 2192; + public static final int TLC_CHECKING_TEMPORAL_PROPS_END = 2267; + public static final int TLC_SUCCESS = 2193; + public static final int TLC_SEARCH_DEPTH = 2194; + public static final int TLC_STATE_GRAPH_OUTDEGREE = 2268; + public static final int TLC_CHECKPOINT_START = 2195; + public static final int TLC_CHECKPOINT_END = 2196; + public static final int TLC_CHECKPOINT_RECOVER_START = 2197; + public static final int TLC_CHECKPOINT_RECOVER_END = 2198; + public static final int TLC_STATS = 2199; + public static final int TLC_STATS_DFID = 2204; + public static final int TLC_STATS_SIMU = 2210; + public static final int TLC_PROGRESS_STATS = 2200; + public static final int TLC_COVERAGE_START = 2201; + public static final int TLC_COVERAGE_END = 2202; + public static final int TLC_CHECKPOINT_RECOVER_END_DFID = 2203; + public static final int TLC_PROGRESS_START_STATS_DFID = 2205; + public static final int TLC_PROGRESS_STATS_DFID = 2206; + public static final int TLC_PROGRESS_SIMU = 2209; + public static final int TLC_FP_COMPLETED = 2211; + + public static final int TLC_LIVE_IMPLIED = 2212; + public static final int TLC_LIVE_CANNOT_HANDLE_FORMULA = 2213; + public static final int TLC_LIVE_WRONG_FORMULA_FORMAT = 2214; + public static final int TLC_LIVE_ENCOUNTERED_ACTIONS = 2249; + public static final int TLC_LIVE_STATE_PREDICATE_NON_BOOL = 2250; + public static final int TLC_LIVE_CANNOT_EVAL_FORMULA = 2251; + public static final int TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE = 2252; + + + public static final int TLC_EXPECTED_VALUE = 2215; + public static final int TLC_EXPECTED_EXPRESSION = 2246; + public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING = 2247; + public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING2 = 2248; + + + // state printing + public static final int TLC_STATE_PRINT1 = 2216; + public static final int TLC_STATE_PRINT2 = 2217; + public static final int TLC_STATE_PRINT3 = 2218; // This seems to be a "Stuttering" message from a liveness-error trace + public static final int TLC_SANY_END = 2219; + public static final int TLC_SANY_START = 2220; + public static final int TLC_COVERAGE_VALUE = 2221; + + // config file errors + public static final int TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM = 2222; + public static final int TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID = 2223; + public static final int TLC_CONFIG_WRONG_SUBSTITUTION = 2224; + public static final int TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS = 2225; + public static final int TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR = 2280; + public static final int TLC_CONFIG_SUBSTITUTION_NON_CONSTANT = 2281; + public static final int TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC = 2226; + public static final int TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT = 2227; + public static final int TLC_CONFIG_ID_REQUIRES_NO_ARG = 2228; + public static final int TLC_CONFIG_SPECIFIED_NOT_DEFINED = 2229; + public static final int TLC_CONFIG_ID_HAS_VALUE = 2230; + public static final int TLC_CONFIG_MISSING_INIT = 2231; + public static final int TLC_CONFIG_MISSING_NEXT = 2232; + public static final int TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT = 2233; + public static final int TLC_CONFIG_OP_NO_ARGS = 2234; + public static final int TLC_CONFIG_OP_NOT_IN_SPEC = 2235; + public static final int TLC_CONFIG_OP_IS_EQUAL = 2236; + public static final int TLC_CONFIG_SPEC_IS_TRIVIAL = 2237; + public static final int TLC_CANT_HANDLE_SUBSCRIPT = 2238; + public static final int TLC_CANT_HANDLE_CONJUNCT = 2239; + public static final int TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS = 2240; + public static final int TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED = 2241; + public static final int TLC_CONFIG_OP_ARITY_INCONSISTENT = 2242; + public static final int TLC_CONFIG_NO_STATE_TYPE = 2243; + public static final int TLC_CANT_HANDLE_REAL_NUMBERS = 2244; + public static final int TLC_NO_MODULES = 2245; + + public static final int TLC_ENABLED_WRONG_FORMULA = 2260; + public static final int TLC_ENCOUNTERED_FORMULA_IN_PREDICATE = 2261; + public static final int TLC_VERSION = 2262; + public static final int TLC_USAGE = 2263; + public static final int TLC_COUNTER_EXAMPLE = 2264; + + public static final int TLC_INTEGER_TOO_BIG = 2265; +} diff --git a/tlatools/src/tlc2/output/MP.java b/tlatools/src/tlc2/output/MP.java index 53fcde9518d41df722230dbf94147897825707b9..2f415833ea6dbfec6f4e08588f18f5a1c91cdb52 100644 --- a/tlatools/src/tlc2/output/MP.java +++ b/tlatools/src/tlc2/output/MP.java @@ -12,6 +12,7 @@ import tlc2.tool.TLCState; import tlc2.tool.TLCStateInfo; import tlc2.tool.liveness.LiveWorker; import tlc2.util.statistics.IBucketStatistics; +import util.Assert; import util.DebugPrinter; import util.Set; import util.ToolIO; @@ -249,7 +250,7 @@ public class MP break; case EC.SYSTEM_ERROR_CLEANING_POOL: if (messageClass == ERROR) { - b.append("Exception %2% cleaning up an obsolete disk files.\n%1%"); + b.append("Exception cleaning up an obsolete disk file.\n%1%"); } else if (messageClass == WARNING) { b.append("Failed to clean up an obsolete disk file. Please manually delete %1% if free disk space is low."); } @@ -348,7 +349,7 @@ public class MP b.append("Evaluating assumption %1% failed.\n%2%"); break; case EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL: - b.append("State is not completely specified by the " + "initial predicate:\n%2%"); + b.append("State is not completely specified by the initial predicate:\n%1%"); break; case EC.TLC_INVARIANT_VIOLATED_INITIAL: b.append("Invariant %1% is violated by the initial state:\n%2%"); @@ -363,9 +364,22 @@ public class MP case EC.TLC_INVARIANT_VIOLATED_BEHAVIOR: b.append("Invariant %1% is violated."); break; + case EC.TLC_INVARIANT_VIOLATED_LEVEL: + b.append("The invariant %1% is not a state predicate (one with no primes or temporal operators)."); + if (parameters.length > 1) { + b.append("\nNote that a bug can cause TLC to incorrectly report this error.\n" + + "If you believe your TLA+ or PlusCal specification to be correct,\n" + + "please check if this bug described in LevelNode.java starting at line 590ff affects you."); + } + break; case EC.TLC_INVARIANT_EVALUATION_FAILED: - b.append("Evaluating invariant %1% failed:\n%2%"); + if (parameters.length == 1) { + b.append("Evaluating invariant %1% failed."); + } + else if (parameters.length == 2) { + b.append("Evaluating invariant %1% failed.\n%2%"); + } break; case EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR: @@ -373,7 +387,12 @@ public class MP break; case EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED: - b.append("Evaluating action property %1% failed."); + if (parameters.length == 1) { + b.append("Evaluating action property %1% failed."); + } + else if (parameters.length == 2) { + b.append("Evaluating action property %1% failed.\n%2%"); + } break; case EC.TLC_DEADLOCK_REACHED: @@ -402,6 +421,16 @@ public class MP b.append("This is probably a TLC bug(%1%)."); break; + case EC.TLC_FINGERPRINT_EXCEPTION: + b.append("TLC was unable to fingerprint." + + "\n" + + "\nFingerprint Stack Trace:" + + "\n%1%" + + "\nReason:" + + "\n%2%"); // reason must come last, with nothing following, because Tool cannot parse the embedded error otherwise + + break; + case EC.TLC_NO_STATES_SATISFYING_INIT: b.append("There is no state satisfying the initial state predicate."); break; @@ -414,9 +443,19 @@ public class MP if (TLCGlobals.tool) { // format same as state printing for easier // parsing by toolbox - b.append("%1%: Back to state: %2%\n"); + if (parameters.length == 1) { + b.append("%1%: Back to state\n"); + } + else if (parameters.length == 2) { + b.append("%1%: Back to state: %2%\n"); + } } else { - b.append("Back to state %1%: %2%\n"); + if (parameters.length == 1) { + b.append("Back to state %1%\n"); + } + else if (parameters.length == 2) { + b.append("Back to state %1%: %2%\n"); + } } break; @@ -497,7 +536,12 @@ public class MP break; case EC.CHECK_PARAM_UNRECOGNIZED: - b.append("Unrecognized option: %1%"); + if (parameters.length == 1) { + b.append("Unrecognized option: %1%"); + } + else if (parameters.length == 2) { + b.append("Unrecognized option in file %2%: %1%"); + } break; case EC.CHECK_PARAM_TOO_MANY_INPUT_FILES: b.append("More than one input files: %1% and %2%"); @@ -626,6 +670,11 @@ public class MP case EC.TLC_FEATURE_UNSUPPORTED: b.append("%1%"); break; + case EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY: + b.append("Declaring symmetry during liveness checking is dangerous. " + + "It might cause TLC to miss violations of the stated liveness properties. " + + "Please check liveness without symmetry defined."); + break; /* Liveness errors */ case EC.TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT: @@ -748,10 +797,13 @@ public class MP b.append("Finished in %1% at (").append(SDF.format(new Date())).append(")"); break; case EC.TLC_MODE_MC: - b.append("Running in Model-Checking mode with %1% worker%2%."); + b.append("Running breadth-first search Model-Checking with %1% worker%2% on %3% cores with %10%MB heap and %11%MB offheap memory (%4% %5% %6%, %7% %8% %9%)."); + break; + case EC.TLC_MODE_MC_DFS: + b.append("Running depth-first search Model-Checking with %1% worker%2% on %3% cores with %10%MB heap and %11%MB offheap memory (%4% %5% %6%, %7% %8% %9%)."); break; case EC.TLC_MODE_SIMU: - b.append("Running Random Simulation with seed %1%."); + b.append("Running Random Simulation with seed %1% with %2% worker%3% on %4% cores with %11%MB heap and %12%MB offheap memory (%5% %6% %7%, %8% %9% %10%)."); break; case EC.TLC_COMPUTING_INIT: b.append("Computing initial states..."); @@ -783,9 +835,12 @@ public class MP + " based on the actual fingerprints: %2%"); break; case EC.TLC_SEARCH_DEPTH: - b.append("The depth of the complete state graph search is %1%."); + b.append("The depth of the complete state graph search is %1%."); break; - case EC.TLC_CHECKPOINT_START: + case EC.TLC_STATE_GRAPH_OUTDEGREE: + b.append("The average outdegree of the complete state graph is %2% (minimum is %1%, the maximum %4% and the 95th percentile is %3%)."); + break; + case EC.TLC_CHECKPOINT_START: b.append("Checkpointing of run %1%"); break; case EC.TLC_CHECKPOINT_END: @@ -814,7 +869,7 @@ public class MP if (parameters.length == 4) { b.append("Progress(%1%) at " + SDF.format(new Date()) + ": %2% states generated, " + "%3% distinct states found, " + "%4% states left on queue."); - } else { + } else if (parameters.length == 6) { b.append("Progress(%1%) at " + SDF.format(new Date()) + ": %2% states generated (" + df.format(Long.valueOf(parameters[4])) + " s/min), %3% distinct states found (" + df.format(Long.valueOf(parameters[5])) + " ds/min), %4% states left on queue."); @@ -871,6 +926,12 @@ public class MP case EC.TLC_CONFIG_WRONG_SUBSTITUTION: b.append("The configuration file substitutes for %1% with the undefined identifier %2%."); break; + case EC.TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR: + b.append("In evaluation, the identifier %1% is either undefined or not an operator.\n%2%"); + break; + case EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT: + b.append("The configuration file substitutes constant %1% with non-constant %2%."); + break; case EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS: b.append("The configuration file substitutes for %1% with %2% of different number of arguments."); break; @@ -881,7 +942,12 @@ public class MP b.append("The configuration file cannot specify both INIT/NEXT and SPECIFICATION fields."); break; case EC.TLC_CONFIG_ID_REQUIRES_NO_ARG: - b.append("TLC requires %1% not to take any argument."); + if (parameters.length == 1) { + b.append("TLC requires %1% not to take any argument."); + } + else if (parameters.length == 2) { + b.append("TLC requires %1% not to take any argument, but one was given: %2%"); + } break; case EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED: b.append("The %1% %2% specified in the configuration file" + "\nis not defined in the specification."); @@ -1068,6 +1134,7 @@ public class MP */ public static String getError(int errorCode, String[] parameters) { + recorder.record(errorCode, parameters); return getMessage(ERROR, errorCode, parameters); } @@ -1098,6 +1165,7 @@ public class MP */ public static String getMessage(int errorCode, String[] parameters) { + recorder.record(errorCode, parameters); return getMessage(NONE, errorCode, parameters); } @@ -1224,7 +1292,14 @@ public class MP } else { msg = msg + "\nThe error occurred when TLC was " + cause + "."; } - msg = msg + "\nThe exception was a " + throwable.getClass().getName() + "\n"; + if (throwable instanceof Assert.TLCRuntimeException) { + // MK 07/25/2017: Disguise TLCRuntimeException with its parent class + // RuntimeException to not change the externally visible TLC output + // when an exception gets reports. + msg = msg + "\nThe exception was a " + RuntimeException.class.getName() + "\n"; + } else { + msg = msg + "\nThe exception was a " + throwable.getClass().getName() + "\n"; + } if (throwable.getMessage() != null) { msg = msg + ": " + throwable.getMessage(); @@ -1338,6 +1413,13 @@ public class MP DebugPrinter.print("leaving printTLCBug(int, String[])"); //$NON-NLS-1$ } + /** + * @see MP#printWarning(int) + */ + public static void printWarning(final int errorCode) { + printWarning(errorCode, new String[0]); + } + /** * @see MP#printWarning(int, String[]) */ diff --git a/tlatools/src/tlc2/tool/AbstractChecker.java b/tlatools/src/tlc2/tool/AbstractChecker.java index a2bf27cfa0e99645b7084f2de566319ef775af3b..805841275422e1e4b08ce8f129f1ae610bdc7eb1 100644 --- a/tlatools/src/tlc2/tool/AbstractChecker.java +++ b/tlatools/src/tlc2/tool/AbstractChecker.java @@ -20,7 +20,7 @@ import tlc2.util.IStateWriter; import tlc2.util.NoopStateWriter; import tlc2.util.ObjLongTable; import tlc2.util.StateWriter; -import tlc2.util.statistics.BucketStatistics; +import tlc2.util.statistics.ConcurrentBucketStatistics; import tlc2.util.statistics.DummyBucketStatistics; import tlc2.util.statistics.IBucketStatistics; import tlc2.value.Value; @@ -136,11 +136,15 @@ public abstract class AbstractChecker implements Cancelable this.actions = this.tool.getActions(); // the sub-actions if (this.checkLiveness) { + if (tool.hasSymmetry()) { + // raise warning... + MP.printWarning(EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY); + } // Initialization for liveness checking: report("initializing liveness checking"); IBucketStatistics stats = new DummyBucketStatistics(); if (LIVENESS_STATS) { - stats = new BucketStatistics("Histogram vertex out-degree", LiveCheck.class.getPackage().getName(), + stats = new ConcurrentBucketStatistics("Histogram vertex out-degree", LiveCheck.class.getPackage().getName(), "DiskGraphsOutDegree"); } if (LIVENESS_TESTING_IMPLEMENTATION) { @@ -215,6 +219,196 @@ public abstract class AbstractChecker implements Cancelable MP.printMessage(EC.TLC_COVERAGE_END); } } + + public static final void reportSuccess(final long numOfDistinctStates, final double actualProb, final long numOfGenStates) throws IOException + { + // shown as 'calculated' in Toolbox + final double optimisticProb = numOfDistinctStates * ((numOfGenStates - numOfDistinctStates) / Math.pow(2, 64)); + /* The following code added by LL on 3 Aug 2009 to print probabilities + * to only one decimal point. Removed by LL on 17 April 2012 because it + * seemed to report probabilities > 10-4 as probability 0. + */ + // final PrintfFormat fmt = new PrintfFormat("val = %.1G"); + // final String optimisticProbStr = fmt.sprintf(optimisticProb); + // final String actualProbStr = fmt.sprintf(actualProb); + + // Following two lines added by LL on 17 April 2012 + final String optimisticProbStr = "val = " + ProbabilityToString(optimisticProb, 2); + // shown as 'observed' in Toolbox + final String actualProbStr = "val = " + ProbabilityToString(actualProb, 2); + MP.printMessage(EC.TLC_SUCCESS, new String[] { optimisticProbStr, actualProbStr }); + } + + /** + * This method added by LL on 17 April 2012 to replace the use of the PrintfFormat + * method in reportSuccess. + * + * Returns a string representing the decimal representation of a probability to + * a given number of significant digits. If the input is not a probability, or if + * some error is found, then it returns the result of applying Double.toString(long) + * to the value. + * + * Warning: the code makes the following assumption: + * - Double.toString(v) returns a decimal representation of v of the + * form [d]* ["." [d]+ ["E" [+ | -] [d]+] where d is a decimal digit and + * [x] = 0 or 1 instance of x + * [x]* = any number of instances of x + * [x]+ = any non-zero number of instances of x + * x | y = an x or a y + * + * @param val - the probability represented as a long; must satisfy 0 <= val <= 1. + * @param significantDigits - the number of significant digits to include; must be > 0. + * @return + */ + private static final String ProbabilityToString(double val, int significantDigits) { + /* + * If val = 0 (which shouldn't happen), return "0.0" + */ + if (val == 0) { + return "0.0"; + } + + String valString = Double.toString(val) ; + int valStringLen = valString.length(); + + String result = ""; + int next = 0; // pointer to the next character in valString to examine. + int significantDigitsFound = 0; + + /* + * Skip past leading zeros. + */ + while ((next < valStringLen) && (valString.charAt(next) == '0')) { + next++ ; + } + + /* + * Append all the following digits to result, incrementing + * significantDigits for each one. + */ + while ( (next < valStringLen) && + Character.isDigit(valString.charAt(next))) { + result = result + valString.charAt(next); + significantDigitsFound++; + next++ ; + } + + /* + * IF next character is not "." + * THEN IF at end THEN return result + * ELSE return valString. + */ + if (next == valStringLen) { + return result; + } else if (valString.charAt(next) != '.') { + return valString; + } + + + /* + * IF significantDigitsFound >= significantDigits, + * THEN skip over "." and the following digits. + * (this should not happen) + * ELSE append "." to result ; + * IF significantDigitsFound = 0 + * THEN copy each of the following "0"s of valString to result; + * copy up to significantDigits - significantDigitsFound + * following digits of valString to result; + * IF next char of valString a digit >= "5" + * THEN propagate a carry backwards over the digits of result + * -- e.g., changing ".019" to ".020"; + * Skip over remaining digits of valString; + */ + if (significantDigitsFound >= significantDigits) { + next++ ; + while ( (next < valStringLen) && + Character.isDigit(valString.charAt(next))) { + next++ ; + } + } else { + next++; + result = result + "."; + if (significantDigitsFound == 0) { + while ((next < valStringLen) && (valString.charAt(next) == '0')) { + next++ ; + result = result + "0"; + } + } + while ((next < valStringLen) && + Character.isDigit(valString.charAt(next)) && + significantDigitsFound < significantDigits ) { + result = result + valString.charAt(next); + next++; + significantDigitsFound++; + } + if ((next < valStringLen) && + Character.isDigit(valString.charAt(next)) && + Character.digit(valString.charAt(next), 10) >= 5) { + int prev = result.length()-1; // the next digit of result to increment + boolean done = false; + while (!done) { + if (prev < 0) { + result = "1" + result; + done = true; + } else { + char prevChar = result.charAt(prev); + String front = result.substring(0, prev); + String back = result.substring(prev+1); + if (Character.isDigit(prevChar)) { + if (prevChar == '9') { + result = front + '0' + back; + } else { + result = front + Character.forDigit(Character.digit(prevChar, 10)+1, 10) + back; + done = true; + } + + } else { + // prevChar must be '.', so just continue + } + } + prev--; + } + } + while ((next < valStringLen) && + Character.isDigit(valString.charAt(next))) { + next++; + } + } + + /* + * IF next at end of valString or at "E" + * THEN copy remaining chars of valString to result; + * return result + * ELSE return valString + */ + if (next >= valStringLen) { + return result; + } + if (valString.charAt(next)=='E') { + next++; + result = result + "E"; + while (next < valStringLen) { + result = result + valString.charAt(next); + next++; + } + return result; + } + return valString; + } + +// The following method used for testing ProbabilityToString +// +// public static void main(String[] args) { +// double[] test = new double[] {.5, .0995, .00000001, 001.000, .0022341, +// .0022351, 3.14159E-12, +// 00.999, .002351111, 22.8E-14, 0.000E-12, +// 37, 0033D, 04.85, -35.3}; +// int i = 0; +// while (i < test.length) { +// System.out.println("" + i + ": " + Double.toString(test[i]) + " -> " + ProbabilityToString(test[i],2)); +// i++; +// } +// } /** * Initialize the model checker @@ -250,9 +444,6 @@ public abstract class AbstractChecker implements Cancelable // every `count' times that the progress is printed. int count = TLCGlobals.coverageInterval / TLCGlobals.progressInterval; - // work to be done prior loop entry - runTLCPreLoop(); - // I added the `if (!this.done)' to the following statement. // I have no idea what this wait is for, but apparently // because of changes made by Simon, it caused TLC to wait for @@ -349,13 +540,6 @@ public abstract class AbstractChecker implements Cancelable */ protected abstract IWorker[] startWorkers(AbstractChecker checker, int checkIndex); - /** - * Hook to run some work before entering the worker loop - */ - protected void runTLCPreLoop() - { - } - /** * Usually * Check liveness: check liveness properties on the partial state graph. diff --git a/tlatools/src/tlc2/tool/CallStack.java b/tlatools/src/tlc2/tool/CallStack.java index efda0f2d88b00852a0f402db065c07c1ec07bb2e..2d1e4360e17cb69151d5052135db3e793aef3215 100644 --- a/tlatools/src/tlc2/tool/CallStack.java +++ b/tlatools/src/tlc2/tool/CallStack.java @@ -16,6 +16,7 @@ public class CallStack { private SemanticNode[] stack; // the call stack private int index; // pointer to the empty slot + private boolean frozen; public final void push(SemanticNode expr) { if (this.index == this.stack.length) { @@ -24,47 +25,60 @@ public class CallStack { this.stack[this.index++] = expr; } - public final void pop() { this.index--; } + public final void pop() { if(!frozen) this.index--; } - public final int size() { return this.index; } + /** + * Calling freeze turns all subsequent pop operations into no-ops. + */ + public void freeze() { + this.frozen = true; + } + public final int size() { return this.index; } + private final void resize() { int len = 2 * this.stack.length; SemanticNode[] stack1 = new SemanticNode[len]; System.arraycopy(this.stack, 0, stack1, 0, this.stack.length); this.stack = stack1; } - + // Returns a string representation of this. - public final String toString() + public final String toString() { - /* - * Moved in the distinction if the call stack is empty or not (from Tool) - */ - if (this.index > 0) - { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < this.index; i++) { - sb.append(i + ". "); - SemanticNode expr = this.stack[i]; - Location loc = expr.getTreeNode().getLocation(); - sb.append("Line "); - sb.append(loc.beginLine()); - sb.append(", column "); - sb.append(loc.beginColumn()); - sb.append(" to line "); - sb.append(loc.endLine()); - sb.append(", column "); - sb.append(loc.endColumn()); - sb.append(" in "); - sb.append(loc.source() + "\n"); - } - sb.append("\n"); - return sb.toString(); - - } else { - return " The error call stack is empty.\n"; + /* + * Moved in the distinction if the call stack is empty or not (from Tool) + */ + if (this.index > 0) + { + final StringBuffer sb = new StringBuffer(); + SemanticNode expr = null; + int stackDepth = 0; + for (int i = 0; i < this.index; i++) { + if(expr == this.stack[i]) { + // Skip consecutive identical SemanticNodes. + continue; + } + expr = this.stack[i]; + Location loc = expr.getTreeNode().getLocation(); + sb.append(stackDepth + ". "); + sb.append("Line "); + sb.append(loc.beginLine()); + sb.append(", column "); + sb.append(loc.beginColumn()); + sb.append(" to line "); + sb.append(loc.endLine()); + sb.append(", column "); + sb.append(loc.endColumn()); + sb.append(" in "); + sb.append(loc.source() + "\n"); + stackDepth++; } + sb.append("\n"); + return sb.toString(); + } else { + return " The error call stack is empty.\n"; + } } } diff --git a/tlatools/src/tlc2/tool/CheckImplFile.java b/tlatools/src/tlc2/tool/CheckImplFile.java index a4f6eaaecb9c82b55b19f62a667bb0e4ce91ef84..25cd86063dc689fd1fdeee36334d3b23f1eff9d7 100644 --- a/tlatools/src/tlc2/tool/CheckImplFile.java +++ b/tlatools/src/tlc2/tool/CheckImplFile.java @@ -280,7 +280,7 @@ public class CheckImplFile extends CheckImpl return; } if (mainFile != null) { - printErrorMsg(MP.getError(EC.CHECK_PARAM_UNRECOGNIZED, new String[]{mainFile, args[index] })); + printErrorMsg(MP.getError(EC.CHECK_PARAM_UNRECOGNIZED, new String[]{args[index], mainFile })); return; } mainFile = args[index++]; diff --git a/tlatools/src/tlc2/tool/DFIDModelChecker.java b/tlatools/src/tlc2/tool/DFIDModelChecker.java index 89d34ff9b5f57af7df1b259c416f7b8442566f6f..05c096038abf261a80901dee8c8fa9b5601a8c94 100644 --- a/tlatools/src/tlc2/tool/DFIDModelChecker.java +++ b/tlatools/src/tlc2/tool/DFIDModelChecker.java @@ -164,11 +164,14 @@ public class DFIDModelChecker extends AbstractChecker } // Start working on this level: - MP.printMessage(EC.TLC_PROGRESS_STATS_DFID, new String[] { String.valueOf(level), + MP.printMessage(EC.TLC_PROGRESS_START_STATS_DFID, new String[] { String.valueOf(level), String.valueOf(this.numOfGenStates), String.valueOf(this.theFPSet.size()) }); FPIntSet.incLevel(); success = this.runTLC(level); + // Recent done flag before after the workers have checked the + // current level in preparation for the next level. + this.done = false; if (!success) return; @@ -705,13 +708,9 @@ public class DFIDModelChecker extends AbstractChecker String.valueOf(this.theFPSet.size()) }); } - public final void reportSuccess() throws IOException + private final void reportSuccess() throws IOException { - long d = this.theFPSet.size(); - double prob1 = (d * (this.numOfGenStates.get() - d)) / Math.pow(2, 64); - double prob2 = this.theFPSet.checkFPs(); - - MP.printMessage(EC.TLC_SUCCESS, new String[] { String.valueOf(prob1), String.valueOf(prob2) }); + reportSuccess(this.theFPSet.size(), this.theFPSet.checkFPs(), numOfGenStates.get()); } /** @@ -722,19 +721,17 @@ public class DFIDModelChecker extends AbstractChecker for (int i = 0; i < this.workers.length; i++) { this.workers[i] = new DFIDWorker(i, checkIndex, checker); + } + // Start all workers once instantiated to avoid a race with setStop, + // when setStop is being called concurrently with startWorkers. This + // happens, if a DFIDWorker terminates immediately. + for (int i = 0; i < this.workers.length; i++) + { this.workers[i].start(); } return this.workers; } - /** - * Run prior the worker loop - */ - protected void runTLCPreLoop() - { - this.done = false; - } - /** * Process calculation * @param count diff --git a/tlatools/src/tlc2/tool/FingerprintException.java b/tlatools/src/tlc2/tool/FingerprintException.java new file mode 100644 index 0000000000000000000000000000000000000000..7bf07e43612f685c6018fdc6c9c4d0d4f5b1910c --- /dev/null +++ b/tlatools/src/tlc2/tool/FingerprintException.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Ian Morris Nieves - initial design and implementation + ******************************************************************************/ + +package tlc2.tool; + +import tlc2.value.Value; +import tla2sany.semantic.SemanticNode; + +public class FingerprintException extends RuntimeException { + + final public Value value; + final public FingerprintException next; + + private FingerprintException(Throwable initCauseThrowable, Value value, FingerprintException next) { + initCause(initCauseThrowable); + this.value = value; + this.next = next; + } + + public static FingerprintException getNewHead(Value v, Throwable t){ + FingerprintException fpe = null; + if(t instanceof FingerprintException) + fpe = ((FingerprintException) t).prependNewHead(v); + else + fpe = FingerprintException.createNewHead(v, t); + return fpe; + } + + private static FingerprintException createNewHead(Value value, Throwable initCauseThrowable){ + if(value == null || initCauseThrowable == null) + return null; + else + return new FingerprintException(initCauseThrowable, value, null); + } + + private FingerprintException prependNewHead(Value value){ + if(value == null) + return null; + else + return new FingerprintException(null, value, this); + } + + public Throwable getRootCause(){ + FingerprintException nextFPE = this; + while(nextFPE.next != null) + nextFPE = nextFPE.next; + return nextFPE.getCause(); + } + + public String getTrace(){ + return getTraceImpl(0, null); + } + + private String getTraceImpl(final int traceIndexLabel, final Integer lastSemanticNodeUid){ + SemanticNode semanticNode = value.getSource(); + if(semanticNode == null){ + if(next == null) + return ""; + else + return next.getTraceImpl(traceIndexLabel, lastSemanticNodeUid); + } + else{ + Integer semanticNodeUid = semanticNode.getUid(); + if(semanticNodeUid.equals(lastSemanticNodeUid)){ // same SemanticNode compared to current top of stack + if(next == null) + return ""; + else + return next.getTraceImpl(traceIndexLabel, lastSemanticNodeUid); + } + else{ // different SemanticNode compared to current top of stack + String description = traceIndexLabel + ") " + semanticNode.toString() + "\n"; + if(next == null) + return description; + else + return next.getTraceImpl(traceIndexLabel+1, semanticNodeUid) + description; + } + } + } + +} diff --git a/tlatools/src/tlc2/tool/IWorker.java b/tlatools/src/tlc2/tool/IWorker.java index b93bf7e5fdd17895ccb07dc4f826e5652a0ffd3f..6e2ddc1f328733126b0fbe1fbbcc8185bf290403 100644 --- a/tlatools/src/tlc2/tool/IWorker.java +++ b/tlatools/src/tlc2/tool/IWorker.java @@ -1,5 +1,6 @@ package tlc2.tool; +import tlc2.TLCGlobals; import tlc2.util.ObjLongTable; import tlc2.value.Value; @@ -9,6 +10,11 @@ import tlc2.value.Value; */ public interface IWorker { + /** + * @return A worker's id in the range 0 to {@link TLCGlobals#getNumWorkers()} - 1 + */ + public int myGetId(); + /** * extracted from Worker and DFID worker * used in the {@link AbstractChecker#reportCoverage(IWorker[])} diff --git a/tlatools/src/tlc2/tool/ModelChecker.java b/tlatools/src/tlc2/tool/ModelChecker.java index e0b990f7bbbb5e963a3c6c92b3563fe61bc33671..0f052f5797aaa994533a0923e17a208926491bae 100644 --- a/tlatools/src/tlc2/tool/ModelChecker.java +++ b/tlatools/src/tlc2/tool/ModelChecker.java @@ -1,7 +1,8 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Wed 4 Jul 2007 at 17:46:34 PST by lamport -// modified on Fri Jan 18 11:33:51 PST 2002 by yuanyu +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Wed 4 Jul 2007 at 17:46:34 PST by lamport +// modified on Fri Jan 18 11:33:51 PST 2002 by yuanyu package tlc2.tool; @@ -164,8 +165,9 @@ public class ModelChecker extends AbstractChecker numberOfInitialStates = 0; // SZ Feb 23, 2009: ignore cancel on error reporting this.doInit(true); - } catch (Throwable e1) - { + } catch (FingerprintException fe){ + MP.printError(EC.TLC_FINGERPRINT_EXCEPTION, new String[]{fe.getTrace(), fe.getRootCause().getMessage()}); + } catch (Throwable e1) { // Assert.printStack(e); MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString()); } @@ -245,6 +247,9 @@ public class ModelChecker extends AbstractChecker try { this.doNext(this.predErrState, new ObjLongTable(10), new Worker(4223, this)); + } catch (FingerprintException e) + { + MP.printError(EC.TLC_FINGERPRINT_EXCEPTION, new String[]{e.getTrace(), e.getRootCause().getMessage()}); } catch (Throwable e) { // Assert.printStack(e); @@ -367,6 +372,7 @@ public class ModelChecker extends AbstractChecker boolean deadLocked = true; TLCState succState = null; SetOfStates liveNextStates = null; + int unseenSuccessorStates = 0; if (this.checkLiveness) { @@ -440,8 +446,9 @@ public class ModelChecker extends AbstractChecker // exploring this new state. Conversely, the state has to // be in the trace in case either invariant or implied action // checks want to print the trace. - long loc = this.trace.writeState(curState, fp); + long loc = this.trace.writeState(curState, fp, worker); succState.uid = loc; + unseenSuccessorStates++; } // For liveness checking: if (this.checkLiveness) @@ -631,12 +638,13 @@ public class ModelChecker extends AbstractChecker threadLocal.set(multiplier + 1); } } + worker.setOutDegree(unseenSuccessorStates); return false; } catch (Throwable e) { // Assert.printStack(e); boolean keep = ((e instanceof StackOverflowError) || (e instanceof OutOfMemoryError) - || (e instanceof AssertionError)); + || (e instanceof AssertionError)); synchronized (this) { if (this.setErrState(curState, succState, !keep)) @@ -839,7 +847,21 @@ public class ModelChecker extends AbstractChecker String.valueOf(this.theFPSet.size()), String.valueOf(this.theStateQueue.size()) }); if (success) { - MP.printMessage(EC.TLC_SEARCH_DEPTH, String.valueOf(this.trace.getLevelForReporting())); + MP.printMessage(EC.TLC_SEARCH_DEPTH, String.valueOf(this.trace.getLevelForFinalReporting())); + + // Aggregate outdegree from statistics maintained by individual workers. + final BucketStatistics aggOutDegree = new BucketStatistics("State Graph OutDegree"); + for (IWorker worker : workers) { + aggOutDegree.add(((Worker) worker).getOutDegree()); + } + // Print graph statistics iff data points were actually collected. + if (aggOutDegree.getObservations() > 0) { + MP.printMessage(EC.TLC_STATE_GRAPH_OUTDEGREE, + new String[] { Integer.toString(aggOutDegree.getMin()), + Long.toString(Math.round(aggOutDegree.getMean())), + Long.toString(Math.round(aggOutDegree.getPercentile(.95))), + Integer.toString(aggOutDegree.getMax()) }); + } } } @@ -873,202 +895,17 @@ public class ModelChecker extends AbstractChecker final double actualProb = anFpSet.checkFPs(); reportSuccess(fpSetSize, actualProb, numOfGenStates); } - - public static final void reportSuccess(final long numOfDistinctStates, final double actualProb, final long numOfGenStates) throws IOException - { - // shown as 'calculated' in Toolbox - final double optimisticProb = numOfDistinctStates * ((numOfGenStates - numOfDistinctStates) / Math.pow(2, 64)); - /* The following code added by LL on 3 Aug 2009 to print probabilities - * to only one decimal point. Removed by LL on 17 April 2012 because it - * seemed to report probabilities > 10-4 as probability 0. - */ - // final PrintfFormat fmt = new PrintfFormat("val = %.1G"); - // final String optimisticProbStr = fmt.sprintf(optimisticProb); - // final String actualProbStr = fmt.sprintf(actualProb); - - // Following two lines added by LL on 17 April 2012 - final String optimisticProbStr = "val = " + ProbabilityToString(optimisticProb, 2); - // shown as 'observed' in Toolbox - final String actualProbStr = "val = " + ProbabilityToString(actualProb, 2); - MP.printMessage(EC.TLC_SUCCESS, new String[] { optimisticProbStr, actualProbStr }); - } - - /** - * This method added by LL on 17 April 2012 to replace the use of the PrintfFormat - * method in reportSuccess. - * - * Returns a string representing the decimal representation of a probability to - * a given number of significant digits. If the input is not a probability, or if - * some error is found, then it returns the result of applying Double.toString(long) - * to the value. - * - * Warning: the code makes the following assumption: - * - Double.toString(v) returns a decimal representation of v of the - * form [d]* ["." [d]+ ["E" [+ | -] [d]+] where d is a decimal digit and - * [x] = 0 or 1 instance of x - * [x]* = any number of instances of x - * [x]+ = any non-zero number of instances of x - * x | y = an x or a y - * - * @param val - the probability represented as a long; must satisfy 0 <= val <= 1. - * @param significantDigits - the number of significant digits to include; must be > 0. - * @return - */ - private static final String ProbabilityToString(double val, int significantDigits) { - /* - * If val = 0 (which shouldn't happen), return "0.0" - */ - if (val == 0) { - return "0.0"; - } - - String valString = Double.toString(val) ; - int valStringLen = valString.length(); - - String result = ""; - int next = 0; // pointer to the next character in valString to examine. - int significantDigitsFound = 0; - - /* - * Skip past leading zeros. - */ - while ((next < valStringLen) && (valString.charAt(next) == '0')) { - next++ ; - } - - /* - * Append all the following digits to result, incrementing - * significantDigits for each one. - */ - while ( (next < valStringLen) && - Character.isDigit(valString.charAt(next))) { - result = result + valString.charAt(next); - significantDigitsFound++; - next++ ; - } - - /* - * IF next character is not "." - * THEN IF at end THEN return result - * ELSE return valString. - */ - if (next == valStringLen) { - return result; - } else if (valString.charAt(next) != '.') { - return valString; - } - - - /* - * IF significantDigitsFound >= significantDigits, - * THEN skip over "." and the following digits. - * (this should not happen) - * ELSE append "." to result ; - * IF significantDigitsFound = 0 - * THEN copy each of the following "0"s of valString to result; - * copy up to significantDigits - significantDigitsFound - * following digits of valString to result; - * IF next char of valString a digit >= "5" - * THEN propagate a carry backwards over the digits of result - * -- e.g., changing ".019" to ".020"; - * Skip over remaining digits of valString; - */ - if (significantDigitsFound >= significantDigits) { - next++ ; - while ( (next < valStringLen) && - Character.isDigit(valString.charAt(next))) { - next++ ; - } - } else { - next++; - result = result + "."; - if (significantDigitsFound == 0) { - while ((next < valStringLen) && (valString.charAt(next) == '0')) { - next++ ; - result = result + "0"; - } - } - while ((next < valStringLen) && - Character.isDigit(valString.charAt(next)) && - significantDigitsFound < significantDigits ) { - result = result + valString.charAt(next); - next++; - significantDigitsFound++; - } - if ((next < valStringLen) && - Character.isDigit(valString.charAt(next)) && - Character.digit(valString.charAt(next), 10) >= 5) { - int prev = result.length()-1; // the next digit of result to increment - boolean done = false; - while (!done) { - if (prev < 0) { - result = "1" + result; - done = true; - } else { - char prevChar = result.charAt(prev); - String front = result.substring(0, prev); - String back = result.substring(prev+1); - if (Character.isDigit(prevChar)) { - if (prevChar == '9') { - result = front + '0' + back; - } else { - result = front + Character.forDigit(Character.digit(prevChar, 10)+1, 10) + back; - done = true; - } - - } else { - // prevChar must be '.', so just continue - } - } - prev--; - } - } - while ((next < valStringLen) && - Character.isDigit(valString.charAt(next))) { - next++; - } - } - - /* - * IF next at end of valString or at "E" - * THEN copy remaining chars of valString to result; - * return result - * ELSE return valString - */ - if (next >= valStringLen) { - return result; - } - if (valString.charAt(next)=='E') { - next++; - result = result + "E"; - while (next < valStringLen) { - result = result + valString.charAt(next); - next++; - } - return result; - } - return valString; - } - -// The following method used for testing ProbabilityToString -// -// public static void main(String[] args) { -// double[] test = new double[] {.5, .0995, .00000001, 001.000, .0022341, -// .0022351, 3.14159E-12, -// 00.999, .002351111, 22.8E-14, 0.000E-12, -// 37, 0033D, 04.85, -35.3}; -// int i = 0; -// while (i < test.length) { -// System.out.println("" + i + ": " + Double.toString(test[i]) + " -> " + ProbabilityToString(test[i],2)); -// i++; -// } -// } /** * Spawn the worker threads */ protected IWorker[] startWorkers(AbstractChecker checker, int checkIndex) { + // Generation of initial states is done at this point. Thus set the + // number of workers on the fpset, for it to adapt any synchronization + // if necessary (e.g. OffHeapDiskFPSet). + this.theFPSet.incWorkers(this.workers.length); + for (int i = 0; i < this.workers.length; i++) { this.workers[i].start(); @@ -1076,14 +913,6 @@ public class ModelChecker extends AbstractChecker return this.workers; } - /** - * Work to be done prior entering to the worker loop - */ - protected void runTLCPreLoop() - { - // nothing to do in this implementation - } - /** * Process calculation. * @@ -1186,7 +1015,7 @@ public class ModelChecker extends AbstractChecker try { // Check if the state is a legal state if (!tool.isGoodState(curState)) { - MP.printError(EC.TLC_INITIAL_STATE, curState.toString()); + MP.printError(EC.TLC_INITIAL_STATE, new String[]{ "current state is not a legal state", curState.toString() }); return returnValue; } boolean inModel = tool.isInModel(curState); diff --git a/tlatools/src/tlc2/tool/Simulator.java b/tlatools/src/tlc2/tool/Simulator.java index 7bdeb0343076c361be15187232f9efb290188b2a..905896c416d8ada68e0bd77250660e73a5572e7c 100644 --- a/tlatools/src/tlc2/tool/Simulator.java +++ b/tlatools/src/tlc2/tool/Simulator.java @@ -169,7 +169,7 @@ public class Simulator implements Cancelable { theInitStates.deepNormalize(); // Start progress report thread: - ProgressReport report = new ProgressReport(); + final ProgressReport report = new ProgressReport(); report.start(); // Start simulating: @@ -301,6 +301,11 @@ public class Simulator implements Cancelable { // LL modified error message on 7 April 2012 this.printBehavior(EC.GENERAL, new String[] { MP.ECGeneralMsg("", e) }, curState, stateTrace); } + } finally { + report.isRunning = false; + synchronized (report) { + report.notify(); + } } } @@ -426,10 +431,13 @@ public class Simulator implements Cancelable { * Reports progress information */ final class ProgressReport extends Thread { + + volatile boolean isRunning = true; + public void run() { int count = TLCGlobals.coverageInterval / TLCGlobals.progressInterval; try { - while (true) { + while (isRunning) { synchronized (this) { this.wait(TLCGlobals.progressInterval); } diff --git a/tlatools/src/tlc2/tool/Spec.java b/tlatools/src/tlc2/tool/Spec.java index b56452b5a3c5834219ba046ba63b2296c2e33953..81bed024e392ba7acf726b38e29d5c56c2520e61 100644 --- a/tlatools/src/tlc2/tool/Spec.java +++ b/tlatools/src/tlc2/tool/Spec.java @@ -951,6 +951,20 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable if (def.getArity() != 0) { Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "invariant", name }); + } + // MK 07/25/2017: Check if the invariant is a valid state predicate and produce + // a meaningful warning otherwise. With this enhancement, a rare bug in TLC's + // level-checking surfaced for which we don't have a fix right now. Fortunately, + // the bug is rather unlikely which is why TLC simply produces a warning for now + // if it "thinks" a user might be affected by the bug. + // see LevelNode.java line 590ff, Test52, TestInvalidInvariant, and related files + // for more context. + if (def.getLevel() >= 2) + { + if (!def.getBody().levelParams.isEmpty()) { + Assert.fail(EC.TLC_INVARIANT_VIOLATED_LEVEL, new String[] { def.getName().toString(), "includeWarning" }); + } + Assert.fail(EC.TLC_INVARIANT_VIOLATED_LEVEL, def.getName().toString()); } this.invNameVec.addElement(name); this.invVec.addElement(new Action(def.getBody(), Context.Empty)); @@ -1559,7 +1573,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable OpDefNode def = (OpDefNode) type; if (def.getArity() != 0) { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type cinstraint", name }); + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type constraint", name }); } return def.getBody(); diff --git a/tlatools/src/tlc2/tool/TLCTrace.java b/tlatools/src/tlc2/tool/TLCTrace.java index 51629a2bf89c42bba0ea970f323938fa4e889e9f..bbdb903fc37a9e7f029b3c983591f5ccd0ef9b07 100644 --- a/tlatools/src/tlc2/tool/TLCTrace.java +++ b/tlatools/src/tlc2/tool/TLCTrace.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.output.OutputCollector; @@ -28,6 +29,11 @@ public class TLCTrace { private static String filename; private BufferedRandomAccessFile raf; private long lastPtr; + // TODO This arrays causes coherence. This problem is easily removed by + // storing each ptr in lastPtrs in the corresponding worker. That's a little + // bit ugly though in terms of separation of concerns. The current solution + // confines all related aspects to TLCTrace. + private final long[] lastPtrs = new long[TLCGlobals.getNumWorkers()]; private TraceApp tool; public TLCTrace(String metadir, String specFile, TraceApp tool) @@ -59,6 +65,13 @@ public class TLCTrace { return writeState(predecessor.uid, aFingerprint); } + public final synchronized long writeState(final TLCState predecessor, final long aFingerprint, final IWorker worker) + throws IOException { + final long lastPtr = writeState(predecessor.uid, aFingerprint); + this.lastPtrs[worker.myGetId()] = lastPtr; + return lastPtr; + } + /** * @param predecessorLoc The location of the state predecessor * @param fp A finger print @@ -89,6 +102,33 @@ public class TLCTrace { return this.raf.readLong(); } + /** + * The level is the length of the longest path in the execution trees + * (forest) (his forest is stored in the trace file). + * <p> + * With multiple workers, the level is an approximation. With multiple + * workers, the forest has an unknown number of leafs and we do not know the + * position of leafs in the trace file. + * <p> + * We know however the vertices, that the set of workers last added to the + * forest. Thus, we calculate the lengths of each path or path fragment + * from those vertices. The maximum length is then our approximation of the + * longest path. It's not elegant but good enough for now. + * + * @see TLCTrace#getLevel() + */ + public final synchronized int getLevelForFinalReporting() throws IOException { + // TODO We generally assume the length and the number of workers to be + // relatively small to not warrant parallelization. The current + // getLevel(lastPtr) cannot be called concurrently, because it uses a + // single BufferedRandomAccessFile. + int max = 0; + for (long lastPtr : lastPtrs) { + max = Math.max(max, lastPtr > 0 ? getLevel(lastPtr) : 0); + } + return max; + } + /** * Returns the level (monotonically increasing)! * @@ -109,7 +149,7 @@ public class TLCTrace { * * @see TLCTrace#getLevel() */ - public final int getLevelForReporting() throws IOException { + public final synchronized int getLevelForReporting() throws IOException { final int calculatedLevel = getLevel(this.lastPtr); if (calculatedLevel > previousLevel) { previousLevel = calculatedLevel; @@ -124,18 +164,51 @@ public class TLCTrace { /** * @see TLCTrace#getLevel(long) + * @return 1 to the length of the longest behavior found so far. */ public final int getLevel() throws IOException { - // This assumption (lastPtr) only holds for the TLC in non-parallel mode. + // This assumption (lastPtr) only holds for the TLC in non-parallel mode. // Generally the last line (logically a state) is not necessarily - // on the highest level of the state tree. This is only the case if - // states are explored strictly by breadth-first search. + // on the highest level of the state tree, which is only true if + // states are explored with strict breadth-first search. + // + // The (execution) trace is a forest of one to n trees, where each path + // in the forest represents the order in which states have been generated + // by the workers. + // The algorithm, with which the diameter is approximated from the trace, + // is pretty simple; too simple. The trace is constantly written to the .st + // file where each "record" in the file is a link from a successor state to + // its predecessor state. Thus, the link is a position in the trace file + // where the predecessor state - actually its fingerprint - is stored. At + // the end of the trace file, there are up to m leaf records for which no + // successors have been appended (yet, assuming there are any). + // + // Once the workers have terminated, TLC traverses the trace from a leaf record + // back to a root record. This height is what is reported as the diameter. + // The selection, from what leaf record TLC starts the traversal, is based on + // the last record inserted into the trace file. If this record is one with a + // low height (because its corresponding worker waited most of the time), the + // diameter will thus be underreport. If, on the other hand, the last record + // happens to be one with a large height, the diameter will be overreported. + // + // The selection of the leaf record is the source of the algorithm's + // non-determinism. With a single worker, the last record in the trace is + // always the same which always corresponds to the longest behavior found so + // far (strict BFS). This invariant does not hold with multiple workers. + // + // Obviously, with multiple workers the approximation of the diameter will + // improve with the size of the state graph. Assuming a well-shaped state graph, + // we can argue that the approximation is good enough and document, that its + // value can be anything from 1 to the longest behavior found so far. return getLevel(this.lastPtr); } /** - * @param startLoc The start location (pointer) from where the level (height) of the state tree should be calculated - * @return The level (height) of the state tree. + * @param startLoc + * The start location (pointer) from where the level (height) of + * the path in the execution tree should be calculated. + * @return The level (height) of the path in the execution tree (the trace) + * starting at startLoc. * @throws IOException */ public synchronized final int getLevel(long startLoc) throws IOException { diff --git a/tlatools/src/tlc2/tool/Tool.java b/tlatools/src/tlc2/tool/Tool.java index bb928bd53e6048cead230b69212cbb24357b8a78..b5005e352a2eba1b48c0f02a3ce231d79ed99cd1 100644 --- a/tlatools/src/tlc2/tool/Tool.java +++ b/tlatools/src/tlc2/tool/Tool.java @@ -1,7 +1,8 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Thu 2 Aug 2007 at 10:25:48 PST by lamport -// modified on Fri Jan 4 22:46:57 PST 2002 by yuanyu +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Thu 2 Aug 2007 at 10:25:48 PST by lamport +// modified on Fri Jan 4 22:46:57 PST 2002 by yuanyu package tlc2.tool; @@ -58,35 +59,36 @@ import tlc2.value.ValueEnumeration; import tlc2.value.ValueExcept; import tlc2.value.ValueVec; import util.Assert; +import util.Assert.TLCRuntimeException; import util.FilenameToStream; import util.UniqueString; /** * This class provides useful methods for tools like model checker * and simulator. - * + * * It's instance serves as a spec handle * This is one of two places in TLC, where not all messages are retrieved from the message printer, - * but constructed just here in the code. - * + * but constructed just here in the code. + * * @version $Id$ */ -public class Tool - extends Spec - implements ValueConstants, ToolGlobals, TraceApp +public class Tool + extends Spec + implements ValueConstants, ToolGlobals, TraceApp { protected Action[] actions; // the list of TLA actions. private CallStack callStack; // the call stack. private Vect actionVec = new Vect(10); - + /** - * Creates a new tool handle + * Creates a new tool handle * @param specDir * @param specFile * @param configFile */ - public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver) + public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver) { super(specDir, specFile, configFile, resolver); this.actions = null; @@ -97,10 +99,10 @@ public class Tool * Initialization. Any Tool object must call it before doing anything. * @param spec - <code>null</code> or a filled spec object from previous SANY run */ - public final SpecObj init(boolean preprocess, SpecObj spec) + public final SpecObj init(boolean preprocess, SpecObj spec) { - - // Parse and process this spec. + + // Parse and process this spec. // It takes care of all overrides. // SZ Feb 20, 2009: added spec reference, // if not null it is just used instead of re-parsing @@ -121,25 +123,25 @@ public class Tool // Finally, process the config file. super.processConfig(); - + return processSpec; } - public final void setCallStack() - { - this.callStack = new CallStack(); + public final void setCallStack() + { + this.callStack = new CallStack(); } - public final CallStack getCallStack() + public final CallStack getCallStack() { return this.callStack; } - + /** - * This method returns the set of all possible actions of the + * This method returns the set of all possible actions of the * spec, and sets the actions field of this object. In fact, we * could simply treat the next predicate as one "giant" action. - * But for efficiency, we preprocess the next state predicate by + * But for efficiency, we preprocess the next state predicate by * splitting it into a set of actions for the maximum prefix * of disjunction and existential quantification. */ @@ -161,8 +163,8 @@ public class Tool return this.actions; } - - + + private final void getActions(SemanticNode next, Context con) { switch (next.getKind()) { case OpApplKind: @@ -282,7 +284,7 @@ public class Tool return; } case OPCODE_dl: // DisjList - case OPCODE_lor: + case OPCODE_lor: { for (int i = 0; i < args.length; i++) { this.getActions(args[i], con); @@ -306,11 +308,11 @@ public class Tool * probably make tools like TLC useless. */ public final StateVec getInitStates() { - final StateVec initStates = new StateVec(0); - getInitStates(initStates); - return initStates; + final StateVec initStates = new StateVec(0); + getInitStates(initStates); + return initStates; } - + public final void getInitStates(IStateFunctor functor) { Vect init = this.getInitStateSpec(); ActionItemList acts = ActionItemList.Empty; @@ -343,60 +345,68 @@ public class Tool private final void getInitStates(SemanticNode init, ActionItemList acts, Context c, TLCState ps, IStateFunctor states) { - switch (init.getKind()) { - case OpApplKind: - { - OpApplNode init1 = (OpApplNode)init; - this.getInitStatesAppl(init1, acts, c, ps, states); - return; - } - case LetInKind: - { - LetInNode init1 = (LetInNode)init; - this.getInitStates(init1.getBody(), acts, c, ps, states); - return; - } - case SubstInKind: - { - SubstInNode init1 = (SubstInNode)init; - Subst[] subs = init1.getSubsts(); - Context c1 = c; - for (int i = 0; i < subs.length; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - this.getInitStates(init1.getBody(), acts, c1, ps, states); - return; - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode init1 = (APSubstInNode)init; - Subst[] subs = init1.getSubsts(); - Context c1 = c; - for (int i = 0; i < subs.length; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - this.getInitStates(init1.getBody(), acts, c1, ps, states); - return; + if (this.callStack != null) this.callStack.push(init); + try { + switch (init.getKind()) { + case OpApplKind: + { + OpApplNode init1 = (OpApplNode)init; + this.getInitStatesAppl(init1, acts, c, ps, states); + return; + } + case LetInKind: + { + LetInNode init1 = (LetInNode)init; + this.getInitStates(init1.getBody(), acts, c, ps, states); + return; + } + case SubstInKind: + { + SubstInNode init1 = (SubstInNode)init; + Subst[] subs = init1.getSubsts(); + Context c1 = c; + for (int i = 0; i < subs.length; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); + } + this.getInitStates(init1.getBody(), acts, c1, ps, states); + return; + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + APSubstInNode init1 = (APSubstInNode)init; + Subst[] subs = init1.getSubsts(); + Context c1 = c; + for (int i = 0; i < subs.length; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); + } + this.getInitStates(init1.getBody(), acts, c1, ps, states); + return; + } + // LabelKind class added by LL on 13 Jun 2007 + case LabelKind: + { + LabelNode init1 = (LabelNode)init; + this.getInitStates(init1.getBody(), acts, c, ps, states); + return; + } + default: + { + Assert.fail("The init state relation is not a boolean expression.\n" + init); + } } - - - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode init1 = (LabelNode)init; - this.getInitStates(init1.getBody(), acts, c, ps, states); - return; - } - default: - { - Assert.fail("The init state relation is not a boolean expression.\n" + init); - } + } catch (TLCRuntimeException | EvalException e) { + if (this.callStack != null) { + // Freeze the callStack to ignore subsequent pop operations. This is + // necessary to ignore the callStack#pop calls in the finally blocks when the + // Java call stack gets unwounded. + this.callStack.freeze(); + } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } } @@ -413,290 +423,307 @@ public class Tool private final void getInitStatesAppl(OpApplNode init, ActionItemList acts, Context c, TLCState ps, IStateFunctor states) { - ExprOrOpArgNode[] args = init.getArgs(); - int alen = args.length; - SymbolNode opNode = init.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - if (opcode == 0) { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - Object val = this.lookup(opNode, c, ps, false); + if (this.callStack != null) this.callStack.push(init); + try { + ExprOrOpArgNode[] args = init.getArgs(); + int alen = args.length; + SymbolNode opNode = init.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); - if (val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); if (opcode == 0) { - // Context c1 = this.getOpContext(opDef, args, c, false); - Context c1 = this.getOpContext(opDef, args, c, true); - this.getInitStates(opDef.getBody(), acts, c1, ps, states); - return; - } - } - // Added 13 Nov 2009 by LL to fix Yuan's fix. - /********************************************************************* - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - if (val instanceof ThmOrAssumpDefNode) { - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - Context c1 = this.getOpContext(opDef, args, c, true); - this.getInitStates(opDef.getBody(), acts, c1, ps, states); - return; - } + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, ps, false); + + if (val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { + // Context c1 = this.getOpContext(opDef, args, c, false); + Context c1 = this.getOpContext(opDef, args, c, true); + this.getInitStates(opDef.getBody(), acts, c1, ps, states); + return; + } + } + // Added 13 Nov 2009 by LL to fix Yuan's fix. + /********************************************************************* + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + if (val instanceof ThmOrAssumpDefNode) { + ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + Context c1 = this.getOpContext(opDef, args, c, true); + this.getInitStates(opDef.getBody(), acts, c1, ps, states); + return; + } - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - if (lv.val == null || lv.val == ValUndef) { - this.getInitStates(lv.expr, acts, lv.con, ps, states); - return; - } - val = lv.val; - } + if (val instanceof LazyValue) { + LazyValue lv = (LazyValue)val; + if (lv.val == null || lv.val == ValUndef) { + this.getInitStates(lv.expr, acts, lv.con, ps, states); + return; + } + val = lv.val; + } - Object bval = val; - if (alen == 0) { - if (val instanceof MethodValue) { - bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); - } - } - else { - if (val instanceof OpValue) { - Applicable opVal = (Applicable)val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) { - argVals[i] = this.eval(args[i], c, ps); - } - // apply the operator: - bval = opVal.apply(argVals, EvalControl.Clear); - } - } + Object bval = val; + if (alen == 0) { + if (val instanceof MethodValue) { + bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); + } + } + else { + if (val instanceof OpValue) { + Applicable opVal = (Applicable)val; + Value[] argVals = new Value[alen]; + // evaluate the actuals: + for (int i = 0; i < alen; i++) { + argVals[i] = this.eval(args[i], c, ps); + } + // apply the operator: + bval = opVal.apply(argVals, EvalControl.Clear); + } + } - if (opcode == 0) + if (opcode == 0) + { + if (!(bval instanceof BoolValue)) { - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "initial states", "boolean", - bval.toString(), init.toString() }); - } - if (((BoolValue) bval).val) - { - this.getInitStates(acts, ps, states); - } - return; + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "initial states", "boolean", + bval.toString(), init.toString() }); + } + if (((BoolValue) bval).val) + { + this.getInitStates(acts, ps, states); } + return; + } } - switch (opcode) { - case OPCODE_dl: // DisjList - case OPCODE_lor: - { - for (int i = 0; i < alen; i++) { - this.getInitStates(args[i], acts, c, ps, states); - } - return; - } - case OPCODE_cl: // ConjList - case OPCODE_land: - { - for (int i = alen-1; i > 0; i--) { - acts = acts.cons(args[i], c, i); - } - this.getInitStates(args[0], acts, c, ps, states); - return; - } - case OPCODE_be: // BoundedExists - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear); - Context c1; - while ((c1 = Enum.nextElement()) != null) { - this.getInitStates(body, acts, c1, ps, states); - } - return; - } - case OPCODE_bf: // BoundedForall - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear); - Context c1 = Enum.nextElement(); - if (c1 == null) { - this.getInitStates(acts, ps, states); - } - else { - ActionItemList acts1 = acts; - Context c2; - while ((c2 = Enum.nextElement()) != null) { - acts1 = acts1.cons(body, c2, -1); + switch (opcode) { + case OPCODE_dl: // DisjList + case OPCODE_lor: + { + for (int i = 0; i < alen; i++) { + this.getInitStates(args[i], acts, c, ps, states); + } + return; } - this.getInitStates(body, acts1, c1, ps, states); - } - return; - } - case OPCODE_ite: // IfThenElse - { - Value guard = this.eval(args[0], c, ps); - if (!(guard instanceof BoolValue)) { - Assert.fail("In computing initial states, a non-boolean expression (" + - guard.getKindString() + ") was used as the condition " + - "of an IF.\n" + init); - } - int idx = (((BoolValue)guard).val) ? 1 : 2; - this.getInitStates(args[idx], acts, c, ps, states); - return; - } - case OPCODE_case: // Case - { - SemanticNode other = null; - for (int i = 0; i < alen; i++) { - OpApplNode pair = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pair.getArgs(); - if (pairArgs[0] == null) { - other = pairArgs[1]; + case OPCODE_cl: // ConjList + case OPCODE_land: + { + for (int i = alen-1; i > 0; i--) { + acts = acts.cons(args[i], c, i); + } + this.getInitStates(args[0], acts, c, ps, states); + return; } - else { - Value bval = this.eval(pairArgs[0], c, ps); - if (!(bval instanceof BoolValue)) { - Assert.fail("In computing initial states, a non-boolean expression (" + - bval.getKindString() + ") was used as a guard condition" + - " of a CASE.\n" + pairArgs[1]); + case OPCODE_be: // BoundedExists + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear); + Context c1; + while ((c1 = Enum.nextElement()) != null) { + this.getInitStates(body, acts, c1, ps, states); } - if (((BoolValue)bval).val) { - this.getInitStates(pairArgs[1], acts, c, ps, states); - return; + return; + } + case OPCODE_bf: // BoundedForall + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear); + Context c1 = Enum.nextElement(); + if (c1 == null) { + this.getInitStates(acts, ps, states); + } + else { + ActionItemList acts1 = acts; + Context c2; + while ((c2 = Enum.nextElement()) != null) { + acts1 = acts1.cons(body, c2, -1); + } + this.getInitStates(body, acts1, c1, ps, states); } + return; } - } - if (other == null) { - Assert.fail("In computing initial states, TLC encountered a CASE with no" + - " conditions true.\n" + init); - } - this.getInitStates(other, acts, c, ps, states); - return; - } - case OPCODE_fa: // FcnApply - { - Value fval = this.eval(args[0], c, ps); - if (fval instanceof FcnLambdaValue) { - FcnLambdaValue fcn = (FcnLambdaValue)fval; - if (fcn.fcnRcd == null) { - Context c1 = this.getFcnContext(fcn, args, c, ps, TLCState.Empty, EvalControl.Clear); - this.getInitStates(fcn.body, acts, c1, ps, states); + case OPCODE_ite: // IfThenElse + { + Value guard = this.eval(args[0], c, ps); + if (!(guard instanceof BoolValue)) { + Assert.fail("In computing initial states, a non-boolean expression (" + + guard.getKindString() + ") was used as the condition " + + "of an IF.\n" + init); + } + int idx = (((BoolValue)guard).val) ? 1 : 2; + this.getInitStates(args[idx], acts, c, ps, states); return; } - fval = fcn.fcnRcd; - } - else if (!(fval instanceof Applicable)) { - Assert.fail("In computing initial states, a non-function (" + - fval.getKindString() + ") was applied as a function.\n" + init); - } + case OPCODE_case: // Case + { + SemanticNode other = null; + for (int i = 0; i < alen; i++) { + OpApplNode pair = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pair.getArgs(); + if (pairArgs[0] == null) { + other = pairArgs[1]; + } + else { + Value bval = this.eval(pairArgs[0], c, ps); + if (!(bval instanceof BoolValue)) { + Assert.fail("In computing initial states, a non-boolean expression (" + + bval.getKindString() + ") was used as a guard condition" + + " of a CASE.\n" + pairArgs[1]); + } + if (((BoolValue)bval).val) { + this.getInitStates(pairArgs[1], acts, c, ps, states); + return; + } + } + } + if (other == null) { + Assert.fail("In computing initial states, TLC encountered a CASE with no" + + " conditions true.\n" + init); + } + this.getInitStates(other, acts, c, ps, states); + return; + } + case OPCODE_fa: // FcnApply + { + Value fval = this.eval(args[0], c, ps); + if (fval instanceof FcnLambdaValue) { + FcnLambdaValue fcn = (FcnLambdaValue)fval; + if (fcn.fcnRcd == null) { + Context c1 = this.getFcnContext(fcn, args, c, ps, TLCState.Empty, EvalControl.Clear); + this.getInitStates(fcn.body, acts, c1, ps, states); + return; + } + fval = fcn.fcnRcd; + } + else if (!(fval instanceof Applicable)) { + Assert.fail("In computing initial states, a non-function (" + + fval.getKindString() + ") was applied as a function.\n" + init); + } Applicable fcn = (Applicable) fval; Value argVal = this.eval(args[1], c, ps); Value bval = fcn.apply(argVal, EvalControl.Clear); if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "initial states", "boolean", - init.toString() }); + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "initial states", "boolean", + init.toString() }); + } + if (((BoolValue)bval).val) { + this.getInitStates(acts, ps, states); + } + return; + } + case OPCODE_eq: + { + SymbolNode var = this.getVar(args[0], c, false); + if (var == null || var.getName().getVarLoc() < 0) { + Value bval = this.eval(init, c, ps); + if (!((BoolValue)bval).val) { + return; + } + } + else { + UniqueString varName = var.getName(); + Value lval = ps.lookup(varName); + Value rval = this.eval(args[1], c, ps); + if (lval == null) { + ps = ps.bind(varName, rval, init); + this.getInitStates(acts, ps, states); + ps.unbind(varName); + return; + } + else { + if (!lval.equals(rval)) { + return; + } + } } - if (((BoolValue)bval).val) { - this.getInitStates(acts, ps, states); - } - return; - } - case OPCODE_eq: - { - SymbolNode var = this.getVar(args[0], c, false); - if (var == null || var.getName().getVarLoc() < 0) { - Value bval = this.eval(init, c, ps); - if (!((BoolValue)bval).val) return; - } - else { - UniqueString varName = var.getName(); - Value lval = ps.lookup(varName); - Value rval = this.eval(args[1], c, ps); - if (lval == null) { - ps = ps.bind(varName, rval, init); this.getInitStates(acts, ps, states); - ps.unbind(varName); return; } - else { - if (!lval.equals(rval)) return; + case OPCODE_in: + { + SymbolNode var = this.getVar(args[0], c, false); + if (var == null || var.getName().getVarLoc() < 0) { + Value bval = this.eval(init, c, ps); + if (!((BoolValue)bval).val) { + return; + } + } + else { + UniqueString varName = var.getName(); + Value lval = ps.lookup(varName); + Value rval = this.eval(args[1], c, ps); + if (lval == null) { + if (!(rval instanceof Enumerable)) { + Assert.fail("In computing initial states, the right side of \\IN" + + " is not enumerable.\n" + init); + } + ValueEnumeration Enum = ((Enumerable)rval).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + ps.bind(varName, elem, init); + this.getInitStates(acts, ps, states); + ps.unbind(varName); + } + return; + } + else { + if (!rval.member(lval)) { + return; + } + } + } + this.getInitStates(acts, ps, states); + return; } - } - this.getInitStates(acts, ps, states); - return; - } - case OPCODE_in: - { - SymbolNode var = this.getVar(args[0], c, false); - if (var == null || var.getName().getVarLoc() < 0) { - Value bval = this.eval(init, c, ps); - if (!((BoolValue)bval).val) return; - } - else { - UniqueString varName = var.getName(); - Value lval = ps.lookup(varName); - Value rval = this.eval(args[1], c, ps); - if (lval == null) { - if (!(rval instanceof Enumerable)) { - Assert.fail("In computing initial states, the right side of \\IN" + - " is not enumerable.\n" + init); - } - ValueEnumeration Enum = ((Enumerable)rval).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - ps.bind(varName, elem, init); + case OPCODE_implies: + { + Value lval = this.eval(args[0], c, ps); + if (!(lval instanceof BoolValue)) { + Assert.fail("In computing initial states of a predicate of form" + + " P => Q, P was " + lval.getKindString() + "\n." + init); + } + if (((BoolValue)lval).val) { + this.getInitStates(args[1], acts, c, ps, states); + } + else { this.getInitStates(acts, ps, states); - ps.unbind(varName); } return; } - else { - if (!rval.member(lval)) return; - } - } - this.getInitStates(acts, ps, states); - return; - } - case OPCODE_implies: - { - Value lval = this.eval(args[0], c, ps); - if (!(lval instanceof BoolValue)) { - Assert.fail("In computing initial states of a predicate of form" + - " P => Q, P was " + lval.getKindString() + "\n." + init); - } - if (((BoolValue)lval).val) { - this.getInitStates(args[1], acts, c, ps, states); - } - else { - this.getInitStates(acts, ps, states); - } - return; - } - // The following case added by LL on 13 Nov 2009 to handle subexpression names. - case OPCODE_nop: - { - this.getInitStates(args[0], acts, c, ps, states); - return; - } - default: - { - // For all the other builtin operators, simply evaluate: - Value bval = this.eval(init, c, ps); - if (!(bval instanceof BoolValue)) { - - Assert.fail("In computing initial states, TLC expected a boolean expression," + - "\nbut instead found " + bval + ".\n" + init); + // The following case added by LL on 13 Nov 2009 to handle subexpression names. + case OPCODE_nop: + { + this.getInitStates(args[0], acts, c, ps, states); + return; } - if (((BoolValue)bval).val) { - this.getInitStates(acts, ps, states); + default: + { + // For all the other builtin operators, simply evaluate: + Value bval = this.eval(init, c, ps); + if (!(bval instanceof BoolValue)) { + + Assert.fail("In computing initial states, TLC expected a boolean expression," + + "\nbut instead found " + bval + ".\n" + init); + } + if (((BoolValue)bval).val) { + this.getInitStates(acts, ps, states); + } + return; + } } - return; - } + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } } @@ -714,59 +741,63 @@ public class Tool private final TLCState getNextStates(SemanticNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss) { - switch (pred.getKind()) { - case OpApplKind: - { - OpApplNode pred1 = (OpApplNode)pred; - return this.getNextStatesAppl(pred1, acts, c, s0, s1, nss); - } - case LetInKind: - { - LetInNode pred1 = (LetInNode)pred; - return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss); - } - case SubstInKind: - { - SubstInNode pred1 = (SubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss); - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode pred1 = (APSubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss); + if (this.callStack != null) this.callStack.push(pred); + try { + switch (pred.getKind()) { + case OpApplKind: + { + OpApplNode pred1 = (OpApplNode)pred; + return this.getNextStatesAppl(pred1, acts, c, s0, s1, nss); + } + case LetInKind: + { + LetInNode pred1 = (LetInNode)pred; + return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss); + } + case SubstInKind: + { + SubstInNode pred1 = (SubstInNode)pred; + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); + } + return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss); + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + APSubstInNode pred1 = (APSubstInNode)pred; + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); + } + return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss); + } + // LabelKind class added by LL on 13 Jun 2007 + case LabelKind: + { + LabelNode pred1 = (LabelNode)pred; + return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss); + } + default: + { + Assert.fail("The next state relation is not a boolean expression.\n" + pred); + } } - - - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode pred1 = (LabelNode)pred; - return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss); - } - default: - { - Assert.fail("The next state relation is not a boolean expression.\n" + pred); - } + return s1; + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } - return s1; } private final TLCState getNextStates(ActionItemList acts, TLCState s0, TLCState s1, @@ -783,9 +814,7 @@ public class Tool Context c = acts.carContext(); ActionItemList acts1 = acts.cdr(); if (kind > 0) { - if (this.callStack != null) this.callStack.push(pred); resState = this.getNextStates(pred, acts1, c, s0, s1, nss); - if (this.callStack != null) this.callStack.pop(); } else if (kind == -1) { resState = this.getNextStates(pred, acts1, c, s0, s1, nss); @@ -801,1285 +830,1327 @@ public class Tool } } } - return resState; } - + private final TLCState getNextStatesAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss) { - ExprOrOpArgNode[] args = pred.getArgs(); - int alen = args.length; - SymbolNode opNode = pred.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - if (opcode == 0) { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - Object val = this.lookup(opNode, c, s0, false); + if (this.callStack != null) this.callStack.push(pred); + try { + ExprOrOpArgNode[] args = pred.getArgs(); + int alen = args.length; + SymbolNode opNode = pred.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); - if (val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); if (opcode == 0) { - // Context c1 = this.getOpContext(opDef, args, c, false); - Context c1 = this.getOpContext(opDef, args, c, true); - return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss); - } - } - - // Added by LL 13 Nov 2009 to fix Yuan's fix - /********************************************************************* - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - if (val instanceof ThmOrAssumpDefNode) { - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; - Context c1 = this.getOpContext(opDef, args, c, true); - return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss); - } + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, s0, false); + + if (val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { + // Context c1 = this.getOpContext(opDef, args, c, false); + Context c1 = this.getOpContext(opDef, args, c, true); + return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss); + } + } - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - if (lv.val == null || lv.val == ValUndef) { - return this.getNextStates(lv.expr, acts, lv.con, s0, s1, nss); - } - val = lv.val; - } + // Added by LL 13 Nov 2009 to fix Yuan's fix + /********************************************************************* + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + if (val instanceof ThmOrAssumpDefNode) { + ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; + Context c1 = this.getOpContext(opDef, args, c, true); + return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss); + } - Object bval = val; - if (alen == 0) { - if (val instanceof MethodValue) { - bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); - } - } - else { - if (val instanceof OpValue) { - Applicable opVal = (Applicable)val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) { - argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Clear); - } - // apply the operator: - bval = opVal.apply(argVals, EvalControl.Clear); - } - } + if (val instanceof LazyValue) { + LazyValue lv = (LazyValue)val; + if (lv.val == null || lv.val == ValUndef) { + return this.getNextStates(lv.expr, acts, lv.con, s0, s1, nss); + } + val = lv.val; + } - if (opcode == 0) + Object bval = val; + if (alen == 0) { + if (val instanceof MethodValue) { + bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); + } + } + else { + if (val instanceof OpValue) { + Applicable opVal = (Applicable)val; + Value[] argVals = new Value[alen]; + // evaluate the actuals: + for (int i = 0; i < alen; i++) { + argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Clear); + } + // apply the operator: + bval = opVal.apply(argVals, EvalControl.Clear); + } + } + + if (opcode == 0) + { + if (!(bval instanceof BoolValue)) { - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", - bval.toString(), pred.toString() }); - } - if (((BoolValue) bval).val) - { - return this.getNextStates(acts, s0, s1, nss); - } - return s1; + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", + bval.toString(), pred.toString() }); } + if (((BoolValue) bval).val) + { + return this.getNextStates(acts, s0, s1, nss); + } + return s1; + } } - TLCState resState = s1; - switch (opcode) { - case OPCODE_cl: // ConjList - case OPCODE_land: - { - ActionItemList acts1 = acts; - for (int i = alen - 1; i > 0; i--) { - acts1 = acts1.cons(args[i], c, i); - } - if (this.callStack != null) this.callStack.push(args[0]); - resState = this.getNextStates(args[0], acts1, c, s0, s1, nss); - if (this.callStack != null) this.callStack.pop(); - return resState; - } - case OPCODE_dl: // DisjList - case OPCODE_lor: - { - for (int i = 0; i < alen; i++) { - if (this.callStack != null) this.callStack.push(args[i]); - resState = this.getNextStates(args[i], acts, c, s0, resState, nss); - if (this.callStack != null) this.callStack.pop(); - } - return resState; - } - case OPCODE_be: // BoundedExists - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear); - Context c1; - while ((c1 = Enum.nextElement()) != null) { - resState = this.getNextStates(body, acts, c1, s0, resState, nss); - } - return resState; - } - case OPCODE_bf: // BoundedForall - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear); - Context c1 = Enum.nextElement(); - if (c1 == null) { - resState = this.getNextStates(acts, s0, s1, nss); - } - else { - ActionItemList acts1 = acts; - Context c2; - while ((c2 = Enum.nextElement()) != null) { - acts1 = acts1.cons(body, c2, -1); + TLCState resState = s1; + switch (opcode) { + case OPCODE_cl: // ConjList + case OPCODE_land: + { + ActionItemList acts1 = acts; + for (int i = alen - 1; i > 0; i--) { + acts1 = acts1.cons(args[i], c, i); + } + return this.getNextStates(args[0], acts1, c, s0, s1, nss); } - resState = this.getNextStates(body, acts1, c1, s0, s1, nss); - } - return resState; - } - case OPCODE_fa: // FcnApply - { - Value fval = this.eval(args[0], c, s0, s1, EvalControl.KeepLazy); - if (fval instanceof FcnLambdaValue) { - FcnLambdaValue fcn = (FcnLambdaValue)fval; - if (fcn.fcnRcd == null) { - Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Clear); - return this.getNextStates(fcn.body, acts, c1, s0, s1, nss); - } - fval = fcn.fcnRcd; - } - if (!(fval instanceof Applicable)) { - Assert.fail("In computing next states, a non-function (" + - fval.getKindString() + ") was applied as a function.\n" + pred); - } - Applicable fcn = (Applicable)fval; - Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Clear); - Value bval = fcn.apply(argVal, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "next states", "boolean", - pred.toString() }); - } - if (((BoolValue)bval).val) { - return this.getNextStates(acts, s0, s1, nss); - } - return resState; - } - case OPCODE_aa: // AngleAct <A>_e - { - ActionItemList acts1 = acts.cons(args[1], c, -3); - return this.getNextStates(args[0], acts1, c, s0, s1, nss); - } - case OPCODE_sa: // [A]_e - { - /* The following two lines of code did not work, and were changed by - * YuanYu to mimic the way \/ works. Change made - * 11 Mar 2009, with LL sitting next to him. - */ - // this.getNextStates(args[0], acts, c, s0, s1, nss); - // return this.processUnchanged(args[1], acts, c, s0, s1, nss); - resState = this.getNextStates(args[0], acts, c, s0, resState, nss); - return this.processUnchanged(args[1], acts, c, s0, resState, nss); - } - case OPCODE_ite: // IfThenElse - { - Value guard = this.eval(args[0], c, s0, s1, EvalControl.Clear); - if (!(guard instanceof BoolValue)) { - Assert.fail("In computing next states, a non-boolean expression (" + - guard.getKindString() + ") was used as the condition of" + - " an IF." + pred); - } - if (((BoolValue)guard).val) { - return this.getNextStates(args[1], acts, c, s0, s1, nss); - } - else { - return this.getNextStates(args[2], acts, c, s0, s1, nss); - } - } - case OPCODE_case: // Case - { - SemanticNode other = null; - for (int i = 0; i < alen; i++) { - OpApplNode pair = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pair.getArgs(); - if (pairArgs[0] == null) { - other = pairArgs[1]; + case OPCODE_dl: // DisjList + case OPCODE_lor: + { + for (int i = 0; i < alen; i++) { + resState = this.getNextStates(args[i], acts, c, s0, resState, nss); + } + return resState; } - else { - Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Clear); + case OPCODE_be: // BoundedExists + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear); + Context c1; + while ((c1 = Enum.nextElement()) != null) { + resState = this.getNextStates(body, acts, c1, s0, resState, nss); + } + return resState; + } + case OPCODE_bf: // BoundedForall + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear); + Context c1 = Enum.nextElement(); + if (c1 == null) { + resState = this.getNextStates(acts, s0, s1, nss); + } + else { + ActionItemList acts1 = acts; + Context c2; + while ((c2 = Enum.nextElement()) != null) { + acts1 = acts1.cons(body, c2, -1); + } + resState = this.getNextStates(body, acts1, c1, s0, s1, nss); + } + return resState; + } + case OPCODE_fa: // FcnApply + { + Value fval = this.eval(args[0], c, s0, s1, EvalControl.KeepLazy); + if (fval instanceof FcnLambdaValue) { + FcnLambdaValue fcn = (FcnLambdaValue)fval; + if (fcn.fcnRcd == null) { + Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Clear); + return this.getNextStates(fcn.body, acts, c1, s0, s1, nss); + } + fval = fcn.fcnRcd; + } + if (!(fval instanceof Applicable)) { + Assert.fail("In computing next states, a non-function (" + + fval.getKindString() + ") was applied as a function.\n" + pred); + } + Applicable fcn = (Applicable)fval; + Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Clear); + Value bval = fcn.apply(argVal, EvalControl.Clear); if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "next states", "boolean", + pred.toString() }); + } + if (((BoolValue)bval).val) { + return this.getNextStates(acts, s0, s1, nss); + } + return resState; + } + case OPCODE_aa: // AngleAct <A>_e + { + ActionItemList acts1 = acts.cons(args[1], c, -3); + return this.getNextStates(args[0], acts1, c, s0, s1, nss); + } + case OPCODE_sa: // [A]_e + { + /* The following two lines of code did not work, and were changed by + * YuanYu to mimic the way \/ works. Change made + * 11 Mar 2009, with LL sitting next to him. + */ + // this.getNextStates(args[0], acts, c, s0, s1, nss); + // return this.processUnchanged(args[1], acts, c, s0, s1, nss); + resState = this.getNextStates(args[0], acts, c, s0, resState, nss); + return this.processUnchanged(args[1], acts, c, s0, resState, nss); + } + case OPCODE_ite: // IfThenElse + { + Value guard = this.eval(args[0], c, s0, s1, EvalControl.Clear); + if (!(guard instanceof BoolValue)) { Assert.fail("In computing next states, a non-boolean expression (" + - bval.getKindString() + ") was used as a guard condition" + - " of a CASE.\n" + pairArgs[1]); + guard.getKindString() + ") was used as the condition of" + + " an IF." + pred); + } + if (((BoolValue)guard).val) { + return this.getNextStates(args[1], acts, c, s0, s1, nss); + } + else { + return this.getNextStates(args[2], acts, c, s0, s1, nss); + } + } + case OPCODE_case: // Case + { + SemanticNode other = null; + for (int i = 0; i < alen; i++) { + OpApplNode pair = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pair.getArgs(); + if (pairArgs[0] == null) { + other = pairArgs[1]; + } + else { + Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Clear); + if (!(bval instanceof BoolValue)) { + Assert.fail("In computing next states, a non-boolean expression (" + + bval.getKindString() + ") was used as a guard condition" + + " of a CASE.\n" + pairArgs[1]); + } + if (((BoolValue)bval).val) { + return this.getNextStates(pairArgs[1], acts, c, s0, s1, nss); + } + } + } + if (other == null) { + Assert.fail("In computing next states, TLC encountered a CASE with no" + + " conditions true.\n" + pred); + } + return this.getNextStates(other, acts, c, s0, s1, nss); + } + case OPCODE_eq: + { + SymbolNode var = this.getPrimedVar(args[0], c, false); + // Assert.check(var.getName().getVarLoc() >= 0); + if (var == null) { + Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); + if (!((BoolValue)bval).val) { + return resState; + } + } + else { + UniqueString varName = var.getName(); + Value lval = s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear); + if (lval == null) { + resState.bind(varName, rval, pred); + resState = this.getNextStates(acts, s0, resState, nss); + resState.unbind(varName); + return resState; + } + else if (!lval.equals(rval)) { + return resState; + } + } + return this.getNextStates(acts, s0, s1, nss); + } + case OPCODE_in: + { + SymbolNode var = this.getPrimedVar(args[0], c, false); + // Assert.check(var.getName().getVarLoc() >= 0); + if (var == null) { + Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); + if (!((BoolValue)bval).val) { + return resState; + } + } + else { + UniqueString varName = var.getName(); + Value lval = s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear); + if (lval == null) { + if (!(rval instanceof Enumerable)) { + Assert.fail("In computing next states, the right side of \\IN" + + " is not enumerable.\n" + pred); + } + ValueEnumeration Enum = ((Enumerable)rval).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + resState.bind(varName, elem, pred); + resState = this.getNextStates(acts, s0, resState, nss); + resState.unbind(varName); + } + return resState; + } + else if (!rval.member(lval)) { + return resState; + } + } + return this.getNextStates(acts, s0, s1, nss); + } + case OPCODE_implies: + { + Value bval = this.eval(args[0], c, s0, s1, EvalControl.Clear); + if (!(bval instanceof BoolValue)) { + Assert.fail("In computing next states of a predicate of the form" + + " P => Q, P was\n" + bval.getKindString() + ".\n" + pred); } if (((BoolValue)bval).val) { - return this.getNextStates(pairArgs[1], acts, c, s0, s1, nss); + return this.getNextStates(args[1], acts, c, s0, s1, nss); + } + else { + return this.getNextStates(acts, s0, s1, nss); } } + case OPCODE_unchanged: + { + return this.processUnchanged(args[0], acts, c, s0, s1, nss); + } + case OPCODE_cdot: + { + Assert.fail("The current version of TLC does not support action composition."); + /*** + TLCState s01 = TLCStateFun.Empty; + StateVec iss = new StateVec(0); + this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); + int sz = iss.size(); + for (int i = 0; i < sz; i++) { + s01 = iss.elementAt(i); + this.getNextStates(args[1], acts, c, s01, s1, nss); + } + ***/ + return s1; + } + // The following case added by LL on 13 Nov 2009 to handle subexpression names. + case OPCODE_nop: + { + return this.getNextStates(args[0], acts, c, s0, s1, nss); } - if (other == null) { - Assert.fail("In computing next states, TLC encountered a CASE with no" + - " conditions true.\n" + pred); - } - return this.getNextStates(other, acts, c, s0, s1, nss); - } - case OPCODE_eq: - { - SymbolNode var = this.getPrimedVar(args[0], c, false); - // Assert.check(var.getName().getVarLoc() >= 0); - if (var == null) { - Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); - if (!((BoolValue)bval).val) { + default: + { + // We handle all the other builtin operators here. + Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", + bval.toString(), pred.toString() }); + } + if (((BoolValue)bval).val) { + resState = this.getNextStates(acts, s0, s1, nss); + } return resState; } } - else { + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } + } + } + + private final TLCState processUnchanged(SemanticNode expr, ActionItemList acts, Context c, + TLCState s0, TLCState s1, StateVec nss) { + if (this.callStack != null) this.callStack.push(expr); + try { + SymbolNode var = this.getVar(expr, c, false); + TLCState resState = s1; + if (var != null) { + // expr is a state variable: UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear); - if (lval == null) { - resState.bind(varName, rval, pred); + Value val0 = s0.lookup(varName); + Value val1 = s1.lookup(varName); + if (val1 == null) { + resState.bind(varName, val0, expr); resState = this.getNextStates(acts, s0, resState, nss); resState.unbind(varName); - return resState; } - else if (!lval.equals(rval)) { - return resState; + else if (val0.equals(val1)) { + resState = this.getNextStates(acts, s0, s1, nss); } - } - return this.getNextStates(acts, s0, s1, nss); - } - case OPCODE_in: - { - SymbolNode var = this.getPrimedVar(args[0], c, false); - // Assert.check(var.getName().getVarLoc() >= 0); - if (var == null) { - Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); - if (!((BoolValue)bval).val) { - return resState; + else { + MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString(), expr.toString()}); } - } - else { - UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear); - if (lval == null) { - if (!(rval instanceof Enumerable)) { - Assert.fail("In computing next states, the right side of \\IN" + - " is not enumerable.\n" + pred); - } - ValueEnumeration Enum = ((Enumerable)rval).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - resState.bind(varName, elem, pred); - resState = this.getNextStates(acts, s0, resState, nss); - resState.unbind(varName); + return resState; + } + + if (expr instanceof OpApplNode) { + OpApplNode expr1 = (OpApplNode)expr; + ExprOrOpArgNode[] args = expr1.getArgs(); + int alen = args.length; + SymbolNode opNode = expr1.getOperator(); + UniqueString opName = opNode.getName(); + int opcode = BuiltInOPs.getOpCode(opName); + + if (opcode == OPCODE_tup) { + // a tuple: + if (alen != 0) { + ActionItemList acts1 = acts; + for (int i = alen-1; i > 0; i--) { + acts1 = acts1.cons(args[i], c, -2); + } + return this.processUnchanged(args[0], acts1, c, s0, s1, nss); } - return resState; + return this.getNextStates(acts, s0, s1, nss); } - else if (!rval.member(lval)) { - return resState; + + if (opcode == 0 && alen == 0) { + // a 0-arity operator: + Object val = this.lookup(opNode, c, false); + + if (val instanceof OpDefNode) { + return this.processUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1, nss); + } + else if (val instanceof LazyValue) { + LazyValue lv = (LazyValue)val; + return this.processUnchanged(lv.expr, acts, lv.con, s0, s1, nss); + } + else { + Assert.fail("In computing next states, TLC found the identifier\n" + + opName + " undefined in an UNCHANGED expression at\n" + expr); + } + return this.getNextStates(acts, s0, s1, nss); } } - return this.getNextStates(acts, s0, s1, nss); - } - case OPCODE_implies: - { - Value bval = this.eval(args[0], c, s0, s1, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail("In computing next states of a predicate of the form" + - " P => Q, P was\n" + bval.getKindString() + ".\n" + pred); - } - if (((BoolValue)bval).val) { - return this.getNextStates(args[1], acts, c, s0, s1, nss); - } - else { - return this.getNextStates(acts, s0, s1, nss); - } - } - case OPCODE_unchanged: - { - return this.processUnchanged(args[0], acts, c, s0, s1, nss); - } - case OPCODE_cdot: - { - Assert.fail("The current version of TLC does not support action composition."); - /*** - TLCState s01 = TLCStateFun.Empty; - StateVec iss = new StateVec(0); - this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); - int sz = iss.size(); - for (int i = 0; i < sz; i++) { - s01 = iss.elementAt(i); - this.getNextStates(args[1], acts, c, s01, s1, nss); - } - ***/ - return s1; - } - // The following case added by LL on 13 Nov 2009 to handle subexpression names. - case OPCODE_nop: - { - return this.getNextStates(args[0], acts, c, s0, s1, nss); - } - default: - { - // We handle all the other builtin operators here. - Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", - bval.toString(), pred.toString() }); - } - if (((BoolValue)bval).val) { + + Value v0 = this.eval(expr, c, s0); + Value v1 = this.eval(expr, c, s1, null, EvalControl.Clear); + if (v0.equals(v1)) { resState = this.getNextStates(acts, s0, s1, nss); } return resState; - } + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } } - private final TLCState processUnchanged(SemanticNode expr, ActionItemList acts, Context c, - TLCState s0, TLCState s1, StateVec nss) { - SymbolNode var = this.getVar(expr, c, false); - TLCState resState = s1; - if (var != null) { - // expr is a state variable: - UniqueString varName = var.getName(); - Value val0 = s0.lookup(varName); - Value val1 = s1.lookup(varName); - if (val1 == null) { - resState.bind(varName, val0, expr); - resState = this.getNextStates(acts, s0, resState, nss); - resState.unbind(varName); - } - else if (val0.equals(val1)) { - resState = this.getNextStates(acts, s0, s1, nss); - } - else { - MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString(), expr.toString()}); - } - return resState; - } - - if (expr instanceof OpApplNode) { - OpApplNode expr1 = (OpApplNode)expr; - ExprOrOpArgNode[] args = expr1.getArgs(); - int alen = args.length; - SymbolNode opNode = expr1.getOperator(); - UniqueString opName = opNode.getName(); - int opcode = BuiltInOPs.getOpCode(opName); - - if (opcode == OPCODE_tup) { - // a tuple: - if (alen != 0) { - ActionItemList acts1 = acts; - for (int i = alen-1; i > 0; i--) { - acts1 = acts1.cons(args[i], c, -2); - } - return this.processUnchanged(args[0], acts1, c, s0, s1, nss); - } - return this.getNextStates(acts, s0, s1, nss); - } - - if (opcode == 0 && alen == 0) { - // a 0-arity operator: - Object val = this.lookup(opNode, c, false); + /* Special version of eval for state expressions. */ + public final Value eval(SemanticNode expr, Context c, TLCState s0) { + return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear); + } - if (val instanceof OpDefNode) { - return this.processUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1, nss); - } - else if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - return this.processUnchanged(lv.expr, acts, lv.con, s0, s1, nss); - } - else { - Assert.fail("In computing next states, TLC found the identifier\n" + - opName + " undefined in an UNCHANGED expression at\n" + expr); - } - return this.getNextStates(acts, s0, s1, nss); - } - } - - Value v0 = this.eval(expr, c, s0); - Value v1 = this.eval(expr, c, s1, null, EvalControl.Clear); - if (v0.equals(v1)) { - resState = this.getNextStates(acts, s0, s1, nss); - } - return resState; - } - - /* Special version of eval for state expressions. */ - public final Value eval(SemanticNode expr, Context c, TLCState s0) { - return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear); - } - /* * This method evaluates the expression expr in the given context, * current state, and partial next state. */ public final Value eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control) { - switch (expr.getKind()) { - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode expr1 = (LabelNode) expr; - return this.eval( expr1.getBody(), c, s0, s1, control) ; - } - case OpApplKind: - { - OpApplNode expr1 = (OpApplNode)expr; - return this.evalAppl(expr1, c, s0, s1, control); - } - case LetInKind: - { - LetInNode expr1 = (LetInNode)expr; - OpDefNode[] letDefs = expr1.getLets(); - int letLen = letDefs.length; - Context c1 = c; - for (int i = 0; i < letLen; i++) { - OpDefNode opDef = letDefs[i]; - if (opDef.getArity() == 0) { - Value rhs = new LazyValue(opDef.getBody(), c1); - c1 = c1.cons(opDef, rhs); + if (this.callStack != null) this.callStack.push(expr); + try { + switch (expr.getKind()) { + /*********************************************************************** + * LabelKind class added by LL on 13 Jun 2007. * + ***********************************************************************/ + case LabelKind: + { + LabelNode expr1 = (LabelNode) expr; + return this.eval(expr1.getBody(), c, s0, s1, control); + } + case OpApplKind: + { + OpApplNode expr1 = (OpApplNode)expr; + return this.evalAppl(expr1, c, s0, s1, control); + } + case LetInKind: + { + LetInNode expr1 = (LetInNode)expr; + OpDefNode[] letDefs = expr1.getLets(); + int letLen = letDefs.length; + Context c1 = c; + for (int i = 0; i < letLen; i++) { + OpDefNode opDef = letDefs[i]; + if (opDef.getArity() == 0) { + Value rhs = new LazyValue(opDef.getBody(), c1); + c1 = c1.cons(opDef, rhs); + } + } + return this.eval(expr1.getBody(), c1, s0, s1, control); + } + case SubstInKind: + { + SubstInNode expr1 = (SubstInNode)expr; + Subst[] subs = expr1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); + } + return this.eval(expr1.getBody(), c1, s0, s1, control); + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + APSubstInNode expr1 = (APSubstInNode)expr; + Subst[] subs = expr1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); + } + return this.eval(expr1.getBody(), c1, s0, s1, control); + } + case NumeralKind: + case DecimalKind: + case StringKind: + { + return Value.getValue(expr); + } + case AtNodeKind: + { + return (Value)c.lookup(EXCEPT_AT); + } + case OpArgKind: + { + OpArgNode expr1 = (OpArgNode)expr; + SymbolNode opNode = expr1.getOp(); + Object val = this.lookup(opNode, c, false); + if (val instanceof OpDefNode) { + return setSource(expr, new OpLambdaValue((OpDefNode)val, this, c, s0, s1)); + } + return (Value)val; + } + default: + { + Assert.fail("Attempted to evaluate an expression that cannot be evaluated.\n" + + expr); + return null; // make compiler happy } } - return this.eval(expr1.getBody(), c1, s0, s1, control); - } - case SubstInKind: - { - SubstInNode expr1 = (SubstInNode)expr; - Subst[] subs = expr1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); - } - return this.eval(expr1.getBody(), c1, s0, s1, control); - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode expr1 = (APSubstInNode)expr; - Subst[] subs = expr1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); - } - return this.eval(expr1.getBody(), c1, s0, s1, control); - } - - - case NumeralKind: - case DecimalKind: - case StringKind: - { - return Value.getValue(expr); - } - case AtNodeKind: - { - return (Value)c.lookup(EXCEPT_AT); - } - case OpArgKind: - { - OpArgNode expr1 = (OpArgNode)expr; - SymbolNode opNode = expr1.getOp(); - Object val = this.lookup(opNode, c, false); - - if (val instanceof OpDefNode) { - return new OpLambdaValue((OpDefNode)val, this, c, s0, s1); - } - return (Value)val; - } - default: - { - Assert.fail("Attempted to evaluate an expression that cannot be evaluated.\n" + - expr); - return null; // make compiler happy - } + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } } public final Value evalAppl(OpApplNode expr, Context c, TLCState s0, TLCState s1, int control) { - ExprOrOpArgNode[] args = expr.getArgs(); - SymbolNode opNode = expr.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); + if (this.callStack != null) this.callStack.push(expr); + try { + ExprOrOpArgNode[] args = expr.getArgs(); + SymbolNode opNode = expr.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); - if (opcode == 0) { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - if (this.callStack != null) this.callStack.push(expr); - Object val = this.lookup(opNode, c, s0, EvalControl.isPrimed(control)); - - // First, unlazy if it is a lazy value. We cannot use the cached - // value when s1 == null or isEnabled(control). - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - if (s1 == null || - lv.val == ValUndef || - EvalControl.isEnabled(control)) { - val = this.eval(lv.expr, lv.con, s0, s1, control); - } - else { - if (lv.val == null) { - lv.val = this.eval(lv.expr, lv.con, s0, s1, control); + if (opcode == 0) { + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, s0, EvalControl.isPrimed(control)); + + // First, unlazy if it is a lazy value. We cannot use the cached + // value when s1 == null or isEnabled(control). + if (val instanceof LazyValue) { + LazyValue lv = (LazyValue)val; + if (s1 == null || + lv.val == ValUndef || + EvalControl.isEnabled(control)) { + val = this.eval(lv.expr, lv.con, s0, s1, control); + } + else { + if (lv.val == null) { + lv.val = this.eval(lv.expr, lv.con, s0, s1, control); + } + val = lv.val; + } } - val = lv.val; - } - } - Value res = null; - if (val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - if (opcode == 0) { - Context c1 = this.getOpContext(opDef, args, c, true); - res = this.eval(opDef.getBody(), c1, s0, s1, control); - } - } - else if (val instanceof Value) { - res = (Value)val; - int alen = args.length; - if (alen == 0) { - if (val instanceof MethodValue) { - res = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); + Value res = null; + if (val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { + Context c1 = this.getOpContext(opDef, args, c, true); + res = this.eval(opDef.getBody(), c1, s0, s1, control); + } } - } - else { - if (val instanceof OpValue) { - Applicable opVal = (Applicable)val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) { - argVals[i] = this.eval(args[i], c, s0, s1, control); + else if (val instanceof Value) { + res = (Value)val; + int alen = args.length; + if (alen == 0) { + if (val instanceof MethodValue) { + res = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); + } + } + else { + if (val instanceof OpValue) { + Applicable opVal = (Applicable)val; + Value[] argVals = new Value[alen]; + // evaluate the actuals: + for (int i = 0; i < alen; i++) { + argVals[i] = this.eval(args[i], c, s0, s1, control); + } + // apply the operator: + res = opVal.apply(argVals, control); + } } - // apply the operator: - res = opVal.apply(argVals, control); + } + /********************************************************************* + * The following added by Yuan Yu on 13 Nov 2009 to allow theorem an * + * assumption names to be used as expressions. * + * * + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + else if (val instanceof ThmOrAssumpDefNode) { +// Assert.fail("Trying to evaluate the theorem or assumption name `" +// + opNode.getName() + "'. \nUse `" + opNode.getName() +// + "!:' instead.\n" +expr); + ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val ; + Context c1 = this.getOpContext(opDef, args, c, true); + return this.eval(opDef.getBody(), c1, s0, s1, control); + } + else { + Assert.fail(EC.TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR, + new String[] { opNode.getName().toString(), expr.toString() }); + } + if (opcode == 0) { + return res; } } - } - /********************************************************************* - * The following added by Yuan Yu on 13 Nov 2009 to allow theorem an * - * assumption names to be used as expressions. * - * * - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - else if (val instanceof ThmOrAssumpDefNode) { -// Assert.fail("Trying to evaluate the theorem or assumption name `" -// + opNode.getName() + "'. \nUse `" + opNode.getName() -// + "!:' instead.\n" +expr); - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val ; - Context c1 = this.getOpContext(opDef, args, c, true); - return this.eval(opDef.getBody(), c1, s0, s1, control); - } - else { - Assert.fail("In evaluation, the identifier " + opNode.getName() + " is either" + - " undefined or not an operator.\n" + expr); - } - if (this.callStack != null) this.callStack.pop(); - if (opcode == 0) return res; - } - switch (opcode) { - case OPCODE_bc: // BoundedChoose - { - SemanticNode pred = args[0]; - SemanticNode inExpr = expr.getBdedQuantBounds()[0]; - Value inVal = this.eval(inExpr, c, s0, s1, control); - if (!(inVal instanceof Enumerable)) { - Assert.fail("Attempted to compute the value of an expression of\n" + - "form CHOOSE x \\in S: P, but S was not enumerable.\n" + expr); - } + switch (opcode) { + case OPCODE_bc: // BoundedChoose + { + SemanticNode pred = args[0]; + SemanticNode inExpr = expr.getBdedQuantBounds()[0]; + Value inVal = this.eval(inExpr, c, s0, s1, control); + if (!(inVal instanceof Enumerable)) { + Assert.fail("Attempted to compute the value of an expression of\n" + + "form CHOOSE x \\in S: P, but S was not enumerable.\n" + expr); + } - // To fix Bugzilla Bug 279 : TLC bug caused by TLC's not preserving the semantics of CHOOSE, - // the statement - // - // inVal.normalize(); - // - // was replaced by the following by LL on 7 Mar 2012. This fix has not yet received - // the blessing of Yuan Yu, so it should be considered to be provisional. - // - Value convertedVal = SetEnumValue.convert(inVal); - if (convertedVal != null) { - inVal = convertedVal; - } else { - inVal.normalize(); - } - // end of fix. - - ValueEnumeration enumSet = ((Enumerable)inVal).elements(); - FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; - boolean isTuple = expr.isBdedQuantATuple()[0]; - if (isTuple) { - // Identifier tuple case: - int cnt = bvars.length; - Value val; - while ((val = enumSet.nextElement()) != null) { - TupleValue tv = TupleValue.convert(val); - if (tv == null || tv.size() != cnt) { - Assert.fail("Attempted to compute the value of an expression of form\n" + - "CHOOSE <<x1, ... , xN>> \\in S: P, but S was not a set\n" + - "of N-tuples.\n" + expr); + // To fix Bugzilla Bug 279 : TLC bug caused by TLC's not preserving the semantics of CHOOSE, + // the statement + // + // inVal.normalize(); + // + // was replaced by the following by LL on 7 Mar 2012. This fix has not yet received + // the blessing of Yuan Yu, so it should be considered to be provisional. + // + Value convertedVal = SetEnumValue.convert(inVal); + if (convertedVal != null) { + inVal = convertedVal; + } else { + inVal.normalize(); } - Context c1 = c; - for (int i = 0; i < cnt; i++) { - c1 = c1.cons(bvars[i], tv.elems[i]); + // end of fix. + + ValueEnumeration enumSet = ((Enumerable)inVal).elements(); + FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; + boolean isTuple = expr.isBdedQuantATuple()[0]; + if (isTuple) { + // Identifier tuple case: + int cnt = bvars.length; + Value val; + while ((val = enumSet.nextElement()) != null) { + TupleValue tv = TupleValue.convert(val); + if (tv == null || tv.size() != cnt) { + Assert.fail("Attempted to compute the value of an expression of form\n" + + "CHOOSE <<x1, ... , xN>> \\in S: P, but S was not a set\n" + + "of N-tuples.\n" + expr); + } + Context c1 = c; + for (int i = 0; i < cnt; i++) { + c1 = c1.cons(bvars[i], tv.elems[i]); + } + Value bval = this.eval(pred, c1, s0, s1, control); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (((BoolValue)bval).val) { + return val; + } + } } - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { + else { + // Simple identifier case: + SymbolNode name = bvars[0]; + Value val; + while ((val = enumSet.nextElement()) != null) { + Context c1 = c.cons(name, val); + Value bval = this.eval(pred, c1, s0, s1, control); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (((BoolValue)bval).val) { + return val; + } + } + } + Assert.fail("Attempted to compute the value of an expression of form\n" + + "CHOOSE x \\in S: P, but no element of S satisfied P.\n" + expr); + return null; // make compiler happy + } + case OPCODE_be: // BoundedExists + { + ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); + SemanticNode body = args[0]; + Context c1; + while ((c1 = Enum.nextElement()) != null) { + Value bval = this.eval(body, c1, s0, s1, control); + if (!(bval instanceof BoolValue)) { Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (((BoolValue)bval).val) { + return ValTrue; + } } - if (((BoolValue)bval).val) return val; + return ValFalse; } - } - else { - // Simple identifier case: - SymbolNode name = bvars[0]; - Value val; - while ((val = enumSet.nextElement()) != null) { - Context c1 = c.cons(name, val); - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { + case OPCODE_bf: // BoundedForall + { + ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); + SemanticNode body = args[0]; + Context c1; + while ((c1 = Enum.nextElement()) != null) { + Value bval = this.eval(body, c1, s0, s1, control); + if (!(bval instanceof BoolValue)) { Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (!((BoolValue)bval).val) { + return ValFalse; + } } - if (((BoolValue)bval).val) return val; + return ValTrue; } - } - Assert.fail("Attempted to compute the value of an expression of form\n" + - "CHOOSE x \\in S: P, but no element of S satisfied P.\n" + expr); - return null; // make compiler happy - } - case OPCODE_be: // BoundedExists - { - ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); - SemanticNode body = args[0]; - Context c1; - while ((c1 = Enum.nextElement()) != null) { - Value bval = this.eval(body, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); - } - if (((BoolValue)bval).val) return ValTrue; - } - return ValFalse; - } - case OPCODE_bf: // BoundedForall - { - ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); - SemanticNode body = args[0]; - Context c1; - while ((c1 = Enum.nextElement()) != null) { - Value bval = this.eval(body, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); - } - if (!((BoolValue)bval).val) return ValFalse; - } - return ValTrue; - } - case OPCODE_case: // Case - { - int alen = args.length; - SemanticNode other = null; - for (int i = 0; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); - if (pairArgs[0] == null) { - other = pairArgs[1]; + case OPCODE_case: // Case + { + int alen = args.length; + SemanticNode other = null; + for (int i = 0; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); + if (pairArgs[0] == null) { + other = pairArgs[1]; + } + else { + Value bval = this.eval(pairArgs[0], c, s0, s1, control); + if (!(bval instanceof BoolValue)) { + Assert.fail("A non-boolean expression (" + bval.getKindString() + + ") was used as a condition of a CASE. " + pairArgs[0]); + } + if (((BoolValue)bval).val) { + return this.eval(pairArgs[1], c, s0, s1, control); + } + } + } + if (other == null) { + Assert.fail("Attempted to evaluate a CASE with no conditions true.\n" + expr); + } + return this.eval(other, c, s0, s1, control); } - else { - Value bval = this.eval(pairArgs[0], c, s0, s1, control); + case OPCODE_cp: // CartesianProd + { + int alen = args.length; + Value[] sets = new Value[alen]; + for (int i = 0; i < alen; i++) { + sets[i] = this.eval(args[i], c, s0, s1, control); + } + return setSource(expr, new SetOfTuplesValue(sets)); + } + case OPCODE_cl: // ConjList + { + int alen = args.length; + for (int i = 0; i < alen; i++) { + Value bval = this.eval(args[i], c, s0, s1, control); + if (!(bval instanceof BoolValue)) { + Assert.fail("A non-boolean expression (" + bval.getKindString() + + ") was used as a formula in a conjunction.\n" + args[i]); + } + if (!((BoolValue)bval).val) { + return ValFalse; + } + } + return ValTrue; + } + case OPCODE_dl: // DisjList + { + int alen = args.length; + for (int i = 0; i < alen; i++) { + Value bval = this.eval(args[i], c, s0, s1, control); + if (!(bval instanceof BoolValue)) { + Assert.fail("A non-boolean expression (" + bval.getKindString() + + ") was used as a formula in a disjunction.\n" + args[i]); + } + if (((BoolValue)bval).val) { + return ValTrue; + } + } + return ValFalse; + } + case OPCODE_exc: // Except + { + int alen = args.length; + Value result = this.eval(args[0], c, s0, s1, control); + // SZ: variable not used ValueExcept[] expts = new ValueExcept[alen-1]; + for (int i = 1; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); + SemanticNode[] cmpts = ((OpApplNode)pairArgs[0]).getArgs(); + + Value[] lhs = new Value[cmpts.length]; + for (int j = 0; j < lhs.length; j++) { + lhs[j] = this.eval(cmpts[j], c, s0, s1, control); + } + Value atVal = result.select(lhs); + if (atVal == null) { + // Do nothing but warn: + MP.printWarning(EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD, new String[]{args[0].toString()}); + } + else { + Context c1 = c.cons(EXCEPT_AT, atVal); + Value rhs = this.eval(pairArgs[1], c1, s0, s1, control); + ValueExcept vex = new ValueExcept(lhs, rhs); + result = result.takeExcept(vex); + } + } + return result; + } + case OPCODE_fa: // FcnApply + { + Value result = null; + Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(control)); + if ((fval instanceof FcnRcdValue) || + (fval instanceof FcnLambdaValue)) { + Applicable fcn = (Applicable)fval; + Value argVal = this.eval(args[1], c, s0, s1, control); + result = fcn.apply(argVal, control); + } + else if ((fval instanceof TupleValue) || + (fval instanceof RecordValue)) { + Applicable fcn = (Applicable)fval; + if (args.length != 2) { + Assert.fail("Attempted to evaluate an expression of form f[e1, ... , eN]" + + "\nwith f a tuple or record and N > 1.\n" + expr); + } + Value aval = this.eval(args[1], c, s0, s1, control); + result = fcn.apply(aval, control); + } + else { + Assert.fail("A non-function (" + fval.getKindString() + ") was applied" + + " as a function.\n" + expr); + } + return result; + } + case OPCODE_fc: // FcnConstructor + case OPCODE_nrfs: // NonRecursiveFcnSpec + case OPCODE_rfs: // RecursiveFcnSpec + { + FormalParamNode[][] formals = expr.getBdedQuantSymbolLists(); + boolean[] isTuples = expr.isBdedQuantATuple(); + ExprNode[] domains = expr.getBdedQuantBounds(); + + Value[] dvals = new Value[domains.length]; + boolean isFcnRcd = true; + for (int i = 0; i < dvals.length; i++) { + dvals[i] = this.eval(domains[i], c, s0, s1, control); + isFcnRcd = isFcnRcd && (dvals[i] instanceof Reducible); + } + FcnParams params = new FcnParams(formals, isTuples, dvals); + + SemanticNode fbody = args[0]; + FcnLambdaValue fval = (FcnLambdaValue) setSource(expr, new FcnLambdaValue(params, fbody, this, c, s0, s1, control)); + if (opcode == OPCODE_rfs) { + SymbolNode fname = expr.getUnbdedQuantSymbols()[0]; + fval.makeRecursive(fname); + isFcnRcd = false; + } + if (isFcnRcd && !EvalControl.isKeepLazy(control)) { + return fval.toFcnRcd(); + } + return fval; + } + case OPCODE_ite: // IfThenElse + { + Value bval = this.eval(args[0], c, s0, s1, control); if (!(bval instanceof BoolValue)) { Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as a condition of a CASE. " + pairArgs[0]); + ") was used as the condition of an IF.\n" + expr); } if (((BoolValue)bval).val) { - return this.eval(pairArgs[1], c, s0, s1, control); + return this.eval(args[1], c, s0, s1, control); } + return this.eval(args[2], c, s0, s1, control); } - } - if (other == null) { - Assert.fail("Attempted to evaluate a CASE with no conditions true.\n" + expr); - } - return this.eval(other, c, s0, s1, control); - } - case OPCODE_cp: // CartesianProd - { - int alen = args.length; - Value[] sets = new Value[alen]; - for (int i = 0; i < alen; i++) { - sets[i] = this.eval(args[i], c, s0, s1, control); - } - return new SetOfTuplesValue(sets); - } - case OPCODE_cl: // ConjList - { - int alen = args.length; - for (int i = 0; i < alen; i++) { - if (this.callStack != null) this.callStack.push(args[i]); - Value bval = this.eval(args[i], c, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as a formula in a conjunction.\n" + args[i]); - } - if (this.callStack != null) this.callStack.pop(); - if (!((BoolValue)bval).val) return ValFalse; - } - return ValTrue; - } - case OPCODE_dl: // DisjList - { - int alen = args.length; - for (int i = 0; i < alen; i++) { - if (this.callStack != null) this.callStack.push(args[i]); - Value bval = this.eval(args[i], c, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as a formula in a disjunction.\n" + args[i]); - } - if (this.callStack != null) this.callStack.pop(); - if (((BoolValue)bval).val) return ValTrue; - } - return ValFalse; - } - case OPCODE_exc: // Except - { - int alen = args.length; - Value result = this.eval(args[0], c, s0, s1, control); - // SZ: variable not used ValueExcept[] expts = new ValueExcept[alen-1]; - for (int i = 1; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); - SemanticNode[] cmpts = ((OpApplNode)pairArgs[0]).getArgs(); - - Value[] lhs = new Value[cmpts.length]; - for (int j = 0; j < lhs.length; j++) { - lhs[j] = this.eval(cmpts[j], c, s0, s1, control); - } - Value atVal = result.select(lhs); - if (atVal == null) { - // Do nothing but warn: - MP.printWarning(EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD, new String[]{args[0].toString()}); + case OPCODE_rc: // RcdConstructor + { + int alen = args.length; + UniqueString[] names = new UniqueString[alen]; + Value[] vals = new Value[alen]; + for (int i = 0; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pair = pairNode.getArgs(); + names[i] = ((StringValue)Value.getValue(pair[0])).getVal(); + vals[i] = this.eval(pair[1], c, s0, s1, control); + } + return setSource(expr, new RecordValue(names, vals, false)); } - else { - Context c1 = c.cons(EXCEPT_AT, atVal); - Value rhs = this.eval(pairArgs[1], c1, s0, s1, control); - ValueExcept vex = new ValueExcept(lhs, rhs); - result = result.takeExcept(vex); + case OPCODE_rs: // RcdSelect + { + Value rval = this.eval(args[0], c, s0, s1, control); + Value sval = Value.getValue(args[1]); + if (rval instanceof RecordValue) { + Value result = ((RecordValue)rval).select(sval); + if (result == null) { + Assert.fail("Attempted to select nonexistent field " + sval + " from the" + + " record\n" + Value.ppr(rval.toString()) + "\n" + expr); + } + return result; + } + else { + FcnRcdValue fcn = FcnRcdValue.convert(rval); + if (fcn == null) { + Assert.fail("Attempted to select field " + sval + " from a non-record" + + " value " + Value.ppr(rval.toString()) + "\n" + expr); + } + return fcn.apply(sval, control); + } } - } - return result; - } - case OPCODE_fa: // FcnApply - { - Value result = null; - if (this.callStack != null) this.callStack.push(expr); - Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(control)); - if ((fval instanceof FcnRcdValue) || - (fval instanceof FcnLambdaValue)) { - Applicable fcn = (Applicable)fval; - Value argVal = this.eval(args[1], c, s0, s1, control); - result = fcn.apply(argVal, control); - } - else if ((fval instanceof TupleValue) || - (fval instanceof RecordValue)) { - Applicable fcn = (Applicable)fval; - if (args.length != 2) { - Assert.fail("Attempted to evaluate an expression of form f[e1, ... , eN]" + - "\nwith f a tuple or record and N > 1.\n" + expr); - } - Value aval = this.eval(args[1], c, s0, s1, control); - result = fcn.apply(aval, control); - } - else { - Assert.fail("A non-function (" + fval.getKindString() + ") was applied" + - " as a function.\n" + expr); - } - if (this.callStack != null) this.callStack.pop(); - return result; - } - case OPCODE_fc: // FcnConstructor - case OPCODE_nrfs: // NonRecursiveFcnSpec - case OPCODE_rfs: // RecursiveFcnSpec - { - FormalParamNode[][] formals = expr.getBdedQuantSymbolLists(); - boolean[] isTuples = expr.isBdedQuantATuple(); - ExprNode[] domains = expr.getBdedQuantBounds(); - - Value[] dvals = new Value[domains.length]; - boolean isFcnRcd = true; - for (int i = 0; i < dvals.length; i++) { - dvals[i] = this.eval(domains[i], c, s0, s1, control); - isFcnRcd = isFcnRcd && (dvals[i] instanceof Reducible); - } - FcnParams params = new FcnParams(formals, isTuples, dvals); - - SemanticNode fbody = args[0]; - FcnLambdaValue fval = new FcnLambdaValue(params, fbody, this, c, s0, s1, control); - if (opcode == OPCODE_rfs) { - SymbolNode fname = expr.getUnbdedQuantSymbols()[0]; - fval.makeRecursive(fname); - isFcnRcd = false; - } - if (isFcnRcd && !EvalControl.isKeepLazy(control)) { - return fval.toFcnRcd(); - } - return fval; - } - case OPCODE_ite: // IfThenElse - { - Value bval = this.eval(args[0], c, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as the condition of an IF.\n" + expr); - } - if (((BoolValue)bval).val) { - return this.eval(args[1], c, s0, s1, control); - } - return this.eval(args[2], c, s0, s1, control); - } - case OPCODE_rc: // RcdConstructor - { - int alen = args.length; - UniqueString[] names = new UniqueString[alen]; - Value[] vals = new Value[alen]; - for (int i = 0; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pair = pairNode.getArgs(); - names[i] = ((StringValue)Value.getValue(pair[0])).getVal(); - vals[i] = this.eval(pair[1], c, s0, s1, control); - } - return new RecordValue(names, vals, false); - } - case OPCODE_rs: // RcdSelect - { - Value rval = this.eval(args[0], c, s0, s1, control); - Value sval = Value.getValue(args[1]); - if (rval instanceof RecordValue) { - Value result = ((RecordValue)rval).select(sval); - if (result == null) { - Assert.fail("Attempted to select nonexistent field " + sval + " from the" + - " record\n" + Value.ppr(rval.toString()) + "\n" + expr); - } - return result; - } - else { - FcnRcdValue fcn = FcnRcdValue.convert(rval); - if (fcn == null) { - Assert.fail("Attempted to select field " + sval + " from a non-record" + - " value " + Value.ppr(rval.toString()) + "\n" + expr); + case OPCODE_se: // SetEnumerate + { + int alen = args.length; + ValueVec vals = new ValueVec(alen); + for (int i = 0; i < alen; i++) { + vals.addElement(this.eval(args[i], c, s0, s1, control)); + } + return setSource(expr, new SetEnumValue(vals, false)); } - return fcn.apply(sval, control); - } - } - case OPCODE_se: // SetEnumerate - { - int alen = args.length; - ValueVec vals = new ValueVec(alen); - for (int i = 0; i < alen; i++) { - vals.addElement(this.eval(args[i], c, s0, s1, control)); - } - return new SetEnumValue(vals, false); - } - case OPCODE_soa: // SetOfAll: {e(x) : x \in S} - { - ValueVec vals = new ValueVec(); - ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); - SemanticNode body = args[0]; - Context c1; - while ((c1 = Enum.nextElement()) != null) { - Value val = this.eval(body, c1, s0, s1, control); - vals.addElement(val); - // vals.addElement1(val); - } - return new SetEnumValue(vals, false); - } - case OPCODE_sor: // SetOfRcds - { - int alen = args.length; - UniqueString names[] = new UniqueString[alen]; - Value vals[] = new Value[alen]; - for (int i = 0; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pair = pairNode.getArgs(); - names[i] = ((StringValue)Value.getValue(pair[0])).getVal(); - vals[i] = this.eval(pair[1], c, s0, s1, control); - } - return new SetOfRcdsValue(names, vals, false); - } - case OPCODE_sof: // SetOfFcns - { - Value lhs = this.eval(args[0], c, s0, s1, control); - Value rhs = this.eval(args[1], c, s0, s1, control); - return new SetOfFcnsValue(lhs, rhs); - } - case OPCODE_sso: // SubsetOf - { - SemanticNode pred = args[0]; - SemanticNode inExpr = expr.getBdedQuantBounds()[0]; - Value inVal = this.eval(inExpr, c, s0, s1, control); - boolean isTuple = expr.isBdedQuantATuple()[0]; - FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; - if (inVal instanceof Reducible) { - ValueVec vals = new ValueVec(); - ValueEnumeration enumSet = ((Enumerable)inVal).elements(); - Value elem; - if (isTuple) { - while ((elem = enumSet.nextElement()) != null) { - Context c1 = c; - Value[] tuple = ((TupleValue)elem).elems; - for (int i = 0; i < bvars.length; i++) { - c1 = c1.cons(bvars[i], tuple[i]); - } - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + - " when P was " + bval.getKindString() + ".\n" + pred); + case OPCODE_soa: // SetOfAll: {e(x) : x \in S} + { + ValueVec vals = new ValueVec(); + ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); + SemanticNode body = args[0]; + Context c1; + while ((c1 = Enum.nextElement()) != null) { + Value val = this.eval(body, c1, s0, s1, control); + vals.addElement(val); + // vals.addElement1(val); + } + return setSource(expr, new SetEnumValue(vals, false)); + } + case OPCODE_sor: // SetOfRcds + { + int alen = args.length; + UniqueString names[] = new UniqueString[alen]; + Value vals[] = new Value[alen]; + for (int i = 0; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pair = pairNode.getArgs(); + names[i] = ((StringValue)Value.getValue(pair[0])).getVal(); + vals[i] = this.eval(pair[1], c, s0, s1, control); + } + return setSource(expr, new SetOfRcdsValue(names, vals, false)); + } + case OPCODE_sof: // SetOfFcns + { + Value lhs = this.eval(args[0], c, s0, s1, control); + Value rhs = this.eval(args[1], c, s0, s1, control); + return setSource(expr, new SetOfFcnsValue(lhs, rhs)); + } + case OPCODE_sso: // SubsetOf + { + SemanticNode pred = args[0]; + SemanticNode inExpr = expr.getBdedQuantBounds()[0]; + Value inVal = this.eval(inExpr, c, s0, s1, control); + boolean isTuple = expr.isBdedQuantATuple()[0]; + FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; + if (inVal instanceof Reducible) { + ValueVec vals = new ValueVec(); + ValueEnumeration enumSet = ((Enumerable)inVal).elements(); + Value elem; + if (isTuple) { + while ((elem = enumSet.nextElement()) != null) { + Context c1 = c; + Value[] tuple = ((TupleValue)elem).elems; + for (int i = 0; i < bvars.length; i++) { + c1 = c1.cons(bvars[i], tuple[i]); + } + Value bval = this.eval(pred, c1, s0, s1, control); + if (!(bval instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + + " when P was " + bval.getKindString() + ".\n" + pred); + } + if (((BoolValue)bval).val) { + vals.addElement(elem); + } + } } - if (((BoolValue)bval).val) { - vals.addElement(elem); + else { + SymbolNode idName = bvars[0]; + while ((elem = enumSet.nextElement()) != null) { + Context c1 = c.cons(idName, elem); + Value bval = this.eval(pred, c1, s0, s1, control); + if (!(bval instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + + " when P was " + bval.getKindString() + ".\n" + pred); + } + if (((BoolValue)bval).val) { + vals.addElement(elem); + } + } } + return setSource(expr, new SetEnumValue(vals, inVal.isNormalized())); + } + else if (isTuple) { + return setSource(expr, new SetPredValue(bvars, inVal, pred, this, c, s0, s1, control)); + } + else { + return setSource(expr, new SetPredValue(bvars[0], inVal, pred, this, c, s0, s1, control)); } } - else { - SymbolNode idName = bvars[0]; - while ((elem = enumSet.nextElement()) != null) { - Context c1 = c.cons(idName, elem); - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + - " when P was " + bval.getKindString() + ".\n" + pred); + case OPCODE_tup: // Tuple + { + int alen = args.length; + Value[] vals = new Value[alen]; + for (int i = 0; i < alen; i++) { + vals[i] = this.eval(args[i], c, s0, s1, control); + } + return setSource(expr, new TupleValue(vals)); + } + case OPCODE_uc: // UnboundedChoose + { + Assert.fail("TLC attempted to evaluate an unbounded CHOOSE.\n" + + "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + + expr); + return null; // make compiler happy + } + case OPCODE_ue: // UnboundedExists + { + Assert.fail("TLC attempted to evaluate an unbounded \\E.\n" + + "Make sure that the expression is of form \\E x \\in S: P(x).\n" + + expr); + return null; // make compiler happy + } + case OPCODE_uf: // UnboundedForall + { + Assert.fail("TLC attempted to evaluate an unbounded \\A.\n" + + "Make sure that the expression is of form \\A x \\in S: P(x).\n" + + expr); + return null; // make compiler happy + } + case OPCODE_lnot: + { + Value arg = this.eval(args[0], c, s0, s1, control); + if (!(arg instanceof BoolValue)) { + Assert.fail("Attempted to apply the operator ~ to a non-boolean\n(" + + arg.getKindString() + ")\n" + expr); + } + return (((BoolValue)arg).val) ? ValFalse : ValTrue; + } + case OPCODE_subset: + { + Value arg = this.eval(args[0], c, s0, s1, control); + return setSource(expr, new SubsetValue(arg)); + } + case OPCODE_union: + { + Value arg = this.eval(args[0], c, s0, s1, control); + return setSource(expr, UnionValue.union(arg)); + } + case OPCODE_domain: + { + Value arg = this.eval(args[0], c, s0, s1, control); + if (!(arg instanceof Applicable)) { + Assert.fail("Attempted to apply the operator DOMAIN to a non-function\n(" + + arg.getKindString() + ")\n" + expr); + } + return setSource(expr, ((Applicable)arg).getDomain()); + } + case OPCODE_enabled: + { + TLCState sfun = TLCStateFun.Empty; + Context c1 = Context.branch(c); + sfun = this.enabled(args[0], ActionItemList.Empty, c1, s0, sfun); + return (sfun != null) ? ValTrue : ValFalse; + } + case OPCODE_eq: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + return (arg1.equals(arg2)) ? ValTrue : ValFalse; + } + case OPCODE_land: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + if (!(arg1 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + + " when P was\n" + arg1.getKindString() + ".\n" + expr); + } + if (((BoolValue)arg1).val) { + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (!(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + + " when Q was\n" + arg2.getKindString() + ".\n" + expr); } - if (((BoolValue)bval).val) { - vals.addElement(elem); + return arg2; + } + return ValFalse; + } + case OPCODE_lor: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + if (!(arg1 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + + " when P was\n" + arg1.getKindString() + ".\n" + expr); + } + if (((BoolValue)arg1).val) { + return ValTrue; + } + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (!(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + + " when Q was\n" + arg2.getKindString() + ".\n" + expr); + } + return arg2; + } + case OPCODE_implies: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + if (!(arg1 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P => Q" + + " when P was\n" + arg1.getKindString() + ".\n" + expr); + } + if (((BoolValue)arg1).val) { + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (!(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P => Q" + + " when Q was\n" + arg2.getKindString() + ".\n" + expr); } + return arg2; } + return ValTrue; } - return new SetEnumValue(vals, inVal.isNormalized()); - } - else if (isTuple) { - return new SetPredValue(bvars, inVal, pred, this, c, s0, s1, control); - } - else { - return new SetPredValue(bvars[0], inVal, pred, this, c, s0, s1, control); - } - } - case OPCODE_tup: // Tuple - { - int alen = args.length; - Value[] vals = new Value[alen]; - for (int i = 0; i < alen; i++) { - vals[i] = this.eval(args[i], c, s0, s1, control); - } - return new TupleValue(vals); - } - case OPCODE_uc: // UnboundedChoose - { - Assert.fail("TLC attempted to evaluate an unbounded CHOOSE.\n" + - "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + - expr); - return null; // make compiler happy - } - case OPCODE_ue: // UnboundedExists - { - Assert.fail("TLC attempted to evaluate an unbounded \\E.\n" + - "Make sure that the expression is of form \\E x \\in S: P(x).\n" + - expr); - return null; // make compiler happy - } - case OPCODE_uf: // UnboundedForall - { - Assert.fail("TLC attempted to evaluate an unbounded \\A.\n" + - "Make sure that the expression is of form \\A x \\in S: P(x).\n" + - expr); - return null; // make compiler happy - } - case OPCODE_lnot: - { - Value arg = this.eval(args[0], c, s0, s1, control); - if (!(arg instanceof BoolValue)) { - Assert.fail("Attempted to apply the operator ~ to a non-boolean\n(" + - arg.getKindString() + ")\n" + expr); - } - return (((BoolValue)arg).val) ? ValFalse : ValTrue; - } - case OPCODE_subset: - { - Value arg = this.eval(args[0], c, s0, s1, control); - return new SubsetValue(arg); - } - case OPCODE_union: - { - Value arg = this.eval(args[0], c, s0, s1, control); - return UnionValue.union(arg); - } - case OPCODE_domain: - { - Value arg = this.eval(args[0], c, s0, s1, control); - if (!(arg instanceof Applicable)) { - Assert.fail("Attempted to apply the operator DOMAIN to a non-function\n(" + - arg.getKindString() + ")\n" + expr); - } - return ((Applicable)arg).getDomain(); - } - case OPCODE_enabled: - { - TLCState sfun = TLCStateFun.Empty; - Context c1 = Context.branch(c); - sfun = this.enabled(args[0], ActionItemList.Empty, c1, s0, sfun); - return (sfun != null) ? ValTrue : ValFalse; - } - case OPCODE_eq: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return (arg1.equals(arg2)) ? ValTrue : ValFalse; - } - case OPCODE_land: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - if (!(arg1 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + - " when P was\n" + arg1.getKindString() + ".\n" + expr); - } - if (((BoolValue)arg1).val) { - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + - " when Q was\n" + arg2.getKindString() + ".\n" + expr); + case OPCODE_equiv: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (!(arg1 instanceof BoolValue) || !(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P <=> Q" + + " when P or Q was not a boolean.\n" + expr); + } + BoolValue bval1 = (BoolValue)arg1; + BoolValue bval2 = (BoolValue)arg2; + return (bval1.val == bval2.val) ? ValTrue : ValFalse; } - return arg2; - } - return ValFalse; - } - case OPCODE_lor: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - if (!(arg1 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + - " when P was\n" + arg1.getKindString() + ".\n" + expr); - } - if (((BoolValue)arg1).val) return ValTrue; - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + - " when Q was\n" + arg2.getKindString() + ".\n" + expr); - } - return arg2; - } - case OPCODE_implies: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - if (!(arg1 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P => Q" + - " when P was\n" + arg1.getKindString() + ".\n" + expr); - } - if (((BoolValue)arg1).val) { - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P => Q" + - " when Q was\n" + arg2.getKindString() + ".\n" + expr); + case OPCODE_noteq: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + return arg1.equals(arg2) ? ValFalse : ValTrue; + } + case OPCODE_subseteq: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (!(arg1 instanceof Enumerable)) { + Assert.fail("Attempted to evaluate an expression of form S \\subseteq T," + + " but S was not enumerable.\n" + expr); + } + return ((Enumerable) arg1).isSubsetEq(arg2); + } + case OPCODE_in: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + return (arg2.member(arg1)) ? ValTrue : ValFalse; + } + case OPCODE_notin: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + return (arg2.member(arg1)) ? ValFalse : ValTrue; + } + case OPCODE_setdiff: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (arg1 instanceof Reducible) { + return setSource(expr, ((Reducible)arg1).diff(arg2)); + } + return setSource(expr, new SetDiffValue(arg1, arg2)); + } + case OPCODE_cap: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (arg1 instanceof Reducible) { + return setSource(expr, ((Reducible)arg1).cap(arg2)); + } + else if (arg2 instanceof Reducible) { + return setSource(expr, ((Reducible)arg2).cap(arg1)); + } + return setSource(expr, new SetCapValue(arg1, arg2)); + } + case OPCODE_nop: + // Added by LL on 2 Aug 2007 + { + return eval(args[0], c, s0, s1, control); + } + case OPCODE_cup: + { + Value arg1 = this.eval(args[0], c, s0, s1, control); + Value arg2 = this.eval(args[1], c, s0, s1, control); + if (arg1 instanceof Reducible) { + return setSource(expr, ((Reducible)arg1).cup(arg2)); + } + else if (arg2 instanceof Reducible) { + return setSource(expr, ((Reducible)arg2).cup(arg1)); + } + return setSource(expr, new SetCupValue(arg1, arg2)); + } + case OPCODE_prime: + { + if (EvalControl.isEnabled(control)) { + // We are now in primed and enabled. + return this.eval(args[0], c, s1, null, EvalControl.setPrimed(control)); + } + return this.eval(args[0], c, s1, null, control); + } + case OPCODE_unchanged: + { + Value v0 = this.eval(args[0], c, s0, TLCState.Empty, control); + if (EvalControl.isEnabled(control)) { + // We are now in primed and enabled. + control = EvalControl.setPrimed(control); + } + Value v1 = this.eval(args[0], c, s1, null, control); + return (v0.equals(v1)) ? ValTrue : ValFalse; + } + case OPCODE_aa: // <A>_e + { + Value res = this.eval(args[0], c, s0, s1, control); + if (!(res instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form <A>_e," + + " but A was not a boolean.\n" + expr); + } + if (!((BoolValue)res).val) { + return ValFalse; + } + Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control); + if (EvalControl.isEnabled(control)) { + // We are now in primed and enabled. + control = EvalControl.setPrimed(control); + } + Value v1 = this.eval(args[1], c, s1, null, control); + return v0.equals(v1) ? ValFalse : ValTrue; + } + case OPCODE_sa: // [A]_e + { + Value res = this.eval(args[0], c, s0, s1, control); + if (!(res instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form [A]_e," + + " but A was not a boolean.\n" + expr); + } + if (((BoolValue)res).val) { + return ValTrue; + } + Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control); + if (EvalControl.isEnabled(control)) { + // We are now in primed and enabled. + control = EvalControl.setPrimed(control); + } + Value v1 = this.eval(args[1], c, s1, null, control); + return (v0.equals(v1)) ? ValTrue : ValFalse; + } + case OPCODE_cdot: + { + Assert.fail("The current version of TLC does not support action composition."); + /*** + TLCState s01 = TLCStateFun.Empty; + StateVec iss = new StateVec(0); + this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); + int sz = iss.size(); + for (int i = 0; i < sz; i++) { + s01 = iss.elementAt(i); + this.eval(args[1], c, s01, s1, control); + } + ***/ + return null; // make compiler happy + } + case OPCODE_sf: // SF + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"SF", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_wf: // WF + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"WF", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_te: // TemporalExists + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\EE", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_tf: // TemporalForAll + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\AA", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_leadto: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a ~> b", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_arrow: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a -+-> formula", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_box: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"[]A", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_diamond: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"<>A", expr.toString()}); + return null; // make compiler happy } - return arg2; - } - return ValTrue; - } - case OPCODE_equiv: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg1 instanceof BoolValue) || !(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P <=> Q" + - " when P or Q was not a boolean.\n" + expr); - } - BoolValue bval1 = (BoolValue)arg1; - BoolValue bval2 = (BoolValue)arg2; - return (bval1.val == bval2.val) ? ValTrue : ValFalse; - } - case OPCODE_noteq: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return arg1.equals(arg2) ? ValFalse : ValTrue; - } - case OPCODE_subseteq: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg1 instanceof Enumerable)) { - Assert.fail("Attempted to evaluate an expression of form S \\subseteq T," + - " but S was not enumerable.\n" + expr); - } - return ((Enumerable) arg1).isSubsetEq(arg2); - } - case OPCODE_in: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return (arg2.member(arg1)) ? ValTrue : ValFalse; - } - case OPCODE_notin: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return (arg2.member(arg1)) ? ValFalse : ValTrue; - } - case OPCODE_setdiff: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (arg1 instanceof Reducible) { - return ((Reducible)arg1).diff(arg2); - } - return new SetDiffValue(arg1, arg2); - } - case OPCODE_cap: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (arg1 instanceof Reducible) { - return ((Reducible)arg1).cap(arg2); - } - else if (arg2 instanceof Reducible) { - return ((Reducible)arg2).cap(arg1); - } - return new SetCapValue(arg1, arg2); - } - - case OPCODE_nop: - /********************************************************************* - * Added by LL on 2 Aug 2007. * - *********************************************************************/ - { return eval(args[0], c, s0, s1, control) ; - } - - case OPCODE_cup: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (arg1 instanceof Reducible) { - return ((Reducible)arg1).cup(arg2); - } - else if (arg2 instanceof Reducible) { - return ((Reducible)arg2).cup(arg1); - } - return new SetCupValue(arg1, arg2); - } - case OPCODE_prime: - { - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - return this.eval(args[0], c, s1, null, EvalControl.setPrimed(control)); - } - return this.eval(args[0], c, s1, null, control); - } - case OPCODE_unchanged: - { - Value v0 = this.eval(args[0], c, s0, TLCState.Empty, control); - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - control = EvalControl.setPrimed(control); - } - Value v1 = this.eval(args[0], c, s1, null, control); - return (v0.equals(v1)) ? ValTrue : ValFalse; - } - case OPCODE_aa: // <A>_e - { - Value res = this.eval(args[0], c, s0, s1, control); - if (!(res instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form <A>_e," + - " but A was not a boolean.\n" + expr); - } - if (!((BoolValue)res).val) return ValFalse; - Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control); - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - control = EvalControl.setPrimed(control); - } - Value v1 = this.eval(args[1], c, s1, null, control); - return v0.equals(v1) ? ValFalse : ValTrue; - } - case OPCODE_sa: // [A]_e - { - Value res = this.eval(args[0], c, s0, s1, control); - if (!(res instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form [A]_e," + - " but A was not a boolean.\n" + expr); - } - if (((BoolValue)res).val) return ValTrue; - Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control); - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - control = EvalControl.setPrimed(control); - } - Value v1 = this.eval(args[1], c, s1, null, control); - return (v0.equals(v1)) ? ValTrue : ValFalse; - } - case OPCODE_cdot: - { - Assert.fail("The current version of TLC does not support action composition."); - /*** - TLCState s01 = TLCStateFun.Empty; - StateVec iss = new StateVec(0); - this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); - int sz = iss.size(); - for (int i = 0; i < sz; i++) { - s01 = iss.elementAt(i); - this.eval(args[1], c, s01, s1, control); - } - ***/ - return null; // make compiler happy - } - case OPCODE_sf: // SF - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"SF", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_wf: // WF - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"WF", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_te: // TemporalExists - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\EE", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_tf: // TemporalForAll - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\AA", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_leadto: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a ~> b", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_arrow: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a -+-> formula", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_box: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"[]A", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_diamond: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"<>A", expr.toString()}); - return null; // make compiler happy - } - default: - { - Assert.fail("TLC BUG: could not evaluate this expression.\n" + expr); - return null; - } + default: + { + Assert.fail("TLC BUG: could not evaluate this expression.\n" + expr); + return null; + } + } + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } + } + } + + private final Value setSource(final SemanticNode expr, final Value value) { + if (this.callStack != null) { + value.setSource(expr); } + return value; } /** @@ -2097,7 +2168,7 @@ public class Tool for (int i = 0; i < constrs.length; i++) { Value bval = this.eval(constrs[i], Context.Empty, state); if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); } if (!((BoolValue)bval).val) return false; } @@ -2110,7 +2181,7 @@ public class Tool for (int i = 0; i < constrs.length; i++) { Value bval = this.eval(constrs[i], Context.Empty, s1, s2, EvalControl.Clear); if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); } if (!((BoolValue)bval).val) return false; } @@ -2124,68 +2195,72 @@ public class Tool */ public final TLCState enabled(SemanticNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1) { - switch (pred.getKind()) { - case OpApplKind: - { - OpApplNode pred1 = (OpApplNode)pred; - return this.enabledAppl(pred1, acts, c, s0, s1); - } - case LetInKind: - { - LetInNode pred1 = (LetInNode)pred; - OpDefNode[] letDefs = pred1.getLets(); - Context c1 = c; - for (int i = 0; i < letDefs.length; i++) { - OpDefNode opDef = letDefs[i]; - if (opDef.getArity() == 0) { - Value rhs = new LazyValue(opDef.getBody(), c1); - c1 = c1.cons(opDef, rhs); + if (this.callStack != null) this.callStack.push(pred); + try { + switch (pred.getKind()) { + case OpApplKind: + { + OpApplNode pred1 = (OpApplNode)pred; + return this.enabledAppl(pred1, acts, c, s0, s1); + } + case LetInKind: + { + LetInNode pred1 = (LetInNode)pred; + OpDefNode[] letDefs = pred1.getLets(); + Context c1 = c; + for (int i = 0; i < letDefs.length; i++) { + OpDefNode opDef = letDefs[i]; + if (opDef.getArity() == 0) { + Value rhs = new LazyValue(opDef.getBody(), c1); + c1 = c1.cons(opDef, rhs); + } + } + return this.enabled(pred1.getBody(), acts, c1, s0, s1); + } + case SubstInKind: + { + SubstInNode pred1 = (SubstInNode)pred; + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); + } + return this.enabled(pred1.getBody(), acts, c1, s0, s1); + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + APSubstInNode pred1 = (APSubstInNode)pred; + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); + } + return this.enabled(pred1.getBody(), acts, c1, s0, s1); + } + // LabelKind class added by LL on 13 Jun 2007 + case LabelKind: + { + LabelNode pred1 = (LabelNode)pred; + return this.enabled(pred1.getBody(), acts, c, s0, s1); + } + default: + { + // We should not compute enabled on anything else. + Assert.fail("Attempted to compute ENABLED on a non-boolean expression.\n" + pred); + return null; // make compiler happy } } - return this.enabled(pred1.getBody(), acts, c1, s0, s1); - } - case SubstInKind: - { - SubstInNode pred1 = (SubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.enabled(pred1.getBody(), acts, c1, s0, s1); - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode pred1 = (APSubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.enabled(pred1.getBody(), acts, c1, s0, s1); - } - - - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode pred1 = (LabelNode)pred; - return this.enabled(pred1.getBody(), acts, c, s0, s1); - } - default: - { - // We should not compute enabled on anything else. - Assert.fail("Attempted to compute ENABLED on a non-boolean expression.\n" + pred); - return null; // make compiler happy - } + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } } @@ -2197,16 +2272,16 @@ public class Tool Context c = acts.carContext(); ActionItemList acts1 = acts.cdr(); if (kind > 0) { - if (this.callStack != null) this.callStack.push(acts.carPred()); TLCState res = this.enabled(pred, acts1, c, s0, s1); - if (this.callStack != null) this.callStack.pop(); return res; } else if (kind == -1) { - return this.enabled(pred, acts1, c, s0, s1); + TLCState res = this.enabled(pred, acts1, c, s0, s1); + return res; } if (kind == -2) { - return this.enabledUnchanged(pred, acts1, c, s0, s1); + TLCState res = this.enabledUnchanged(pred, acts1, c, s0, s1); + return res; } Value v1 = this.eval(pred, c, s0, TLCState.Empty, EvalControl.Enabled); @@ -2214,11 +2289,14 @@ public class Tool Value v2 = this.eval(pred, c, s1, null, EvalControl.Primed); if (v1.equals(v2)) return null; - return this.enabled(acts1, s0, s1); + TLCState res = this.enabled(acts1, s0, s1); + return res; } - private final TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1) - { + private final TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1) + { + if (this.callStack != null) this.callStack.push(pred); + try { ExprOrOpArgNode[] args = pred.getArgs(); int alen = args.length; SymbolNode opNode = pred.getOperator(); @@ -2226,309 +2304,313 @@ public class Tool if (opcode == 0) { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - Object val = this.lookup(opNode, c, s0, false); - - if (val instanceof OpDefNode) + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, s0, false); + + if (val instanceof OpDefNode) + { + OpDefNode opDef = (OpDefNode) val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { - OpDefNode opDef = (OpDefNode) val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - if (opcode == 0) - { - // Context c1 = this.getOpContext(opDef, args, c, false); - Context c1 = this.getOpContext(opDef, args, c, true); - return this.enabled(opDef.getBody(), acts, c1, s0, s1); - } + // Context c1 = this.getOpContext(opDef, args, c, false); + Context c1 = this.getOpContext(opDef, args, c, true); + return this.enabled(opDef.getBody(), acts, c1, s0, s1); } - + } - // Added 13 Nov 2009 by LL to handle theorem or assumption names - /********************************************************************* - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - if (val instanceof ThmOrAssumpDefNode) - { - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val; - Context c1 = this.getOpContext(opDef, args, c, true); - return this.enabled(opDef.getBody(), acts, c1, s0, s1); - } + + // Added 13 Nov 2009 by LL to handle theorem or assumption names + /********************************************************************* + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + if (val instanceof ThmOrAssumpDefNode) + { + ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val; + Context c1 = this.getOpContext(opDef, args, c, true); + return this.enabled(opDef.getBody(), acts, c1, s0, s1); + } - if (val instanceof LazyValue) - { - LazyValue lv = (LazyValue) val; - return this.enabled(lv.expr, acts, lv.con, s0, s1); - } + if (val instanceof LazyValue) + { + LazyValue lv = (LazyValue) val; + return this.enabled(lv.expr, acts, lv.con, s0, s1); + } - Object bval = val; - if (alen == 0) + Object bval = val; + if (alen == 0) + { + if (val instanceof MethodValue) { - if (val instanceof MethodValue) - { - bval = ((MethodValue) val).apply(EmptyArgs, EvalControl.Clear); - } - } else + bval = ((MethodValue) val).apply(EmptyArgs, EvalControl.Clear); + } + } else + { + if (val instanceof OpValue) { - if (val instanceof OpValue) - { - Applicable op = (Applicable) val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) - { - argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Enabled); - } - // apply the operator: - bval = op.apply(argVals, EvalControl.Enabled); - } + Applicable op = (Applicable) val; + Value[] argVals = new Value[alen]; + // evaluate the actuals: + for (int i = 0; i < alen; i++) + { + argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Enabled); + } + // apply the operator: + bval = op.apply(argVals, EvalControl.Enabled); } + } - if (opcode == 0) + if (opcode == 0) + { + if (!(bval instanceof BoolValue)) { - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", - bval.toString(), pred.toString() }); - } - if (((BoolValue) bval).val) - { - return this.enabled(acts, s0, s1); - } - return null; + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", + bval.toString(), pred.toString() }); + } + if (((BoolValue) bval).val) + { + return this.enabled(acts, s0, s1); } + return null; + } } switch (opcode) { case OPCODE_aa: // AngleAct <A>_e - { + { ActionItemList acts1 = acts.cons(args[1], c, -3); return this.enabled(args[0], acts1, c, s0, s1); - } + } case OPCODE_be: // BoundedExists - { + { SemanticNode body = args[0]; ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled); Context c1; while ((c1 = Enum.nextElement()) != null) { - TLCState s2 = this.enabled(body, acts, c1, s0, s1); - if (s2 != null) - return s2; + TLCState s2 = this.enabled(body, acts, c1, s0, s1); + if (s2 != null) { + return s2; + } } return null; - } + } case OPCODE_bf: // BoundedForall - { + { SemanticNode body = args[0]; ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled); Context c1 = Enum.nextElement(); if (c1 == null) { - return this.enabled(acts, s0, s1); + return this.enabled(acts, s0, s1); } ActionItemList acts1 = acts; Context c2; while ((c2 = Enum.nextElement()) != null) { - acts1 = acts1.cons(body, c2, -1); + acts1 = acts1.cons(body, c2, -1); } return this.enabled(body, acts1, c1, s0, s1); - } + } case OPCODE_case: // Case - { + { SemanticNode other = null; for (int i = 0; i < alen; i++) { - OpApplNode pair = (OpApplNode) args[i]; - ExprOrOpArgNode[] pairArgs = pair.getArgs(); - if (pairArgs[0] == null) + OpApplNode pair = (OpApplNode) args[i]; + ExprOrOpArgNode[] pairArgs = pair.getArgs(); + if (pairArgs[0] == null) + { + other = pairArgs[1]; + } else + { + Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Enabled); + if (!(bval instanceof BoolValue)) { - other = pairArgs[1]; - } else + Assert.fail("In computing ENABLED, a non-boolean expression(" + bval.getKindString() + + ") was used as a guard condition" + " of a CASE.\n" + pairArgs[1]); + } + if (((BoolValue) bval).val) { - Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Enabled); - if (!(bval instanceof BoolValue)) - { - Assert.fail("In computing ENABLED, a non-boolean expression(" + bval.getKindString() - + ") was used as a guard condition" + " of a CASE.\n" + pairArgs[1]); - } - if (((BoolValue) bval).val) - { - return this.enabled(pairArgs[1], acts, c, s0, s1); - } + return this.enabled(pairArgs[1], acts, c, s0, s1); } + } } if (other == null) { - Assert.fail("In computing ENABLED, TLC encountered a CASE with no" + " conditions true.\n" + pred); + Assert.fail("In computing ENABLED, TLC encountered a CASE with no" + " conditions true.\n" + pred); } return this.enabled(other, acts, c, s0, s1); - } + } case OPCODE_cl: // ConjList - case OPCODE_land: { + case OPCODE_land: + { ActionItemList acts1 = acts; for (int i = alen - 1; i > 0; i--) { - acts1 = acts1.cons(args[i], c, i); + acts1 = acts1.cons(args[i], c, i); } - if (this.callStack != null) - this.callStack.push(args[0]); - TLCState res = this.enabled(args[0], acts1, c, s0, s1); - if (this.callStack != null) - this.callStack.pop(); - return res; - } + return this.enabled(args[0], acts1, c, s0, s1); + } case OPCODE_dl: // DisjList - case OPCODE_lor: { + case OPCODE_lor: + { for (int i = 0; i < alen; i++) { - if (this.callStack != null) - this.callStack.push(args[i]); - TLCState s2 = this.enabled(args[i], acts, c, s0, s1); - if (this.callStack != null) - this.callStack.pop(); - if (s2 != null) - return s2; + TLCState s2 = this.enabled(args[i], acts, c, s0, s1); + if (s2 != null) { + return s2; + } } return null; - } + } case OPCODE_fa: // FcnApply - { + { Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(EvalControl.Enabled)); if (fval instanceof FcnLambdaValue) { - FcnLambdaValue fcn = (FcnLambdaValue) fval; - if (fcn.fcnRcd == null) - { - Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Enabled); - return this.enabled(fcn.body, acts, c1, s0, s1); - } - fval = fcn.fcnRcd; + FcnLambdaValue fcn = (FcnLambdaValue) fval; + if (fcn.fcnRcd == null) + { + Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Enabled); + return this.enabled(fcn.body, acts, c1, s0, s1); + } + fval = fcn.fcnRcd; } if (fval instanceof Applicable) { - Applicable fcn = (Applicable) fval; - Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Enabled); - Value bval = fcn.apply(argVal, EvalControl.Enabled); - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "ENABLED", "boolean", - pred.toString() }); - - } - if (!((BoolValue) bval).val) - return null; + Applicable fcn = (Applicable) fval; + Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Enabled); + Value bval = fcn.apply(argVal, EvalControl.Enabled); + if (!(bval instanceof BoolValue)) + { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "ENABLED", "boolean", + pred.toString() }); + } + if (!((BoolValue) bval).val) { + return null; + } } else { - Assert.fail("In computing ENABLED, a non-function (" + fval.getKindString() - + ") was applied as a function.\n" + pred); + Assert.fail("In computing ENABLED, a non-function (" + fval.getKindString() + + ") was applied as a function.\n" + pred); } return this.enabled(acts, s0, s1); - } + } case OPCODE_ite: // IfThenElse - { + { Value guard = this.eval(args[0], c, s0, s1, EvalControl.Enabled); if (!(guard instanceof BoolValue)) { - Assert.fail("In computing ENABLED, a non-boolean expression(" + guard.getKindString() - + ") was used as the guard condition" + " of an IF.\n" + pred); + Assert.fail("In computing ENABLED, a non-boolean expression(" + guard.getKindString() + + ") was used as the guard condition" + " of an IF.\n" + pred); } int idx = (((BoolValue) guard).val) ? 1 : 2; return this.enabled(args[idx], acts, c, s0, s1); - } + } case OPCODE_sa: // SquareAct [A]_e - { + { TLCState s2 = this.enabled(args[0], acts, c, s0, s1); - if (s2 != null) - return s2; + if (s2 != null) { + return s2; + } return this.enabledUnchanged(args[1], acts, c, s0, s1); - } + } case OPCODE_te: // TemporalExists case OPCODE_tf: // TemporalForAll - { + { Assert.fail("In computing ENABLED, TLC encountered temporal quantifier.\n" + pred); return null; // make compiler happy - } + } case OPCODE_uc: // UnboundedChoose - { + { Assert.fail("In computing ENABLED, TLC encountered unbounded CHOOSE. " + "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + pred); return null; // make compiler happy - } + } case OPCODE_ue: // UnboundedExists - { + { Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. " + "Make sure that the expression is of form \\E x \\in S: P(x).\n" + pred); return null; // make compiler happy - } + } case OPCODE_uf: // UnboundedForall - { + { Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. " + "Make sure that the expression is of form \\A x \\in S: P(x).\n" + pred); return null; // make compiler happy - } + } case OPCODE_sf: // SF - { + { Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[]{ "SF", pred.toString()}); return null; // make compiler happy - } + } case OPCODE_wf: // WF - { + { Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "WF", pred.toString() }); return null; // make compiler happy - } - case OPCODE_box: { + } + case OPCODE_box: + { Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "[]", pred.toString() }); return null; // make compiler happy - } - case OPCODE_diamond: { + } + case OPCODE_diamond: + { Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "<>", pred.toString() }); return null; // make compiler happy - } - case OPCODE_unchanged: { + } + case OPCODE_unchanged: + { return this.enabledUnchanged(args[0], acts, c, s0, s1); - } - case OPCODE_eq: { + } + case OPCODE_eq: + { SymbolNode var = this.getPrimedVar(args[0], c, true); if (var == null) { - Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled); - if (!((BoolValue) bval).val) - return null; + Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled); + if (!((BoolValue) bval).val) { + return null; + } } else { - UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled); - if (lval == null) - { - TLCState s2 = s1.bind(var, rval, pred); - return this.enabled(acts, s0, s2); - } else - { - if (!lval.equals(rval)) - return null; + UniqueString varName = var.getName(); + Value lval = s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled); + if (lval == null) + { + TLCState s2 = s1.bind(var, rval, pred); + return this.enabled(acts, s0, s2); + } else + { + if (!lval.equals(rval)) { + return null; } + } } return this.enabled(acts, s0, s1); - } - case OPCODE_implies: { + } + case OPCODE_implies: + { Value bval = this.eval(args[0], c, s0, s1, EvalControl.Enabled); if (!(bval instanceof BoolValue)) { - Assert.fail("While computing ENABLED of an expression of the form" + " P => Q, P was " - + bval.getKindString() + ".\n" + pred); + Assert.fail("While computing ENABLED of an expression of the form" + " P => Q, P was " + + bval.getKindString() + ".\n" + pred); } if (((BoolValue) bval).val) { - return this.enabled(args[1], acts, c, s0, s1); + return this.enabled(args[1], acts, c, s0, s1); } return this.enabled(acts, s0, s1); - } - case OPCODE_cdot: { + } + case OPCODE_cdot: + { Assert.fail("The current version of TLC does not support action composition."); /*** TLCState s01 = TLCStateFun.Empty; @@ -2542,147 +2624,172 @@ public class Tool } ***/ return null; // make compiler happy - } - case OPCODE_leadto: { + } + case OPCODE_leadto: + { Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a ~> b).\n" + pred); return null; // make compiler happy - } - case OPCODE_arrow: { + } + case OPCODE_arrow: + { Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a -+-> formula).\n" + pred); return null; // make compiler happy - } - case OPCODE_in: { + } + case OPCODE_in: + { SymbolNode var = this.getPrimedVar(args[0], c, true); if (var == null) { Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled); - if (!((BoolValue) bval).val) - return null; + if (!((BoolValue) bval).val) { + return null; + } } else { - UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled); - if (lval == null) + UniqueString varName = var.getName(); + Value lval = s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled); + if (lval == null) + { + if (!(rval instanceof Enumerable)) { - if (!(rval instanceof Enumerable)) - { - Assert.fail("The right side of \\IN is not enumerable.\n" + pred); - } - ValueEnumeration Enum = ((Enumerable) rval).elements(); - Value val; - while ((val = Enum.nextElement()) != null) - { - TLCState s2 = s1.bind(var, val, pred); - s2 = this.enabled(acts, s0, s2); - if (s2 != null) - return s2; - } - return null; - } else + Assert.fail("The right side of \\IN is not enumerable.\n" + pred); + } + ValueEnumeration Enum = ((Enumerable) rval).elements(); + Value val; + while ((val = Enum.nextElement()) != null) { - if (!rval.member(lval)) - return null; + TLCState s2 = s1.bind(var, val, pred); + s2 = this.enabled(acts, s0, s2); + if (s2 != null) { + return s2; + } } + return null; + } else + { + if (!rval.member(lval)) { + return null; + } + } } return this.enabled(acts, s0, s1); - } + } // The following case added by LL on 13 Nov 2009 to handle subexpression names. case OPCODE_nop: - { + { return this.enabled(args[0], acts, c, s0, s1); - } - - default: { + } + + default: + { // We handle all the other builtin operators here. Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled); if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", - bval.toString(), pred.toString() }); + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", + bval.toString(), pred.toString() }); } if (((BoolValue) bval).val) { - return this.enabled(acts, s0, s1); + return this.enabled(acts, s0, s1); } return null; + } } - } + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } + } private final TLCState enabledUnchanged(SemanticNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1) { - SymbolNode var = this.getVar(expr, c, true); - if (var != null) { - // a state variable: - UniqueString varName = var.getName(); - Value v0 = this.eval(expr, c, s0, s1, EvalControl.Enabled); - Value v1 = s1.lookup(varName); - if (v1 == null) { - s1 = s1.bind(var, v0, expr); - return this.enabled(acts, s0, s1); - } - if (v1.equals(v0)) { - return this.enabled(acts, s0, s1); - } - MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString() , expr.toString()}); - return null; - } - - if (expr instanceof OpApplNode) { - OpApplNode expr1 = (OpApplNode)expr; - ExprOrOpArgNode[] args = expr1.getArgs(); - int alen = args.length; - SymbolNode opNode = expr1.getOperator(); - UniqueString opName = opNode.getName(); - int opcode = BuiltInOPs.getOpCode(opName); - - if (opcode == OPCODE_tup) { - // a tuple: - if (alen != 0) { - ActionItemList acts1 = acts; - for (int i = 1; i < alen; i++) { - acts1 = acts1.cons(args[i], c, -2); - } - return this.enabledUnchanged(args[0], acts1, c, s0, s1); - } - return this.enabled(acts, s0, s1); - } + if (this.callStack != null) this.callStack.push(expr); + try { + SymbolNode var = this.getVar(expr, c, true); + if (var != null) { + // a state variable: + UniqueString varName = var.getName(); + Value v0 = this.eval(expr, c, s0, s1, EvalControl.Enabled); + Value v1 = s1.lookup(varName); + if (v1 == null) { + s1 = s1.bind(var, v0, expr); + return this.enabled(acts, s0, s1); + } + if (v1.equals(v0)) { + return this.enabled(acts, s0, s1); + } + MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString() , expr.toString()}); + return null; + } + + if (expr instanceof OpApplNode) { + OpApplNode expr1 = (OpApplNode)expr; + ExprOrOpArgNode[] args = expr1.getArgs(); + int alen = args.length; + SymbolNode opNode = expr1.getOperator(); + UniqueString opName = opNode.getName(); + int opcode = BuiltInOPs.getOpCode(opName); + + if (opcode == OPCODE_tup) { + // a tuple: + if (alen != 0) { + ActionItemList acts1 = acts; + for (int i = 1; i < alen; i++) { + acts1 = acts1.cons(args[i], c, -2); + } + return this.enabledUnchanged(args[0], acts1, c, s0, s1); + } + return this.enabled(acts, s0, s1); + } - if (opcode == 0 && alen == 0) { - // a 0-arity operator: - Object val = this.lookup(opNode, c, false); + if (opcode == 0 && alen == 0) { + // a 0-arity operator: + Object val = this.lookup(opNode, c, false); - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - return this.enabledUnchanged(lv.expr, acts, lv.con, s0, s1); - } - else if (val instanceof OpDefNode) { - return this.enabledUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1); + if (val instanceof LazyValue) { + LazyValue lv = (LazyValue)val; + return this.enabledUnchanged(lv.expr, acts, lv.con, s0, s1); + } + else if (val instanceof OpDefNode) { + return this.enabledUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1); + } + else if (val == null) { + Assert.fail("In computing ENABLED, TLC found the undefined identifier\n" + + opName + " in an UNCHANGED expression at\n" + expr); + } + return this.enabled(acts, s0, s1); + } } - else if (val == null) { - Assert.fail("In computing ENABLED, TLC found the undefined identifier\n" + - opName + " in an UNCHANGED expression at\n" + expr); + + Value v0 = this.eval(expr, c, s0, TLCState.Empty, EvalControl.Enabled); + Value v1 = this.eval(expr, c, s1, TLCState.Empty, EvalControl.Primed); + if (!v0.equals(v1)) { + return null; } return this.enabled(acts, s0, s1); - } + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, TLCState, IStateFunctor) + if (this.callStack != null) { this.callStack.freeze(); } + throw e; + } finally { + if (this.callStack != null) { this.callStack.pop(); } } - - Value v0 = this.eval(expr, c, s0, TLCState.Empty, EvalControl.Enabled); - Value v1 = this.eval(expr, c, s1, TLCState.Empty, EvalControl.Primed); - if (!v0.equals(v1)) return null; - return this.enabled(acts, s0, s1); } /* This method determines if the action predicate is valid in (s0, s1). */ public final boolean isValid(Action act, TLCState s0, TLCState s1) { Value val = this.eval(act.pred, act.con, s0, s1, EvalControl.Clear); if (!(val instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", act.pred.toString()}); + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", act.pred.toString()}); } return ((BoolValue)val).val; } - + /* Returns true iff the predicate is valid in the state. */ public final boolean isValid(Action act, TLCState state) { return this.isValid(act, state, TLCState.Empty); @@ -2696,44 +2803,74 @@ public class Tool public final boolean isValid(ExprNode expr) { Value val = this.eval(expr, Context.Empty, TLCState.Empty); if (!(val instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); } return ((BoolValue)val).val; } - - /* Reconstruct the initial state whose fingerprint is fp. */ - public final TLCStateInfo getState(long fp) { - StateVec initStates = this.getInitStates(); - for (int i = 0; i < initStates.size(); i++) { - TLCState state = initStates.elementAt(i); - long nfp = state.fingerPrint(); - if (fp == nfp) { - String info = "<Initial predicate>"; - final TLCStateInfo tlcStateInfo = new TLCStateInfo(state, info, 1, fp); - return tlcStateInfo; - } - } - return null; - } - - /** + + /* Reconstruct the initial state whose fingerprint is fp. */ + public final TLCStateInfo getState(final long fp) { + class InitStateSelectorFunctor implements IStateFunctor { + private final long fp; + public TLCState state; + public InitStateSelectorFunctor(long fp) { + this.fp = fp; + } + @Override + public Object addElement(TLCState state) { + if (state == null) { + return null; + } else if (this.state != null) { + // Always return the first match found. Do not let later matches override + // this.state. This is in line with the original implementation that called + // getInitStates(). + return null; + } else if (fp == state.fingerPrint()) { + this.state = state; + // TODO Stop generation of initial states preemptively. E.g. make the caller of + // addElement check for a special return value such as this (the functor). + } + return null; + } + } + // Registry a selector that extract out of the (possibly) large set of initial + // states the one identified by fp. The functor pattern has the advantage + // compared to this.getInitStates(), that it kind of streams the initial states + // to the functor whereas getInitStates() stores _all_ init states in a set + // which is traversed afterwards. This is also consistent with + // ModelChecker#DoInitFunctor. Using the functor pattern for the processing of + // init states in ModelChecker#doInit but calling getInitStates() here results + // in a bug during error trace generation when the set of initial states is too + // large for getInitStates(). Earlier TLC would have refused to run the model + // during ModelChecker#doInit. + final InitStateSelectorFunctor functor = new InitStateSelectorFunctor(fp); + this.getInitStates(functor); + if (functor.state != null) { + final String info = "<Initial predicate>"; + final TLCStateInfo tlcStateInfo = new TLCStateInfo(functor.state, info, 1, fp); + return tlcStateInfo; + } + return null; + } + + /** * Reconstruct the next state of state s whose fingerprint is fp. - * + * * @return Returns the TLCState wrapped in TLCStateInfo. TLCStateInfo stores * the stateNumber (relative to the given sinfo) and a pointer to * the predecessor. */ - public final TLCStateInfo getState(long fp, TLCStateInfo sinfo) { - final TLCStateInfo tlcStateInfo = getState(fp, sinfo.state); - if (tlcStateInfo == null) { - throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT); - } - tlcStateInfo.stateNumber = sinfo.stateNumber + 1; - tlcStateInfo.predecessorState = sinfo; - tlcStateInfo.fp = fp; - return tlcStateInfo; - } - + public final TLCStateInfo getState(long fp, TLCStateInfo sinfo) { + final TLCStateInfo tlcStateInfo = getState(fp, sinfo.state); + if (tlcStateInfo == null) { + throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT); + } + tlcStateInfo.stateNumber = sinfo.stateNumber + 1; + tlcStateInfo.predecessorState = sinfo; + tlcStateInfo.fp = fp; + return tlcStateInfo; + } + /* Reconstruct the next state of state s whose fingerprint is fp. */ public final TLCStateInfo getState(long fp, TLCState s) { for (int i = 0; i < this.actions.length; i++) { @@ -2771,7 +2908,7 @@ public class Tool if (name.length() == 0) return null; Object symm = this.defns.get(name); if (symm == null) { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "symmetry function", name}); + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "symmetry function", name}); } if (!(symm instanceof OpDefNode)) { Assert.fail("The symmetry function " + name + " must specify a set of permutations."); @@ -2784,13 +2921,13 @@ public class Tool return MVPerm.permutationSubgroup(Enum); } - public boolean hasSymmetry() { - if (this.config == null) { - return false; - } - final String name = this.config.getSymmetry(); - return name.length() > 0; - } + public boolean hasSymmetry() { + if (this.config == null) { + return false; + } + final String name = this.config.getSymmetry(); + return name.length() > 0; + } public final Context getFcnContext(FcnLambdaValue fcn, ExprOrOpArgNode[] args, Context c, TLCState s0, TLCState s1, @@ -2801,7 +2938,7 @@ public class Tool Value[] domains = fcn.params.domains; boolean[] isTuples = fcn.params.isTuples; Value argVal = this.eval(args[1], c, s0, s1, control); - + if (plen == 1) { if (!domains[0].member(argVal)) { Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) + @@ -2831,11 +2968,11 @@ public class Tool Assert.fail("Attempted to apply a function to an argument not in its" + " domain.\n" + args[0]); } - int argn = 0; + int argn = 0; Value[] elems = tv.elems; for (int i = 0; i < formals.length; i++) { FormalParamNode[] ids = formals[i]; - Value domain = domains[i]; + Value domain = domains[i]; if (isTuples[i]) { if (!domain.member(elems[argn])) { Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) + @@ -2853,7 +2990,7 @@ public class Tool Value[] avals = tv1.elems; for (int j = 0; j < ids.length; j++) { fcon = fcon.cons(ids[j], avals[j]); - } + } } else { for (int j = 0; j < ids.length; j++) { @@ -2911,25 +3048,25 @@ public class Tool * This method converts every definition that is constant into TLC * value. By doing this, TLC avoids evaluating the same expression * multiple times. - * + * * The method runs for every module in the module tables. - * + * * Modified by LL on 23 July 2013 so it is not run for modules that are * instantiated and have parameters (CONSTANT or VARIABLE declarations) */ private void processConstantDefns() { ModuleNode[] mods = this.moduleTbl.getModuleNodes(); for (int i = 0; i < mods.length; i++) { - if ( (! mods[i].isInstantiated()) - || ( (mods[i].getConstantDecls().length == 0) - && (mods[i].getVariableDecls().length == 0) ) ) { + if ( (! mods[i].isInstantiated()) + || ( (mods[i].getConstantDecls().length == 0) + && (mods[i].getVariableDecls().length == 0) ) ) { this.processConstantDefns(mods[i]); - } + } } } /** - * Converts the constant definitions in the corresponding value for the + * Converts the constant definitions in the corresponding value for the * module -- that is, it "converts" (which seems to mean calling deepNormalize) * the values substituted for the declared constants. On 17 Mar 2012 it was * modified by LL to evaluate the OpDefNode when a defined operator is substituted @@ -2937,80 +3074,85 @@ public class Tool * evaluation, the definition gets re-evaluated every time TLC evaluates the declared * constant. LL also added a check that an operator substituted for the declared * constant also has the correct arity. - * + * * @param mod the module to run on */ private void processConstantDefns(ModuleNode mod) { - // run for constant definitions - OpDeclNode[] consts = mod.getConstantDecls(); - for (int i = 0; i < consts.length; i++) { - Object val = consts[i].getToolObject(TLCGlobals.ToolId); - if (val != null && val instanceof Value) { - ((Value)val).deepNormalize(); - // System.err.println(consts[i].getName() + ": " + val); - } // The following else clause was added by LL on 17 March 2012. - else if (val != null && val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode) val; - // The following check logically belongs in Spec.processSpec, but it's not there. - // So, LL just added it here. This error cannot occur when running TLC from - // the Toolbox. - Assert.check(opDef.getArity() == consts[i].getArity(), - EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, - new String[] {consts[i].getName().toString(), opDef.getName().toString()}); - - if (opDef.getArity() == 0) { - Value defVal = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty); - defVal.deepNormalize(); - consts[i].setToolObject(TLCGlobals.ToolId, defVal); - } + // run for constant definitions + OpDeclNode[] consts = mod.getConstantDecls(); + for (int i = 0; i < consts.length; i++) { + Object val = consts[i].getToolObject(TLCGlobals.ToolId); + if (val != null && val instanceof Value) { + ((Value)val).deepNormalize(); + // System.err.println(consts[i].getName() + ": " + val); + } // The following else clause was added by LL on 17 March 2012. + else if (val != null && val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode) val; + // The following check logically belongs in Spec.processSpec, but it's not there. + // So, LL just added it here. This error cannot occur when running TLC from + // the Toolbox. + Assert.check(opDef.getArity() == consts[i].getArity(), + EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, + new String[] {consts[i].getName().toString(), opDef.getName().toString()}); + + if (opDef.getArity() == 0) { + try { + Value defVal = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty); + defVal.deepNormalize(); + consts[i].setToolObject(TLCGlobals.ToolId, defVal); + } catch (Assert.TLCRuntimeException e) { + Assert.fail(EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT, + new String[] { consts[i].getName().toString(), opDef.getName().toString() }); } + } } + } - // run for constant operator definitions - OpDefNode[] opDefs = mod.getOpDefs(); - for (int i = 0; i < opDefs.length; i++) { - OpDefNode opDef = opDefs[i]; - - // The following variable evaluate and its value added by LL on 24 July 2013 - // to prevent pre-evaluation of a definition from an EXTENDS of a module that - // is also instantiated. - ModuleNode moduleNode = opDef.getOriginallyDefinedInModuleNode() ; - boolean evaluate = (moduleNode == null) - || (! moduleNode.isInstantiated()) - || ( (moduleNode.getConstantDecls().length == 0) - && (moduleNode.getVariableDecls().length == 0) ) ; - - if (evaluate && opDef.getArity() == 0) { - Object realDef = this.lookup(opDef, Context.Empty, false); - if (realDef instanceof OpDefNode) { - opDef = (OpDefNode)realDef; - if (this.getLevelBound(opDef.getBody(), Context.Empty) == 0) { - try { - UniqueString opName = opDef.getName(); - // System.err.println(opName); - Value val = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty); - val.deepNormalize(); - // System.err.println(opName + ": " + val); - opDef.setToolObject(TLCGlobals.ToolId, val); - Object def = this.defns.get(opName); - if (def == opDef) { - this.defns.put(opName, val); - } - } - catch (Throwable e) { - // Assert.printStack(e); - } - } + // run for constant operator definitions + OpDefNode[] opDefs = mod.getOpDefs(); + for (int i = 0; i < opDefs.length; i++) { + OpDefNode opDef = opDefs[i]; + + // The following variable evaluate and its value added by LL on 24 July 2013 + // to prevent pre-evaluation of a definition from an EXTENDS of a module that + // is also instantiated. + ModuleNode moduleNode = opDef.getOriginallyDefinedInModuleNode() ; + boolean evaluate = (moduleNode == null) + || (! moduleNode.isInstantiated()) + || ( (moduleNode.getConstantDecls().length == 0) + && (moduleNode.getVariableDecls().length == 0) ) ; + + if (evaluate && opDef.getArity() == 0) { + Object realDef = this.lookup(opDef, Context.Empty, false); + if (realDef instanceof OpDefNode) { + opDef = (OpDefNode)realDef; + if (this.getLevelBound(opDef.getBody(), Context.Empty) == 0) { + try { + UniqueString opName = opDef.getName(); + // System.err.println(opName); + Value val = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty); + val.deepNormalize(); + // System.err.println(opName + ": " + val); + opDef.setToolObject(TLCGlobals.ToolId, val); + Object def = this.defns.get(opName); + if (def == opDef) { + this.defns.put(opName, val); } + } + catch (Throwable e) { + // Assert.printStack(e); + } } + } } + } - // run for all inner modules - ModuleNode[] imods = mod.getInnerModules(); - for (int i = 0; i < imods.length; i++) { - this.processConstantDefns(imods[i]); - } + // run for all inner modules + ModuleNode[] imods = mod.getInnerModules(); + for (int i = 0; i < imods.length; i++) { + this.processConstantDefns(imods[i]); + } } } diff --git a/tlatools/src/tlc2/tool/Worker.java b/tlatools/src/tlc2/tool/Worker.java index 0772c38f14695702bd80769a04c0e11a19807c1c..2d8bbbf89a02bef1b65e2811212e4d917981660f 100644 --- a/tlatools/src/tlc2/tool/Worker.java +++ b/tlatools/src/tlc2/tool/Worker.java @@ -10,6 +10,8 @@ import tlc2.output.MP; import tlc2.tool.queue.IStateQueue; import tlc2.util.IdThread; import tlc2.util.ObjLongTable; +import tlc2.util.statistics.FixedSizedBucketStatistics; +import tlc2.util.statistics.IBucketStatistics; public class Worker extends IdThread implements IWorker { @@ -18,10 +20,12 @@ public class Worker extends IdThread implements IWorker { * pretty much eat up all the cycles of a processor running single threaded. * We expect to get linear speedup with respect to the number of processors. */ - private ModelChecker tlc; - private IStateQueue squeue; - private ObjLongTable astCounts; + private final ModelChecker tlc; + private final IStateQueue squeue; + private final ObjLongTable astCounts; + private final IBucketStatistics outDegree; private long statesGenerated; + // SZ Feb 20, 2009: changed due to super type introduction public Worker(int id, AbstractChecker tlc) { @@ -31,6 +35,7 @@ public class Worker extends IdThread implements IWorker { this.tlc = (ModelChecker) tlc; this.squeue = this.tlc.theStateQueue; this.astCounts = new ObjLongTable(10); + this.outDegree = new FixedSizedBucketStatistics(this.getName(), 32); // maximum outdegree of 32 appears sufficient for now. this.setName("TLCWorkerThread-" + String.format("%03d", id)); } @@ -79,4 +84,12 @@ public class Worker extends IdThread implements IWorker { long getStatesGenerated() { return this.statesGenerated; } + + void setOutDegree(final int numOfSuccessors) { + this.outDegree.addSample(numOfSuccessors); + } + + public IBucketStatistics getOutDegree() { + return this.outDegree; + } } diff --git a/tlatools/src/tlc2/tool/distributed/TLCApp.java b/tlatools/src/tlc2/tool/distributed/TLCApp.java index 3f26330fa31a71fabd3713eac3793604461fbc9e..696b53305179ca4169a3062c818a441dccd7366a 100644 --- a/tlatools/src/tlc2/tool/distributed/TLCApp.java +++ b/tlatools/src/tlc2/tool/distributed/TLCApp.java @@ -354,12 +354,18 @@ public class TLCApp extends DistApp { index++; if (index < args.length) { try { - TLCGlobals.coverageInterval = Integer - .parseInt(args[index]) * 1000; - if (TLCGlobals.coverageInterval < 0) { - printErrorMsg("Error: expect a nonnegative integer for -coverage option."); - return null; - } + // Coverage reporting is broken is distributed TLC. Thus + // warn the user and continue ignoring the parameter. + // Consume its value though. + ToolIO.out.println( + "Warning: coverage reporting not supported in distributed TLC, ignoring -coverage " + + args[index] + " parameter."); +// TLCGlobals.coverageInterval = Integer +// .parseInt(args[index]) * 1000; +// if (TLCGlobals.coverageInterval < 0) { +// printErrorMsg("Error: expect a nonnegative integer for -coverage option."); +// return null; +// } index++; } catch (Exception e) { printErrorMsg("Error: An integer for coverage report interval required." diff --git a/tlatools/src/tlc2/tool/fp/DiskFPSet.java b/tlatools/src/tlc2/tool/fp/DiskFPSet.java index ae82abc57857825c5e235a3eb810962b0b9018fc..f29aacb04958bb5961ef97428be2986627b300cf 100644 --- a/tlatools/src/tlc2/tool/fp/DiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/DiskFPSet.java @@ -9,15 +9,13 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.net.InetAddress; import java.rmi.RemoteException; +import java.text.DecimalFormat; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.logging.Level; +import java.util.concurrent.atomic.LongAdder; import java.util.logging.Logger; import javax.management.NotCompliantMBeanException; -import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.TLCTrace; @@ -25,7 +23,6 @@ import tlc2.tool.fp.management.DiskFPSetMXWrapper; import tlc2.tool.management.TLCStandardMBean; import tlc2.util.BufferedRandomAccessFile; import tlc2.util.IdThread; -import tlc2.util.Striped; import util.Assert; import util.FileUtil; @@ -59,8 +56,11 @@ import util.FileUtil; @SuppressWarnings("serial") public abstract class DiskFPSet extends FPSet implements FPSetStatistic { - private final static Logger LOGGER = Logger.getLogger(DiskFPSet.class.getName()); + protected final static Logger LOGGER = Logger.getLogger(DiskFPSet.class.getName()); + protected static final long MARK_FLUSHED = 0x8000000000000000L; + protected static final long FLUSHED_MASK = 0x7FFFFFFFFFFFFFFFL; + // fields /** * upper bound on "tblCnt" @@ -76,24 +76,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { */ protected String fpFilename; protected String tmpFilename; - - /** - * Number of locks in the striped lock (#StripeLocks = 2^LogLockCnt).<br> - * Theoretically best performance should be seen with on lock per bucket in - * the primary hash table. An some point though (not yet measured), this - * performance benefit is probably eaten up by the memory consumption of the - * striped lock {@link DiskFPSet#rwLock} itself, which reduces the memory - * available to the hash set. - */ - protected static final int LogLockCnt = Integer.getInteger(DiskFPSet.class.getName() + ".logLockCnt", (31 - Integer.numberOfLeadingZeros(TLCGlobals.getNumWorkers()) + 8)); - /** - * protects n memory buckets - */ - protected final Striped rwLock; - /** - * Is (1 << LogLockCnt) and exposed here for subclasses - */ - protected final int lockCnt; /** * Number of entries on disk. This is equivalent to the current number of fingerprints stored on disk. * @see DiskFPSet#getFileCnt() @@ -111,13 +93,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * number of entries in "tbl". This is equivalent to the current number of fingerprints stored in in-memory cache/index. * @see DiskFPSet#getTblCnt() */ - protected AtomicLong tblCnt; - + protected LongAdder tblCnt; + // http://concurrencyfreaks.blogspot.de/2013/09/longadder-is-not-sequentially-consistent.html /** * Number of used slots in tbl by a bucket * @see DiskFPSet#getTblLoad() */ - protected long tblLoad; + protected LongAdder tblLoad; /** * Number of allocated bucket slots across the complete index table. tblCnt will always <= bucketCnt; @@ -143,16 +125,16 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { protected long[] index; // statistics - private AtomicLong memHitCnt = new AtomicLong(0); - private AtomicLong diskLookupCnt = new AtomicLong(0); - private AtomicLong diskHitCnt = new AtomicLong(0); - private AtomicLong diskWriteCnt = new AtomicLong(0); - private AtomicLong diskSeekCnt = new AtomicLong(0); - private AtomicLong diskSeekCache = new AtomicLong(0); + protected LongAdder memHitCnt = new LongAdder(); + protected LongAdder diskHitCnt = new LongAdder(); + private LongAdder diskLookupCnt = new LongAdder(); + protected LongAdder diskWriteCnt = new LongAdder(); + private LongAdder diskSeekCnt = new LongAdder(); + private LongAdder diskSeekCache = new LongAdder(); // indicate how many cp or disk grow in put(long) has occurred private int checkPointMark; - private int growDiskMark; + protected int growDiskMark; /** * The load factor and initial capacity for the hashtable. @@ -179,7 +161,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * Accumulated wall clock time it has taken to flush this {@link FPSet} to * disk */ - private long flushTime = 0L; + protected long flushTime = 0L; /** * @@ -201,24 +183,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { */ protected DiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException { super(fpSetConfig); - // Ideally we use one lock per bucket because even with relatively high - // lock counts, we fall victim to the birthday paradox. However, locks - // ain't cheap, which is why we have to find the sweet spot. We - // determined the constant 8 empirically on Intel Xeon CPU E5-2670 v2 @ - // 2.50GHz. This is based on the number of cores only. We ignore their - // speed. MultiThreadedMSBDiskFPSet is the most suitable performance - // test available for the job. It just inserts long values into the - // set. int is obviously going to be too small once 2^23 cores become - // commonplace. - this.lockCnt = 1 << LogLockCnt; - this.rwLock = Striped.readWriteLock(lockCnt); this.maxTblCnt = fpSetConfig.getMemoryInFingerprintCnt(); if (maxTblCnt <= 0) { throw new IllegalArgumentException("Negative or zero upper storage limit"); } this.fileCnt = 0; - this.tblCnt = new AtomicLong(0); + this.tblCnt = new LongAdder(); + this.tblLoad = new LongAdder(); this.flusherChosen = new AtomicBoolean(false); this.index = null; @@ -299,7 +271,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @see tlc2.tool.fp.FPSet#size() */ public long size() { - return this.tblCnt.get() + this.fileCnt; + return this.getTblCnt() + this.fileCnt; } public abstract long sizeof(); @@ -338,106 +310,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { } } - /* (non-Javadoc) - * @see tlc2.tool.fp.FPSet#put(long) - * - * 0 and {@link Long#MIN_VALUE} always return false - * - * Locking is as follows: - * - * Acquire mem read lock - * Acquire disk read lock - * Release mem read lock - * - * Acquire mem read/write lock - * Release disk read lock // interleaved - * insert into mem - * Acquire disk write lock (might cause potential writer to wait() which releases mem read lock (monitor)) - * flushToDisk - * Release disk write lock - * Release mem read lock - * - * asserts: - * - Exclusive access to disk and memory for a writer - * - */ - public final boolean put(long fp) throws IOException { - fp = checkValid(fp); - // zeros the msb - long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; - - final Lock readLock = rwLock.getAt(getLockIndex(fp0)).readLock(); - readLock.lock(); - // First, look in in-memory buffer - if (this.memLookup(fp0)) { - readLock.unlock(); - this.memHitCnt.getAndIncrement(); - return true; - } - - // blocks => wait() if disk is being re-written - // (means the current thread returns rwLock monitor) - // Why not return monitor first and then acquire read lock? - // => prevent deadlock by acquiring threads in same order? - - // next, look on disk - boolean diskHit = this.diskLookup(fp0); - - // In event of disk hit, return - if (diskHit) { - readLock.unlock(); - this.diskHitCnt.getAndIncrement(); - return true; - } - - readLock.unlock(); - - // Another writer could write the same fingerprint here if it gets - // interleaved. This is no problem though, because memInsert again - // checks existence for fp to be inserted - - final Lock w = rwLock.getAt(getLockIndex(fp0)).writeLock(); - w.lock(); - - // if disk lookup failed, add to memory buffer - if (this.memInsert(fp0)) { - w.unlock(); - this.memHitCnt.getAndIncrement(); - return true; - } - - // test if buffer is full && block until there are no more readers - if (needsDiskFlush() && this.flusherChosen.compareAndSet(false, true)) { - - // statistics - growDiskMark++; - long timestamp = System.currentTimeMillis(); - - // acquire _all_ write locks - rwLock.acquireAllLocks(); - - // flush memory entries to disk - flusher.flushTable(); - - // release _all_ write locks - rwLock.releaseAllLocks(); - - // reset forceFlush to false - forceFlush = false; - - // finish writing - this.flusherChosen.set(false); - - long l = System.currentTimeMillis() - timestamp; - flushTime += l; - - LOGGER.log(Level.FINE, "Flushed disk {0} {1}. time, in {2} sec", new Object[] { - ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l}); - } - w.unlock(); - return false; - } - /** * @return true iff the current in-memory buffer has to be flushed to disk * to make room. @@ -448,42 +320,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { // A) the FP distribution causes the index tbl to be unevenly populated. // B) the FP distribution reassembles linear fill-up/down which // causes tblCnt * buckets with initial load factor to be allocated. - return (this.tblCnt.get() >= this.maxTblCnt) || forceFlush ; + return (this.getTblCnt() >= this.maxTblCnt) || forceFlush ; } - /* (non-Javadoc) - * @see tlc2.tool.fp.FPSet#contains(long) - * - * 0 and {@link Long#MIN_VALUE} always return false - */ - public final boolean contains(long fp) throws IOException { - fp = checkValid(fp); - // zeros the msb - long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; - final Lock readLock = this.rwLock.getAt(getLockIndex(fp0)).readLock(); - readLock.lock(); - // First, look in in-memory buffer - if (this.memLookup(fp0)) { - readLock.unlock(); - this.memHitCnt.getAndIncrement(); - return true; - } - - // block if disk is being re-written - // next, look on disk - boolean diskHit = this.diskLookup(fp0); - // increment while still locked - if(diskHit) { - diskHitCnt.getAndIncrement(); - } - - // end read; add to memory buffer if necessary - readLock.unlock(); - return diskHit; - } - - protected abstract int getLockIndex(long fp); - /** * Checks if the given fingerprint has a value that can be correctly stored * by this FPSet @@ -512,9 +351,16 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /** * Return "true" if "fp" is contained in the hash table; otherwise, insert * it and return "false". Precondition: msb(fp) = 0 + * @throws IOException */ - abstract boolean memInsert(long fp); + abstract boolean memInsert(long fp) throws IOException; + /** + * Locks and unlocks tbl + */ + abstract void acquireTblWriteLock(); + abstract void releaseTblWriteLock(); + /** * Look on disk for the fingerprint "fp". This method requires that * "this.rwLock" has been acquired for reading by the caller. @@ -526,7 +372,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { return false; // Increment disk lookup counter - this.diskLookupCnt.getAndIncrement(); + this.diskLookupCnt.increment(); // search in index for position to seek to // do interpolated binary search @@ -618,9 +464,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { // midEntry calculation done on logical indices, // addressing done on bytes, thus convert to long-addressing (* LongSize) if (raf.seeek(midEntry * LongSize)) { - diskSeekCnt.getAndIncrement(); + diskSeekCnt.increment(); } else { - diskSeekCache.getAndIncrement(); + diskSeekCache.increment(); } long v = raf.readLong(); @@ -692,7 +538,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { protected final void writeFP(RandomAccessFile outRAF, long fp) throws IOException { outRAF.writeLong(fp); - diskWriteCnt.getAndIncrement(); + diskWriteCnt.increment(); // update in-memory index file if (this.counter == 0) { this.index[this.currIndex++] = fp; @@ -706,6 +552,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @return The new required length for the {@link DiskFPSet#index} */ protected int calculateIndexLen(final long buffLen) { + // +2L because we always need the lo and hi bounds for the index. long indexLen = ((this.fileCnt + buffLen - 1L) / (long) NumEntriesPerPage) + 2L; //TODO this can cause a NegativeArraySizeException if fileCnt becomes sufficiently large @@ -754,13 +601,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#checkFPs() */ - public final double checkFPs() throws IOException { + public double checkFPs() throws IOException { // It seems pointless to acquire the locks when checkFPs is only - // executed after model checking has finished. Sill lock the disk + // executed after model checking has finished. Lock the disk // fingerprint sets though. Acquiring the locks is cheap. - rwLock.acquireAllLocks(); + acquireTblWriteLock(); flusher.flushTable(); - rwLock.releaseAllLocks(); + releaseTblWriteLock(); RandomAccessFile braf = new BufferedRandomAccessFile( this.fpFilename, "r"); @@ -784,24 +631,24 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#beginChkpt(java.lang.String) */ - public final void beginChkpt(String fname) throws IOException { + public void beginChkpt(String fname) throws IOException { this.flusherChosen.set(true); - rwLock.acquireAllLocks(); + acquireTblWriteLock(); flusher.flushTable(); FileUtil.copyFile(this.fpFilename, this.getChkptName(fname, "tmp")); checkPointMark++; - rwLock.releaseAllLocks(); + releaseTblWriteLock(); this.flusherChosen.set(false); } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#commitChkpt(java.lang.String) */ - public final void commitChkpt(String fname) throws IOException { + public void commitChkpt(String fname) throws IOException { File oldChkpt = new File(this.getChkptName(fname, "chkpt")); File newChkpt = new File(this.getChkptName(fname, "tmp")); if (!newChkpt.renameTo(oldChkpt)) { @@ -813,7 +660,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#recover(java.lang.String) */ - public final void recover(String fname) throws IOException { + public void recover(String fname) throws IOException { RandomAccessFile chkptRAF = new BufferedRandomAccessFile( this.getChkptName(fname, "chkpt"), "r"); RandomAccessFile currRAF = new BufferedRandomAccessFile( @@ -905,9 +752,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { // statistics code to speed up recovery. Thus, recovery relys on // exclusive access to the fingerprint set, which it has during // recovery. - long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; + long fp0 = fp & FLUSHED_MASK; boolean unique = !this.memInsert(fp0); - Assert.check(unique, EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT); + Assert.check(unique, EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT, ""); if (needsDiskFlush()) { this.flusher.flushTable(); } @@ -939,7 +786,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @see tlc2.tool.fp.FPSet#checkInvariant() */ public boolean checkInvariant() throws IOException { - rwLock.acquireAllLocks(); + acquireTblWriteLock(); flusher.flushTable(); // No need for any lock here final RandomAccessFile braf = new BufferedRandomAccessFile( this.fpFilename, "r"); @@ -957,7 +804,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { } } finally { braf.close(); - rwLock.releaseAllLocks(); + releaseTblWriteLock(); } return true; } @@ -1006,14 +853,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * {@link DiskFPSet#getTblLoad()} <= {@link DiskFPSet#getTblCnt()} */ public long getTblLoad() { - return tblLoad; + return tblLoad.sum(); } /** * @return the amount of fingerprints stored in memory. This is less or equal to {@link DiskFPSet#getTblCnt()} depending on if there collision buckets exist. */ public long getTblCnt() { - return tblCnt.get(); + return tblCnt.sum(); } /** @@ -1034,42 +881,42 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @return the diskLookupCnt */ public long getDiskLookupCnt() { - return diskLookupCnt.get(); + return diskLookupCnt.sum(); } /** * @return the diskHitCnt */ public long getMemHitCnt() { - return memHitCnt.get(); + return memHitCnt.sum(); } /** * @return the diskHitCnt */ public long getDiskHitCnt() { - return diskHitCnt.get(); + return diskHitCnt.sum(); } /** * @return the diskWriteCnt */ public long getDiskWriteCnt() { - return diskWriteCnt.get(); + return diskWriteCnt.sum(); } /** * @return the diskSeekCnt */ public long getDiskSeekCnt() { - return diskSeekCnt.get(); + return diskSeekCnt.sum(); } /** * @return the diskSeekCache */ public long getDiskSeekCache() { - return diskSeekCache.get(); + return diskSeekCache.sum(); } /** @@ -1101,7 +948,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @return The (static) number of locks used to guard the set. */ public int getLockCnt() { - return this.rwLock.size(); + return 0; } /** @@ -1123,7 +970,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * load factor, <code>-1d</code> is returned. */ public double getLoadFactor() { - return this.tblCnt.doubleValue() / (double) this.maxTblCnt; + return ((double) this.getTblCnt()) / (double) this.maxTblCnt; } // /** @@ -1185,7 +1032,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * for writing by the caller, and that the mutex "this.rwLock" is also held. */ void flushTable() throws IOException { - if (tblCnt.get() == 0) + if (getTblCnt() == 0) return; prepareTable(); @@ -1207,9 +1054,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { throw new IOException(msg); } - tblCnt.set(0); + tblCnt.reset(); bucketsCapacity = 0; - tblLoad = 0; + tblLoad.reset(); } /** @@ -1218,7 +1065,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * associated with "this.rwLock" must be held, as must the mutex * "this.rwLock" itself. */ - private final void mergeNewEntries() throws IOException { + protected void mergeNewEntries() throws IOException { // Implementation Note: Unfortunately, because the RandomAccessFile // class (and hence, the BufferedRandomAccessFile class) does not // provide a way to re-use an existing RandomAccessFile object on @@ -1236,6 +1083,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { File tmpFile = new File(tmpFilename); tmpFile.delete(); RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tmpFile, "rw"); + tmpRAF.setLength((getTblCnt() + fileCnt) * FPSet.LongSize); // merge mergeNewEntries(braf, tmpRAF); @@ -1261,9 +1109,94 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { // Better way would be to provide method BRAF.open brafPool[i] = new BufferedRandomAccessFile(fpFilename, "r"); } + + // Verify disk file is sorted. + assert checkFile(braf[0], index, fileCnt); + poolIndex = 0; } - protected abstract void mergeNewEntries(RandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException; + protected abstract void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException; + + } + + private static boolean checkFile(BufferedRandomAccessFile braf, long[] index, long elements) throws IOException { + final long fileLen = braf.length(); + if (fileLen / LongSize != elements) { + return false; + } + final long ptr = braf.getFilePointer(); + long predecessor = Long.MIN_VALUE; + if (fileLen > 0) { + predecessor = braf.readLong(); + if (predecessor != index[0]) { + return false; + } + while (braf.getFilePointer() < fileLen) { + long l = braf.readLong(); + if (predecessor >= l) { + return false; + } + predecessor = l; + } + } + braf.seek(ptr); + return predecessor == index[index.length - 1]; + } + + /* + * Helper to read a fingerprint file (.fp) front to end and check for inconsistencies. + */ + + @SuppressWarnings("resource") + public static void main(String[] args) throws IOException { + if (args.length == 1 && !args[0].equals("")) { + + final BufferedRandomAccessFile braf = new BufferedRandomAccessFile(new File(args[0]), "r"); + + final long elements = braf.length() / FPSet.LongSize; + final DecimalFormat df = new DecimalFormat("###,###.###"); + System.out.println(String.format("About to scan %s elements.", df.format(elements))); + + long elem = 0L; + for (long i = 0; i < elements; i++) { + final long l = braf.readLong(); + if (l < elem) { + System.err.println( + String.format("Inconsistent elements %s at pos %s < %s at pos %s.", elem, i - 1L, l, i)); + } + elem = l; + if (i > 0 && i % 100000000L == 0L) { + System.out.println(String.format("Scanned %s elements.", df.format(i))); + } + } + } else if (args.length == 2 && !args[0].equals("") && !args[1].equals("")) { + final BufferedRandomAccessFile superset = new BufferedRandomAccessFile(new File(args[0]), "r"); + final BufferedRandomAccessFile subset = new BufferedRandomAccessFile(new File(args[1]), "r"); + + final long elements = subset.length() / FPSet.LongSize; + final long fileLen = superset.length(); + + OUTER: for (long i = 0; i < elements; i++) { + final long l = subset.readLong(); + while (superset.getFilePointer() < fileLen) { + final long m = superset.readLong(); + if (l == m) { + continue OUTER; + } else if (m > l) { + System.err + .println(String.format("Inconsistent element in superset %s not in superset at pos %s.", + m, superset.getFilePointer())); + } + } + System.err.println( + String.format("Element in subset %s not in superset at pos %s.", l, subset.getFilePointer())); + } + + System.out.println("Finished scanning files."); + } else { + System.err.println("Usage: DiskFPSet file.fp OR superset.fp subset.fp"); + System.exit(1); + } } } diff --git a/tlatools/src/tlc2/tool/fp/FPSet.java b/tlatools/src/tlc2/tool/fp/FPSet.java index f3e74ec57c3082f9e82f9562c77a244ca60d38a9..316abdb58b19f412aafd2ab49dc35c09868ea5e1 100644 --- a/tlatools/src/tlc2/tool/fp/FPSet.java +++ b/tlatools/src/tlc2/tool/fp/FPSet.java @@ -48,6 +48,10 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI * after the constructor but before any of the other methods below. */ public abstract void init(int numThreads, String metadir, String filename) throws IOException; + + public void incWorkers(int num) { + // subclasses may override + } /* Returns the number of fingerprints in this set. */ /* (non-Javadoc) diff --git a/tlatools/src/tlc2/tool/fp/FPSetFactory.java b/tlatools/src/tlc2/tool/fp/FPSetFactory.java index 08a4f0bcdb99f3f3660b034bd85c8bb3749622ee..0843520d451097fcba31c1b9a74bfdfc989b514b 100644 --- a/tlatools/src/tlc2/tool/fp/FPSetFactory.java +++ b/tlatools/src/tlc2/tool/fp/FPSetFactory.java @@ -9,6 +9,8 @@ import java.util.List; import tlc2.output.EC; import tlc2.output.MP; +import util.TLCRuntime; +import util.TLCRuntime.ARCH; public abstract class FPSetFactory { @@ -22,7 +24,7 @@ public abstract class FPSetFactory { return !OffHeapDiskFPSet.class.isAssignableFrom(clazz); } - static boolean allocatesOnHeap(final String clazz) { + public static boolean allocatesOnHeap(final String clazz) { try { final ClassLoader classLoader = FPSet.class.getClassLoader(); @SuppressWarnings("unchecked") @@ -33,6 +35,26 @@ public abstract class FPSetFactory { return false; } } + + private static boolean supportsArchitecture(String clazz) { + try { + final ClassLoader classLoader = FPSet.class.getClassLoader(); + @SuppressWarnings("unchecked") + Class<? extends FPSet> cls = (Class<? extends FPSet>) classLoader + .loadClass(clazz); + return supports32Bits(cls); + } catch (ClassNotFoundException e) { + return false; + } + } + + private static boolean supports32Bits(final Class<? extends FPSet> clazz) { + if (TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86 + && OffHeapDiskFPSet.class.isAssignableFrom(clazz)) { + return false; + } + return true; + } private static boolean isDiskFPSet(Class<? extends FPSet> cls) { return DiskFPSet.class.isAssignableFrom(cls); @@ -78,10 +100,20 @@ public abstract class FPSetFactory { return new MultiFPSet(fpSetConfig); } else { if (implClassname != null) { - return loadImplementation(implClassname, fpSetConfig); - } else { - return new MSBDiskFPSet(fpSetConfig); + if (supportsArchitecture(implClassname)) { + return loadImplementation(implClassname, fpSetConfig); + } else { + final ARCH architecture = TLCRuntime.getInstance().getArchitecture(); + final String msg = String.format( + "Selected fingerprint set (set of visited states) %s does not support current architecture %s. " + + "Reverting to default fingerprint set. " + + "Off-heap memory allocated via -XX:MaxDirectMemorySize flag cannot be used by default " + + "fingerprint set and is therefore wasted.", + implClassname, architecture); + MP.printWarning(EC.TLC_FEATURE_UNSUPPORTED, msg); + } } + return new MSBDiskFPSet(fpSetConfig); } } diff --git a/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java b/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java index f96b9c16f0745a6ce7911f09faf4323943feaa9c..9a0d165478c60db6d3f562cf341cc1648e1a9e4d 100644 --- a/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java @@ -1,12 +1,37 @@ // Copyright (c) 2012 Microsoft Corporation. All rights reserved. package tlc2.tool.fp; +import java.io.IOException; import java.rmi.RemoteException; +import java.util.concurrent.locks.Lock; +import java.util.logging.Level; +import tlc2.TLCGlobals; +import tlc2.tool.fp.management.DiskFPSetMXWrapper; +import tlc2.util.Striped; import util.Assert; @SuppressWarnings("serial") public abstract class HeapBasedDiskFPSet extends DiskFPSet { + + /** + * Number of locks in the striped lock (#StripeLocks = 2^LogLockCnt).<br> + * Theoretically best performance should be seen with on lock per bucket in + * the primary hash table. An some point though (not yet measured), this + * performance benefit is probably eaten up by the memory consumption of the + * striped lock {@link DiskFPSet#rwLock} itself, which reduces the memory + * available to the hash set. + */ + protected static final int LogLockCnt = Integer.getInteger(DiskFPSet.class.getName() + ".logLockCnt", (31 - Integer.numberOfLeadingZeros(TLCGlobals.getNumWorkers()) + 8)); + /** + * protects n memory buckets + */ + protected final Striped rwLock; + /** + * Is (1 << LogLockCnt) and exposed here for subclasses + */ + protected final int lockCnt; + protected final int lockMask; /** * in-memory buffer of new entries @@ -38,7 +63,19 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { protected HeapBasedDiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException { super(fpSetConfig); - + + // Ideally we use one lock per bucket because even with relatively high + // lock counts, we fall victim to the birthday paradox. However, locks + // ain't cheap, which is why we have to find the sweet spot. We + // determined the constant 8 empirically on Intel Xeon CPU E5-2670 v2 @ + // 2.50GHz. This is based on the number of cores only. We ignore their + // speed. MultiThreadedMSBDiskFPSet is the most suitable performance + // test available for the job. It just inserts long values into the + // set. int is obviously going to be too small once 2^23 cores become + // commonplace. + this.lockCnt = 1 << LogLockCnt; + this.rwLock = Striped.readWriteLock(lockCnt); + // Reserve a portion of the memory for the auxiliary storage long maxMemCnt = (long) (fpSetConfig.getMemoryInFingerprintCnt() / getAuxiliaryStorageRequirement()); @@ -75,7 +112,7 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { // guard against negative maxTblCnt // LL modified error message on 7 April 2012 - Assert.check(maxTblCnt > capacity && capacity > tblCnt.get(), + Assert.check(maxTblCnt > capacity && capacity > getTblCnt(), "negative maxTblCnt"); this.mask = capacity - 1; @@ -103,6 +140,13 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { rwLock.releaseAllLocks(); return size; } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#getLockCnt() + */ + public int getLockCnt() { + return this.rwLock.size(); + } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#getTblCapacity() @@ -131,6 +175,36 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { return fp & aMask; } + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#contains(long) + * + * 0 and {@link Long#MIN_VALUE} always return false + */ + public final boolean contains(long fp) throws IOException { + fp = checkValid(fp); + // zeros the msb + long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; + final Lock readLock = this.rwLock.getAt(getLockIndex(fp0)).readLock(); + readLock.lock(); + // First, look in in-memory buffer + if (this.memLookup(fp0)) { + readLock.unlock(); + this.memHitCnt.increment(); + return true; + } + + // block if disk is being re-written + // next, look on disk + boolean diskHit = this.diskLookup(fp0); + // increment while still locked + if(diskHit) { + diskHitCnt.increment(); + } + + // end read; add to memory buffer if necessary + readLock.unlock(); + return diskHit; + } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#memLookup(long) @@ -151,6 +225,107 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { return false; } + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#put(long) + * + * 0 and {@link Long#MIN_VALUE} always return false + * + * Locking is as follows: + * + * Acquire mem read lock + * Acquire disk read lock + * Release mem read lock + * + * Acquire mem read/write lock + * Release disk read lock // interleaved + * insert into mem + * Acquire disk write lock (might cause potential writer to wait() which releases mem read lock (monitor)) + * flushToDisk + * Release disk write lock + * Release mem read lock + * + * asserts: + * - Exclusive access to disk and memory for a writer + * + */ + public final boolean put(long fp) throws IOException { + fp = checkValid(fp); + // zeros the msb + long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; + + final Lock readLock = rwLock.getAt(getLockIndex(fp0)).readLock(); + readLock.lock(); + // First, look in in-memory buffer + if (this.memLookup(fp0)) { + readLock.unlock(); + this.memHitCnt.increment(); + return true; + } + + // blocks => wait() if disk is being re-written + // (means the current thread returns rwLock monitor) + // Why not return monitor first and then acquire read lock? + // => prevent deadlock by acquiring threads in same order? + + // next, look on disk + boolean diskHit = this.diskLookup(fp0); + + // In event of disk hit, return + if (diskHit) { + readLock.unlock(); + this.diskHitCnt.increment(); + return true; + } + + readLock.unlock(); + + // Another writer could write the same fingerprint here if it gets + // interleaved. This is no problem though, because memInsert again + // checks existence for fp to be inserted + + final Lock w = rwLock.getAt(getLockIndex(fp0)).writeLock(); + w.lock(); + + // if disk lookup failed, add to memory buffer + if (this.memInsert(fp0)) { + w.unlock(); + this.memHitCnt.increment(); + return true; + } + + // test if buffer is full && block until there are no more readers + if (needsDiskFlush() && this.flusherChosen.compareAndSet(false, true)) { + + // statistics + growDiskMark++; + final long timestamp = System.currentTimeMillis(); + final long insertions = getTblCnt(); + + // acquire _all_ write locks + rwLock.acquireAllLocks(); + + // flush memory entries to disk + flusher.flushTable(); + + // release _all_ write locks + rwLock.releaseAllLocks(); + + // reset forceFlush to false + forceFlush = false; + + // finish writing + this.flusherChosen.set(false); + + long l = System.currentTimeMillis() - timestamp; + flushTime += l; + + LOGGER.log(Level.FINE, "Flushed disk {0} {1}. time, in {2} sec after {3} insertions.", new Object[] { + ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l, insertions}); + } + w.unlock(); + return false; + } + /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#memInsert(long) */ @@ -166,7 +341,7 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { bucket[0] = fp; this.tbl[index] = bucket; this.bucketsCapacity += InitialBucketCapacity; - this.tblLoad++; + this.tblLoad.increment(); } else { // search for entry in existing bucket int bucketLen = bucket.length; @@ -204,7 +379,21 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { bucket[i] = fp; } } - this.tblCnt.getAndIncrement(); + this.tblCnt.increment(); return false; } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#acquireTblWriteLock() + */ + void acquireTblWriteLock() { + rwLock.acquireAllLocks(); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#releaseTblWriteLock() + */ + void releaseTblWriteLock() { + rwLock.releaseAllLocks(); + } } diff --git a/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java b/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java index a0a4871050f967fa058c1f1483e4f8cc859448b1..aa126bb88e1d6f150fd9699d0c752648d03ad968 100644 --- a/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java @@ -9,6 +9,7 @@ import java.rmi.RemoteException; import java.util.Arrays; import tlc2.output.EC; +import tlc2.util.BufferedRandomAccessFile; import util.Assert; @SuppressWarnings("serial") @@ -36,7 +37,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet { @Override protected void prepareTable() { // Verify tblCnt is still within positive Integer.MAX_VALUE bounds - int cnt = (int) tblCnt.get(); + int cnt = (int) getTblCnt(); Assert.check(cnt > 0, EC.GENERAL); // Why not sort this.tbl in-place rather than doubling memory @@ -76,7 +77,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet { * @see tlc2.tool.fp.DiskFPSet.Flusher#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile) */ @Override - protected void mergeNewEntries(RandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { final int buffLen = buff.length; // Precompute the maximum value of the new file diff --git a/tlatools/src/tlc2/tool/fp/LongArray.java b/tlatools/src/tlc2/tool/fp/LongArray.java index c90ba08d523e544110f5b70e217c5e90c43b947b..ee29c7ec352cb40ab0777d8219475be36794302a 100644 --- a/tlatools/src/tlc2/tool/fp/LongArray.java +++ b/tlatools/src/tlc2/tool/fp/LongArray.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -14,6 +15,7 @@ import java.util.concurrent.Future; import sun.misc.Unsafe; import tlc2.output.EC; import util.Assert; +import util.TLCRuntime; /** * This implementation uses sun.misc.Unsafe instead of a wrapping @@ -58,7 +60,37 @@ public final class LongArray { Assert.check(this.unsafe.addressSize() == (Long.SIZE / 8), EC.GENERAL); baseAddress = this.unsafe.allocateMemory(positions << logAddressSize); } + + LongArray(final Collection<Long> from) { + this(from.size()); + + final Iterator<Long> itr = from.iterator(); + long i = 0L; + while(itr.hasNext()) { + Long next = itr.next(); + set(i++, next); + } + } + /** + * @return true iff LongArray can be used on the current JVM. It cannot be used + * if the architecture is not 64 bit and the sun.misc.Unsafe class + * cannot be loaded (on some JVM implementations, this isn't possible). + */ + public static boolean isSupported() { + if (TLCRuntime.ARCH.x86_64 != TLCRuntime.getInstance().getArchitecture()) { + return false; + } + try { + if (getUnsafe() != null) { + return true; + } + return false; + } catch (RuntimeException e) { + return false; + } + } + /** * @return An Unsafe object or a {@link RuntimeException} wrapping any {@link Exception}. */ @@ -180,6 +212,18 @@ public final class LongArray { rangeCheck(position); return this.unsafe.getAddress(log2phy(position)); } + + /** + * Swaps elements at pos1 and pos2. This is not atomic. The element at pos1 + * will for a moment not be an element of {@link LongArray}. + */ + public final void swap(final long position1, final long position2) { + rangeCheck(position1); + rangeCheck(position2); + final long tmp = get(position1); + set(position1, get(position2)); + set(position2, tmp); + } /** * Returns the number of elements in this array. @@ -189,19 +233,23 @@ public final class LongArray { public final long size() { return length; } - + /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { - long iMax = length - 1; + return toString(0L, length - 1L); + } + + public String toString(long start, long end) { + long iMax = end; if (iMax == -1L) { return "[]"; } final StringBuilder b = new StringBuilder(); b.append('['); - for (long i = 0; ; i++) { + for (long i = start; ; i++) { final long lng = get(i); if (lng == 0L) { b.append("e"); diff --git a/tlatools/src/tlc2/tool/fp/LongArrays.java b/tlatools/src/tlc2/tool/fp/LongArrays.java new file mode 100644 index 0000000000000000000000000000000000000000..179e1a464fdcb007475e913da18fd7d00a64e053 --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/LongArrays.java @@ -0,0 +1,57 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +public class LongArrays { + + /** + * Sorts the specified range [left,right] of the array. In other words, + * sorts the elements from the array index <code>left</code> to index + * <code>right</code>. + */ + public static void sort(final LongArray a, final long left, final long right, final LongComparator cmp) { + final long size = a.size(); + if (size == 0) { + return; + } + + //+++++++ Insertions sort +++++++// + for (long i = left, j = i; i < right; j = ++i) { + final long lo = (i + 1) % size; + final long ai = a.get(lo); + while (cmp.compare(ai, lo, a.get(j % size), j % size) <= -1) { + a.set((j + 1) % size, a.get(j % size)); + if (j-- == left) { + break; + } + } + a.set((j + 1) % size, ai); + } + } + + public static class LongComparator { + public int compare(long lo, long loPos, long hi, long hiPos) { + // The default comparator ignores the positions. + return Long.compare(lo, hi); + } + } + + public static void sort(final LongArray a, final long left, final long right) { + sort(a, left, right, new LongComparator()); + } + + public static void sort(final LongArray a) { + sort(a, 0, a.size() - 1L, new LongComparator()); + } + + /** + * DOES NOT HANDLE LONG ARRAYS LARGER THAN INTEGER.MAX_VALUE + */ + public static long[] toArray(final LongArray array) { + final long[] res = new long[(int) array.size()]; + for (int i = 0; i < array.size(); i++) { + long l = array.get(i); + res[i] = l; + } + return res; + } +} diff --git a/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java b/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java index 3640ad35dbcd2548dbdc8c239a9635d2cd09ef49..d0afaf9b6e713c4d7ded23fb2207a8e82c7b028c 100644 --- a/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.NoSuchElementException; import tlc2.output.EC; +import tlc2.util.BufferedRandomAccessFile; import util.Assert; @SuppressWarnings("serial") @@ -100,8 +101,8 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#mergeNewEntries(long[], int, java.io.RandomAccessFile, java.io.RandomAccessFile) */ - protected void mergeNewEntries(RandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { - final long buffLen = tblCnt.get(); + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { + final long buffLen = getTblCnt(); final TLCIterator itr = new TLCIterator(tbl); // Precompute the maximum value of the new file. diff --git a/tlatools/src/tlc2/tool/fp/MultiFPSet.java b/tlatools/src/tlc2/tool/fp/MultiFPSet.java index 9160d0b57067753f5072c5e4723208e1c1787e5a..68425c98007aea4239d9b836c37b534a42fac6a1 100644 --- a/tlatools/src/tlc2/tool/fp/MultiFPSet.java +++ b/tlatools/src/tlc2/tool/fp/MultiFPSet.java @@ -77,6 +77,14 @@ public class MultiFPSet extends FPSet { } } + + @Override + public void incWorkers(int num) { + for (int i = 0; i < this.sets.length; i++) { + this.sets[i].incWorkers(num); + } + } + /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#size() */ diff --git a/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java b/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java new file mode 100644 index 0000000000000000000000000000000000000000..5e8e7680af32e2d63f499c447e20efde415a8cdb --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.io.IOException; +import java.rmi.RemoteException; + +import tlc2.output.EC; +import tlc2.output.MP; + +@SuppressWarnings("serial") +public abstract class NonCheckpointableDiskFPSet extends DiskFPSet { + + protected NonCheckpointableDiskFPSet(FPSetConfiguration fpSetConfig) throws RemoteException { + super(fpSetConfig); + } + + public void beginChkpt(String fname) throws IOException { + MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName()); + } + + public void commitChkpt(String fname) throws IOException { + MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName()); + } + + public void recover(String fname) throws IOException { + MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName()); + } +} diff --git a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java index fb0e1979c0cbdae93b3a10000989230db60f47a6..04e41bcefc688fdecb03249d29015a163caca372 100644 --- a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java @@ -1,104 +1,244 @@ // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. package tlc2.tool.fp; -import java.io.EOFException; +import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.LongBuffer; import java.rmi.RemoteException; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; import java.util.NoSuchElementException; -import java.util.TreeSet; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.Phaser; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.ToLongFunction; +import java.util.logging.Level; +import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.fp.LongArrays.LongComparator; +import tlc2.tool.fp.management.DiskFPSetMXWrapper; +import tlc2.util.BufferedRandomAccessFile; import util.Assert; +/** + * see OpenAddressing.tla + */ @SuppressWarnings({ "serial" }) -public final class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic { +public final class OffHeapDiskFPSet extends NonCheckpointableDiskFPSet implements FPSetStatistic { - protected static final double COLLISION_BUCKET_RATIO = .025d; - - protected final int bucketCapacity; + private static class OffHeapSynchronizer { + + private final Set<OffHeapDiskFPSet> sets = new HashSet<OffHeapDiskFPSet>(); + + private final AtomicBoolean flusherChosen = new AtomicBoolean(); + + // This barrier gets run after one thread signals the need to suspend + // put and contains operations to evict to secondary. Signaling is done + // via the flusherChoosen AtomicBoolean. All threads (numThreads) will + // then await on the barrier and the Runnable be executed when the + // last of numThreads arrives. + // Compared to an AtomicBoolean, the barrier operation use locks and + // are thus comparably expensive. + private final Phaser phaser = new Phaser(1) { + + @Override + protected boolean onAdvance(int phase, int registeredParties) { + // Atomically evict and reset flusherChosen to make sure no + // thread re-read flusherChosen=true after an eviction and + // waits again. + for (OffHeapDiskFPSet set : sets) { + set.evict(); + } + + // Release exclusive access. It has to be done by the runnable + // before workers waiting on the barrier wake up again. + Assert.check(flusherChosen.compareAndSet(true, false), EC.GENERAL); + + return super.onAdvance(phase, registeredParties); + } + }; + + private OffHeapSynchronizer() { + // Don't want any copies. + } + + public final void add(final OffHeapDiskFPSet aSet) { + this.sets.add(aSet); + } + + public final void incWorkers(final int numWorkers) { + final int parties = phaser.getRegisteredParties(); + if (parties < numWorkers) { + phaser.bulkRegister(numWorkers - parties); + } + } + + public final boolean evictPending() { + return flusherChosen.get(); + } + + public final void evict() { + flusherChosen.compareAndSet(false, true); + } + + public final void awaitEviction() { + phaser.arriveAndAwaitAdvance(); + } + + public AtomicBoolean getFlusherChosen() { + return flusherChosen; + } + } - private final LongArray array; + // We require a singleton here, because if TLC is run with multiple instances + // of FPSets - the default - workers will call evict and awaitEvict on + // all FPSet instances. Thus, an individual synchronization internal to each + // FPSet would never see the complete set of waiting workers. TLC would thus + // deadlock. This is why SYNC is a singleton and shared by all FPSet instances. + private static final OffHeapSynchronizer SYNC = new OffHeapSynchronizer(); + + private static final int PROBE_LIMIT = Integer.getInteger(OffHeapDiskFPSet.class.getName() + ".probeLimit", 1024); + static final long EMPTY = 0L; + /** - * A bucket containing collision elements which is used as a fall-back if a - * bucket is fully used up. Buckets cannot grow as the whole in-memory - * data-structure is static and not designed to be resized. - * - * <p> - * Open addressing - contrary to separate chaining - is not an option for an - * {@link OffHeapDiskFPSet}, because it does not support the invariant of - * monotonic increasing buckets required by the {@link Indexer}. Adhering to - * this invariant has the benefit, that only the elements in a bucket have - * to be sorted, but they don't change buckets during sort. Thus, a - * temporary sort array as in {@link LSBDiskFPSet.LSBFlusher#prepareTable()} is - * obsolete halving the memory footprint. - * </p> + * @see LongArray#isSupported() */ - protected final CollisionBucket collisionBucket; + public static boolean isSupported() { + return LongArray.isSupported(); + } + + private final transient LongArray array; /** * The indexer maps a fingerprint to a in-memory bucket and the associated lock */ - private final Indexer indexer; - - private final ReadWriteLock csRWLock = new ReentrantReadWriteLock(); + private final transient Indexer indexer; + + private int numThreads; protected OffHeapDiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException { super(fpSetConfig); - final long memoryInFingerprintCnt = fpSetConfig.getMemoryInFingerprintCnt(); + final long positions = fpSetConfig.getMemoryInFingerprintCnt(); // Determine base address which varies depending on machine architecture. - this.array = new LongArray(memoryInFingerprintCnt); - this.bucketCapacity = InitialBucketCapacity; - - final int csCapacity = (int) (maxTblCnt * COLLISION_BUCKET_RATIO); - this.collisionBucket = new TreeSetCollisionBucket(csCapacity); - - this.flusher = new OffHeapMSBFlusher(); - - // Calculate Hamming weight of maxTblCnt - final int bitCount = Long.bitCount(memoryInFingerprintCnt); + this.array = new LongArray(positions); // If Hamming weight is 1, the logical index address can be calculated // significantly faster by bit-shifting. However, with large memory // sizes, only supporting increments of 2^n sizes would waste memory // (e.g. either 32GiB or 64Gib). Hence, we check if the bitCount allows // us to use bit-shifting. If not, we fall back to less efficient - // calculations. Additionally we increase the bucket capacity to make - // use of extra memory. The down side is, that larger buckets mean - // increased linear search. But linear search on maximally 31 elements - // still outperforms disk I/0. - if (bitCount == 1) { - this.indexer = new BitshiftingIndexer(bucketCapacity, memoryInFingerprintCnt, fpSetConfig.getFpBits()); + // calculations. + if (Long.bitCount(positions) == 1) { + this.indexer = new BitshiftingIndexer(positions, fpSetConfig.getFpBits()); } else { // non 2^n buckets cannot use a bit shifting indexer - this.indexer = new Indexer(bucketCapacity, memoryInFingerprintCnt, fpSetConfig.getFpBits()); + this.indexer = new Indexer(positions, fpSetConfig.getFpBits()); } + + // Use the non-concurrent flusher as the default. Will be replaced by + // the CyclicBarrier-Runnable later. Just set to prevent NPEs when + // eviction/flush is called before init. + this.flusher = new OffHeapMSBFlusher(array); + + this.flusherChosen = SYNC.getFlusherChosen(); + SYNC.add(this); } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#init(int, java.lang.String, java.lang.String) */ - public void init(int numThreads, String aMetadir, String filename) + @Override + public void init(final int numThreads, String aMetadir, String filename) throws IOException { super.init(numThreads, aMetadir, filename); + this.numThreads = numThreads; array.zeroMemory(numThreads); } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#incWorkers(int) + */ + public void incWorkers(int numWorkers) { + assert numWorkers == this.numThreads; + SYNC.incWorkers(numWorkers); + } + + public void evict() { + // statistics + growDiskMark++; + final long timestamp = System.currentTimeMillis(); + final long insertions = tblCnt.longValue(); + final double lf = tblCnt.doubleValue() / (double) maxTblCnt; + + LOGGER.log(Level.FINE, + "Started eviction of disk {0} the {1}. time at {2} after {3} insertions, load factor {4} and reprobe of {5}.", + new Object[] { ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), + timestamp, insertions, lf, PROBE_LIMIT }); + + // Check that the table adheres to our invariant. Otherwise, we + // can't hope to successfully evict it. + assert checkInput(array, indexer, PROBE_LIMIT) : "Table violates invariants prior to eviction: " + + array.toString(); + + // Only pay the price of creating threads when array is + // sufficiently large and the array size is large enough to + // partition it for multiple threads. + flusher = getFlusher(numThreads, insertions); + + try { + flusher.flushTable(); // Evict() + } catch (IOException e) { + // wrap in unchecked. + throw new OffHeapRuntimeException(e); + } + + // statistics and logging again. + final long l = System.currentTimeMillis() - timestamp; + flushTime += l; + LOGGER.log(Level.FINE, + "Finished eviction of disk {0} the {1}. time at {2}, in {3} sec after {4} insertions, load factor {5} and reprobe of {6}.", + new Object[] { ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l, + System.currentTimeMillis(), insertions, lf, PROBE_LIMIT }); + } + + private Flusher getFlusher(final int numThreads, final long insertions) { + if (array.size() >= 8192 && Math.floor(array.size() / (double) numThreads) > 2 * PROBE_LIMIT) { + return new ConcurrentOffHeapMSBFlusher(array, PROBE_LIMIT, numThreads, insertions); + } else { + return this.flusher; + } + } + + private boolean checkEvictPending() { + if (SYNC.evictPending()) { + SYNC.awaitEviction(); + return true; + } + return false; + } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#sizeof() */ public long sizeof() { long size = 44; // approx size of this DiskFPSet object - size += maxTblCnt * (long) LongSize; + size += maxTblCnt * LongSize; size += getIndexCapacity() * 4; return size; } @@ -106,12 +246,9 @@ public final class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#needsDiskFlush() */ + @Override protected final boolean needsDiskFlush() { - // Only flush due to collision ratio when primary hash table is at least - // 25% full. Otherwise a second flush potentially immediately follows a - // first one, when both values for tblCnt and collision size can be small. - return (collisionRatioExceeds(COLLISION_BUCKET_RATIO) && loadFactorExceeds(.25d)) - || loadFactorExceeds(1d) || forceFlush; + return loadFactorExceeds(1d) || forceFlush; } /** @@ -124,124 +261,162 @@ public final class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic * @return true iff the current hash table load exceeds the given limit */ private final boolean loadFactorExceeds(final double limit) { - // Base this one the primary hash table only and exclude the - // collision bucket - final double d = (this.tblCnt.doubleValue() - collisionBucket.size()) / (double) this.maxTblCnt; - return d >= limit; - } - - /** - * @param limit A limit the collsionBucket is not allowed to exceed - * @return The proportional size of the collision bucket compared to the - * size of the set. - */ - private final boolean collisionRatioExceeds(final double limit) { - // Do not use the thread safe getCollisionRatio here to avoid - // unnecessary locking. put() calls us holding a memory write locking - // which also blocks writers to collisionBucket. - final long size = collisionBucket.size(); - // Subtract size from overall tblCnt as it includes the cs size - // @see put(long) - final double d = (double) size / (tblCnt.doubleValue() - size); + final double d = (this.tblCnt.doubleValue()) / (double) this.maxTblCnt; return d >= limit; } - + + private static final int FOUND = -1; + /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#getLockIndex(long) + * @see tlc2.tool.fp.DiskFPSet#memLookup(long) */ - @Override - protected final int getLockIndex(long fp) { - return this.indexer.getLockIndex(fp); + final boolean memLookup(final long fp0) { + return memLookup0(fp0) == FOUND; + } + + final int memLookup0(final long fp0) { + int free = PROBE_LIMIT; + for (int i = 0; i <= PROBE_LIMIT; i++) { + final long position = indexer.getIdx(fp0, i); + final long l = array.get(position); + if (fp0 == (l & FLUSHED_MASK)) { + // zero the long msb (which is 1 if fp has been flushed to disk) + return FOUND; + } else if (l == EMPTY) { + return Math.min(i, free); + } else if (l < EMPTY && free == PROBE_LIMIT) { + free = i; + } + } + + return free; } /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#memLookup(long) + * @see tlc2.tool.fp.DiskFPSet#memInsert(long) */ - final boolean memLookup(long fp) { - final long position = indexer.getLogicalPosition(fp); - - // Linearly search the logical bucket; 0L is an invalid fp and marks the - // end of the allocated bucket - long l = -1L; - for (int i = 0; i < bucketCapacity && l != 0L; i++) { - l = array.get(position + i); - // zero the long msb (which is 1 if fp has been flushed to disk) - if (fp == (l & 0x7FFFFFFFFFFFFFFFL)) { + final boolean memInsert(final long fp0) throws IOException { + return memInsert0(fp0, 0); + } + + final boolean memInsert0(final long fp0, final int start) throws IOException { + // See OffHeapDiskFPSetJPFTest for a (verbatim) version that has + // additionally been verified with JPF. + for (int i = start; i < PROBE_LIMIT; i++) { + final long position = indexer.getIdx(fp0, i); + final long expected = array.get(position); + if (expected == EMPTY || (expected < 0 && fp0 != (expected & FLUSHED_MASK))) { + // Try to CAS the new fingerprint. + if (array.trySet(position, expected, fp0)) { + this.tblCnt.increment(); + return false; + } else { + // Retry at current position because another thread wrote a + // value concurrently (possibly the same one this thread is + // trying to write). + i = i - 1; + continue; + } + } + + // Expected is the fingerprint to be inserted. + if ((expected & FLUSHED_MASK) == fp0) { return true; } } + - return csLookup(fp); + // We failed to insert into primary. Consequently, lets try and make + // some room by signaling all threads to wait for eviction. + forceFlush(); + // We've signaled for eviction to start or failed because some other + // thread beat us to it. Actual eviction and setting flusherChosen back + // to false is done by the Barrier's Runnable. We cannot set + // flusherChosen back to false after barrier.awaits returns because it + // leaves a window during which other threads read the old true value of + // flusherChosen a second time and immediately wait again. + + return put(fp0); } - - /** - * Probes {@link OffHeapDiskFPSet#collisionBucket} for the given fingerprint. - * @param fp - * @return true iff fp is in the collision bucket + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#put(long) */ - protected final boolean csLookup(long fp) { - try { - csRWLock.readLock().lock(); - return collisionBucket.contains(fp); - } finally { - csRWLock.readLock().unlock(); + public final boolean put(final long fp) throws IOException { + if (checkEvictPending()) { + return put(fp); + } + + // zeros the msb + final long fp0 = fp & FLUSHED_MASK; + + // Only check primary and disk iff there exists a disk file. index is + // created when we wait and thus cannot race. + int start = 0; + if (index != null) { + // Lookup primary memory + if ((start = memLookup0(fp0)) == FOUND) { + this.memHitCnt.increment(); + return true; + } + + // Lookup on disk + if (this.diskLookup(fp0)) { + this.diskHitCnt.increment(); + return true; + } } + + // Lastly, try to insert into memory. + return memInsert0(fp0, start); } /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#memInsert(long) + * @see tlc2.tool.fp.FPSet#contains(long) */ - final boolean memInsert(long fp) { - final long position = indexer.getLogicalPosition(fp); - - long l = -1; - long freePosition = -1L; - for (int i = 0; i < bucketCapacity && l != 0L; i++) { - l = array.get(position + i); - // zero the long msb (which is 1 if fp has been flushed to disk) - if (fp == (l & 0x7FFFFFFFFFFFFFFFL)) { - return true; - } else if (l == 0L && freePosition == -1) { - if (i == 0) { - tblLoad++; - } - // empty or disk written slot found, simply insert at _current_ position - this.array.set(position + i, fp); - this.tblCnt.getAndIncrement(); - return false; - } else if (l < 0L && freePosition == -1) { - // record free (disk written fp) slot - freePosition = position + i; - } + public final boolean contains(final long fp) throws IOException { + // maintains happen-before with regards to successful put + + if (checkEvictPending()) { + return contains(fp); } - if (freePosition > -1 && !csLookup(fp)) { - // Write to free slot - this.array.set(freePosition, fp); - this.tblCnt.getAndIncrement(); - return false; - } else { - // Index slot (bucket) overflow, thus add to collisionBucket - boolean success = csInsert(fp); - if (success) { - this.tblCnt.getAndIncrement(); - } - return !success; + // zeros the msb + final long fp0 = fp & FLUSHED_MASK; + + // Lookup in primary + if (memLookup(fp0)) { + return true; + } + + // Lookup on secondary/disk + if (this.diskLookup(fp0)) { + diskHitCnt.increment(); + return true; } + + return false; } - /** - * Inserts the given fingerprint into the {@link OffHeapDiskFPSet#collisionBucket}. - * @param fp - * @return true iff fp has been added to the collision bucket + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#forceFlush() */ - protected final boolean csInsert(long fp) { - try { - csRWLock.writeLock().lock(); - return collisionBucket.add(fp); - } finally { - csRWLock.writeLock().unlock(); - } + public void forceFlush() { + SYNC.evict(); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#acquireTblWriteLock() + */ + void acquireTblWriteLock() { + // no-op for now + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#releaseTblWriteLock() + */ + void releaseTblWriteLock() { + // no-op for now } /* (non-Javadoc) @@ -250,355 +425,827 @@ public final class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic public long getTblCapacity() { return maxTblCnt; } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#getTblLoad() + */ + public long getTblLoad() { + return getTblCnt(); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#getOverallCapacity() + */ + public long getOverallCapacity() { + return array.size(); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#getBucketCapacity() + */ + public long getBucketCapacity() { + // A misnomer, but bucketCapacity is obviously not applicable with open + // addressing. + return PROBE_LIMIT; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#checkFPs() + */ + @Override + public double checkFPs() throws IOException { + if (getTblCnt() <= 0) { + return Double.MAX_VALUE; + } + // The superclass implementation provides insufficient performance and + // scalability. These problems get more pronounced with a large array. + // In order to provide a faster solution to the closest pair problem in + // one-dimensional space, we cheat and determine the minimum distance + // between fingerprints only on a subset of fingerprints. The subset are + // those fingerprints which happen to be in main memory and have not yet + // been evicted to disk. We believe this to be a sufficient approximation. + // The algorithm to find the closest pair in one-dimensional space is: + // 1) Sort all fingerprints. + // 2) Perform a linear scan/search + // => O(n log n) + // Scalability is achieved by partitioning the array for 1) and 2), + // which is possible due to the bounded disorder in array. + + // 1) Sort all fingerprints, either with a sequential or concurrent + // flusher depending on the size of the array. + final int numThreads = TLCGlobals.getNumWorkers(); + this.flusher = getFlusher(numThreads, getTblCnt()); + this.flusher.prepareTable(); + + // 2) A task finds the pair with the minimum distance in an ordered + // subrange of array. Each task is assigned an id. This id determines + // which subrange of array (partition) it searches. + final Collection<Callable<Long>> tasks = new ArrayList<Callable<Long>>(numThreads); + final long length = (long) Math.floor(array.size() / (double) numThreads); + for (int i = 0; i < numThreads; i++) { + final int id = i; + tasks.add(new Callable<Long>() { + @Override + public Long call() throws Exception { + final boolean isLast = id == numThreads - 1; + final long start = id * length; + // Partitions overlap in case the two fingerprints with + // minimum distance are in two adjacent partitions. + final long end = (isLast ? array.size() - 1L : start + length) + 1L; + + long distance = Long.MAX_VALUE; + + // Reuse Iterator implementation which already handles the + // various cases related to the cyclic table layout. Pass it + // getTblCnt to make sure next does not keep going forever + // if the array is empty. Instead, check the itr's position. + final Iterator itr = new Iterator(array, getTblCnt(), start, indexer, + isLast && id != 0 ? Iterator.WRAP.FORBIDDEN : Iterator.WRAP.ALLOWED); + try { + // If the partition is empty, next throws a NSEE. + long x = itr.next(); + long y = 0; + while ((y = itr.next(end)) != EMPTY) { + long d = y - x; + // d < 0 indicates that the array is not sorted. + // d == 0 indicates two identical elements in the + // fingerprint set. Iff this is the last partition, + // itr.next might have returned the first element of the + // first partition. + assert d > 0 ^ (d < 0 && itr.getPos() > end && isLast); + distance = Math.min(distance, d); + x = y; + } + return distance; + } catch (NoSuchElementException e) { + return distance; + } + } + }); + } + + // Start as many tasks as we have workers. Afterwards, collect the + // minimum distance out of the set of results provided by the workers. + final ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + try { + final long distance = executorService.invokeAll(tasks).stream().min(new Comparator<Future<Long>>() { + public int compare(Future<Long> o1, Future<Long> o2) { + try { + return Long.compare(o1.get(), o2.get()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(e); + } catch (ExecutionException e) { + throw new OffHeapRuntimeException(e); + } + } + }).get().get(); + return 1.0 / distance; + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(ie); + } catch (ExecutionException e) { + throw new OffHeapRuntimeException(e); + } finally { + executorService.shutdown(); + } + } + //**************************** Indexer ****************************// + public static class Indexer { - protected final int bcktCapacity; + + private static final long minFingerprint = 1L; //Minimum possible fingerprint (0L marks an empty position) + + private final double tblScalingFactor; + private final long maxFingerprint; + protected final long positions; - protected final long prefixMask; - /** - * Number of bits to right shift bits during index calculation - * @see MSBDiskFPSet#moveBy - */ - protected final int moveBy; - /** - * Number of bits to right shift bits during index calculation of - * striped lock. - */ - protected final int lockMoveBy; + public Indexer(final long positions, final int fpBits) { + this(positions, fpBits, 0xFFFFFFFFFFFFFFFFL >>> fpBits); + assert fpBits > 0; + } - public Indexer(final int bucketCapacity, final long positions, int prefixBits) { - this.bcktCapacity = bucketCapacity; - this.prefixMask = 0xFFFFFFFFFFFFFFFFL >>> prefixBits; + public Indexer(final long positions, final int fpBits, final long maxValue) { + this.positions = positions; + this.maxFingerprint = maxValue; + // (position-1L) because array is zero indexed. + this.tblScalingFactor = (positions - 1L) / ((maxFingerprint - minFingerprint) * 1f); + } + + protected long getIdx(final long fp) { + return getIdx(fp, 0); + } + + protected long getIdx(final long fp, final int probe) { + long idx = Math.round(tblScalingFactor * (fp - minFingerprint)) + probe; + return idx % positions; + } + } + + public static class BitshiftingIndexer extends Indexer { + private final long prefixMask; + private final int rShift; - // Move n as many times to the right to calculate moveBy. moveBy is the - // number of bits the (fp & mask) has to be right shifted to make the - // logical bucket index. - long n = (0xFFFFFFFFFFFFFFFFL >>> prefixBits) - (positions - 1); + public BitshiftingIndexer(final long positions, final int fpBits) throws RemoteException { + super(positions, fpBits); + + this.prefixMask = 0xFFFFFFFFFFFFFFFFL >>> fpBits; + + long n = (0xFFFFFFFFFFFFFFFFL >>> fpBits) - (positions - 1); int moveBy = 0; while (n >= positions) { moveBy++; n = n >>> 1; // == (n/2) } - this.moveBy = moveBy; - - // ...same for lock index. - n = (0xFFFFFFFFFFFFFFFFL >>> prefixBits) - ((1 << LogLockCnt) - 1); - moveBy = 0; - while (n >= 1 << LogLockCnt) { - moveBy++; - n = n >>> 1; // == (n/2) - } - this.lockMoveBy = moveBy; + this.rShift = moveBy; } - /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#getLockIndex(long) - */ - protected int getLockIndex(long fp) { - return (int) ((fp & prefixMask) >> lockMoveBy); + @Override + protected long getIdx(final long fp) { + return (fp & prefixMask) >>> rShift; } + + @Override + protected long getIdx(final long fp, int probe) { + // Have to mod positions because probe might cause us to overshoot. + return (((fp & prefixMask) >>> rShift) + probe) % positions; + } + } - /** - * @param fp - * @return The logical bucket position in the table for the given fingerprint. - */ - protected long getLogicalPosition(final long fp) { - // push MSBs for moveBy positions to the right and align with a bucket address - long position = (fp & prefixMask) >> moveBy; - position = floorToBucket(position); - return position; + //**************************** Flusher ****************************// + + private LongComparator getLongComparator() { + return new LongComparator() { + @Override + public int compare(long fpA, long posA, long fpB, long posB) { + + // Elements not in Nat \ {0} remain at their current + // position. + if (fpA <= EMPTY || fpB <= EMPTY) { + return 0; + } + + final boolean wrappedA = indexer.getIdx(fpA) > posA; + final boolean wrappedB = indexer.getIdx(fpB) > posB; + + if (wrappedA == wrappedB && posA > posB) { + return fpA < fpB ? -1 : 1; + } else if (wrappedA ^ wrappedB) { + if (posA < posB && fpA < fpB) { + return -1; + } + if (posA > posB && fpA > fpB) { + return -1; + } + } + return 0; + } + }; + } + + /** + * Returns the number of fingerprints stored in table/array in the range + * [start,limit]. + */ + private long getTableOffset(final LongArray a, final long reprobe, final Indexer indexer, final long start, + final long limit) { + long occupied = 0L; + for (long pos = start; pos < limit; pos++) { + final long fp = a.get(pos % a.size()); + if (fp <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(fp); + if (idx > pos) { + // Ignore the elements that wrapped around the + // end when scanning the first partition. + continue; + } + if (idx + reprobe < pos) { + // Ignore the elements of the first partition + // when wrapping around for the last partition. + continue; + } + occupied = occupied + 1L; + } + return occupied; + } + + private long getNextLower(long idx) { + // Reverse to the next non-evicted/empty fp that belongs to this partition. + long fp = array.get(idx); + while (fp <= EMPTY || indexer.getIdx(fp) > idx) { + fp = array.get(--idx); + } + return fp; + } + + /** + * The number of fingerprints stored on disk smaller than fp. + */ + private long getDiskOffset(final int id, final long fp) throws IOException { + if (this.index == null) { + return 0L; } + + final int indexLength = this.index.length; + int loPage = 0; + int hiPage = indexLength - 1; + long loVal = this.index[loPage]; + long hiVal = this.index[hiPage]; - public long getNextBucketBasePosition(long logicalPosition) { - return floorToBucket(logicalPosition + bcktCapacity); + if (fp <= loVal) { + return 0L; + } + if (fp >= hiVal) { + return this.braf[id].length() / FPSet.LongSize; } + // See DiskFPSet#diskLookup for comments. - /** - * Returns the largest position that - * is less than or equal to the argument and is equal to bucket base address. - * - * @param logicalPosition - * @return - */ - private long floorToBucket(long logicalPosition) { - long d = (long) Math.floor(logicalPosition / bcktCapacity); - return bcktCapacity * d; + // Lookup the corresponding disk page in index. + final double dfp = (double) fp; + while (loPage < hiPage - 1) { + final double dhi = (double) hiPage; + final double dlo = (double) loPage; + final double dhiVal = (double) hiVal; + final double dloVal = (double) loVal; + int midPage = (loPage + 1) + (int) ((dhi - dlo - 1.0) * (dfp - dloVal) / (dhiVal - dloVal)); + if (midPage == hiPage) { + midPage--; + } + final long v = this.index[midPage]; + if (fp < v) { + hiPage = midPage; + hiVal = v; + } else if (fp > v) { + loPage = midPage; + loVal = v; + } else { + return (midPage * 1L) * (NumEntriesPerPage * 1L); + } } + // no page is in between loPage and hiPage at this point + Assert.check(hiPage == loPage + 1, EC.SYSTEM_INDEX_ERROR); + assert this.index[loPage] < fp && fp < this.index[hiPage]; + + // Read the disk page and try to find the given fingerprint or the next + // smaller one. Calculate its offset in file. + long midEntry = -1L; + long loEntry = ((long) loPage) * NumEntriesPerPage; + long hiEntry = ((loPage == indexLength - 2) ? this.fileCnt - 1 : ((long) hiPage) * NumEntriesPerPage); + final BufferedRandomAccessFile raf = this.braf[id]; + while (loEntry < hiEntry) { + midEntry = calculateMidEntry(loVal, hiVal, dfp, loEntry, hiEntry); + raf.seek(midEntry * LongSize); + final long v = raf.readLong(); - /** - * @param logicalPosition - * @return true iff logicalPosition is a multiple of bucketCapacity - */ - public boolean isBucketBasePosition(long logicalPosition) { - return logicalPosition % bcktCapacity == 0; + if (fp < v) { + hiEntry = midEntry; + hiVal = v; + } else if (fp > v) { + loEntry = midEntry + 1; + loVal = v; + midEntry = loEntry; + } else { + break; + } + } + + assert isHigher(midEntry, fp, raf); + return midEntry; + } + + public boolean isHigher(long midEntry, long fp, BufferedRandomAccessFile raf) throws IOException { + raf.seek((midEntry - 1L) * LongSize); + final long low = raf.readLong(); + final long high = raf.readLong(); + if (low < fp && fp < high) { + return true; } + return false; } - - /** - * A {@link BitshiftingIndexer} uses the more efficient AND operation - * compared to MODULO and DIV used by {@link Indexer}. Since indexing is - * executed on every {@link FPSet#put(long)} or {@link FPSet#contains(long)} - * , it is worthwhile to minimize is execution overhead. - */ - public static class BitshiftingIndexer extends Indexer { + + public class ConcurrentOffHeapMSBFlusher extends OffHeapMSBFlusher { + private final int numThreads; + private final ExecutorService executorService; + private final int r; + private final long insertions; /** - * Mask used to round of to a bucket address which is a power of 2. + * The length of a single partition. */ - protected final long bucketBaseIdx; + private final long length; + private List<Result> offsets; + + public ConcurrentOffHeapMSBFlusher(final LongArray array, final int r, final int numThreads, + final long insertions) { + super(array); + this.r = r; + this.numThreads = numThreads; + this.insertions = insertions; - public BitshiftingIndexer(final int bucketCapacity, final long memoryInFingerprintCnt, final int prefixBits) throws RemoteException { - super(bucketCapacity, memoryInFingerprintCnt, prefixBits); - this.bucketBaseIdx = 0xFFFFFFFFFFFFFFFFL - (bcktCapacity - 1); + this.length = (long) Math.floor(a.size() / (double) numThreads); + this.executorService = Executors.newFixedThreadPool(numThreads); } /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#getLogicalPosition(long) + * @see tlc2.tool.fp.DiskFPSet.Flusher#prepareTable() */ @Override - protected long getLogicalPosition(final long fp) { - // push MSBs for moveBy positions to the right and align with a bucket address - return ((fp & prefixMask) >> moveBy) & bucketBaseIdx; + protected void prepareTable() { + final long now = System.currentTimeMillis(); + final CyclicBarrier phase = new CyclicBarrier(this.numThreads); + final Collection<Callable<Result>> tasks = new ArrayList<Callable<Result>>(numThreads); + for (int i = 0; i < numThreads; i++) { + final int id = i; + tasks.add(new Callable<Result>() { + @Override + public Result call() throws Exception { + final boolean isFirst = id == 0; + final boolean isLast = id == numThreads - 1; + final long start = id * length; + final long end = isLast ? a.size() - 1L : start + length; + + // Sort partition p_n. We have exclusive access. + LongArrays.sort(a, isFirst ? 0L : start + 1L, end, getLongComparator()); + assert checkSorted(a, indexer, r, isFirst ? 0L : start + 1L, end) == -1L : String.format( + "Array %s not fully sorted at index %s and reprobe %s in range [%s,%s].", a.toString(), + checkSorted(array, indexer, r, start, end), r, start, end); + + // Wait for the other threads sorting p_n+1 to be done + // before we stitch together p_n and p_n+1. + phase.await(); + + // Sort the range between partition p_n and + // p_n+1 bounded by reprobe. + LongArrays.sort(a, end - r+1L, end + r+1L, getLongComparator()); + + // Wait for table to be fully sorted before we calculate + // the offsets. Offsets can only be correctly calculated + // on a sorted table. + phase.await(); + + // Count the occupied positions for this + // partition. Occupied positions are those which + // get evicted (written to disk). + // This could be done as part of (insertion) sort + // above at the price of higher complexity. Thus, + // it's done here until it becomes a bottleneck. + final long limit = isLast ? a.size() + r : end; + final long occupied = getTableOffset(a, r, indexer, start, limit); + assert occupied <= limit - start; + + if (index == null) { + // No index, no need to calculate a disk offset. + return new Result(occupied, 0L); + } + + // Determine number of elements in the old/current file. + if (isFirst && isLast) { + return new Result(occupied, fileCnt); + } else if (isFirst) { + return new Result(occupied, getDiskOffset(id, getNextLower(end))); + } else if (isLast) { + return new Result(occupied, fileCnt - getDiskOffset(id, getNextLower(start))); + } else { + return new Result(occupied, + getDiskOffset(id, getNextLower(end)) - getDiskOffset(id, getNextLower(start))); + } + } + }); + } + try { + offsets = futuresToResults(executorService.invokeAll(tasks)); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(ie); + } catch (ExecutionException notExpectedToHappen) { + throw new OffHeapRuntimeException(notExpectedToHappen); + } + + assert checkSorted(a, indexer, r) == -1L : String.format( + "Array %s not fully sorted at index %s and reprobe %s.", a.toString(), + checkSorted(array, indexer, r), r); + + LOGGER.log(Level.FINE, "Sorted in-memory table with {0} workers and reprobe {1} in {2} ms.", + new Object[] { numThreads, r, System.currentTimeMillis() - now }); + } + + private List<Result> futuresToResults(List<Future<Result>> futures) throws InterruptedException, ExecutionException { + final List<Result> res = new ArrayList<Result>(futures.size()); + for (Future<Result> future : futures) { + res.add(future.get()); + } + return res; } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#getNextBucketPosition(long) - */ @Override - public long getNextBucketBasePosition(long logicalPosition) { - return (logicalPosition + bcktCapacity) & bucketBaseIdx; + protected void mergeNewEntries(final BufferedRandomAccessFile[] inRAFs, final RandomAccessFile outRAF, final Iterator ignored) throws IOException { + final long now = System.currentTimeMillis(); + assert offsets.stream().mapToLong(new ToLongFunction<Result>() { + public long applyAsLong(Result result) { + return result.getTable(); + } + }).sum() == insertions : "Missing inserted elements during eviction."; + assert offsets.stream().mapToLong(new ToLongFunction<Result>() { + public long applyAsLong(Result result) { + return result.getDisk(); + } + }).sum() == fileCnt : "Missing disk elements during eviction."; + + // Calculate offsets for in and the out file, i.e. sum up the + // combined number of elements in lower partitions. + for (int id = 1; id < numThreads; id++) { + final Result prev = offsets.get(id - 1); + final Result result = offsets.get(id); + result.setInOffset(prev.getInOffset() + prev.getDisk()); + result.setOutOffSet(prev.getOutOffset() + prev.getTotal()); + } + + final long outLength = outRAF.length(); + final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>(numThreads); + final BufferedRandomAccessFile[] tmpRAFs = new BufferedRandomAccessFile[numThreads]; + for (int i = 0; i < numThreads; i++) { + final int id = i; + + // Create a new RAF instance. The outRAF instance is + // otherwise shared by multiple writers leading to race + // conditions and inconsistent fingerprint set files. + tmpRAFs[id] = new BufferedRandomAccessFile(new File(tmpFilename), "rw"); + tmpRAFs[id].setLength(outLength); + + // Set offsets into the out (tmp) file. + final Result result = offsets.get(id); + tmpRAFs[id].seekAndMark(result.getOutOffset() * FPSet.LongSize); + + // Set offset and the number of elements the + // iterator is supposed to return. + final Iterator itr = new Iterator(a, result.getTable(), id * length, indexer, + id == 0 ? Iterator.WRAP.ALLOWED : Iterator.WRAP.FORBIDDEN); + + final BufferedRandomAccessFile inRAF = inRAFs[id]; + assert (result.getInOffset() + result.getDisk()) * FPSet.LongSize <= inRAF.length(); + inRAF.seekAndMark(result.getInOffset() * FPSet.LongSize); + + // Stop reading after diskReads elements (after + // which the next thread continues) except for the + // last thread which reads until EOF. Pass 0 when + // nothing can be read from disk. + final long diskReads = id == numThreads - 1 ? fileCnt - result.getInOffset() : result.getDisk(); + + tasks.add(new Callable<Void>() { + public Void call() throws Exception { + ConcurrentOffHeapMSBFlusher.super.mergeNewEntries(inRAF, tmpRAFs[id], itr, diskReads); + assert tmpRAFs[id].getFilePointer() == (result.getOutOffset() + result.getTotal()) * FPSet.LongSize : id + + " writer did not write expected amount of fingerprints to disk."; + return null; + } + }); + } + // Combine the callable results. + try { + executorService.invokeAll(tasks); + executorService.shutdown(); + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + assert checkRAFs(tmpRAFs); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(ie); + } finally { + executorService.shutdown(); + } + + // Finally close the out rafs after all tasks have finished. On + // Linux, closing an instance of the tmpRAFs appears to be racy when + // other tasks still execute. + for (int i = 0; i < tmpRAFs.length; i++) { + tmpRAFs[i].close(); + } + + assert checkRAFs(inRAFs); + assert checkTable(a) : "Missed element during eviction."; + + LOGGER.log(Level.FINE, "Wrote table to disk with {0} workers in {1} ms.", + new Object[] { numThreads, (System.currentTimeMillis() - now) }); } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#isBucketBase(long) - */ - @Override - public boolean isBucketBasePosition(long logicalPosition) { - return (logicalPosition & (bcktCapacity - 1)) == 0; + private class Result { + private final long occupiedTable; + private final long occupiedDisk; + private long outOffset; + private long inOffset; + + public Result(long occupiedTable, long occupiedDisk) { + this.occupiedTable = occupiedTable; + this.occupiedDisk = occupiedDisk; + } + public long getDisk() { + return occupiedDisk; + } + public long getTable() { + return occupiedTable; + } + public long getTotal() { + return occupiedDisk + occupiedTable; + } + public void setOutOffSet(long offset) { + this.outOffset = offset; + } + public void setInOffset(long offset) { + this.inOffset = offset; + } + public long getInOffset() { + return this.inOffset; + } + public long getOutOffset() { + return this.outOffset; + } } } public class OffHeapMSBFlusher extends Flusher { + protected final LongArray a; + + public OffHeapMSBFlusher(LongArray array) { + a = array; + } + /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet.Flusher#flushTable() + * @see tlc2.tool.fp.DiskFPSet.Flusher#prepareTable() */ @Override - void flushTable() throws IOException { - super.flushTable(); + protected void prepareTable() { + super.prepareTable(); + final int r = PROBE_LIMIT; + + assert checkInput(array, indexer, r) : "Table violates invariants prior to eviction"; - // garbage old values in collision bucket - collisionBucket.clear(); + // Sort with a single thread. + LongArrays.sort(a, 0, a.size() - 1L + r, getLongComparator()); + + assert checkSorted(a, indexer, r) == -1L : String.format( + "Array %s not fully sorted at index %s and reprobe %s.", a.toString(), + checkSorted(array, indexer, r), r); } - + /* (non-Javadoc) * @see tlc2.tool.fp.MSBDiskFPSet#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile) */ @Override - protected void mergeNewEntries(RandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { - final long buffLen = tblCnt.get(); - ByteBufferIterator itr = new ByteBufferIterator(array, collisionBucket, buffLen); - - // Precompute the maximum value of the new file - long maxVal = itr.getLast(); - if (index != null) { - maxVal = Math.max(maxVal, index[index.length - 1]); - } + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { + final long buffLen = tblCnt.sum(); + final Iterator itr = new Iterator(array, buffLen, indexer); - int indexLen = calculateIndexLen(buffLen); + final int indexLen = calculateIndexLen(buffLen); index = new long[indexLen]; - index[indexLen - 1] = maxVal; - currIndex = 0; - counter = 0; - - // initialize positions in "buff" and "inRAF" - long value = 0L; // initialize only to make compiler happy - boolean eof = false; - if (fileCnt > 0) { - try { - value = inRAFs[0].readLong(); - } catch (EOFException e) { - eof = true; - } + mergeNewEntries(inRAFs, outRAF, itr); + + final long length = (outRAF.length() / LongSize) - 1L; + writeIndex(index, outRAF, length); + assert checkIndex(index) : "Broken disk index."; + assert checkIndex(index, outRAF, length) : "Misaligned disk index."; + + // maintain object invariants + fileCnt += buffLen; + } + + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF, Iterator itr) + throws IOException { + inRAFs[0].seek(0); + mergeNewEntries(inRAFs[0], outRAF, itr, inRAFs[0].length() / FPSet.LongSize); + } + + /* + * See PlusCal spec OpenAddressing.ConcurrentFlusher.tla which has been checked for Nat == 0..6. + */ + protected void mergeNewEntries(BufferedRandomAccessFile inRAF, RandomAccessFile outRAF, final Iterator itr, + long diskReads) throws IOException { + + // Disk might be empty. + long value = 0L; + if (diskReads > 0) { + value = inRAF.readLong(); } else { - eof = true; + assert fileCnt == 0L; } - - // merge while both lists still have elements remaining - boolean eol = false; - long fp = itr.next(); - while (!eof || !eol) { - if ((value < fp || eol) && !eof) { - writeFP(outRAF, value); - try { - value = inRAFs[0].readLong(); - } catch (EOFException e) { - eof = true; - } - } else { - // prevent converting every long to String when assertion holds (this is expensive) - if (value == fp) { - //MAK: Commented cause a duplicate does not pose a risk for correctness. - // It merely indicates a bug somewhere. - //Assert.check(false, EC.TLC_FP_VALUE_ALREADY_ON_DISK, - // String.valueOf(value)); - MP.printWarning(EC.TLC_FP_VALUE_ALREADY_ON_DISK, String.valueOf(value)); - } - writeFP(outRAF, fp); - // we used one fp up, thus move to next one - try { - fp = itr.next(); - } catch (NoSuchElementException e) { - // has read all elements? - Assert.check(!itr.hasNext(), EC.GENERAL); - eol = true; + + long tableReads = itr.elements; + long fp = itr.markNext(); + + do { + if (value == fp) { + MP.printWarning(EC.TLC_FP_VALUE_ALREADY_ON_DISK, String.valueOf(value)); + } + assert fp > EMPTY : "Wrote an invalid fingerprint to disk."; + + // From memory/table + if (tableReads > 0 && (fp < value || diskReads == 0)) { + outRAF.writeLong(fp); + tableReads--; + diskWriteCnt.increment(); + // Read next value if any. + if (tableReads > 0) { + final long nextFP = itr.markNext(); + assert nextFP > fp : nextFP + " > " + fp + " from table at pos " + itr.pos + " " + + a.toString(itr.pos - 10L, itr.pos + 10L); + fp = nextFP; + } + } + + // From file/disk + if (diskReads > 0 && (value < fp || tableReads == 0)) { + outRAF.writeLong(value); + diskReads--; + diskWriteCnt.increment(); + // Read next value if any. + if (diskReads > 0) { + final long nextValue = inRAF.readLong(); + assert value < nextValue; + value = nextValue; } } - } + + } while (diskReads > 0 || tableReads > 0); // both sets used up completely - Assert.check(eof && eol, EC.GENERAL); - - // currIndex is amount of disk writes - Assert.check(currIndex == indexLen - 1, EC.SYSTEM_INDEX_ERROR); + Assert.check(diskReads == 0L && tableReads == 0L, EC.GENERAL); + assert !itr.hasNext(); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#calculateIndexLen(long) + */ + @Override + protected int calculateIndexLen(final long tblcnt) { + int indexLen = super.calculateIndexLen(tblcnt); + if ((tblcnt + fileCnt - 1L) % NumEntriesPerPage == 0L) { + // This is the special case where the largest fingerprint + // happened is going to end up on the last entry of the previous + // page. Thus, we won't need the last extra index cell. + indexLen--; + } + return indexLen; + } - // maintain object invariants - fileCnt += buffLen; + protected void writeIndex(long[] index, final RandomAccessFile raf, long length) throws IOException { + for (int i = 0; i < index.length; i++) { + long pos = Math.min(i * NumEntriesPerPage, length); + raf.seek(pos * LongSize); + final long value = raf.readLong(); + index[i] = value; } } /** - * A non-thread safe Iterator + * A non-thread safe Iterator whose next method returns the next largest + * element. */ - public class ByteBufferIterator { + public static class Iterator { - private final CollisionBucket cs; - /** - * Number of elements in the buffer - */ - private long bufferElements; - /** - * Total amount of elements in both the buffer as well as the collisionBucket. - */ - private final long totalElements; - /** - * The logical position is the position inside the {@link LongBuffer} and - * thus reflects a fingerprints - */ - private long logicalPosition = 0; - /** - * Used to verify that the elements we hand out are strictly monotonic - * increasing. - */ - private long previous = -1l; - /** - * Number of elements read with next() - */ - private long readElements = 0L; + private enum WRAP { + ALLOWED, FORBIDDEN; + } - private long cache = -1L; + private final long elements; private final LongArray array; + private final Indexer indexer; + private final WRAP canWrap; - public ByteBufferIterator(LongArray helper, CollisionBucket collisionBucket, long expectedElements) { - this.array = helper; - this.logicalPosition = 0L; - this.totalElements = expectedElements; - - // Do calculation before prepareForFlush() potentially empties the cs causing size() to return 0 - this.bufferElements = expectedElements - collisionBucket.size(); - - this.cs = collisionBucket; - this.cs.prepareForFlush(); + private long pos = 0; + private long elementsRead = 0L; + + public Iterator(final LongArray array, final long elements, final Indexer indexer) { + this(array, elements, 0L, indexer, WRAP.ALLOWED); } - /** - * Returns the next element in the iteration. - * - * @return the next element in the iteration. - * @exception NoSuchElementException iteration has no more elements. - */ - public long next() { - long result = -1l; + public Iterator(final LongArray array, final long elements, final long start, final Indexer indexer) { + this(array, elements, start, indexer, WRAP.FORBIDDEN); + } - if (cache < 0L && bufferElements > 0) { - result = getNextFromBuffer(); - bufferElements--; - } else { - result = cache; - cache = -1L; - } + public Iterator(final LongArray array, final long elements, final long start, final Indexer indexer, + final WRAP canWrap) { + this.array = array; + this.elements = elements; + this.indexer = indexer; + this.pos = start; + this.canWrap = canWrap; + } - if (!cs.isEmpty()) { - long first = cs.first(); - if (result > first || result == -1L) { - cs.remove(first); - cache = result; - result = first; - } - } - - // adhere to the general Iterator contract to fail fast and not hand out - // meaningless values - if (result == -1L) { - throw new NoSuchElementException(); - } - - // hand out strictly monotonic increasing elements - Assert.check(previous < result, EC.GENERAL); - previous = result; - - // maintain read statistics - readElements++; - - return result; + public long getPos() { + return pos; } - private long getNextFromBuffer() { - sortNextBucket(); - - long l = array.get(logicalPosition); - if (l > 0L) { - array.set(logicalPosition++, l | 0x8000000000000000L); - return l; - } - - while ((l = array.get(logicalPosition)) <= 0L && logicalPosition < maxTblCnt) { - // increment position to next bucket - logicalPosition = indexer.getNextBucketBasePosition(logicalPosition); - sortNextBucket(); - } - - if (l > 0L) { - array.set(logicalPosition++, l | 0x8000000000000000L); - return l; + /** + * Returns the next element in the iteration that is not EMPTY nor + * marked evicted. + * + * @return the next element in the iteration that is not EMPTY nor + * marked evicted. + * @exception NoSuchElementException + * iteration has no more elements. + */ + public long next() { + return next0(false, Long.MAX_VALUE); + } + + long next(long maxPos) { + if (pos >= maxPos) { + return EMPTY; } - throw new NoSuchElementException(); + return next0(false, maxPos); + } + + /** + * Returns the next element in the iteration that is not EMPTY nor + * marked evicted and marks it to be evicted. + * <p> + * THIS IS NOT SIDEEFFECT FREE. AFTERWARDS, THE ELEMENT WILL BE MARKED + * EVICTED. + * + * @return the next element in the iteration that is not EMPTY nor + * marked evicted. + * @exception NoSuchElementException + * iteration has no more elements. + */ + public long markNext() { + return next0(true, Long.MAX_VALUE); } - // sort the current logical bucket if we reach the first slot of the - // bucket - private void sortNextBucket() { - if (indexer.isBucketBasePosition(logicalPosition)) { - long[] longBuffer = new long[bucketCapacity]; - int i = 0; - for (; i < bucketCapacity; i++) { - long l = array.get(logicalPosition + i); - if (l <= 0L) { - break; - } else { - longBuffer[i] = l; - } + private long next0(final boolean mark, final long maxPos) { + long elem; + do { + final long position = pos % array.size(); + elem = array.get(position); + if (elem == EMPTY) { + pos = pos + 1L; + continue; } - if (i > 0) { - Arrays.sort(longBuffer, 0, i); - for (int j = 0; j < i; j++) { - array.set(logicalPosition + j, - longBuffer[j]); - } + if (elem < EMPTY) { + pos = pos + 1L; + continue; + } + final long baseIdx = indexer.getIdx(elem); + if (baseIdx > pos) { + // This branch should only be active for thread with id 0. + assert canWrap == WRAP.ALLOWED; + pos = pos + 1L; + continue; } + pos = pos + 1L; + // mark elem in array as being evicted. + if (mark) {array.set(position, elem | MARK_FLUSHED);} + elementsRead = elementsRead + 1L; + return elem; + } while (hasNext() && pos < maxPos); + if (pos >= maxPos) { + return EMPTY; } + throw new NoSuchElementException(); } - + /** * Returns <tt>true</tt> if the iteration has more elements. (In other * words, returns <tt>true</tt> if <tt>next</tt> would return an element @@ -607,144 +1254,135 @@ public final class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic * @return <tt>true</tt> if the iterator has more elements. */ public boolean hasNext() { - // hasNext does not move the indices at all! - return readElements < totalElements; - } - - /** - * @return The last element in the iteration. - * @exception NoSuchElementException if iteration is empty. - */ - public long getLast() { - // Remember current position - final long tmpLogicalPosition = logicalPosition; - - // Calculate last bucket position and have it sorted - logicalPosition = maxTblCnt - bucketCapacity; - sortNextBucket(); - - // Reverse the current bucket to obtain last element (More elegantly - // this could be achieved recursively, but this can cause a - // stack overflow). - long l = 1L; - while ((l = array.get(logicalPosition-- + bucketCapacity - 1)) <= 0L) { - sortNextBucket(); - } - - // Done searching in-memory storage backwards, reset position to - // original value. - logicalPosition = tmpLogicalPosition; - - // Compare max element found in main in-memory buffer to man - // element in collisionBucket. Return max of the two. - if (!cs.isEmpty()) { - l = Math.max(cs.last(), l); - } - - // Either return the maximum element or fail fast. - if (l > 0L) { - return l; - } - throw new NoSuchElementException(); + return elementsRead < elements; } } - - public interface CollisionBucket { - void clear(); - void prepareForFlush(); + public static class OffHeapRuntimeException extends RuntimeException { - void remove(long first); - - long first(); - - long last(); - - boolean isEmpty(); - - /** - * @param fp - * @return {@code true} if this set did not already contain the specified - * fingerprint - */ - boolean add(long fp); - - boolean contains(long fp); - - long size(); + public OffHeapRuntimeException(Exception ie) { + super(ie); + } } - public class TreeSetCollisionBucket implements CollisionBucket { - private final TreeSet<Long> set; - - public TreeSetCollisionBucket(int initialCapacity) { - this.set = new TreeSet<Long>(); - } + // ***************** assertion helpers *******************// - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#clear() - */ - public void clear() { - set.clear(); + private static boolean checkInput(final LongArray array, final Indexer indexer, final int reprobe) { + for (long pos = 0; pos <= array.size() + reprobe - 1L; pos++) { + long tmp = array.get(pos % array.size()); + if (tmp == EMPTY) { + continue; + } + if (tmp < EMPTY) { + // Convert evicted fps back. + tmp = tmp & FLUSHED_MASK; + } + final long idx = indexer.getIdx(tmp); + // Accept wrapped elements. + if (pos < reprobe && idx > (array.size() - 1L - pos - reprobe)) { + continue; + } + // Accept non-wrapped elements when pos > array.size + if (pos > array.size() - 1L && idx + reprobe < pos) { + continue; + } + // Accept any other valid elements where pos is within the range + // given by [idx,idx + reprobe]. + if (idx <= pos && pos <= idx + reprobe) { + continue; + } + System.err.println(String.format("%s with idx %s at pos %s (reprobe: %s).", tmp, idx, pos, reprobe)); + return false; } + return true; + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#prepareForFlush() - */ - public void prepareForFlush() { - // no-op + /** + * @return -1L iff array is sorted, index/position of the element that violates otherwise in the range [start, end]. + */ + private static long checkSorted(final LongArray array, final Indexer indexer, int reprobe, long start, long end) { + if (reprobe >= array.size()) { + // When the array is smaller than the number of positions/slots, the + // loop below will run past its bounds. If this happens, it compares + // elements the element at the start with the element at the end. + reprobe = (int) (array.size() - 1); } - - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#remove(long) - */ - public void remove(long first) { - set.remove(first); + long e = 0L; + for (long pos = start; pos <= end; pos++) { + final long tmp = array.get(pos % array.size()); + if (tmp <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(tmp); + if (idx > pos) { + continue; + } + if (idx + reprobe < pos) { + continue; + } + if (e == 0L) { + // Initialize e with the first element that is not <=EMPTY + // or has wrapped. + e = tmp; + continue; + } + if (e >= tmp) { + System.err.println(String.format("%s >= %s at pos %s.", e, tmp, pos)); + return pos; + } + e = tmp; } + return -1L; + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#first() - */ - public long first() { - return set.first(); - } - - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#last() - */ - public long last() { - return set.last(); - } + /** + * @return -1L iff array is sorted, index/position of the element that violates otherwise. + */ + private static long checkSorted(final LongArray array, final Indexer indexer, final int reprobe) { + return checkSorted(array, indexer, reprobe, 0, array.size() - 1L + reprobe); + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#isEmpty() - */ - public boolean isEmpty() { - return set.isEmpty(); + private static boolean checkRAFs(final BufferedRandomAccessFile[] rafs) throws IOException { + for (int i = 0; i < rafs.length - 1; i++) { + final long end = rafs[i].getFilePointer(); + final long start = rafs[i + 1].getMark(); + if (end != start) { + return false; + } } - - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#add(long) - * - * If this set already contains the element, the call leaves the set - * unchanged and returns false. - */ - public boolean add(long fp) { - return set.add(fp); + return true; + } + + private static boolean checkTable(LongArray array) { + for (long i = 0L; i < array.size(); i++) { + long elem = array.get(i); + if (elem > EMPTY) { + System.err.println(String.format("%s elem at pos %s.", elem, i)); + return false; + } } + return true; + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#contains(long) - */ - public boolean contains(long fp) { - return set.contains(fp); + private static boolean checkIndex(final long[] idx) { + for (int i = 1; i < idx.length; i++) { + if (idx[i - 1] >= idx[i]) { + return false; + } } - - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#size() - */ - public long size() { - return set.size(); + return true; + } + + private static boolean checkIndex(final long[] idx, final RandomAccessFile raf, final long length) throws IOException { + for (long i = 0L; i < idx.length; i++) { + final long pos = Math.min(i * NumEntriesPerPage, length); + raf.seek(pos * LongSize); + final long value = raf.readLong(); + final long index = idx[(int) i]; + if (value != index) { + return false; + } } + return true; } } diff --git a/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla b/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla new file mode 100644 index 0000000000000000000000000000000000000000..53eafd5599f16d9f73070695b96a256869a38a82 --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla @@ -0,0 +1,225 @@ +------------------------------- MODULE Merge ------------------------------- +EXTENDS Integers, TLC, Sequences, FiniteSets + +(**********************************) +(* The larger of the two elements *) +(**********************************) +Max(a,b) == IF a > b THEN a ELSE b + +(***************************************************************************) +(* The image of the function F. *) +(***************************************************************************) +Image(F) == { F[x] : x \in DOMAIN F } + +(***************************************************************************) +(* Convertes the given Sequence seq into a Set of all the *) +(* Sequence's elements. In other words, the image of the function *) +(* that seq is. *) +(***************************************************************************) +SeqToSet(seq) == {seq[i] : i \in 1..Len(seq)} + +(***************************************************************************) +(* Returns a Set of all possible permutations with distinct elemenents *) +(* created out of the elements of Set set. All elements of set occur in *) +(* in the sequence. *) +(***************************************************************************) +IsOrdered(seq) == IF seq = <<>> \/ Len(seq) = 1 THEN TRUE ELSE + \A i \in 1..(Len(seq) - 1): seq[i] < seq[i+1] +OrderedSequences(set) == UNION {{perm \in [1..Cardinality(set) -> set]: + IsOrdered(perm)}} + +(*** +--fair algorithm Merge { + + variables history = {}, + a = 0, + aLength = 0, + A = <<>>, + b = 0, + bLength = 0, + B = <<>>, + O = <<>>; + + define { + Inv == /\ Image(O) \subseteq history + /\ IsOrdered(O) + } + + macro Consume(var, seq) { + var := Head(seq); + seq := Tail(seq); + } + { + + (* Initialize a and b with all possible combination of sorted sequences. *) + init: + with (r \in SUBSET Nat \ {{}}) { + with (s \in ((SUBSET (Nat \ r)) \ {{}})) { + with (t \in OrderedSequences(s) \cup {<<>>}) { + B := t; + bLength := Len(B); + with (u \in OrderedSequences(r) \cup {<<>>}) { + A := u; + aLength := Len(A); + } + }; + }; + }; + + (* Keep history. *) + hstry: + history := SeqToSet(A) \cup SeqToSet(B); + if (Cardinality(history) = 0) { + goto "Done"; + }; + + (* Merge sorted A and B to obtain sorted O. *) + mrg1: + if (aLength > 0) { + Consume(a, A); + }; + if (bLength > 0) { + Consume(b, B); + }; + + mrg2: + if (aLength > 0 /\ (a < b \/ bLength = 0)) { + O := Append(O, a); + aLength := aLength - 1; + if (aLength > 0) { + Consume(a, A); + }; + }; + mrg2b: + if (bLength > 0 /\ (b < a \/ aLength = 0)) { + O := Append(O, b); + bLength := bLength - 1; + if (bLength > 0) { + Consume(b, B); + }; + }; + mrg3: + if (bLength > 0 \/ aLength > 0) { + goto mrg2; + }; + mrg4: + assert bLength = 0 /\ aLength = 0; + assert Len(O) = Cardinality(history); + assert Image(O) = history; + } +} + +*** this ends the comment containg the pluscal code **********) +\* BEGIN TRANSLATION +VARIABLES history, a, aLength, A, b, bLength, B, O, pc + +(* define statement *) +Inv == /\ Image(O) \subseteq history + /\ IsOrdered(O) + + +vars == << history, a, aLength, A, b, bLength, B, O, pc >> + +Init == (* Global variables *) + /\ history = {} + /\ a = 0 + /\ aLength = 0 + /\ A = <<>> + /\ b = 0 + /\ bLength = 0 + /\ B = <<>> + /\ O = <<>> + /\ pc = "init" + +init == /\ pc = "init" + /\ \E r \in SUBSET Nat \ {{}}: + \E s \in ((SUBSET (Nat \ r)) \ {{}}): + \E t \in OrderedSequences(s) \cup {<<>>}: + /\ B' = t + /\ bLength' = Len(B') + /\ \E u \in OrderedSequences(r) \cup {<<>>}: + /\ A' = u + /\ aLength' = Len(A') + /\ pc' = "hstry" + /\ UNCHANGED << history, a, b, O >> + +hstry == /\ pc = "hstry" + /\ history' = (SeqToSet(A) \cup SeqToSet(B)) + /\ IF Cardinality(history') = 0 + THEN /\ pc' = "Done" + ELSE /\ pc' = "mrg1" + /\ UNCHANGED << a, aLength, A, b, bLength, B, O >> + +mrg1 == /\ pc = "mrg1" + /\ IF aLength > 0 + THEN /\ a' = Head(A) + /\ A' = Tail(A) + ELSE /\ TRUE + /\ UNCHANGED << a, A >> + /\ IF bLength > 0 + THEN /\ b' = Head(B) + /\ B' = Tail(B) + ELSE /\ TRUE + /\ UNCHANGED << b, B >> + /\ pc' = "mrg2" + /\ UNCHANGED << history, aLength, bLength, O >> + +mrg2 == /\ pc = "mrg2" + /\ IF aLength > 0 /\ (a < b \/ bLength = 0) + THEN /\ O' = Append(O, a) + /\ aLength' = aLength - 1 + /\ IF aLength' > 0 + THEN /\ a' = Head(A) + /\ A' = Tail(A) + ELSE /\ TRUE + /\ UNCHANGED << a, A >> + ELSE /\ TRUE + /\ UNCHANGED << a, aLength, A, O >> + /\ pc' = "mrg2b" + /\ UNCHANGED << history, b, bLength, B >> + +mrg2b == /\ pc = "mrg2b" + /\ IF bLength > 0 /\ (b < a \/ aLength = 0) + THEN /\ O' = Append(O, b) + /\ bLength' = bLength - 1 + /\ IF bLength' > 0 + THEN /\ b' = Head(B) + /\ B' = Tail(B) + ELSE /\ TRUE + /\ UNCHANGED << b, B >> + ELSE /\ TRUE + /\ UNCHANGED << b, bLength, B, O >> + /\ pc' = "mrg3" + /\ UNCHANGED << history, a, aLength, A >> + +mrg3 == /\ pc = "mrg3" + /\ IF bLength > 0 \/ aLength > 0 + THEN /\ pc' = "mrg2" + ELSE /\ pc' = "mrg4" + /\ UNCHANGED << history, a, aLength, A, b, bLength, B, O >> + +mrg4 == /\ pc = "mrg4" + /\ Assert(bLength = 0 /\ aLength = 0, + "Failure of assertion at line 106, column 8.") + /\ Assert(Len(O) = Cardinality(history), + "Failure of assertion at line 107, column 8.") + /\ Assert(Image(O) = history, + "Failure of assertion at line 108, column 8.") + /\ pc' = "Done" + /\ UNCHANGED << history, a, aLength, A, b, bLength, B, O >> + +Next == init \/ hstry \/ mrg1 \/ mrg2 \/ mrg2b \/ mrg3 \/ mrg4 + \/ (* Disjunct to prevent deadlock on termination *) + (pc = "Done" /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION + +============================================================================= +\* Modification History +\* Last modified Tue Jun 06 20:39:04 CEST 2017 by markus +\* Created Mon Jun 05 23:14:05 CEST 2017 by markus diff --git a/tlatools/src/tlc2/tool/fp/OpenAddressing.tla b/tlatools/src/tlc2/tool/fp/OpenAddressing.tla new file mode 100644 index 0000000000000000000000000000000000000000..9505db8a30cc7e216caa18fc2e17e737534e4f14 --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/OpenAddressing.tla @@ -0,0 +1,866 @@ +\begin{ppcal} +-------------------------- MODULE OpenAddressing -------------------------- +EXTENDS Sequences, FiniteSets, Integers, TLC + +(***************************************************************************) +(* K: The overall number of fingerprints that fit into the table. *) +(* fps: The set of fingerprints to be inserted into the hash table. *) +(* empty: An empty (model) value. Used to mark an unoccupied table element.*) +(* Writer: The set of processes/threads which insert fingerprints. Reader: *) +(* The set of processes which check fingerprints with contains. L: The *) +(* probing limit. *) +(***************************************************************************) +CONSTANT K, fps, empty, Writer, Reader, L + +(***************************************************************************) +(* K is a positive natural. emtpy is disjunct to all elements in fps. *) +(* fingerprints are natural numbers and can be well-ordered. *) +(***************************************************************************) +ASSUME /\ K \in (Nat \ {0}) + /\ \A fp \in fps: fp \in (Nat \ {0}) + /\ empty \notin fps + /\ (2*L) <= K + +---------------------------------------------------------------------------- + +(***************************************************************************) +(* The element of position Len(seq) of a sequence seq. *) +(***************************************************************************) +last(seq) == seq[Len(seq)] + +(***************************************************************************) +(* The largest element in the sequence, assuming sequence to be sorted in *) +(* ascending order. *) +(***************************************************************************) +largestElem(sortedSeq) == IF sortedSeq = <<>> THEN 0 ELSE last(sortedSeq) + +(***************************************************************************) +(* All elements of seq1 smaller than elem and the largest element in seq2. *) +(***************************************************************************) +subSetSmaller(seq1, seq2, elem) == SelectSeq(seq1, LAMBDA p: + p < elem /\ p > largestElem(seq2)) + +(***************************************************************************) +(* All elements of seq1 larger than the largest element in seq2. *) +(***************************************************************************) +subSetLarger(seq1, seq2) == IF seq2 = <<>> + THEN seq1 + ELSE SelectSeq(seq1, LAMBDA p: + p > largestElem(seq2)) + +(***************************************************************************) +(* TRUE iff the sequence seq contains the element elem. *) +(***************************************************************************) +containsElem(seq, elem) == \E i \in 1..Len(seq): seq[i] = elem + +(***************************************************************************) +(* The minimum and maximum element in set s. *) +(***************************************************************************) +min(S) == CHOOSE s \in S: \A a \in S: s <= a +max(S) == CHOOSE s \in S: \A a \in S: s >= a + +(***************************************************************************) +(* The smaller of the two values. *) +(***************************************************************************) +minimum(a, b) == IF a < b THEN a ELSE b + +(***************************************************************************) +(* The given index i modulo the sequences' length. *) +(***************************************************************************) +mod(i,len) == IF i % len = 0 THEN len ELSE (i % len) + +(***************************************************************************) +(* Logical bit-shifting to the right (shifts in zeros from the left/MSB). *) +(* TLC's standard division does not round towards zero, thus this is *) +(* specified recursively, manually taking care of rounding. *) +(***************************************************************************) +RECURSIVE shiftR(_,_) +shiftR(n,pos) == IF pos = 0 THEN n + ELSE LET odd(z) == z % 2 = 1 + m == IF odd(n) THEN (n-1) \div 2 ELSE n \div 2 + IN shiftR(m, pos - 1) + +(***************************************************************************) +(* Bitshifting (faster for any real implementation). *) +(***************************************************************************) +bitshift(fp, p) == LET k == CHOOSE k \in 1..K: 2^k = K + IN mod(shiftR(fp, k - 1) + 1 + p, K) + +(***************************************************************************) +(* Re-scale. *) +(***************************************************************************) +rescale(k,maxF,minF,fp,p) == LET f == (k - 1) \div (maxF - minF) + IN mod((f * (fp - minF + 1)) + p, k) + +(***************************************************************************) +(* Calculates an fp's index where fp \in fps. p is an alternative address, *) +(* such that: p \in 0..P. Uses bitshifting iff K is power of two. *) +(***************************************************************************) +idx(fp, p) == rescale(K, max(fps), min(fps), fp, p) + +(***************************************************************************) +(* TRUE iff the fingerprint at table position index is equal to fp or its *) +(* corresponding negative fp value (marked as to be copied to secondary). *) +(***************************************************************************) +isMatch(fp, index, table) == \/ table[index] = fp + \/ table[index] = (-1*fp) + +(***************************************************************************) +(* TRUE iff the table at position index is empty. *) +(***************************************************************************) +isEmpty(index, table) == table[index] = empty + +(***************************************************************************) +(* TRUE iff the table at position index is marked evicted. *) +(***************************************************************************) +isMarked(index, table) == table[index] < 0 + +(***************************************************************************) +(* t is sorted iff its empty-filtered sub-sequence is sorted. An empty *) +(* sequence is defined to be sorted. *) +(***************************************************************************) +isSorted(seq) == LET sub == SelectSeq(seq, LAMBDA e: e # empty) + IN IF sub = <<>> THEN TRUE + ELSE \A i \in 1..(Len(sub) - 1): + sub[mod(i, Len(sub))] < sub[mod(i+1, Len(sub))] + +(***************************************************************************) +(* TRUE iff fingerprint fp is either found within table according to *) +(* isMatch or in seq. *) +(***************************************************************************) +contains(f,t,seq,Q) == \/ \E i \in 0..Q: isMatch(f,idx(f,i),t) + \/ \E i \in 1..Len(seq): seq[i] = f + +---------------------------------------------------------------------------- + +(***************************************************************************) +(* A fp wrapped around if its alternate indices are beyond K and its *) +(* actual index is lower than its primary idx. Another mental picture for *) +(* table is a circular list and wrapped means that a fingerprint crossed *) +(* the logically first position of table. *) +(***************************************************************************) +wrapped(fp, pos) == idx(fp, 0) > mod(pos, K) + +(***************************************************************************) +(* TRUE iff the given two fingerprints have to be swapped to satisfy the *) +(* expected order. *) +(***************************************************************************) +compare(fp1,i1,fp2,i2) == IF fp1 # empty /\ fp2 # empty + THEN IF wrapped(fp1, i1) = wrapped(fp2, i2) /\ i1 > i2 + THEN IF fp1 < fp2 THEN -1 ELSE 1 + ELSE IF ~(wrapped(fp1, i1) <=> wrapped(fp2, i2)) + THEN IF i1 < i2 /\ fp1 < fp2 + THEN -1 + ELSE IF i1 > i2 /\ fp1 > fp2 + THEN -1 + ELSE 0 + ELSE 0 + ELSE 0 + +---------------------------------------------------------------------------- + +(*** this is a comment containing the PlusCal code * +--fair algorithm OpenAddressing +(***************************************************************************) +(* table: The actual hash table specified as a TLA+ sequence. history: An *) +(* auxiliary (history) variable unrelated to the actual hash table *) +(* specification. It just records the inserted fingerprints to be verified *) +(* by Inv. An implementation won't need history. secondary: The secondary *) +(* storage where fingerprints are eventually evicted to. outer/inner: *) +(* Index variables local to the sort action. P: The number of times an *) +(* alterante index is to be tried. *) +(***************************************************************************) +{ variable table = [i \in 1..K |-> empty], + secondary = <<>>, + newsecondary = <<>>, + P = 0, \* LongAccumulator in Java + evict = FALSE, \* AtomicBoolean in Java + waitCnt = 0, \* CyclicBarrier in Java + history = {}; + + (************************************************************************) + (* Compare-and-swap is linearizable (appears atomic from the POV of *) + (* observers such as other threads) and disjoint-access-parallel (two *) + (* CAS operations on disjoint memory locations do not synchronize). *) + (* *) + (* Variants of CAS (only basic CAS supported by Java. No HW support *) + (* for DCAS, CASN): *) + (* CAS: Basic compare and swap of a 64bit memory location. *) + (* DWCAS: CAS of two adjacent/contiguous 64bit memory locations. *) + (* (CMPXCHG16) (to swap to adjacent positions in e.g table) *) + (* DCAS: CAS of two arbitrary 64bit memory locations (swap of arbitrary *) + (* locations). *) + (* CASN: CAS N arbitrary 64bit memory locations. *) + (* *) + (* http://liblfds.org/mediawiki/index.php?title=Article:CAS_and_LL/ *) + (* SC_Implementation_Details_by_Processor_family *) + (************************************************************************) + + (* Atomically compare and swap. *) + macro CAS(result, pos, expected, new) { + if (table[pos] = expected) { + table[pos] := new; + result := TRUE + } else { + result := FALSE + } + } + + procedure Evict() + variables ei = 1, ej = 1, lo = 0; { + (* Insertion sort. *) + strIns: while (ei <= K+P) { + lo := table[mod(ei + 1, K)]; + nestedIns: while (compare(lo, mod(ei + 1, K), + table[mod(ej, K)], mod(ej, K)) <= -1) { + table[mod(ej + 1, K)] := table[mod(ej, K)]; + if (ej = 0) { + ej := ej - 1; + goto set; + } else { + ej := ej - 1; + }; + }; + set: table[mod(ej + 1, K)] := lo; + ej := ei + 1; + ei := ei + 1; + }; + ei := 1; + + (* Write to external storage. *) + flush: while (ei <= K+P) { + lo := table[mod(ei, K)]; + if (lo # empty /\ + lo > largestElem(newsecondary) /\ + ((ei <= K /\ ~wrapped(lo,ei)) \/ + (ei > K /\ wrapped(lo,ei)))) { + (* Copy all smaller fps than lo from *) + (* secondary to newsecondary. *) + newsecondary := Append(newsecondary \o + subSetSmaller(secondary, newsecondary, lo), lo); + (* Mark table[mod(cpy,table)] as being *) + (* written to secondary. *) + table[mod(ei, K)] := lo * (-1); + }; + ei := ei + 1; + }; + (* Append remainder of secondary to newsecondary and *) + (* assign newsecondary to secondary. *) + secondary := newsecondary \o + subSetLarger(secondary, newsecondary); + newsecondary := <<>>; + (* Setting P to 0 means a larger portion of cntns queries *) + (* go to secondary. Leaving P unchanged results in a longer *) + (* lookup chain. However neither violates any invariants. *) + (* An implementation is likely to set P to the *) + (* mean/median/some other fancy statistics or treats P as *) + (* as an optimization to speedup insertions prior to the *) + (* first eviction. *) + with (p \in 0..P) { + P := p; + }; + rtrn: return; + } + + process (q \in Reader) + variables rfp = 0, rindex = 0, checked = {}; { + rwait: await history # {}; + rpick: while (TRUE) { + if (checked = history /\ history = fps) { + goto Done; + } else { + with (f \in history) { + rfp := f; + checked := checked \cup {f}; + }; + }; + + rcntns: rindex := 0; + if (evict) { + waitCnt := waitCnt + 1; + rwaitEv: await evict = FALSE; + rendWEv: waitCnt := waitCnt - 1; + goto rcntns + }; + + ronPrm: while (rindex <= P) { + if (isMatch(rfp, idx(rfp, rindex), table)) { + goto rpick + } else { + if (isEmpty(idx(rfp, rindex), table) + \/ isMarked(idx(rfp, rindex), table)) { + goto ronSnc; + } else { + rindex := rindex + 1 + } + } + }; + ronSnc: if (containsElem(secondary,rfp)) { + goto rpick + } else { + (* Since we picked a fp from history, it always *) + (* either has to be in table or secondary. *) + assert(FALSE); + }; + } + } + + (* A weak fair process. *) + fair process (p \in Writer) + variables fp = 0, index = 0, result = FALSE, expected = -1; { + pick: while (TRUE) { + (* No deadlock once all fingerprints have been inserted. *) + if ((fps \ history) = {}) { + goto Done; + } else { + (* Select some fp to be inserted *) + with (f \in (fps \ history)) { fp := f; }; + }; + + put: index := 0; + result := FALSE; + (* Set expected to infinity. expected is reused when *) + (* the algorithm runs a primary lookup and finds a *) + (* position which is either EMPTY or isMarked(...). *) + (* expected stores the (open) position for later use *) + (* where the fp is inserted. Maximally, a position *) + (* can be K + L, thus expected is set to K + L + 1; *) + (* as an approximation of infinity. *) + expected := K + L + 1; + (* Wait for eviction thread to do its work. *) + if (evict) { + waitCnt := waitCnt + 1; + waitEv: await evict = FALSE; + endWEv: waitCnt := waitCnt - 1; + goto put + }; + + (* Check secondary unless empty. First though, we do *) + (* a primary lookup in case the fp in question has not *) + (* been evicted to secondary yet. *) + chkSnc: if (secondary # <<>>) { + (* Primary lookup. *) + cntns: while (index < P) { + if (isMatch(fp, idx(fp, index), table)) { + goto pick + } else { + if (isEmpty(idx(fp, index), table)) { + (* Found an EMPTY position which proves *) + (* that fp cannot be found at higher *) + (* positions. Thus, no need to continue.*) + expected := minimum(expected, index); + goto onSnc; + } else { + if (isMarked(idx(fp, index), table)) { + (* None of the lower positions has *) + (* fp, thus keep the lowest position *) + (* for the second loop as the start *) + (* index. No point in checking known *) + (* lower positions in the loop again. *) + expected := minimum(expected, index); + index := index + 1; + } else { + index := index + 1 + } + } + } + }; + + + (* Secondary lookup. *) + onSnc: if (containsElem(secondary,fp)) { + goto pick + } else { + (* Have next loop start at expected determined *) + (* by previous loop. *) + index := expected; + (* Re-init expected to be used for its alternate purpose. *) + expected := -1; + }; + }; + + (* Put inserts the given fp into the hash table by sequentially *) + (* trying the primary to the P's alternate position. *) + insrt: while (index < L) { + expected := table[idx(fp,index)]; + if (expected = empty \/ + (expected < 0 /\ expected # (-1) * fp)) { + incP: if (index > P) {P := index}; + cas: CAS(result, idx(fp,index), expected, fp); + if (result) { + history := history \cup {fp}; + goto pick + } else { + (* Has been occupied in the meantime, *) + (* try to find another position. *) + goto insrt + } + }; + + (* Has fp been inserted by another process? Check isMatch *) + (* AFTER empty and on-secondary because of two reasons: *) + (* a) Thread A finds table[pos] to be empty but fails *) + (* to CAS fpX. Thread B concurrently also finds *) + (* table[pos] to be empty and succeeds to CAS fpX. *) + (* b) Iff table[pos] is empty or -1, higher positions *) + (* cannot be a match. *) + isMth: if (isMatch(fp,idx(fp,index),table)) { + goto pick + } else { + index := index + 1; + }; + }; \* end of while/insrt + + + (* We failed to insert fp into a full table, thus try *) + (* to become the thread that evicts to secondary. *) + tryEv: if (evict = FALSE) { + evict := TRUE; + (* Wait for all other insertion threads and *) + (* the one reader to park. *) + waitIns: await waitCnt = Cardinality(Writer) - 1 + Cardinality(Reader); + call Evict(); + endEv: evict := FALSE; + goto put; + } else { + goto put + } + } + } \* end while/pick + } +} +*** this ends the comment containg the pluscal code **********) +---------------------------------------------------------------------------- +\* BEGIN TRANSLATION +VARIABLES table, secondary, newsecondary, P, evict, waitCnt, history, pc, + stack, ei, ej, lo, rfp, rindex, checked, fp, index, result, + expected + +vars == << table, secondary, newsecondary, P, evict, waitCnt, history, pc, + stack, ei, ej, lo, rfp, rindex, checked, fp, index, result, + expected >> + +ProcSet == (Reader) \cup (Writer) + +Init == (* Global variables *) + /\ table = [i \in 1..K |-> empty] + /\ secondary = <<>> + /\ newsecondary = <<>> + /\ P = 0 + /\ evict = FALSE + /\ waitCnt = 0 + /\ history = {} + (* Procedure Evict *) + /\ ei = [ self \in ProcSet |-> 1] + /\ ej = [ self \in ProcSet |-> 1] + /\ lo = [ self \in ProcSet |-> 0] + (* Process q *) + /\ rfp = [self \in Reader |-> 0] + /\ rindex = [self \in Reader |-> 0] + /\ checked = [self \in Reader |-> {}] + (* Process p *) + /\ fp = [self \in Writer |-> 0] + /\ index = [self \in Writer |-> 0] + /\ result = [self \in Writer |-> FALSE] + /\ expected = [self \in Writer |-> -1] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self \in Reader -> "rwait" + [] self \in Writer -> "pick"] + +strEv(self) == /\ pc[self] = "strEv" + /\ IF ei[self] <= K+P + THEN /\ lo' = [lo EXCEPT ![self] = table[mod(ei[self] + 1, K)]] + /\ pc' = [pc EXCEPT ![self] = "fdsaf"] + /\ ei' = ei + ELSE /\ ei' = [ei EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "flush"] + /\ lo' = lo + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ej, rfp, rindex, + checked, fp, index, result, expected >> + +fdsaf(self) == /\ pc[self] = "fdsaf" + /\ IF compare(lo[self], mod(ei[self] + 1, K), table[mod(ej[self], K)], mod(ej[self], K)) <= -1 + THEN /\ table' = [table EXCEPT ![mod(ej[self] + 1, K)] = table[mod(ej[self], K)]] + /\ IF ej[self] = 0 + THEN /\ ej' = [ej EXCEPT ![self] = ej[self] - 1] + /\ pc' = [pc EXCEPT ![self] = "set"] + ELSE /\ ej' = [ej EXCEPT ![self] = ej[self] - 1] + /\ pc' = [pc EXCEPT ![self] = "fdsaf"] + ELSE /\ pc' = [pc EXCEPT ![self] = "set"] + /\ UNCHANGED << table, ej >> + /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, + history, stack, ei, lo, rfp, rindex, checked, + fp, index, result, expected >> + +set(self) == /\ pc[self] = "set" + /\ table' = [table EXCEPT ![mod(ej[self] + 1, K)] = lo[self]] + /\ ej' = [ej EXCEPT ![self] = ei[self] + 1] + /\ ei' = [ei EXCEPT ![self] = ei[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "strEv"] + /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, + history, stack, lo, rfp, rindex, checked, fp, + index, result, expected >> + +flush(self) == /\ pc[self] = "flush" + /\ IF ei[self] <= K+P + THEN /\ lo' = [lo EXCEPT ![self] = table[mod(ei[self], K)]] + /\ IF lo'[self] # empty /\ + lo'[self] > largestElem(newsecondary) /\ + ((ei[self] <= K /\ ~wrapped(lo'[self],ei[self])) \/ + (ei[self] > K /\ wrapped(lo'[self],ei[self]))) + THEN /\ newsecondary' = Append(newsecondary \o subSetSmaller(secondary, newsecondary, lo'[self]), lo'[self]) + /\ table' = [table EXCEPT ![mod(ei[self], K)] = lo'[self] * (-1)] + ELSE /\ TRUE + /\ UNCHANGED << table, newsecondary >> + /\ ei' = [ei EXCEPT ![self] = ei[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "flush"] + /\ UNCHANGED << secondary, P >> + ELSE /\ secondary' = newsecondary \o subSetLarger(secondary, newsecondary) + /\ newsecondary' = <<>> + /\ P' = 0 + /\ pc' = [pc EXCEPT ![self] = "rtrn"] + /\ UNCHANGED << table, ei, lo >> + /\ UNCHANGED << evict, waitCnt, history, stack, ej, rfp, rindex, + checked, fp, index, result, expected >> + +rtrn(self) == /\ pc[self] = "rtrn" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ ei' = [ei EXCEPT ![self] = Head(stack[self]).ei] + /\ ej' = [ej EXCEPT ![self] = Head(stack[self]).ej] + /\ lo' = [lo EXCEPT ![self] = Head(stack[self]).lo] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, rfp, rindex, checked, fp, + index, result, expected >> + +Evict(self) == strEv(self) \/ fdsaf(self) \/ set(self) \/ flush(self) + \/ rtrn(self) + +rwait(self) == /\ pc[self] = "rwait" + /\ history # {} + /\ pc' = [pc EXCEPT ![self] = "rpick"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +rpick(self) == /\ pc[self] = "rpick" + /\ IF checked[self] = history /\ history = fps + THEN /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << rfp, checked >> + ELSE /\ \E f \in history: + /\ rfp' = [rfp EXCEPT ![self] = f] + /\ checked' = [checked EXCEPT ![self] = checked[self] \cup {f}] + /\ pc' = [pc EXCEPT ![self] = "rcntns"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rindex, fp, + index, result, expected >> + +rcntns(self) == /\ pc[self] = "rcntns" + /\ rindex' = [rindex EXCEPT ![self] = 0] + /\ IF evict + THEN /\ waitCnt' = waitCnt + 1 + /\ pc' = [pc EXCEPT ![self] = "rwaitEv"] + ELSE /\ pc' = [pc EXCEPT ![self] = "ronPrm"] + /\ UNCHANGED waitCnt + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + history, stack, ei, ej, lo, rfp, checked, fp, + index, result, expected >> + +rwaitEv(self) == /\ pc[self] = "rwaitEv" + /\ evict = FALSE + /\ pc' = [pc EXCEPT ![self] = "rendWEv"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +rendWEv(self) == /\ pc[self] = "rendWEv" + /\ waitCnt' = waitCnt - 1 + /\ pc' = [pc EXCEPT ![self] = "rcntns"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +ronPrm(self) == /\ pc[self] = "ronPrm" + /\ IF rindex[self] <= P + THEN /\ IF isMatch(rfp[self], idx(rfp[self], rindex[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "rpick"] + /\ UNCHANGED rindex + ELSE /\ IF isEmpty(idx(rfp[self], rindex[self]), table) + \/ isMarked(idx(rfp[self], rindex[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "ronSnc"] + /\ UNCHANGED rindex + ELSE /\ rindex' = [rindex EXCEPT ![self] = rindex[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "ronPrm"] + ELSE /\ pc' = [pc EXCEPT ![self] = "ronSnc"] + /\ UNCHANGED rindex + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + checked, fp, index, result, expected >> + +ronSnc(self) == /\ pc[self] = "ronSnc" + /\ IF containsElem(secondary,rfp[self]) + THEN /\ pc' = [pc EXCEPT ![self] = "rpick"] + ELSE /\ Assert((FALSE), + "Failure of assertion at line 291, column 23.") + /\ pc' = [pc EXCEPT ![self] = "rpick"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +q(self) == rwait(self) \/ rpick(self) \/ rcntns(self) \/ rwaitEv(self) + \/ rendWEv(self) \/ ronPrm(self) \/ ronSnc(self) + +pick(self) == /\ pc[self] = "pick" + /\ IF (fps \ history) = {} + THEN /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ fp' = fp + ELSE /\ \E f \in (fps \ history): + fp' = [fp EXCEPT ![self] = f] + /\ pc' = [pc EXCEPT ![self] = "put"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, rindex, + checked, index, result, expected >> + +put(self) == /\ pc[self] = "put" + /\ index' = [index EXCEPT ![self] = 0] + /\ result' = [result EXCEPT ![self] = FALSE] + /\ expected' = [expected EXCEPT ![self] = -1] + /\ IF evict + THEN /\ waitCnt' = waitCnt + 1 + /\ pc' = [pc EXCEPT ![self] = "waitEv"] + ELSE /\ pc' = [pc EXCEPT ![self] = "chkSnc"] + /\ UNCHANGED waitCnt + /\ UNCHANGED << table, secondary, newsecondary, P, evict, history, + stack, ei, ej, lo, rfp, rindex, checked, fp >> + +waitEv(self) == /\ pc[self] = "waitEv" + /\ evict = FALSE + /\ pc' = [pc EXCEPT ![self] = "endWEv"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +endWEv(self) == /\ pc[self] = "endWEv" + /\ waitCnt' = waitCnt - 1 + /\ pc' = [pc EXCEPT ![self] = "put"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +chkSnc(self) == /\ pc[self] = "chkSnc" + /\ IF secondary # <<>> + THEN /\ pc' = [pc EXCEPT ![self] = "cntns"] + ELSE /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +cntns(self) == /\ pc[self] = "cntns" + /\ IF index[self] < P + THEN /\ IF isMatch(fp[self], idx(fp[self], index[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "pick"] + /\ index' = index + ELSE /\ IF isEmpty(idx(fp[self], index[self]), table) + \/ isMarked(idx(fp[self], index[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "onSnc"] + /\ index' = index + ELSE /\ index' = [index EXCEPT ![self] = index[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "cntns"] + ELSE /\ pc' = [pc EXCEPT ![self] = "onSnc"] + /\ index' = index + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, result, expected >> + +onSnc(self) == /\ pc[self] = "onSnc" + /\ IF containsElem(secondary,fp[self]) + THEN /\ pc' = [pc EXCEPT ![self] = "pick"] + /\ index' = index + ELSE /\ index' = [index EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, result, expected >> + +insrt(self) == /\ pc[self] = "insrt" + /\ IF index[self] < L + THEN /\ expected' = [expected EXCEPT ![self] = table[idx(fp[self],index[self])]] + /\ IF expected'[self] = empty \/ (expected'[self] < 0 /\ expected'[self] # (-1) * fp[self]) + THEN /\ pc' = [pc EXCEPT ![self] = "incP"] + ELSE /\ pc' = [pc EXCEPT ![self] = "isMth"] + ELSE /\ pc' = [pc EXCEPT ![self] = "tryEv"] + /\ UNCHANGED expected + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result >> + +isMth(self) == /\ pc[self] = "isMth" + /\ IF isMatch(fp[self],idx(fp[self],index[self]),table) + THEN /\ pc' = [pc EXCEPT ![self] = "pick"] + /\ index' = index + ELSE /\ index' = [index EXCEPT ![self] = index[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, result, expected >> + +incP(self) == /\ pc[self] = "incP" + /\ IF index[self] > P + THEN /\ P' = index[self] + ELSE /\ TRUE + /\ P' = P + /\ pc' = [pc EXCEPT ![self] = "cas"] + /\ UNCHANGED << table, secondary, newsecondary, evict, waitCnt, + history, stack, ei, ej, lo, rfp, rindex, checked, + fp, index, result, expected >> + +cas(self) == /\ pc[self] = "cas" + /\ IF table[(idx(fp[self],index[self]))] = expected[self] + THEN /\ table' = [table EXCEPT ![(idx(fp[self],index[self]))] = fp[self]] + /\ result' = [result EXCEPT ![self] = TRUE] + ELSE /\ result' = [result EXCEPT ![self] = FALSE] + /\ table' = table + /\ IF result'[self] + THEN /\ history' = (history \cup {fp[self]}) + /\ pc' = [pc EXCEPT ![self] = "pick"] + ELSE /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED history + /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, stack, + ei, ej, lo, rfp, rindex, checked, fp, index, + expected >> + +tryEv(self) == /\ pc[self] = "tryEv" + /\ IF evict = FALSE + THEN /\ evict' = TRUE + /\ pc' = [pc EXCEPT ![self] = "waitIns"] + ELSE /\ pc' = [pc EXCEPT ![self] = "put"] + /\ evict' = evict + /\ UNCHANGED << table, secondary, newsecondary, P, waitCnt, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +waitIns(self) == /\ pc[self] = "waitIns" + /\ waitCnt = Cardinality(Writer) - 1 + Cardinality(Reader) + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "Evict", + pc |-> "endEv", + ei |-> ei[self], + ej |-> ej[self], + lo |-> lo[self] ] >> + \o stack[self]] + /\ ei' = [ei EXCEPT ![self] = 1] + /\ ej' = [ej EXCEPT ![self] = 1] + /\ lo' = [lo EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "strEv"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, rfp, rindex, checked, fp, + index, result, expected >> + +endEv(self) == /\ pc[self] = "endEv" + /\ evict' = FALSE + /\ pc' = [pc EXCEPT ![self] = "put"] + /\ UNCHANGED << table, secondary, newsecondary, P, waitCnt, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +p(self) == pick(self) \/ put(self) \/ waitEv(self) \/ endWEv(self) + \/ chkSnc(self) \/ cntns(self) \/ onSnc(self) \/ insrt(self) + \/ isMth(self) \/ incP(self) \/ cas(self) \/ tryEv(self) + \/ waitIns(self) \/ endEv(self) + +Next == (\E self \in ProcSet: Evict(self)) + \/ (\E self \in Reader: q(self)) + \/ (\E self \in Writer: p(self)) + \/ (* Disjunct to prevent deadlock on termination *) + ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + /\ \A self \in Writer : WF_vars(p(self)) /\ WF_vars(Evict(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION + +(***************************************************************************) +(* A type correctness invariant for outer, inner and P. *) +(***************************************************************************) +TypeOK == /\ P \in Nat + /\ P <= L + +(***************************************************************************) +(* All fingerprint in history are (always) hash table members, *) +(* all fps \ history never are. *) +(***************************************************************************) +Inv == evict = FALSE => /\ \A seen \in history: + contains(seen,table,secondary,P+1) + /\ \A unseen \in (fps \ history): + ~contains(unseen,table,secondary,P+1) + +(***************************************************************************) +(* FALSE iff table contains duplicate elements (excluding empty). *) +(***************************************************************************) +Duplicates == evict = FALSE => LET sub == SelectSeq(table, LAMBDA e: e # empty) + IN IF Len(sub) < 2 THEN TRUE + ELSE \A i \in 1..(Len(sub) - 1): + /\ sub[i] # sub[i+1] + /\ sub[i] # (-1) * sub[i+1] + +(***************************************************************************) +(* Secondary storage is always sorted in ascending order. *) +(***************************************************************************) +Sorted == isSorted(secondary) /\ isSorted(newsecondary) + +(***************************************************************************) +(* TRUE iff f is found in table within idx(f,0)..id(f,r). *) +(***************************************************************************) +containedInTable(f) == \E r \in 0..P+1: IF f < 0 + THEN table[idx((-1)*f, r)] = f + ELSE table[idx(f, r)] = f + +(***************************************************************************) +(* TRUE iff f is found in secondary. *) +(***************************************************************************) +containedInSecondary(f) == containsElem(secondary,f) + +(***************************************************************************) +(* TRUE iff f is found in newsecondary. *) +(***************************************************************************) +containedInNewSecondary(f) == containsElem(newsecondary,f) + +(***************************************************************************) +(* TRUE iff fingerprints correctly transition from table to newsecondary *) +(* to secondary. *) +(***************************************************************************) +Consistent == evict = FALSE => \A seen \in history: + /\ (containedInTable(seen) => ~containedInSecondary(seen)) + /\ (containedInTable(seen) => ~containedInNewSecondary(seen)) + /\ (~containedInTable(seen) => (containedInSecondary(seen)) \/ + containedInNewSecondary(seen)) + /\ (containedInTable(seen * (-1)) => (containedInSecondary(seen)) \/ + containedInNewSecondary(seen)) +\* Universal quantification and leads-to not supported by TLC +\* /\ (containedInTable((-1)*seen) ~> containedInSecondary(seen)) + +(***************************************************************************) +(* TRUE iff the maximum displacement of all elements (without empty) is *) +(* equal to P. In other words, P is always minimal. *) +(***************************************************************************) +Minimal == \A i \in 1..Len(table): IF table[i] = empty + THEN TRUE + ELSE LET f == IF table[i] < 0 + THEN -1 * table[i] + ELSE table[i] + IN (i - idx(f, 0)) <= P + +(***************************************************************************) +(* P has to reach the limit L in some behaviors. *) +(***************************************************************************) +Maxed == <>(P = L) + +(***************************************************************************) +(* Under all behaviors, the algorithm makes progress. *) +(***************************************************************************) +Prop == <>[](history = fps) +============================================================================= +\end{ppcal} diff --git a/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java b/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java index b5c54f5871671fac520b04fb33b6bbb1395ac3d7..afd11858d68d50233b33d66cddf4339bb752d522 100644 --- a/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java +++ b/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java @@ -51,7 +51,7 @@ public class AddAndCheckLiveCheck extends LiveCheck { public AddAndCheckLiveCheck(Tool tool, Action[] actions, String metadir, IBucketStatistics stats) throws IOException { super(tool, actions, metadir, stats); - MP.printWarning(EC.UNIT_TEST, "!!!WARNING: TLC is running in inefficient unit testing mode!!!"); + MP.printWarning(EC.UNIT_TEST, new String[]{ "!!!WARNING: TLC is running in inefficient unit testing mode!!!", ""} ); } /* (non-Javadoc) diff --git a/tlatools/src/tlc2/tool/liveness/LiveCheck.java b/tlatools/src/tlc2/tool/liveness/LiveCheck.java index 85589a55002ba896882785602af197b2aa35e195..773fc5564c3adc2aeb9a5decf21df0a7bcd758f2 100644 --- a/tlatools/src/tlc2/tool/liveness/LiveCheck.java +++ b/tlatools/src/tlc2/tool/liveness/LiveCheck.java @@ -151,7 +151,7 @@ public class LiveCheck implements ILiveCheck { if (forceCheck) { return check0(false); } - if (Boolean.getBoolean(Liveness.class.getName() + ".finalCheckOnly")) { + if (!TLCGlobals.doLiveness()) { // The user requested to only check liveness once, on the complete // state graph. return true; diff --git a/tlatools/src/tlc2/tool/liveness/LiveWorker.java b/tlatools/src/tlc2/tool/liveness/LiveWorker.java index 59e65365db6265224fc3d18e094b38d0d3063d46..2f12432af3f99eb6a8344a073aa9407ba35ed5eb 100644 --- a/tlatools/src/tlc2/tool/liveness/LiveWorker.java +++ b/tlatools/src/tlc2/tool/liveness/LiveWorker.java @@ -492,9 +492,9 @@ public class LiveWorker extends IdThread { * contains a counterexample, and this method then calls printErrorTrace to * print an error trace and returns false. * <p> - * Speaking in words of Manna & Pnueli (Page 422ff), it checks if ¬φ + * Speaking in words of Manna & Pnueli (Page 422ff), it checks if ~φ * (which is PEM) is "P-satisfiable" (i.e. is there a computation that - * satisfies ψ). ¬φ (called ψ by MP) is the negation of the + * satisfies ψ). ~φ (called ψ by MP) is the negation of the * liveness formula φ which has to be "P-valid" for the liveness * properties to be valid. * @throws ExecutionException @@ -599,7 +599,7 @@ public class LiveWorker extends IdThread { for (int i = 0; i < aeslen; i++) { // Only ever set AEStateRes[i] to true, but never to false // once it was true. It only matters if one state in com - // satisfies PEM's liveness property due to []<>¬p (which is + // satisfies PEM's liveness property due to []<>~p (which is // the inversion of <>[]p). // // It obviously has to check all nodes in the component diff --git a/tlatools/src/tlc2/tool/queue/MemStateQueue.java b/tlatools/src/tlc2/tool/queue/MemStateQueue.java index 938288bcac6f647970d6d7fcc25bb656b7a68ce2..fe46f15df54413f6770be8d7ac00b0489fae5628 100644 --- a/tlatools/src/tlc2/tool/queue/MemStateQueue.java +++ b/tlatools/src/tlc2/tool/queue/MemStateQueue.java @@ -31,7 +31,7 @@ public final class MemStateQueue extends StateQueue { final void enqueueInner(TLCState state) { if (this.len > Integer.MAX_VALUE) { - Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, "Amount of states exceeds internal storage"); + Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, new String[]{"queue", "Amount of states exceeds internal storage"}); } if (this.len == this.states.length) { // grow the array diff --git a/tlatools/src/tlc2/util/BufferedRandomAccessFile.java b/tlatools/src/tlc2/util/BufferedRandomAccessFile.java index 7187bd7c67fbb4bd66328a1d7484d8b10e31c91f..d768f360902452fb8755da24590df6fdf53fccaa 100644 --- a/tlatools/src/tlc2/util/BufferedRandomAccessFile.java +++ b/tlatools/src/tlc2/util/BufferedRandomAccessFile.java @@ -37,8 +37,9 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { private long maxHi; // this.lo + this.buff.length private boolean hitEOF; // buffer contains last file block? private long diskPos; // disk position + private long mark; - private static Object mu = new Object(); // protects the following fields + private static final Object mu = new Object(); // protects the following fields private static byte[][] availBuffs = new byte[100][]; private static int numAvailBuffs = 0; @@ -175,7 +176,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { } /* Flush any dirty bytes in the buffer to disk. */ - private void flushBuffer() throws IOException { + private boolean flushBuffer() throws IOException { if (this.dirty) { // Assert.check(this.curr > this.lo); if (this.diskPos != this.lo) super.seek(this.lo); @@ -183,7 +184,9 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { super.write(this.buff, 0, len); this.diskPos = this.curr; this.dirty = false; + return true; } + return false; } /* Read at most "this.buff.length" bytes into "this.buff", @@ -248,7 +251,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { // seeking inside current buffer -- no read required if (pos < this.curr) { // if seeking backwards, we must flush to maintain V4 - this.flushBuffer(); + pageReadNeeded = this.flushBuffer(); } else { pageReadNeeded = false; } @@ -442,6 +445,21 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { setLength(0); this.init(); } + + public long getMark() { + return this.mark; + } + + public long mark() { + final long oldMark = this.mark; + this.mark = getFilePointer(); + return oldMark; + } + + public void seekAndMark(long pos) throws IOException { + this.mark = pos; + this.seek(pos); + } public static void main(String[] args) throws IOException { String name = "xxx"; diff --git a/tlatools/src/tlc2/util/Context.java b/tlatools/src/tlc2/util/Context.java index 65a03219eb8417dd838b6f0a1e83b268309f23bf..a3bf18284b112f9895972cf8fd2388f144f44fe8 100644 --- a/tlatools/src/tlc2/util/Context.java +++ b/tlatools/src/tlc2/util/Context.java @@ -7,6 +7,22 @@ package tlc2.util; import tla2sany.semantic.SymbolNode; +// Context is used two times: +// 1) To determine the level boundedness of the expression appearing in the spec +// (see Tool#getLevelBounds*). This is done once as part of parsing. +// 2) To store the variable/name binding at each scope during state exploration. +// The hierarchy of levels/scopes is represented as a chain of context instances +// (link-list or associative list). The Empty context is the outer most scope +// (first level). +// 1) is not relevant performance-wise, but 2) has - provided non-trivial level - +// a significant performance impact because of excessive lookups. What makes +// optimizations difficult, is the fact that the context chain is recreated each +// time a state is generated, as part of the next state relation. An optimization +// thus has to ensure that it doesn't trade fast - ideally constant - lookups for +// an equally expensive creation complexity. +// +// The contrived spec at the bottom exhibits this problem. Increasing the level, +// the number of lookups go through the roof. public final class Context { /** * A link list of name and value pairs. When adding <name, value> to the @@ -17,8 +33,12 @@ public final class Context { private final Object value; private final Context next; + public int CNT = 1; + + public final static Context Empty = new Context(null, null, null); - public final static Context BaseBranch = new Context(null, null, Empty); + + private final static Context BaseBranch = new Context(null, null, Empty); private Context(SymbolNode name, Object value, final Context next) { this.name = name; @@ -26,6 +46,9 @@ public final class Context { this.next = next; } + // This method is only called within the context of the ENABLED (temporal) + // operator. A branching context is specially handled during lookup below + // if cutoff is true. public static Context branch(Context base) { if (base == Empty) { // Avoid new instance if the next context in the chain is the Empty @@ -120,3 +143,43 @@ public final class Context { return sb.toString(); } } +/* +----------------------------- MODULE Scoping ----------------------------- +EXTENDS Naturals +CONSTANT Limit +VARIABLE var + +A(a) == TRUE +B(b) == A(b) +C(c) == B(c) +D(d) == C(d) +E(e) == D(e) +F(f) == E(f) +G(g) == F(g) +H(h) == G(h) +I(i) == H(i) +J(j) == I(j) +K(k) == J(k) +L(l) == K(l) +M(m) == L(m) +N(n) == M(n) +O(o) == N(o) +P(p) == O(p) +Q(q) == P(q) +R(r) == Q(r) +S(s) == R(s) +T(t) == S(t) +U(u) == T(u) +V(v) == U(v) +W(w) == V(w) +X(x) == W(x) +Y(y) == X(y) +Z(z) == U(z) + +Next == /\ var' = var + 1 + /\ var < 1 + /\ Z(1) + +Spec == var=0 /\ [][Next]_<<var>> +============================================================================= +*/ diff --git a/tlatools/src/tlc2/util/DotStateWriter.java b/tlatools/src/tlc2/util/DotStateWriter.java index beb9c42226e980bebb8c0cf3437d4ea1535ad71c..92ce992ea0f6dd2d571114253b97b54453a4fa11 100644 --- a/tlatools/src/tlc2/util/DotStateWriter.java +++ b/tlatools/src/tlc2/util/DotStateWriter.java @@ -44,7 +44,8 @@ public class DotStateWriter extends StateWriter { public DotStateWriter(final String fname, final String strict) throws IOException { super(fname); this.writer.append(strict + "digraph DiskGraph {\n"); // strict removes redundant edges - this.writer.append("rankdir=LR;\n"); // Left to right rather than top to bottom + // Turned off LR because top to bottom provides better results with GraphViz viewer. +// this.writer.append("rankdir=LR;\n"); // Left to right rather than top to bottom this.writer.flush(); } diff --git a/tlatools/src/tlc2/util/IdThread.java b/tlatools/src/tlc2/util/IdThread.java index 140d2052e3f4b3216931c235a252fd179741e861..1941cc10a1310b6ae7ec7baf73020d49fe9c0689 100644 --- a/tlatools/src/tlc2/util/IdThread.java +++ b/tlatools/src/tlc2/util/IdThread.java @@ -16,7 +16,12 @@ public class IdThread extends Thread { this.id = id; } - /** Return this thread's ID. */ + public IdThread(Runnable runnable, String name, int id) { + super(runnable, name); + this.id = id; + } + + /** Return this thread's ID. */ // This method was originally called getId, but a later // version of Java introduced a getId method into the Thread // class which returned a long, and it doesn't allow it to be diff --git a/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java b/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..bdea80b92ec7b0c3c954fb09f79b9bc2859c9f81 --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.NavigableMap; + +import javax.management.NotCompliantMBeanException; + +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.management.TLCStandardMBean; +import tlc2.util.statistics.management.BucketStatisticsMXWrapper; + +public abstract class AbstractBucketStatistics implements IBucketStatistics { + + /** + * Human readable statistics name (used in toString()) + */ + protected final String title; + + public AbstractBucketStatistics(String aTitle) { + super(); + this.title = aTitle; + } + + /** + * @param aTitle + * A title for console pretty printing + * @param pkg + * A package name for this statistics, e.g. tlc2.tool.liveness + * for stats are from classes in the liveness package. + * @param name + * The (class) name of the source of the statistics. + */ + public AbstractBucketStatistics(final String aTitle, final String pkg, final String name) { + this(aTitle); + try { + //TODO unregister somehow + new BucketStatisticsMXWrapper(this, name, pkg); + } catch (NotCompliantMBeanException e) { + // not expected to happen would cause JMX to be broken, hence just log and + // continue + MP.printWarning( + EC.GENERAL, + "Failed to create MBean wrapper for BucketStatistics. No statistics/metrics will be avaiable.", + e); + TLCStandardMBean.getNullTLCStandardMBean(); + } + } + + public String toString() { + final StringBuffer buf = new StringBuffer(); + buf.append("============================%n"); + buf.append("=" + title + "=%n"); + buf.append("============================%n"); + buf.append(String.format("Observations: %d%n", getObservations())); + buf.append(String.format("Min: %d%n", getMin())); + buf.append(String.format("Max: %d%n", getMax())); + buf.append(String.format("Mean: %.2f%n", getMean())); + buf.append(String.format("Median: %d%n", getMedian())); + buf.append(String.format("Standard deviation: %.2f%n", getStdDev())); + buf.append(String.format("75%%: %.2f%n", getPercentile(0.75d))); + buf.append(String.format("95%%: %.2f%n", getPercentile(0.95d))); + buf.append(String.format("98%%: %.2f%n", getPercentile(0.98d))); + buf.append(String.format("99%%: %.2f%n", getPercentile(0.99d))); + buf.append(String.format("99.9%%: %.2f%n", getPercentile(0.999d))); + buf.append("numEdges/occurrences (log scale)%n"); + buf.append("--------------------------------%n"); + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + Entry<Integer, Long> next = iterator.next(); + long amount = next.getValue(); + int i = next.getKey(); + buf.append(String.format("%02d", i)); + buf.append(":"); + buf.append(String.format("%02d", amount)); + buf.append(" "); + for (int j = 0; j < Math.log(amount); j++) { + buf.append("#"); + } + buf.append("%n"); + } + buf.append("============================"); + return buf.toString(); + } + + public int getMedian() { + long l = getObservations(); + if (l <= 0) { + return -1; + } + // skip forward for as many elements as 1/2 observations. The + // corresponding bucket is the median. + long sum = 0L; + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + final Entry<Integer, Long> next = iterator.next(); + sum += next.getValue(); + if (sum > (l / 2)) { + return next.getKey(); + } + } + // make compiler happy + throw new RuntimeException("bug, shoud not get here"); + } + + public double getMean() { + long sum = 0L; + // Sum up values and count + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + final Entry<Integer, Long> next = iterator.next(); + final long value = next.getValue(); + final int i = next.getKey(); + sum += value * i; + } + if (getObservations() > 0) { + return (sum / (getObservations() * 1.0d)); + } else { + // No mean yet + return -1; + } + } + + public int getMin() { + if (getObservations() <= 0) { + return -1; + } + return getSamples().firstKey(); + } + + public int getMax() { + if (getObservations() <= 0) { + return -1; + } + return getSamples().lastKey(); + } + + public double getStdDev() { + final long N = getObservations(); + if (N <= 0) { + return -1.0d; + } + final double mean = getMean() * 1.0d; + double sum = 0.0d; + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + Entry<Integer, Long> next = iterator.next(); + double Xi = next.getKey() * 1.0d; + double diff = Xi - mean; + sum += (diff * diff) * ((next.getValue() * 1.0d)); // diff^2 + } + double variance = sum / (N * 1.0d); + double stdDev = Math.sqrt(variance); + return stdDev; + } + + public double getPercentile(double quantile) { + if (Double.isNaN(quantile)) { + throw new IllegalArgumentException("NaN"); + } + final long obsv = getObservations(); + if (obsv <= 0) { + return -1.0; + } + // adjust values to valid range + quantile = Math.min(1.0, quantile); + quantile = Math.max(0, quantile); + + final NavigableMap<Integer, Long> samples = getSamples(); + + // calculate the elements position for the + // given quantile + final int pos = (int) ((obsv * 1.0d) * quantile); + if (pos > obsv) { + return samples.size(); + } + if (pos < 0) { + return 0; + } + + // advance to the bucket at position + long cnt = 0l; + final Iterator<Entry<Integer, Long>> iterator = samples.entrySet().iterator(); + while(iterator.hasNext()) { + Entry<Integer, Long> next = iterator.next(); + int i = next.getKey(); + cnt += next.getValue(); + if (cnt > pos) { + return i; + } + } + return quantile; + } + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/util/statistics/BucketStatistics.java b/tlatools/src/tlc2/util/statistics/BucketStatistics.java index 0b7ebc76dcef85be2bad5ee284015f26284a1a72..049930161ef1a2620388570966d9b8b5e33c21fd 100644 --- a/tlatools/src/tlc2/util/statistics/BucketStatistics.java +++ b/tlatools/src/tlc2/util/statistics/BucketStatistics.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Microsoft Research. All rights reserved. + * Copyright (c) 2017 Microsoft Research. All rights reserved. * * The MIT License (MIT) * @@ -23,29 +23,21 @@ * Contributors: * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ - package tlc2.util.statistics; -import java.util.Iterator; +import java.util.HashMap; +import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.atomic.AtomicLong; - -import javax.management.NotCompliantMBeanException; +import java.util.NavigableMap; +import java.util.TreeMap; -import tlc2.output.EC; -import tlc2.output.MP; -import tlc2.tool.management.TLCStandardMBean; -import tlc2.util.statistics.management.BucketStatisticsMXWrapper; +public class BucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { -public class BucketStatistics implements IBucketStatistics { - /** * The amount of samples seen by this statistics. It's identical * to the sum of the value of all buckets. */ - protected final AtomicLong observations = new AtomicLong(0); + private long observations; /** * Instead of using an ever-growing list of samples, identical @@ -54,270 +46,66 @@ public class BucketStatistics implements IBucketStatistics { * seen two times. * The map is thread safe, so are the values. */ - protected final ConcurrentNavigableMap<Integer, AtomicLong> buckets = new ConcurrentSkipListMap<Integer, AtomicLong>(); - - /** - * Human readable statistics name (used in toString()) - */ - private final String title; - - /** - * Upper limit for the number of buckets available for sampling. If a sample - * exceeds maximum, it will go into the very last bucket. It serves as an - * "overflow" bucket. - * Useful if samples flatten out to the right and at many buckets with just - * a single sample would be added. - */ - private final int maximum; + private final Map<Integer, Long> buckets; - // Used for unit testing only - BucketStatistics() { - this("Historgram"); - } - - /** - * @see {@link BucketStatistics#BucketStatistics(String, int)} - */ - public BucketStatistics(final String aTitle) { - this(aTitle, Integer.MAX_VALUE); - } - - /** - * @see {@link BucketStatistics#BucketStatistics(String, int, String, String)} - */ - public BucketStatistics(final String aTitle, final String pkg, final String name) { - this(aTitle, Integer.MAX_VALUE, pkg, name); - } - - /** - * @param aTitle - * A title for console pretty printing - * @param aMaxmimum - * see {@link BucketStatistics#maximum} - */ - public BucketStatistics(final String aTitle, final int aMaxmimum) { - this.title = aTitle; - this.maximum = aMaxmimum; + public BucketStatistics(String aTitle) { + super(aTitle); + this.buckets = new HashMap<Integer, Long>(); } - /** - * @param aTitle - * A title for console pretty printing - * @param aMaxmimum - * see {@link BucketStatistics#maximum} - * @param pkg - * A package name for this statistics, e.g. tlc2.tool.liveness - * for stats are from classes in the liveness package. - * @param name - * The (class) name of the source of the statistics. - */ - public BucketStatistics(final String aTitle, final int aMaxmimum, final String pkg, final String name) { - this(aTitle, aMaxmimum); - - TLCStandardMBean graphStatsMXWrapper; - try { - //TODO unregister somehow - graphStatsMXWrapper = new BucketStatisticsMXWrapper(this, name, pkg); - } catch (NotCompliantMBeanException e) { - // not expected to happen - // would cause JMX to be broken, hence just log and continue - MP.printWarning( - EC.GENERAL, - "Failed to create MBean wrapper for BucketStatistics. No statistics/metrics will be avaiable.", - e); - graphStatsMXWrapper = TLCStandardMBean.getNullTLCStandardMBean(); - } + public BucketStatistics(String aTitle, final String pkg, final String name) { + super(aTitle, pkg, name); + this.buckets = new HashMap<Integer, Long>(); } - + /* (non-Javadoc) * @see tlc2.util.statistics.IBucketStatistics#addSample(int) */ - public void addSample(final int amount) { + public void addSample(int amount) { if (amount < 0) { throw new IllegalArgumentException("Negative amount invalid"); } - // If the amount exceeds the fixed maximum, increment the overflow - // bucket. The overflow bucket is the very last bucket. - final int idx = Math.min(maximum, amount); - - final AtomicLong atomicLong = buckets.get(idx); - if(atomicLong == null) { - buckets.putIfAbsent(idx, new AtomicLong(1)); + Long l = buckets.get(amount); + if(l == null) { + buckets.put(amount, 1L); } else { - atomicLong.incrementAndGet(); + buckets.replace(amount, ++l); } - observations.getAndIncrement(); - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getObservations() - */ - public long getObservations() { - return observations.get(); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - final StringBuffer buf = new StringBuffer(); - buf.append("============================%n"); - buf.append("=" + title + "=%n"); - buf.append("============================%n"); - buf.append(String.format("Observations: %d%n", observations.get())); - buf.append(String.format("Min: %d%n", getMin())); - buf.append(String.format("Max: %d%n", getMax())); - buf.append(String.format("Mean: %.2f%n", getMean())); - buf.append(String.format("Median: %d%n", getMedian())); - buf.append(String.format("Standard deviation: %.2f%n", getStdDev())); - buf.append(String.format("75%%: %.2f%n", getPercentile(0.75d))); - buf.append(String.format("95%%: %.2f%n", getPercentile(0.95d))); - buf.append(String.format("98%%: %.2f%n", getPercentile(0.98d))); - buf.append(String.format("99%%: %.2f%n", getPercentile(0.99d))); - buf.append(String.format("99.9%%: %.2f%n", getPercentile(0.999d))); - buf.append("numEdges/occurrences (log scale)%n"); - buf.append("--------------------------------%n"); - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - Entry<Integer, AtomicLong> next = iterator.next(); - long amount = next.getValue().get(); - int i = next.getKey(); - buf.append(String.format("%02d", i)); - buf.append(":"); - buf.append(String.format("%02d", amount)); - buf.append(" "); - for (int j = 0; j < Math.log(amount); j++) { - buf.append("#"); - } - buf.append("%n"); - } - buf.append("============================"); - return buf.toString(); - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMedian() - */ - public int getMedian() { - long l = observations.get(); - if (l <= 0) { - return -1; - } - // skip forward for as many elements as 1/2 observations. The - // corresponding bucket is the median. - long sum = 0L; - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - final Entry<Integer, AtomicLong> next = iterator.next(); - sum += next.getValue().get(); - if (sum > (l / 2)) { - return next.getKey(); - } - } - // make compiler happy - throw new RuntimeException("bug, shoud not get here"); - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMean() - */ - public double getMean() { - long sum = 0L; - // Sum up values and count - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - final Entry<Integer, AtomicLong> next = iterator.next(); - final long value = next.getValue().get(); - final int i = next.getKey(); - sum += value * i; - } - if (observations.get() > 0) { - return (sum / (observations.get() * 1.0d)); - } else { - // No mean yet - return -1; - } - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMin() - */ - public int getMin() { - if (observations.get() <= 0) { - return -1; - } - return buckets.firstKey(); + observations++; } /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMax() + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() */ - public int getMax() { - if (observations.get() <= 0) { - return -1; - } - return buckets.lastKey(); + public long getObservations() { + return observations; } /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getStdDev() + * @see tlc2.util.statistics.IBucketStatistics#getSamples() */ - public double getStdDev() { - final long N = observations.get(); - if (N <= 0) { - return -1.0d; - } - final double mean = getMean() * 1.0d; - double sum = 0.0d; - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - Entry<Integer, AtomicLong> next = iterator.next(); - double Xi = next.getKey() * 1.0d; - double diff = Xi - mean; - sum += (diff * diff) * ((next.getValue().get() * 1.0d)); // diff² + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (Entry<Integer, Long> entry : this.buckets.entrySet()) { + res.put(entry.getKey(), entry.getValue()); } - double variance = sum / (N * 1.0d); - double stdDev = Math.sqrt(variance); - return stdDev; + return res; } - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getPercentile(double) + /** + * Adds the observations of <code>stat</code> to this {@link BucketStatistics}. */ - public double getPercentile(double quantile) { - if (Double.isNaN(quantile)) { - throw new IllegalArgumentException("NaN"); - } - final long obsv = observations.get(); - if (obsv <= 0) { - return -1.0; - } - // adjust values to valid range - quantile = Math.min(1.0, quantile); - quantile = Math.max(0, quantile); + public void add(final IBucketStatistics stat) { + this.observations += stat.getObservations(); - // calculate the elements position for the - // given quantile - final int pos = (int) ((obsv * 1.0d) * quantile); - if (pos > obsv) { - return buckets.size(); - } - if (pos < 0) { - return 0; - } - - // advance to the bucket at position - long cnt = 0l; - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - Entry<Integer, AtomicLong> next = iterator.next(); - int i = next.getKey(); - cnt += next.getValue().get(); - if (cnt > pos) { - return i; + for (Entry<Integer, Long> entry : stat.getSamples().entrySet()) { + final Long l = this.buckets.get(entry.getKey()); + if (l == null) { + this.buckets.put(entry.getKey(), entry.getValue()); + } else { + this.buckets.replace(entry.getKey(), l + entry.getValue()); } } - return quantile; } } diff --git a/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java b/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..a69e0eb765a38e21eeb8f83309ed0800322fa13c --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAdder; + +public class ConcurrentBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { + + /** + * The amount of samples seen by this statistics. It's identical + * to the sum of the value of all buckets. + */ + private final LongAdder observations = new LongAdder(); + + /** + * Instead of using an ever-growing list of samples, identical + * samples are counted in a bucket. E.g. the sample 5 is stored + * in a bucket with key 5 and a value of 2 if the sample has been + * seen two times. + * The map is thread safe, so are the values. + */ + private final ConcurrentNavigableMap<Integer, AtomicLong> buckets = new ConcurrentSkipListMap<Integer, AtomicLong>(); + + ConcurrentBucketStatistics() { + super("Concurrent Historgram"); + } + + /** + * @see {@link BucketStatistics#BucketStatistics(String, int)} + */ + public ConcurrentBucketStatistics(final String aTitle) { + super(aTitle); + } + + /** + * @see {@link BucketStatistics#BucketStatistics(String, int, String, String)} + */ + public ConcurrentBucketStatistics(final String aTitle, final String pkg, final String name) { + super(aTitle, pkg, name); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSample(int) + */ + public void addSample(final int amount) { + if (amount < 0) { + throw new IllegalArgumentException("Negative amount invalid"); + } + + final AtomicLong atomicLong = buckets.get(amount); + if(atomicLong == null) { + buckets.putIfAbsent(amount, new AtomicLong(1)); + } else { + atomicLong.incrementAndGet(); + } + observations.increment(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() + */ + public long getObservations() { + return observations.sum(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (Entry<Integer, AtomicLong> entry : buckets.entrySet()) { + res.put(entry.getKey(), entry.getValue().get()); + } + return res; + } +} diff --git a/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java b/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java index ddecbf410538c29ef6cbfa4cc85e5eb9ea4469e8..72880929f53ffbc9db5ba8ea8d3d9f5506ae670d 100644 --- a/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java +++ b/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java @@ -26,6 +26,9 @@ package tlc2.util.statistics; +import java.util.NavigableMap; +import java.util.TreeMap; + /** * Doesn't really do a thing. Can be instantiated multiple times contrary to * BucketStatistics which registers itself via JMX. @@ -87,4 +90,17 @@ public class DummyBucketStatistics implements IBucketStatistics { public double getPercentile(double quantile) { return 0; } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + return new TreeMap<Integer, Long>(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSamples(tlc2.util.statistics.IBucketStatistics) + */ + public void addSamples(IBucketStatistics stat) { + } } diff --git a/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java b/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..c1743379e705b7c87f39a0d29df682a76115a128 --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.NavigableMap; +import java.util.TreeMap; + +public class FixedSizedBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { + + /** + * The amount of samples seen by this statistics. It's identical + * to the sum of the value of all buckets. + */ + private long observations; + + /** + * Instead of using an ever-growing list of samples, identical + * samples are counted in a bucket. E.g. the sample 5 is stored + * in a bucket with key 5 and a value of 2 if the sample has been + * seen two times. + * The map is thread safe, so are the values. + */ + private final long[] buckets; + + /** + * @param aTitle + * A title for console pretty printing + * @param aMaxmimum + * see {@link BucketStatistics#maximum} + */ + public FixedSizedBucketStatistics(final String aTitle, final int aMaxmimum) { + super(aTitle); + this.buckets = new long[aMaxmimum]; + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSample(int) + */ + public void addSample(int amount) { + if (amount < 0) { + throw new IllegalArgumentException("Negative amount invalid"); + } + + // If the amount exceeds the fixed maximum, increment the overflow + // bucket. The overflow bucket is the very last bucket. + final int idx = Math.min(this.buckets.length - 1, amount); + + this.buckets[idx]++; + this.observations++; + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() + */ + public long getObservations() { + return observations; + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (int i = 0; i < this.buckets.length; i++) { + long value = this.buckets[i]; + if (value > 0) { + res.put(i, value); + } + } + return res; + } +} diff --git a/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java b/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..c6da9197785319d207a01792246e3087d8307326 --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.LongAdder; + +public class FixedSizedConcurrentBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { + + /** + * The amount of samples seen by this statistics. It's identical + * to the sum of the value of all buckets. + */ + private final LongAdder observations = new LongAdder(); + + /** + * Instead of using an ever-growing list of samples, identical + * samples are counted in a bucket. E.g. the sample 5 is stored + * in a bucket with key 5 and a value of 2 if the sample has been + * seen two times. + * The map is thread safe, so are the values. + */ + protected final AtomicLongArray buckets; + + /** + * @param aTitle + * A title for console pretty printing + * @param aMaxmimum + * see {@link BucketStatistics#maximum} + */ + public FixedSizedConcurrentBucketStatistics(final String aTitle, final int aMaxmimum) { + super(aTitle); + this.buckets = new AtomicLongArray(aMaxmimum); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSample(int) + */ + public void addSample(final int amount) { + if (amount < 0) { + throw new IllegalArgumentException("Negative amount invalid"); + } + + // If the amount exceeds the fixed maximum, increment the overflow + // bucket. The overflow bucket is the very last bucket. + final int idx = Math.min(this.buckets.length() - 1, amount); + + this.buckets.incrementAndGet(idx); + this.observations.increment(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() + */ + public long getObservations() { + return observations.sum(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (int i = 0; i < this.buckets.length(); i++) { + long value = this.buckets.get(i); + if (value > 0) { + res.put(i, value); + } + } + return res; + } +} diff --git a/tlatools/src/tlc2/util/statistics/IBucketStatistics.java b/tlatools/src/tlc2/util/statistics/IBucketStatistics.java index d00cd9c237c441d01e5a9b372902ddd03c98cced..490dcecda8235ad32a5a3f8df834bb4651b00957 100644 --- a/tlatools/src/tlc2/util/statistics/IBucketStatistics.java +++ b/tlatools/src/tlc2/util/statistics/IBucketStatistics.java @@ -1,5 +1,7 @@ package tlc2.util.statistics; +import java.util.NavigableMap; + /** * Keeps statistics about any samples added. */ @@ -48,4 +50,5 @@ public interface IBucketStatistics { */ double getPercentile(double quantile); + NavigableMap<Integer, Long> getSamples(); } \ No newline at end of file diff --git a/tlatools/src/tlc2/value/BoolValue.java b/tlatools/src/tlc2/value/BoolValue.java index 3df9f9784c6f1e12077d6a69efe0c38bcd347b7a..77988ab3287e53534610625a574c2991f5c5a416 100644 --- a/tlatools/src/tlc2/value/BoolValue.java +++ b/tlatools/src/tlc2/value/BoolValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:01:16 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:01:16 PST by lamport // modified on Fri Aug 10 15:07:07 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.util.FP64; import util.Assert; @@ -17,61 +20,103 @@ public class BoolValue extends Value { public final byte getKind() { return BOOLVALUE; } public final int compareTo(Object obj) { - if (obj instanceof BoolValue) { - int x = this.val ? 1 : 0; - int y = ((BoolValue)obj).val ? 1 : 0; - return x - y; + try { + if (obj instanceof BoolValue) { + int x = this.val ? 1 : 0; + int y = ((BoolValue)obj).val ? 1 : 0; + return x - y; + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare boolean " + ppr(this.toString()) + + " with non-boolean:\n" + ppr(obj.toString())); + } + return 1; } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare boolean " + ppr(this.toString()) + - " with non-boolean:\n" + ppr(obj.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return 1; } public final boolean equals(Object obj) { - if (obj instanceof BoolValue) { - return this.val == ((BoolValue)obj).val; + try { + if (obj instanceof BoolValue) { + return this.val == ((BoolValue)obj).val; + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare equality of boolean " + ppr(this.toString()) + + " with non-boolean:\n" + ppr(obj.toString())); + } + return ((ModelValue) obj).modelValueEquals(this) ; } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare equality of boolean " + ppr(this.toString()) + - " with non-boolean:\n" + ppr(obj.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ((ModelValue) obj).modelValueEquals(this) ; } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the boolean " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of the boolean " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the boolean " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the boolean " + ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the boolean " + - ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the boolean " + + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the boolean " + - ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the boolean " + + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - Assert.fail("Attempted to compute the number of elements in the boolean " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the boolean " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { return true; } @@ -83,22 +128,40 @@ public class BoolValue extends Value { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return ((val instanceof BoolValue) && - this.val == ((BoolValue)val).val); + try { + return ((val instanceof BoolValue) && + this.val == ((BoolValue)val).val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint method */ public final long fingerPrint(long fp) { - fp = FP64.Extend(fp, BOOLVALUE) ; - fp = FP64.Extend(fp, (this.val) ? 't' : 'f') ; - return fp ; + try { + fp = FP64.Extend(fp, BOOLVALUE) ; + fp = FP64.Extend(fp, (this.val) ? 't' : 'f') ; + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { return this; } /* The string representation */ public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append((this.val) ? "TRUE" : "FALSE"); + try { + return sb.append((this.val) ? "TRUE" : "FALSE"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/EnumerableValue.java b/tlatools/src/tlc2/value/EnumerableValue.java index d90733d822a025fc3c1f6abb50cf22827a58c696..9aa9d7a1643b782587cae8d27f91304d31e63b3f 100644 --- a/tlatools/src/tlc2/value/EnumerableValue.java +++ b/tlatools/src/tlc2/value/EnumerableValue.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Research. All rights reserved. + * Copyright (c) 2017 Microsoft Research. All rights reserved. * * The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies @@ -11,8 +11,8 @@ * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * + * copies or substantial portions of the Software. + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR @@ -22,19 +22,31 @@ * * Contributors: * Markus Alexander Kuppe - initial API and implementation + * Ian Morris Nieves - added support for fingerprint stack trace ******************************************************************************/ + package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; + public abstract class EnumerableValue extends Value implements Enumerable, ValueConstants { - public Value isSubsetEq(Value other) { - final ValueEnumeration Enum = this.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!other.member(elem)) { - return ValFalse; - } - } - return ValTrue; - } + public Value isSubsetEq(Value other) { + try { + final ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!other.member(elem)) { + return ValFalse; + } + } + return ValTrue; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + } diff --git a/tlatools/src/tlc2/value/FcnLambdaValue.java b/tlatools/src/tlc2/value/FcnLambdaValue.java index 6846a525f33cafd29b969dfa3aaf83d0a1d7aaf8..04112012d2c4607529cf121fdab5c91a3c1938bc 100644 --- a/tlatools/src/tlc2/value/FcnLambdaValue.java +++ b/tlatools/src/tlc2/value/FcnLambdaValue.java @@ -1,6 +1,7 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Wed 4 Jul 2007 at 17:31:23 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Wed 4 Jul 2007 at 17:31:23 PST by lamport // modified on Thu Dec 6 21:46:34 PST 2001 by yuanyu package tlc2.value; @@ -12,6 +13,8 @@ import java.io.ObjectOutputStream; import tla2sany.semantic.FormalParamNode; import tla2sany.semantic.SemanticNode; import tla2sany.semantic.SymbolNode; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.tool.EvalControl; import tlc2.tool.EvalException; import tlc2.tool.TLCState; @@ -32,19 +35,19 @@ public class FcnLambdaValue extends Value implements Applicable { /* Constructor */ public FcnLambdaValue(FcnParams params, SemanticNode body, Tool tool, - Context c, TLCState s0, TLCState s1, int control) { + Context c, TLCState s0, TLCState s1, int control) { this.params = params; this.body = body; this.excepts = null; this.tool = tool; this.con = c; - this.state = s0.copy(); // copy() added 12 Mar 2010 by Yuan Yu. + this.state = s0.copy(); // copy() added 12 Mar 2010 by Yuan Yu. if (s1 != null) { // see SetPredValue constructor. - this.pstate = s1.copy(); + this.pstate = s1.copy(); } else { this.pstate = null; } - + this.control = control; this.fcnRcd = null; } @@ -64,356 +67,449 @@ public class FcnLambdaValue extends Value implements Applicable { public final byte getKind() { return FCNLAMBDAVALUE; } public final void makeRecursive(SymbolNode fname) { - this.con = this.con.cons(fname, this); - this.control = EvalControl.setKeepLazy(this.control); + try { + this.con = this.con.cons(fname, this); + this.control = EvalControl.setKeepLazy(this.control); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final int compareTo(Object obj) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.compareTo(obj); + try { + FcnRcdValue fcn = FcnRcdValue.convert(this); + return fcn.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.equals(obj); + try { + FcnRcdValue fcn = FcnRcdValue.convert(this); + return fcn.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the function " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of the function " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the function:\n" + ppr(this.toString()) + - "\nis a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the function:\n" + ppr(this.toString()) + + "\nis a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - /* Apply this function to the arguments given by args. */ + /* Apply this function to the arguments given by args. */ public final Value apply(Value args, int control) throws EvalException { - if (this.fcnRcd != null) { - return this.fcnRcd.apply(args, control); - } - - // First, find all the excepts that match args. - Value res = null; - int num = 0; - ValueExcept[] excepts1 = null; - if (this.excepts != null) { - int exlen = this.excepts.length; - for (int i = exlen-1; i >= 0; i--) { - ValueExcept ex = this.excepts[i]; - Value arg = ex.current(); - boolean inExcept = true; - inExcept = arg.equals(args); - if (inExcept) { - if (ex.isLast()) { res = ex.value; break; } - if (excepts1 == null) excepts1 = new ValueExcept[exlen]; - excepts1[num++] = new ValueExcept(ex, ex.idx+1); - } + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.apply(args, control); } - } - // Second, evaluate the function application. - if (res == null) { - Context c1 = this.con; - FormalParamNode[][] formals = this.params.formals; - Value[] domains = this.params.domains; - boolean[] isTuples = this.params.isTuples; - int plen = this.params.length(); - - if (plen == 1) { - if (!domains[0].member(args)) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe first argument is:\n" + Value.ppr(args.toString()) + - "\nwhich is not in its domain.\n"); - } - if (isTuples[0]) { - FormalParamNode[] ids = formals[0]; - TupleValue argVal = TupleValue.convert(args); - if (argVal == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe first argument is:\n" + Value.ppr(args.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - if (argVal.size() != ids.length) return null; - Value[] elems = argVal.elems; - for (int i = 0; i < ids.length; i++) { - c1 = c1.cons(ids[i], elems[i]); - } - } - else { - c1 = c1.cons(formals[0][0], args); - } + // First, find all the excepts that match args. + Value res = null; + int num = 0; + ValueExcept[] excepts1 = null; + if (this.excepts != null) { + int exlen = this.excepts.length; + for (int i = exlen-1; i >= 0; i--) { + ValueExcept ex = this.excepts[i]; + Value arg = ex.current(); + boolean inExcept = true; + inExcept = arg.equals(args); + if (inExcept) { + if (ex.isLast()) { res = ex.value; break; } + if (excepts1 == null) excepts1 = new ValueExcept[exlen]; + excepts1[num++] = new ValueExcept(ex, ex.idx+1); + } + } } - else { - TupleValue tv = TupleValue.convert(args); - if (tv == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument list is:\n" + Value.ppr(args.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - Value[] elems = tv.elems; - int argn = 0; - for (int i = 0; i < formals.length; i++) { - FormalParamNode[] ids = formals[i]; - Value domain = domains[i]; - if (isTuples[i]) { - if (!domain.member(elems[argn])) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + (argn+1) + " is:\n" + - Value.ppr(elems[argn].toString()) + - "\nwhich is not in its domain.\n"); - } - TupleValue tv1 = TupleValue.convert(elems[argn++]); - if (tv1 == null || tv1.size() != ids.length) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + argn + " is:\n" + - Value.ppr(elems[argn-1].toString()) + - "\nwhich does not match its formal parameter.\n"); - } - Value[] avals = tv1.elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - for (int j = 0; j < ids.length; j++) { - if (!domain.member(elems[argn])) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + (argn+1) + " is:\n" + - Value.ppr(elems[argn].toString()) + "\nwhich is not in its domain.\n"); - } - c1 = c1.cons(ids[j], elems[argn++]); - } - } - } + + // Second, evaluate the function application. + if (res == null) { + Context c1 = this.con; + FormalParamNode[][] formals = this.params.formals; + Value[] domains = this.params.domains; + boolean[] isTuples = this.params.isTuples; + int plen = this.params.length(); + + if (plen == 1) { + if (!domains[0].member(args)) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe first argument is:\n" + Value.ppr(args.toString()) + + "\nwhich is not in its domain.\n"); + } + if (isTuples[0]) { + FormalParamNode[] ids = formals[0]; + TupleValue argVal = TupleValue.convert(args); + if (argVal == null) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe first argument is:\n" + Value.ppr(args.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + if (argVal.size() != ids.length) return null; + Value[] elems = argVal.elems; + for (int i = 0; i < ids.length; i++) { + c1 = c1.cons(ids[i], elems[i]); + } + } + else { + c1 = c1.cons(formals[0][0], args); + } + } + else { + TupleValue tv = TupleValue.convert(args); + if (tv == null) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe argument list is:\n" + Value.ppr(args.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + Value[] elems = tv.elems; + int argn = 0; + for (int i = 0; i < formals.length; i++) { + FormalParamNode[] ids = formals[i]; + Value domain = domains[i]; + if (isTuples[i]) { + if (!domain.member(elems[argn])) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe argument number " + (argn+1) + " is:\n" + + Value.ppr(elems[argn].toString()) + + "\nwhich is not in its domain.\n"); + } + TupleValue tv1 = TupleValue.convert(elems[argn++]); + if (tv1 == null || tv1.size() != ids.length) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe argument number " + argn + " is:\n" + + Value.ppr(elems[argn-1].toString()) + + "\nwhich does not match its formal parameter.\n"); + } + Value[] avals = tv1.elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + for (int j = 0; j < ids.length; j++) { + if (!domain.member(elems[argn])) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe argument number " + (argn+1) + " is:\n" + + Value.ppr(elems[argn].toString()) + "\nwhich is not in its domain.\n"); + } + c1 = c1.cons(ids[j], elems[argn++]); + } + } + } + } + res = this.tool.eval(this.body, c1, this.state, this.pstate, control); + } + + // Finally, apply the matching excepts on the result. + if (num == 0) return res; + ValueExcept[] excepts2 = new ValueExcept[num]; + for (int i = 0; i < num; i++) { + excepts2[num-1-i] = excepts1[i]; } - res = this.tool.eval(this.body, c1, this.state, this.pstate, control); + return res.takeExcept(excepts2); + } - - // Finally, apply the matching excepts on the result. - if (num == 0) return res; - ValueExcept[] excepts2 = new ValueExcept[num]; - for (int i = 0; i < num; i++) { - excepts2[num-1-i] = excepts1[i]; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return res.takeExcept(excepts2); } /* This one does not seem to be needed anymore. */ public final Value apply(Value[] args, int control) throws EvalException { - return this.apply(new TupleValue(args), control); + try { + return this.apply(new TupleValue(args), control); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value select(Value arg) { - if (this.fcnRcd != null) { - return this.fcnRcd.select(arg); - } - - // First, find all the excepts that match arg. - Value res = null; - int num = 0; - ValueExcept[] excepts1 = null; - if (this.excepts != null) { - int exlen = this.excepts.length; - for (int i = exlen-1; i >= 0; i--) { - ValueExcept ex = this.excepts[i]; - Value exArg = ex.current(); - boolean inExcept = exArg.equals(arg); - if (inExcept) { - if (ex.isLast()) { res = ex.value; break; } - if (excepts1 == null) excepts1 = new ValueExcept[exlen]; - excepts1[num++] = new ValueExcept(ex, ex.idx+1); - } + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.select(arg); } - } - // Second, evaluate the function application. - if (res == null) { - Context c1 = this.con; - FormalParamNode[][] formals = this.params.formals; - Value[] domains = this.params.domains; - boolean[] isTuples = this.params.isTuples; - int plen = this.params.length(); - - if (plen == 1) { - if (!domains[0].member(arg)) return null; - if (isTuples[0]) { - FormalParamNode[] ids = formals[0]; - TupleValue argVal = TupleValue.convert(arg); - /* - * SZA: Changed from argVal.toString() to arg.toString() to prevent a NullPointerException - */ - if (argVal == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe first argument is:\n" + Value.ppr(arg.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - if (argVal.size() != ids.length) return null; - Value[] elems = argVal.elems; - for (int i = 0; i < ids.length; i++) { - c1 = c1.cons(ids[i], elems[i]); - } - } - else { - c1 = c1.cons(formals[0][0], arg); - } + // First, find all the excepts that match arg. + Value res = null; + int num = 0; + ValueExcept[] excepts1 = null; + if (this.excepts != null) { + int exlen = this.excepts.length; + for (int i = exlen-1; i >= 0; i--) { + ValueExcept ex = this.excepts[i]; + Value exArg = ex.current(); + boolean inExcept = exArg.equals(arg); + if (inExcept) { + if (ex.isLast()) { res = ex.value; break; } + if (excepts1 == null) excepts1 = new ValueExcept[exlen]; + excepts1[num++] = new ValueExcept(ex, ex.idx+1); + } + } } - else { - TupleValue tv = TupleValue.convert(arg); - if (tv == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument list is:\n" + Value.ppr(arg.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - Value[] elems = tv.elems; - int argn = 0; - for (int i = 0; i < formals.length; i++) { - FormalParamNode[] ids = formals[i]; - Value domain = domains[i]; - if (isTuples[i]) { - if (!domain.member(elems[argn])) return null; - TupleValue tv1 = TupleValue.convert(elems[argn++]); - if (tv1 == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + argn + " is:\n" + - Value.ppr(elems[argn-1].toString()) + - "\nwhich does not match its formal parameter.\n"); - } - if (tv1.size() != ids.length) return null; - Value[] avals = tv1.elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - for (int j = 0; j < ids.length; j++) { - if (!domain.member(elems[argn])) return null; - c1 = c1.cons(ids[j], elems[argn++]); - } - } - } + + // Second, evaluate the function application. + if (res == null) { + Context c1 = this.con; + FormalParamNode[][] formals = this.params.formals; + Value[] domains = this.params.domains; + boolean[] isTuples = this.params.isTuples; + int plen = this.params.length(); + + if (plen == 1) { + if (!domains[0].member(arg)) return null; + if (isTuples[0]) { + FormalParamNode[] ids = formals[0]; + TupleValue argVal = TupleValue.convert(arg); + /* + * SZA: Changed from argVal.toString() to arg.toString() to prevent a NullPointerException + */ + if (argVal == null) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe first argument is:\n" + Value.ppr(arg.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + if (argVal.size() != ids.length) return null; + Value[] elems = argVal.elems; + for (int i = 0; i < ids.length; i++) { + c1 = c1.cons(ids[i], elems[i]); + } + } + else { + c1 = c1.cons(formals[0][0], arg); + } + } + else { + TupleValue tv = TupleValue.convert(arg); + if (tv == null) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe argument list is:\n" + Value.ppr(arg.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + Value[] elems = tv.elems; + int argn = 0; + for (int i = 0; i < formals.length; i++) { + FormalParamNode[] ids = formals[i]; + Value domain = domains[i]; + if (isTuples[i]) { + if (!domain.member(elems[argn])) return null; + TupleValue tv1 = TupleValue.convert(elems[argn++]); + if (tv1 == null) { + Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + + ",\nthe argument number " + argn + " is:\n" + + Value.ppr(elems[argn-1].toString()) + + "\nwhich does not match its formal parameter.\n"); + } + if (tv1.size() != ids.length) return null; + Value[] avals = tv1.elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + for (int j = 0; j < ids.length; j++) { + if (!domain.member(elems[argn])) return null; + c1 = c1.cons(ids[j], elems[argn++]); + } + } + } + } + res = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); } - res = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + + // Finally, apply the matching excepts on the result. + if (num == 0) return res; + ValueExcept[] excepts2 = new ValueExcept[num]; + for (int i = 0; i < num; i++) { + excepts2[num-1-i] = excepts1[i]; + } + return res.takeExcept(excepts2); + } - - // Finally, apply the matching excepts on the result. - if (num == 0) return res; - ValueExcept[] excepts2 = new ValueExcept[num]; - for (int i = 0; i < num; i++) { - excepts2[num-1-i] = excepts1[i]; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return res.takeExcept(excepts2); } - + /* This method returns a new function value by taking except. */ public final Value takeExcept(ValueExcept ex) { - if (ex.idx >= ex.path.length) return ex.value; - - if (this.fcnRcd != null) { - return this.fcnRcd.takeExcept(ex); - } - FcnLambdaValue fcn = new FcnLambdaValue(this); - if (this.excepts == null) { - fcn.excepts = new ValueExcept[1]; - fcn.excepts[0] = ex; - } - else { - int exlen = this.excepts.length; - fcn.excepts = new ValueExcept[exlen+1]; - for (int i = 0; i < exlen; i++) { - fcn.excepts[i] = this.excepts[i]; + try { + + if (ex.idx >= ex.path.length) return ex.value; + + if (this.fcnRcd != null) { + return this.fcnRcd.takeExcept(ex); } - fcn.excepts[exlen] = ex; + FcnLambdaValue fcn = new FcnLambdaValue(this); + if (this.excepts == null) { + fcn.excepts = new ValueExcept[1]; + fcn.excepts[0] = ex; + } + else { + int exlen = this.excepts.length; + fcn.excepts = new ValueExcept[exlen+1]; + for (int i = 0; i < exlen; i++) { + fcn.excepts[i] = this.excepts[i]; + } + fcn.excepts[exlen] = ex; + } + return fcn; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return fcn; } /* This method returns a new function value by taking excepts. */ public final Value takeExcept(ValueExcept[] exs) { - if (this.fcnRcd != null) { - return this.fcnRcd.takeExcept(exs); - } - FcnLambdaValue fcn = new FcnLambdaValue(this); - int exslen = exs.length; - if (exslen != 0) { - int i = 0; - for (i = exs.length-1; i >= 0; i--) { - if (exs[i].idx >= exs[i].path.length) break; - } - if (i >= 0) { - int xlen = exslen-i-1; - fcn.excepts = new ValueExcept[xlen]; - System.arraycopy(exs, i+1, fcn.excepts, 0, xlen); - } - else if (this.excepts == null) { - fcn.excepts = new ValueExcept[exslen]; - System.arraycopy(exs, 0, fcn.excepts, 0, exslen); + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.takeExcept(exs); } - else { - int len = this.excepts.length; - fcn.excepts = new ValueExcept[len + exslen]; - System.arraycopy(this.excepts, 0, fcn.excepts, 0, len); - System.arraycopy(exs, 0, fcn.excepts, len, exslen); + FcnLambdaValue fcn = new FcnLambdaValue(this); + int exslen = exs.length; + if (exslen != 0) { + int i = 0; + for (i = exs.length-1; i >= 0; i--) { + if (exs[i].idx >= exs[i].path.length) break; + } + if (i >= 0) { + int xlen = exslen-i-1; + fcn.excepts = new ValueExcept[xlen]; + System.arraycopy(exs, i+1, fcn.excepts, 0, xlen); + } + else if (this.excepts == null) { + fcn.excepts = new ValueExcept[exslen]; + System.arraycopy(exs, 0, fcn.excepts, 0, exslen); + } + else { + int len = this.excepts.length; + fcn.excepts = new ValueExcept[len + exslen]; + System.arraycopy(this.excepts, 0, fcn.excepts, 0, len); + System.arraycopy(exs, 0, fcn.excepts, len, exslen); + } } + return fcn; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return fcn; } public final Value getDomain() { - if (this.fcnRcd != null) { - return this.fcnRcd.getDomain(); - } - int len = this.params.length(); - if (len == 1) { - return this.params.domains[0]; - } - Value[] sets = new Value[len]; - int dlen = this.params.domains.length; - boolean[] isTuples = this.params.isTuples; - int idx = 0; - for (int i = 0; i < dlen; i++) { - FormalParamNode[] formal = this.params.formals[i]; - Value domain = this.params.domains[i]; - if (isTuples[i]) { - sets[idx++] = domain; + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.getDomain(); } - else { - for (int j = 0; j < formal.length; j++) { - sets[idx++] = domain; - } + int len = this.params.length(); + if (len == 1) { + return this.params.domains[0]; + } + Value[] sets = new Value[len]; + int dlen = this.params.domains.length; + boolean[] isTuples = this.params.isTuples; + int idx = 0; + for (int i = 0; i < dlen; i++) { + FormalParamNode[] formal = this.params.formals[i]; + Value domain = this.params.domains[i]; + if (isTuples[i]) { + sets[idx++] = domain; + } + else { + for (int j = 0; j < formal.length; j++) { + sets[idx++] = domain; + } + } } + return new SetOfTuplesValue(sets); + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetOfTuplesValue(sets); } - + public final int size() { - if (this.fcnRcd == null) { - return this.params.size(); + try { + if (this.fcnRcd == null) { + return this.params.size(); + } + return this.fcnRcd.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.fcnRcd.size(); } public final boolean isDefined() { return true; } public final Value deepCopy() { - FcnLambdaValue fcn = new FcnLambdaValue(this); - // A bug occured when printing a function whose domain is a Cartesian product because this.fcnRcd - // is null at this point. On 5 Mar 2012, LL wrapped the following null test around the assignment. - if (this.fcnRcd != null) { - fcn.fcnRcd = (FcnRcdValue)this.fcnRcd.deepCopy(); + try { + FcnLambdaValue fcn = new FcnLambdaValue(this); + // A bug occured when printing a function whose domain is a Cartesian product because this.fcnRcd + // is null at this point. On 5 Mar 2012, LL wrapped the following null test around the assignment. + if (this.fcnRcd != null) { + fcn.fcnRcd = (FcnRcdValue)this.fcnRcd.deepCopy(); + } + return fcn; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return fcn; } public final boolean assignable(Value val) { - return (val instanceof FcnLambdaValue); + try { + return (val instanceof FcnLambdaValue); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { this.fcnRcd = (FcnRcdValue)ois.readObject(); } @@ -421,102 +517,140 @@ public class FcnLambdaValue extends Value implements Applicable { FcnRcdValue res = this.toFcnRcd(); oos.writeObject(res); } - + public final boolean isNormalized() { - if (this.fcnRcd == null) { - return false; + try { + if (this.fcnRcd == null) { + return false; + } + return this.fcnRcd.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.fcnRcd.isNormalized(); } public final void normalize() { - if (this.fcnRcd != null) { - this.fcnRcd.normalize(); + try { + if (this.fcnRcd != null) { + this.fcnRcd.normalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final FcnRcdValue toFcnRcd() { - if (this.fcnRcd == null) { - int sz = this.params.size(); - FormalParamNode[][] formals = this.params.formals; - boolean[] isTuples = this.params.isTuples; - - Value[] domain = new Value[sz]; - Value[] values = new Value[sz]; - int idx = 0; - ValueEnumeration Enum = this.params.elements(); - Value arg; - if (this.params.length() == 1) { - while ((arg = Enum.nextElement()) != null) { - domain[idx] = arg; - Context c1 = this.con; - if (isTuples[0]) { - FormalParamNode[] ids = formals[0]; - Value[] avals = ((TupleValue)arg).elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - c1 = c1.cons(formals[0][0], arg); - } - values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); - } - } - else { - while ((arg = Enum.nextElement()) != null) { - domain[idx] = arg; - Value[] argList = ((TupleValue)arg).elems; - int argn = 0; - Context c1 = this.con; - for (int i = 0; i < formals.length; i++) { - FormalParamNode[] ids = formals[i]; - if (isTuples[i]) { - Value[] avals = ((TupleValue)argList[argn++]).elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], argList[argn++]); - } - } - } - values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); - } - } - this.fcnRcd = new FcnRcdValue(domain, values, false); - if (this.excepts != null) { - this.fcnRcd = (FcnRcdValue)fcnRcd.takeExcept(this.excepts); + try { + + if (this.fcnRcd == null) { + int sz = this.params.size(); + FormalParamNode[][] formals = this.params.formals; + boolean[] isTuples = this.params.isTuples; + + Value[] domain = new Value[sz]; + Value[] values = new Value[sz]; + int idx = 0; + ValueEnumeration Enum = this.params.elements(); + Value arg; + if (this.params.length() == 1) { + while ((arg = Enum.nextElement()) != null) { + domain[idx] = arg; + Context c1 = this.con; + if (isTuples[0]) { + FormalParamNode[] ids = formals[0]; + Value[] avals = ((TupleValue)arg).elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + c1 = c1.cons(formals[0][0], arg); + } + values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + } + } + else { + while ((arg = Enum.nextElement()) != null) { + domain[idx] = arg; + Value[] argList = ((TupleValue)arg).elems; + int argn = 0; + Context c1 = this.con; + for (int i = 0; i < formals.length; i++) { + FormalParamNode[] ids = formals[i]; + if (isTuples[i]) { + Value[] avals = ((TupleValue)argList[argn++]).elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], argList[argn++]); + } + } + } + values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + } + } + this.fcnRcd = new FcnRcdValue(domain, values, false); + if (this.excepts != null) { + this.fcnRcd = (FcnRcdValue)fcnRcd.takeExcept(this.excepts); + } } + return this.fcnRcd; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.fcnRcd; } - + /* The fingerprint methods. */ public final long fingerPrint(long fp) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.fingerPrint(fp); + try { + FcnRcdValue fcn = FcnRcdValue.convert(this); + return fcn.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.permute(perm); + try { + FcnRcdValue fcn = FcnRcdValue.convert(this); + return fcn.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + /* The string representation of this function. */ public final StringBuffer toString(StringBuffer sb, int offset) { - if (expand || this.params == null) { - try { - Value val = FcnRcdValue.convert(this); - return val.toString(sb, offset); + try { + if (expand || this.params == null) { + try { + Value val = FcnRcdValue.convert(this); + return val.toString(sb, offset); + } + catch (Throwable e) { /*SKIP*/ } } - catch (Throwable e) { /*SKIP*/ } + sb.append("[" + this.params.toString()); + sb.append(" |-> <expression " + this.body + ">]"); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - sb.append("[" + this.params.toString()); - sb.append(" |-> <expression " + this.body + ">]"); - return sb; } } diff --git a/tlatools/src/tlc2/value/FcnRcdValue.java b/tlatools/src/tlc2/value/FcnRcdValue.java index f47940cd5531193837de4ec085e21ad7ba1af540..15eafbcdec6e8f5471e6975fdaf47cf434e4ce97 100644 --- a/tlatools/src/tlc2/value/FcnRcdValue.java +++ b/tlatools/src/tlc2/value/FcnRcdValue.java @@ -1,12 +1,15 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:07:06 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:07:06 PST by lamport // modified on Fri Aug 10 15:07:25 PDT 2001 by yuanyu package tlc2.value; import java.util.Arrays; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.tool.EvalControl; import tlc2.util.FP64; import util.Assert; @@ -54,13 +57,13 @@ public class FcnRcdValue extends Value implements Applicable { Arrays.fill(tbl, -1); synchronized(this) { - for (int i = 0; i < this.domain.length; i++) { - int loc = (this.domain[i].hashCode() & 0x7FFFFFFF) % len; - while (tbl[loc] != -1) { - loc = (loc + 1) % len; - } - tbl[loc] = i; - } + for (int i = 0; i < this.domain.length; i++) { + int loc = (this.domain[i].hashCode() & 0x7FFFFFFF) % len; + while (tbl[loc] != -1) { + loc = (loc + 1) % len; + } + tbl[loc] = i; + } } synchronized(this) { this.indexTbl = tbl; } @@ -73,296 +76,372 @@ public class FcnRcdValue extends Value implements Applicable { while (true) { int idx = this.indexTbl[loc]; if (idx == -1) { - Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) + - "\nto argument " + ppr(arg.toString()) + - ", which is not in the domain of the function."); + Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) + + "\nto argument " + ppr(arg.toString()) + + ", which is not in the domain of the function."); } if (this.domain[idx].equals(arg)) { - return idx; + return idx; } loc = (loc + 1) % len; } } - - public final int compareTo(Object obj) { - FcnRcdValue fcn = convert(obj); - if (fcn == null) { - if (obj instanceof ModelValue) return 1; - Assert.fail("Attempted to compare the function " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - fcn.normalize(); - int result = this.values.length - fcn.values.length; - if (result != 0) return result; - - if (this.intv != null) { - if (fcn.intv != null) { - result = this.intv.low - fcn.intv.low; - if (result != 0) return result; - for (int i = 0; i < this.values.length; i++) { - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } - } - else { - for (int i = 0; i < fcn.domain.length; i++) { - Value dElem = fcn.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare integer with non-integer:\n" + - ppr(dElem.toString()) + "."); - } - result = this.intv.low + i - ((IntValue)dElem).val; - if (result != 0) return result; - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } - } - } - else { - if (fcn.intv != null) { - for (int i = 0; i < this.domain.length; i++) { - Value dElem = this.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare integer with non-integer\n" + - ppr(dElem.toString()) + "."); - } - result = ((IntValue)dElem).val - (fcn.intv.low + i); - if (result != 0) return result; - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } + public final int compareTo(Object obj) { + try { + + FcnRcdValue fcn = convert(obj); + if (fcn == null) { + if (obj instanceof ModelValue) return 1; + Assert.fail("Attempted to compare the function " + ppr(this.toString()) + + " with the value:\n" + ppr(obj.toString())); + } + this.normalize(); + fcn.normalize(); + + int result = this.values.length - fcn.values.length; + if (result != 0) return result; + + if (this.intv != null) { + if (fcn.intv != null) { + result = this.intv.low - fcn.intv.low; + if (result != 0) return result; + for (int i = 0; i < this.values.length; i++) { + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) return result; + } + } + else { + for (int i = 0; i < fcn.domain.length; i++) { + Value dElem = fcn.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail("Attempted to compare integer with non-integer:\n" + + ppr(dElem.toString()) + "."); + } + result = this.intv.low + i - ((IntValue)dElem).val; + if (result != 0) return result; + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) return result; + } + } } else { - for (int i = 0; i < this.domain.length; i++) { - result = this.domain[i].compareTo(fcn.domain[i]); - if (result != 0) return result; - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } - } + if (fcn.intv != null) { + for (int i = 0; i < this.domain.length; i++) { + Value dElem = this.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail("Attempted to compare integer with non-integer\n" + + ppr(dElem.toString()) + "."); + } + result = ((IntValue)dElem).val - (fcn.intv.low + i); + if (result != 0) return result; + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) return result; + } + } + else { + for (int i = 0; i < this.domain.length; i++) { + result = this.domain[i].compareTo(fcn.domain[i]); + if (result != 0) return result; + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) return result; + } + } + } + return 0; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return 0; } public final boolean equals(Object obj) { - FcnRcdValue fcn = convert(obj); - if (fcn == null) { - if (obj instanceof ModelValue) - return ((ModelValue) obj).modelValueEquals(this) ; - Assert.fail("Attempted to check equality of the function " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - fcn.normalize(); - - if (this.intv != null) { - if (fcn.intv != null) { - if (!this.intv.equals(fcn.intv)) return false; - for (int i = 0; i < this.values.length; i++) { - if (!this.values[i].equals(fcn.values[i])) - return false; - } - } - else { - if (fcn.domain.length != this.intv.size()) return false; - for (int i = 0; i < fcn.domain.length; i++) { - Value dElem = fcn.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare an integer with non-integer:\n" + - ppr(dElem.toString()) + "."); - } - if (((IntValue)dElem).val != (this.intv.low + i) || - !this.values[i].equals(fcn.values[i])) { - return false; - } - } - } - } - else { - if (this.values.length != fcn.values.length) return false; - if (fcn.intv != null) { - for (int i = 0; i < this.domain.length; i++) { - Value dElem = this.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare an integer with non-integer:\n" + - ppr(dElem.toString()) + "."); - } - if (((IntValue)dElem).val != (fcn.intv.low + i) || - !this.values[i].equals(fcn.values[i])) { - return false; - } - } + try { + + FcnRcdValue fcn = convert(obj); + if (fcn == null) { + if (obj instanceof ModelValue) + return ((ModelValue) obj).modelValueEquals(this) ; + Assert.fail("Attempted to check equality of the function " + ppr(this.toString()) + + " with the value:\n" + ppr(obj.toString())); + } + this.normalize(); + fcn.normalize(); + + if (this.intv != null) { + if (fcn.intv != null) { + if (!this.intv.equals(fcn.intv)) return false; + for (int i = 0; i < this.values.length; i++) { + if (!this.values[i].equals(fcn.values[i])) + return false; + } + } + else { + if (fcn.domain.length != this.intv.size()) return false; + for (int i = 0; i < fcn.domain.length; i++) { + Value dElem = fcn.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail("Attempted to compare an integer with non-integer:\n" + + ppr(dElem.toString()) + "."); + } + if (((IntValue)dElem).val != (this.intv.low + i) || + !this.values[i].equals(fcn.values[i])) { + return false; + } + } + } } else { - for (int i = 0; i < this.domain.length; i++) { - if (!this.domain[i].equals(fcn.domain[i]) || - !this.values[i].equals(fcn.values[i])) { - return false; - } - } - } + if (this.values.length != fcn.values.length) return false; + if (fcn.intv != null) { + for (int i = 0; i < this.domain.length; i++) { + Value dElem = this.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail("Attempted to compare an integer with non-integer:\n" + + ppr(dElem.toString()) + "."); + } + if (((IntValue)dElem).val != (fcn.intv.low + i) || + !this.values[i].equals(fcn.values[i])) { + return false; + } + } + } + else { + for (int i = 0; i < this.domain.length; i++) { + if (!this.domain[i].equals(fcn.domain[i]) || + !this.values[i].equals(fcn.values[i])) { + return false; + } + } + } + } + return true; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the function " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of the function " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { return true; } public final Value apply(Value arg, int control) { - Value result = this.select(arg); - if (result == null) { - Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) + - "\nto argument " + ppr(arg.toString()) + ", which is" + - " not in the domain of the function."); + try { + Value result = this.select(arg); + if (result == null) { + Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) + + "\nto argument " + ppr(arg.toString()) + ", which is" + + " not in the domain of the function."); + } + return result; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return result; } /* This one does not seem to be needed anymore. */ public final Value apply(Value[] args, int control) { - return this.apply(new TupleValue(args), EvalControl.Clear); + try { + return this.apply(new TupleValue(args), EvalControl.Clear); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value select(Value arg) { - if (this.intv != null) { - // domain is represented as an integer interval: - if (!(arg instanceof IntValue)) { - Assert.fail("Attempted to apply function with integer domain to" + - " the non-integer argument " + ppr(arg.toString())); + try { + + if (this.intv != null) { + // domain is represented as an integer interval: + if (!(arg instanceof IntValue)) { + Assert.fail("Attempted to apply function with integer domain to" + + " the non-integer argument " + ppr(arg.toString())); + } + int idx = ((IntValue)arg).val; + if ((idx >= this.intv.low) && (idx <= this.intv.high)) { + return this.values[idx - this.intv.low]; + } } - int idx = ((IntValue)arg).val; - if ((idx >= this.intv.low) && (idx <= this.intv.high)) { - return this.values[idx - this.intv.low]; + else { + // domain is represented as an array of values: + if (this.indexTbl != null) { + return this.values[this.lookupIndex(arg)]; + } + if (this.isNorm) this.createIndex(); + + if (this.indexTbl != null) { + return this.values[this.lookupIndex(arg)]; + } + else { + int len = this.domain.length; + for (int i = 0; i < len; i++) { + if (this.domain[i].equals(arg)) { + return this.values[i]; + } + } + } } + return null; + } - else { - // domain is represented as an array of values: - if (this.indexTbl != null) { - return this.values[this.lookupIndex(arg)]; - } - if (this.isNorm) this.createIndex(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } - if (this.indexTbl != null) { - return this.values[this.lookupIndex(arg)]; + public final boolean assign(Value[] args, Value val) { + try { + + if (this.intv != null) { + // domain is represented as an integer interval: + if (args.length != 1) { + Assert.fail("Wrong number of function arguments."); + } + if (args[0] instanceof IntValue) { + int idx = ((IntValue)args[0]).val; + if ((idx >= this.intv.low) && (idx <= this.intv.high)) { + int vIdx = idx - this.intv.low; + if (this.values[vIdx] == ValUndef || + this.values[vIdx].equals(val)) { + this.values[vIdx] = val; + return true; + } + return false; + } + } } else { - int len = this.domain.length; - for (int i = 0; i < len; i++) { - if (this.domain[i].equals(arg)) { - return this.values[i]; - } - } - } + // domain is represented as an array of values: + Value argv = new TupleValue(args); + int len = this.domain.length; + for (int i = 0; i < len; i++) { + if (this.domain[i].equals(argv)) { + if (this.values[i] == ValUndef || + this.values[i].equals(val)) { + this.values[i] = val; + return true; + } + return false; + } + } + } + Assert.fail("Function initialization out of domain."); + return false; // make compiler happy + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return null; } - public final boolean assign(Value[] args, Value val) { - if (this.intv != null) { - // domain is represented as an integer interval: - if (args.length != 1) { - Assert.fail("Wrong number of function arguments."); - } - if (args[0] instanceof IntValue) { - int idx = ((IntValue)args[0]).val; - if ((idx >= this.intv.low) && (idx <= this.intv.high)) { - int vIdx = idx - this.intv.low; - if (this.values[vIdx] == ValUndef || - this.values[vIdx].equals(val)) { - this.values[vIdx] = val; - return true; - } - return false; - } - } - } - else { - // domain is represented as an array of values: - Value argv = new TupleValue(args); - int len = this.domain.length; - for (int i = 0; i < len; i++) { - if (this.domain[i].equals(argv)) { - if (this.values[i] == ValUndef || - this.values[i].equals(val)) { - this.values[i] = val; - return true; - } - return false; - } - } - } - Assert.fail("Function initialization out of domain."); - return false; // make compiler happy - } - public final Value takeExcept(ValueExcept ex) { - if (ex.idx >= ex.path.length) return ex.value; + try { - int flen = this.values.length; - Value[] newValues = new Value[flen]; - for (int i = 0; i < flen; i++) { - newValues[i] = this.values[i]; - } - Value arg = ex.path[ex.idx]; + if (ex.idx >= ex.path.length) return ex.value; - if (this.intv != null) { - // domain is represented as an integer interval: - if (arg instanceof IntValue) { - int idx = ((IntValue)arg).val; - if ((idx >= this.intv.low) && (idx <= this.intv.high)) { - int vidx = idx - this.intv.low; - ex.idx++; - newValues[idx] = this.values[vidx].takeExcept(ex); - } - return new FcnRcdValue(this.intv, newValues); - } - } - else { - // domain is represented as an array of values: + int flen = this.values.length; + Value[] newValues = new Value[flen]; for (int i = 0; i < flen; i++) { - if (arg.equals(this.domain[i])) { - ex.idx++; - newValues[i] = newValues[i].takeExcept(ex); - Value[] newDomain = this.domain; - if (!this.isNorm) { - newDomain = new Value[flen]; - for (int j = 0; j < flen; j++) { - newDomain[j] = this.domain[j]; - } - } - return new FcnRcdValue(newDomain, newValues, this.isNorm); - } - } - } - return this; + newValues[i] = this.values[i]; + } + Value arg = ex.path[ex.idx]; + + if (this.intv != null) { + // domain is represented as an integer interval: + if (arg instanceof IntValue) { + int idx = ((IntValue)arg).val; + if ((idx >= this.intv.low) && (idx <= this.intv.high)) { + int vidx = idx - this.intv.low; + ex.idx++; + newValues[idx] = this.values[vidx].takeExcept(ex); + } + return new FcnRcdValue(this.intv, newValues); + } + } + else { + // domain is represented as an array of values: + for (int i = 0; i < flen; i++) { + if (arg.equals(this.domain[i])) { + ex.idx++; + newValues[i] = newValues[i].takeExcept(ex); + Value[] newDomain = this.domain; + if (!this.isNorm) { + newDomain = new Value[flen]; + for (int j = 0; j < flen; j++) { + newDomain[j] = this.domain[j]; + } + } + return new FcnRcdValue(newDomain, newValues, this.isNorm); + } + } + } + return this; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value takeExcept(ValueExcept[] exs) { - Value res = this; - for (int i = 0; i < exs.length; i++) { - res = res.takeExcept(exs[i]); + try { + Value res = this; + for (int i = 0; i < exs.length; i++) { + res = res.takeExcept(exs[i]); + } + return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return res; } public final Value getDomain() { - if (this.intv != null) { - return this.intv; + try { + if (this.intv != null) { + return this.intv; + } + this.normalize(); + return new SetEnumValue(this.domain, true); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.normalize(); - return new SetEnumValue(this.domain, true); } public final int size() { - this.normalize(); - return this.values.length; + try { + this.normalize(); + return this.values.length; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* @@ -371,168 +450,210 @@ public class FcnRcdValue extends Value implements Applicable { */ public static final FcnRcdValue convert(Object val) { switch (((Value)val).getKind()) { - case FCNRCDVALUE: - { - return (FcnRcdValue)val; - } - case FCNLAMBDAVALUE: - { - return ((FcnLambdaValue)val).toFcnRcd(); - } - case RECORDVALUE: - { - RecordValue rcd = (RecordValue)val; - rcd.normalize(); - Value[] dom = new Value[rcd.names.length]; - for (int i = 0; i < rcd.names.length; i++) { - dom[i] = new StringValue(rcd.names[i]); - } - return new FcnRcdValue(dom, rcd.values, rcd.isNormalized()); - } - case TUPLEVALUE: - { - Value[] elems = ((TupleValue)val).elems; - IntervalValue intv = new IntervalValue(1, elems.length); - return new FcnRcdValue(intv, elems); - } - default: - // return null if not convertable - return null; + case FCNRCDVALUE: + { + return (FcnRcdValue)val; + } + case FCNLAMBDAVALUE: + { + return ((FcnLambdaValue)val).toFcnRcd(); + } + case RECORDVALUE: + { + RecordValue rcd = (RecordValue)val; + rcd.normalize(); + Value[] dom = new Value[rcd.names.length]; + for (int i = 0; i < rcd.names.length; i++) { + dom[i] = new StringValue(rcd.names[i]); + } + return new FcnRcdValue(dom, rcd.values, rcd.isNormalized()); + } + case TUPLEVALUE: + { + Value[] elems = ((TupleValue)val).elems; + IntervalValue intv = new IntervalValue(1, elems.length); + return new FcnRcdValue(intv, elems); + } + default: + // return null if not convertable + return null; } } /* Return true iff this function is in normal form. */ public final boolean isNormalized() { return this.isNorm; } - + /* This method normalizes (destructively) this function. */ public final void normalize() { - if (!this.isNorm) { - // Assert.check(this.domain != null) - int dlen = this.domain.length; - for (int i = 1; i < dlen; i++) { - int cmp = this.domain[0].compareTo(this.domain[i]); - if (cmp == 0) { - Assert.fail("The value\n" + this.domain[i] + - "\noccurs multiple times in the function domain."); - } - else if (cmp > 0) { - Value tv = this.domain[0]; - this.domain[0] = this.domain[i]; - this.domain[i] = tv; - tv = this.values[0]; - this.values[0] = this.values[i]; - this.values[i] = tv; - } - } - for (int i = 2; i < dlen; i++) { - Value d = this.domain[i]; - Value v = this.values[i]; - int j = i; - int cmp; - while ((cmp = d.compareTo(this.domain[j-1])) < 0) { - this.domain[j] = this.domain[j-1]; - this.values[j] = this.values[j-1]; - j--; - } - if (cmp == 0) { - Assert.fail("The value\n" + this.domain[i] + - "\noccurs multiple times in the function domain."); - } - this.domain[j] = d; - this.values[j] = v; - } - this.isNorm = true; + try { + + if (!this.isNorm) { + // Assert.check(this.domain != null) + int dlen = this.domain.length; + for (int i = 1; i < dlen; i++) { + int cmp = this.domain[0].compareTo(this.domain[i]); + if (cmp == 0) { + Assert.fail("The value\n" + this.domain[i] + + "\noccurs multiple times in the function domain."); + } + else if (cmp > 0) { + Value tv = this.domain[0]; + this.domain[0] = this.domain[i]; + this.domain[i] = tv; + tv = this.values[0]; + this.values[0] = this.values[i]; + this.values[i] = tv; + } + } + for (int i = 2; i < dlen; i++) { + Value d = this.domain[i]; + Value v = this.values[i]; + int j = i; + int cmp; + while ((cmp = d.compareTo(this.domain[j-1])) < 0) { + this.domain[j] = this.domain[j-1]; + this.values[j] = this.values[j-1]; + j--; + } + if (cmp == 0) { + Assert.fail("The value\n" + this.domain[i] + + "\noccurs multiple times in the function domain."); + } + this.domain[j] = d; + this.values[j] = v; + } + this.isNorm = true; + } + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final boolean isDefined() { - boolean defined = true; - if (this.intv == null) { + try { + + boolean defined = true; + if (this.intv == null) { + for (int i = 0; i < this.values.length; i++) { + defined = defined && this.domain[i].isDefined(); + } + } for (int i = 0; i < this.values.length; i++) { - defined = defined && this.domain[i].isDefined(); + defined = defined && this.values[i].isDefined(); } + return defined; + } - for (int i = 0; i < this.values.length; i++) { - defined = defined && this.values[i].isDefined(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return defined; } public final Value deepCopy() { - Value[] vals = new Value[this.values.length]; - for (int i = 0; i < vals.length; i++) { - vals[i] = this.values[i].deepCopy(); + try { + Value[] vals = new Value[this.values.length]; + for (int i = 0; i < vals.length; i++) { + vals[i] = this.values[i].deepCopy(); + } + return new FcnRcdValue(this, vals); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new FcnRcdValue(this, vals); } public final boolean assignable(Value val) { - boolean canAssign = ((val instanceof FcnRcdValue) && - this.values.length == ((FcnRcdValue)val).values.length); - if (!canAssign) return false; - FcnRcdValue fcn = (FcnRcdValue)val; - for (int i = 0; i < this.values.length; i++) { - canAssign = (canAssign && - this.domain[i].equals(fcn.domain[i]) && - this.values[i].assignable(fcn.values[i])); - } - return canAssign; + try { + boolean canAssign = ((val instanceof FcnRcdValue) && + this.values.length == ((FcnRcdValue)val).values.length); + if (!canAssign) return false; + FcnRcdValue fcn = (FcnRcdValue)val; + for (int i = 0; i < this.values.length; i++) { + canAssign = (canAssign && + this.domain[i].equals(fcn.domain[i]) && + this.values[i].assignable(fcn.values[i])); + } + return canAssign; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint method. */ public final long fingerPrint(long fp) { - this.normalize(); - int flen = this.values.length; - fp = FP64.Extend(fp, FCNRCDVALUE); - fp = FP64.Extend(fp, flen); - if (this.intv == null) { - for (int i = 0; i < flen; i++) { - fp = this.domain[i].fingerPrint(fp); - fp = this.values[i].fingerPrint(fp); + try { + this.normalize(); + int flen = this.values.length; + fp = FP64.Extend(fp, FCNRCDVALUE); + fp = FP64.Extend(fp, flen); + if (this.intv == null) { + for (int i = 0; i < flen; i++) { + fp = this.domain[i].fingerPrint(fp); + fp = this.values[i].fingerPrint(fp); + } } - } - else { - for (int i = 0; i < flen; i++) { - fp = FP64.Extend(fp, INTVALUE); - fp = FP64.Extend(fp, i + this.intv.low); - fp = this.values[i].fingerPrint(fp); + else { + for (int i = 0; i < flen; i++) { + fp = FP64.Extend(fp, INTVALUE); + fp = FP64.Extend(fp, i + this.intv.low); + fp = this.values[i].fingerPrint(fp); + } } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return fp; } public final Value permute(MVPerm perm) { - this.normalize(); - int flen = this.domain.length; - Value[] vals = new Value[flen]; + try { - boolean vchanged = false; - for (int i = 0; i < flen; i++) { - vals[i] = this.values[i].permute(perm); - vchanged = vchanged || (vals[i] != this.values[i]); - } + this.normalize(); + int flen = this.domain.length; + Value[] vals = new Value[flen]; - if (this.intv == null) { - Value[] dom = new Value[flen]; - boolean dchanged = false; + boolean vchanged = false; for (int i = 0; i < flen; i++) { - dom[i] = this.domain[i].permute(perm); - dchanged = dchanged || (dom[i] != this.domain[i]); + vals[i] = this.values[i].permute(perm); + vchanged = vchanged || (vals[i] != this.values[i]); } - if (dchanged) { - return new FcnRcdValue(dom, vals, false); + if (this.intv == null) { + Value[] dom = new Value[flen]; + boolean dchanged = false; + for (int i = 0; i < flen; i++) { + dom[i] = this.domain[i].permute(perm); + dchanged = dchanged || (dom[i] != this.domain[i]); + } + + if (dchanged) { + return new FcnRcdValue(dom, vals, false); + } + else if (vchanged) { + return new FcnRcdValue(this.domain, vals, true); + } } - else if (vchanged) { - return new FcnRcdValue(this.domain, vals, true); + else { + if (vchanged) { + return new FcnRcdValue(this.intv, vals); + } } + return this; + } - else { - if (vchanged) { - return new FcnRcdValue(this.intv, vals); - } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } private static final boolean isName(String name) { @@ -554,7 +675,7 @@ public class FcnRcdValue extends Value implements Applicable { for (int i = 0; i < this.domain.length; i++) { Value dval = this.domain[i]; boolean isName = ((dval instanceof StringValue) && - isName(((StringValue)dval).val.toString())); + isName(((StringValue)dval).val.toString())); if (!isName) return false; } return true; @@ -566,62 +687,70 @@ public class FcnRcdValue extends Value implements Applicable { } for (int i = 0; i < this.domain.length; i++) { if (!(this.domain[i] instanceof IntValue)) { - return false; + return false; } } this.normalize(); for (int i = 0; i < this.domain.length; i++) { if (((IntValue)this.domain[i]).val != (i+1)) { - return false; + return false; } } return true; } - + /* The string representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - int len = this.values.length; - if (len == 0) { - sb.append("<< >>"); - } - else if (this.isRcd()) { - sb.append("["); - sb.append(((StringValue)this.domain[0]).val + " |-> "); - sb = this.values[0].toString(sb, offset); - - for (int i = 1; i < len; i++) { - sb.append(", "); - sb.append(((StringValue)this.domain[i]).val + " |-> "); - sb = this.values[i].toString(sb, offset); + try { + + int len = this.values.length; + if (len == 0) { + sb.append("<< >>"); + } + else if (this.isRcd()) { + sb.append("["); + sb.append(((StringValue)this.domain[0]).val + " |-> "); + sb = this.values[0].toString(sb, offset); + + for (int i = 1; i < len; i++) { + sb.append(", "); + sb.append(((StringValue)this.domain[i]).val + " |-> "); + sb = this.values[i].toString(sb, offset); + } + sb.append("]"); + } + else if (this.isTuple()) { + // It is actually a sequence: + sb = sb.append("<<"); + sb = this.values[0].toString(sb, offset); + + for (int i = 1; i < len; i++) { + sb.append(", "); + sb = this.values[i].toString(sb, offset); + } + sb.append(">>"); } - sb.append("]"); - } - else if (this.isTuple()) { - // It is actually a sequence: - sb = sb.append("<<"); - sb = this.values[0].toString(sb, offset); + else { + sb = sb.append("("); + sb = this.domain[0].toString(sb, offset); + sb.append(" :> "); + sb = this.values[0].toString(sb, offset); - for (int i = 1; i < len; i++) { - sb.append(", "); - sb = this.values[i].toString(sb, offset); + for (int i = 1; i < len; i++) { + sb.append(" @@ "); + sb = this.domain[i].toString(sb, offset); + sb.append(" :> "); + sb = this.values[i].toString(sb, offset); + } + sb.append(")"); } - sb.append(">>"); - } - else { - sb = sb.append("("); - sb = this.domain[0].toString(sb, offset); - sb.append(" :> "); - sb = this.values[0].toString(sb, offset); + return sb; - for (int i = 1; i < len; i++) { - sb.append(" @@ "); - sb = this.domain[i].toString(sb, offset); - sb.append(" :> "); - sb = this.values[i].toString(sb, offset); - } - sb.append(")"); } - return sb; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/IntValue.java b/tlatools/src/tlc2/value/IntValue.java index cbba09d9eb1cb01f0322138d4d16f58b579c8a34..aaf785cf22308717cb9ff42f4f62d40dd75d075e 100644 --- a/tlatools/src/tlc2/value/IntValue.java +++ b/tlatools/src/tlc2/value/IntValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:08:05 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:08:05 PST by lamport // modified on Fri Aug 10 15:07:30 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.util.FP64; import util.Assert; @@ -12,7 +15,7 @@ public class IntValue extends Value { private static final IntValue[] cache; public int val; - + private IntValue(int i) { this.val = i; } static { @@ -34,8 +37,14 @@ public class IntValue extends Value { } // the number of bits needed to encode the value of this int - public final int nbits() { - return nbits(this.val); + public final int nbits() { + try { + return nbits(this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public static IntValue gen(int i) { @@ -46,63 +55,105 @@ public class IntValue extends Value { } public final int compareTo(Object obj) { - if (obj instanceof IntValue) { - return this.val - ((IntValue)obj).val; + try { + if (obj instanceof IntValue) { + return this.val - ((IntValue)obj).val; + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare integer " + ppr(this.toString()) + + " with non-integer:\n" + ppr(obj.toString())); + } + return 1; } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare integer " + ppr(this.toString()) + - " with non-integer:\n" + ppr(obj.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return 1; } - + public final boolean equals(Object obj) { - if (obj instanceof IntValue) { - return this.val == ((IntValue)obj).val; + try { + if (obj instanceof IntValue) { + return this.val == ((IntValue)obj).val; + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to check equality of integer " + ppr(this.toString()) + + " with non-integer:\n" + ppr(obj.toString())); + } + return ((ModelValue) obj).modelValueEquals(this); } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to check equality of integer " + ppr(this.toString()) + - " with non-integer:\n" + ppr(obj.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ((ModelValue) obj).modelValueEquals(this) ; } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the integer " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of the integer " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the integer " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the integer " + ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to appy EXCEPT construct to the integer " + - ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to appy EXCEPT construct to the integer " + + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the integer " + - ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the integer " + + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - Assert.fail("Attempted to compute the number of elements in the integer " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the integer " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { return true; } - + public final void normalize() { /*nop*/ } public final boolean isDefined() { return true; } @@ -110,20 +161,38 @@ public class IntValue extends Value { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return ((val instanceof IntValue) && - this.val == ((IntValue)val).val); + try { + return ((val instanceof IntValue) && + this.val == ((IntValue)val).val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint methods */ public final long fingerPrint(long fp) { - return FP64.Extend(FP64.Extend(fp, INTVALUE), this.val); + try { + return FP64.Extend(FP64.Extend(fp, INTVALUE), this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { return this; } /* The string representation. */ public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append(this.val); + try { + return sb.append(this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/IntervalValue.java b/tlatools/src/tlc2/value/IntervalValue.java index 5229578ba25ce79d3c5254cf2574045039b8b775..7342a96f0ef6720121e0a83e7aa1156ef305bda7 100644 --- a/tlatools/src/tlc2/value/IntervalValue.java +++ b/tlatools/src/tlc2/value/IntervalValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:12:59 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:12:59 PST by lamport // modified on Fri Aug 10 15:07:36 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.util.FP64; import util.Assert; @@ -21,115 +24,175 @@ implements Enumerable, Reducible { public final byte getKind() { return INTERVALVALUE; } public final int compareTo(Object obj) { - if (obj instanceof IntervalValue) { - IntervalValue intv = (IntervalValue)obj; - int cmp = this.size() - intv.size(); - if (cmp != 0) return cmp; - if (this.size() == 0) return 0; - return this.low - intv.low; + try { + if (obj instanceof IntervalValue) { + IntervalValue intv = (IntervalValue)obj; + int cmp = this.size() - intv.size(); + if (cmp != 0) return cmp; + if (this.size() == 0) return 0; + return this.low - intv.low; + } + // Well, we have to convert them to sets and compare. + return SetEnumValue.convert(this).compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - // Well, we have to convert them to sets and compare. - return SetEnumValue.convert(this).compareTo(obj); } public final boolean equals(Object obj) { - if (obj instanceof IntervalValue) { - IntervalValue intv = (IntervalValue)obj; - if (this.size() == 0) return intv.size() == 0; - return (this.low == intv.low) && (this.high == intv.high); + try { + if (obj instanceof IntervalValue) { + IntervalValue intv = (IntervalValue)obj; + if (this.size() == 0) return intv.size() == 0; + return (this.low == intv.low) && (this.high == intv.high); + } + // Well, we have to convert them to sets and compare. + return SetEnumValue.convert(this).equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - // Well, we have to convert them to sets and compare. - return SetEnumValue.convert(this).equals(obj); } - + public final boolean member(Value elem) { - if (elem instanceof IntValue) { - int x = ((IntValue)elem).val; - return (x >= low) && (x <= high); + try { + if (elem instanceof IntValue) { + int x = ((IntValue)elem).val; + return (x >= low) && (x <= high); + } + if ( (this.low <= this.high) + && ( !(elem instanceof ModelValue) + || (((ModelValue) elem).type != 0)) ) { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis in the integer interval " + ppr(this.toString())); + } + return false; } - if ( (this.low <= this.high) - && ( !(elem instanceof ModelValue) - || (((ModelValue) elem).type != 0)) ) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis in the integer interval " + ppr(this.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return false; } - public Value isSubsetEq(Value other) { - if (other instanceof IntervalValue) { - final IntervalValue iv = (IntervalValue) other; - if (iv.low <= low && iv.high >= high) { - return ValTrue; - } - } - return super.isSubsetEq(other); - } + public Value isSubsetEq(Value other) { + try { + if (other instanceof IntervalValue) { + final IntervalValue iv = (IntervalValue) other; + if (iv.low <= low && iv.high >= high) { + return ValTrue; + } + } + return super.isSubsetEq(other); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } public final boolean isFinite() { return true; } public final int size() { - if (this.high < this.low) return 0; - return this.high - this.low + 1; + try { + if (this.high < this.low) return 0; + return this.high - this.low + 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* Return this - val. */ public final Value diff(Value val) { - ValueVec diffElems = new ValueVec(); - for (int i = this.low; i <= this.high; i++) { - Value elem = IntValue.gen(i); - if (!val.member(elem)) diffElems.addElement(elem); + try { + ValueVec diffElems = new ValueVec(); + for (int i = this.low; i <= this.high; i++) { + Value elem = IntValue.gen(i); + if (!val.member(elem)) diffElems.addElement(elem); + } + return new SetEnumValue(diffElems, true); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetEnumValue(diffElems, true); } /* Return this \cap val. */ public final Value cap(Value val) { - ValueVec capElems = new ValueVec(); - for (int i = this.low; i <= this.high; i++) { - Value elem = IntValue.gen(i); - if (val.member(elem)) capElems.addElement(elem); + try { + ValueVec capElems = new ValueVec(); + for (int i = this.low; i <= this.high; i++) { + Value elem = IntValue.gen(i); + if (val.member(elem)) capElems.addElement(elem); + } + return new SetEnumValue(capElems, true); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetEnumValue(capElems, true); } /* Return this \cup val. */ public final Value cup(Value set) { - if (this.size() == 0) return set; + try { + if (this.size() == 0) return set; - if (set instanceof Reducible) { - ValueVec cupElems = new ValueVec(); - for (int i = this.low; i <= this.high; i++) { - cupElems.addElement(IntValue.gen(i)); - } - ValueEnumeration Enum = ((Enumerable)set).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!this.member(elem)) cupElems.addElement(elem); + if (set instanceof Reducible) { + ValueVec cupElems = new ValueVec(); + for (int i = this.low; i <= this.high; i++) { + cupElems.addElement(IntValue.gen(i)); + } + ValueEnumeration Enum = ((Enumerable)set).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!this.member(elem)) cupElems.addElement(elem); + } + return new SetEnumValue(cupElems, false); } - return new SetEnumValue(cupElems, false); + return new SetCupValue(this, set); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetCupValue(this, set); } public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the interval value " + - ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the interval value " + + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the interval value " + - ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the interval value " + + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final boolean isNormalized() { return true; } - + public final void normalize() { /*nop*/ } public final boolean isDefined() { return true; } @@ -137,38 +200,62 @@ implements Enumerable, Reducible { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return ((val instanceof IntervalValue) && - this.high == ((IntervalValue)val).high && - this.low == ((IntervalValue)val).low); + try { + return ((val instanceof IntervalValue) && + this.high == ((IntervalValue)val).high && + this.low == ((IntervalValue)val).low); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint method */ public final long fingerPrint(long fp) { - fp = FP64.Extend(fp, SETENUMVALUE); - fp = FP64.Extend(fp, this.size()) ; - for (int i = this.low; i <= this.high; i++) { - fp = FP64.Extend(fp, INTVALUE); - fp = FP64.Extend(fp, i); + try { + fp = FP64.Extend(fp, SETENUMVALUE); + fp = FP64.Extend(fp, this.size()) ; + for (int i = this.low; i <= this.high; i++) { + fp = FP64.Extend(fp, INTVALUE); + fp = FP64.Extend(fp, i); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return fp; } public final Value permute(MVPerm perm) { return this; } - + /* The string representation */ public final StringBuffer toString(StringBuffer sb, int offset) { - if (this.low <= this.high) { - return sb.append(this.low).append("..").append(this.high); + try { + if (this.low <= this.high) { + return sb.append(this.low).append("..").append(this.high); + } + return sb.append("{").append("}"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return sb.append("{").append("}"); } public final ValueEnumeration elements() { - return new Enumerator(); + try { + return new Enumerator(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + final class Enumerator implements ValueEnumeration { int index = low; @@ -176,11 +263,11 @@ implements Enumerable, Reducible { public final Value nextElement() { if (this.index <= high) { - return IntValue.gen(this.index++); + return IntValue.gen(this.index++); } return null; } - + } - + } diff --git a/tlatools/src/tlc2/value/LazyValue.java b/tlatools/src/tlc2/value/LazyValue.java index 453e67ef61fed807c99c6cd23f928e5e7f163135..bb378a6d629003219d9c3e20f746a4a6f3cf14ce 100644 --- a/tlatools/src/tlc2/value/LazyValue.java +++ b/tlatools/src/tlc2/value/LazyValue.java @@ -1,6 +1,7 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:08 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:08 PST by lamport // modified on Thu Feb 8 21:23:55 PST 2001 by yuanyu package tlc2.value; @@ -9,6 +10,8 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tla2sany.semantic.SemanticNode; import tlc2.util.Context; import util.Assert; @@ -37,56 +40,97 @@ public class LazyValue extends Value { public final byte getKind() { return LAZYVALUE; } public final int compareTo(Object obj) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to compare lazy values."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to compare lazy values."); + } + return this.val.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.compareTo(obj); } - + public final boolean equals(Object obj) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to check equality of lazy values."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to check equality of lazy values."); + } + return this.val.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.equals(obj); } public final boolean member(Value elem) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to check set membership of lazy values."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to check set membership of lazy values."); + } + return this.val.member(elem); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.member(elem); } public final boolean isFinite() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to check if a lazy value is a finite set."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to check if a lazy value is a finite set."); + } + return this.val.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.isFinite(); } - + public final Value takeExcept(ValueExcept ex) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); + } + return this.val.takeExcept(ex); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.takeExcept(ex); } public final Value takeExcept(ValueExcept[] exs) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); + } + return this.val.takeExcept(exs); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.takeExcept(exs); } public final int size() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to compute size of lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to compute size of lazy value."); + } + return this.val.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.size(); } - private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { this.val = (Value)ois.readObject(); } @@ -96,57 +140,99 @@ public class LazyValue extends Value { } oos.writeObject(this.val); } - + /* Nothing to normalize. */ public final boolean isNormalized() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to normalize lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to normalize lazy value."); + } + return this.val.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.isNormalized(); } - + public final void normalize() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to normalize lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to normalize lazy value."); + } + this.val.normalize(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.val.normalize(); } public final boolean isDefined() { return true; } public final Value deepCopy() { - if (this.val == null || this.val == ValUndef) return this; - return this.val.deepCopy(); + try { + if (this.val == null || this.val == ValUndef) return this; + return this.val.deepCopy(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean assignable(Value val) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to call assignable on lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to call assignable on lazy value."); + } + return this.val.assignable(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.assignable(val); } /* The fingerprint method */ public final long fingerPrint(long fp) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to fingerprint a lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to fingerprint a lazy value."); + } + return this.val.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.fingerPrint(fp); } public final Value permute(MVPerm perm) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to apply permutation to lazy value."); + try { + if (this.val == null || this.val == ValUndef) { + Assert.fail("Error(TLC): Attempted to apply permutation to lazy value."); + } + return this.val.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.permute(perm); } /* The string representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - if (this.val == null || this.val == ValUndef) { - return sb.append("<LAZY " + this.expr + ">"); + try { + if (this.val == null || this.val == ValUndef) { + return sb.append("<LAZY " + this.expr + ">"); + } + return this.val.toString(sb, offset); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.val.toString(sb, offset); } } diff --git a/tlatools/src/tlc2/value/MethodValue.java b/tlatools/src/tlc2/value/MethodValue.java index ae68732b5410e7c4aff2222f04428bc1fa4a2bc8..147d8f5173c4ce0a9ec1d907aadb33f8ad47515f 100644 --- a/tlatools/src/tlc2/value/MethodValue.java +++ b/tlatools/src/tlc2/value/MethodValue.java @@ -1,6 +1,7 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:00 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:00 PST by lamport // modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu package tlc2.value; @@ -9,100 +10,180 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import tlc2.output.EC; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.tool.EvalException; import util.Assert; import util.WrongInvocationException; public class MethodValue extends OpValue implements Applicable { public Method md; - + /* Constructor */ public MethodValue(Method md) { this.md = md; } public final byte getKind() { return METHODVALUE; } public final int compareTo(Object obj) { - Assert.fail("Attempted to compare operator " + this.toString() + - " with value:\n" + obj == null ? "null" : ppr(obj.toString())); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compare operator " + this.toString() + + " with value:\n" + obj == null ? "null" : ppr(obj.toString())); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - Assert.fail("Attempted to check equality of operator " + this.toString() + - " with value:\n" + obj == null ? "null" : ppr(obj.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check equality of operator " + this.toString() + + " with value:\n" + obj == null ? "null" : ppr(obj.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : ppr(elem.toString()) + - "\nis an element of operator " + this.toString()); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : ppr(elem.toString()) + + "\nis an element of operator " + this.toString()); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the operator " + this.toString() + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the operator " + this.toString() + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value apply(Value arg, int control) { - throw new WrongInvocationException("It is a TLC bug: Should use the other apply method."); + try { + throw new WrongInvocationException("It is a TLC bug: Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value apply(Value[] args, int control) { + try { Value res = null; - try + try { res = (Value)this.md.invoke(null, (Object[]) args); - } catch (Exception e) + } catch (Exception e) { - if (e instanceof InvocationTargetException) + if (e instanceof InvocationTargetException) { Throwable targetException = ((InvocationTargetException)e).getTargetException(); throw new EvalException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), targetException.getMessage()}); - } else + } else { Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), e.getMessage()}); } } return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value select(Value arg) { + try { throw new WrongInvocationException("It is a TLC bug: Attempted to call MethodValue.select()."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value takeExcept(ValueExcept ex) { - Assert.fail("Attempted to appy EXCEPT construct to the operator " + - this.toString() + "."); - return null; // make compiler happy + try { + Assert.fail("Attempted to appy EXCEPT construct to the operator " + + this.toString() + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value takeExcept(ValueExcept[] exs) { - Assert.fail("Attempted to apply EXCEPT construct to the operator " + - this.toString() + "."); - return null; // make compiler happy + try { + Assert.fail("Attempted to apply EXCEPT construct to the operator " + + this.toString() + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value getDomain() { - Assert.fail("Attempted to compute the domain of the operator " + - this.toString() + "."); - return EmptySet; // make compiler happy + try { + Assert.fail("Attempted to compute the domain of the operator " + + this.toString() + "."); + return EmptySet; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final int size() { - Assert.fail("Attempted to compute the number of elements in the operator " + - this.toString() + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the operator " + + this.toString() + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* Should never normalize an operator. */ public final boolean isNormalized() { + try { throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final void normalize() { + try { throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isDefined() { return true; } @@ -110,12 +191,24 @@ public class MethodValue extends OpValue implements Applicable { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { + try { throw new WrongInvocationException("It is a TLC bug: Attempted to initialize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* String representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append("<Java Method: " + this.md + ">"); + try { + return sb.append("<Java Method: " + this.md + ">"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/ModelValue.java b/tlatools/src/tlc2/value/ModelValue.java index 38fe2acf4b8106c03cf1f487b954d117a16f73f8..5027b8e2778385e51a8ff35f5c4de9b3e9e958b8 100644 --- a/tlatools/src/tlc2/value/ModelValue.java +++ b/tlatools/src/tlc2/value/ModelValue.java @@ -1,6 +1,7 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:32:25 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:32:25 PST by lamport // modified on Fri Aug 10 15:07:47 PDT 2001 by yuanyu /*************************************************************************** @@ -38,12 +39,14 @@ package tlc2.value; import java.util.Enumeration; import java.util.Hashtable; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.util.FP64; import util.Assert; import util.UniqueString; public class ModelValue extends Value { - + /** * A method to reset the model values * All callers should make sure that the model value class has been initialized @@ -58,11 +61,11 @@ public class ModelValue extends Value { /** * Workround to the static usage */ - static + static { init(); } - + private static int count; private static Hashtable mvTable; // SZ Mar 9, 2009: public accessed field, this will cause troubles @@ -71,7 +74,7 @@ public class ModelValue extends Value { public UniqueString val; public int index; public char type; // type = 0 means untyped. - + /* Constructor */ private ModelValue(String val) { // SZ 11.04.2009: changed access method @@ -79,7 +82,7 @@ public class ModelValue extends Value { this.index = count++; if ( (val.length() > 2) && (val.charAt(1) == '_')) { - this.type = val.charAt(0) ; + this.type = val.charAt(0) ; } else { this.type = 0 ; } ; } @@ -95,7 +98,7 @@ public class ModelValue extends Value { /* Collect all the model values defined thus far. */ public static void setValues() { - mvs = new ModelValue[mvTable.size()]; + mvs = new ModelValue[mvTable.size()]; Enumeration Enum = mvTable.elements(); while (Enum.hasMoreElements()) { ModelValue mv = (ModelValue)Enum.nextElement(); @@ -106,34 +109,46 @@ public class ModelValue extends Value { public final byte getKind() { return MODELVALUE; } public final int compareTo(Object obj) { - if (obj instanceof ModelValue) { - return this.val.compareTo(((ModelValue)obj).val); + try { + if (obj instanceof ModelValue) { + return this.val.compareTo(((ModelValue)obj).val); + } + return -1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return -1; } public final boolean equals(Object obj) { - if (this.type == 0) { - return (obj instanceof ModelValue && - this.val.equals(((ModelValue)obj).val)); - }; - if (obj instanceof ModelValue) { - ModelValue mobj = (ModelValue) obj ; - if ( (mobj.type == this.type) - || (mobj.type == 0) ) { - return mobj.val == this.val ; - } - else { - Assert.fail("Attempted to check equality " - + "of the differently-typed model values " - + ppr(this.toString()) + " and " - + ppr(mobj.toString())); - } ; - } ; - Assert.fail("Attempted to check equality of typed model value " - + ppr(this.toString()) + " and non-model value\n" - + ppr(obj.toString())) ; - return false; // make compiler happy + try { + if (this.type == 0) { + return (obj instanceof ModelValue && + this.val.equals(((ModelValue)obj).val)); + }; + if (obj instanceof ModelValue) { + ModelValue mobj = (ModelValue) obj ; + if ( (mobj.type == this.type) + || (mobj.type == 0) ) { + return mobj.val == this.val ; + } + else { + Assert.fail("Attempted to check equality " + + "of the differently-typed model values " + + ppr(this.toString()) + " and " + + ppr(mobj.toString())); + } ; + } ; + Assert.fail("Attempted to check equality of typed model value " + + ppr(this.toString()) + " and non-model value\n" + + ppr(obj.toString())) ; + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /************************************************************************* @@ -142,58 +157,100 @@ public class ModelValue extends Value { * this model value is untyped and raise an exception if it is typed. * *************************************************************************/ public final boolean modelValueEquals(Object obj){ - if (this.type != 0) { - Assert.fail("Attempted to check equality of the typed model value " - + ppr(this.toString()) + " and the non-model value\n" - + ppr(obj.toString())) ; + try { + if (this.type != 0) { + Assert.fail("Attempted to check equality of the typed model value " + + ppr(this.toString()) + " and the non-model value\n" + + ppr(obj.toString())) ; - } ; - return false ; + } ; + return false ; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean modelValueMember(Object obj){ - if (this.type != 0) { - Assert.fail("Attempted to check if the typed model value " - + ppr(this.toString()) - + " is an element of\n" - + ppr(obj.toString())) ; + try { + if (this.type != 0) { + Assert.fail("Attempted to check if the typed model value " + + ppr(this.toString()) + + " is an element of\n" + + ppr(obj.toString())) ; - } ; - return false ; + } ; + return false ; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the model value " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of the model value " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the model value " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the model value " + ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the model value " + - ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the model value " + + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } - + public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the model value " + - ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the model value " + + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - Assert.fail("Attempted to compute the number of elements in the model value " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the model value " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { return true; } @@ -205,24 +262,48 @@ public class ModelValue extends Value { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return ((val instanceof ModelValue) && - this.val.equals(((ModelValue)val).val)); + try { + return ((val instanceof ModelValue) && + this.val.equals(((ModelValue)val).val)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint methods */ public final long fingerPrint(long fp) { - return this.val.fingerPrint(FP64.Extend(fp, MODELVALUE)); + try { + return this.val.fingerPrint(FP64.Extend(fp, MODELVALUE)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - Value res = perm.get(this); - if (res == null) return this; - return res; + try { + Value res = perm.get(this); + if (res == null) return this; + return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The string representation. */ public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append(this.val); + try { + return sb.append(this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } diff --git a/tlatools/src/tlc2/value/OpLambdaValue.java b/tlatools/src/tlc2/value/OpLambdaValue.java index c7b35723637609a71d3fd9c401ff765eba2cced7..e90364ab5660a46789645f1eeec0bab87a00e7a9 100644 --- a/tlatools/src/tlc2/value/OpLambdaValue.java +++ b/tlatools/src/tlc2/value/OpLambdaValue.java @@ -1,12 +1,15 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport // modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu package tlc2.value; import tla2sany.semantic.FormalParamNode; import tla2sany.semantic.OpDefNode; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.tool.TLCState; import tlc2.tool.Tool; import tlc2.util.Context; @@ -19,10 +22,10 @@ public class OpLambdaValue extends OpValue implements Applicable { public Context con; public TLCState state; public TLCState pstate; - + /* Constructor */ public OpLambdaValue(OpDefNode op, Tool tool, Context con, - TLCState state, TLCState pstate) { + TLCState state, TLCState pstate) { this.opDef = op; this.tool = tool; this.state = state; @@ -33,83 +36,161 @@ public class OpLambdaValue extends OpValue implements Applicable { public final byte getKind() { return OPLAMBDAVALUE; } public final int compareTo(Object obj) { - Assert.fail("Attempted to compare operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compare operator " + ppr(this.toString()) + + " with value:\n" + ppr(obj.toString())); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) + + " with value:\n" + ppr(obj.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of operator " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of operator " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the operator " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the operator " + ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value apply(Value arg, int control) { + try { throw new WrongInvocationException("Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value apply(Value[] args, int control) { - int alen = this.opDef.getArity(); - if (alen != args.length) { - Assert.fail("Applying the operator " + ppr(this.toString()) + - " with wrong number of arguments."); + try { + int alen = this.opDef.getArity(); + if (alen != args.length) { + Assert.fail("Applying the operator " + ppr(this.toString()) + + " with wrong number of arguments."); + } + Context c1 = this.con; + FormalParamNode[] formals = this.opDef.getParams(); + for (int i = 0; i < alen; i++) { + c1 = c1.cons(formals[i], args[i]); + } + return this.tool.eval(this.opDef.getBody(), c1, this.state, this.pstate, + control); } - Context c1 = this.con; - FormalParamNode[] formals = this.opDef.getParams(); - for (int i = 0; i < alen; i++) { - c1 = c1.cons(formals[i], args[i]); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.tool.eval(this.opDef.getBody(), c1, this.state, this.pstate, - control); } public final Value select(Value arg) { + try { throw new WrongInvocationException("Error(TLC): attempted to call OpLambdaValue.select()."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value takeExcept(ValueExcept ex) { - Assert.fail("Attempted to appy EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy + try { + Assert.fail("Attempted to appy EXCEPT construct to the operator " + + ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value takeExcept(ValueExcept[] exs) { - Assert.fail("Attempted to apply EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy + try { + Assert.fail("Attempted to apply EXCEPT construct to the operator " + + ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value getDomain() { - Assert.fail("Attempted to compute the domain of the operator " + - ppr(this.toString()) + "."); - return EmptySet; // make compiler happy + try { + Assert.fail("Attempted to compute the domain of the operator " + + ppr(this.toString()) + "."); + return EmptySet; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final int size() { - Assert.fail("Attempted to compute the number of elements in the operator " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the operator " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* Should never normalize an operator. */ public final boolean isNormalized() { + try { throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final void normalize() { + try { throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isDefined() { return true; } @@ -117,13 +198,25 @@ public class OpLambdaValue extends OpValue implements Applicable { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { + try { throw new WrongInvocationException("Should not initialize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* String representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - String opName = this.opDef.getName().toString(); - return sb.append("<Operator ").append(opName).append(">"); + try { + String opName = this.opDef.getName().toString(); + return sb.append("<Operator ").append(opName).append(">"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/OpRcdValue.java b/tlatools/src/tlc2/value/OpRcdValue.java index fe25a594cf148fc35178e8b691bd513f50440c06..fd7d7f6f0eb4b802e7e05694682614e4bee433ff 100644 --- a/tlatools/src/tlc2/value/OpRcdValue.java +++ b/tlatools/src/tlc2/value/OpRcdValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:01 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:01 PST by lamport // modified on Sat Nov 13 12:43:44 PST 1999 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.util.Vect; import util.Assert; import util.WrongInvocationException; @@ -12,7 +15,7 @@ import util.WrongInvocationException; public class OpRcdValue extends OpValue implements Applicable { public Vect domain; public Vect values; - + /* Constructor */ public OpRcdValue() { this.domain = new Vect(); @@ -27,147 +30,249 @@ public class OpRcdValue extends OpValue implements Applicable { public final byte getKind() { return OPRCDVALUE; } public final int compareTo(Object obj) { - Assert.fail("Attempted to compare operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compare operator " + ppr(this.toString()) + + " with value:\n" + ppr(obj.toString())); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) + + " with value:\n" + ppr(obj.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of operator " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of operator " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the operator " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the operator " + ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final void addLine(Vect vs) { - int len = vs.size(); - Value[] args = new Value[len-2]; - for (int i = 0; i < len-2; i++) { - args[i] = (Value)vs.elementAt(i+1); + try { + int len = vs.size(); + Value[] args = new Value[len-2]; + for (int i = 0; i < len-2; i++) { + args[i] = (Value)vs.elementAt(i+1); + } + this.domain.addElement(args); + this.values.addElement(vs.elementAt(len-1)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.domain.addElement(args); - this.values.addElement(vs.elementAt(len-1)); } public final Value apply(Value arg, int control) { + try { throw new WrongInvocationException("Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value apply(Value[] args, int control) { - int sz = this.domain.size(); - for (int i = 0; i < sz; i++) { - Value[] vals = (Value[])this.domain.elementAt(i); - if (args.length != vals.length) { - Assert.fail("Attempted to apply the operator " + ppr(this.toString()) + - "\nwith wrong number of arguments."); + try { + int sz = this.domain.size(); + for (int i = 0; i < sz; i++) { + Value[] vals = (Value[])this.domain.elementAt(i); + if (args.length != vals.length) { + Assert.fail("Attempted to apply the operator " + ppr(this.toString()) + + "\nwith wrong number of arguments."); + } + boolean matched = true; + for (int j = 0; j < vals.length; j++) { + matched = vals[j].equals(args[j]); + if (!matched) break; + } + if (matched) { + return (Value)this.values.elementAt(i); + } } - boolean matched = true; - for (int j = 0; j < vals.length; j++) { - matched = vals[j].equals(args[j]); - if (!matched) break; - } - if (matched) { - return (Value)this.values.elementAt(i); + // Generate the error message: + String msg = "Attempted to apply operator:\n" + ppr(this.toString()) + + "\nto arguments ("; + if (args.length > 0) msg += args[0]; + for (int i = 1; i < args.length; i++) { + msg += ", " + args[i]; } + Assert.fail(msg + "), which is undefined."); + return null; // make compiler happy } - // Generate the error message: - String msg = "Attempted to apply operator:\n" + ppr(this.toString()) + - "\nto arguments ("; - if (args.length > 0) msg += args[0]; - for (int i = 1; i < args.length; i++) { - msg += ", " + args[i]; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - Assert.fail(msg + "), which is undefined."); - return null; // make compiler happy } public final Value select(Value arg) { - Assert.fail("Attempted to call OpRcdValue.select(). This is a TLC bug."); - return null; // make compiler happy + try { + Assert.fail("Attempted to call OpRcdValue.select(). This is a TLC bug."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value takeExcept(ValueExcept ex) { - Assert.fail("Attempted to appy EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy + try { + Assert.fail("Attempted to appy EXCEPT construct to the operator " + + ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value takeExcept(ValueExcept[] exs) { - Assert.fail("Attempted to apply EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy + try { + Assert.fail("Attempted to apply EXCEPT construct to the operator " + + ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value getDomain() { - Assert.fail("Attempted to compute the domain of the operator " + - ppr(this.toString()) + "."); - return EmptySet; // make compiler happy + try { + Assert.fail("Attempted to compute the domain of the operator " + + ppr(this.toString()) + "."); + return EmptySet; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final int size() { - Assert.fail("Attempted to compute the number of elements in the operator " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the operator " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* Should never normalize an operator. */ public final boolean isNormalized() { + try { throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final void normalize() { + try { throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.values.size(); i++) { - defined = defined && ((Value)this.values.elementAt(i)).isDefined(); + try { + boolean defined = true; + for (int i = 0; i < this.values.size(); i++) { + defined = defined && ((Value)this.values.elementAt(i)).isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return defined; } - + public final Value deepCopy() { return this; } public final boolean assignable(Value val) { + try { throw new WrongInvocationException("Should not initialize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* Pretty-printing */ public final StringBuffer toString(StringBuffer sb, int offset) { - sb.append("{ "); - if (this.values.size() != 0) { - sb.append("<"); - Value[] args = (Value[])this.domain.elementAt(0); - for (int j = 0; j < args.length; j++) { - sb = args[j].toString(sb, offset); - sb.append(", "); + try { + sb.append("{ "); + if (this.values.size() != 0) { + sb.append("<"); + Value[] args = (Value[])this.domain.elementAt(0); + for (int j = 0; j < args.length; j++) { + sb = args[j].toString(sb, offset); + sb.append(", "); + } + sb = ((Value)this.values.elementAt(0)).toString(sb, offset); + sb.append(">"); } - sb = ((Value)this.values.elementAt(0)).toString(sb, offset); - sb.append(">"); - } - for (int i = 1; i < this.values.size(); i++) { - sb.append(", <"); - Value[] args = (Value[])this.domain.elementAt(i); - for (int j = 0; j < args.length; j++) { - sb = args[j].toString(sb, offset); - sb.append(", "); + for (int i = 1; i < this.values.size(); i++) { + sb.append(", <"); + Value[] args = (Value[])this.domain.elementAt(i); + for (int j = 0; j < args.length; j++) { + sb = args[j].toString(sb, offset); + sb.append(", "); + } + sb = ((Value)this.values.elementAt(i)).toString(sb, offset); + sb.append(">"); } - sb = ((Value)this.values.elementAt(i)).toString(sb, offset); - sb.append(">"); + return sb.append("}"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return sb.append("}"); } } diff --git a/tlatools/src/tlc2/value/RecordValue.java b/tlatools/src/tlc2/value/RecordValue.java index 3995be054ac126d043eec12315fdd7c6ea4302ec..1d10c9e6832a16710eb9dd339132d72c6ce7f016 100644 --- a/tlatools/src/tlc2/value/RecordValue.java +++ b/tlatools/src/tlc2/value/RecordValue.java @@ -1,12 +1,15 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:15:47 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:15:47 PST by lamport // modified on Fri Aug 10 15:09:07 PDT 2001 by yuanyu package tlc2.value; import java.util.Arrays; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.output.EC; import tlc2.output.MP; import tlc2.util.FP64; @@ -17,7 +20,7 @@ public class RecordValue extends Value implements Applicable { public UniqueString[] names; // the field names public Value[] values; // the field values private boolean isNorm; - + /* Constructor */ public RecordValue(UniqueString[] names, Value[] values, boolean isNorm) { this.names = names; @@ -28,93 +31,123 @@ public class RecordValue extends Value implements Applicable { public final byte getKind() { return RECORDVALUE; } public final int compareTo(Object obj) { - RecordValue rcd = convert(obj); - if (rcd == null) { - if (obj instanceof ModelValue) return 1; - Assert.fail("Attempted to compare record:\n" + ppr(this.toString()) + - "\nwith non-record\n" + ppr(obj.toString())); - } - this.normalize(); - rcd.normalize(); - int len = this.names.length; - int cmp = len - rcd.names.length; - if (cmp == 0) { - for (int i = 0; i < len; i++) { - cmp = this.names[i].compareTo(rcd.names[i]); - if (cmp != 0) break; - cmp = this.values[i].compareTo(rcd.values[i]); - if (cmp != 0) break; + try { + RecordValue rcd = convert(obj); + if (rcd == null) { + if (obj instanceof ModelValue) return 1; + Assert.fail("Attempted to compare record:\n" + ppr(this.toString()) + + "\nwith non-record\n" + ppr(obj.toString())); + } + this.normalize(); + rcd.normalize(); + int len = this.names.length; + int cmp = len - rcd.names.length; + if (cmp == 0) { + for (int i = 0; i < len; i++) { + cmp = this.names[i].compareTo(rcd.names[i]); + if (cmp != 0) break; + cmp = this.values[i].compareTo(rcd.values[i]); + if (cmp != 0) break; + } } + return cmp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return cmp; } public final boolean equals(Object obj) { - RecordValue rcd = convert(obj); - if (rcd == null) { - if (obj instanceof ModelValue) - return ((ModelValue) obj).modelValueEquals(this) ; - Assert.fail("Attempted to check equality of record:\n" + ppr(this.toString()) + - "\nwith non-record\n" + ppr(obj.toString())); - } - this.normalize(); - rcd.normalize(); - int len = this.names.length; - if (len != rcd.names.length) return false; - for (int i = 0; i < len; i++) { - if ((!(this.names[i].equals(rcd.names[i]))) || - (!(this.values[i].equals(rcd.values[i])))) - return false; - } - return true; + try { + RecordValue rcd = convert(obj); + if (rcd == null) { + if (obj instanceof ModelValue) + return ((ModelValue) obj).modelValueEquals(this) ; + Assert.fail("Attempted to check equality of record:\n" + ppr(this.toString()) + + "\nwith non-record\n" + ppr(obj.toString())); + } + this.normalize(); + rcd.normalize(); + int len = this.names.length; + if (len != rcd.names.length) return false; + for (int i = 0; i < len; i++) { + if ((!(this.names[i].equals(rcd.names[i]))) || + (!(this.values[i].equals(rcd.values[i])))) + return false; + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check if element:\n" + ppr(elem.toString()) + - "\nis in the record:\n" + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if element:\n" + ppr(elem.toString()) + + "\nis in the record:\n" + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { return true; } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - int rlen = this.names.length; - Value[] newValues = new Value[rlen]; - Value arcVal = ex.path[ex.idx]; - if (arcVal instanceof StringValue) { - UniqueString arc = ((StringValue)arcVal).val; - for (int i = 0; i < rlen; i++) { - if (this.names[i].equals(arc)) { - ex.idx++; - newValues[i] = this.values[i].takeExcept(ex); - } - else { - newValues[i] = this.values[i]; - } - } - UniqueString[] newNames = this.names; - if (!this.isNorm) { - newNames = new UniqueString[rlen]; - for (int i = 0; i < rlen; i++) { - newNames[i] = this.names[i]; - } - } - return new RecordValue(newNames, newValues, this.isNorm); - } - else { - MP.printWarning(EC.TLC_WRONG_RECORD_FIELD_NAME, new String[]{ppr(arcVal.toString())}); + try { + if (ex.idx < ex.path.length) { + int rlen = this.names.length; + Value[] newValues = new Value[rlen]; + Value arcVal = ex.path[ex.idx]; + if (arcVal instanceof StringValue) { + UniqueString arc = ((StringValue)arcVal).val; + for (int i = 0; i < rlen; i++) { + if (this.names[i].equals(arc)) { + ex.idx++; + newValues[i] = this.values[i].takeExcept(ex); + } + else { + newValues[i] = this.values[i]; + } + } + UniqueString[] newNames = this.names; + if (!this.isNorm) { + newNames = new UniqueString[rlen]; + for (int i = 0; i < rlen; i++) { + newNames[i] = this.names[i]; + } + } + return new RecordValue(newNames, newValues, this.isNorm); + } + else { + MP.printWarning(EC.TLC_WRONG_RECORD_FIELD_NAME, new String[]{ppr(arcVal.toString())}); + } } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - Value res = this; - for (int i = 0; i < exs.length; i++) { - res = res.takeExcept(exs[i]); + try { + Value res = this; + for (int i = 0; i < exs.length; i++) { + res = res.takeExcept(exs[i]); + } + return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return res; } /* @@ -126,212 +159,292 @@ public class RecordValue extends Value implements Applicable { return (RecordValue)val; } else if (val instanceof FcnRcdValue || - val instanceof FcnLambdaValue) { + val instanceof FcnLambdaValue) { FcnRcdValue fcn = FcnRcdValue.convert(val); if (fcn == null || fcn.domain == null) return null; fcn.normalize(); UniqueString[] vars = new UniqueString[fcn.domain.length]; for (int i = 0; i < fcn.domain.length; i++) { - if (!(fcn.domain[i] instanceof StringValue)) { - return null; - } - vars[i] = ((StringValue)fcn.domain[i]).getVal(); + if (!(fcn.domain[i] instanceof StringValue)) { + return null; + } + vars[i] = ((StringValue)fcn.domain[i]).getVal(); } return new RecordValue(vars, fcn.values, fcn.isNormalized()); } else if ((val instanceof TupleValue) && - ((TupleValue)val).size() == 0) { + ((TupleValue)val).size() == 0) { return EmptyRcd; } // return null if not convertable return null; } - - public final int size() { return this.names.length; } + + public final int size() { + try { + return this.names.length; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } public final Value apply(Value arg, int control) { - if (!(arg instanceof StringValue)) { - Assert.fail("Attempted to apply record to a non-string value " + - ppr(arg.toString()) + "."); - } - UniqueString name = ((StringValue)arg).getVal(); - int rlen = this.names.length; - for (int i = 0; i < rlen; i++) { - if (name.equals(this.names[i])) { - return this.values[i]; + try { + if (!(arg instanceof StringValue)) { + Assert.fail("Attempted to apply record to a non-string value " + + ppr(arg.toString()) + "."); } + UniqueString name = ((StringValue)arg).getVal(); + int rlen = this.names.length; + for (int i = 0; i < rlen; i++) { + if (name.equals(this.names[i])) { + return this.values[i]; + } + } + Assert.fail("Attempted to apply the record\n" + ppr(this.toString()) + + "\nto nonexistent record field " + name + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - Assert.fail("Attempted to apply the record\n" + ppr(this.toString()) + - "\nto nonexistent record field " + name + "."); - return null; // make compiler happy } public final Value apply(Value[] args, int control) { - if (args.length != 1) { - Assert.fail("Attempted to apply record to more than one arguments."); + try { + if (args.length != 1) { + Assert.fail("Attempted to apply record to more than one arguments."); + } + return this.apply(args[0], control); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.apply(args[0], control); } /* This method returns the named component of the record. */ public final Value select(Value arg) { - if (!(arg instanceof StringValue)) { - Assert.fail("Attempted to apply record to a non-string argument " + - ppr(arg.toString()) + "."); - } - UniqueString name = ((StringValue)arg).getVal(); - int rlen = this.names.length; - for (int i = 0; i < rlen; i++) { - if (name.equals(this.names[i])) { - return this.values[i]; + try { + if (!(arg instanceof StringValue)) { + Assert.fail("Attempted to apply record to a non-string argument " + + ppr(arg.toString()) + "."); } + UniqueString name = ((StringValue)arg).getVal(); + int rlen = this.names.length; + for (int i = 0; i < rlen; i++) { + if (name.equals(this.names[i])) { + return this.values[i]; + } + } + return null; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return null; } public final Value getDomain() { - Value[] dElems = new Value[this.names.length]; - for (int i = 0; i < this.names.length; i++) { - dElems[i] = new StringValue(this.names[i]); + try { + Value[] dElems = new Value[this.names.length]; + for (int i = 0; i < this.names.length; i++) { + dElems[i] = new StringValue(this.names[i]); + } + return new SetEnumValue(dElems, this.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetEnumValue(dElems, this.isNormalized()); } public final boolean assign(UniqueString name, Value val) { - for (int i = 0; i < this.names.length; i++) { - if (name.equals(this.names[i])) { - if (this.values[i] == ValUndef || - this.values[i].equals(val)) { - this.values[i] = val; - return true; - } - return false; + try { + for (int i = 0; i < this.names.length; i++) { + if (name.equals(this.names[i])) { + if (this.values[i] == ValUndef || + this.values[i].equals(val)) { + this.values[i] = val; + return true; + } + return false; + } } + Assert.fail("Attempted to assign to nonexistent record field " + name + "."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - Assert.fail("Attempted to assign to nonexistent record field " + name + "."); - return false; // make compiler happy } public final boolean isNormalized() { return this.isNorm; } - + public final void normalize() { - if (!this.isNorm) { - int len = this.names.length; - for (int i = 1; i < len; i++) { - int cmp = this.names[0].compareTo(this.names[i]); - if (cmp == 0) { - Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); - } - else if (cmp > 0) { - UniqueString ts = this.names[0]; - this.names[0] = this.names[i]; - this.names[i] = ts; - Value tv = this.values[0]; - this.values[0] = this.values[i]; - this.values[i] = tv; - } - } - for (int i = 2; i < len; i++) { - int j = i; - UniqueString st = this.names[i]; - Value val = this.values[i]; - int cmp; - while ((cmp = st.compareTo(this.names[j-1])) < 0) { - this.names[j] = this.names[j-1]; - this.values[j] = this.values[j-1]; - j--; - } - if (cmp == 0) { - Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); - } - this.names[j] = st; - this.values[j] = val; + try { + if (!this.isNorm) { + int len = this.names.length; + for (int i = 1; i < len; i++) { + int cmp = this.names[0].compareTo(this.names[i]); + if (cmp == 0) { + Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); + } + else if (cmp > 0) { + UniqueString ts = this.names[0]; + this.names[0] = this.names[i]; + this.names[i] = ts; + Value tv = this.values[0]; + this.values[0] = this.values[i]; + this.values[i] = tv; + } + } + for (int i = 2; i < len; i++) { + int j = i; + UniqueString st = this.names[i]; + Value val = this.values[i]; + int cmp; + while ((cmp = st.compareTo(this.names[j-1])) < 0) { + this.names[j] = this.names[j-1]; + this.values[j] = this.values[j-1]; + j--; + } + if (cmp == 0) { + Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); + } + this.names[j] = st; + this.values[j] = val; + } + this.isNorm = true; } - this.isNorm = true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.values.length; i++) { - defined = defined && this.values[i].isDefined(); + try { + boolean defined = true; + for (int i = 0; i < this.values.length; i++) { + defined = defined && this.values[i].isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return defined; } public final Value deepCopy() { - Value[] vals = new Value[this.values.length]; - for (int i = 0; i < this.values.length; i++) { - vals[i] = this.values[i].deepCopy(); - } - // Following code modified 16 June 2015 by adding Arrays.copyOf to fix - // the following bug that seems to have manifested itself only in TLC.Print and - // TLC.PrintT: Calling normalize on the original modifies the - // order of the names array in the deepCopy (and vice-versa) without doing the - // corresponding modification on the values array. Thus, the names are - // copied too to prevent any modification/normalization done to the - // original to appear in the deepCopy. - return new RecordValue(Arrays.copyOf(this.names, this.names.length), vals, this.isNorm); + try { + Value[] vals = new Value[this.values.length]; + for (int i = 0; i < this.values.length; i++) { + vals[i] = this.values[i].deepCopy(); + } + // Following code modified 16 June 2015 by adding Arrays.copyOf to fix + // the following bug that seems to have manifested itself only in TLC.Print and + // TLC.PrintT: Calling normalize on the original modifies the + // order of the names array in the deepCopy (and vice-versa) without doing the + // corresponding modification on the values array. Thus, the names are + // copied too to prevent any modification/normalization done to the + // original to appear in the deepCopy. + return new RecordValue(Arrays.copyOf(this.names, this.names.length), vals, this.isNorm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean assignable(Value val) { - boolean canAssign = ((val instanceof RecordValue) && - this.names.length == ((RecordValue)val).names.length); - if (!canAssign) return false; - for (int i = 0; i < this.values.length; i++) { - canAssign = (canAssign && - this.names[i].equals(((RecordValue)val).names[i]) && - this.values[i].assignable(((RecordValue)val).values[i])); - } - return canAssign; + try { + boolean canAssign = ((val instanceof RecordValue) && + this.names.length == ((RecordValue)val).names.length); + if (!canAssign) return false; + for (int i = 0; i < this.values.length; i++) { + canAssign = (canAssign && + this.names[i].equals(((RecordValue)val).names[i]) && + this.values[i].assignable(((RecordValue)val).values[i])); + } + return canAssign; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + /* The fingerprint methods. */ public final long fingerPrint(long fp) { - this.normalize(); - int rlen = this.names.length; - fp = FP64.Extend(fp, FCNRCDVALUE); - fp = FP64.Extend(fp, rlen); - for (int i = 0; i < rlen; i++) { - String str = this.names[i].toString(); - fp = FP64.Extend(fp, STRINGVALUE); - fp = FP64.Extend(fp, str.length()); - fp = FP64.Extend(fp, str); - fp = this.values[i].fingerPrint(fp); - } - return fp; + try { + this.normalize(); + int rlen = this.names.length; + fp = FP64.Extend(fp, FCNRCDVALUE); + fp = FP64.Extend(fp, rlen); + for (int i = 0; i < rlen; i++) { + String str = this.names[i].toString(); + fp = FP64.Extend(fp, STRINGVALUE); + fp = FP64.Extend(fp, str.length()); + fp = FP64.Extend(fp, str); + fp = this.values[i].fingerPrint(fp); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.normalize(); - int rlen = this.names.length; - Value[] vals = new Value[rlen]; - boolean changed = false; - for (int i = 0; i < rlen; i++) { - vals[i] = this.values[i].permute(perm); - changed = changed || (vals[i] != this.values[i]); - } - if (changed) { - return new RecordValue(this.names, vals, true); - } - return this; + try { + this.normalize(); + int rlen = this.names.length; + Value[] vals = new Value[rlen]; + boolean changed = false; + for (int i = 0; i < rlen; i++) { + vals[i] = this.values[i].permute(perm); + changed = changed || (vals[i] != this.values[i]); + } + if (changed) { + return new RecordValue(this.names, vals, true); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The string representation */ public final StringBuffer toString(StringBuffer sb, int offset) { - int len = this.names.length; + try { + int len = this.names.length; - sb.append("["); - if (len > 0) { - sb.append(this.names[0] + " |-> "); - sb = this.values[0].toString(sb, offset); + sb.append("["); + if (len > 0) { + sb.append(this.names[0] + " |-> "); + sb = this.values[0].toString(sb, offset); + } + for (int i = 1; i < len; i++) { + sb.append(", "); + sb.append(this.names[i] + " |-> "); + sb = this.values[i].toString(sb, offset); + } + return sb.append("]"); } - for (int i = 1; i < len; i++) { - sb.append(", "); - sb.append(this.names[i] + " |-> "); - sb = this.values[i].toString(sb, offset); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return sb.append("]"); } } diff --git a/tlatools/src/tlc2/value/SetCapValue.java b/tlatools/src/tlc2/value/SetCapValue.java index 5d37c7b163b18d5a0914ece8ea749cb31ec0a7ce..a54011a541a8fac6bc99a44937e6a5b5a0438597 100644 --- a/tlatools/src/tlc2/value/SetCapValue.java +++ b/tlatools/src/tlc2/value/SetCapValue.java @@ -1,17 +1,20 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport // modified on Fri Aug 10 15:09:21 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import util.Assert; public class SetCapValue extends EnumerableValue implements Enumerable { public Value set1; public Value set2; protected SetEnumValue capSet; - + /* Constructor */ public SetCapValue(Value set1, Value set2) { this.set1 = set1; @@ -22,79 +25,159 @@ public class SetCapValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SETCAPVALUE; } public final int compareTo(Object obj) { - this.convertAndCache(); - return this.capSet.compareTo(obj); + try { + this.convertAndCache(); + return this.capSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - this.convertAndCache(); - return this.capSet.equals(obj); + try { + this.convertAndCache(); + return this.capSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - return (this.set1.member(elem) && this.set2.member(elem)); + try { + return (this.set1.member(elem) && this.set2.member(elem)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - if (!this.set1.isFinite() && !this.set2.isFinite()) { - Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite."); + try { + if (!this.set1.isFinite() && !this.set2.isFinite()) { + Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite."); + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - this.convertAndCache(); - return this.capSet.size(); + try { + this.convertAndCache(); + return this.capSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { - if (this.capSet == null || this.capSet == DummyEnum) { - return (this.set1.isNormalized() && this.set2.isNormalized()); + try { + if (this.capSet == null || this.capSet == DummyEnum) { + return (this.set1.isNormalized() && this.set2.isNormalized()); + } + return this.capSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.capSet.isNormalized(); } - + public final void normalize() { - if (this.capSet == null || this.capSet == DummyEnum) { - this.set1.normalize(); - this.set2.normalize(); + try { + if (this.capSet == null || this.capSet == DummyEnum) { + this.set1.normalize(); + this.set2.normalize(); + } + else { + this.capSet.normalize(); + } } - else { - this.capSet.normalize(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final boolean isDefined() { - return this.set1.isDefined() && this.set2.isDefined(); + try { + return this.set1.isDefined() && this.set2.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value deepCopy() { return this; } - public final boolean assignable(Value val) { return this.equals(val); } + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } /* The fingerprint methods */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.capSet.fingerPrint(fp); + try { + this.convertAndCache(); + return this.capSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.capSet.permute(perm); + try { + this.convertAndCache(); + return this.capSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -104,38 +187,50 @@ public class SetCapValue extends EnumerableValue implements Enumerable { else if (this.capSet == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.capSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.capSet == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.capSet == DummyEnum) { this.capSet = val; } + if (this.capSet == DummyEnum) { this.capSet = val; } } } } - + /* String representation of this value. */ public final StringBuffer toString(StringBuffer sb, int offset) { try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); + try { + if (expand) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); + } } - } - catch (Throwable e) { /*SKIP*/ } + catch (Throwable e) { /*SKIP*/ } - sb = this.set1.toString(sb, offset); - sb = sb.append(" \\cap "); - sb = this.set2.toString(sb, offset); - return sb; + sb = this.set1.toString(sb, offset); + sb = sb.append(" \\cap "); + sb = this.set2.toString(sb, offset); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final ValueEnumeration elements() { - if (this.capSet == null || this.capSet == DummyEnum) { - return new Enumerator(); + try { + if (this.capSet == null || this.capSet == DummyEnum) { + return new Enumerator(); + } + return this.capSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.capSet.elements(); } final class Enumerator implements ValueEnumeration { @@ -144,30 +239,30 @@ public class SetCapValue extends EnumerableValue implements Enumerable { public Enumerator() { if (set1 instanceof Enumerable) { - this.enum1 = ((Enumerable)set1).elements(); - this.set = set2; + this.enum1 = ((Enumerable)set1).elements(); + this.set = set2; } else if (set2 instanceof Enumerable) { - this.enum1 = ((Enumerable)set2).elements(); - this.set = set1; + this.enum1 = ((Enumerable)set2).elements(); + this.set = set1; } else { - Assert.fail("Attempted to enumerate S \\cap T when neither S:\n" + - ppr(set1.toString()) + "\nnor T:\n" + ppr(set2.toString()) + - "\nis enumerable"); + Assert.fail("Attempted to enumerate S \\cap T when neither S:\n" + + ppr(set1.toString()) + "\nnor T:\n" + ppr(set2.toString()) + + "\nis enumerable"); } } public final void reset() { this.enum1.reset(); } - + public final Value nextElement() { Value elem = this.enum1.nextElement(); while (elem != null) { - if (this.set.member(elem)) return elem; - elem = this.enum1.nextElement(); + if (this.set.member(elem)) return elem; + elem = this.enum1.nextElement(); } return null; } } - + } diff --git a/tlatools/src/tlc2/value/SetCupValue.java b/tlatools/src/tlc2/value/SetCupValue.java index 640996544a4a5fcf293a84baec681af8362bcca5..f2280615128b25cd438c03c3f63ec8d672d553f7 100644 --- a/tlatools/src/tlc2/value/SetCupValue.java +++ b/tlatools/src/tlc2/value/SetCupValue.java @@ -1,17 +1,20 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport // modified on Fri Aug 10 15:09:28 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import util.Assert; public class SetCupValue extends EnumerableValue implements Enumerable { public Value set1; public Value set2; - protected SetEnumValue cupSet; - + protected SetEnumValue cupSet; + /* Constructor */ public SetCupValue(Value set1, Value set2) { this.set1 = set1; @@ -22,73 +25,151 @@ public class SetCupValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SETCUPVALUE; } public final int compareTo(Object obj) { - this.convertAndCache(); - return this.cupSet.compareTo(obj); + try { + this.convertAndCache(); + return this.cupSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - this.convertAndCache(); - return this.cupSet.equals(obj); + try { + this.convertAndCache(); + return this.cupSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - return this.set1.member(elem) || this.set2.member(elem); + try { + return this.set1.member(elem) || this.set2.member(elem); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - return this.set1.isFinite() && this.set2.isFinite(); + try { + return this.set1.isFinite() && this.set2.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - this.convertAndCache(); - return this.cupSet.size(); + try { + this.convertAndCache(); + return this.cupSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { - return (this.cupSet != null && - this.cupSet != DummyEnum && - this.cupSet.isNormalized()); + try { + return (this.cupSet != null && + this.cupSet != DummyEnum && + this.cupSet.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final void normalize() { - if (this.cupSet != null && this.cupSet != DummyEnum) { - this.cupSet.normalize(); + try { + if (this.cupSet != null && this.cupSet != DummyEnum) { + this.cupSet.normalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final boolean isDefined() { - return this.set1.isDefined() && this.set2.isDefined(); + try { + return this.set1.isDefined() && this.set2.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint methods */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.cupSet.fingerPrint(fp); + try { + this.convertAndCache(); + return this.cupSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.cupSet.permute(perm); + try { + this.convertAndCache(); + return this.cupSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -98,38 +179,51 @@ public class SetCupValue extends EnumerableValue implements Enumerable { else if (this.cupSet == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.cupSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.cupSet == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.cupSet == DummyEnum) { this.cupSet = val; } + if (this.cupSet == DummyEnum) { this.cupSet = val; } } } } - + /* String representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); + try { + if (expand) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); + } } - } - catch (Throwable e) { /*SKIP*/ } + catch (Throwable e) { /*SKIP*/ } - sb = this.set1.toString(sb, offset); - sb = sb.append(" \\cup "); - sb = this.set2.toString(sb, offset); - return sb; + sb = this.set1.toString(sb, offset); + sb = sb.append(" \\cup "); + sb = this.set2.toString(sb, offset); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final ValueEnumeration elements() { - if (this.cupSet == null || this.cupSet == DummyEnum) { - return new Enumerator(); + try { + if (this.cupSet == null || this.cupSet == DummyEnum) { + return new Enumerator(); + } + return this.cupSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.cupSet.elements(); + } final class Enumerator implements ValueEnumeration { @@ -138,14 +232,14 @@ public class SetCupValue extends EnumerableValue implements Enumerable { public Enumerator() { if ((set1 instanceof Enumerable) && - (set2 instanceof Enumerable)) { - this.enum1 = ((Enumerable)set1).elements(); - this.enum2 = ((Enumerable)set2).elements(); + (set2 instanceof Enumerable)) { + this.enum1 = ((Enumerable)set1).elements(); + this.enum2 = ((Enumerable)set2).elements(); } else { - Assert.fail("Attempted to enumerate S \\cup T when S:\n" + - ppr(set1.toString()) + "\nand T:\n" + ppr(set2.toString()) + - "\nare not both enumerable"); + Assert.fail("Attempted to enumerate S \\cup T when S:\n" + + ppr(set1.toString()) + "\nand T:\n" + ppr(set2.toString()) + + "\nare not both enumerable"); } } @@ -153,7 +247,7 @@ public class SetCupValue extends EnumerableValue implements Enumerable { this.enum1.reset(); this.enum2.reset(); } - + public final Value nextElement() { Value elem = this.enum1.nextElement(); if (elem != null) return elem; @@ -161,5 +255,5 @@ public class SetCupValue extends EnumerableValue implements Enumerable { return elem; } } - + } diff --git a/tlatools/src/tlc2/value/SetDiffValue.java b/tlatools/src/tlc2/value/SetDiffValue.java index 9bc78405a302ac80e8ada4b2a70266da4e6d68ea..023ac0d325b22623011efb58fbdb72e01ad0c369 100644 --- a/tlatools/src/tlc2/value/SetDiffValue.java +++ b/tlatools/src/tlc2/value/SetDiffValue.java @@ -1,17 +1,20 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport // modified on Fri Aug 10 15:09:34 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import util.Assert; public class SetDiffValue extends EnumerableValue implements Enumerable { public Value set1; public Value set2; - protected SetEnumValue diffSet; - + protected SetEnumValue diffSet; + /* Constructor */ public SetDiffValue(Value set1, Value set2) { this.set1 = set1; @@ -22,84 +25,162 @@ public class SetDiffValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SETDIFFVALUE; } public final int compareTo(Object obj) { - this.convertAndCache(); - return this.diffSet.compareTo(obj); + try { + this.convertAndCache(); + return this.diffSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - this.convertAndCache(); - return this.diffSet.equals(obj); + try { + this.convertAndCache(); + return this.diffSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - return (this.set1.member(elem) && !this.set2.member(elem)); + try { + return (this.set1.member(elem) && !this.set2.member(elem)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - if (this.set1.isFinite()) { - return true; + try { + if (this.set1.isFinite()) { + return true; + } + if (!this.set2.isFinite()) { + Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite."); + } + return false; } - if (!this.set2.isFinite()) { - Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite."); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return false; } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - this.convertAndCache(); - return this.diffSet.size(); + try { + this.convertAndCache(); + return this.diffSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { - if (this.diffSet == null || this.diffSet == DummyEnum) { - return this.set1.isNormalized(); + try { + if (this.diffSet == null || this.diffSet == DummyEnum) { + return this.set1.isNormalized(); + } + return this.diffSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.diffSet.isNormalized(); } - + public final void normalize() { - if (this.diffSet == null || this.diffSet == DummyEnum) { - this.set1.normalize(); - this.set2.normalize(); + try { + if (this.diffSet == null || this.diffSet == DummyEnum) { + this.set1.normalize(); + this.set2.normalize(); + } + else { + this.diffSet.normalize(); + } } - else { - this.diffSet.normalize(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final boolean isDefined() { - return this.set1.isDefined() && this.set2.isDefined(); + try { + return this.set1.isDefined() && this.set2.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint methods */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.diffSet.fingerPrint(fp); + try { + this.convertAndCache(); + return this.diffSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.diffSet.permute(perm); + try { + this.convertAndCache(); + return this.diffSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -109,13 +190,13 @@ public class SetDiffValue extends EnumerableValue implements Enumerable { else if (this.diffSet == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.diffSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.diffSet == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.diffSet == DummyEnum) { this.diffSet = val; } + if (this.diffSet == DummyEnum) { this.diffSet = val; } } } } @@ -123,24 +204,37 @@ public class SetDiffValue extends EnumerableValue implements Enumerable { /* The string representation */ public final StringBuffer toString(StringBuffer sb, int offset) { try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); + try { + if (expand) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); + } } - } - catch (Throwable e) { /*SKIP*/ } + catch (Throwable e) { /*SKIP*/ } - sb = this.set1.toString(sb, offset); - sb = sb.append(" \\ "); - sb = this.set2.toString(sb, offset); - return sb; + sb = this.set1.toString(sb, offset); + sb = sb.append(" \\ "); + sb = this.set2.toString(sb, offset); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final ValueEnumeration elements() { - if (this.diffSet == null || this.diffSet == DummyEnum) { - return new Enumerator(); + try { + if (this.diffSet == null || this.diffSet == DummyEnum) { + return new Enumerator(); + } + return this.diffSet.elements(); } - return this.diffSet.elements(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } final class Enumerator implements ValueEnumeration { @@ -148,21 +242,21 @@ public class SetDiffValue extends EnumerableValue implements Enumerable { public Enumerator() { if (set1 instanceof Enumerable) { - this.enum1 = ((Enumerable)set1).elements(); + this.enum1 = ((Enumerable)set1).elements(); } else { - Assert.fail("Attempted to enumerate S \\ T when S:\n" + - ppr(set1.toString()) + "\nis not enumerable."); + Assert.fail("Attempted to enumerate S \\ T when S:\n" + + ppr(set1.toString()) + "\nis not enumerable."); } } public final void reset() { this.enum1.reset(); } - + public final Value nextElement() { Value elem = this.enum1.nextElement(); while (elem != null) { - if (!set2.member(elem)) return elem; - elem = this.enum1.nextElement(); + if (!set2.member(elem)) return elem; + elem = this.enum1.nextElement(); } return null; } diff --git a/tlatools/src/tlc2/value/SetEnumValue.java b/tlatools/src/tlc2/value/SetEnumValue.java index bb1794f65ab0f2d8fb57cc0d76ed5c1e24e35b04..ef4cb444e935210cc78e9eab95c23d0bb39506d7 100644 --- a/tlatools/src/tlc2/value/SetEnumValue.java +++ b/tlatools/src/tlc2/value/SetEnumValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:16:24 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:16:24 PST by lamport // modified on Mon Aug 20 10:54:08 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.util.FP64; import util.Assert; @@ -27,352 +30,452 @@ implements Enumerable, Reducible { public final byte getKind() { return SETENUMVALUE; } public final int compareTo(Object obj) { - SetEnumValue set = convert(obj); - if (set == null) { - if (obj instanceof ModelValue) return 1; - Assert.fail("Attempted to compare the set " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - set.normalize(); - int sz = this.elems.size(); - int cmp = sz - set.elems.size(); - if (cmp != 0) return cmp; - for (int i = 0; i < sz; i++) { - cmp = this.elems.elementAt(i).compareTo(set.elems.elementAt(i)); + try { + SetEnumValue set = convert(obj); + if (set == null) { + if (obj instanceof ModelValue) return 1; + Assert.fail("Attempted to compare the set " + ppr(this.toString()) + + " with the value:\n" + ppr(obj.toString())); + } + this.normalize(); + set.normalize(); + int sz = this.elems.size(); + int cmp = sz - set.elems.size(); if (cmp != 0) return cmp; + for (int i = 0; i < sz; i++) { + cmp = this.elems.elementAt(i).compareTo(set.elems.elementAt(i)); + if (cmp != 0) return cmp; + } + return 0; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return 0; } - + public final boolean equals(Object obj) { - SetEnumValue set = convert(obj); - if (set == null) { - if (obj instanceof ModelValue) - return ((ModelValue) obj).modelValueEquals(this) ; - Assert.fail("Attempted to check equality of the set " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - set.normalize(); - int sz = this.elems.size(); - if (sz != set.elems.size()) { - return false; - } - for (int i = 0; i < sz; i++) { - if (!this.elems.elementAt(i).equals(set.elems.elementAt(i))) { - return false; + try { + SetEnumValue set = convert(obj); + if (set == null) { + if (obj instanceof ModelValue) + return ((ModelValue) obj).modelValueEquals(this) ; + Assert.fail("Attempted to check equality of the set " + ppr(this.toString()) + + " with the value:\n" + ppr(obj.toString())); } + this.normalize(); + set.normalize(); + int sz = this.elems.size(); + if (sz != set.elems.size()) { + return false; + } + for (int i = 0; i < sz; i++) { + if (!this.elems.elementAt(i).equals(set.elems.elementAt(i))) { + return false; + } + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } public final boolean member(Value elem) { - return this.elems.search(elem, this.isNorm); + try { + return this.elems.search(elem, this.isNorm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { return true; } - + public final Value diff(Value val) { - int sz = this.elems.size(); - ValueVec diffElems = new ValueVec(); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - if (!val.member(elem)) { - diffElems.addElement(elem); + try { + int sz = this.elems.size(); + ValueVec diffElems = new ValueVec(); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + if (!val.member(elem)) { + diffElems.addElement(elem); + } } + return new SetEnumValue(diffElems, this.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetEnumValue(diffElems, this.isNormalized()); } public final Value cap(Value val) { - int sz = this.elems.size(); - ValueVec capElems = new ValueVec(); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - if (val.member(elem)) { - capElems.addElement(elem); + try { + int sz = this.elems.size(); + ValueVec capElems = new ValueVec(); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + if (val.member(elem)) { + capElems.addElement(elem); + } } + return new SetEnumValue(capElems, this.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetEnumValue(capElems, this.isNormalized()); } public final Value cup(Value set) { - int sz = this.elems.size(); - if (sz == 0) return set; + try { + int sz = this.elems.size(); + if (sz == 0) return set; - if (set instanceof Reducible) { - ValueVec cupElems = new ValueVec(); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - cupElems.addElement(elem); + if (set instanceof Reducible) { + ValueVec cupElems = new ValueVec(); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + cupElems.addElement(elem); + } + ValueEnumeration Enum = ((Enumerable)set).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!this.member(elem)) cupElems.addElement(elem); + } + return new SetEnumValue(cupElems, false); } - ValueEnumeration Enum = ((Enumerable)set).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!this.member(elem)) cupElems.addElement(elem); - } - return new SetEnumValue(cupElems, false); + return new SetCupValue(this, set); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new SetCupValue(this, set); } public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } - + public final int size() { - this.normalize(); - return this.elems.size(); + try { + this.normalize(); + return this.elems.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* This method normalizes (destructively) this set. */ public final boolean isNormalized() { return this.isNorm; } - + public final void normalize() { - if (!this.isNorm) { - this.elems.sort(true); // duplicates eliminated - this.isNorm = true; + try { + if (!this.isNorm) { + this.elems.sort(true); // duplicates eliminated + this.isNorm = true; + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } /* Convert val into a SetEnumValue. Returns null if not possible. */ public static final SetEnumValue convert(Object val) { switch (((Value)val).getKind()) { - case SETENUMVALUE: - return (SetEnumValue)val; - case INTERVALVALUE: - { - IntervalValue intv = (IntervalValue)val; - Value[] vals = new Value[intv.size()]; - for (int i = 0; i < vals.length; i++) { - vals[i] = IntValue.gen(i + intv.low); - } - return new SetEnumValue(vals, true); - } - case SETCAPVALUE: - { - SetCapValue cap = (SetCapValue)val; - if (cap.capSet != null && cap.capSet != DummyEnum) { - return cap.capSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = cap.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, cap.isNormalized()); - } - case SETCUPVALUE: - { - SetCupValue cup = (SetCupValue)val; - if (cup.cupSet != null && cup.cupSet != DummyEnum) { - return cup.cupSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = cup.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, false); - } - case SETDIFFVALUE: - { - SetDiffValue diff = (SetDiffValue)val; - if (diff.diffSet != null && diff.diffSet != DummyEnum) { - return diff.diffSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = diff.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, diff.set1.isNormalized()); - } - case UNIONVALUE: - { - UnionValue uv = (UnionValue)val; - if (uv.realSet != null && uv.realSet != DummyEnum) { - return uv.realSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = uv.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, false); - } - case SUBSETVALUE: - { - SubsetValue pset = (SubsetValue)val; - if (pset.pset != null && pset.pset != DummyEnum) { - return pset.pset; - } - ValueVec vals = new ValueVec(pset.size()); - ValueEnumeration Enum = pset.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, false); - } - case SETOFRCDSVALUE: - { - SetOfRcdsValue rcds = (SetOfRcdsValue)val; - if (rcds.rcdSet != null && rcds.rcdSet != DummyEnum) { - return rcds.rcdSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = rcds.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, rcds.isNormalized()); - } - case SETOFFCNSVALUE: - { - SetOfFcnsValue fcns = (SetOfFcnsValue)val; - if (fcns.fcnSet != null && fcns.fcnSet != DummyEnum) { - return fcns.fcnSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = fcns.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, fcns.isNormalized()); - } - case SETOFTUPLESVALUE: - { - SetOfTuplesValue tvs = (SetOfTuplesValue)val; - if (tvs.tupleSet != null && tvs.tupleSet != DummyEnum) { - return tvs.tupleSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = tvs.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, tvs.isNormalized()); - } - case SETPREDVALUE: - { - SetPredValue sPred = (SetPredValue)val; - if (sPred.tool == null) return (SetEnumValue)sPred.inVal; - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = sPred.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, sPred.isNormalized()); - } - default: - // null if val can not be converted. - return null; + case SETENUMVALUE: + return (SetEnumValue)val; + case INTERVALVALUE: + { + IntervalValue intv = (IntervalValue)val; + Value[] vals = new Value[intv.size()]; + for (int i = 0; i < vals.length; i++) { + vals[i] = IntValue.gen(i + intv.low); + } + return new SetEnumValue(vals, true); + } + case SETCAPVALUE: + { + SetCapValue cap = (SetCapValue)val; + if (cap.capSet != null && cap.capSet != DummyEnum) { + return cap.capSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = cap.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, cap.isNormalized()); + } + case SETCUPVALUE: + { + SetCupValue cup = (SetCupValue)val; + if (cup.cupSet != null && cup.cupSet != DummyEnum) { + return cup.cupSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = cup.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, false); + } + case SETDIFFVALUE: + { + SetDiffValue diff = (SetDiffValue)val; + if (diff.diffSet != null && diff.diffSet != DummyEnum) { + return diff.diffSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = diff.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, diff.set1.isNormalized()); + } + case UNIONVALUE: + { + UnionValue uv = (UnionValue)val; + if (uv.realSet != null && uv.realSet != DummyEnum) { + return uv.realSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = uv.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, false); + } + case SUBSETVALUE: + { + SubsetValue pset = (SubsetValue)val; + if (pset.pset != null && pset.pset != DummyEnum) { + return pset.pset; + } + ValueVec vals = new ValueVec(pset.size()); + ValueEnumeration Enum = pset.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, false); + } + case SETOFRCDSVALUE: + { + SetOfRcdsValue rcds = (SetOfRcdsValue)val; + if (rcds.rcdSet != null && rcds.rcdSet != DummyEnum) { + return rcds.rcdSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = rcds.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, rcds.isNormalized()); + } + case SETOFFCNSVALUE: + { + SetOfFcnsValue fcns = (SetOfFcnsValue)val; + if (fcns.fcnSet != null && fcns.fcnSet != DummyEnum) { + return fcns.fcnSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = fcns.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, fcns.isNormalized()); + } + case SETOFTUPLESVALUE: + { + SetOfTuplesValue tvs = (SetOfTuplesValue)val; + if (tvs.tupleSet != null && tvs.tupleSet != DummyEnum) { + return tvs.tupleSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = tvs.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, tvs.isNormalized()); + } + case SETPREDVALUE: + { + SetPredValue sPred = (SetPredValue)val; + if (sPred.tool == null) return (SetEnumValue)sPred.inVal; + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = sPred.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return new SetEnumValue(vals, sPred.isNormalized()); + } + default: + // null if val can not be converted. + return null; } } - + public final boolean isDefined() { - boolean defined = true; - int sz = this.elems.size(); - for (int i = 0; i < sz; i++) { - defined = defined && this.elems.elementAt(i).isDefined(); + try { + boolean defined = true; + int sz = this.elems.size(); + for (int i = 0; i < sz; i++) { + defined = defined && this.elems.elementAt(i).isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return defined; } public final Value deepCopy() { return this; } - public final boolean assignable(Value val) { return this.equals(val); } + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } /* The fingerprint methods */ public final long fingerPrint(long fp) { - this.normalize(); - int sz = this.elems.size(); - fp = FP64.Extend(fp, SETENUMVALUE); - fp = FP64.Extend(fp, sz); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - fp = elem.fingerPrint(fp); - } - return fp; + try { + this.normalize(); + int sz = this.elems.size(); + fp = FP64.Extend(fp, SETENUMVALUE); + fp = FP64.Extend(fp, sz); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + fp = elem.fingerPrint(fp); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - int sz = this.elems.size(); - Value[] vals = new Value[sz]; - boolean changed = false; - for (int i = 0; i < sz; i++) { - vals[i] = this.elems.elementAt(i).permute(perm); - changed = (changed || vals[i] != this.elems.elementAt(i)); + try { + int sz = this.elems.size(); + Value[] vals = new Value[sz]; + boolean changed = false; + for (int i = 0; i < sz; i++) { + vals[i] = this.elems.elementAt(i).permute(perm); + changed = (changed || vals[i] != this.elems.elementAt(i)); + } + if (changed) { + return new SetEnumValue(vals, false); + } + return this; } - if (changed) { - return new SetEnumValue(vals, false); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } /* The string representation */ public final StringBuffer toString(StringBuffer sb, int offset) { - // If this SetEnumValue object is created by a union, at least one of - // whose elements is a Cartesian product, then this can be an unnormalized - // set with repeated elements. It would therefore seem like a good idea to - // normalize this object here. Since this toString method is probably - // used only for printing the value, it seems that correcting this should - // not do any harm. Therefore, LL added the following if statement - // on 5 Mar 2012. - if (!this.isNormalized()) { - this.normalize(); - } - - int len = this.elems.size(); - sb = sb.append("{"); - if (len > 0) { - this.elems.elementAt(0).toString(sb, offset); - } - for (int i = 1; i < len; i++) { - sb.append(", "); - this.elems.elementAt(i).toString(sb, offset); - } - sb.append("}"); - return sb; + try { + // If this SetEnumValue object is created by a union, at least one of + // whose elements is a Cartesian product, then this can be an unnormalized + // set with repeated elements. It would therefore seem like a good idea to + // normalize this object here. Since this toString method is probably + // used only for printing the value, it seems that correcting this should + // not do any harm. Therefore, LL added the following if statement + // on 5 Mar 2012. + if (!this.isNormalized()) { + this.normalize(); + } + + int len = this.elems.size(); + sb = sb.append("{"); + if (len > 0) { + this.elems.elementAt(0).toString(sb, offset); + } + for (int i = 1; i < len; i++) { + sb.append(", "); + this.elems.elementAt(i).toString(sb, offset); + } + sb.append("}"); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final ValueEnumeration elements() { + try { + return new Enumerator(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - public final ValueEnumeration elements() { return new Enumerator(); } - final class Enumerator implements ValueEnumeration { int index = 0; public Enumerator() { normalize(); } - + public final void reset() { this.index = 0; } public final Value nextElement() { if (this.index < elems.size()) { - return elems.elementAt(this.index++); + return elems.elementAt(this.index++); } return null; } } - + } diff --git a/tlatools/src/tlc2/value/SetOfFcnsValue.java b/tlatools/src/tlc2/value/SetOfFcnsValue.java index 026ff814c06b5fbf26c5085cd337dd500008bfcc..04fac160475032f7edd9c57bd5b7de5e84e2ec05 100644 --- a/tlatools/src/tlc2/value/SetOfFcnsValue.java +++ b/tlatools/src/tlc2/value/SetOfFcnsValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:17:11 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:17:11 PST by lamport // modified on Fri Aug 10 15:09:46 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.TLCGlobals; import util.Assert; @@ -12,7 +15,7 @@ public class SetOfFcnsValue extends EnumerableValue implements Enumerable { public Value domain; /* Function domain */ public Value range; /* Function range */ protected SetEnumValue fcnSet; - + /* Constructor */ public SetOfFcnsValue(Value domain, Value range) { this.domain = domain; @@ -23,121 +26,199 @@ public class SetOfFcnsValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SETOFFCNSVALUE; } public final int compareTo(Object obj) { - this.convertAndCache(); - return this.fcnSet.compareTo(obj); + try { + this.convertAndCache(); + return this.fcnSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - if (obj instanceof SetOfFcnsValue) { - SetOfFcnsValue fcns = (SetOfFcnsValue)obj; - return (this.domain.equals(fcns.domain) && - this.range.equals(fcns.range)); + try { + if (obj instanceof SetOfFcnsValue) { + SetOfFcnsValue fcns = (SetOfFcnsValue)obj; + return (this.domain.equals(fcns.domain) && + this.range.equals(fcns.range)); + } + this.convertAndCache(); + return this.fcnSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.convertAndCache(); - return this.fcnSet.equals(obj); } public final boolean member(Value elem) { - FcnRcdValue fcn = FcnRcdValue.convert(elem); - if (fcn == null) { - if (elem instanceof ModelValue) - return ((ModelValue) elem).modelValueMember(this) ; - Assert.fail("Attempted to check if \n" + elem + "\nwhich is not a TLC function" + - " value, is in the set of functions:\n" + ppr(this.toString())); - } - if (fcn.intv == null) { - fcn.normalize(); - Value fdom = new SetEnumValue(fcn.domain, true); - if (this.domain.equals(fdom)) { - for (int i = 0; i < fcn.values.length; i++) { - if (!this.range.member(fcn.values[i])) { - return false; - } - } - return true; + try { + FcnRcdValue fcn = FcnRcdValue.convert(elem); + if (fcn == null) { + if (elem instanceof ModelValue) + return ((ModelValue) elem).modelValueMember(this) ; + Assert.fail("Attempted to check if \n" + elem + "\nwhich is not a TLC function" + + " value, is in the set of functions:\n" + ppr(this.toString())); } - } - else { - if (fcn.intv.equals(this.domain)) { - for (int i = 0; i < fcn.values.length; i++) { - if (!this.range.member(fcn.values[i])) return false; - } - return true; + if (fcn.intv == null) { + fcn.normalize(); + Value fdom = new SetEnumValue(fcn.domain, true); + if (this.domain.equals(fdom)) { + for (int i = 0; i < fcn.values.length; i++) { + if (!this.range.member(fcn.values[i])) { + return false; + } + } + return true; + } + } + else { + if (fcn.intv.equals(this.domain)) { + for (int i = 0; i < fcn.values.length; i++) { + if (!this.range.member(fcn.values[i])) return false; + } + return true; + } } + return false; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return false; } public final boolean isFinite() { - return this.domain.isFinite() && this.range.isFinite(); + try { + return this.domain.isFinite() && this.range.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + - ppr(this.toString())); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + + ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + - ppr(this.toString())); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + + ppr(this.toString())); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - int dsz = this.domain.size(); - int rsz = this.range.size(); - long sz = 1; - for (int i = 0; i < dsz; i++) { - sz *= rsz; - if (sz < -2147483648 || sz > 2147483647) { - Assert.fail("Overflow when computing the number of elements in:\n" + - ppr(toString())); + try { + int dsz = this.domain.size(); + int rsz = this.range.size(); + long sz = 1; + for (int i = 0; i < dsz; i++) { + sz *= rsz; + if (sz < -2147483648 || sz > 2147483647) { + Assert.fail("Overflow when computing the number of elements in:\n" + + ppr(toString())); + } } + return (int)sz; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return (int)sz; } public final boolean isNormalized() { - if (this.fcnSet == null || this.fcnSet == DummyEnum) { - return this.domain.isNormalized() && this.range.isNormalized(); + try { + if (this.fcnSet == null || this.fcnSet == DummyEnum) { + return this.domain.isNormalized() && this.range.isNormalized(); + } + return this.fcnSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.fcnSet.isNormalized(); } - + public final void normalize() { - if (this.fcnSet == null || this.fcnSet == DummyEnum) { - this.domain.normalize(); - this.range.normalize(); + try { + if (this.fcnSet == null || this.fcnSet == DummyEnum) { + this.domain.normalize(); + this.range.normalize(); + } + else { + this.fcnSet.normalize(); + } } - else { - this.fcnSet.normalize(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final boolean isDefined() { - return this.domain.isDefined() && this.range.isDefined(); + try { + return this.domain.isDefined() && this.range.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.fcnSet.fingerPrint(fp); + try { + this.convertAndCache(); + return this.fcnSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.fcnSet.permute(perm); + try { + this.convertAndCache(); + return this.fcnSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -147,56 +228,68 @@ public class SetOfFcnsValue extends EnumerableValue implements Enumerable { else if (this.fcnSet == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.fcnSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.fcnSet == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.fcnSet == DummyEnum) { this.fcnSet = val; } + if (this.fcnSet == DummyEnum) { this.fcnSet = val; } } } } - + /* The string representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; try { + boolean unlazy = expand; + try { + if (unlazy) { + int dsz = this.domain.size(); + int rsz = this.range.size(); + long sz = 1; + for (int i = 0; i < dsz; i++) { + sz *= rsz; + if (sz < -2147483648 || sz > 2147483647) { + unlazy = false; + break; + } + } + unlazy = sz < TLCGlobals.enumBound; + } + } + catch (Throwable e) { unlazy = false; } + if (unlazy) { - int dsz = this.domain.size(); - int rsz = this.range.size(); - long sz = 1; - for (int i = 0; i < dsz; i++) { - sz *= rsz; - if (sz < -2147483648 || sz > 2147483647) { - unlazy = false; - break; - } - } - unlazy = sz < TLCGlobals.enumBound; + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); + } + else { + sb.append("["); + this.domain.toString(sb, offset); + sb.append(" -> "); + this.range.toString(sb, offset); + sb.append("]"); + return sb; } } - catch (Throwable e) { unlazy = false; } - - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - sb.append("["); - this.domain.toString(sb, offset); - sb.append(" -> "); - this.range.toString(sb, offset); - sb.append("]"); - return sb; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } - + public final ValueEnumeration elements() { - if (this.fcnSet == null || this.fcnSet == DummyEnum) { - return new Enumerator(); + try { + if (this.fcnSet == null || this.fcnSet == DummyEnum) { + return new Enumerator(); + } + return this.fcnSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.fcnSet.elements(); } final class Enumerator implements ValueEnumeration { @@ -204,75 +297,75 @@ public class SetOfFcnsValue extends EnumerableValue implements Enumerable { private ValueEnumeration[] enums; private Value[] currentElems; private boolean isDone; - + public Enumerator() { this.isDone = false; SetEnumValue domSet = SetEnumValue.convert(domain); if (domSet == null) - Assert.fail("Attempted to enumerate a set of the form [D -> R]," + - "but the domain D:\n" + ppr(domain.toString()) + - "\ncannot be enumerated."); + Assert.fail("Attempted to enumerate a set of the form [D -> R]," + + "but the domain D:\n" + ppr(domain.toString()) + + "\ncannot be enumerated."); domSet.normalize(); ValueVec elems = domSet.elems; int sz = elems.size(); if (range instanceof Enumerable) { - this.dom = new Value[sz]; - this.enums = new ValueEnumeration[sz]; - this.currentElems = new Value[sz]; - // SZ Feb 24, 2009: never read locally - // ValueEnumeration enumeration = ((Enumerable)domSet).elements(); - for (int i = 0; i < sz; i++) { - this.dom[i] = elems.elementAt(i); - this.enums[i] = ((Enumerable)range).elements(); - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] == null) { - this.enums = null; - this.isDone = true; - break; - } - } + this.dom = new Value[sz]; + this.enums = new ValueEnumeration[sz]; + this.currentElems = new Value[sz]; + // SZ Feb 24, 2009: never read locally + // ValueEnumeration enumeration = ((Enumerable)domSet).elements(); + for (int i = 0; i < sz; i++) { + this.dom[i] = elems.elementAt(i); + this.enums[i] = ((Enumerable)range).elements(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] == null) { + this.enums = null; + this.isDone = true; + break; + } + } } else { - Assert.fail("Attempted to enumerate a set of the form [D -> R]," + - "but the range R:\n" + ppr(range.toString()) + - "\ncannot be enumerated."); + Assert.fail("Attempted to enumerate a set of the form [D -> R]," + + "but the range R:\n" + ppr(range.toString()) + + "\ncannot be enumerated."); } } public final void reset() { if (this.enums != null) { - for (int i = 0; i < this.enums.length; i++) { - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - this.isDone = false; + for (int i = 0; i < this.enums.length; i++) { + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + this.isDone = false; } } - + public final Value nextElement() { if (this.isDone) return null; Value[] elems = new Value[this.currentElems.length]; if (elems.length == 0) { - this.isDone = true; + this.isDone = true; } else { - for (int i = 0; i < elems.length; i++) { - elems[i] = this.currentElems[i]; - } - for (int i = elems.length-1; i >= 0; i--) { - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] != null) break; - if (i == 0) { - this.isDone = true; - break; - } - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } + for (int i = 0; i < elems.length; i++) { + elems[i] = this.currentElems[i]; + } + for (int i = elems.length-1; i >= 0; i--) { + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] != null) break; + if (i == 0) { + this.isDone = true; + break; + } + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } } return new FcnRcdValue(this.dom, elems, true); } - + } } diff --git a/tlatools/src/tlc2/value/SetOfRcdsValue.java b/tlatools/src/tlc2/value/SetOfRcdsValue.java index 987d0df792f80f0d3cf42261a874b5a943073744..c8c9ebfd60a0c00fddc09d50bbc90ed9470f785a 100644 --- a/tlatools/src/tlc2/value/SetOfRcdsValue.java +++ b/tlatools/src/tlc2/value/SetOfRcdsValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:18:01 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:18:01 PST by lamport // modified on Fri Aug 10 15:09:53 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.TLCGlobals; import tlc2.output.EC; import util.Assert; @@ -14,7 +17,7 @@ public class SetOfRcdsValue extends EnumerableValue implements Enumerable { public UniqueString[] names; // The names of the fields. public Value[] values; // The values of the fields. protected SetEnumValue rcdSet; - + /* Constructor */ public SetOfRcdsValue(UniqueString[] names, Value[] values, boolean isNorm) { this.names = names; @@ -28,126 +31,180 @@ public class SetOfRcdsValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SETOFRCDSVALUE; } public final int compareTo(Object obj) { - this.convertAndCache(); - return this.rcdSet.compareTo(obj); + try { + this.convertAndCache(); + return this.rcdSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - if (obj instanceof SetOfRcdsValue) { - SetOfRcdsValue rcds = (SetOfRcdsValue)obj; - - boolean isEmpty1 = this.isEmpty(); - if (isEmpty1) return rcds.isEmpty(); - if (rcds.isEmpty()) return isEmpty1; - - if (this.names.length != rcds.names.length) { - return false; - } - for (int i = 0; i < this.names.length; i++) { - if (!this.names[i].equals(rcds.names[i]) || - !this.values[i].equals(rcds.values[i])) { - return false; - } + try { + if (obj instanceof SetOfRcdsValue) { + SetOfRcdsValue rcds = (SetOfRcdsValue)obj; + + boolean isEmpty1 = this.isEmpty(); + if (isEmpty1) return rcds.isEmpty(); + if (rcds.isEmpty()) return isEmpty1; + + if (this.names.length != rcds.names.length) { + return false; + } + for (int i = 0; i < this.names.length; i++) { + if (!this.names[i].equals(rcds.names[i]) || + !this.values[i].equals(rcds.values[i])) { + return false; + } + } + return true; } - return true; + this.convertAndCache(); + return this.rcdSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.convertAndCache(); - return this.rcdSet.equals(obj); } public final boolean member(Value elem) { - RecordValue rcd = RecordValue.convert(elem); - if (rcd == null) { - if (elem instanceof ModelValue) - return ((ModelValue) elem).modelValueMember(this) ; - Assert.fail("Attempted to check if non-record\n" + elem + "\nis in the" + - " set of records:\n" + ppr(this.toString())); - } - rcd.normalize(); - if (this.names.length != rcd.names.length) { - return false; - } - for (int i = 0; i < this.names.length; i++) { - if ((!this.names[i].equals(rcd.names[i])) || - (!this.values[i].member(rcd.values[i]))) { - return false; + try { + RecordValue rcd = RecordValue.convert(elem); + if (rcd == null) { + if (elem instanceof ModelValue) + return ((ModelValue) elem).modelValueMember(this) ; + Assert.fail("Attempted to check if non-record\n" + elem + "\nis in the" + + " set of records:\n" + ppr(this.toString())); + } + rcd.normalize(); + if (this.names.length != rcd.names.length) { + return false; + } + for (int i = 0; i < this.names.length; i++) { + if ((!this.names[i].equals(rcd.names[i])) || + (!this.values[i].member(rcd.values[i]))) { + return false; + } } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } public final boolean isFinite() { - for (int i = 0; i < this.values.length; i++) { - if (!this.values[i].isFinite()) return false; + try { + for (int i = 0; i < this.values.length; i++) { + if (!this.values[i].isFinite()) return false; + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + - ppr(this.toString())); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + + ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + - ppr(this.toString())); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + + ppr(this.toString())); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - long sz = 1; - for (int i = 0; i < this.values.length; i++) { - sz *= this.values[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + - ppr(this.toString())); + try { + long sz = 1; + for (int i = 0; i < this.values.length; i++) { + sz *= this.values[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + + ppr(this.toString())); + } } + return (int)sz; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return (int)sz; } public final boolean isNormalized() { - if (this.rcdSet == null || this.rcdSet == DummyEnum) { - for (int i = 0; i < this.names.length; i++) { - if (!this.values[i].isNormalized()) { - return false; - } + try { + if (this.rcdSet == null || this.rcdSet == DummyEnum) { + for (int i = 0; i < this.names.length; i++) { + if (!this.values[i].isNormalized()) { + return false; + } + } + return true; } - return true; + return this.rcdSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.rcdSet.isNormalized(); } public final void normalize() { - if (this.rcdSet == null || this.rcdSet == DummyEnum) { - for (int i = 0; i < this.names.length; i++) { - this.values[i].normalize(); + try { + if (this.rcdSet == null || this.rcdSet == DummyEnum) { + for (int i = 0; i < this.names.length; i++) { + this.values[i].normalize(); + } + } + else { + this.rcdSet.normalize(); } } - else { - this.rcdSet.normalize(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } - + private final void sortByNames() { for (int i = 1; i < this.names.length; i++) { int cmp = this.names[0].compareTo(this.names[i]); if (cmp == 0) { - Assert.fail("Field name " + this.names[0] + " occurs multiple times" + - " in set of records."); + Assert.fail("Field name " + this.names[0] + " occurs multiple times" + + " in set of records."); } else if (cmp > 0) { - UniqueString ts = this.names[0]; - this.names[0] = this.names[i]; - this.names[i] = ts; - Value tv = this.values[0]; - this.values[0] = this.values[i]; - this.values[i] = tv; + UniqueString ts = this.names[0]; + this.names[0] = this.names[i]; + this.names[i] = ts; + Value tv = this.values[0]; + this.values[0] = this.values[i]; + this.values[i] = tv; } } for (int i = 2; i < this.names.length; i++) { @@ -156,13 +213,13 @@ public class SetOfRcdsValue extends EnumerableValue implements Enumerable { Value val = this.values[i]; int cmp; while ((cmp = st.compareTo(this.names[j-1])) < 0) { - this.names[j] = this.names[j-1]; - this.values[j] = this.values[j-1]; - j--; + this.names[j] = this.names[j-1]; + this.values[j] = this.values[j-1]; + j--; } if (cmp == 0) { - Assert.fail("Field name " + this.names[i] + " occurs multiple times" + - " in set of records."); + Assert.fail("Field name " + this.names[i] + " occurs multiple times" + + " in set of records."); } this.names[j] = st; this.values[j] = val; @@ -170,28 +227,52 @@ public class SetOfRcdsValue extends EnumerableValue implements Enumerable { } public final boolean isDefined() { - boolean isDefined = true; - for (int i = 0; i < this.values.length; i++) { - isDefined = isDefined && this.values[i].isDefined(); + try { + boolean isDefined = true; + for (int i = 0; i < this.values.length; i++) { + isDefined = isDefined && this.values[i].isDefined(); + } + return isDefined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return isDefined; } public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + /* The fingerprint */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.rcdSet.fingerPrint(fp); + try { + this.convertAndCache(); + return this.rcdSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.rcdSet.permute(perm); + try { + this.convertAndCache(); + return this.rcdSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -201,61 +282,73 @@ public class SetOfRcdsValue extends EnumerableValue implements Enumerable { else if (this.rcdSet == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.rcdSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.rcdSet == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.rcdSet == DummyEnum) { this.rcdSet = val; } + if (this.rcdSet == DummyEnum) { this.rcdSet = val; } } } } - + /* The string representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; try { - if (unlazy) { - long sz = 1; - for (int i = 0; i < this.values.length; i++) { - sz *= this.values[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - unlazy = false; - break; - } - } - unlazy = sz < TLCGlobals.enumBound; + boolean unlazy = expand; + try { + if (unlazy) { + long sz = 1; + for (int i = 0; i < this.values.length; i++) { + sz *= this.values[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + unlazy = false; + break; + } + } + unlazy = sz < TLCGlobals.enumBound; + } } - } - catch (Throwable e) { unlazy = false; } - - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - sb.append("["); - int len = this.names.length; - if (len != 0) { - sb.append(names[0] + ": "); - this.values[0].toString(sb, offset); + catch (Throwable e) { unlazy = false; } + + if (unlazy) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); } - for (int i = 1; i < len; i++) { - sb.append(", "); - sb.append(names[i] + ": "); - this.values[i].toString(sb, offset); + else { + sb.append("["); + int len = this.names.length; + if (len != 0) { + sb.append(names[0] + ": "); + this.values[0].toString(sb, offset); + } + for (int i = 1; i < len; i++) { + sb.append(", "); + sb.append(names[i] + ": "); + this.values[i].toString(sb, offset); + } + sb.append("]"); + return sb; } - sb.append("]"); - return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final ValueEnumeration elements() { - if (this.rcdSet == null || this.rcdSet == DummyEnum) { - return new Enumerator(); + try { + if (this.rcdSet == null || this.rcdSet == DummyEnum) { + return new Enumerator(); + } + return this.rcdSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.rcdSet.elements(); } final class Enumerator implements ValueEnumeration { @@ -268,52 +361,52 @@ public class SetOfRcdsValue extends EnumerableValue implements Enumerable { this.currentElems = new Value[values.length]; this.isDone = false; for (int i = 0; i < values.length; i++) { - if (values[i] instanceof Enumerable) { - this.enums[i] = ((Enumerable)values[i]).elements(); - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] == null) { - this.enums = null; - this.isDone = true; - break; - } - } - else { - Assert.fail("Attempted to enumerate a set of the form [l1 : v1, ..., ln : vn]," + - "\nbut can't enumerate the value of the `" + names[i] + "' field:\n" + - ppr(values[i].toString())); - } + if (values[i] instanceof Enumerable) { + this.enums[i] = ((Enumerable)values[i]).elements(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] == null) { + this.enums = null; + this.isDone = true; + break; + } + } + else { + Assert.fail("Attempted to enumerate a set of the form [l1 : v1, ..., ln : vn]," + + "\nbut can't enumerate the value of the `" + names[i] + "' field:\n" + + ppr(values[i].toString())); + } } } public final void reset() { if (this.enums != null) { - for (int i = 0; i < this.enums.length; i++) { - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - this.isDone = false; + for (int i = 0; i < this.enums.length; i++) { + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + this.isDone = false; } } - + public final Value nextElement() { if (this.isDone) return null; Value[] elems = new Value[this.currentElems.length]; for (int i = 0; i < elems.length; i++) { - elems[i] = this.currentElems[i]; + elems[i] = this.currentElems[i]; } for (int i = elems.length-1; i >= 0; i--) { - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] != null) break; - if (i == 0) { - this.isDone = true; - break; - } - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] != null) break; + if (i == 0) { + this.isDone = true; + break; + } + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); } return new RecordValue(names, elems, true); } - + } } diff --git a/tlatools/src/tlc2/value/SetOfTuplesValue.java b/tlatools/src/tlc2/value/SetOfTuplesValue.java index 88ba04dced8b9da21c508b2eb3369873f38ddac8..4d6c38f65242ab15e33851c6191f0f46cfd549bd 100644 --- a/tlatools/src/tlc2/value/SetOfTuplesValue.java +++ b/tlatools/src/tlc2/value/SetOfTuplesValue.java @@ -1,17 +1,20 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:18:39 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:18:39 PST by lamport // modified on Fri Aug 10 15:09:59 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.TLCGlobals; import util.Assert; public class SetOfTuplesValue extends EnumerableValue implements Enumerable { public Value[] sets; protected SetEnumValue tupleSet; - + /* Constructor */ public SetOfTuplesValue(Value[] sets) { this.sets = sets; @@ -34,144 +37,222 @@ public class SetOfTuplesValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SETOFTUPLESVALUE; } public final int compareTo(Object obj) { - this.convertAndCache(); - return this.tupleSet.compareTo(obj); + try { + this.convertAndCache(); + return this.tupleSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - if (obj instanceof SetOfTuplesValue) { - SetOfTuplesValue tvs = (SetOfTuplesValue)obj; + try { + if (obj instanceof SetOfTuplesValue) { + SetOfTuplesValue tvs = (SetOfTuplesValue)obj; - boolean isEmpty1 = this.isEmpty(); - if (isEmpty1) return tvs.isEmpty(); - if (tvs.isEmpty()) return isEmpty1; + boolean isEmpty1 = this.isEmpty(); + if (isEmpty1) return tvs.isEmpty(); + if (tvs.isEmpty()) return isEmpty1; - if (this.sets.length != tvs.sets.length) { - return false; - } - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].equals(tvs.sets[i])) { - return false; - } + if (this.sets.length != tvs.sets.length) { + return false; + } + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].equals(tvs.sets[i])) { + return false; + } + } + return true; } - return true; + this.convertAndCache(); + return this.tupleSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.convertAndCache(); - return this.tupleSet.equals(obj); } public final boolean member(Value elem) { - TupleValue tv = TupleValue.convert(elem); - if (tv == null) { - FcnRcdValue fcn = FcnRcdValue.convert(elem); - if (fcn == null) { - if (elem instanceof ModelValue) - return ((ModelValue) elem).modelValueMember(this) ; - Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) + - "\nis in the set of tuples:\n" + ppr(this.toString())); + try { + TupleValue tv = TupleValue.convert(elem); + if (tv == null) { + FcnRcdValue fcn = FcnRcdValue.convert(elem); + if (fcn == null) { + if (elem instanceof ModelValue) + return ((ModelValue) elem).modelValueMember(this) ; + Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) + + "\nis in the set of tuples:\n" + ppr(this.toString())); + } + if (fcn.intv != null) return false; + for (int i = 0; i < fcn.domain.length; i++) { + if (!(fcn.domain[i] instanceof IntValue)) { + Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) + + "\nis in the set of tuples:\n" + ppr(this.toString())); + } + } + return false; } - if (fcn.intv != null) return false; - for (int i = 0; i < fcn.domain.length; i++) { - if (!(fcn.domain[i] instanceof IntValue)) { - Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) + - "\nis in the set of tuples:\n" + ppr(this.toString())); - } + if (tv.elems.length == this.sets.length) { + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].member(tv.elems[i])) + return false; + } + return true; } return false; } - if (tv.elems.length == this.sets.length) { - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].member(tv.elems[i])) - return false; - } - return true; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return false; } public final boolean isFinite() { - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].isFinite()) { - return false; + try { + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].isFinite()) { + return false; + } } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + - ppr(this.toString())); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + + ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + - ppr(this.toString())); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + + ppr(this.toString())); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - long sz = 1; - for (int i = 0; i < this.sets.length; i++) { - sz *= this.sets[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - Assert.fail("Overflow when computing the number of elements in " + - ppr(this.toString())); + try { + long sz = 1; + for (int i = 0; i < this.sets.length; i++) { + sz *= this.sets[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + Assert.fail("Overflow when computing the number of elements in " + + ppr(this.toString())); + } } + return (int)sz; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return (int)sz; } public final boolean isNormalized() { - if (this.tupleSet == null || this.tupleSet == DummyEnum) { - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].isNormalized()) { - return false; - } + try { + if (this.tupleSet == null || this.tupleSet == DummyEnum) { + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].isNormalized()) { + return false; + } + } + return true; } - return true; + return this.tupleSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.tupleSet.isNormalized(); } public final void normalize() { - if (this.tupleSet == null || this.tupleSet == DummyEnum) { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].normalize(); + try { + if (this.tupleSet == null || this.tupleSet == DummyEnum) { + for (int i = 0; i < this.sets.length; i++) { + this.sets[i].normalize(); + } + } + else { + this.tupleSet.normalize(); } } - else { - this.tupleSet.normalize(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.sets.length; i++) { - defined = defined && this.sets[i].isDefined(); + try { + boolean defined = true; + for (int i = 0; i < this.sets.length; i++) { + defined = defined && this.sets[i].isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return defined; } public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.tupleSet.fingerPrint(fp); + try { + this.convertAndCache(); + return this.tupleSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.tupleSet.permute(perm); + try { + this.convertAndCache(); + return this.tupleSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -181,113 +262,125 @@ public class SetOfTuplesValue extends EnumerableValue implements Enumerable { else if (this.tupleSet == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.tupleSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.tupleSet == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.tupleSet == DummyEnum) { this.tupleSet = val; } + if (this.tupleSet == DummyEnum) { this.tupleSet = val; } } } } - + /* The string representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; try { - if (unlazy) { - long sz = 1; - for (int i = 0; i < this.sets.length; i++) { - sz *= this.sets[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - unlazy = false; - break; - } - } - unlazy = sz < TLCGlobals.enumBound; + boolean unlazy = expand; + try { + if (unlazy) { + long sz = 1; + for (int i = 0; i < this.sets.length; i++) { + sz *= this.sets[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + unlazy = false; + break; + } + } + unlazy = sz < TLCGlobals.enumBound; + } } - } - catch (Throwable e) { unlazy = false; } + catch (Throwable e) { unlazy = false; } - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - if (this.sets.length > 0) { - sb.append("("); - this.sets[0].toString(sb, offset); - } - for (int i = 1; i < this.sets.length; i++) { - sb.append(" X "); - this.sets[i].toString(sb, offset); + if (unlazy) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); } - if (this.sets.length > 0) { - sb.append(")"); + else { + if (this.sets.length > 0) { + sb.append("("); + this.sets[0].toString(sb, offset); + } + for (int i = 1; i < this.sets.length; i++) { + sb.append(" X "); + this.sets[i].toString(sb, offset); + } + if (this.sets.length > 0) { + sb.append(")"); + } + return sb; } - return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } - + public final ValueEnumeration elements() { - if (this.tupleSet == null || this.tupleSet == DummyEnum) { - return new Enumerator(); + try { + if (this.tupleSet == null || this.tupleSet == DummyEnum) { + return new Enumerator(); + } + return this.tupleSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.tupleSet.elements(); } final class Enumerator implements ValueEnumeration { private ValueEnumeration[] enums; private Value[] currentElems; private boolean isDone; - + public Enumerator() { this.enums = new ValueEnumeration[sets.length]; this.currentElems = new Value[sets.length]; this.isDone = false; for (int i = 0; i < sets.length; i++) { - if (sets[i] instanceof Enumerable) { - this.enums[i] = ((Enumerable)sets[i]).elements(); - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] == null) { - this.enums = null; - this.isDone = true; - break; - } - } - else { - Assert.fail("Attempted to enumerate a set of the form s1 \\X s2 ... \\X sn," + - "\nbut can't enumerate s" + i + ":\n" + ppr(sets[i].toString())); - } + if (sets[i] instanceof Enumerable) { + this.enums[i] = ((Enumerable)sets[i]).elements(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] == null) { + this.enums = null; + this.isDone = true; + break; + } + } + else { + Assert.fail("Attempted to enumerate a set of the form s1 \\X s2 ... \\X sn," + + "\nbut can't enumerate s" + i + ":\n" + ppr(sets[i].toString())); + } } } public final void reset() { if (this.enums != null) { - for (int i = 0; i < this.enums.length; i++) { - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - this.isDone = false; + for (int i = 0; i < this.enums.length; i++) { + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + this.isDone = false; } } - + public final Value nextElement() { if (this.isDone) return null; Value[] elems = new Value[this.currentElems.length]; for (int i = 0; i < elems.length; i++) { - elems[i] = this.currentElems[i]; + elems[i] = this.currentElems[i]; } for (int i = elems.length-1; i >= 0; i--) { - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] != null) break; - if (i == 0) { - this.isDone = true; - break; - } - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] != null) break; + if (i == 0) { + this.isDone = true; + break; + } + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); } return new TupleValue(elems); } diff --git a/tlatools/src/tlc2/value/SetPredValue.java b/tlatools/src/tlc2/value/SetPredValue.java index 93ed6b378fbd27dd2a72c9f443bef7166797ad4b..3e153104dae84bd032d08e7b8aff33f3214024a9 100644 --- a/tlatools/src/tlc2/value/SetPredValue.java +++ b/tlatools/src/tlc2/value/SetPredValue.java @@ -1,6 +1,7 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Thu 5 Jul 2007 at 4:44:23 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Thu 5 Jul 2007 at 4:44:23 PST by lamport // modified on Fri Aug 10 15:10:04 PDT 2001 by yuanyu package tlc2.value; @@ -11,6 +12,8 @@ import java.io.ObjectOutputStream; import tla2sany.semantic.FormalParamNode; import tla2sany.semantic.SemanticNode; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.tool.EvalException; import tlc2.tool.TLCState; import tlc2.tool.Tool; @@ -32,13 +35,13 @@ public class SetPredValue extends EnumerableValue implements Enumerable { /* Constructor */ public SetPredValue(Object vars, Value inVal, SemanticNode pred, Tool tool, - Context con, TLCState s0, TLCState s1, int control) { + Context con, TLCState s0, TLCState s1, int control) { this.vars = vars; this.inVal = inVal; this.pred = pred; this.tool = tool; this.con = con; - this.state = s0.copy(); + this.state = s0.copy(); if (s1 != null) { this.pstate = s1.copy(); } else { @@ -57,88 +60,129 @@ public class SetPredValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SETPREDVALUE; } public final int compareTo(Object obj) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.compareTo(obj); + try { + this.inVal = SetEnumValue.convert(this); + this.tool = null; + return this.inVal.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.equals(obj); + try { + this.inVal = SetEnumValue.convert(this); + this.tool = null; + return this.inVal.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - if (this.tool == null) { - return this.inVal.member(elem); - } try { - if (this.inVal.member(elem)) { - Context con1 = this.con; - if (this.vars instanceof FormalParamNode) { - con1 = con1.cons((FormalParamNode)this.vars, elem); - } - else { - FormalParamNode[] ids = (FormalParamNode[])this.vars; - TupleValue tv = TupleValue.convert(elem); - if ((tv != null) && (tv.elems.length == ids.length)) { - Value[] vals = ((TupleValue)tv).elems; - for (int i = 0; i < ids.length; i++) { - con1 = con1.cons(ids[i], vals[i]); - } - } - else { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of a set of " + ids.length + "-tuples."); - } - } - Value res = this.tool.eval(this.pred, con1, this.state, this.pstate, this.control); - if (!(res instanceof BoolValue)) { - Assert.fail("The evaluation of predicate " + this.pred + - " yielded non-Boolean value."); - } - return ((BoolValue)res).val; + if (this.tool == null) { + return this.inVal.member(elem); + } + try { + if (this.inVal.member(elem)) { + Context con1 = this.con; + if (this.vars instanceof FormalParamNode) { + con1 = con1.cons((FormalParamNode)this.vars, elem); + } + else { + FormalParamNode[] ids = (FormalParamNode[])this.vars; + TupleValue tv = TupleValue.convert(elem); + if ((tv != null) && (tv.elems.length == ids.length)) { + Value[] vals = ((TupleValue)tv).elems; + for (int i = 0; i < ids.length; i++) { + con1 = con1.cons(ids[i], vals[i]); + } + } + else { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of a set of " + ids.length + "-tuples."); + } + } + Value res = this.tool.eval(this.pred, con1, this.state, this.pstate, this.control); + if (!(res instanceof BoolValue)) { + Assert.fail("The evaluation of predicate " + this.pred + + " yielded non-Boolean value."); + } + return ((BoolValue)res).val; + } } + catch (EvalException e) { + Assert.fail("Cannot decide if element:\n" + ppr(elem.toString()) + + "\n is element of:\n" + ppr(this.inVal.toString()) + + "\nand satisfies the predicate " + this.pred); + } + return false; } - catch (EvalException e) { - Assert.fail("Cannot decide if element:\n" + ppr(elem.toString()) + - "\n is element of:\n" + ppr(this.inVal.toString()) + - "\nand satisfies the predicate " + this.pred); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return false; } public final boolean isFinite() { - if (!(this.inVal.isFinite())) { - Assert.fail("Attempted to check if expression of form {x \\in S : p(x)} is a " + - "finite set, but cannot check if S:\n" + ppr(this.inVal.toString()) + - "\nis finite."); + try { + if (!(this.inVal.isFinite())) { + Assert.fail("Attempted to check if expression of form {x \\in S : p(x)} is a " + + "finite set, but cannot check if S:\n" + ppr(this.inVal.toString()) + + "\nis finite."); + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.size(); + try { + this.inVal = SetEnumValue.convert(this); + this.tool = null; + return this.inVal.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - private final void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { + private final void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { this.inVal = (Value)ois.readObject(); this.tool = null; } @@ -150,112 +194,156 @@ public class SetPredValue extends EnumerableValue implements Enumerable { } oos.writeObject(this.inVal); } - + /* This method normalizes (destructively) this set. */ public final boolean isNormalized() { - return this.inVal.isNormalized(); + try { + return this.inVal.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - public final void normalize() { this.inVal.normalize(); } + public final void normalize() { + try { + this.inVal.normalize(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } public final boolean isDefined() { return true; } public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The fingerprint method */ public final long fingerPrint(long fp) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.fingerPrint(fp); + try { + this.inVal = SetEnumValue.convert(this); + this.tool = null; + return this.inVal.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.permute(perm); + try { + this.inVal = SetEnumValue.convert(this); + this.tool = null; + return this.inVal.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The string representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); + try { + if (expand) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); + } } - } - catch (Throwable e) { /*SKIP*/ } + catch (Throwable e) { /*SKIP*/ } - sb.append("{"); - if (this.vars instanceof FormalParamNode) { - sb.append(((FormalParamNode)this.vars).getName()); - } - else { - FormalParamNode[] ids = (FormalParamNode[])this.vars; - if (ids.length != 0) sb.append(ids[0].getName()); - for (int i = 1; i < ids.length; i++) { - sb.append(", " + ids[i].getName()); + sb.append("{"); + if (this.vars instanceof FormalParamNode) { + sb.append(((FormalParamNode)this.vars).getName()); + } + else { + FormalParamNode[] ids = (FormalParamNode[])this.vars; + if (ids.length != 0) sb.append(ids[0].getName()); + for (int i = 1; i < ids.length; i++) { + sb.append(", " + ids[i].getName()); + } } + sb.append(" \\in " + this.inVal + " : <expression "); + sb.append(this.pred + "> }"); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - sb.append(" \\in " + this.inVal + " : <expression "); - sb.append(this.pred + "> }"); - return sb; } - + public final ValueEnumeration elements() { - if (this.tool == null) { - return ((SetEnumValue)this.inVal).elements(); + try { + if (this.tool == null) { + return ((SetEnumValue)this.inVal).elements(); + } + return new Enumerator(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new Enumerator(); } - + final class Enumerator implements ValueEnumeration { ValueEnumeration Enum; public Enumerator() { if (!(inVal instanceof Enumerable)) { - Assert.fail("Attempted to enumerate { x \\in S : p(x) } when S:\n" + - ppr(inVal.toString()) + "\nis not enumerable"); + Assert.fail("Attempted to enumerate { x \\in S : p(x) } when S:\n" + + ppr(inVal.toString()) + "\nis not enumerable"); } this.Enum = ((Enumerable)inVal).elements(); } - + public final void reset() { this.Enum.reset(); } public final Value nextElement() { Value elem; while ((elem = this.Enum.nextElement()) != null) { - Context con1 = con; - if (vars instanceof FormalParamNode) { - con1 = con1.cons((FormalParamNode)vars, elem); - } - else { - FormalParamNode[] ids = (FormalParamNode[])vars; - TupleValue tv = TupleValue.convert(elem); - if ((tv != null) && - (((TupleValue)tv).elems.length == ids.length)) { - Value[] vals = ((TupleValue)tv).elems; - for (int i = 0; i < ids.length; i++) { - con1 = con1.cons(ids[i], vals[i]); - } - } - else { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of a set of " + ids.length + "-tuples."); - } - } - Value res = tool.eval(pred, con1, state, pstate, control); - if (!(res instanceof BoolValue)) { - Assert.fail("Evaluating predicate " + pred + " yielded non-Boolean value."); - } - if (((BoolValue)res).val) return elem; + Context con1 = con; + if (vars instanceof FormalParamNode) { + con1 = con1.cons((FormalParamNode)vars, elem); + } + else { + FormalParamNode[] ids = (FormalParamNode[])vars; + TupleValue tv = TupleValue.convert(elem); + if ((tv != null) && + (((TupleValue)tv).elems.length == ids.length)) { + Value[] vals = ((TupleValue)tv).elems; + for (int i = 0; i < ids.length; i++) { + con1 = con1.cons(ids[i], vals[i]); + } + } + else { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of a set of " + ids.length + "-tuples."); + } + } + Value res = tool.eval(pred, con1, state, pstate, control); + if (!(res instanceof BoolValue)) { + Assert.fail("Evaluating predicate " + pred + " yielded non-Boolean value."); + } + if (((BoolValue)res).val) return elem; } return null; } - + } } diff --git a/tlatools/src/tlc2/value/StringValue.java b/tlatools/src/tlc2/value/StringValue.java index 3e5507a7c20d31b14a14ef4179fb7fcb9ef6b63c..e637dad783d60409899c8d1fde30d81bd03c019b 100644 --- a/tlatools/src/tlc2/value/StringValue.java +++ b/tlatools/src/tlc2/value/StringValue.java @@ -1,16 +1,19 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:19:41 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:19:41 PST by lamport // modified on Fri Aug 10 15:06:37 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.util.FP64; import util.Assert; import util.UniqueString; public class StringValue extends Value { - public UniqueString val; + public UniqueString val; /* Constructor */ public StringValue(String str) { @@ -21,65 +24,107 @@ public class StringValue extends Value { public StringValue(UniqueString var) { this.val = var; } - + public final byte getKind() { return STRINGVALUE; } public final UniqueString getVal() { return this.val; } public final int compareTo(Object obj) { - if (obj instanceof StringValue) { - return this.val.compareTo(((StringValue)obj).val); + try { + if (obj instanceof StringValue) { + return this.val.compareTo(((StringValue)obj).val); + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare string " + ppr(this.toString()) + + " with non-string:\n" + ppr(obj.toString())); + } + return 1; } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare string " + ppr(this.toString()) + - " with non-string:\n" + ppr(obj.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return 1; } public final boolean equals(Object obj) { - if (obj instanceof StringValue) { - return this.val.equals(((StringValue)obj).getVal()); + try { + if (obj instanceof StringValue) { + return this.val.equals(((StringValue)obj).getVal()); + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to check equality of string " + ppr(this.toString()) + + " with non-string:\n" + ppr(obj.toString())); + } + return ((ModelValue) obj).modelValueEquals(this) ; } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to check equality of string " + ppr(this.toString()) + - " with non-string:\n" + ppr(obj.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ((ModelValue) obj).modelValueEquals(this) ; } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the string " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element of the string " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the string " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the string " + ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the string " + - ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the string " + + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the string " + - ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the string " + + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - Assert.fail("Attempted to compute the number of elements in the string " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the string " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { return true; } @@ -91,18 +136,38 @@ public class StringValue extends Value { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return ((val instanceof StringValue) && - this.equals(val)); + try { + return ((val instanceof StringValue) && + this.equals(val)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final int length() { + try { + return this.val.length(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - public final int length() { return this.val.length(); } - /* The fingerprint method */ public final long fingerPrint(long fp) { - fp = FP64.Extend(fp, STRINGVALUE) ; - fp = FP64.Extend(fp, this.val.length()) ; - fp = FP64.Extend(fp, this.val.toString()); - return fp; + try { + fp = FP64.Extend(fp, STRINGVALUE) ; + fp = FP64.Extend(fp, this.val.length()) ; + fp = FP64.Extend(fp, this.val.toString()); + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { return this; } @@ -112,39 +177,51 @@ public class StringValue extends Value { * with special characters are printed properly. * *************************************************************************/ final String PrintVersion(String str) { - StringBuffer buf = new StringBuffer(str.length()) ; - for (int i = 0 ; i < str.length() ; i++) { - switch (str.charAt(i)) { - case '\"' : - buf.append("\\\"") ; - break ; - case '\\' : - buf.append("\\\\") ; - break ; - case '\t' : - buf.append("\\t") ; - break ; - case '\n' : - buf.append("\\n") ; - break ; - case '\f' : - buf.append("\\f") ; - break ; - case '\r' : - buf.append("\\r") ; - break ; - default : - buf.append(str.charAt(i)) ; - break ; - } // switch - }; // for - return buf.toString(); + try { + StringBuffer buf = new StringBuffer(str.length()) ; + for (int i = 0 ; i < str.length() ; i++) { + switch (str.charAt(i)) { + case '\"' : + buf.append("\\\"") ; + break ; + case '\\' : + buf.append("\\\\") ; + break ; + case '\t' : + buf.append("\\t") ; + break ; + case '\n' : + buf.append("\\n") ; + break ; + case '\f' : + buf.append("\\f") ; + break ; + case '\r' : + buf.append("\\r") ; + break ; + default : + buf.append(str.charAt(i)) ; + break ; + } // switch + }; // for + return buf.toString(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The string representation of the value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append("\"" + PrintVersion(this.val.toString()) + "\""); + try { + return sb.append("\"" + PrintVersion(this.val.toString()) + "\""); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/SubsetValue.java b/tlatools/src/tlc2/value/SubsetValue.java index 7f36d6803422dfc43b18a9c7ed08e5de2f66728e..174b79990dce543e2e1ecc36341453f0bfde3115 100644 --- a/tlatools/src/tlc2/value/SubsetValue.java +++ b/tlatools/src/tlc2/value/SubsetValue.java @@ -1,12 +1,15 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:46:07 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:46:07 PST by lamport // modified on Fri Aug 10 15:10:16 PDT 2001 by yuanyu package tlc2.value; import java.util.BitSet; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.output.EC; import util.Assert; @@ -23,101 +26,191 @@ public class SubsetValue extends EnumerableValue implements Enumerable { public final byte getKind() { return SUBSETVALUE; } public final int compareTo(Object obj) { - if (obj instanceof SubsetValue) { - return this.set.compareTo(((SubsetValue)obj).set); + try { + if (obj instanceof SubsetValue) { + return this.set.compareTo(((SubsetValue)obj).set); + } + this.convertAndCache(); + return this.pset.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.convertAndCache(); - return this.pset.compareTo(obj); } - + public final boolean equals(Object obj) { - if (obj instanceof SubsetValue) { - return this.set.equals(((SubsetValue)obj).set); + try { + if (obj instanceof SubsetValue) { + return this.set.equals(((SubsetValue)obj).set); + } + this.convertAndCache(); + return this.pset.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - this.convertAndCache(); - return this.pset.equals(obj); } public final boolean member(Value val) { - if (val instanceof Enumerable) { - ValueEnumeration Enum = ((Enumerable)val).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!this.set.member(elem)) return false; + try { + if (val instanceof Enumerable) { + ValueEnumeration Enum = ((Enumerable)val).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!this.set.member(elem)) return false; + } } + else { + Assert.fail("Attempted to check if the non-enumerable value\n" + + ppr(val.toString()) + "\nis element of\n" + ppr(this.toString())); + } + return true; } - else { - Assert.fail("Attempted to check if the non-enumerable value\n" + - ppr(val.toString()) + "\nis element of\n" + ppr(this.toString())); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } - public Value isSubsetEq(Value other) { - // Reduce (SUBSET A \subseteq SUBSET B) to (A \subseteq B) to avoid - // exponential blowup inherent in generating the power set. - if (other instanceof SubsetValue && this.set instanceof EnumerableValue) { - final SubsetValue sv = (SubsetValue) other; - return ((EnumerableValue) this.set).isSubsetEq(sv.set); - } - return super.isSubsetEq(other); - } + public Value isSubsetEq(Value other) { + try { + // Reduce (SUBSET A \subseteq SUBSET B) to (A \subseteq B) to avoid + // exponential blowup inherent in generating the power set. + if (other instanceof SubsetValue && this.set instanceof EnumerableValue) { + final SubsetValue sv = (SubsetValue) other; + return ((EnumerableValue) this.set).isSubsetEq(sv.set); + } + return super.isSubsetEq(other); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean isFinite() { + try { + return this.set.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } - public final boolean isFinite() { return this.set.isFinite(); } - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - int sz = this.set.size(); - if (sz >= 31) { - Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + - ppr(this.toString())); + try { + int sz = this.set.size(); + if (sz >= 31) { + Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + + ppr(this.toString())); + } + return (1 << sz); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return (1 << sz); } public final boolean isNormalized() { - return (this.pset != null && - this.pset != DummyEnum && - this.pset.isNormalized()); + try { + return (this.pset != null && + this.pset != DummyEnum && + this.pset.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final void normalize() { - if (this.pset == null || this.pset == DummyEnum) { - this.set.normalize(); + try { + if (this.pset == null || this.pset == DummyEnum) { + this.set.normalize(); + } + else { + this.pset.normalize(); + } } - else { - this.pset.normalize(); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } - public final boolean isDefined() { return this.set.isDefined(); } + public final boolean isDefined() { + try { + return this.set.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } public final Value deepCopy() { return this; } - public final boolean assignable(Value val) { return this.equals(val); } + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } /* The fingerprint */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.pset.fingerPrint(fp); + try { + this.convertAndCache(); + return this.pset.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.pset.permute(perm); + try { + this.convertAndCache(); + return this.pset.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -127,49 +220,61 @@ public class SubsetValue extends EnumerableValue implements Enumerable { else if (this.pset == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.pset == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.pset == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.pset == DummyEnum) { this.pset = val; } + if (this.pset == DummyEnum) { this.pset = val; } } } } - + /* The string representation */ public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; try { - if (unlazy) { - unlazy = this.set.size() < 7; + boolean unlazy = expand; + try { + if (unlazy) { + unlazy = this.set.size() < 7; + } } - } - catch (Throwable e) { unlazy = false; } + catch (Throwable e) { unlazy = false; } - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); + if (unlazy) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); + } + else { + sb = sb.append("SUBSET "); + sb = this.set.toString(sb, offset); + return sb; + } } - else { - sb = sb.append("SUBSET "); - sb = this.set.toString(sb, offset); - return sb; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final ValueEnumeration elements() { - if (this.pset == null || this.pset == DummyEnum) { - return new Enumerator(); + try { + if (this.pset == null || this.pset == DummyEnum) { + return new Enumerator(); + } + return this.pset.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.pset.elements(); } final class Enumerator implements ValueEnumeration { ValueVec elems; private BitSet descriptor; - + public Enumerator() { set = SetEnumValue.convert(set); set.normalize(); @@ -180,37 +285,37 @@ public class SubsetValue extends EnumerableValue implements Enumerable { public final void reset() { this.descriptor = new BitSet(this.elems.size()); } - + public final Value nextElement() { if (this.descriptor == null) return null; ValueVec vals = new ValueVec(); int sz = this.elems.size(); if (sz == 0) { - this.descriptor = null; + this.descriptor = null; } else { - for (int i = 0; i < sz; i++) { - if (this.descriptor.get(i)) { - vals.addElement(this.elems.elementAt(i)); - } - } - for (int i = 0; i < sz; i++) { - if (this.descriptor.get(i)) { - this.descriptor.clear(i); - if (i >= sz - 1) { - this.descriptor = null; - break; - } - } - else { - this.descriptor.set(i); - break; - } - } + for (int i = 0; i < sz; i++) { + if (this.descriptor.get(i)) { + vals.addElement(this.elems.elementAt(i)); + } + } + for (int i = 0; i < sz; i++) { + if (this.descriptor.get(i)) { + this.descriptor.clear(i); + if (i >= sz - 1) { + this.descriptor = null; + break; + } + } + else { + this.descriptor.set(i); + break; + } + } } return new SetEnumValue(vals, true); } - + } - + } diff --git a/tlatools/src/tlc2/value/TupleValue.java b/tlatools/src/tlc2/value/TupleValue.java index d385eeaad817079385d1c9f80c6a730d395eb91a..9424f81c0f74e96b84f706b836ad005f497ea56b 100644 --- a/tlatools/src/tlc2/value/TupleValue.java +++ b/tlatools/src/tlc2/value/TupleValue.java @@ -1,11 +1,14 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport // modified on Fri Aug 10 15:10:22 PDT 2001 by yuanyu package tlc2.value; import tla2sany.semantic.SymbolNode; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.EvalControl; @@ -23,119 +26,173 @@ public class TupleValue extends Value implements Applicable { this.elems = new Value[1]; this.elems[0] = v; } - + public TupleValue(Value v1, Value v2) { this.elems = new Value[2]; this.elems[0] = v1; this.elems[1] = v2; } - + public final byte getKind() { return TUPLEVALUE; } public final int compareTo(Object obj) { - TupleValue tv = convert(obj); - if (tv == null) { - // Well, we have to convert this to function and compare. - return FcnRcdValue.convert(this).compareTo(obj); - } - int len = this.elems.length; - int cmp = len - tv.elems.length; - if (cmp == 0) { - for (int i = 0; i < len; i++) { - cmp = this.elems[i].compareTo(tv.elems[i]); - if (cmp != 0) break; + try { + TupleValue tv = convert(obj); + if (tv == null) { + // Well, we have to convert this to function and compare. + return FcnRcdValue.convert(this).compareTo(obj); } + int len = this.elems.length; + int cmp = len - tv.elems.length; + if (cmp == 0) { + for (int i = 0; i < len; i++) { + cmp = this.elems[i].compareTo(tv.elems[i]); + if (cmp != 0) break; + } + } + return cmp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return cmp; } public final boolean equals(Object obj) { - TupleValue tv = convert(obj); - if (tv == null) { - // Well, we have to convert this to function and compare. - return FcnRcdValue.convert(this).equals(obj); - } - int len = this.elems.length; - if (len != tv.elems.length) - return false; - for (int i = 0; i < len; i++) { - if (!this.elems[i].equals(tv.elems[i])) - return false; - } - return true; + try { + TupleValue tv = convert(obj); + if (tv == null) { + // Well, we have to convert this to function and compare. + return FcnRcdValue.convert(this).equals(obj); + } + int len = this.elems.length; + if (len != tv.elems.length) + return false; + for (int i = 0; i < len; i++) { + if (!this.elems[i].equals(tv.elems[i])) + return false; + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check set membership in a tuple value."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check set membership in a tuple value."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { return true; } - + public final Value apply(Value arg, int control) { - if (!(arg instanceof IntValue)) { - Assert.fail("Attempted to apply tuple to a non-integer argument."); + try { + if (!(arg instanceof IntValue)) { + Assert.fail("Attempted to apply tuple to a non-integer argument."); + } + int idx = ((IntValue)arg).val; + if (idx <= 0 || idx > this.elems.length) { + Assert.fail("Attempted to apply tuple\n" + ppr(this.toString()) + + "\nto integer " + idx + " which is out of domain."); + } + return this.elems[idx-1]; } - int idx = ((IntValue)arg).val; - if (idx <= 0 || idx > this.elems.length) { - Assert.fail("Attempted to apply tuple\n" + ppr(this.toString()) + - "\nto integer " + idx + " which is out of domain."); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.elems[idx-1]; } public final Value apply(Value[] args, int control) { - if (args.length != 1) { - Assert.fail("Attetmpted to apply tuple with wrong number of arguments."); + try { + if (args.length != 1) { + Assert.fail("Attetmpted to apply tuple with wrong number of arguments."); + } + return this.apply(args[0], EvalControl.Clear); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.apply(args[0], EvalControl.Clear); } - + public final Value select(Value arg) { - if (!(arg instanceof IntValue)) { - Assert.fail("Attempted to apply tuple to a non-integer argument " + - ppr(arg.toString()) + "."); + try { + if (!(arg instanceof IntValue)) { + Assert.fail("Attempted to apply tuple to a non-integer argument " + + ppr(arg.toString()) + "."); + } + int idx = ((IntValue)arg).val; + if (idx > 0 && idx <= this.elems.length) { + return this.elems[idx-1]; + } + return null; } - int idx = ((IntValue)arg).val; - if (idx > 0 && idx <= this.elems.length) { - return this.elems[idx-1]; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return null; } public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - int tlen = this.elems.length; - Value[] newElems = new Value[tlen]; - Value arcVal = ex.path[ex.idx]; - if (arcVal instanceof IntValue) { - int idx = ((IntValue)arcVal).val - 1; - if (0 <= idx && idx < tlen) { - for (int i = 0; i < tlen; i++) { - newElems[i] = this.elems[i]; - } - ex.idx++; - newElems[idx] = this.elems[idx].takeExcept(ex); - } - return new TupleValue(newElems); + try { + if (ex.idx < ex.path.length) { + int tlen = this.elems.length; + Value[] newElems = new Value[tlen]; + Value arcVal = ex.path[ex.idx]; + if (arcVal instanceof IntValue) { + int idx = ((IntValue)arcVal).val - 1; + if (0 <= idx && idx < tlen) { + for (int i = 0; i < tlen; i++) { + newElems[i] = this.elems[i]; + } + ex.idx++; + newElems[idx] = this.elems[idx].takeExcept(ex); + } + return new TupleValue(newElems); + } + MP.printWarning(EC.TLC_WRONG_TUPLE_FIELD_NAME, new String[]{ppr(arcVal.toString())}); } - MP.printWarning(EC.TLC_WRONG_TUPLE_FIELD_NAME, new String[]{ppr(arcVal.toString())}); + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - Value val = this; - for (int i = 0; i < exs.length; i++) { - val = val.takeExcept(exs[i]); + try { + Value val = this; + for (int i = 0; i < exs.length; i++) { + val = val.takeExcept(exs[i]); + } + return val; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return val; } public final Value getDomain() { - return new IntervalValue(1, this.size()); + try { + return new IntervalValue(1, this.size()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final int size() { return this.elems.length; } /* @@ -149,21 +206,21 @@ public class TupleValue extends Value implements Applicable { else if (val instanceof FcnRcdValue) { FcnRcdValue fcn = (FcnRcdValue)val; if (fcn.intv != null) { - if (fcn.intv.low != 1) return null; - return new TupleValue(fcn.values); + if (fcn.intv.low != 1) return null; + return new TupleValue(fcn.values); } int len = fcn.values.length; Value[] elems = new Value[len]; for (int i = 0; i < len; i++) { - if (!(fcn.domain[i] instanceof IntValue)) return null; - int idx = ((IntValue)fcn.domain[i]).val; - if (0 < idx && idx <= len) { - if (elems[idx-1] != null) return null; - elems[idx-1] = fcn.values[i]; - } - else { - return null; - } + if (!(fcn.domain[i] instanceof IntValue)) return null; + int idx = ((IntValue)fcn.domain[i]).val; + if (0 < idx && idx <= len) { + if (elems[idx-1] != null) return null; + elems[idx-1] = fcn.values[i]; + } + else { + return null; + } } return new TupleValue(elems); } @@ -173,31 +230,31 @@ public class TupleValue extends Value implements Applicable { Value dom = fcn.params.domains[0]; SymbolNode var = fcn.params.formals[0][0]; if (dom instanceof IntervalValue) { - IntervalValue intv = (IntervalValue)dom; - if (intv.low != 1) return null; - Value[] elems = new Value[intv.high]; - for (int i = 1; i <= intv.high; i++) { - Context c1 = fcn.con.cons(var, IntValue.gen(i)); - elems[i-1] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control); - } - return new TupleValue(elems); + IntervalValue intv = (IntervalValue)dom; + if (intv.low != 1) return null; + Value[] elems = new Value[intv.high]; + for (int i = 1; i <= intv.high; i++) { + Context c1 = fcn.con.cons(var, IntValue.gen(i)); + elems[i-1] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control); + } + return new TupleValue(elems); } else { - SetEnumValue eSet = SetEnumValue.convert(dom); - if (eSet == null) - Assert.fail("To convert a function of form [x \\in S |-> f(x)] " + - "to a tuple, the set S must be enumerable."); - eSet.normalize(); - int len = eSet.size(); - Value[] elems = new Value[len]; - for (int i = 0; i < len; i++) { - Value argVal = eSet.elems.elementAt(i); - if (!(argVal instanceof IntValue)) return null; - if (((IntValue)argVal).val != i + 1) return null; - Context c1 = fcn.con.cons(var, argVal); - elems[i] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control); - } - return new TupleValue(elems); + SetEnumValue eSet = SetEnumValue.convert(dom); + if (eSet == null) + Assert.fail("To convert a function of form [x \\in S |-> f(x)] " + + "to a tuple, the set S must be enumerable."); + eSet.normalize(); + int len = eSet.size(); + Value[] elems = new Value[len]; + for (int i = 0; i < len; i++) { + Value argVal = eSet.elems.elementAt(i); + if (!(argVal instanceof IntValue)) return null; + if (((IntValue)argVal).val != i + 1) return null; + Context c1 = fcn.con.cons(var, argVal); + elems[i] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control); + } + return new TupleValue(elems); } } else if ((val instanceof RecordValue) && @@ -209,74 +266,110 @@ public class TupleValue extends Value implements Applicable { /* The normalization of the value. */ public final boolean isNormalized() { return true; } - + public final void normalize() { /*nop*/ } public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.elems.length; i++) { - defined = defined && this.elems[i].isDefined(); + try { + boolean defined = true; + for (int i = 0; i < this.elems.length; i++) { + defined = defined && this.elems[i].isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return defined; } public final Value deepCopy() { - Value[] vals = new Value[this.elems.length]; - for (int i = 0; i < this.elems.length; i++) { - vals[i] = this.elems[i].deepCopy(); + try { + Value[] vals = new Value[this.elems.length]; + for (int i = 0; i < this.elems.length; i++) { + vals[i] = this.elems[i].deepCopy(); + } + return new TupleValue(vals); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return new TupleValue(vals); } public final boolean assignable(Value val) { - boolean canAssign = ((val instanceof TupleValue) && - (this.elems.length == ((TupleValue)val).elems.length)); - if (!canAssign) return false; - for (int i = 0; i < this.elems.length; i++) { - canAssign = canAssign && this.elems[i].assignable(((TupleValue)val).elems[i]); + try { + boolean canAssign = ((val instanceof TupleValue) && + (this.elems.length == ((TupleValue)val).elems.length)); + if (!canAssign) return false; + for (int i = 0; i < this.elems.length; i++) { + canAssign = canAssign && this.elems[i].assignable(((TupleValue)val).elems[i]); + } + return canAssign; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return canAssign; } - + /* The fingerprint method: tuples are functions. */ public final long fingerPrint(long fp) { - int len = this.elems.length; - fp = FP64.Extend(fp, FCNRCDVALUE); - fp = FP64.Extend(fp, len); - for (int i = 0; i < len; i++) { - fp = FP64.Extend(fp, INTVALUE); - fp = FP64.Extend(fp, i+1); - fp = this.elems[i].fingerPrint(fp); - } - return fp; + try { + int len = this.elems.length; + fp = FP64.Extend(fp, FCNRCDVALUE); + fp = FP64.Extend(fp, len); + for (int i = 0; i < len; i++) { + fp = FP64.Extend(fp, INTVALUE); + fp = FP64.Extend(fp, i+1); + fp = this.elems[i].fingerPrint(fp); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - Value[] vals = new Value[this.elems.length]; - boolean changed = false; - for (int i = 0; i < vals.length; i++) { - vals[i] = this.elems[i].permute(perm); - changed = changed || (vals[i] != this.elems[i]); + try { + Value[] vals = new Value[this.elems.length]; + boolean changed = false; + for (int i = 0; i < vals.length; i++) { + vals[i] = this.elems[i].permute(perm); + changed = changed || (vals[i] != this.elems[i]); + } + if (changed) { + return new TupleValue(vals); + } + return this; } - if (changed) { - return new TupleValue(vals); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } /* The string representation of this value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - sb.append("<<"); - int len = this.elems.length; - if (len > 0) { - sb = this.elems[0].toString(sb, offset); + try { + sb.append("<<"); + int len = this.elems.length; + if (len > 0) { + sb = this.elems[0].toString(sb, offset); + } + for (int i = 1; i < len; i++) { + sb = sb.append(", "); + sb = this.elems[i].toString(sb, offset); + } + sb.append(">>"); + return sb; } - for (int i = 1; i < len; i++) { - sb = sb.append(", "); - sb = this.elems[i].toString(sb, offset); + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - sb.append(">>"); - return sb; } } diff --git a/tlatools/src/tlc2/value/UndefValue.java b/tlatools/src/tlc2/value/UndefValue.java index 34765bdb0019eb63e66fdc19e18e9e1b5cad9aba..4947a6f9ab525b7c38df7c35d738b8f4a96e068a 100644 --- a/tlatools/src/tlc2/value/UndefValue.java +++ b/tlatools/src/tlc2/value/UndefValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:06 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:06 PST by lamport // modified on Tue Aug 15 23:08:23 PDT 2000 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import util.Assert; public class UndefValue extends Value { @@ -14,45 +17,87 @@ public class UndefValue extends Value { public byte getKind() { return UNDEFVALUE; } public final int compareTo(Object obj) { - return (obj instanceof UndefValue) ? 0 : 1; + try { + return (obj instanceof UndefValue) ? 0 : 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - return (obj instanceof UndefValue); + try { + return (obj instanceof UndefValue); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element " + ppr(this.toString())); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + + "\nis an element " + ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isFinite() { - Assert.fail("Attempted to check if the value " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy + try { + Assert.fail("Attempted to check if the value " + ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the value " + - ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the value " + + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } - + public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the value " + - ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the value " + + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - Assert.fail("Attempted to compute the number of elements in the value " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the value " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { return true; } @@ -67,7 +112,13 @@ public class UndefValue extends Value { /* The string representation. */ public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append("UNDEF"); + try { + return sb.append("UNDEF"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/UnionValue.java b/tlatools/src/tlc2/value/UnionValue.java index 3473991521c08fd6bda434e863700f8ca9a73176..b203236454d5b48a3ee5517887a4ddd38932d07e 100644 --- a/tlatools/src/tlc2/value/UnionValue.java +++ b/tlatools/src/tlc2/value/UnionValue.java @@ -1,16 +1,19 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:46:50 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:46:50 PST by lamport // modified on Fri Aug 10 15:10:39 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import util.Assert; public class UnionValue extends EnumerableValue implements Enumerable { public Value set; protected SetEnumValue realSet; - + /* Constructor */ public UnionValue(Value set) { this.set = set; @@ -20,79 +23,147 @@ public class UnionValue extends EnumerableValue implements Enumerable { public byte getKind() { return UNIONVALUE; } public final int compareTo(Object obj) { - this.convertAndCache(); - return this.realSet.compareTo(obj); + try { + this.convertAndCache(); + return this.realSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final boolean equals(Object obj) { - this.convertAndCache(); - return this.realSet.equals(obj); + try { + this.convertAndCache(); + return this.realSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value elem) { - if (!(this.set instanceof Enumerable)) { - Assert.fail("Attempted to check if:\n " + ppr(elem.toString()) + - "\nis an element of the non-enumerable set:\n " + - ppr(this.toString())); + try { + if (!(this.set instanceof Enumerable)) { + Assert.fail("Attempted to check if:\n " + ppr(elem.toString()) + + "\nis an element of the non-enumerable set:\n " + + ppr(this.toString())); + } + ValueEnumeration Enum = ((Enumerable)this.set).elements(); + Value val; + while ((val = Enum.nextElement()) != null) { + if (val.member(elem)) return true; + } + return false; } - ValueEnumeration Enum = ((Enumerable)this.set).elements(); - Value val; - while ((val = Enum.nextElement()) != null) { - if (val.member(elem)) return true; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return false; } public final boolean isFinite() { - if (!(this.set instanceof Enumerable)) { - Assert.fail("Attempted to check if the nonenumerable set:\n" + ppr(this.toString()) + - "\nis a finite set."); + try { + if (!(this.set instanceof Enumerable)) { + Assert.fail("Attempted to check if the nonenumerable set:\n" + ppr(this.toString()) + + "\nis a finite set."); + } + ValueEnumeration Enum = ((Enumerable)this.set).elements(); + Value val; + while ((val = Enum.nextElement()) != null) { + if (!val.isFinite()) return false; + } + return true; } - ValueEnumeration Enum = ((Enumerable)this.set).elements(); - Value val; - while ((val = Enum.nextElement()) != null) { - if (!val.isFinite()) return false; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return true; } - + public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set:\n" + ppr(this.toString())); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set:\n" + ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set:\n " + ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set:\n " + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - this.convertAndCache(); - return this.realSet.size(); + try { + this.convertAndCache(); + return this.realSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean isNormalized() { - return (this.realSet != null && - this.realSet != DummyEnum && - this.realSet.isNormalized()); - } - + try { + return (this.realSet != null && + this.realSet != DummyEnum && + this.realSet.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + public final void normalize() { - if (this.realSet != null && this.realSet != DummyEnum) { - this.realSet.normalize(); + try { + if (this.realSet != null && this.realSet != DummyEnum) { + this.realSet.normalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } - public final boolean isDefined() { return this.set.isDefined(); } + public final boolean isDefined() { + try { + return this.set.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public static Value union(Value val) { @@ -100,20 +171,20 @@ public class UnionValue extends EnumerableValue implements Enumerable { if (canCombine) { ValueVec elems = ((SetEnumValue)val).elems; for (int i = 0; i < elems.size(); i++) { - canCombine = (canCombine && - (elems.elementAt(i) instanceof SetEnumValue)); + canCombine = (canCombine && + (elems.elementAt(i) instanceof SetEnumValue)); } if (canCombine) { - ValueVec resElems = new ValueVec(); - Value result = new SetEnumValue(resElems, false); - for (int i = 0; i < elems.size(); i++) { - ValueVec elems1 = ((SetEnumValue)elems.elementAt(i)).elems; - for (int j = 0; j < elems1.size(); j++) { - Value elem = elems1.elementAt(j); - if (!result.member(elem)) resElems.addElement(elem); - } - } - return result; + ValueVec resElems = new ValueVec(); + Value result = new SetEnumValue(resElems, false); + for (int i = 0; i < elems.size(); i++) { + ValueVec elems1 = ((SetEnumValue)elems.elementAt(i)).elems; + for (int j = 0; j < elems1.size(); j++) { + Value elem = elems1.elementAt(j); + if (!result.member(elem)) resElems.addElement(elem); + } + } + return result; } } return new UnionValue(val); @@ -121,13 +192,25 @@ public class UnionValue extends EnumerableValue implements Enumerable { /* The fingerprint */ public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.realSet.fingerPrint(fp); + try { + this.convertAndCache(); + return this.realSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.realSet.permute(perm); + try { + this.convertAndCache(); + return this.realSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } private final void convertAndCache() { @@ -137,55 +220,67 @@ public class UnionValue extends EnumerableValue implements Enumerable { else if (this.realSet == DummyEnum) { SetEnumValue val = null; synchronized(this) { - if (this.realSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } + if (this.realSet == DummyEnum) { + val = SetEnumValue.convert(this); + val.deepNormalize(); + } } synchronized(this) { - if (this.realSet == DummyEnum) { this.realSet = val; } + if (this.realSet == DummyEnum) { this.realSet = val; } } } } - + /* String representation of this value. */ public final StringBuffer toString(StringBuffer sb, int offset) { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); + try { + if (expand) { + Value val = SetEnumValue.convert(this); + return val.toString(sb, offset); + } + else { + sb = sb.append("UNION("); + sb = this.set.toString(sb, offset); + sb.append(")"); + return sb; + } } - else { - sb = sb.append("UNION("); - sb = this.set.toString(sb, offset); - sb.append(")"); - return sb; + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } public final ValueEnumeration elements() { - if (this.realSet == null || this.realSet == DummyEnum) { - return new Enumerator(); + try { + if (this.realSet == null || this.realSet == DummyEnum) { + return new Enumerator(); + } + return this.realSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this.realSet.elements(); } - + final class Enumerator implements ValueEnumeration { ValueEnumeration Enum; Value elemSet; ValueEnumeration elemSetEnum; - + public Enumerator() { if (!(set instanceof Enumerable)) { - Assert.fail("Attempted to enumerate the nonenumerable set:\n"+ - ppr(this.toString())); + Assert.fail("Attempted to enumerate the nonenumerable set:\n"+ + ppr(this.toString())); } this.Enum = ((Enumerable)set).elements(); this.elemSet = this.Enum.nextElement(); if (this.elemSet != null) { - if (!(this.elemSet instanceof Enumerable)) { - Assert.fail("Attempted to enumerate UNION(s), but some element of s is nonenumerable."); - } - this.elemSetEnum = ((Enumerable)this.elemSet).elements(); + if (!(this.elemSet instanceof Enumerable)) { + Assert.fail("Attempted to enumerate UNION(s), but some element of s is nonenumerable."); + } + this.elemSetEnum = ((Enumerable)this.elemSet).elements(); } } @@ -199,19 +294,19 @@ public class UnionValue extends EnumerableValue implements Enumerable { if (this.elemSet == null) return null; Value val = this.elemSetEnum.nextElement(); if (val == null) { - this.elemSet = this.Enum.nextElement(); - if (this.elemSet == null) return null; - if (!(this.elemSet instanceof Enumerable)) { - Assert.fail("Attempted to enumerate the nonenumerable set:\n" + - ppr(this.elemSet.toString()) + - "\nwhen enumerating:\n" + ppr(this.toString())); - } - this.elemSetEnum = ((Enumerable)this.elemSet).elements(); - val = this.nextElement(); + this.elemSet = this.Enum.nextElement(); + if (this.elemSet == null) return null; + if (!(this.elemSet instanceof Enumerable)) { + Assert.fail("Attempted to enumerate the nonenumerable set:\n" + + ppr(this.elemSet.toString()) + + "\nwhen enumerating:\n" + ppr(this.toString())); + } + this.elemSetEnum = ((Enumerable)this.elemSet).elements(); + val = this.nextElement(); } return val; } - + } } diff --git a/tlatools/src/tlc2/value/UserValue.java b/tlatools/src/tlc2/value/UserValue.java index 0fb75732f5b151e2c22c2b5f168bf6fc00634b7f..4e9215341462e4de55c1f2834f2c50e3a9e558e6 100644 --- a/tlatools/src/tlc2/value/UserValue.java +++ b/tlatools/src/tlc2/value/UserValue.java @@ -1,10 +1,13 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport // modified on Fri May 11 15:14:04 PDT 2001 by yuanyu package tlc2.value; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import util.Assert; public class UserValue extends Value { @@ -15,50 +18,94 @@ public class UserValue extends Value { public final byte getKind() { return USERVALUE; } public final int compareTo(Object obj) { - if (obj instanceof UserValue) { - return this.userObj.compareTo((Value)obj); + try { + if (obj instanceof UserValue) { + return this.userObj.compareTo((Value)obj); + } + if (!(obj instanceof ModelValue)) + Assert.fail("Attempted to compare overridden value " + ppr(this.toString()) + + " with non-overridden value:\n" + ppr(obj.toString())); + return 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - if (!(obj instanceof ModelValue)) - Assert.fail("Attempted to compare overridden value " + ppr(this.toString()) + - " with non-overridden value:\n" + ppr(obj.toString())); - return 1; } - + public final boolean equals(Object obj) { - return (this.compareTo(obj) == 0); + try { + return (this.compareTo(obj) == 0); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public final boolean member(Value val) { - return this.userObj.member(val); + try { + return this.userObj.member(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean isFinite() { + try { + return this.userObj.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - public final boolean isFinite() { return this.userObj.isFinite(); } - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the overridden value " + - ppr(this.toString()) + "."); + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the overridden value " + + ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return ex.value; } public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the overridden value " + - ppr(this.toString()) + "."); + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the overridden value " + + ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return this; } public final int size() { - Assert.fail("Attempted to compute the number of elements in the overridden value " + - ppr(this.toString()) + "."); - return 0; // make compiler happy + try { + Assert.fail("Attempted to compute the number of elements in the overridden value " + + ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* Nothing to normalize. */ public final boolean isNormalized() { return true; } - + public final void normalize() { /*SKIP*/ } public final boolean isDefined() { return true; } @@ -66,12 +113,24 @@ public class UserValue extends Value { public final Value deepCopy() { return this; } public final boolean assignable(Value val) { - return this.equals(val); + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* The string representation. */ public final StringBuffer toString(StringBuffer sb, int offset) { - return this.userObj.toString(sb, offset); + try { + return this.userObj.toString(sb, offset); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } } diff --git a/tlatools/src/tlc2/value/Value.java b/tlatools/src/tlc2/value/Value.java index 1b37f3cadb46dc75cbe6b1a67180de7502166b83..1d69dfa0105e2885b5fe3378682239432a602920 100644 --- a/tlatools/src/tlc2/value/Value.java +++ b/tlatools/src/tlc2/value/Value.java @@ -1,6 +1,7 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:13 PST by lamport +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:13 PST by lamport // modified on Wed Dec 5 23:18:07 PST 2001 by yuanyu package tlc2.value; @@ -8,6 +9,8 @@ package tlc2.value; import java.io.Serializable; import tla2sany.semantic.SemanticNode; +import tlc2.tool.ModelChecker; +import tlc2.tool.FingerprintException; import tlc2.TLCGlobals; import tlc2.pprint.PrettyPrint; import tlc2.util.FP64; @@ -25,11 +28,19 @@ public abstract class Value implements ValueConstants, Serializable { */ public abstract byte getKind(); - public String getKindString() { return ValueImage[this.getKind()]; } + public String getKindString() { + try { + return ValueImage[this.getKind()]; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } /* This method compares this with val. */ public abstract int compareTo(Object val); - + /* This method returns true iff elem is a member of this. */ public abstract boolean member(Value elem); @@ -39,6 +50,24 @@ public abstract class Value implements ValueConstants, Serializable { /* This method returns a new value after taking the excepts. */ public abstract Value takeExcept(ValueExcept[] exs); + /** + * These methods allow storage and retrieval of the SemanticNode used to create the Value, + * which is helpful for FingerprintException. + */ + private transient SemanticNode source = null; + + public void setSource(final SemanticNode semanticNode) { + source = semanticNode; + } + + public SemanticNode getSource() { + return source; + } + + public boolean hasSource() { + return source != null; + } + /** * This method normalizes (destructively) the representation of * the value. It is essential for equality comparison. @@ -46,248 +75,269 @@ public abstract class Value implements ValueConstants, Serializable { public abstract boolean isNormalized(); public abstract void normalize(); - public final boolean isEmpty() - { - switch (this.getKind()) { - case SETENUMVALUE: - { - SetEnumValue set = (SetEnumValue)this; - return set.elems.size() == 0; - } - case INTERVALVALUE: - { - IntervalValue intv = (IntervalValue)this; - return intv.size() == 0; - } - case SETCAPVALUE: - { - SetCapValue cap = (SetCapValue)this; - return cap.elements().nextElement() == null; - } - case SETCUPVALUE: - { - SetCupValue cup = (SetCupValue)this; - return cup.elements().nextElement() == null; - } - case SETDIFFVALUE: - { - SetDiffValue diff = (SetDiffValue)this; - return diff.elements().nextElement() == null; - } - case SETOFFCNSVALUE: - { - SetOfFcnsValue fcns = (SetOfFcnsValue)this; - return fcns.elements().nextElement() == null; - } - case SETOFRCDSVALUE: - { - SetOfRcdsValue srv = (SetOfRcdsValue)this; - return srv.elements().nextElement() == null; - } - case SETOFTUPLESVALUE: - { - SetOfTuplesValue stv = (SetOfTuplesValue)this; - return stv.elements().nextElement() == null; - } - case SUBSETVALUE: - { - // SUBSET S is never empty. (It always contains {}.) - return false; - } - case UNIONVALUE: - { - UnionValue uv = (UnionValue)this; - return uv.elements().nextElement() == null; - } - case SETPREDVALUE: - { - SetPredValue spv = (SetPredValue)this; - return spv.elements().nextElement() == null; + public final boolean isEmpty() { + try { + + switch (this.getKind()) { + case SETENUMVALUE: + { + SetEnumValue set = (SetEnumValue)this; + return set.elems.size() == 0; + } + case INTERVALVALUE: + { + IntervalValue intv = (IntervalValue)this; + return intv.size() == 0; + } + case SETCAPVALUE: + { + SetCapValue cap = (SetCapValue)this; + return cap.elements().nextElement() == null; + } + case SETCUPVALUE: + { + SetCupValue cup = (SetCupValue)this; + return cup.elements().nextElement() == null; + } + case SETDIFFVALUE: + { + SetDiffValue diff = (SetDiffValue)this; + return diff.elements().nextElement() == null; + } + case SETOFFCNSVALUE: + { + SetOfFcnsValue fcns = (SetOfFcnsValue)this; + return fcns.elements().nextElement() == null; + } + case SETOFRCDSVALUE: + { + SetOfRcdsValue srv = (SetOfRcdsValue)this; + return srv.elements().nextElement() == null; + } + case SETOFTUPLESVALUE: + { + SetOfTuplesValue stv = (SetOfTuplesValue)this; + return stv.elements().nextElement() == null; + } + case SUBSETVALUE: + { + // SUBSET S is never empty. (It always contains {}.) + return false; + } + case UNIONVALUE: + { + UnionValue uv = (UnionValue)this; + return uv.elements().nextElement() == null; + } + case SETPREDVALUE: + { + SetPredValue spv = (SetPredValue)this; + return spv.elements().nextElement() == null; + } + default: + Assert.fail("Shouldn't call isEmpty() on value " + Value.ppr(this.toString())); + return false; } - default: - Assert.fail("Shouldn't call isEmpty() on value " + Value.ppr(this.toString())); - return false; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } /* Fully normalize this (composite) value. */ public final void deepNormalize() { - switch (this.getKind()) { - case RECORDVALUE: - { - RecordValue rcd = (RecordValue)this; - for (int i = 0; i < rcd.values.length; i++) { - rcd.values[i].deepNormalize(); - } - rcd.normalize(); - break; - } - case FCNRCDVALUE: - { - FcnRcdValue fcn = (FcnRcdValue)this; - for (int i = 0; i < fcn.values.length; i++) { - fcn.values[i].deepNormalize(); - } - fcn.normalize(); - break; - } - case SETENUMVALUE: - { - SetEnumValue set = (SetEnumValue)this; - for (int i = 0; i < set.elems.size(); i++) { - set.elems.elementAt(i).deepNormalize(); - } - set.normalize(); - break; - } - case TUPLEVALUE: - { - TupleValue tv = (TupleValue)this; - for (int i = 0; i < tv.elems.length; i++) { - tv.elems[i].deepNormalize(); - } - break; - } - case SETCAPVALUE: - { - SetCapValue cap = (SetCapValue)this; - cap.set1.deepNormalize(); - cap.set2.deepNormalize(); - if (cap.capSet == null) { - cap.capSet = DummyEnum; - } - else if (cap.capSet != DummyEnum) { - cap.capSet.deepNormalize(); - } - break; - } - case SETCUPVALUE: - { - SetCupValue cup = (SetCupValue)this; - cup.set1.deepNormalize(); - cup.set2.deepNormalize(); - if (cup.cupSet == null) { - cup.cupSet = DummyEnum; - } - else if (cup.cupSet != DummyEnum) { - cup.cupSet.deepNormalize(); - } - break; - } - case SETDIFFVALUE: - { - SetDiffValue diff = (SetDiffValue)this; - diff.set1.deepNormalize(); - diff.set2.deepNormalize(); - if (diff.diffSet == null) { - diff.diffSet = DummyEnum; - } - else if (diff.diffSet != DummyEnum) { - diff.diffSet.deepNormalize(); - } - break; - } - case SETOFFCNSVALUE: - { - SetOfFcnsValue fcns = (SetOfFcnsValue)this; - fcns.domain.deepNormalize(); - fcns.range.deepNormalize(); - if (fcns.fcnSet == null) { - fcns.fcnSet = DummyEnum; - } - else if (fcns.fcnSet != DummyEnum) { - fcns.fcnSet.deepNormalize(); - } - break; - } - case SETOFRCDSVALUE: - { - SetOfRcdsValue srv = (SetOfRcdsValue)this; - for (int i = 0; i < srv.values.length; i++) { - srv.values[i].deepNormalize(); - } - if (srv.rcdSet == null) { - srv.rcdSet = DummyEnum; - } - else if (srv.rcdSet != DummyEnum) { - srv.rcdSet.deepNormalize(); - } - break; - } - case SETOFTUPLESVALUE: - { - SetOfTuplesValue stv = (SetOfTuplesValue)this; - for (int i = 0; i < stv.sets.length; i++) { - stv.sets[i].deepNormalize(); - } - if (stv.tupleSet == null) { - stv.tupleSet = DummyEnum; - } - else if (stv.tupleSet != DummyEnum) { - stv.tupleSet.deepNormalize(); - } - break; - } - case SUBSETVALUE: - { - SubsetValue pset = (SubsetValue)this; - pset.set.deepNormalize(); - if (pset.pset == null) { - pset.pset = DummyEnum; - } - else if (pset.pset != DummyEnum) { - pset.pset.deepNormalize(); - } - break; - } - case UNIONVALUE: - { - UnionValue uv = (UnionValue)this; - if (uv.realSet == null) { - uv.realSet = DummyEnum; - } - else if (uv.realSet != DummyEnum) { - uv.realSet.deepNormalize(); - } - break; - } - case SETPREDVALUE: - { - SetPredValue spv = (SetPredValue)this; - spv.inVal.deepNormalize(); - break; - } - case FCNLAMBDAVALUE: - { - FcnLambdaValue flv = (FcnLambdaValue)this; - if (flv.fcnRcd == null) { - if (flv.excepts != null) { - for (int i = 0; i < flv.excepts.length; i++) { - flv.excepts[i].value.deepNormalize(); - for (int j = 0; j < flv.excepts[i].path.length; j++) { - flv.excepts[i].path[j].deepNormalize(); - } - } - } - Value[] paramDoms = flv.params.domains; - for (int i = 0; i < paramDoms.length; i++) { - paramDoms[i].deepNormalize(); - } - } - else { - flv.fcnRcd.deepNormalize(); - } + try { + + switch (this.getKind()) { + case RECORDVALUE: + { + RecordValue rcd = (RecordValue)this; + for (int i = 0; i < rcd.values.length; i++) { + rcd.values[i].deepNormalize(); + } + rcd.normalize(); + break; + } + case FCNRCDVALUE: + { + FcnRcdValue fcn = (FcnRcdValue)this; + for (int i = 0; i < fcn.values.length; i++) { + fcn.values[i].deepNormalize(); + } + fcn.normalize(); + break; + } + case SETENUMVALUE: + { + SetEnumValue set = (SetEnumValue)this; + for (int i = 0; i < set.elems.size(); i++) { + set.elems.elementAt(i).deepNormalize(); + } + set.normalize(); + break; + } + case TUPLEVALUE: + { + TupleValue tv = (TupleValue)this; + for (int i = 0; i < tv.elems.length; i++) { + tv.elems[i].deepNormalize(); + } + break; + } + case SETCAPVALUE: + { + SetCapValue cap = (SetCapValue)this; + cap.set1.deepNormalize(); + cap.set2.deepNormalize(); + if (cap.capSet == null) { + cap.capSet = DummyEnum; + } + else if (cap.capSet != DummyEnum) { + cap.capSet.deepNormalize(); + } + break; + } + case SETCUPVALUE: + { + SetCupValue cup = (SetCupValue)this; + cup.set1.deepNormalize(); + cup.set2.deepNormalize(); + if (cup.cupSet == null) { + cup.cupSet = DummyEnum; + } + else if (cup.cupSet != DummyEnum) { + cup.cupSet.deepNormalize(); + } + break; + } + case SETDIFFVALUE: + { + SetDiffValue diff = (SetDiffValue)this; + diff.set1.deepNormalize(); + diff.set2.deepNormalize(); + if (diff.diffSet == null) { + diff.diffSet = DummyEnum; + } + else if (diff.diffSet != DummyEnum) { + diff.diffSet.deepNormalize(); + } + break; + } + case SETOFFCNSVALUE: + { + SetOfFcnsValue fcns = (SetOfFcnsValue)this; + fcns.domain.deepNormalize(); + fcns.range.deepNormalize(); + if (fcns.fcnSet == null) { + fcns.fcnSet = DummyEnum; + } + else if (fcns.fcnSet != DummyEnum) { + fcns.fcnSet.deepNormalize(); + } + break; + } + case SETOFRCDSVALUE: + { + SetOfRcdsValue srv = (SetOfRcdsValue)this; + for (int i = 0; i < srv.values.length; i++) { + srv.values[i].deepNormalize(); + } + if (srv.rcdSet == null) { + srv.rcdSet = DummyEnum; + } + else if (srv.rcdSet != DummyEnum) { + srv.rcdSet.deepNormalize(); + } + break; + } + case SETOFTUPLESVALUE: + { + SetOfTuplesValue stv = (SetOfTuplesValue)this; + for (int i = 0; i < stv.sets.length; i++) { + stv.sets[i].deepNormalize(); + } + if (stv.tupleSet == null) { + stv.tupleSet = DummyEnum; + } + else if (stv.tupleSet != DummyEnum) { + stv.tupleSet.deepNormalize(); + } + break; + } + case SUBSETVALUE: + { + SubsetValue pset = (SubsetValue)this; + pset.set.deepNormalize(); + if (pset.pset == null) { + pset.pset = DummyEnum; + } + else if (pset.pset != DummyEnum) { + pset.pset.deepNormalize(); + } + break; + } + case UNIONVALUE: + { + UnionValue uv = (UnionValue)this; + if (uv.realSet == null) { + uv.realSet = DummyEnum; + } + else if (uv.realSet != DummyEnum) { + uv.realSet.deepNormalize(); + } + break; + } + case SETPREDVALUE: + { + SetPredValue spv = (SetPredValue)this; + spv.inVal.deepNormalize(); + break; + } + case FCNLAMBDAVALUE: + { + FcnLambdaValue flv = (FcnLambdaValue)this; + if (flv.fcnRcd == null) { + if (flv.excepts != null) { + for (int i = 0; i < flv.excepts.length; i++) { + flv.excepts[i].value.deepNormalize(); + for (int j = 0; j < flv.excepts[i].path.length; j++) { + flv.excepts[i].path[j].deepNormalize(); + } + } + } + Value[] paramDoms = flv.params.domains; + for (int i = 0; i < paramDoms.length; i++) { + paramDoms[i].deepNormalize(); + } + } + else { + flv.fcnRcd.deepNormalize(); + } + } + default: + break; } - default: - break; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } } - + /* This method returns the fingerprint of this value. */ public long fingerPrint(long fp) { - Assert.fail("TLC has found a state in which the value of a variable contains " + - Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access - return 0; // make compiler happy + try { + Assert.fail("TLC has found a state in which the value of a variable contains " + + Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /** @@ -295,14 +345,20 @@ public abstract class Value implements ValueConstants, Serializable { * returns this if nothing is permuted. */ public Value permute(MVPerm perm) { - Assert.fail("TLC has found a state in which the value of a variable contains " + - Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access - return null; // make compiler happy + try { + Assert.fail("TLC has found a state in which the value of a variable contains " + + Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } /* This method returns true iff the value is finite. */ public abstract boolean isFinite(); - + /* This method returns the size of the value. */ public abstract int size(); @@ -317,37 +373,49 @@ public abstract class Value implements ValueConstants, Serializable { /* This method returns the hash code of this value. */ public final int hashCode() { - long fp = this.fingerPrint(FP64.New()); - int high = (int)(fp >> 32); - int low = (int)fp; - return high ^ low; + try { + long fp = this.fingerPrint(FP64.New()); + int high = (int)(fp >> 32); + int low = (int)fp; + return high ^ low; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } public static boolean expand = true; - + /** * This method selects the component of this value. The component is * specified by path. */ public final Value select(Value[] path) { - Value result = this; - for (int i = 0; i < path.length; i++) { - if (!(result instanceof Applicable)) { - Assert.fail("Attempted to apply EXCEPT construct to the value " + - ppr(result.toString()) + "."); + try { + Value result = this; + for (int i = 0; i < path.length; i++) { + if (!(result instanceof Applicable)) { + Assert.fail("Attempted to apply EXCEPT construct to the value " + + ppr(result.toString()) + "."); + } + Value elem = path[i]; + result = ((Applicable)result).select(elem); + if (result == null) return null; } - Value elem = path[i]; - result = ((Applicable)result).select(elem); - if (result == null) return null; + return result; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } } - return result; } - + public static Value getValue(SemanticNode expr) { return (Value)expr.getToolObject(TLCGlobals.ToolId); } - + /** * This abstract method returns a string representation of this * value. Each subclass must provide its own implementation. @@ -356,16 +424,28 @@ public abstract class Value implements ValueConstants, Serializable { /* The string representation of this value */ public final String toString() { - StringBuffer sb = new StringBuffer(); - return this.toString(sb, 0).toString(); + try { + StringBuffer sb = new StringBuffer(); + return this.toString(sb, 0).toString(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } } - + public final String toString(String delim) { - StringBuffer sb = new StringBuffer(); - sb = this.toString(sb, 0); - sb.append(delim); - return sb.toString(); - } + try { + StringBuffer sb = new StringBuffer(); + sb = this.toString(sb, 0); + sb.append(delim); + return sb.toString(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } public static String ppr(String s) { return PrettyPrint.mypp(s, 80) ; diff --git a/tlatools/src/util/Assert.java b/tlatools/src/util/Assert.java index 3984967242870b9b71a1e248a283d3b7f488bf69..90fae76922d60fc2e2f951e8e447b79d3f6da51d 100644 --- a/tlatools/src/util/Assert.java +++ b/tlatools/src/util/Assert.java @@ -11,7 +11,6 @@ import tlc2.output.MP; * A toolkit for checking conditions and throwing unchecked exceptions if they are not met. * * @author Yuan Yu, Simon Zambrovski - * @version $Id$ */ public class Assert { @@ -22,7 +21,7 @@ public class Assert */ public static void fail(String reason) throws RuntimeException { - throw new RuntimeException(reason); + throw new TLCRuntimeException(reason); } /** @@ -32,7 +31,7 @@ public class Assert */ public static void fail(int errorCode, String[] parameters) { - throw new RuntimeException(MP.getMessage(errorCode, parameters)); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode, parameters)); } /** @@ -42,7 +41,7 @@ public class Assert */ public static void fail(int errorCode, String parameter) { - throw new RuntimeException(MP.getMessage(errorCode, parameter)); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode, parameter)); } /** @@ -52,7 +51,7 @@ public class Assert */ public static void fail(int errorCode, Throwable cause) { - throw new RuntimeException(MP.getMessage(errorCode, cause.getMessage()), cause); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode, cause.getMessage()), cause); } /** @@ -61,7 +60,7 @@ public class Assert */ public static void fail(int errorCode) { - throw new RuntimeException(MP.getMessage(errorCode)); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode)); } /** @@ -75,7 +74,7 @@ public class Assert { if (!condition) { - throw new RuntimeException(MP.getMessage(errorCode, parameters)); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode, parameters)); } } @@ -90,7 +89,7 @@ public class Assert { if (!condition) { - throw new RuntimeException(MP.getMessage(errorCode, parameter)); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode, parameter)); } } @@ -104,7 +103,7 @@ public class Assert { if (!condition) { - throw new RuntimeException(MP.getMessage(errorCode)); + throw new TLCRuntimeException(MP.getMessage(errorCode)); } } @@ -126,8 +125,27 @@ public class Assert { if (!condition) { - throw new RuntimeException(errorMsg); + throw new TLCRuntimeException(errorMsg); } } + public static class TLCRuntimeException extends RuntimeException { + + public final int errorCode; + + public TLCRuntimeException(String errorMsg) { + super(errorMsg); + this.errorCode = -1; // Unknown error code. + } + + public TLCRuntimeException(int errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public TLCRuntimeException(int errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + } } diff --git a/tlatools/src/util/MailSender.java b/tlatools/src/util/MailSender.java index 7c96294ade8226c058833e567d3b841a9ab7ae04..df6ea98cde3567ebb49e7bbe73cb62e7b21513c5 100644 --- a/tlatools/src/util/MailSender.java +++ b/tlatools/src/util/MailSender.java @@ -8,6 +8,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Properties; import java.util.Scanner; @@ -17,6 +18,7 @@ import javax.activation.FileDataSource; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; +import javax.mail.SendFailedException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; @@ -42,32 +44,45 @@ public class MailSender { /** * @param from "Foo bar <foo@bar.com>" * @param to An email address _with_ domain part (foo@bar.com) - * @param domain domain part (bar.com). Used to lookup mx records * @param subject * @param messages */ - private static boolean send(final String from, final String to, - final String domain, final String subject, final String body, final File[] files) { + private static boolean send(final InternetAddress from, final InternetAddress to, final String subject, final String body, final File[] files) { + // https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html final Properties properties = System.getProperties(); + // Prefer email to be delivered encrypted (assumes to lower likelihood of SMTP + // rejection or classification as spam too). Falls back to plain text if SMTP + // server does not support starttls. + properties.put("mail.smtp.starttls.enable", "true"); //properties.put("mail.debug", "true"); + if (!to.getAddress().contains("@")) { + // no domain, no MX record to lookup + return false; + } + List<MXRecord> mailhosts; try { - final List<MXRecord> mailhosts = getMXForDomain(domain); - - // retry all mx host - for (MXRecord mxRecord : mailhosts) { - properties.put("mail.smtp.host", mxRecord.hostname); + mailhosts = getMXForDomain(to.getAddress().split("@")[1]); + } catch (NamingException e) { + e.printStackTrace(); + return false; + } + // retry all mx host + for (int i = 0; i < mailhosts.size(); i++) { + final MXRecord mxRecord = mailhosts.get(i); + properties.put("mail.smtp.host", mxRecord.hostname); + try { final Session session = Session.getDefaultInstance(properties); final Message msg = new MimeMessage(session); - msg.setFrom(new InternetAddress(from)); - msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); + msg.setFrom(from); + msg.addRecipient(Message.RecipientType.TO, to); msg.setSubject(subject); // not sure why the extra body part is needed here MimeBodyPart messageBodyPart = new MimeBodyPart(); - + final Multipart multipart = new MimeMultipart(); // The main body part. Having a main body appears to have a very @@ -77,7 +92,7 @@ public class MailSender { messageBodyPart = new MimeBodyPart(); messageBodyPart.setContent(body, "text/plain"); multipart.addBodyPart(messageBodyPart); - + // attach file(s) for (File file : files) { if (file == null) { @@ -94,16 +109,38 @@ public class MailSender { Transport.send(msg); return true; + } catch (SendFailedException e) { + final Exception next = e.getNextException(); + if (next != null && next.getMessage() != null && next.getMessage().toLowerCase().contains("greylist") + && !properties.containsKey((String) properties.get("mail.smtp.host") + ".greylisted")) { + // mark receiver as greylisted to not retry over and over again. + properties.put((String) properties.get("mail.smtp.host") + ".greylisted", "true"); + throttleRetry(String.format( + "%s EMail Report: Detected greylisting when sending to %s at %s, will retry in %s minutes...", + new Date(), to.getAddress(), mxRecord.hostname, 10L), 10L); + i = i - 1; + } else { + throttleRetry(String.format( + "%s EMail Report: Slowing down due to errors when sending to %s at %s, will continue in 1 minute...", + new Date(), to.getAddress(), mxRecord.hostname, 1L), 1L); + } + } catch (AddressException e) { + e.printStackTrace(); + } catch (MessagingException e) { + e.printStackTrace(); } - } catch (NamingException e) { - e.printStackTrace(); - } catch (AddressException e) { - e.printStackTrace(); - } catch (MessagingException e) { - e.printStackTrace(); } return false; } + + private static void throttleRetry(final String msg, long minutes) { + try { + System.err.println(msg); + Thread.sleep(minutes * 60L * 1000L); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } private static List<MXRecord> getMXForDomain(String aDomain) throws NamingException { final InitialDirContext ctx = new InitialDirContext(); @@ -149,37 +186,45 @@ public class MailSender { return weight.compareTo(o.weight); } } + + // For testing only. + public static void main(String[] args) throws AddressException, FileNotFoundException, UnknownHostException { + MailSender mailSender = new MailSender(); + mailSender.send(); + } - // if null, no Mail is going to be send - private final String mailto; private String modelName = "unknown model"; private String specName = "unknown spec"; private File err; private File out; - private String from; - private String domain; + // if null, no Mail is going to be send + private InternetAddress[] toAddresses; + private InternetAddress from; + private InternetAddress fromAlt; - public MailSender() throws FileNotFoundException, UnknownHostException { + public MailSender() throws FileNotFoundException, UnknownHostException, AddressException { ModelInJar.loadProperties(); // Reads result.mail.address and so on. - mailto = System.getProperty(MAIL_ADDRESS); + final String mailto = System.getProperty(MAIL_ADDRESS); if (mailto != null) { - domain = mailto.split("@")[1]; + this.toAddresses = InternetAddress.parse(mailto); - from = "TLC - The friendly model checker <" + this.from = new InternetAddress("TLC - The friendly model checker <" + + toAddresses[0].getAddress() + ">"); + this.fromAlt = new InternetAddress("TLC - The friendly model checker <" + System.getProperty("user.name") + "@" - + InetAddress.getLocalHost().getHostName() + ">"; + + InetAddress.getLocalHost().getHostName() + ">"); // Record/Log output to later send it by email final String tmpdir = System.getProperty("java.io.tmpdir"); - out = new File(tmpdir + File.separator + "MC.out"); + this.out = new File(tmpdir + File.separator + "MC.out"); ToolIO.out = new LogPrintStream(out); - err = new File(tmpdir + File.separator + "MC.err"); + this.err = new File(tmpdir + File.separator + "MC.err"); ToolIO.err = new LogPrintStream(err); } } - public MailSender(String mainFile) throws FileNotFoundException, UnknownHostException { + public MailSender(String mainFile) throws FileNotFoundException, UnknownHostException, AddressException { this(); setModelName(mainFile); } @@ -197,15 +242,26 @@ public class MailSender { } public boolean send(List<File> files) { - if (mailto != null) { + if (toAddresses != null) { files.add(0, out); // Only add the err file if there is actually content if (err.length() != 0L) { files.add(0, err); } - // Try sending the mail with the model checking result to the receiver - return send(from, mailto, domain, "Model Checking result for " + modelName + " with spec " + specName, - extractBody(out), files.toArray(new File[files.size()])); + // Try sending the mail with the model checking result to the receivers. Returns + // true if a least one email was delivered successfully. + boolean success = false; + for (final InternetAddress toAddress : toAddresses) { + if (send(from, toAddress, "Model Checking result for " + modelName + " with spec " + specName, + extractBody(out), files.toArray(new File[files.size()]))) { + success = true; + } else if (send(fromAlt, toAddress, "Model Checking result for " + modelName + " with spec " + specName, + extractBody(out), files.toArray(new File[files.size()]))) { + // Try with alternative from address which some receivers might actually accept. + success = true; + } + } + return success; } else { // ignore, just signal everything is fine return true; diff --git a/tlatools/src/util/TLCRuntime.java b/tlatools/src/util/TLCRuntime.java index a4ecdfb0ab381ba331f9b246f9e0fdb149bfca87..eac53fc36b05fffcdc098d33449f4ef62b176b6f 100644 --- a/tlatools/src/util/TLCRuntime.java +++ b/tlatools/src/util/TLCRuntime.java @@ -3,11 +3,17 @@ package util; import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; -import java.lang.reflect.Method; import java.util.List; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; + import tlc2.tool.fp.FPSet; /** @@ -30,6 +36,7 @@ public class TLCRuntime { } return runtime; } + private long physicalSystemMemory = -1; @@ -37,26 +44,15 @@ public class TLCRuntime { * @return the total amount of memory, measured in bytes. */ private long getPhysicalSystemMemory() { - - // try to read the total physical memory via a MXBean. Unfortunately, - // these methods are not meant as public API, which requires us to pull - // a visibility reflection hack. - // This hack is expected to work on Linux, Windows (up to 7) and Max OSX - final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory - .getOperatingSystemMXBean(); - for (Method method : operatingSystemMXBean.getClass() - .getDeclaredMethods()) { - if (method.getName().equals("getTotalPhysicalMemorySize")) { - method.setAccessible(true); - try { - return (Long) method.invoke(operatingSystemMXBean); - } catch (Exception e) { - break; - } - } + final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + try { + return (Long) mBeanServer.getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), + "TotalPhysicalMemorySize"); + } catch (InstanceNotFoundException | AttributeNotFoundException | MalformedObjectNameException + | ReflectionException | MBeanException | ClassCastException e) { + // as a safeguard default to the total memory available to this JVM + return Runtime.getRuntime().totalMemory(); } - // as a safeguard default to the total memory available to this JVM - return Runtime.getRuntime().totalMemory(); } /** @@ -148,4 +144,25 @@ public class TLCRuntime { } return (long) fpMemSize; } + + public enum ARCH { + x86, + x86_64; + } + + public ARCH getArchitecture() { + if (System.getProperty("sun.arch.data.model") != null + && System.getProperty("sun.arch.data.model").equals("64")) { + return ARCH.x86_64; + } + if (System.getProperty("com.ibm.vm.bitmode") != null + && System.getProperty("com.ibm.vm.bitmode").equals("64")) { + return ARCH.x86_64; + } + if (System.getProperty("java.vm.version") != null + && System.getProperty("java.vm.version").contains("_64")) { + return ARCH.x86_64; + } + return ARCH.x86; + } } diff --git a/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java b/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java new file mode 100644 index 0000000000000000000000000000000000000000..804f6c81c18e13b610a378cbb67835f3ab4b3408 --- /dev/null +++ b/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java @@ -0,0 +1,227 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.junit.Test; + +import tlc2.util.BufferedRandomAccessFile; + +public class ConcurrentWriteTest { + @Test + public void test() throws IOException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test", ".bin"); + + RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF0.setLength(4000L * Long.BYTES); + for(long i = 0L; i < 1000; i++) { + tmpRAF0.writeLong(i); + } + + for(long i = 1000L; i < 2000; i++) { + tmpRAF0.writeLong(i); + } + + for(long i = 2000L; i < 3000; i++) { + tmpRAF0.writeLong(i); + } + + for(long i = 3000L; i < 4000; i++) { + tmpRAF0.writeLong(i); + } + + tmpRAF0.close(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < 4000; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test1() throws IOException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test1", ".bin"); + final long limit = 4000000L; + final long partition = limit / 4L; + + final RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF0.setLength(limit * Long.BYTES); + for(long i = 0L; i < partition; i++) { + tmpRAF0.writeLong(i); + } + + final RandomAccessFile tmpRAF1 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF1.setLength(limit * Long.BYTES); + tmpRAF1.seek(partition * Long.BYTES); + for (long i = partition; i < (2L * partition); i++) { + tmpRAF1.writeLong(i); + } + + final RandomAccessFile tmpRAF2 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF2.setLength(limit * Long.BYTES); + tmpRAF2.seek((2L * partition) * Long.BYTES); + for (long i = (2L * partition); i < (3L * partition); i++) { + tmpRAF2.writeLong(i); + } + + final RandomAccessFile tmpRAF3 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF3.setLength(limit * Long.BYTES); + tmpRAF3.seek((3L * partition) * Long.BYTES); + for (long i = (3L * partition); i < (4L * partition); i++) { + tmpRAF3.writeLong(i); + } + + tmpRAF0.close(); + tmpRAF1.close(); + tmpRAF2.close(); + tmpRAF3.close(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test2() throws IOException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test2", ".bin"); + final long limit = 4000000L; + final long partition = limit / 4L; + + final RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF0.setLength(limit * Long.BYTES); + + final RandomAccessFile tmpRAF1 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF1.setLength(limit * Long.BYTES); + tmpRAF1.seek(partition * Long.BYTES); + + final RandomAccessFile tmpRAF2 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF2.setLength(limit * Long.BYTES); + tmpRAF2.seek((2L * partition) * Long.BYTES); + + final RandomAccessFile tmpRAF3 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF3.setLength(limit * Long.BYTES); + tmpRAF3.seek((3L * partition) * Long.BYTES); + + for(long i = 0L; i < (1L * partition); i++) { + tmpRAF0.writeLong(i); + } + + for(long i = (1L * partition); i < (2L * partition); i++) { + tmpRAF1.writeLong(i); + } + + for(long i = (2L * partition); i < (3L * partition); i++) { + tmpRAF2.writeLong(i); + } + + for(long i = (3L * partition); i < (4L * partition); i++) { + tmpRAF3.writeLong(i); + } + + tmpRAF0.close(); + tmpRAF1.close(); + tmpRAF2.close(); + tmpRAF3.close(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test3() throws IOException, InterruptedException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test3", ".bin"); + final long limit = 400000000L; + final long writers = 8L; + final long partition = limit / writers; + + final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>((int) writers); + for (long i = 0L; i < writers; i++) { + final long id = i; + tasks.add(new Callable<Void>() { + public Void call() throws Exception { + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF.setLength(limit * Long.BYTES); + tmpRAF.seek(id * partition * Long.BYTES); + + for (long j = (id * partition); j < ((id + 1) * partition); j++) { + tmpRAF.writeLong(j); + } + + tmpRAF.close(); + return null; + } + }); + } + + final ExecutorService executorService = Executors.newFixedThreadPool((int) writers); + executorService.invokeAll(tasks); + executorService.shutdown(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test4() throws IOException, InterruptedException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test4", ".bin"); + final long limit = 400000000L; + final long writers = 8L; + final long partition = limit / writers; + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF.setLength(limit * Long.BYTES); + final FileChannel channel = tmpRAF.getChannel(); + + final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>((int) writers); + for (long i = 0L; i < writers; i++) { + final long id = i; + tasks.add(new Callable<Void>() { + public Void call() throws Exception { + long position = id * partition * Long.BYTES; + final ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES/* * 1024*/); + for (long j = (id * partition); j < ((id + 1) * partition); j++/*j+=1024L*/) { +// for (int i = 0; i < buffer.capacity(); i++) { + buffer.putLong(j/* + i*/); + buffer.flip(); +// } + channel.write(buffer, position + (j * Long.BYTES)); + buffer.clear(); + } + channel.force(false); + return null; + } + }); + }; + + final ExecutorService executorService = Executors.newFixedThreadPool((int) writers); + executorService.invokeAll(tasks); + executorService.shutdown(); + + tmpRAF.close(); + + final BufferedRandomAccessFile checkRaf = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, checkRaf.readLong()); + } + checkRaf.close(); + } +} diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java index 9db569ecbc9507f39d0e2561503dfe352b0ba773..d607913bc11d39b46f4c81255b29761e5011d0c4 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java @@ -22,6 +22,7 @@ import tlc2.tool.fp.generator.BatchedFingerPrintGenerator; import tlc2.tool.fp.generator.FingerPrintGenerator; import tlc2.tool.fp.generator.LongVecFingerPrintGenerator; import tlc2.tool.fp.generator.PartitionedFingerPrintGenerator; +import tlc2.util.IdThread; public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { @@ -99,6 +100,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { // "FingerPrintGenerator" itself. Assume.assumeFalse(System.getProperty(MultiThreadedFPSetTest.class.getName() + ".excludes", "") .contains("_" + fpgClass.getSimpleName())); + System.out.println("Running test: " + fpgClass.getSimpleName()); TLCGlobals.setNumWorkers(NUM_THREADS); final FPSet fpSet = getFPSetInitialized(NUM_THREADS); @@ -108,7 +110,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { int.class, int.class, FPSet.class, CountDownLatch.class, long.class, long.class, CyclicBarrier.class }); // Take timestamp after instantiating FPSet to not measure zero'ing/initializing FPSet. - previousTimestamp = startTimestamp = System.currentTimeMillis(); + startTimestamp = System.currentTimeMillis(); final Timer timer = new Timer(); final CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS, new Runnable() { @@ -118,11 +120,19 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { // reporter. final TimerTask reporter = new TimerTask() { public void run() { - printInsertionSpeed(fpSet); + final long currentSize = fpSet.size(); + final long insertions = currentSize - previousSize; + if (fpSet instanceof FPSetStatistic) { + FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet; + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor"); + } else { + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min"); + } + previousSize = currentSize; } }; // Take timestamp after instantiating FPSet to not measure zero'ing/initializing FPSet. - previousTimestamp = startTimestamp = System.currentTimeMillis(); + startTimestamp = System.currentTimeMillis(); timer.scheduleAtFixedRate(reporter, 1L, 60 * 1000); } }); @@ -132,7 +142,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { for (int i = 0; i < fpgs.length; i++) { fpgs[i] = (FingerPrintGenerator) constructor.newInstance( this, i, fpgs.length, fpSet, latch, seed++, INSERTIONS, barrier); - Thread thread = new Thread(fpgs[i], "Producer#" + i); + Thread thread = new IdThread(fpgs[i], "Producer#" + i, i); thread.start(); } diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java index 09372a227b439d78aa60aa8ca274c5f25fd1710b..aa0db19ed87e79b675dca8252f54e86e0e3d6f58 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java @@ -16,6 +16,7 @@ public class FingerPrintGenerator implements Runnable { protected final long totalInsertions; protected final long perThreadInsertions; + protected final long seed; protected final Random rnd; protected final FPSet fpSet; protected final CountDownLatch latch; @@ -34,6 +35,7 @@ public class FingerPrintGenerator implements Runnable { this.fpSet = fpSet; this.latch = latch; this.barrier = barrier; + this.seed = seed; this.rnd = new Random(seed); this.totalInsertions = totalInsertions; this.perThreadInsertions = (long) Math.floor(totalInsertions / numThreads); @@ -58,6 +60,17 @@ public class FingerPrintGenerator implements Runnable { predecessor = rnd.nextLong(); + // Periodically verify the FPSet's content. This causes a + // drastic slow down. +// if (fpSet.size() % 10000 == 0) { +// final Random verify = new Random(seed); +// long fp = verify.nextLong(); +// while (fp != predecessor) { +// Assert.assertTrue(fpSet.contains(fp)); +// fp = verify.nextLong(); +// } +// } +// boolean put = fpSet.put(predecessor); if (put == false) { puts++; diff --git a/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java b/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java similarity index 97% rename from tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java rename to tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java index 6b2b424fc1fcf5a2f98410830a912bcc4c682f31..079f81f4e313437c6447a77b7ad6289cb277589f 100644 --- a/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java +++ b/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java @@ -9,7 +9,7 @@ import java.util.Random; import org.junit.Test; import util.TLCRuntime; -public class OffHeapDiskFPSetTest extends FPSetTest { +public class OffHeapDiskFPSetLongTest extends FPSetTest { private static final int FLUSHES = 4; diff --git a/tlatools/test-model/AssertExpressionStack.cfg b/tlatools/test-model/AssertExpressionStack.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/AssertExpressionStack.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/AssertExpressionStack.tla b/tlatools/test-model/AssertExpressionStack.tla new file mode 100644 index 0000000000000000000000000000000000000000..5d344919bb01af72f67c5accf6653042d1524969 --- /dev/null +++ b/tlatools/test-model/AssertExpressionStack.tla @@ -0,0 +1,7 @@ +------------------------------ MODULE AssertExpressionStack ------------------------------ +EXTENDS Naturals, TLC + +VARIABLES x + +Spec == x = 0 /\ [][x'=1/\Assert(x=0, "Fails after initial state")]_x +============================================================================= diff --git a/tlatools/test-model/DieHardTLA.cfg b/tlatools/test-model/DieHardTLA.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bb7713361d8dc3c0f1759e274a1b41aaacc3411a --- /dev/null +++ b/tlatools/test-model/DieHardTLA.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +TypeOK \ No newline at end of file diff --git a/tlatools/test-model/DieHardTLA.tla b/tlatools/test-model/DieHardTLA.tla new file mode 100644 index 0000000000000000000000000000000000000000..78d44d483e7540b4de6476503dd9a8c316c66eba --- /dev/null +++ b/tlatools/test-model/DieHardTLA.tla @@ -0,0 +1,44 @@ +------------------------------ MODULE DieHardTLA ------------------------------ +EXTENDS Integers + +VARIABLES small, big + +TypeOK == /\ small \in 0..3 + /\ big \in 0..5 + +Init == /\ big = 0 + /\ small = 0 + +FillSmall == /\ small' = 3 + /\ big' = big + +FillBig == /\ big' = 5 + /\ small' = small + +EmptySmall == /\ small' = 0 + /\ big' = big + +EmptyBig == /\ big' = 0 + /\ small' = small + +SmallToBig == IF big + small =< 5 + THEN /\ big' = big + small + /\ small' = 0 + ELSE /\ big' = 5 + /\ small' = small - (5 - big) + +BigToSmall == IF big + small =< 3 + THEN /\ big' = 0 + /\ small' = big + small + ELSE /\ big' = small - (3 - big) + /\ small' = 3 + +Next == \/ FillSmall + \/ FillBig + \/ EmptySmall + \/ EmptyBig + \/ SmallToBig + \/ BigToSmall + +Spec == Init /\ [][Next]_<<small, big>> +============================================================================= diff --git a/tlatools/test-model/FingerprintExceptionInit.cfg b/tlatools/test-model/FingerprintExceptionInit.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3de9a85ec4e2058d21906ad4967bf50084684036 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionInit.cfg @@ -0,0 +1,5 @@ +INIT +Init + +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/FingerprintExceptionInit.tla b/tlatools/test-model/FingerprintExceptionInit.tla new file mode 100644 index 0000000000000000000000000000000000000000..647fc4e6fd198e673d6b3945c9b1a7fe7c2b1a39 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionInit.tla @@ -0,0 +1,11 @@ +------------------------ MODULE FingerprintExceptionInit -------------------- + +EXTENDS Integers + +VARIABLE x + +Init == x = SUBSET(SUBSET(1..36)) +Next == IF {1} \in SUBSET(1..31) THEN x' = SUBSET(SUBSET(1..32)) ELSE x' = 2 +Spec == Init /\ [][Next]_x + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/FingerprintExceptionNext.cfg b/tlatools/test-model/FingerprintExceptionNext.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3de9a85ec4e2058d21906ad4967bf50084684036 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionNext.cfg @@ -0,0 +1,5 @@ +INIT +Init + +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/FingerprintExceptionNext.tla b/tlatools/test-model/FingerprintExceptionNext.tla new file mode 100644 index 0000000000000000000000000000000000000000..009b6d79491a586a770fa3ee754afd832edae9f2 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionNext.tla @@ -0,0 +1,11 @@ +------------------------ MODULE FingerprintExceptionNext -------------------- + +EXTENDS Integers + +VARIABLE x + +Init == x = 0 +Next == IF {1} \in SUBSET(1..31) THEN x' = SUBSET(SUBSET(1..32)) ELSE x' = 2 +Spec == Init /\ [][Next]_x + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/IllegalOperatorTest.tla b/tlatools/test-model/IllegalOperatorTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..d1748f8cbd4c046eb8acf5e4ed6f6b7d94559ce5 --- /dev/null +++ b/tlatools/test-model/IllegalOperatorTest.tla @@ -0,0 +1,4 @@ +------------------------- MODULE IllegalOperatorTest ------------------------ +D(Op(_)) == Op(42) +Foo == D(D) +============================================================================= diff --git a/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..f9a90034431352c6bccbbc1544fbcec7b99f6eb2 --- /dev/null +++ b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla @@ -0,0 +1,9 @@ +---------------- MODULE TraceWithLargeSetOfInitialStatesTest ---------------- +EXTENDS Integers + +VARIABLES x,y + +Spec == (x \in 1..11 /\ y = FALSE) /\ [][x' = x /\ y' = TRUE]_<<x,y>> + +Inv == y = FALSE +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test52.cfg b/tlatools/test-model/suite/test52.cfg index f0e860231ad0256721aa61b69aec5db318681a6c..8990f16d455df1338b40f248faa8eb41101160d1 100644 --- a/tlatools/test-model/suite/test52.cfg +++ b/tlatools/test-model/suite/test52.cfg @@ -1,3 +1,6 @@ SPECIFICATION Spec PROPERTY Property -INVARIANT Invariant +\* The Invariant is commented because of a known bug in TLC. +\* If this bug is ever addressed, Invariant can be re-enabled +\* and the test test52enabled be deleted. +\*INVARIANT Invariant diff --git a/tlatools/test-model/suite/test52.tla b/tlatools/test-model/suite/test52.tla index 10b5c5b0d6eb3cb35cdb085398ea3c01400659a3..dbdd9e94de638cd5ce31d7fbfc6f3d6f22a1d9b2 100644 --- a/tlatools/test-model/suite/test52.tla +++ b/tlatools/test-model/suite/test52.tla @@ -34,9 +34,9 @@ Init == /\ x = {"a"} /\ y = {"a"} Next == /\ x'= x \cup {"a"} /\ y'= y \cup {"b"} -Spec == Init /\ [][Next]_x +Spec == Init /\ [][Next]_<<x, y>> -EnabledTest(Foo(_)) == Foo(x'=x) +EnabledTest(Foo(_)) == Foo(x'=x) UnchangedTest(Foo(_)) == Foo(x) PrimeTest(Foo(_)) == Foo(x) = x diff --git a/tlatools/test-model/suite/test57.cfg b/tlatools/test-model/suite/test57.cfg index 2e385425054877319618031959b1771ec5b65ca3..eafb6be0934c428f10a4f7987554ed5e2426af96 100644 --- a/tlatools/test-model/suite/test57.cfg +++ b/tlatools/test-model/suite/test57.cfg @@ -1,3 +1,3 @@ SPECIFICATION Spec -INVARIANT Invariant0 Invariant1 Invariant2 +INVARIANT Invariant0 Invariant2 Invariant3 Invariant1 PROPERTY Property \ No newline at end of file diff --git a/tlatools/test-model/suite/test57.tla b/tlatools/test-model/suite/test57.tla index 18539eb3e576a32528123ddd14aea25dc3a0c219..fc9a77c38ed1ab471b60f289c92fe0bc7e8a06a3 100644 --- a/tlatools/test-model/suite/test57.tla +++ b/tlatools/test-model/suite/test57.tla @@ -12,6 +12,7 @@ Spec == Init /\ [][Next]_x Invariant0 == I!B(Next) Invariant1 == I!C Invariant2 == ~ I!B(I!A) +Invariant3 == I!D Property == [][~I!A]_x ============================================================================= I!C = ENABLED((u'=x) /\ (v' = x+1)) diff --git a/tlatools/test-model/suite/test57a.tla b/tlatools/test-model/suite/test57a.tla index d90b00c1be9dcd095ff9389681ef82062ee665cf..e319f592379eeec07234cb18fc43a91305114801 100644 --- a/tlatools/test-model/suite/test57a.tla +++ b/tlatools/test-model/suite/test57a.tla @@ -5,4 +5,5 @@ VARIABLES u, v A == (u'=u) /\ (v'=v+1) B(d) == ENABLED d C == B(A) +D == ENABLED A ============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/test52.cfg b/tlatools/test-model/test52.cfg index f0e860231ad0256721aa61b69aec5db318681a6c..8990f16d455df1338b40f248faa8eb41101160d1 100644 --- a/tlatools/test-model/test52.cfg +++ b/tlatools/test-model/test52.cfg @@ -1,3 +1,6 @@ SPECIFICATION Spec PROPERTY Property -INVARIANT Invariant +\* The Invariant is commented because of a known bug in TLC. +\* If this bug is ever addressed, Invariant can be re-enabled +\* and the test test52enabled be deleted. +\*INVARIANT Invariant diff --git a/tlatools/test-model/test52.tla b/tlatools/test-model/test52.tla index 10b5c5b0d6eb3cb35cdb085398ea3c01400659a3..fca1986e07ac0a46c857d22cf49eb36482e08e21 100644 --- a/tlatools/test-model/test52.tla +++ b/tlatools/test-model/test52.tla @@ -34,7 +34,7 @@ Init == /\ x = {"a"} /\ y = {"a"} Next == /\ x'= x \cup {"a"} /\ y'= y \cup {"b"} -Spec == Init /\ [][Next]_x +Spec == Init /\ [][Next]_<<x, y>> EnabledTest(Foo(_)) == Foo(x'=x) UnchangedTest(Foo(_)) == Foo(x) diff --git a/tlatools/test-model/test57.cfg b/tlatools/test-model/test57.cfg index 2e385425054877319618031959b1771ec5b65ca3..1c93dc177bbf3221d89d0e33bbdccfd430a54c82 100644 --- a/tlatools/test-model/test57.cfg +++ b/tlatools/test-model/test57.cfg @@ -1,3 +1,3 @@ SPECIFICATION Spec -INVARIANT Invariant0 Invariant1 Invariant2 +INVARIANT Invariant0 Invariant2 Invariant3 Invariant1 PROPERTY Property \ No newline at end of file diff --git a/tlatools/test-model/test57.tla b/tlatools/test-model/test57.tla index 18539eb3e576a32528123ddd14aea25dc3a0c219..fc9a77c38ed1ab471b60f289c92fe0bc7e8a06a3 100644 --- a/tlatools/test-model/test57.tla +++ b/tlatools/test-model/test57.tla @@ -12,6 +12,7 @@ Spec == Init /\ [][Next]_x Invariant0 == I!B(Next) Invariant1 == I!C Invariant2 == ~ I!B(I!A) +Invariant3 == I!D Property == [][~I!A]_x ============================================================================= I!C = ENABLED((u'=x) /\ (v' = x+1)) diff --git a/tlatools/test-model/test57a.tla b/tlatools/test-model/test57a.tla index d90b00c1be9dcd095ff9389681ef82062ee665cf..e319f592379eeec07234cb18fc43a91305114801 100644 --- a/tlatools/test-model/test57a.tla +++ b/tlatools/test-model/test57a.tla @@ -5,4 +5,5 @@ VARIABLES u, v A == (u'=u) /\ (v'=v+1) B(d) == ENABLED d C == B(A) +D == ENABLED A ============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/testinvalidinvariant.cfg b/tlatools/test-model/testinvalidinvariant.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f0e860231ad0256721aa61b69aec5db318681a6c --- /dev/null +++ b/tlatools/test-model/testinvalidinvariant.cfg @@ -0,0 +1,3 @@ +SPECIFICATION Spec +PROPERTY Property +INVARIANT Invariant diff --git a/tlatools/test-model/testinvalidinvariant.tla b/tlatools/test-model/testinvalidinvariant.tla new file mode 100644 index 0000000000000000000000000000000000000000..e00c9bd9158a664ad6639c0094f6e0d3f014fff7 --- /dev/null +++ b/tlatools/test-model/testinvalidinvariant.tla @@ -0,0 +1,12 @@ +------------------ MODULE testinvalidinvariant -------------------- +EXTENDS Integers + +VARIABLES x +Init == /\ x = 1 +Next == /\ x'= x + 1 +Spec == Init /\ [][Next]_<<x>> + +\* Primed variable is invalid. +Invariant == x' \in Nat + +================================================================== diff --git a/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java b/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0ec0a2225b03b7003b5d6697af8d79e8870e596c --- /dev/null +++ b/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import gov.nasa.jpf.util.test.TestJPF; + +// This class verifies a core part of tlc2.tool.fp.OffHeapDiskFPSet. It is - more or less - a verbatim copy. +public class OffHeapDiskFPSetJPFTest extends TestJPF { + private static final int PROBE_LIMIT = 2; + + @Test + public void test() throws InterruptedException, BrokenBarrierException { + if (verifyNoPropertyViolation("+listener=.listener.AssertionProperty,.listener.ErrorTraceGenerator")) { + final int size = 3; + final int max = 3; + final DummyOffHeapDiskFPSet fpSet = new DummyOffHeapDiskFPSet(size, max); + + // Two concurrent writers. + for (int i = 0; i < 2; i++) { + Thread worker = new Thread(new Runnable() { + public void run() { + for (long i = 1; i <= max; i++) { + fpSet.memInsert(i); + } + } + }, "Worker"); + worker.start(); + } + assert fpSet.checkInvariant() : "FPSet violates its invariant: " + Arrays.toString(fpSet.array.array); + } + } + + private static class DummyOffHeapDiskFPSet { + + private static final int EMPTY = 0; + + private final OffHeapDiskFPSet.Indexer indexer; + private final DummyLongArray array; + // AtomicInteger should be LongAdder but JPF fails with what seems to be an + // internal bug. + private final AtomicInteger tblCnt; + + public DummyOffHeapDiskFPSet(int positions, long max) { + this.tblCnt = new AtomicInteger(0); + this.array = new DummyLongArray(positions); + this.indexer = new OffHeapDiskFPSet.Indexer(positions, 1, max); + } + + // This is what we want to verify. + public boolean memInsert(final long fp) { + for (int i = 0; i < PROBE_LIMIT; i++) { + final int position = (int) indexer.getIdx(fp, i); + final long expected = array.get(position); + if (expected == EMPTY) { + if (array.trySet(position, expected, fp)) { + tblCnt.incrementAndGet(); + return false; + } else { + i = i - 1; + continue; + } + } + if (expected == fp) { + return true; + } + } + return false; + } + + // Heap-variant of LongArray with synchronized instead of CAS. JPF does + // not seem to support sun.misc.Unsafe. + private static class DummyLongArray { + private final long[] array; + + public DummyLongArray(int positions) { + this.array = new long[positions]; + } + + public long get(int position) { + return this.array[position]; + } + + public synchronized boolean trySet(int position, long expected, long l) { + if (this.array[position] == expected) { + this.array[position] = l; + return true; + } + return false; + } + } + + //**** Assertion Helper ****// + + public synchronized boolean checkInvariant() { + // No duplicates. + int cnt = 0; + final Set<Long> s = new HashSet<Long>(array.array.length); + for (int i = 0; i < array.array.length; i++) { + if (array.array[i] > 0) { + s.add(array.array[i]); + cnt++; + } + } + return cnt == s.size(); + } + } +} diff --git a/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java b/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2cec33db7440ac436d9ded5a8760feac439187f3 --- /dev/null +++ b/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tla2sany.drivers; + +import org.junit.Test; + +import tlc2.tool.CommonTestCase; +import util.TestPrintStream; +import util.ToolIO; + +public class IllegalOperatorTest { + + @Test + public void test() { + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + + SANY.SANYmain( + new String[] { CommonTestCase.BASE_PATH + "IllegalOperatorTest" }); + + testPrintStream.assertSubstring("*** Errors: 1\n" + + "\n" + + "line 3, col 8 to line 3, col 11 of module IllegalOperatorTest\n" + + "\n" + + "Argument number 1 to operator 'D' \n" + + "should be a 1-parameter operator."); + } +} diff --git a/tlatools/test/tlc2/TLCTest.java b/tlatools/test/tlc2/TLCTest.java index 1db35c0900c7102f741e225036ccb0063d4e5bd2..dc3ffe81d0110ab6afb15017f7fb50f10b068ea3 100644 --- a/tlatools/test/tlc2/TLCTest.java +++ b/tlatools/test/tlc2/TLCTest.java @@ -3,8 +3,15 @@ package tlc2; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; +import org.junit.Assume; import org.junit.Test; + +import tlc2.TLC; +import tlc2.TLCGlobals; +import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.fp.FPSetFactory; import util.TLCRuntime; public class TLCTest { @@ -34,8 +41,10 @@ public class TLCTest { public void testHandleParametersAllocateLowerBound() { final TLC tlc = new TLC(); assertTrue(tlc.handleParameters(new String[] {"-fpmem", "0", "MC"})); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Allocating to little should result in min default", - TLCRuntime.MinFpMemSize, tlc.getFPSetConfiguration() + TLCRuntime.MinFpMemSize, fpSetConfiguration .getMemoryInBytes()); } @@ -47,8 +56,10 @@ public class TLCTest { final TLC tlc = new TLC(); assertTrue(tlc.handleParameters(new String[] {"-fpmem", Long.toString(Long.MAX_VALUE), "MC"})); final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.75d); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Overallocating should result in max default (75%)", - maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes()); + maxMemory, fpSetConfiguration.getMemoryInBytes()); } /** @@ -59,8 +70,10 @@ public class TLCTest { final TLC tlc = new TLC(); assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".5", "MC"})); final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.50d); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Overallocating should result in max default (50%)", - maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes()); + maxMemory, fpSetConfiguration.getMemoryInBytes()); } /** @@ -71,8 +84,10 @@ public class TLCTest { final TLC tlc = new TLC(); assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".99", "MC"})); final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.99d); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Overallocating should result in max default (99%)", - maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes()); + maxMemory, fpSetConfiguration.getMemoryInBytes()); } /** diff --git a/tlatools/test/tlc2/TestMPRecorder.java b/tlatools/test/tlc2/TestMPRecorder.java index 32a02cbfdbb8c3f951cfadb2bb2914cb67ff91e7..b5cdb150c33edb10a05d722b69691fcb5ce6726c 100644 --- a/tlatools/test/tlc2/TestMPRecorder.java +++ b/tlatools/test/tlc2/TestMPRecorder.java @@ -49,6 +49,10 @@ public class TestMPRecorder extends tlc2.output.MPRecorder { return records.get(code); } + public int getRecordAsInt(int code) { + return Integer.parseInt(((String[]) records.get(code).get(0))[0]); + } + // This is a best effort implementation that only checks the first // elements of the nested records and contained arrays public boolean recordedWithStringValue(int code, String str) { diff --git a/tlatools/test/tlc2/tool/AssertExpressionStack.java b/tlatools/test/tlc2/tool/AssertExpressionStack.java new file mode 100644 index 0000000000000000000000000000000000000000..11c2624a4eb1dd6ad17e2dd60c8bb36125b4c898 --- /dev/null +++ b/tlatools/test/tlc2/tool/AssertExpressionStack.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssertExpressionStack extends ModelCheckerTestCase { + + public AssertExpressionStack() { + super("AssertExpressionStack"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // Assert a proper nested expression has been recorded which represents the call stack. + assertFalse(recorder.recordedWithStringValue(EC.TLC_NESTED_EXPRESSION, " The error call stack is empty.\n")); + } +} diff --git a/tlatools/test/tlc2/tool/CommonTestCase.java b/tlatools/test/tlc2/tool/CommonTestCase.java index 06b0b8a34fcc1c471d64809762fb24d425d45dd8..ac3370ee934b899b070690f9211201f7eecd52ec 100644 --- a/tlatools/test/tlc2/tool/CommonTestCase.java +++ b/tlatools/test/tlc2/tool/CommonTestCase.java @@ -45,7 +45,7 @@ public abstract class CommonTestCase { protected static final String BASE_DIR = System.getProperty("basedir", ""); protected static final String TEST_MODEL = "test-model" + File.separator; - protected static final String BASE_PATH = BASE_DIR + TEST_MODEL; + public static final String BASE_PATH = BASE_DIR + TEST_MODEL; protected final TestMPRecorder recorder; diff --git a/tlatools/test/tlc2/tool/DepthFirstTerminate.java b/tlatools/test/tlc2/tool/DepthFirstTerminate.java index 9e7c7ca2db6e0ec252e3a6431e2d50e20591fb7b..25f832a9970327e88e14b993779f263ea5a74cfd 100644 --- a/tlatools/test/tlc2/tool/DepthFirstTerminate.java +++ b/tlatools/test/tlc2/tool/DepthFirstTerminate.java @@ -51,7 +51,7 @@ public class DepthFirstTerminate extends ModelCheckerTestCase { */ @Override protected int getNumberOfThreads() { - // With >1, TLC's depth first search never terminates. - return 1; + // Run this test with as many threads possible to hopefully spot concurrency issues. + return Runtime.getRuntime().availableProcessors(); } } diff --git a/tlatools/test/tlc2/tool/DiameterTest.java b/tlatools/test/tlc2/tool/DiameterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..94d8e9c8a86f289a5e2d844f60860820dfd2fbf1 --- /dev/null +++ b/tlatools/test/tlc2/tool/DiameterTest.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DiameterTest extends ModelCheckerTestCase { + + public DiameterTest() { + super("DieHardTLA"); + } + + @Test + public void testSpec() { + // ModelChecker has finished without errors and generated the expected + // amount of states + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "97", "16", "0")); + + // The diameter is known to be 8 as reported by TLC running with a + // single worker. With multiple workers, it's possible to get a higher + // or a lower number. + final int level = recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH); + assertTrue(String.format("Level below threshold: %s", level), level >= 8); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#getNumberOfThreads() + */ + @Override + protected int getNumberOfThreads() { + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/DumpAsDotTest.dot b/tlatools/test/tlc2/tool/DumpAsDotTest.dot index 9f2510ac1ff6d8275ac596f1a6810828bcb0b29b..18e710a0b296e028c48056bd9332ee9a8090fec8 100644 --- a/tlatools/test/tlc2/tool/DumpAsDotTest.dot +++ b/tlatools/test/tlc2/tool/DumpAsDotTest.dot @@ -1,5 +1,4 @@ strict digraph DiskGraph { -rankdir=LR; 609737673425276830 [style = filled] [label="/\\ b = FALSE /\\ x = 0"] 6816998822487979083 [style = filled] [label="/\\ b = TRUE diff --git a/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java b/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..defa78974cba2f033bf415f77c88ef0de37560fa --- /dev/null +++ b/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Ian Morris Nieves - initial design and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class FingerprintExceptionInitTest extends ModelCheckerTestCase { + + public FingerprintExceptionInitTest() { + super("FingerprintExceptionInit"); + } + + @Test + public void testSpec() { + // ModelChecker has finished with a fingerprint exception and underlying overflow exception + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + String arg1 = "1) line 7, col 20 to line 7, col 32 of module FingerprintExceptionInit\n" + + "0) line 7, col 13 to line 7, col 33 of module FingerprintExceptionInit\n"; + String arg2 = "Overflow when computing the number of elements in:\n" + + "SUBSET 1..36"; + assertTrue(recorder.recordedWithStringValues(EC.TLC_FINGERPRINT_EXCEPTION, arg1, arg2)); + } + +} diff --git a/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java b/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a516c8c0f9f2320789b95404092664fe2b3ab017 --- /dev/null +++ b/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Ian Morris Nieves - initial design and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class FingerprintExceptionNextTest extends ModelCheckerTestCase { + + public FingerprintExceptionNextTest() { + super("FingerprintExceptionNext"); + } + + @Test + public void testSpec() { + // ModelChecker has finished with a general exception, a fingerprint exception and underlying overflow exception + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.GENERAL)); + String arg1 = "1) line 8, col 51 to line 8, col 63 of module FingerprintExceptionNext\n" + + "0) line 8, col 44 to line 8, col 64 of module FingerprintExceptionNext\n"; + String arg2 = "Overflow when computing the number of elements in:\n" + + "SUBSET 1..32"; + assertTrue(recorder.recordedWithStringValues(EC.TLC_FINGERPRINT_EXCEPTION, arg1, arg2)); + } + +} diff --git a/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java b/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..11fbafd364040f1c87b45cd7c9e0fedb2b61bfb6 --- /dev/null +++ b/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TraceWithLargeSetOfInitialStatesTest extends ModelCheckerTestCase { + + public TraceWithLargeSetOfInitialStatesTest() { + super("TraceWithLargeSetOfInitialStatesTest", new String[] { "-maxSetSize", "10" }); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("/\\ x = 1\n/\\ y = FALSE"); + expectedTrace.add("/\\ x = 1\n/\\ y = TRUE"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java b/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java index aa03b3535feb6fef57ed40484b85bf5948660cd1..6155eecdcd5f95e363c2e6c427638ac9d7c97885 100644 --- a/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java +++ b/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java @@ -30,6 +30,7 @@ import java.util.concurrent.CountDownLatch; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import tlc2.TestMPRecorder; @@ -64,6 +65,7 @@ public abstract class DistributedTLCTestCase extends CommonTestCase { @Before public void setUp() { + Assume.assumeTrue("DistributedTLCTestCase broken with OffHeapDiskFPSet.", false); // Remember the original security manager and have it replaced with this // custom one. Contrary to the original, this security manager // intercepts calls to System.exit(int) and throws an exception instead diff --git a/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java b/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java index 3fb25ea844805cbb230f0aa20c55e6960206cf4a..832e61dfb2532ea502aa4d19d06c919deab8d7c9 100644 --- a/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java @@ -100,7 +100,7 @@ public abstract class AbstractFPSetTest { long insertions = (long) ((currentSize - previousSize) * factor); if (fpSet instanceof FPSetStatistic) { FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet; - System.out.println(System.currentTimeMillis() + " s; " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor"); + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor"); } else { System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min"); } diff --git a/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java b/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java index 4d7a26d8af7bed453d888aa30004c2eed57de29b..4f0481ca2c5f321fc2bba66d88935a2cda29c96d 100644 --- a/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java +++ b/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java @@ -15,7 +15,7 @@ public class DummyFPSetConfiguration extends FPSetConfiguration { return memoryInBytes; } - public void setMemoryInFingerprintCnt(int numberOfFingerprints) { - this.memoryInBytes = numberOfFingerprints * FPSet.LongSize; + public void setMemoryInFingerprintCnt(long length) { + this.memoryInBytes = length * FPSet.LongSize; } } diff --git a/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java b/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java index f7f2999f040c2715537939fd6b9cab868d82113b..17a150a3ac935918ac82a01ebe27344db7535486 100644 --- a/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java +++ b/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java @@ -7,9 +7,13 @@ import static org.junit.Assert.assertTrue; import java.rmi.NoSuchObjectException; import java.rmi.RemoteException; + +import org.junit.Assume; +import org.junit.Before; import org.junit.Test; import tlc2.tool.distributed.fp.FPSetRMI; +import util.TLCRuntime; @SuppressWarnings("deprecation") public class FPSetFactoryTest { @@ -61,6 +65,7 @@ public class FPSetFactoryTest { @Test public void testGetFPSetOffHeap() throws RemoteException { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration); @@ -92,18 +97,6 @@ public class FPSetFactoryTest { doTestNested(LSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); } - - @Test - public void testGetFPSetOffHeapWithMem() throws RemoteException { - System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); - final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); - fpSetConfiguration.setMemory(MEMORY); - fpSetConfiguration.setRatio(1.0d); - FPSet fpSet = doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration); - assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes()); - - doTestNested(OffHeapDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); - } /* Test single FPSet with explicit memory and ratio */ @@ -132,21 +125,6 @@ public class FPSetFactoryTest { doTestNested(LSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); } - - @Test - public void testGetFPSetOffHeapWithMemAndRatio() throws RemoteException { - System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); - final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); - fpSetConfiguration.setMemory(MEMORY); - fpSetConfiguration.setRatio(.5d); - FPSet fpSet = doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration); - - // Offheap allocates all 100% of memory as it's the only consumer of - // non-heap memory - assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes()); - - doTestNested(OffHeapDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); - } /* Test MultiFPSet with default memory */ @@ -173,6 +151,7 @@ public class FPSetFactoryTest { @Test public void testGetFPSetOffHeapMultiFPSet() throws RemoteException { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); fpSetConfiguration.setFpBits(1); @@ -210,6 +189,7 @@ public class FPSetFactoryTest { @Test public void testGetFPSetOffHeapMultiFPSetWithMem() throws RemoteException { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); fpSetConfiguration.setMemory(MEMORY); diff --git a/tlatools/test/tlc2/tool/fp/LongArrayTest.java b/tlatools/test/tlc2/tool/fp/LongArrayTest.java index 5b3ccd1a3923319bee6ced5c1ac1293270e91334..98ef627e9078ca64f824272d8bff9bd73d6fa273 100644 --- a/tlatools/test/tlc2/tool/fp/LongArrayTest.java +++ b/tlatools/test/tlc2/tool/fp/LongArrayTest.java @@ -25,19 +25,27 @@ ******************************************************************************/ package tlc2.tool.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; -import junit.framework.TestCase; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; -public class LongArrayTest extends TestCase { +import util.TLCRuntime; +public class LongArrayTest { + + @Before + public void setup() { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); + } + + @Test public void testGetAndSet() throws IOException { - if (!System.getProperty("sun.arch.data.model").equals("64")) { - // LongArray only works on 64bit architectures. See comment in - // LongArray ctor. - return; - } - final int elements = 100; final LongArray array = new LongArray(elements); @@ -72,6 +80,7 @@ public class LongArrayTest extends TestCase { } } + @Test public void testOutOfRangePositive() throws IOException { final LongArray array = new LongArray(1); try { @@ -82,6 +91,7 @@ public class LongArrayTest extends TestCase { fail(); } + @Test public void testOutOfRangeNegative() throws IOException { final LongArray array = new LongArray(1); try { @@ -92,6 +102,45 @@ public class LongArrayTest extends TestCase { fail(); } + @Test + public void testGetAndTrySet() throws IOException { + final int elements = 100; + + final LongArray array = new LongArray(elements); + array.zeroMemory(1); + + // Assert zero successful + for (long i = 0L; i < elements; i++) { + assertEquals(0L, array.get(i)); + } + + // trySet linear elements. + for (long i = 0L; i < elements; i++) { + assertTrue(array.trySet(i, 0, i)); + } + for (long i = 0L; i < elements; i++) { + assertEquals(i, array.get(i)); + } + + // Replace with largest possible values + for (long i = 0L; i < elements; i++) { + array.trySet(i, i, Long.MAX_VALUE - i); + } + for (long i = 0L; i < elements; i++) { + assertEquals(Long.MAX_VALUE - i, array.get(i)); + } + + + // Replace with smallest possible values + for (long i = 0L; i < elements; i++) { + array.trySet(i, Long.MAX_VALUE - i, Long.MIN_VALUE + i); + } + for (long i = 0L; i < elements; i++) { + assertEquals(Long.MIN_VALUE + i, array.get(i)); + } + } + + @Test public void testZeroMemory() throws IOException { for (int k = 1; k < 8; k++) { for (int i = 1; i < 128; i++) { diff --git a/tlatools/test/tlc2/tool/fp/LongArraysTest.java b/tlatools/test/tlc2/tool/fp/LongArraysTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9fce0cb921e2c56079135064c63650b63adfb0fe --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/LongArraysTest.java @@ -0,0 +1,875 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static tlc2.tool.fp.OffHeapDiskFPSet.EMPTY; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import tlc2.tool.fp.LongArrays.LongComparator; +import tlc2.tool.fp.OffHeapDiskFPSet.Indexer; +import util.TLCRuntime; + +public class LongArraysTest { + + @Before + public void setup() { + Assume.assumeTrue(LongArray.isSupported()); + } + + @Test + public void testEmpty1() { + doTest(new ArrayList<Long>(0), 1L, 0, new OffHeapDiskFPSet.Indexer(0, 1)); + } + + @Test + public void testEmpty2() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + + doTest(expected, 1L, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1)); + } + + @Test + public void testBasic1() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(5L); + expected.add(8L); + expected.add(1L); + expected.add(7L); + expected.add(0L); + expected.add(3L); + final LongArray array = new LongArray(expected); + LongArrays.sort(array); + + // This amounts to a regular/basic insertion sort because there are no + // sentinels in the array. doTest fails for this array, because the + // indices calculated by the indexer are invalid. + for (int i = 1; i < array.size(); i++) { + assertTrue(array.get(i - 1L) < array.get(i)); + } + } + + @Test + public void testBasic2() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(74236458333421747L); + expected.add(9185197375878056627L); + expected.add(9017810141411942826L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(764880467681476738L); + expected.add(1028380228728529428L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(1963327988900916594L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(2754416910109403115L); + expected.add(3528296600587602855L); + expected.add(3766154305485605955L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(4760603841378765728L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(7575172208974668528L); + expected.add(8240485391672917634L); + expected.add(8572429495433200993L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9136953010061430590L); + expected.add(9195197379878056627L); + final LongArray array = new LongArray(expected); + LongArrays.sort(array); + + // This amounts to a regular/basic insertion sort because there are no + // sentinels in the array. doTest fails for this array, because the + // indices calculated by the indexer are invalid. + for (int i = 1; i < array.size(); i++) { + assertTrue(array.get(i - 1L) < array.get(i)); + } + } + + @Test + public void test0() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(22102288204167208L); + expected.add(225160948165161873L); + expected.add(0L); + expected.add(1638602644344629957L); + expected.add(1644442600000000000L); + expected.add(0L); + + doTest(expected, 1L, 3, new OffHeapDiskFPSet.Indexer(expected.size(), 1)); + } + + @Test + public void test1() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(22102288204167208L); + expected.add(225160948165161873L); + expected.add(0L); + expected.add(0L); + expected.add(810435887525385357L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1638602644344629957L); + expected.add(0L); + expected.add(0L); + expected.add(2068351286375637679L); + expected.add(0L); + expected.add(2528370576879701538L); + expected.add(2453870502940122045L); + expected.add(0L); + expected.add(3145830401686811393L); + expected.add(3192897355035876677L); + expected.add(3527505876050247287L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4563398963865761585L); + expected.add(0L); + expected.add(4858869653769863593L); + expected.add(5180223017321191209L); + expected.add(0L); + expected.add(0L); + expected.add(5635076245116608576L); + expected.add(5649139415351271641L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6703691584433488410L); + expected.add(0L); + expected.add(7143040549630863225L); + expected.add(7205281130519852628L); + expected.add(7012967342342885117L); + expected.add(7709106021212022085L); + expected.add(7908712604546919197L); + expected.add(7246110956693059329L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8781691546738212390L); + expected.add(8897195185152846807L); + expected.add(0L); + + doTest(expected); + } + + @Test + public void test2() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(0L); + expected.add(0L); + expected.add(22102288204167208L); + expected.add(225160948165161873L); + expected.add(810435887525385357L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1638602644344629957L); + expected.add(0L); + expected.add(0L); + expected.add(2068351286375637679L); + expected.add(0L); + expected.add(2528370576879701538L); + expected.add(2453870502940122045L); + expected.add(0L); + expected.add(3145830401686811393L); + expected.add(3192897355035876677L); + expected.add(3527505876050247287L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4563398963865761585L); + expected.add(0L); + expected.add(4858869653769863593L); + expected.add(5180223017321191209L); + expected.add(0L); + expected.add(0L); + expected.add(5635076245116608576L); + expected.add(5649139415351271641L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6703691584433488410L); + expected.add(0L); + expected.add(7143040549630863225L); + expected.add(7205281130519852628L); + expected.add(7012967342342885117L); + expected.add(7709106021212022085L); + expected.add(7908712604546919197L); + expected.add(7246110956693059329L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8781691546738212390L); + expected.add(8897195185152846807L); + expected.add(0L); + + doTest(expected); + } + + @Test + public void test3() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(9183932681676589496L); + expected.add(0L); + expected.add(0L); + expected.add(329728050397015749L); + expected.add(436139026681109109L); + expected.add(556905678415593173L); + expected.add(0L); + expected.add(796460649423573389L); + expected.add(797798112015065380L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1632374027957690827L); + expected.add(1756811852021281877L); + expected.add(0L); + expected.add(1881448932687659007L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(2342821865031748924L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(2736147834640710575L); + expected.add(2864022862265935958L); + expected.add(2773542629236699928L); + expected.add(2957298868366608281L); + expected.add(0L); + expected.add(3330257111892751888L); + expected.add(3295675356431597478L); + expected.add(3395836867027940583L); + expected.add(3681469222400184316L); + expected.add(3754947896063147473L); + expected.add(3698681814958844261L); + expected.add(3951382885893085878L); + expected.add(0L); + expected.add(4188454649677385650L); + expected.add(4129247165607948084L); + expected.add(4365409305525871332L); + expected.add(4526757821913904014L); + expected.add(4254202026550171921L); + expected.add(4557871951994955815L); + expected.add(4806497834029622101L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(5236202638577037427L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(5936146187640212534L); + expected.add(0L); + expected.add(6127434886073515781L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6547025209145878563L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6931928829149329960L); + expected.add(0L); + expected.add(0L); + expected.add(7244186580741581738L); + expected.add(0L); + expected.add(0L); + expected.add(7634041392899269082L); + expected.add(7590982629575593986L); + expected.add(0L); + expected.add(7954723745221262664L); + expected.add(0L); + expected.add(8156105620374757718L); + expected.add(8305398393196381769L); + expected.add(8318253237689249492L); + expected.add(8487954051864981042L); + expected.add(8411933954485687818L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(9175849669163144218L); + + doTest(expected); + } + + @Test + public void test4() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(9136953010061430590L); + expected.add(74236458333421747L); + expected.add(0L); + expected.add(0L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(0L); + expected.add(764880467681476738L); + expected.add(0L); + expected.add(0L); + expected.add(1028380228728529428L); + expected.add(0L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1963327988900916594L); + expected.add(0L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(0L); + expected.add(0L); + expected.add(2754416910109403115L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(3528296600587602855L); + expected.add(0L); + expected.add(3766154305485605955L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(0L); + expected.add(4760603841378765728L); + expected.add(0L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(7575172208974668528L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8240485391672917634L); + expected.add(0L); + expected.add(0L); + expected.add(8572429495433200993L); + expected.add(0L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9017810141411942826L); + expected.add(9195197379878056627L); + + doTest(expected); + } + + @Test + public void test5() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(9185197375878056627L); + expected.add(74236458333421747L); + expected.add(9017810141411942826L); + expected.add(0L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(0L); + expected.add(764880467681476738L); + expected.add(0L); + expected.add(0L); + expected.add(1028380228728529428L); + expected.add(0L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1963327988900916594L); + expected.add(0L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(0L); + expected.add(0L); + expected.add(2754416910109403115L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(3528296600587602855L); + expected.add(0L); + expected.add(3766154305485605955L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(0L); + expected.add(4760603841378765728L); + expected.add(0L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(7575172208974668528L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8240485391672917634L); + expected.add(0L); + expected.add(0L); + expected.add(8572429495433200993L); + expected.add(0L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9136953010061430590L); + expected.add(9195197379878056627L); + + doTest(expected); + } + + @Test + public void test6() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(9185197375878056627L); + expected.add(9017810141411942826L); + expected.add(0L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(0L); + expected.add(764880467681476738L); + expected.add(0L); + expected.add(0L); + expected.add(1028380228728529428L); + expected.add(0L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1963327988900916594L); + expected.add(0L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(0L); + expected.add(0L); + expected.add(2754416910109403115L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(3528296600587602855L); + expected.add(0L); + expected.add(3766154305485605955L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(0L); + expected.add(4760603841378765728L); + expected.add(0L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(7575172208974668528L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8240485391672917634L); + expected.add(0L); + expected.add(0L); + expected.add(8572429495433200993L); + expected.add(0L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9136953010061430590L); + expected.add(9195197379878056627L); + + doTest(expected); + } + + @Test + public void test7() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(0L); + expected.add(4L); + expected.add(0L); + expected.add(6L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(13L); + + doTest(expected, 1, 0, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + @Test + public void test8() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(11L); + expected.add(3L); + expected.add(4L); + expected.add(5L); + expected.add(6L); + expected.add(7L); + expected.add(8L); + expected.add(9L); + expected.add(10L); + expected.add(12L); + + final OffHeapDiskFPSet.Indexer indexer = new OffHeapDiskFPSet.Indexer(expected.size(), 1, 12); + final LongArray array = new LongArray(expected); + final LongComparator comparator = getComparator(indexer); + LongArrays.sort(array, 0, array.size() - 1L + 3, comparator); + verify(expected, 3, indexer, array); + + } + + @Test + public void test9a() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(12L); + expected.add(1L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(11L); + + doTest(expected, 1, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + @Test + public void test9b() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(11L); + expected.add(1L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(12L); + + doTest(expected, 1, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + @Test + public void test9c() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(12L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(11L); + + doTest(expected, 1, 3, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + private void doTest(final List<Long> expected) { + final Indexer indexer = new OffHeapDiskFPSet.Indexer(expected.size(), 1); + for (int i = 1; i < (expected.size() / 2); i++) { + doTest(expected, i, 2, indexer); + } + } + + private void doTest(final List<Long> expected, final long partitions, final int reprobe, final Indexer indexer) { + final LongArray array = new LongArray(expected); + final LongComparator comparator = getComparator(indexer); + final long length = expected.size() / partitions; + + // Sort each disjunct partition. + for (long i = 0; i < partitions; i++) { + final long start = i * length; + final long end = i + 1L == partitions ? array.size() - 1L: start + length; + LongArrays.sort(array, start, end, comparator); + } + // Stitch the disjunct partitions together. Only need if more than one + // partition, but done with one partition anyway to see that it causes + // no harm. + for (long i = 0; i < partitions; i++) { + final long end = getEnd(partitions, array, length, i); + LongArrays.sort(array, end - reprobe, end + reprobe, comparator); + } + + verify(expected, reprobe, indexer, array); + } + + private long getEnd(final long partitions, final LongArray array, final long length, long idx) { + return idx + 1L == partitions ? array.size() - 1L: (idx + 1L) * length; + } + + private static LongComparator getComparator(final Indexer indexer) { + return new LongComparator() { + public int compare(final long fpA, final long posA, final long fpB, final long posB) { + // Elements not in Nat \ {0} remain at their current + // position. + if (fpA <= EMPTY || fpB <= EMPTY) { + return 0; + } + + final boolean wrappedA = indexer.getIdx(fpA) > posA; + final boolean wrappedB = indexer.getIdx(fpB) > posB; + + if (wrappedA == wrappedB && posA > posB) { + return fpA < fpB ? -1 : 1; + } else if ((wrappedA ^ wrappedB)) { + if (posA < posB && fpA < fpB) { + // Swap fpB, which is at the end of array a, with fpA. + // fpA is less than fpB. fpB was inserted into array a + // before fpA. + return -1; + } + if (posA > posB && fpA > fpB) { + return -1; + } + } + return 0; + } + }; + } + + private void verify(final List<Long> expected, final int reprobe, final Indexer indexer, final LongArray array) { + // Verify that negative and EMPTY elements remain at their position. + // Lets call them sentinels. + int sentinel = 0; + OUTER: for (int j = 0; j < expected.size(); j++) { + final long l = expected.get(j); + if (l == EMPTY) { + // EMPTY remain at their original positions. + assertEquals(EMPTY, array.get(j)); + sentinel++; + } else if (l < EMPTY) { + // Negative remain at their original positions. + assertEquals(l, array.get(j)); + sentinel++; + } else { + // Verify that all non-sentinels are still + // array members. + for (int k = 0; k < array.size(); k++) { + if (array.get(k) == l) { + continue OUTER; + } + } + fail(String.format("long %s not found.", l)); + } + } + + // Verify elements stayed within their lookup range. + for (int pos = 0; pos < array.size(); pos++) { + final long l = array.get(pos); + if (l <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(l); + assertTrue(String.format("%s, pos: %s, idx: %s, r: %s (was at: %s)", l, pos, idx, reprobe, + expected.indexOf(l)), isInRange(idx, reprobe, pos, array.size())); + } + + // Verify that non-sentinels are sorted is ascending order. Take + // care of wrapped elements too. A) First find the first non-sentinel, + // non-wrapped element. + long pos = 0; + final List<Long> seen = new ArrayList<Long>(expected.size()); + while (pos < array.size()) { + long e = array.get(pos); + if (e <= EMPTY || indexer.getIdx(e) > pos) { + // Either sentinel or wrapped. + pos++; + continue; + } + seen.add(e); + pos++; + break; + } + // B) Collect all elements into seen but skip those at the beginning that + // wrapped, and those that didn't wrap at the end (array.size + reprobe). + for (; pos < array.size() + reprobe; pos++) { + long actual = array.get(pos % array.size()); + if (actual <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(actual); + if (pos < array.size() && idx > pos) { + // When not wrapped, ignore elements belonging to the end that wrapped. + continue; + } + if (pos > array.size() - 1L && idx + reprobe < pos) { + // When wrapped, ignore elements at beginning which do not + // belong to the end. + continue; + } + seen.add(actual); + } + // C) Verify that all elements are sorted. + for (int i = 1; i < seen.size(); i++) { + final long lo = seen.get(i - 1); + final long hi = seen.get(i); + assertTrue(String.format("%s > %s", lo, hi), lo < hi); + } + // D) Verify we saw all expected elements. + assertEquals(expected.size() - sentinel, seen.size()); + } + + @Test + public void testIsInRange() { + assertTrue(isInRange(0, 0, 0, 4)); + + assertFalse(isInRange(0, 0, 1, 4)); + assertFalse(isInRange(0, 0, 2, 4)); + assertFalse(isInRange(0, 0, 3, 4)); + assertFalse(isInRange(0, 0, 4, 4)); + + assertTrue(isInRange(0, 1, 1, 4)); + assertFalse(isInRange(0, 1, 2, 4)); + assertTrue(isInRange(0, 2, 2, 4)); + assertFalse(isInRange(0, 2, 3, 4)); + assertTrue(isInRange(0, 3, 3, 4)); + assertFalse(isInRange(0, 3, 4, 4)); + + assertTrue(isInRange(3, 0, 3, 4)); + assertTrue(isInRange(3, 1, 0, 4)); + assertTrue(isInRange(3, 2, 1, 4)); + assertFalse(isInRange(3, 2, 2, 4)); + } + + private static boolean isInRange(long idx, int reprobe, int pos, long size) { + if (idx + reprobe >= size && pos < idx) { + return pos <= (idx + reprobe) % size; + } else { + return idx <= pos && pos <= idx + reprobe; + } + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java new file mode 100644 index 0000000000000000000000000000000000000000..be6422e9817e6ad9647f108bc83132f6262e9322 --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static tlc2.tool.fp.DiskFPSet.MARK_FLUSHED; +import static tlc2.tool.fp.OffHeapDiskFPSet.EMPTY; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.rmi.RemoteException; +import java.util.Arrays; +import java.util.Random; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import util.TLCRuntime; + +public class OffHeapDiskFPSetTest { + + protected static final String filename = "OffHeapDiskFPSetTest"; + + @Before + public void setup() { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); + } + +// @Test +// public void testInsertAndEvictRnd() throws Exception { +// Random rnd = new Random(); +// for (int i = 0; i < 1000; i++) { +// doTest(System.currentTimeMillis(), rnd.nextInt(255) + 1); +// } +// } + + @Test + public void testInsertAndEvict1() throws Exception { + doTest(1473793977852L, 87); + } + + @Test + public void testInsertAndEvict2() throws Exception { + doTest(1473793976137L, 87); + } + + @Test + public void testInsertAndEvict3() throws Exception { + doTest(1473839150698L, 46); + } + + @Test + public void testInsertAndEvict4() throws Exception { + doTest(1473839150698L, 46); + } + + @Test + public void testInsertAndEvict5() throws Exception { + doTest(1473839322351L, 23); + } + + @Test + public void testInsertAndEvict6() throws Exception { + doTest(1473839380539L, 23); + } + + @Test + public void testInsertAndEvict7() throws Exception { + doTest(1473839422899L, 11); + } + + @Test + public void testInsertAndEvict8() throws Exception { + doTest(1473839543883L, 11); + } + + @Test + public void testInsertAndEvict9() throws Exception { + doTest(1473871461079L, 64); + } + + @Test + public void testInsertAndEvict10() throws Exception { + doTest(1473871462765L, 64); + } + + @Test + public void testInsertAndEvict11() throws Exception { + doTest(1473871522834L, 32); + } + + @Test + public void testInsertAndEvict12() throws Exception { + doTest(1473871526136L, 32); + } + + @Test + public void testInsertAndEvict13() throws Exception { + doTest(1473873732723L, 47); + } + + @Test + public void testInsertAndEvict14() throws Exception { + doTest(1473871294851L, 93); + } + + @Test + public void testInsertAndEvict15() throws Exception { + doTest(1473871365625L, 93); + } + + @Test + public void testInsertAndEvict16() throws Exception { + doTest(1473871209569L, 157); + } + + private void doTest(final long rgenseed, final long length) throws RemoteException, IOException, NoSuchFieldException, IllegalAccessException { + final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration(); + fpSetConfig.setMemoryInFingerprintCnt(length); + + final DiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig); + fpSet.init(1, createTmpFile(), filename); + + // Insert n randomly choosen positive longs. + Random random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + assertFalse(fpSet.put(getFingerprint(random))); + } + + // Get the current content of LongArray for later comparison of special elements. + Field field = OffHeapDiskFPSet.class.getDeclaredField("array"); + field.setAccessible(true); + final long[] expected = LongArrays.toArray((LongArray) field.get(fpSet)); + + // Flush/Evict the first time and assure its successful. + assertTrue(fpSet.getGrowDiskMark() == 0); + fpSet.forceFlush(); + fpSet.contains(1L); // contains triggers eviction + assertTrue(fpSet.getGrowDiskMark() == 1); + + // Special elements (EMPTY or marked evicted) do not change positions + // when sorted. + final LongArray actual = (LongArray) field.get(fpSet); + for (int i = 0; i < expected.length; i++) { + if (expected[i] == EMPTY) { + assertEquals( + String.format( + "Expected empty position with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }), + EMPTY, actual.get(i)); + } else if (expected[i] < EMPTY) { + assertEquals( + String.format( + "Expected negative position with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }), + EMPTY, actual.get(i)); + } + } + + random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + final long fp = getFingerprint(random); + assertTrue(String.format("Failed to find fp %s/%s with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { fp, (fp | MARK_FLUSHED), rgenseed, length, Arrays.toString(expected), + actual.toString() }), + fpSet.contains(fp)); + } + + assertTrue( + String.format("Invariant violated with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }), + fpSet.checkInvariant()); + + // Clear the index to secondary/disk which hides secondary from + // DiskFPSet. + field = DiskFPSet.class.getDeclaredField("index"); + field.setAccessible(true); + field.set(fpSet, null); + + // Confirm that even without secondary, it's possible to lookup the + // fingerprints. + random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + final long fp = getFingerprint(random); + assertTrue(String.format("Failed to find fp %s/%s with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { fp, (fp | MARK_FLUSHED), rgenseed, length, Arrays.toString(expected), + actual.toString() }), + fpSet.contains(fp)); + } + + fpSet.close(); + } + + @Test + public void testOffset1Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + final long length = DiskFPSet.NumEntriesPerPage; + doTestOffset(length, 1474536306841L); + } + + @Test + public void testOffset3Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + long length = DiskFPSet.NumEntriesPerPage * 3L; + doTestOffset(length, 1474536306841L); + } + + @Test + public void testOffset5Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + long length = DiskFPSet.NumEntriesPerPage * 5L; + doTestOffset(length, 1474536306841L); + } + + @Test + public void testOffset9Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + long length = DiskFPSet.NumEntriesPerPage * 9L; + doTestOffset(length, 1474536306841L); + } + + private void doTestOffset(long length, long rgenseed) throws RemoteException, IOException, NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + + final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration(); + fpSetConfig.setMemoryInFingerprintCnt(length); + + final OffHeapDiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig); + fpSet.init(1, createTmpFile(), filename); + + final SortedSet<Long> longs = new TreeSet<Long>(); + + // Insert n randomly chosen positive longs. + Random random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + long fingerprint = getFingerprint(random); + assertFalse(fpSet.put(fingerprint)); + longs.add(fingerprint); + } + fpSet.forceFlush(); + assertFalse(fpSet.contains(1L)); // contains triggers flush + + final Method field = OffHeapDiskFPSet.class.getDeclaredMethod("getDiskOffset", new Class[] {int.class, long.class}); + field.setAccessible(true); + + for (long i = 0L; i < longs.size(); i++) { + long fp = longs.first(); + assertEquals(String.format("Length: %s with seed: %s", length, rgenseed), i + 1L, + field.invoke(fpSet, 0, fp + 1L)); + longs.remove(fp); + } + } + + private static String createTmpFile() { + final String tmpdir = System.getProperty("java.io.tmpdir") + File.separator + "OffHeapDiskFPSetTest" + + System.currentTimeMillis(); + new File(tmpdir).mkdirs(); + return tmpdir; + } + + private static long getFingerprint(Random random) { + final long fp = (((long) random.nextInt(Integer.MAX_VALUE - 1) + 1) << 32) | (random.nextInt() & 0xffffffffL); + return fp; + } +} diff --git a/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java b/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java index 900c6fa7179bcaba23fdc390d3e38cebd42e122e..b3f8c35b5c93a890d2f58014727c0e96144f342c 100644 --- a/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java +++ b/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java @@ -37,39 +37,115 @@ public class OffHeapIndexerTest { @Test public void testBitshifting() throws RemoteException { final int fpBits = 1; - final int bucketCapacity = 1; final long positions = 128L; final int logPos = 8; - doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(bucketCapacity, positions, fpBits)); + doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits)); } @Test public void testRescale() throws RemoteException { final int fpBits = 1; - final int bucketCapacity = 1; - final long positions = 128L; - final int logPos = 8; - doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.Indexer(bucketCapacity, positions, fpBits)); + final long positions = 96L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1, 0)); + Assert.assertEquals(48, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1)); + } + + @Test + public void testRescale99() throws RemoteException { + final int fpBits = 1; + final long positions = 99L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(49, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1)); } @Test public void testBitshifting2() throws RemoteException { final int fpBits = 2; - final int bucketCapacity = 1; final long positions = 128L; final int logPos = 9; - doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(bucketCapacity, positions, fpBits)); + doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits)); } @Test public void testRescale2() throws RemoteException { final int fpBits = 2; - final int bucketCapacity = 1; - final long positions = 128L; - final int logPos = 9; - doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.Indexer(bucketCapacity, positions, fpBits)); + final long positions = 96L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(48, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(95, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 97)); } + @Test + public void testRescale299() throws RemoteException { + final int fpBits = 2; + final long positions = 99L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(49, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1)); + } + + @Test + public void testRescaleMaximum() throws RemoteException { + final int fpBits = 1; + final long positions = 11L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits, 11L); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(10, indexer.getIdx(11)); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx(11, 1)); + } + + @Test + public void testBitshiftOvershoot() throws RemoteException { + final int fpBits = 1; + final long positions = 536870912L; + + final Indexer indexer = new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits); + Assert.assertEquals(0, indexer.getIdx(9223371952792813846L, 5)); + } + private void doTest(final int fpBits, final long positions, final int logPos, final Indexer indexer) { Assert.assertTrue(Double.compare(Math.pow(2, logPos - fpBits), positions) == 0); @@ -77,10 +153,10 @@ public class OffHeapIndexerTest { for (long l = 0; l < positions; l++) { final long fp = l << (Long.SIZE - logPos); - Assert.assertEquals(l, indexer.getLogicalPosition(fp)); + Assert.assertEquals(l, indexer.getIdx(fp)); final long fpNext = ((l+1L) << (Long.SIZE - logPos)) - 1; - Assert.assertEquals(l, indexer.getLogicalPosition(fpNext)); + Assert.assertEquals(l, indexer.getIdx(fpNext)); } - Assert.assertEquals(0, indexer.getLogicalPosition(positions << (Long.SIZE - logPos))); + Assert.assertEquals(0, indexer.getIdx(positions << (Long.SIZE - logPos))); } } diff --git a/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java b/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..83f6ff0e2a06a9e118345078c72a7a6e9c03072d --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java @@ -0,0 +1,71 @@ +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import tlc2.tool.fp.OffHeapDiskFPSet.Iterator; +import util.TLCRuntime; + +public class OffHeapIteratorTest { + + @Before + public void setup() { + Assume.assumeTrue(LongArray.isSupported()); + } + + @Test + public void testNext() { + final long elements = 32L; + final LongArray array = new LongArray(2L * elements); + for (int i = 0; i < array.size(); i++) { + array.set(i, i+1); + } + + final Iterator itr = new OffHeapDiskFPSet.Iterator(array, elements, + new OffHeapDiskFPSet.Indexer(2L * elements, 1)); + + long actual = 1L; + while (itr.hasNext()) { + long next = itr.next(); + assertEquals(actual, next); + actual++; + } + assertEquals(elements, actual - 1); + + for (int i = 0; i < array.size(); i++) { + assertEquals(i + 1, array.get(i)); + } + } + + @Test + public void testMarkNext() { + final int elements = 32; + final LongArray array = new LongArray(2L * elements); + for (int i = 0; i < array.size(); i++) { + array.set(i, i+1); + } + + final Iterator itr = new OffHeapDiskFPSet.Iterator(array, elements, + new OffHeapDiskFPSet.Indexer(2L * elements, 1)); + + long actual = 1L; + while (itr.hasNext()) { + long next = itr.markNext(); + assertEquals(actual, next); + actual++; + } + assertEquals(elements, actual - 1); + + for (int i = elements; i < array.size(); i++) { + assertEquals(elements + 1, array.get(elements)); + } + for (int i = 0; i < elements; i++) { + long expected = i + 1 | 0x8000000000000000L; + actual = array.get(i); + assertEquals(expected, actual); + } + } +} diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java index fcc2126e15b5a6dba3dd0d191db8b76e97c389a3..f40f7e0a9a0a36b74e9ac4af9e5282ffe0c9cd79 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java @@ -49,7 +49,7 @@ import tlc2.output.EC; public class CodePlexBug08EWD840FL2FromCheckpointTest extends ModelCheckerTestCase { public CodePlexBug08EWD840FL2FromCheckpointTest() { - super("EWD840MC2", "CodePlexBug08", new String[] {"-recover", BASE_DIR + TEST_MODEL + "CodePlexBug08" + File.separator + "checkpoint"}); + super("EWD840MC2", "CodePlexBug08", new String[] {"-gzip", "-recover", BASE_DIR + TEST_MODEL + "CodePlexBug08" + File.separator + "checkpoint"}); } diff --git a/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java b/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java index 037ed6d20c98e9874d6ed2ce6f3e692aaa94e1f1..daa793eb5aeea0eaef8a7114ee73a3cfa10c09d7 100644 --- a/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java +++ b/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java @@ -34,15 +34,17 @@ import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; + import org.junit.Test; + import tlc2.util.BitVector; import tlc2.util.LongVec; -import tlc2.util.statistics.BucketStatistics; +import tlc2.util.statistics.FixedSizedBucketStatistics; import tlc2.util.statistics.IBucketStatistics; public class DiskGraphTest { - private static final IBucketStatistics GRAPH_STATS = new BucketStatistics("Test Dummy", 16); + private static final IBucketStatistics GRAPH_STATS = new FixedSizedBucketStatistics("Test Dummy", 16); private static final int NUMBER_OF_SOLUTIONS = 1; private static final int NO_TABLEAU = -1; private static final int NUMBER_OF_ACTIONS = 0; diff --git a/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java b/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java new file mode 100644 index 0000000000000000000000000000000000000000..d2ca8be69fc2ffb7fc0fdd5ef9562c342f012884 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class LivenessSymmetryWarning extends ModelCheckerTestCase { + + public LivenessSymmetryWarning() { + super("April25MC", "symmetry"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java b/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java index 8ff6006c787a2bcdff6f128cabdf447cd36a1686..675fcd1a8ccf63e47f032b12bb1c91f893184f66 100644 --- a/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java +++ b/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java @@ -61,6 +61,10 @@ public abstract class ModelCheckerTestCase extends CommonTestCase { this.path = path; } + public ModelCheckerTestCase(String spec, String[] extraArguments) { + this(spec, "", extraArguments); + } + public ModelCheckerTestCase(String spec, String path, String[] extraArguments) { this(spec, path); this.extraArguments = extraArguments; diff --git a/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java b/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java index 2ea24ef276c1aea4ce7465a16c5119b884c66b2e..ac0cb958289c0c260e94c75f76868b84de3a3737 100644 --- a/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java +++ b/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java @@ -32,15 +32,17 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; + import org.junit.Test; + import tlc2.util.BitVector; import tlc2.util.LongVec; -import tlc2.util.statistics.BucketStatistics; +import tlc2.util.statistics.FixedSizedBucketStatistics; import tlc2.util.statistics.IBucketStatistics; public class TableauDiskGraphTest extends DiskGraphTest { - private static final IBucketStatistics GRAPH_STATS = new BucketStatistics("Test Dummy", 16); + private static final IBucketStatistics GRAPH_STATS = new FixedSizedBucketStatistics("Test Dummy", 16); private static final int NUMBER_OF_SOLUTIONS = 1; private static final int NUMBER_OF_ACTIONS = 0; private static final BitVector NO_ACTIONS = null; diff --git a/tlatools/test/tlc2/tool/suite/ETest4.java b/tlatools/test/tlc2/tool/suite/ETest4.java index 068547335dac9d9a545d3b2d9956ee88fb3f5bbc..09187b196bfb83a7c217ce7cecc696a769abd666 100644 --- a/tlatools/test/tlc2/tool/suite/ETest4.java +++ b/tlatools/test/tlc2/tool/suite/ETest4.java @@ -1,9 +1,9 @@ /******************************************************************************* - * Copyright (c) 2016 Microsoft Research. All rights reserved. + * Copyright (c) 2016 Microsoft Research. All rights reserved. * * The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies @@ -11,8 +11,8 @@ * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * + * copies or substantial portions of the Software. + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR @@ -37,6 +37,11 @@ public class ETest4 extends SuiteETestCase { public void testSpec() { assertTrue(recorder.recorded(EC.TLC_FINISHED)); assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); - assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION, "0. Line 15, column 12 to line 15, column 22 in etest4\n\n")); + String s = "0. Line 13, column 9 to line 16, column 51 in etest4\n" + + "1. Line 13, column 12 to line 13, column 16 in etest4\n" + + "2. Line 14, column 12 to line 14, column 77 in etest4\n" + + "3. Line 15, column 12 to line 15, column 26 in etest4\n" + + "4. Line 15, column 12 to line 15, column 22 in etest4\n\n"; + assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION, s)); } } diff --git a/tlatools/test/tlc2/tool/suite/ETest7.java b/tlatools/test/tlc2/tool/suite/ETest7.java index 2ae7371cf00dba2e06d1a51fedd5a411788e1bf7..c9da47ecb9470cc98eb9740bbd51fbc33bb0b043 100644 --- a/tlatools/test/tlc2/tool/suite/ETest7.java +++ b/tlatools/test/tlc2/tool/suite/ETest7.java @@ -36,6 +36,6 @@ public class ETest7 extends SuiteETestCase { @Test public void testSpec() { assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL, - "In evaluation, the identifier x is either undefined or not an operator.\nline 11, col 8 to line 11, col 8 of module etest7")); + "The configuration file substitutes constant C with non-constant Foo.")); } } diff --git a/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java b/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java new file mode 100644 index 0000000000000000000000000000000000000000..1e7f5b1d3ff37fe87eee2fc38fff698e7bbeea7b --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TestInvalidInvariant extends ModelCheckerTestCase { + + public TestInvalidInvariant() { + super("testinvalidinvariant"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_LEVEL, "Invariant")); + assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL, + "The invariant Invariant is not a state predicate (one with no primes or temporal operators).")); + // See LevelNode.java line 590ff and tlc2.tool.Spec.processConfigInvariants(). + assertFalse(recorder.recordedWithSubStringValue(EC.GENERAL, + "Note that a bug can cause TLC to incorrectly report this error.")); + } +} diff --git a/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java b/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java index 245bd76005302333ff50430116a63c4bc8a32e66..8c52812dfa3403bf7cbc19f1b52177b9f6e40630 100644 --- a/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java +++ b/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java @@ -18,7 +18,7 @@ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINbucketStatistic IN THE SOFTWARE. * * Contributors: * Markus Alexander Kuppe - initial API and implementation @@ -30,174 +30,190 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; + import org.junit.Test; -import tlc2.util.statistics.BucketStatistics; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(Parameterized.class) public class BucketStatisticsTest { - + + @SuppressWarnings("rawtypes") + @Parameterized.Parameters + public static Collection<Class> bucketStatistics() { + return Arrays.asList( + new Class[] { ConcurrentBucketStatistics.class, BucketStatistics.class }); + } + + private final IBucketStatistics bucketStatistic; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public BucketStatisticsTest(Class bucketStatistic) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + this.bucketStatistic = (IBucketStatistics) bucketStatistic.getConstructor(String.class) + .newInstance("BucketStatisticsTest"); + } + @Test public void testInvalidArgument() { try { - final IBucketStatistics gs = new BucketStatistics(); - gs.addSample(-1); + bucketStatistic.addSample(-1); } catch (IllegalArgumentException e) { return; } fail(); } - + @Test public void testMean() { - final IBucketStatistics gs = new BucketStatistics(); - assertTrue(Double.compare(-1.0d, gs.getMean()) == 0); - - gs.addSample(0); - assertTrue(Double.compare(0.0d, gs.getMean()) == 0); - - gs.addSample(1); - gs.addSample(2); - assertTrue(Double.compare(1.0d, gs.getMean()) == 0); - - gs.addSample(2); - gs.addSample(2); - assertTrue(Double.compare(1.4d, gs.getMean()) == 0); + assertTrue(Double.compare(-1.0d, bucketStatistic.getMean()) == 0); + + bucketStatistic.addSample(0); + assertTrue(Double.compare(0.0d, bucketStatistic.getMean()) == 0); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + assertTrue(Double.compare(1.0d, bucketStatistic.getMean()) == 0); + + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertTrue(Double.compare(1.4d, bucketStatistic.getMean()) == 0); } @Test public void testMedian() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1, gs.getMedian()); - - gs.addSample(0); - assertEquals(0, gs.getMedian()); - - gs.addSample(1); - gs.addSample(2); - assertEquals(1, gs.getMedian()); - - gs.addSample(2); - gs.addSample(2); - assertEquals(2, gs.getMedian()); + assertEquals(-1, bucketStatistic.getMedian()); + + bucketStatistic.addSample(0); + assertEquals(0, bucketStatistic.getMedian()); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + assertEquals(1, bucketStatistic.getMedian()); + + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(2, bucketStatistic.getMedian()); } @Test public void testMin() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1, gs.getMin()); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - assertEquals(0, gs.getMin()); + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(0, bucketStatistic.getMin()); } - + + @Test + public void testMin2() { + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(1, bucketStatistic.getMin()); + } + @Test public void testMax() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1, gs.getMax()); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - assertEquals(3, gs.getMax()); + assertEquals(-1, bucketStatistic.getMax()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertEquals(3, bucketStatistic.getMax()); } @Test public void testStandardDeviation() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1.0, gs.getStdDev(), 0); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - assertTrue(Double.compare(1.005d, (Math.round(gs.getStdDev() * 10000d) / 10000d)) == 0); + assertEquals(-1.0, bucketStatistic.getStdDev(), 0); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertTrue(Double.compare(1.005d, (Math.round(bucketStatistic.getStdDev() * 10000d) / 10000d)) == 0); } @Test public void testGetPercentile() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1.0, gs.getPercentile(1), 0); - + assertEquals(-1.0, bucketStatistic.getPercentile(1), 0); + try { - gs.addSample(1); // <- first element - gs.getPercentile(1.1); - gs.getPercentile(-0.1); + bucketStatistic.addSample(1); // <- first element + bucketStatistic.getPercentile(1.1); + bucketStatistic.getPercentile(-0.1); } catch (Exception e) { fail(e.getMessage()); } - - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); // <- 0.5 percentile - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - assertTrue(Double.compare(2.0d, gs.getPercentile(0.5d)) == 0); - assertTrue(Double.compare(2.0d, gs.getPercentile(0.5d)) == 0); - assertTrue(Double.compare(2.0d, gs.getPercentile(0.75d)) == 0); - assertTrue(Double.compare(3.0d, gs.getPercentile(0.999d)) == 0); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); // <- 0.5 percentile + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.5d)) == 0); + assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.5d)) == 0); + assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.75d)) == 0); + assertTrue(Double.compare(3.0d, bucketStatistic.getPercentile(0.999d)) == 0); } + // NaN test @Test public void testGetPercentileNaN() { try { - final IBucketStatistics gs = new BucketStatistics(); - gs.getPercentile(Double.NaN); + bucketStatistic.getPercentile(Double.NaN); } catch (IllegalArgumentException e) { return; } fail("Parameter not a number"); } - + @Test public void testToString() { try { - //just invoke to check for exceptions - final IBucketStatistics gs = new BucketStatistics(); - gs.toString(); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - gs.toString(); + // just invoke to check for exceptions + bucketStatistic.toString(); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + bucketStatistic.toString(); } catch (Exception e) { fail(e.getMessage()); } } - - @Test - public void testMaximum() { - final IBucketStatistics gs = new BucketStatistics("test title", 8); - gs.addSample(16); - gs.addSample(16); - gs.addSample(16); - assertEquals(8, gs.getMedian()); - assertEquals(3, gs.getObservations()); - } } \ No newline at end of file diff --git a/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java b/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..377150c6cb0200b152b83bc4ef581a53c0e2732c --- /dev/null +++ b/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINbucketStatistic IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util.statistics; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class FixedSizedBucketStatisticsTest { + + @SuppressWarnings("rawtypes") + @Parameterized.Parameters + public static Collection<Class> bucketStatistics() { + return Arrays.asList( + new Class[] { FixedSizedConcurrentBucketStatistics.class, FixedSizedBucketStatistics.class }); + } + + private final IBucketStatistics bucketStatistic; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public FixedSizedBucketStatisticsTest(Class bucketStatistic) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + this.bucketStatistic = (IBucketStatistics) bucketStatistic.getConstructor(String.class, int.class) + .newInstance("FixedSizedBucketStatisticsTest", 8); + } + + @Test + public void testMin() { + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(0, bucketStatistic.getMin()); + } + + @Test + public void testMin2() { + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(1, bucketStatistic.getMin()); + } + + @Test + public void testMax() { + assertEquals(-1, bucketStatistic.getMax()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertEquals(3, bucketStatistic.getMax()); + } + + @Test + public void testInvalidArgument() { + try { + bucketStatistic.addSample(-1); + } catch (IllegalArgumentException e) { + return; + } + fail(); + } + + // NaN test + @Test + public void testGetPercentileNaN() { + try { + bucketStatistic.getPercentile(Double.NaN); + } catch (IllegalArgumentException e) { + return; + } + fail("Parameter not a number"); + } + + @Test + public void testMaximum() { + bucketStatistic.addSample(16); + bucketStatistic.addSample(16); + bucketStatistic.addSample(16); + assertEquals(7, bucketStatistic.getMax()); + assertEquals(7, bucketStatistic.getMedian()); + assertEquals(3, bucketStatistic.getObservations()); + } +} \ No newline at end of file diff --git a/tlatools/test/util/TestPrintStream.java b/tlatools/test/util/TestPrintStream.java new file mode 100644 index 0000000000000000000000000000000000000000..fc362d0bd6c7066a15136076f62528d34df2ce71 --- /dev/null +++ b/tlatools/test/util/TestPrintStream.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import static org.junit.Assert.fail; + +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +public class TestPrintStream extends PrintStream { + + private final List<String> strings = new ArrayList<String>(); + + public TestPrintStream() { + super(new PipedOutputStream()); + } + + /* (non-Javadoc) + * @see java.io.PrintStream#println(java.lang.String) + */ + public void println(String x) { + strings.add(x); + System.out.println(x); + super.println(x); + } + + public void assertSubstring(String substring) { + for (String string : strings) { + if (string.contains(substring)) { + return; + } + } + fail("Substring not found"); + } +} \ No newline at end of file