Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
Fix crash when undoing past the first print transaction. #160
Conversation
|
Very nice, I like the approach! I believe most of my comments are on implementation details, nothing fundamental. |
| ///\param[in] prev - merge into transaction prior to T or T itself | ||
| ///\returns the parent transaction of the merge. | ||
| /// | ||
| const Transaction* mergeTransactionsAfter(const Transaction *T, bool prev); |
Axel-Naumann
Jun 27, 2017
Member
I don't like that prev boolean as an interface. Can we change the interface into mergeNextTransactions(Transaction* into); that merges transactions after the argument into the argument? And then either pass T or T->getNext()?
I don't like that prev boolean as an interface. Can we change the interface into mergeNextTransactions(Transaction* into); that merges transactions after the argument into the argument? And then either pass T or T->getNext()?
marsupial
Jun 27, 2017
Author
Contributor
To me that couples the interfaces in a way that is undesirable. That is: to use the function (or wrapper class) one would also need to know an implementation detail of Transaction. A simple bool avoids this.
To me that couples the interfaces in a way that is undesirable. That is: to use the function (or wrapper class) one would also need to know an implementation detail of Transaction. A simple bool avoids this.
Axel-Naumann
Jun 27, 2017
Member
I do not understand your argument.
I do not understand your argument.
marsupial
Jun 27, 2017
Author
Contributor
-
To use this functionality I don't think a requirement should be an #include "Transaction.h" and the caller knowing how Transactions are implemented.
-
The difference is more complicated than T or T->getNext(). As Previous get's the Transaction before what was passed.
-
To use this functionality I don't think a requirement should be an
#include "Transaction.h"and the caller knowing how Transactions are implemented. -
The difference is more complicated than T or T->getNext(). As Previous get's the Transaction before what was passed.
Axel-Naumann
Jun 29, 2017
Member
Okay, we don't agree. Let's try from a different angle: do you actually need that argument? All calls seem to pass true. Can we just remove the argument and rename this to mergeTransactionsInfoPrevious(const Transaction *T)?
Okay, we don't agree. Let's try from a different angle: do you actually need that argument? All calls seem to pass true. Can we just remove the argument and rename this to mergeTransactionsInfoPrevious(const Transaction *T)?
| @@ -141,6 +141,16 @@ namespace cling { | |||
| } | |||
| } | |||
|
|
|||
| Interpreter::TransactionMerge::TransactionMerge(Interpreter *interp, | |||
| bool prev) : | |||
Axel-Naumann
Jun 27, 2017
Member
ditto for prev
ditto for prev
marsupial
Jun 27, 2017
Author
Contributor
ditto for prev
ditto for prev
| @@ -1193,20 +1203,40 @@ namespace cling { | |||
| return kSuccess; | |||
| } | |||
|
|
|||
| const Transaction *prntT = m_CachedTrns[kPrintValueTransaction]; | |||
Axel-Naumann
Jun 27, 2017
Member
Rename to printTBefore?
Rename to printTBefore?
| // If print value Transaction has changed value, then lastT is now | ||
| // the transaction that holds the transaction(s) of loading up the | ||
| // value printer and must be recorded as such. | ||
| if ( prntT != m_CachedTrns[kPrintValueTransaction] ) { |
Axel-Naumann
Jun 27, 2017
Member
No space after '(' and before ')', please.
No space after '(' and before ')', please.
| if ( prntT != m_CachedTrns[kPrintValueTransaction] ) { | ||
| assert(m_CachedTrns[kPrintValueTransaction] != nullptr | ||
| && "Value printer failed to load after success?"); | ||
| // As the stack is unwinded from recursive calls to EvaluateInternal |
Axel-Naumann
Jun 27, 2017
Member
"unwound"
"unwound"
Axel-Naumann
Jun 27, 2017
Member
"As the stack is unwinded from recursive calls to EvaluateInternal" are you saying that m_CachedTrns must be a top-most transaction? (I believe so - unload() can only unload top-most transactions.) If so, could you state that explicitly?
"As the stack is unwinded from recursive calls to EvaluateInternal" are you saying that m_CachedTrns must be a top-most transaction? (I believe so - unload() can only unload top-most transactions.) If so, could you state that explicitly?
| merge.pop_back(); | ||
| break; | ||
| } | ||
| } |
Axel-Naumann
Jun 27, 2017
Member
If T is not a top-most transaction, or unloaded, or rolled back, then it will not be part of m_Transactions. In that case, your code currently merges all transactions into the first (if I read your code correctly).
You should at least assert that you've seen T in m_Transactions. I'd even simply use find() instead of building the auxiliary SmallVector merge.
If T is not a top-most transaction, or unloaded, or rolled back, then it will not be part of m_Transactions. In that case, your code currently merges all transactions into the first (if I read your code correctly).
You should at least assert that you've seen T in m_Transactions. I'd even simply use find() instead of building the auxiliary SmallVector merge.
marsupial
Jun 27, 2017
Author
Contributor
No it checks merge.size()>1 which I'll change to !merge.empty().
The auxiliary vector is used for keeping and inspecting the existing parent structure.
No it checks merge.size()>1 which I'll change to !merge.empty().
The auxiliary vector is used for keeping and inspecting the existing parent structure.
marsupial
Jun 27, 2017
Author
Contributor
To be clearer "keeping and inspecting the existing parent structure," means that the Transactions must be cached as their real pointer values first, as iterators will be invalidated when fiddling with the nesting structure.
To be clearer "keeping and inspecting the existing parent structure," means that the Transactions must be cached as their real pointer values first, as iterators will be invalidated when fiddling with the nesting structure.
Axel-Naumann
Jun 29, 2017
Member
I don't think your comment addresses my concern:
If I call IncrementalParser::mergeTransactionsAfter((Transaction*)0x123, prev) it will merge all transactions into the first. Or crash. Probably depending on the value of prev. I don't see how !merge.empty() prevents that, as merge will contain all transactions.
I don't think your comment addresses my concern:
If I call IncrementalParser::mergeTransactionsAfter((Transaction*)0x123, prev) it will merge all transactions into the first. Or crash. Probably depending on the value of prev. I don't see how !merge.empty() prevents that, as merge will contain all transactions.
| // Try to keep everything current | ||
| if (m_Consumer->getTransaction()==cur) | ||
| m_Consumer->setTransaction(parent); | ||
| if (cur==parent->getNext()) // Make sure parent->next isn't a child |
Axel-Naumann
Jun 27, 2017
Member
patent->getNext() must point to the first transaction to be merged. You should probably assert that, and then set it to nullptr, because after mergeTransactionsAfter() is done the parent aught to not have a subsequent transaction anymore.
patent->getNext() must point to the first transaction to be merged. You should probably assert that, and then set it to nullptr, because after mergeTransactionsAfter() is done the parent aught to not have a subsequent transaction anymore.
marsupial
Jun 27, 2017
Author
Contributor
I tried to make it more apparent, but considering the case:
<cling::Transaction* A isEmpty=0 isCommitted=1>
<cling::Transaction* B isEmpty=0 isCommitted=1>
<cling::Transaction* C isEmpty=0 isCommitted=1>
<cling::Transaction* D isEmpty=0 isCommitted=1>
<cling::Transaction* E isEmpty=0 isCommitted=1>
Interp->mergeTransactionsAfter(A)
As each lower Transaction is put into Parent(A), Parent should absorb whatever next the Child had, until finally reaching E where it's next is nullptr.
I tried to make it more apparent, but considering the case:
<cling::Transaction* A isEmpty=0 isCommitted=1>
<cling::Transaction* B isEmpty=0 isCommitted=1>
<cling::Transaction* C isEmpty=0 isCommitted=1>
<cling::Transaction* D isEmpty=0 isCommitted=1>
<cling::Transaction* E isEmpty=0 isCommitted=1>
Interp->mergeTransactionsAfter(A)
As each lower Transaction is put into Parent(A), Parent should absorb whatever next the Child had, until finally reaching E where it's next is nullptr.
Axel-Naumann
Jun 29, 2017
Member
Let me try again. In the loop you do
Parent->setNext(const_cast<Transaction*>(CurChild->getNext()));
In the end you do
assert(Parent->getNext() == nullptr && "Parent still has next");
Why do you update Parent->getName() during the loop?
Let me try again. In the loop you do
Parent->setNext(const_cast<Transaction*>(CurChild->getNext()));
In the end you do
assert(Parent->getNext() == nullptr && "Parent still has next");
Why do you update Parent->getName() during the loop?
marsupial
Jun 29, 2017
Author
Contributor
Because as it loops it is stealing next from whatever it is absorbing.
<cling::Transaction* A isEmpty=0 isCommitted=1>
<cling::Transaction* B isEmpty=0 isCommitted=1>
<cling::Transaction* C isEmpty=0 isCommitted=1>
<cling::Transaction* D isEmpty=0 isCommitted=1>
<cling::Transaction* E isEmpty=0 isCommitted=1>
A.Next = B, B.Next = C, C.Next = D, D.Next = NULL
Interp->mergeTransactionsAfter(A):
Make B a child: A.Next = B.Next, B.Next = 0
Make C a child: A.Next = C.Next, C.Next = 0
Make D a child: A.Next = D.Next, D.Next = 0
Make D a child: A.Next = E.Next, E.Next = 0
If an exception is thrown before all children could be nested, then it should hopefully be in a state that is valid.
Because as it loops it is stealing next from whatever it is absorbing.
<cling::Transaction* A isEmpty=0 isCommitted=1>
<cling::Transaction* B isEmpty=0 isCommitted=1>
<cling::Transaction* C isEmpty=0 isCommitted=1>
<cling::Transaction* D isEmpty=0 isCommitted=1>
<cling::Transaction* E isEmpty=0 isCommitted=1>
A.Next = B, B.Next = C, C.Next = D, D.Next = NULL
Interp->mergeTransactionsAfter(A):
Make B a child: A.Next = B.Next, B.Next = 0
Make C a child: A.Next = C.Next, C.Next = 0
Make D a child: A.Next = D.Next, D.Next = 0
Make D a child: A.Next = E.Next, E.Next = 0
If an exception is thrown before all children could be nested, then it should hopefully be in a state that is valid.
| m_Consumer->setTransaction(parent); | ||
| if (cur==parent->getNext()) // Make sure parent->next isn't a child | ||
| parent->setNext(const_cast<Transaction*>(cur->getNext())); | ||
| while (cur->getParent()) // Keep parents in place |
Axel-Naumann
Jun 27, 2017
Member
Are you saying `m_Transaction" contains nested transactions? IIRC that would be a bug... I.e. this should probably be an assert.
Are you saying `m_Transaction" contains nested transactions? IIRC that would be a bug... I.e. this should probably be an assert.
| @@ -280,6 +280,55 @@ namespace cling { | |||
| return m_Consumer->getTransaction(); | |||
| } | |||
|
|
|||
| const Transaction* | |||
Axel-Naumann
Jun 27, 2017
Member
The return type seems unused. Can we make it void until we need something?
The return type seems unused. Can we make it void until we need something?
| @@ -0,0 +1,2 @@ | |||
|
|
|||
| ERROR; | |||
Axel-Naumann
Jun 27, 2017
Member
:-) Could you maybe add a comment that this is meant to simulate a parsing error of cling's original RuntimePrintValue.h?
:-) Could you maybe add a comment that this is meant to simulate a parsing error of cling's original RuntimePrintValue.h?
75ba416
to
1e519a0
…tring for undefined values.
…esizer::Transform.
…nd report errors.
…expression. Fix crash when RuntimePrintValue.h cannot be loaded. Merge value-printing transactions with the transaction that invoked it. The latter fixes poor functionality in the .undo system where the user must have knowledge of how many transactions are added when an expression is printed.

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

The use of a static makes this crash, but also makes it so a second interpreter in a session would never work. It also merges all Transactions generated by printing, so a statement become atomic for undo rather than a series unrelated Transactions (which abstracts the implementation away from the user a bit better).
Was
Becomes
This includes and is dependent on #157 & #159.