Change translation of implicit throws#1598
Change translation of implicit throws#1598chriseth merged 9 commits intoargotorg:developfrom wuestholz:develop
Conversation
|
|
||
| CompilerContext& CompilerContext::appendConditionalInvalid() | ||
| { | ||
| eth::AssemblyItem falseTag = appendConditionalJump(); |
There was a problem hiding this comment.
I wounder whether it might be better to just have a single jump:
ISZERO <continue> JUMPI INVALID continue:
Your code does:
<error> JUMPI <continue> JUMP error: INVALID continue:
There was a problem hiding this comment.
I intentionally tried not to be too clever about encoding the jumps. I had considered the same optimization. I have pushed this now.
| //Propagate error condition (if DELEGATECALL pushes 0 on stack). | ||
| a << Instruction::ISZERO; | ||
| a.appendJumpI(a.errorTag()); | ||
| eth::AssemblyItem falseTag = a.appendJumpI(); |
There was a problem hiding this comment.
Why don't you use appendConditionalInvalid() here?
There was a problem hiding this comment.
It's not available for class Assembly and I didn't want to add it since it is only used here. However, I'd be happy to add it if you want.
There was a problem hiding this comment.
Oh right, I'm sorry, I didn't see the full context. Is this case this is fine.
|
Thanks! Please don't forget to change the documentation, too. |
|
@chriseth Thank you very much for the feedback. I pushed changes that should address it. Did you have chance to look into why those three tests might be failing now? It it possible that my changes somehow uncovered a separate issue? |
| { | ||
| *this << Instruction::ISZERO; | ||
| eth::AssemblyItem afterTag = appendConditionalJump(); | ||
| return *this << Instruction::INVALID << afterTag; |
There was a problem hiding this comment.
The code generator is not too smart about the stack height with regards to control flow, so you have to manually adjust the stack height at the end of this function (it assumes that control flow continues after invalid). This is the reason for the test failure. You can try this snippet to check whether it worked:
contract test {
function x() returns (uint) { return 1; }
function y() returns (uint) { return 2; }
function f(bool cond) returns (uint) {
var z = cond ? x : y;
return z();
}
}
There was a problem hiding this comment.
Thanks! I tried the following:
unsigned startStackHeight = stackHeight();
*this << Instruction::ISZERO;
eth::AssemblyItem afterTag = appendConditionalJump();
*this << Instruction::INVALID << afterTag;
setStackOffset(startStackHeight - 1);
return *this;However, this didn't seem to do the trick. Were you thinking of something else?
|
Can you allow me access to your pull request branch (this is a new github feature, I think)? Then I will make the required adjustments. |
|
@chriseth Thanks! That's fine with me. I believe I checked some box about allowing this when I created the PR. I have also added you as a collaborator just in case. |
libevmasm/Instruction.h
Outdated
| LOG3, ///< Makes a log entry; 3 topics. | ||
| LOG4, ///< Makes a log entry; 4 topics. | ||
|
|
||
| INVALID = 0xef, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) |
There was a problem hiding this comment.
Please update to 0xfe as per the final EIP141.
|
This is still not fixed, but I think we are getting closer. |
|
Ok, found the problem: Zero-initialized variables of internal function type have to be a tag. I found a solution above that is not the best yet, will submit a new version shortly. |
This adds a new invalid instruction that is used for encoding implicit throws that are emitted by the compiler. This makes it possible to distinguish such runtime errors from user-provided, explicit throws.
|
@chriseth Great! Thanks for looking into this! |
docs/control-structures.rst
Outdated
|
|
||
| Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. | ||
| Internally, Solidity performs an "invalid jump" when a user-provided exception is thrown. In contrast, it performs an invalid operation | ||
| (code ``0xfe``) if a runtime exception is encountered. In both cases, this causes |
There was a problem hiding this comment.
Maybe "performs an invalid operation (0xfe instruction)" sounds a bit clearer?
| a << Instruction::ISZERO; | ||
| a.appendJumpI(a.errorTag()); | ||
| a << Instruction::ISZERO; | ||
| eth::AssemblyItem afterTag = a.appendJumpI(); |
There was a problem hiding this comment.
Shouldn't this have a .tag()?
There was a problem hiding this comment.
I guess it didn't fail because we don't have a test for it?
There was a problem hiding this comment.
Yes, clone binaries are an undocumented and also mostly useless feature.
There was a problem hiding this comment.
Created an issue for that (#1618) so we can track.
|
It is missing an entry in the changelog. |
|
Updated. |
|
Is the idea for |
|
@axic I don't think catch can really work in a consistent way (it has to roll back changes inside a single call), but if we do something like that, then |
Fixes #1589.
DO NOT MERGE, depensd on ethereum/EIPs#141
@chriseth @pirapira This adds a new invalid instruction that is used for encoding implicit throws that are emitted by the compiler. This makes it possible to distinguish such runtime errors from user-provided, explicit throws.
For some reason there seem to be three failing tests:
SolidityEndToEndTest/function_delete_stack,SolidityEndToEndTest/call_function_returning_function, andSolidityEndToEndTest/conditional_expression_functions. The failures are due to a failing assertion inCompilerUtils::moveToStackVariable. However, it's not really clear to me why these changes should affect that assertion. It would be great if someone could have a look.