Skip to content
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

Feat/version 3.6.1 #283

Merged
merged 3 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "woke"
version = "3.6.0"
version = "3.6.1"
description = "Woke is a Python-based development and testing framework for Solidity."
license = "ISC"
authors = ["Ackee Blockchain"]
Expand Down
107 changes: 63 additions & 44 deletions woke/ast/ir/expression/identifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from woke.ast.nodes import AstNodeId, SolcIdentifier

if TYPE_CHECKING:
from woke.ast.ir.declaration.function_definition import FunctionDefinition
from woke.ast.ir.meta.import_directive import ImportDirective
from woke.ast.ir.meta.source_unit import SourceUnit

Expand All @@ -27,7 +28,7 @@ class Identifier(ExpressionAbc):

_name: str
_overloaded_declarations: List[AstNodeId]
_referenced_declaration_id: Optional[AstNodeId]
_referenced_declaration_ids: Set[AstNodeId]

def __init__(
self, init: IrInitTuple, identifier: SolcIdentifier, parent: SolidityAbc
Expand All @@ -37,50 +38,58 @@ def __init__(
super().__init__(init, identifier, parent)
self._name = identifier.name
self._overloaded_declarations = list(identifier.overloaded_declarations)
self._referenced_declaration_id = identifier.referenced_declaration
if self._referenced_declaration_id is None:
if identifier.referenced_declaration is None:
assert isinstance(self._parent, ImportDirective)
self._referenced_declaration_ids = set()
else:
self._referenced_declaration_ids = {identifier.referenced_declaration}
init.reference_resolver.register_post_process_callback(
self._post_process, priority=-1
)

def _post_process(self, callback_params: CallbackParams):
from ..meta.import_directive import ImportDirective

assert self._referenced_declaration_id is not None
if self._referenced_declaration_id < 0:
global_symbol = GlobalSymbolsEnum(self._referenced_declaration_id)
self._reference_resolver.register_global_symbol_reference(
global_symbol, self
)
self._reference_resolver.register_destroy_callback(
self.file, partial(self._destroy, global_symbol)
)
else:
node = self._reference_resolver.resolve_node(
self._referenced_declaration_id, self._cu_hash
)
new_referenced_declaration_ids = set()

if isinstance(node, DeclarationAbc):
node.register_reference(self)
for referenced_declaration_id in self._referenced_declaration_ids:
if referenced_declaration_id < 0:
global_symbol = GlobalSymbolsEnum(referenced_declaration_id)
self._reference_resolver.register_global_symbol_reference(
global_symbol, self
)
self._reference_resolver.register_destroy_callback(
self.file, partial(self._destroy, node)
self.file, partial(self._destroy, global_symbol)
)
elif isinstance(node, ImportDirective):
# make this node to reference the source unit directly
assert node.unit_alias is not None
source_unit = callback_params.source_units[node.imported_file]
node_path_order = self._reference_resolver.get_node_path_order(
AstNodeId(source_unit.ast_node_id),
source_unit.cu_hash,
new_referenced_declaration_ids.add(referenced_declaration_id)
else:
node = self._reference_resolver.resolve_node(
referenced_declaration_id, self._cu_hash
)
self._referenced_declaration_id = (
self._reference_resolver.get_ast_id_from_cu_node_path_order(
node_path_order, self.cu_hash

if isinstance(node, DeclarationAbc):
node.register_reference(self)
self._reference_resolver.register_destroy_callback(
self.file, partial(self._destroy, node)
)
)
else:
raise TypeError(f"Unexpected type: {type(node)}")
new_referenced_declaration_ids.add(referenced_declaration_id)
elif isinstance(node, ImportDirective):
# make this node to reference the source unit directly
assert node.unit_alias is not None
source_unit = callback_params.source_units[node.imported_file]
node_path_order = self._reference_resolver.get_node_path_order(
AstNodeId(source_unit.ast_node_id),
source_unit.cu_hash,
)
new_referenced_declaration_ids.add(
self._reference_resolver.get_ast_id_from_cu_node_path_order(
node_path_order, self.cu_hash
)
)
else:
raise TypeError(f"Unexpected type: {type(node)}")

self._referenced_declaration_ids = new_referenced_declaration_ids

def _destroy(
self, referenced_declaration: Union[GlobalSymbolsEnum, DeclarationAbc]
Expand Down Expand Up @@ -119,20 +128,30 @@ def overloaded_declarations(self) -> Tuple[DeclarationAbc, ...]:
@property
def referenced_declaration(
self,
) -> Union[DeclarationAbc, GlobalSymbolsEnum, SourceUnit]:
from ..meta.source_unit import SourceUnit
) -> Union[DeclarationAbc, GlobalSymbolsEnum, SourceUnit, Set[FunctionDefinition]]:
def resolve(referenced_declaration_id: AstNodeId):
if referenced_declaration_id < 0:
return GlobalSymbolsEnum(referenced_declaration_id)

node = self._reference_resolver.resolve_node(
referenced_declaration_id, self._cu_hash
)
assert isinstance(
node, (DeclarationAbc, SourceUnit)
), f"Unexpected type: {type(node)}\n{node.source}\n{self.source}\n{self.file}"
return node

assert self._referenced_declaration_id is not None
if self._referenced_declaration_id < 0:
return GlobalSymbolsEnum(self._referenced_declaration_id)
from ..declaration.function_definition import FunctionDefinition
from ..meta.source_unit import SourceUnit

node = self._reference_resolver.resolve_node(
self._referenced_declaration_id, self._cu_hash
)
assert isinstance(
node, (DeclarationAbc, SourceUnit)
), f"Unexpected type: {type(node)}\n{node.source}\n{self.source}\n{self.file}"
return node
assert len(self._referenced_declaration_ids) != 0
if len(self._referenced_declaration_ids) == 1:
return resolve(next(iter(self._referenced_declaration_ids)))
else:
# Identifier in ImportDirective symbol alias referencing multiple overloaded functions
ret = set(map(resolve, self._referenced_declaration_ids))
assert all(isinstance(x, FunctionDefinition) for x in ret)
return ret # pyright: ignore reportGeneralTypeIssues

@property
@lru_cache(maxsize=2048)
Expand Down
46 changes: 28 additions & 18 deletions woke/ast/ir/meta/import_directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,32 +124,37 @@ def __iter__(self) -> Iterator[IrAbc]:
yield from symbol_alias.foreign

def _post_process(self, callback_params: CallbackParams):
# referenced declaration ID is missing (for whatever reason) in import directive symbol aliases
from ..declaration.function_definition import FunctionDefinition

# referenced declaration ID is missing in import directive symbol aliases
# the reason is that the Identifier may refer to multiple overloaded functions
# for example `import { SafeType } from "SafeLib.sol";`
# fix: find these reference IDs manually
# seems to be fixed in solc >= 0.8.12
for symbol_alias in self._symbol_aliases:
if symbol_alias.foreign._referenced_declaration_id is not None:
if len(symbol_alias.foreign._referenced_declaration_ids) != 0:
continue

source_units_queue: Deque[SourceUnit] = deque(
[callback_params.source_units[self._imported_file]]
)
processed_source_units: Set[Path] = {self._imported_file}
referenced_declaration = None
referenced_declarations = set()
search = True
searched_name = symbol_alias.foreign.name

while source_units_queue and referenced_declaration is None:
while source_units_queue and search:
imported_source_unit = source_units_queue.pop()

for declaration in imported_source_unit.declarations_iter():
if declaration.canonical_name == searched_name:
referenced_declaration = declaration
break
if declaration.name == searched_name:
referenced_declarations.add(declaration)
if not isinstance(declaration, FunctionDefinition):
search = False
break

for import_ in imported_source_unit.imports:
if import_.unit_alias == searched_name:
referenced_declaration = import_
referenced_declarations.add(import_)
break

# handle the case when an imported symbol is an alias of another symbol
Expand All @@ -162,17 +167,22 @@ def _post_process(self, callback_params: CallbackParams):
)
processed_source_units.add(import_.imported_file)

assert referenced_declaration is not None
node_path_order = self._reference_resolver.get_node_path_order(
AstNodeId(referenced_declaration.ast_node_id),
referenced_declaration.cu_hash,
)
referenced_declaration_id = (
self._reference_resolver.get_ast_id_from_cu_node_path_order(
node_path_order, self.cu_hash
assert len(referenced_declarations) > 0

referenced_declaration_ids = set()
for referenced_declaration in referenced_declarations:
node_path_order = self._reference_resolver.get_node_path_order(
AstNodeId(referenced_declaration.ast_node_id),
referenced_declaration.cu_hash,
)
referenced_declaration_ids.add(
self._reference_resolver.get_ast_id_from_cu_node_path_order(
node_path_order, self.cu_hash
)
)
symbol_alias.foreign._referenced_declaration_ids = (
referenced_declaration_ids
)
symbol_alias.foreign._referenced_declaration_id = referenced_declaration_id

@property
def parent(self) -> SourceUnit:
Expand Down
13 changes: 10 additions & 3 deletions woke/ast/ir/statement/expression_statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from ..expression.index_range_access import IndexRangeAccess
from ..expression.literal import Literal
from ..expression.member_access import MemberAccess
from ..expression.new_expression import NewExpression
from ..expression.tuple_expression import TupleExpression
from ..expression.unary_operation import UnaryOperation

Expand All @@ -46,9 +47,9 @@ class ExpressionStatement(StatementAbc):
- a [FunctionCall][woke.ast.ir.expression.function_call.FunctionCall]:
- `:::solidity require(arr.length > 1)` in line 3,
- a [FunctionCallOptions][woke.ast.ir.expression.function_call_options.FunctionCallOptions]:
- `:::solidity payable(msg.sender).call{value: 1}` in line 16,
- `:::solidity payable(msg.sender).call{value: 1}` in line 17,
- an [Identifier][woke.ast.ir.expression.identifier.Identifier]:
- `:::solidity this` in line 15,
- `:::solidity this` in line 16,
- an [IndexAccess][woke.ast.ir.expression.index_access.IndexAccess]:
- `:::solidity arr[0]` in line 9,
- an [IndexRangeAccess][woke.ast.ir.expression.index_range_access.IndexRangeAccess]:
Expand All @@ -57,8 +58,10 @@ class ExpressionStatement(StatementAbc):
- `:::solidity 10` in line 12,
- a [MemberAccess][woke.ast.ir.expression.member_access.MemberAccess]:
- `:::solidity arr.length` in line 13,
- a [NewExpression][woke.ast.ir.expression.new_expression.NewExpression]:
- `:::solidity new uint[]` in line 14,
- a [TupleExpression][woke.ast.ir.expression.tuple_expression.TupleExpression]:
- `:::solidity (arr)` in line 14,
- `:::solidity (arr)` in line 15,
- an [UnaryOperation][woke.ast.ir.expression.unary_operation.UnaryOperation]:
- `:::solidity i++` in line 6.

Expand All @@ -76,6 +79,7 @@ class ExpressionStatement(StatementAbc):
arr[0] + arr[1];
10;
arr.length;
new uint[];
(arr);
this; // silence state mutability warning without generating bytecode
payable(msg.sender).call{value: 1};
Expand Down Expand Up @@ -105,6 +109,7 @@ class ExpressionStatement(StatementAbc):
IndexRangeAccess,
Literal,
MemberAccess,
NewExpression,
TupleExpression,
UnaryOperation,
]
Expand All @@ -130,6 +135,7 @@ def __init__(
IndexRangeAccess,
Literal,
MemberAccess,
NewExpression,
TupleExpression,
UnaryOperation,
),
Expand Down Expand Up @@ -171,6 +177,7 @@ def expression(
IndexRangeAccess,
Literal,
MemberAccess,
NewExpression,
TupleExpression,
UnaryOperation,
]:
Expand Down
Loading
Loading