-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
Add modifier areas #3939
Add modifier areas #3939
Conversation
608a004
to
58824b8
Compare
Look at that... already found a (significant) bug. (now fixed) |
58824b8
to
da4fe8d
Compare
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.
At least the dependency cycle between modifier areas and function definitions needs to be resolved.
@@ -12,7 +12,7 @@ ContractDefinition = ( 'contract' | 'library' | 'interface' ) Identifier | |||
'{' ContractPart* '}' | |||
|
|||
ContractPart = StateVariableDeclaration | UsingForDeclaration | |||
| StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition | |||
| StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition | ModifierArea |
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 a line break may be good.
docs/grammar.txt
Outdated
@@ -32,6 +32,9 @@ EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';' | |||
EnumValue = Identifier | |||
EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}' | |||
|
|||
ModifierArea = 'using modifier' ModifierInvocation (',' ModifierInvocation)* | |||
'{' (ModifierArea | FunctionDefinition) '}' |
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.
Is this correct? Can't there be multiple function definitions and modifier areas in a modifier area? Actually there should also be test cases for that (at the moment I only see test cases with a single function definition in a modifier area).
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. I think I just forgot the asterisk.
docs/structure-of-a-contract.rst
Outdated
|
||
Multiple modifier invocations can be used in a single modifier area: | ||
`using modifier A, B { /* ... */ }` declares a modifier area | ||
where both modifiers are used on every function.a |
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 the a
in the end is a typo?
libsolidity/ast/AST.cpp
Outdated
std::vector<FunctionDefinition const*> totalFunctions = filteredNodes<FunctionDefinition>(m_subNodes); | ||
std::vector<ModifierArea const*> baseModifierAreas = modifierAreas(); | ||
std::vector<ModifierArea const*> nextGroups; | ||
while (!baseModifierAreas.empty()) |
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 think this may be easier to read, if you used a queue
of ModifierArea const*
. Then you could extract the front element into a local variable, pop it from the queue, process it and while doing so just add the subareas back to the queue (and continue until the queue is empty).
This may only be my personal taste, though, and it should also be fine as it is now.
libsolidity/ast/AST.cpp
Outdated
if (m_modifierArea) | ||
{ | ||
auto const areaModifiers = _modifierArea->totalModifiers(); | ||
realModifiers.insert(realModifiers.begin(), areaModifiers.begin(), areaModifiers.end()); |
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.
There are overloads for +
and +=
for vectors in libdevcore (I think) - so you should be able to just use
realModifiers = areaModifiers + realModifiers;
.
libsolidity/ast/AST.cpp
Outdated
if (m_parent) | ||
{ | ||
std::vector<ASTPointer<ModifierInvocation>> parentModifiers = m_parent->totalModifiers(); | ||
completeVector.insert(completeVector.begin(), parentModifiers.begin(), parentModifiers.end()); |
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.
Same as above. Also: why do you need the local variable completeVector
? You could just start by setting m_totalModifiers = m_declaredModifiers;
and then continue with m_totalModifiers = parentModifiers + m_totalModifiers;
. Even better: do it the other way round: if there is a parent first set m_totalModifiers = parentModifers;
and then use m_totalModifiers += m_declaredModifiers;
after the ìf
.
if (_visitor.visit(*this)) | ||
{ | ||
listAccept(m_declaredModifiers, _visitor); | ||
listAccept(*m_functions, _visitor); |
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.
Somewhere you should probably assert that these members (m_functions
, m_subAreas
) aren't null pointers. Either here or in the constructor of ModifierArea
or even at both places (or otherwise this should be an if (m_functions) listAccept(...)
if the pointer may actually be null, although it doesn't look like they can... as stated above it may make sense for them not to be pointer at all).
libsolidity/parsing/Parser.cpp
Outdated
RecursionGuard recursionGuard(*this); | ||
|
||
if (m_scanner->peekNextToken() == Token::Identifier) | ||
{ |
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.
We usually don't use brackets for single line if
, resp. else
.
libsolidity/ast/AST.h
Outdated
private: | ||
std::vector<ASTPointer<ModifierInvocation>> m_totalModifiers; | ||
std::vector<ASTPointer<ModifierInvocation>> m_declaredModifiers; | ||
ASTPointer<std::vector<ASTPointer<FunctionDefinition>> const> m_functions; |
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.
Actually: it doesn't seem like those can ever be null or that they are passed around, resp. shared - so why use ASTPointer
s and not just the vector
s themselves as members?
libsolidity/parsing/Parser.cpp
Outdated
ASTPointer<std::vector<ASTPointer<FunctionDefinition>>> functions = std::make_shared<std::vector<ASTPointer<FunctionDefinition>>>(); | ||
ASTPointer<std::vector<ASTPointer<ModifierArea>>> subAreas = std::make_shared<std::vector<ASTPointer<ModifierArea>>>(); | ||
|
||
ASTPointer<ModifierArea> modifierArea = nodeFactory.createNode<ModifierArea>(modifiers, functions, subAreas, _parent); |
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.
OK - this is probably why you use ASTPointer
's for the functions and subareas in ModifierArea
- so that you can create the modifier area here and still fill in the functions and subareas below. However: are you sure you don't introduce cyclic dependencies of shared pointers here? The modifier areas contains a list of shared pointers to function definitions and the function definitions contain a shared pointer back to the modifier area? You probably at least need to change the parent pointers in the function definitions to a plain pointer instead of a shared pointer, although it would even be better if you could avoid such cyclic constructions altogether, if at all possible.
@ekpyron , thanks for all of the feedback. I can't actually do anything for 5 days for personal reasons, but I will definitely look at it then :). |
@meowingtwurtle No worries :-)! |
What would be the syntax if we add visibility eventually? Something like this?
Or is it going to be possible to put visibility besides other modifiers? In that case, are we ok with prefixing the whole list with the keyword Edit: perhaps I should have brought this up in #623 or #2608 (comment) |
I would actually prefer to have visibility (and payability) specifiers and modifiers treated in the same way, although I would not use a comma as separator (we also do not have a comma in the function syntax). |
Okay, I see two ways to do this, and I would love ideas:
|
@meowingtwurtle You should definitely have AST nodes for the modifier areas. The main problem with the current code is that it leaks memory. Since the modifier area AST node contains shared pointers to the function definitions and the function definitions contain shared pointers to the modifier area, they can never be freed, since the reference counters of the shared pointers will never fall to zero. Do you see the problem? The easiest solution for that would be to use a plain, non-reference-counted pointer to the modifier area in the function definitions. That way you still have access to the modifier area from the function definitions, but you still break the reference cycle. You can compare with e.g. An alternative would be to not have a pointer to the modifier area in the function definitions themselves, but instead to add a (plain) pointer to |
Okay, thank you! |
Okay, taking @ekpyron 's advice about pointers and @chriseth 's comment about visibility, mutability, and comma separation. (haven't finished yet) Right now, the only way to differentiate between modifier area and using directive for parsing is whether or not the token after |
cec8e15
to
dccdbce
Compare
|
dccdbce
to
935198b
Compare
Add ModifierArea class Add methods to appropriate classes
935198b
to
69d6bef
Compare
libsolidity/parsing/Token.h
Outdated
@@ -182,6 +182,7 @@ namespace solidity | |||
K(Var, "var", 0) \ | |||
K(View, "view", 0) \ | |||
K(While, "while", 0) \ | |||
K(Apply, "apply", 0) \ |
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.
This section is alphabetically ordered.
libsolidity/parsing/Parser.cpp
Outdated
else if (_modifierArea && _modifierArea->visibility() != Declaration::Visibility::Default) | ||
{ | ||
parserError("Cannot override modifier area's visibility."); | ||
m_scanner->next(); |
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.
Can these checks be moved to SyntaxtChecker?
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
@@ -723,6 +721,42 @@ class VariableDeclaration: public Declaration | |||
Location m_location; ///< Location of the variable if it is of reference type. | |||
}; | |||
|
|||
class ModifierArea: public ASTNode | |||
{ | |||
public: |
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 think indentation here is off.
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 just copied it from surrounding classes
@@ -646,6 +643,7 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public | |||
bool m_isConstructor; | |||
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers; | |||
ASTPointer<Block> m_body; | |||
ModifierArea* m_modifierArea; |
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.
Perhaps use ASTPointer
?
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.
This was something @ekpyron mentioned. We can''t use a smart pointer here because then there is a dependency loop and neither ModifierArea nor FunctionDefinition would be destructed.
libsolidity/ast/AST.h
Outdated
@@ -620,6 +614,9 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public | |||
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } | |||
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } | |||
Block const& body() const { solAssert(m_body, ""); return *m_body; } | |||
ModifierArea* modifierArea() { return m_modifierArea; } | |||
|
|||
std::string fullyQualifiedName() const; |
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 think this isn't needed anymore?
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.
Done
Remove contract name parameters Fix function visibility/state mutability
Codecov Report
@@ Coverage Diff @@
## develop #3939 +/- ##
===========================================
- Coverage 87.56% 87.54% -0.03%
===========================================
Files 313 313
Lines 30813 30954 +141
Branches 3662 3677 +15
===========================================
+ Hits 26981 27098 +117
- Misses 2579 2589 +10
- Partials 1253 1267 +14
|
Closing since this is unfortunately too much outdate for now :( |
Closes #623.
Yes, the code here is pretty sloppy. Feedback (from maintainers and others) is welcome and encouraged!
I'm especially interested in hearing suggestions people have for syntax.
Syntax:
The ellipsis after apply can contain any modifier invocations, a state mutability specifier, and a visibility mutability specifier, or any combination of the above. Inside the area there can be more modifier areas (modifier invocations are cumulative) and function declarations. Nested modifier areas and function declarations cannot have a different visibility or mutability than their modifier areas.