Skip to content

Commit

Permalink
add print builtin (#2818)
Browse files Browse the repository at this point in the history
equivalent of nomic's console.log. it sends ABI encoded data to a
special address which is intercepted by debug tooling to generate a
print statement
  • Loading branch information
charles-cooper authored Apr 21, 2022
1 parent 37fd1e0 commit 981ec17
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 3 deletions.
8 changes: 5 additions & 3 deletions vyper/ast/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,17 +331,19 @@ def __eq__(self, other):
def __repr__(self):
cls = type(self)
class_repr = f"{cls.__module__}.{cls.__qualname__}"
return f"{class_repr}:\n{self._annotated_source}"

source_annotation = annotate_source_code(
@property
def _annotated_source(self):
# return source with context / line/col info
return annotate_source_code(
self.full_source_code,
self.lineno,
self.col_offset,
context_lines=VYPER_ERROR_CONTEXT_LINES,
line_numbers=VYPER_ERROR_LINE_NUMBERS,
)

return f"{class_repr}:\n{source_annotation}"

@property
def description(self):
"""
Expand Down
44 changes: 44 additions & 0 deletions vyper/builtin_functions/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
SArrayType,
StringType,
TupleType,
get_type_for_exact_size,
is_base_type,
is_bytes_m_type,
parse_integer_typeinfo,
Expand Down Expand Up @@ -86,9 +87,11 @@
DECIMAL_DIVISOR,
MemoryPositions,
SizeLimits,
abi_method_id,
bytes_to_int,
fourbytes_to_int,
keccak256,
vyper_warn,
)

from .signatures import Optional, validate_inputs
Expand Down Expand Up @@ -1867,6 +1870,46 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode("~empty", typ=output_type)


class Print(_SimpleBuiltinFunction):
_id = "print"
_inputs = [("arg", "*")]

_warned = False

def fetch_call_return(self, node):
if not self._warned:
vyper_warn("`print` should only be used for debugging!\n" + node._annotated_source)
self._warned = True

validate_call_args(node, 1)
return None

@validate_inputs
def build_IR(self, expr, args, kwargs, context):
args = [Expr(arg, context).ir_node for arg in expr.args]
args_tuple_t = TupleType([x.typ for x in args])
args_as_tuple = IRnode.from_list(["multi"] + [x for x in args], typ=args_tuple_t)
args_abi_t = args_tuple_t.abi_type
# create a signature like "log(uint256)"
sig = "log" + "(" + ",".join([arg.typ.abi_type.selector_name() for arg in args]) + ")"
method_id = abi_method_id(sig)

buflen = 32 + args_abi_t.size_bound()

# 32 bytes extra space for the method id
buf = context.new_internal_variable(get_type_for_exact_size(buflen))

ret = ["seq"]
ret.append(["mstore", buf, method_id])
encode = abi_encode(buf + 32, args_as_tuple, context, buflen, returns_len=True)

# debug address that tooling uses
CONSOLE_ADDRESS = 0x000000000000000000636F6E736F6C652E6C6F67
ret.append(["staticcall", "gas", CONSOLE_ADDRESS, buf + 28, encode, 0, 0])

return IRnode.from_list(ret, annotation="print:" + sig)


class ABIEncode(_SimpleBuiltinFunction):
_id = "_abi_encode" # TODO prettier to rename this to abi.encode
# signature: *, ensure_tuple=<literal_bool> -> Bytes[<calculated len>]
Expand Down Expand Up @@ -2046,6 +2089,7 @@ def build_IR(self, expr, context):

STMT_DISPATCH_TABLE = {
"send": Send(),
"print": Print(),
"selfdestruct": SelfDestruct(),
"raw_call": RawCall(),
"raw_log": RawLog(),
Expand Down
5 changes: 5 additions & 0 deletions vyper/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ def trace(n=5, out=sys.stderr):
print("END TRACE", file=out)


# print a warning
def vyper_warn(msg, prefix="Warning: ", file_=sys.stderr):
print(f"{prefix}{msg}", file=file_)


# converts a signature like Func(bool,uint256,address) to its 4 byte method ID
# TODO replace manual calculations in codebase with this
def abi_method_id(method_sig):
Expand Down

0 comments on commit 981ec17

Please sign in to comment.