-
Notifications
You must be signed in to change notification settings - Fork 997
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
fix: reorder named arguments to match declaration order #1949
Changes from 5 commits
1384ff6
84dd44a
f631ac7
c6da288
189051c
02f37d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -385,6 +385,70 @@ def integrate_value_gas(result: List[Operation]) -> List[Operation]: | |
################################################################################### | ||
|
||
|
||
def get_declared_param_names(ins: Call) -> Optional[List[str]]: | ||
""" | ||
Given a call operation, return the list of parameter names, in the order | ||
listed in the function declaration. | ||
#### Parameters | ||
ins - | ||
The call instruction | ||
#### Possible Returns | ||
List[str] - | ||
A list of the parameters in declaration order | ||
None - | ||
Workaround: Unable to obtain list of parameters in declaration order | ||
""" | ||
if isinstance(ins, NewStructure): | ||
return [x.name for x in ins.structure.elems_ordered if not isinstance(x.type, MappingType)] | ||
if isinstance(ins, NewContract): | ||
return [p.name for p in ins.contract.constructor.parameters] | ||
if isinstance(ins, (LowLevelCall, NewElementaryType, NewArray)): | ||
# named arguments are incompatible with these call forms | ||
assert False | ||
if isinstance(ins, HighLevelCall) and isinstance(ins.function, str): | ||
return None | ||
if isinstance(ins, (InternalCall, LibraryCall, HighLevelCall)): | ||
if isinstance(ins.function, Function): | ||
return [p.name for p in ins.function.parameters] | ||
return None | ||
if isinstance(ins, InternalDynamicCall): | ||
return [p.name for p in ins.function_type.params] | ||
|
||
assert isinstance(ins, EventCall) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this assertion for? Should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suppose that someone were to add a new subclass of The assertion communicates that we intend for this function to exhaustively handle all subclasses of Another way to write this would be:
Ideally, the |
||
return None | ||
|
||
|
||
def reorder_arguments( | ||
args: List[Variable], call_names: List[str], decl_names: List[str] | ||
) -> List[Variable]: | ||
""" | ||
Reorder named struct constructor arguments so that they match struct declaration ordering rather | ||
than call ordering | ||
E.g. for `struct S { int x; int y; }` we reorder `S({y : 2, x : 3})` to `S(3, 2)` | ||
#### Parameters | ||
args - | ||
Arguments to constructor call, in call order | ||
names - | ||
Parameter names in call order | ||
decl_names - | ||
Parameter names in declaration order | ||
#### Returns | ||
Reordered arguments to constructor call, now in declaration order | ||
""" | ||
assert isinstance(args, list) | ||
assert isinstance(call_names, list) | ||
assert isinstance(decl_names, list) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these can be removed since the function signature is typed and indicates this |
||
assert len(args) == len(call_names) | ||
assert len(call_names) == len(decl_names) | ||
|
||
args_ret = [] | ||
for n in decl_names: | ||
ind = call_names.index(n) | ||
args_ret.append(args[ind]) | ||
|
||
return args_ret | ||
|
||
|
||
def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]: | ||
""" | ||
Propagate the types variables and convert tmp call to real call operation | ||
|
@@ -434,6 +498,11 @@ def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> Li | |
if ins.call_id in calls_gas and isinstance(ins, (HighLevelCall, InternalDynamicCall)): | ||
ins.call_gas = calls_gas[ins.call_id] | ||
|
||
if isinstance(ins, Call) and (ins.names is not None): | ||
decl_param_names = get_declared_param_names(ins) | ||
if decl_param_names is not None: | ||
call_data = reorder_arguments(call_data, ins.names, decl_param_names) | ||
|
||
if isinstance(ins, (Call, NewContract, NewStructure)): | ||
# We might have stored some arguments for libraries | ||
if ins.arguments: | ||
|
@@ -855,7 +924,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, | |
if isinstance(ins.ori.variable_left, Contract): | ||
st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right) | ||
if st: | ||
op = NewStructure(st, ins.lvalue) | ||
op = NewStructure(st, ins.lvalue, names=ins.names) | ||
op.set_expression(ins.expression) | ||
op.call_id = ins.call_id | ||
return op | ||
|
@@ -892,6 +961,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, | |
ins.nbr_arguments, | ||
ins.lvalue, | ||
ins.type_call, | ||
names=ins.names, | ||
) | ||
libcall.set_expression(ins.expression) | ||
libcall.call_id = ins.call_id | ||
|
@@ -950,6 +1020,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, | |
len(lib_func.parameters), | ||
ins.lvalue, | ||
"d", | ||
names=ins.names, | ||
) | ||
lib_call.set_expression(ins.expression) | ||
lib_call.set_node(ins.node) | ||
|
@@ -1031,6 +1102,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, | |
ins.nbr_arguments, | ||
ins.lvalue, | ||
ins.type_call, | ||
names=ins.names, | ||
) | ||
msgcall.call_id = ins.call_id | ||
|
||
|
@@ -1082,7 +1154,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, | |
return n | ||
|
||
if isinstance(ins.called, Structure): | ||
op = NewStructure(ins.called, ins.lvalue) | ||
op = NewStructure(ins.called, ins.lvalue, names=ins.names) | ||
op.set_expression(ins.expression) | ||
op.call_id = ins.call_id | ||
op.set_expression(ins.expression) | ||
|
@@ -1106,7 +1178,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, | |
if len(ins.called.constructor.parameters) != ins.nbr_arguments: | ||
return Nop() | ||
internalcall = InternalCall( | ||
ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call | ||
ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call, ins.names | ||
) | ||
internalcall.call_id = ins.call_id | ||
internalcall.set_expression(ins.expression) | ||
|
@@ -1439,6 +1511,7 @@ def look_for_library_or_top_level( | |
ir.nbr_arguments, | ||
ir.lvalue, | ||
ir.type_call, | ||
names=ir.names, | ||
) | ||
lib_call.set_expression(ir.expression) | ||
lib_call.set_node(ir.node) | ||
|
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 we update the function signature to only be subclasses of call which are accepted and remove this? We can add an assertion like
assert isinstance(ins, NewStructure, NewContract... )
, tooThere 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 might be better to add
assert not isinstance(LowLevelCall, NewElementaryType, NewArray)
at the top of theget_declared_param_names
function.We could also make the
assert False
case returnNone
instead. Of course, it would be a hassle to get test coverage for this.