Skip to content

Commit

Permalink
Merge branch 'master' into fix/bytestring_compare_type
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper committed Dec 7, 2023
2 parents 8b537c4 + cbac5ab commit 27490e3
Show file tree
Hide file tree
Showing 41 changed files with 3,147 additions and 283 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ make dev-init
python setup.py test
```

## Developing (working on the compiler)

A useful script to have in your PATH is something like the following:
```bash
$ cat ~/.local/bin/vyc
#!/usr/bin/env bash
PYTHONPATH=. python vyper/cli/vyper_compile.py "$@"
```

To run a python performance profile (to find compiler perf hotspots):
```bash
PYTHONPATH=. python -m cProfile -s tottime vyper/cli/vyper_compile.py "$@"
```

To get a call graph from a python profile, https://stackoverflow.com/a/23164271/ is helpful.


# Contributing
* See Issues tab, and feel free to submit your own issues
* Add PRs if you discover a solution to an existing issue
Expand Down
2 changes: 1 addition & 1 deletion docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,4 @@ If you are making a larger change, please consult first with the `Vyper (Smart C

Although we do CI testing, please make sure that the tests pass for supported Python version and ensure that it builds locally before submitting a pull request.

Thank you for your help! ​
Thank you for your help!
6 changes: 5 additions & 1 deletion examples/crowdfund.vy
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Setup private variables (only callable from within the contract)
###########################################################################
## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
###########################################################################

# example of a crowd funding contract

funders: HashMap[address, uint256]
beneficiary: address
Expand Down
2 changes: 1 addition & 1 deletion examples/market_maker/on_chain_market_maker.vy
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ invariant: public(uint256)
token_address: ERC20
owner: public(address)

# Sets the on chain market maker with its owner, intial token quantity,
# Sets the on chain market maker with its owner, initial token quantity,
# and initial ether quantity
@external
@payable
Expand Down
7 changes: 6 additions & 1 deletion examples/tokens/ERC1155ownable.vy
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
###########################################################################
## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
###########################################################################

# @version >=0.3.4
"""
@dev Implementation of ERC-1155 non-fungible token standard ownable, with approval, OPENSEA compatible (name, symbol)
@dev example implementation of ERC-1155 non-fungible token standard ownable, with approval, OPENSEA compatible (name, symbol)
@author Dr. Pixel (github: @Doc-Pixel)
"""

############### imports ###############
from vyper.interfaces import ERC165

Expand Down
6 changes: 5 additions & 1 deletion examples/tokens/ERC20.vy
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# @dev Implementation of ERC-20 token standard.
###########################################################################
## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
###########################################################################

# @dev example implementation of an ERC20 token
# @author Takayuki Jimba (@yudetamago)
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md

Expand Down
7 changes: 7 additions & 0 deletions examples/tokens/ERC4626.vy
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# NOTE: Copied from https://github.com/fubuloubu/ERC4626/blob/1a10b051928b11eeaad15d80397ed36603c2a49b/contracts/VyperVault.vy

# example implementation of an ERC4626 vault

###########################################################################
## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
###########################################################################

from vyper.interfaces import ERC20
from vyper.interfaces import ERC4626

Expand Down
6 changes: 5 additions & 1 deletion examples/tokens/ERC721.vy
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# @dev Implementation of ERC-721 non-fungible token standard.
###########################################################################
## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
###########################################################################

# @dev example implementation of ERC-721 non-fungible token standard.
# @author Ryuya Nakamura (@nrryuya)
# Modified from: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy

Expand Down
7 changes: 5 additions & 2 deletions examples/wallet/wallet.vy
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# An example of how you can do a wallet in Vyper.
# Warning: NOT AUDITED. Do not use to store substantial quantities of funds.
###########################################################################
## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
###########################################################################

# An example of how you can implement a wallet in Vyper.

# A list of the owners addresses (there are a maximum of 5 owners)
owners: public(address[5])
Expand Down
28 changes: 28 additions & 0 deletions tests/compiler/venom/test_duplicate_operands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from vyper.compiler.settings import OptimizationLevel
from vyper.venom import generate_assembly_experimental
from vyper.venom.basicblock import IRLiteral
from vyper.venom.function import IRFunction


def test_duplicate_operands():
"""
Test the duplicate operands code generation.
The venom code:
%1 = 10
%2 = add %1, %1
%3 = mul %1, %2
stop
Should compile to: [PUSH1, 10, DUP1, DUP1, DUP1, ADD, MUL, STOP]
"""
ctx = IRFunction()

op = ctx.append_instruction("store", [IRLiteral(10)])
sum = ctx.append_instruction("add", [op, op])
ctx.append_instruction("mul", [sum, op])
ctx.append_instruction("stop", [], False)

asm = generate_assembly_experimental(ctx, OptimizationLevel.CODESIZE)

assert asm == ["PUSH1", 10, "DUP1", "DUP1", "DUP1", "ADD", "MUL", "STOP", "REVERT"]
96 changes: 96 additions & 0 deletions tests/compiler/venom/test_multi_entry_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from vyper.venom.analysis import calculate_cfg
from vyper.venom.basicblock import IRLiteral
from vyper.venom.function import IRBasicBlock, IRFunction, IRLabel
from vyper.venom.passes.normalization import NormalizationPass


def test_multi_entry_block_1():
ctx = IRFunction()

finish_label = IRLabel("finish")
target_label = IRLabel("target")
block_1_label = IRLabel("block_1", ctx)

op = ctx.append_instruction("store", [IRLiteral(10)])
acc = ctx.append_instruction("add", [op, op])
ctx.append_instruction("jnz", [acc, finish_label, block_1_label], False)

block_1 = IRBasicBlock(block_1_label, ctx)
ctx.append_basic_block(block_1)
acc = ctx.append_instruction("add", [acc, op])
op = ctx.append_instruction("store", [IRLiteral(10)])
ctx.append_instruction("mstore", [acc, op], False)
ctx.append_instruction("jnz", [acc, finish_label, target_label], False)

target_bb = IRBasicBlock(target_label, ctx)
ctx.append_basic_block(target_bb)
ctx.append_instruction("mul", [acc, acc])
ctx.append_instruction("jmp", [finish_label], False)

finish_bb = IRBasicBlock(finish_label, ctx)
ctx.append_basic_block(finish_bb)
ctx.append_instruction("stop", [], False)

calculate_cfg(ctx)
assert not ctx.normalized, "CFG should not be normalized"

NormalizationPass.run_pass(ctx)

assert ctx.normalized, "CFG should be normalized"

finish_bb = ctx.get_basic_block(finish_label.value)
cfg_in = list(finish_bb.cfg_in.keys())
assert cfg_in[0].label.value == "target", "Should contain target"
assert cfg_in[1].label.value == "finish_split_global", "Should contain finish_split_global"
assert cfg_in[2].label.value == "finish_split_block_1", "Should contain finish_split_block_1"


# more complicated one
def test_multi_entry_block_2():
ctx = IRFunction()

finish_label = IRLabel("finish")
target_label = IRLabel("target")
block_1_label = IRLabel("block_1", ctx)
block_2_label = IRLabel("block_2", ctx)

op = ctx.append_instruction("store", [IRLiteral(10)])
acc = ctx.append_instruction("add", [op, op])
ctx.append_instruction("jnz", [acc, finish_label, block_1_label], False)

block_1 = IRBasicBlock(block_1_label, ctx)
ctx.append_basic_block(block_1)
acc = ctx.append_instruction("add", [acc, op])
op = ctx.append_instruction("store", [IRLiteral(10)])
ctx.append_instruction("mstore", [acc, op], False)
ctx.append_instruction("jnz", [acc, target_label, finish_label], False)

block_2 = IRBasicBlock(block_2_label, ctx)
ctx.append_basic_block(block_2)
acc = ctx.append_instruction("add", [acc, op])
op = ctx.append_instruction("store", [IRLiteral(10)])
ctx.append_instruction("mstore", [acc, op], False)
# switch the order of the labels, for fun
ctx.append_instruction("jnz", [acc, finish_label, target_label], False)

target_bb = IRBasicBlock(target_label, ctx)
ctx.append_basic_block(target_bb)
ctx.append_instruction("mul", [acc, acc])
ctx.append_instruction("jmp", [finish_label], False)

finish_bb = IRBasicBlock(finish_label, ctx)
ctx.append_basic_block(finish_bb)
ctx.append_instruction("stop", [], False)

calculate_cfg(ctx)
assert not ctx.normalized, "CFG should not be normalized"

NormalizationPass.run_pass(ctx)

assert ctx.normalized, "CFG should be normalized"

finish_bb = ctx.get_basic_block(finish_label.value)
cfg_in = list(finish_bb.cfg_in.keys())
assert cfg_in[0].label.value == "target", "Should contain target"
assert cfg_in[1].label.value == "finish_split_global", "Should contain finish_split_global"
assert cfg_in[2].label.value == "finish_split_block_1", "Should contain finish_split_block_1"
5 changes: 5 additions & 0 deletions tests/compiler/venom/test_stack_at_external_return.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def test_stack_at_external_return():
"""
TODO: USE BOA DO GENERATE THIS TEST
"""
pass
8 changes: 3 additions & 5 deletions vyper/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@
# -*- coding: UTF-8 -*-
import sys

from vyper.cli import vyper_compile, vyper_ir, vyper_serve
from vyper.cli import vyper_compile, vyper_ir

if __name__ == "__main__":
allowed_subcommands = ("--vyper-compile", "--vyper-ir", "--vyper-serve")
allowed_subcommands = ("--vyper-compile", "--vyper-ir")

if len(sys.argv) <= 1 or sys.argv[1] not in allowed_subcommands:
# default (no args, no switch in first arg): run vyper_compile
vyper_compile._parse_cli_args()
else:
# pop switch and forward args to subcommand
subcommand = sys.argv.pop(1)
if subcommand == "--vyper-serve":
vyper_serve._parse_cli_args()
elif subcommand == "--vyper-ir":
if subcommand == "--vyper-ir":
vyper_ir._parse_cli_args()
else:
vyper_compile._parse_cli_args()
25 changes: 13 additions & 12 deletions vyper/builtins/_signatures.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import functools
from typing import Dict
from typing import Any, Optional

from vyper.ast import nodes as vy_ast
from vyper.ast.validation import validate_call_args
Expand Down Expand Up @@ -74,12 +74,14 @@ def decorator_fn(self, node, context):
return decorator_fn


class BuiltinFunction(VyperType):
class BuiltinFunctionT(VyperType):
_has_varargs = False
_kwargs: Dict[str, KwargSettings] = {}
_inputs: list[tuple[str, Any]] = []
_kwargs: dict[str, KwargSettings] = {}
_return_type: Optional[VyperType] = None

# helper function to deal with TYPE_DEFINITIONs
def _validate_single(self, arg, expected_type):
def _validate_single(self, arg: vy_ast.VyperNode, expected_type: VyperType) -> None:
# TODO using "TYPE_DEFINITION" is a kludge in derived classes,
# refactor me.
if expected_type == "TYPE_DEFINITION":
Expand All @@ -89,15 +91,15 @@ def _validate_single(self, arg, expected_type):
else:
validate_expected_type(arg, expected_type)

def _validate_arg_types(self, node):
def _validate_arg_types(self, node: vy_ast.Call) -> None:
num_args = len(self._inputs) # the number of args the signature indicates

expect_num_args = num_args
expect_num_args: Any = num_args
if self._has_varargs:
# note special meaning for -1 in validate_call_args API
expect_num_args = (num_args, -1)

validate_call_args(node, expect_num_args, self._kwargs)
validate_call_args(node, expect_num_args, list(self._kwargs.keys()))

for arg, (_, expected) in zip(node.args, self._inputs):
self._validate_single(arg, expected)
Expand All @@ -118,13 +120,12 @@ def _validate_arg_types(self, node):
# ensures the type can be inferred exactly.
get_exact_type_from_node(arg)

def fetch_call_return(self, node):
def fetch_call_return(self, node: vy_ast.Call) -> Optional[VyperType]:
self._validate_arg_types(node)

if self._return_type:
return self._return_type
return self._return_type

def infer_arg_types(self, node):
def infer_arg_types(self, node: vy_ast.Call) -> list[VyperType]:
self._validate_arg_types(node)
ret = [expected for (_, expected) in self._inputs]

Expand All @@ -136,7 +137,7 @@ def infer_arg_types(self, node):
ret.extend(get_exact_type_from_node(arg) for arg in varargs)
return ret

def infer_kwarg_types(self, node):
def infer_kwarg_types(self, node: vy_ast.Call) -> dict[str, VyperType]:
return {i.arg: self._kwargs[i.arg].typ for i in node.keywords}

def __repr__(self):
Expand Down
Loading

0 comments on commit 27490e3

Please sign in to comment.