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

Folding builtins #1949

Merged
merged 21 commits into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
29d7431
fix: do not require message when raising VyperInternalException
iamdefinitelyahuman May 6, 2020
8cb5d5c
feat: add validate_call_args function
iamdefinitelyahuman Apr 26, 2020
2b222b5
feat: add high-level function for folding builtin functions
iamdefinitelyahuman Apr 26, 2020
d2face5
feat: add folding logic for builtin `floor` and `ceil`
iamdefinitelyahuman Apr 26, 2020
3fbd437
test: add tests for folding builtin floor/ceil functions
iamdefinitelyahuman Apr 26, 2020
eeba22d
feat: add folding logic for builtin `len`
iamdefinitelyahuman May 4, 2020
901e57d
test: add test cases for folding `len`
iamdefinitelyahuman May 6, 2020
e2db22f
chore: add evaluate todos for builtin funcs
iamdefinitelyahuman May 4, 2020
dc83ed2
feat: add folding logic for builtin `as_wei_value`
iamdefinitelyahuman May 4, 2020
0a5fde1
test: add tests for folding builtin `as_wei_value`
iamdefinitelyahuman May 7, 2020
af80127
feat: add folding logic for `uint256_addmod` and `uint256_mulmod`
iamdefinitelyahuman May 4, 2020
f273661
test: add tests for folding addmod/mulmod builtins
iamdefinitelyahuman May 6, 2020
f29c74e
feat: add folding logic for builtin `min` and `max`
iamdefinitelyahuman May 4, 2020
bb3f689
test: add tests for folding min/max builtins
iamdefinitelyahuman May 6, 2020
0260ece
feat: add folding logic for builtin `method_id`
iamdefinitelyahuman May 4, 2020
4dec9a9
feat: add folding logic for bitwise builtins
iamdefinitelyahuman May 6, 2020
d70e334
feat: add folding logic for builtin `shift`
iamdefinitelyahuman May 6, 2020
3b61e8e
test: add test cases for folding bitwise functions
iamdefinitelyahuman May 7, 2020
9cae52b
feat: add folding logic for `sha256` and `keccak256` builtins
iamdefinitelyahuman May 6, 2020
6e7adee
test: add test cases for keccak256 and sha256 builtins
iamdefinitelyahuman May 6, 2020
82ae1af
chore: cleanup TODOs
iamdefinitelyahuman May 7, 2020
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
23 changes: 23 additions & 0 deletions tests/ast/test_folding.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,26 @@ def test_replace_userdefined_constant_no(source):
folding.replace_user_defined_constants(folded_ast)

assert vy_ast.compare_nodes(unmodified_ast, folded_ast)


builtin_folding_functions = [("ceil(4.2)", "5"), ("floor(4.2)", "4")]

builtin_folding_sources = [
"{}",
"foo = {}",
"foo = [{0}, {0}]",
"def foo(): {}",
"def foo(): return {}",
"def foo(bar: {}): pass",
]


@pytest.mark.parametrize("source", builtin_folding_sources)
@pytest.mark.parametrize("original,result", builtin_folding_functions)
def test_replace_builtins(source, original, result):
original_ast = vy_ast.parse_to_ast(source.format(original))
target_ast = vy_ast.parse_to_ast(source.format(result))

folding.replace_builtin_functions(original_ast)

assert vy_ast.compare_nodes(original_ast, target_ast)
35 changes: 35 additions & 0 deletions tests/functions/folding/test_addmod_mulmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from hypothesis import (
assume,
given,
settings,
strategies as st,
)
import pytest

from vyper import (
ast as vy_ast,
functions as vy_fn,
)

st_uint256 = st.integers(min_value=0, max_value=2 ** 256 - 1)


@pytest.mark.fuzzing
@settings(max_examples=50, deadline=1000)
@given(a=st_uint256, b=st_uint256, c=st_uint256)
@pytest.mark.parametrize('fn_name', ['uint256_addmod', 'uint256_mulmod'])
def test_modmath(get_contract, a, b, c, fn_name):
assume(c > 0)

source = f"""
@public
def foo(a: uint256, b: uint256, c: uint256) -> uint256:
return {fn_name}(a, b, c)
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({a}, {b}, {c})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)

assert contract.foo(a, b, c) == new_node.value
71 changes: 71 additions & 0 deletions tests/functions/folding/test_bitwise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from hypothesis import (
given,
settings,
strategies as st,
)
import pytest

from vyper import (
ast as vy_ast,
functions as vy_fn,
)

st_uint256 = st.integers(min_value=0, max_value=2 ** 256 - 1)


@pytest.mark.fuzzing
@settings(max_examples=50, deadline=1000)
@given(a=st_uint256, b=st_uint256)
@pytest.mark.parametrize('fn_name', ['bitwise_and', 'bitwise_or', 'bitwise_xor'])
def test_bitwise(get_contract, a, b, fn_name):

source = f"""
@public
def foo(a: uint256, b: uint256) -> uint256:
return {fn_name}(a, b)
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({a}, {b})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)

assert contract.foo(a, b) == new_node.value


@pytest.mark.fuzzing
@settings(max_examples=50, deadline=1000)
@given(value=st_uint256)
def test_bitwise_not(get_contract, value):

source = f"""
@public
def foo(a: uint256) -> uint256:
return bitwise_not(a)
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"bitwise_not({value})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.BitwiseNot().evaluate(old_node)

assert contract.foo(value) == new_node.value


@pytest.mark.fuzzing
@settings(max_examples=50, deadline=1000)
@given(value=st_uint256, steps=st.integers(min_value=-256, max_value=256))
def test_shift(get_contract, value, steps):

source = f"""
@public
def foo(a: uint256, b: int128) -> uint256:
return shift(a, b)
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"shift({value}, {steps})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.Shift().evaluate(old_node)

assert contract.foo(value, steps) == new_node.value
47 changes: 47 additions & 0 deletions tests/functions/folding/test_floor_ceil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from decimal import (
Decimal,
)

from hypothesis import (
example,
given,
settings,
strategies as st,
)
import pytest

from vyper import (
ast as vy_ast,
functions as vy_fn,
)

st_decimals = st.decimals(
min_value=-(2 ** 32),
max_value=2 ** 32,
allow_nan=False,
allow_infinity=False,
places=10,
)


@pytest.mark.fuzzing
@settings(max_examples=50, deadline=1000)
@given(value=st_decimals)
@example(value=Decimal("0.9999999999"))
@example(value=Decimal("0.0000000001"))
@example(value=Decimal("-0.9999999999"))
@example(value=Decimal("-0.0000000001"))
@pytest.mark.parametrize("fn_name", ["floor", "ceil"])
def test_floor_ceil(get_contract, value, fn_name):
source = f"""
@public
def foo(a: decimal) -> int128:
return {fn_name}(a)
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({value})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)

assert contract.foo(value) == new_node.value
60 changes: 60 additions & 0 deletions tests/functions/folding/test_fold_as_wei_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from hypothesis import (
given,
settings,
strategies as st,
)
import pytest

from vyper import (
ast as vy_ast,
functions as vy_fn,
)

denoms = [x for k in vy_fn.AsWeiValue.wei_denoms.keys() for x in k]


st_decimals = st.decimals(
min_value=0,
max_value=2 ** 32,
fubuloubu marked this conversation as resolved.
Show resolved Hide resolved
allow_nan=False,
allow_infinity=False,
places=10,
fubuloubu marked this conversation as resolved.
Show resolved Hide resolved
)


@pytest.mark.fuzzing
@settings(max_examples=10, deadline=1000)
@given(value=st_decimals)
@pytest.mark.parametrize('denom', denoms)
def test_decimal(get_contract, value, denom):
source = f"""
@public
def foo(a: decimal) -> uint256:
return as_wei_value(a, '{denom}')
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"as_wei_value({value:.10f}, '{denom}')")
fubuloubu marked this conversation as resolved.
Show resolved Hide resolved
old_node = vyper_ast.body[0].value
new_node = vy_fn.AsWeiValue().evaluate(old_node)

assert contract.foo(value) == new_node.value


@pytest.mark.fuzzing
@settings(max_examples=10, deadline=1000)
@given(value=st.integers(min_value=0, max_value=2 ** 128))
@pytest.mark.parametrize('denom', denoms)
def test_integer(get_contract, value, denom):
source = f"""
@public
def foo(a: uint256) -> uint256:
return as_wei_value(a, '{denom}')
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"as_wei_value({value}, '{denom}')")
old_node = vyper_ast.body[0].value
new_node = vy_fn.AsWeiValue().evaluate(old_node)

assert contract.foo(value) == new_node.value
73 changes: 73 additions & 0 deletions tests/functions/folding/test_keccak_sha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from hypothesis import (
given,
settings,
strategies as st,
)
import pytest

from vyper import (
ast as vy_ast,
functions as vy_fn,
)

alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&()*+,-./:;<=>?@[]^_`{|}~' # NOQA: E501


@pytest.mark.fuzzing
@given(value=st.text(alphabet=alphabet, min_size=0, max_size=100))
@settings(max_examples=50, deadline=1000)
@pytest.mark.parametrize('fn_name', ['keccak256', 'sha256'])
def test_string(get_contract, value, fn_name):

source = f"""
@public
def foo(a: string[100]) -> bytes32:
return {fn_name}(a)
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"{fn_name}('''{value}''')")
old_node = vyper_ast.body[0].value
new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)

assert contract.foo(value) == new_node.value


@pytest.mark.fuzzing
@given(value=st.binary(min_size=0, max_size=100))
@settings(max_examples=50, deadline=1000)
@pytest.mark.parametrize('fn_name', ['keccak256', 'sha256'])
def test_bytes(get_contract, value, fn_name):
source = f"""
@public
def foo(a: bytes[100]) -> bytes32:
return {fn_name}(a)
"""
contract = get_contract(source)

vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({value})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)

assert contract.foo(value) == new_node.value


@pytest.mark.fuzzing
@given(value=st.binary(min_size=1, max_size=100))
@settings(max_examples=50, deadline=1000)
@pytest.mark.parametrize('fn_name', ['keccak256', 'sha256'])
def test_hex(get_contract, value, fn_name):
source = f"""
@public
def foo(a: bytes[100]) -> bytes32:
return {fn_name}(a)
"""
contract = get_contract(source)

value = f"0x{value.hex()}"

vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({value})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)

assert contract.foo(value) == new_node.value
60 changes: 60 additions & 0 deletions tests/functions/folding/test_len.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import pytest

from vyper import (
ast as vy_ast,
functions as vy_fn,
)


@pytest.mark.parametrize("length", [0, 1, 32, 33, 64, 65, 1024])
def test_len_string(get_contract, length):
source = f"""
@public
def foo(a: string[1024]) -> int128:
return len(a)
"""
contract = get_contract(source)

value = "a" * length

vyper_ast = vy_ast.parse_to_ast(f"len('{value}')")
old_node = vyper_ast.body[0].value
new_node = vy_fn.Len().evaluate(old_node)

assert contract.foo(value) == new_node.value


@pytest.mark.parametrize("length", [0, 1, 32, 33, 64, 65, 1024])
def test_len_bytes(get_contract, length):
source = f"""
@public
def foo(a: bytes[1024]) -> int128:
return len(a)
"""
contract = get_contract(source)

value = "a" * length

vyper_ast = vy_ast.parse_to_ast(f"len(b'{value}')")
old_node = vyper_ast.body[0].value
new_node = vy_fn.Len().evaluate(old_node)

assert contract.foo(value.encode()) == new_node.value


@pytest.mark.parametrize("length", [1, 32, 33, 64, 65, 1024])
def test_len_hex(get_contract, length):
source = f"""
@public
def foo(a: bytes[1024]) -> int128:
return len(a)
"""
contract = get_contract(source)

value = f"0x{'00' * length}"

vyper_ast = vy_ast.parse_to_ast(f"len({value})")
old_node = vyper_ast.body[0].value
new_node = vy_fn.Len().evaluate(old_node)

assert contract.foo(value) == new_node.value
Loading