-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change translation of implicit throws #1598
Conversation
|
||
CompilerContext& CompilerContext::appendConditionalInvalid() | ||
{ | ||
eth::AssemblyItem falseTag = appendConditionalJump(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I intentionally tried not to be too clever about encoding the jumps. I had considered the same optimization. I have pushed this now.
@@ -910,7 +910,9 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() | |||
a << Instruction::DELEGATECALL; | |||
//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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't you use appendConditionalInvalid()
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
@@ -171,6 +171,8 @@ enum class Instruction: uint8_t | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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! |
|
||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe "performs an invalid operation (0xfe
instruction)" sounds a bit clearer?
@@ -918,7 +918,9 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() | |||
a << Instruction::DELEGATECALL; | |||
//Propagate error condition (if DELEGATECALL pushes 0 on stack). | |||
a << Instruction::ISZERO; | |||
a.appendJumpI(a.errorTag()); | |||
a << Instruction::ISZERO; | |||
eth::AssemblyItem afterTag = a.appendJumpI(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this have a .tag()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it didn't fail because we don't have a test for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, clone binaries are an undocumented and also mostly useless feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.