diff --git a/.travis.yml b/.travis.yml index 09f6974..e42b725 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ script: coverage run -a --source=hebi -m hebi compile_pluto examples/smart_contracts/assert_sum.py - > coverage run -a --source=hebi -m hebi build examples/smart_contracts/assert_sum.py +# all smart contracts - > for i in $(find examples -type f -name "*.py" -not \( -name "broken*" -o -name "extract*" -o -name "__*" \)); do echo "$i" diff --git a/docs/hebi/compiler.html b/docs/hebi/compiler.html index f7687a1..3621597 100644 --- a/docs/hebi/compiler.html +++ b/docs/hebi/compiler.html @@ -74,6 +74,7 @@

Module hebi.compiler

from .optimize.optimize_remove_comments import OptimizeRemoveDeadconstants from .rewrite.rewrite_forbidden_overwrites import RewriteForbiddenOverwrites +from .rewrite.rewrite_guaranteed_variables import RewriteGuaranteedVariables from .rewrite.rewrite_import import RewriteImport from .rewrite.rewrite_import_dataclasses import RewriteImportDataclasses from .rewrite.rewrite_import_hashlib import RewriteImportHashlib @@ -774,6 +775,7 @@

Module hebi.compiler

RewriteImportDataclasses(), RewriteInjectBuiltins(), RewriteDuplicateAssignment(), + RewriteGuaranteedVariables(), # The type inference needs to be run after complex python operations were rewritten AggressiveTypeInferencer(), # Rewrites that circumvent the type inference or use its results @@ -838,6 +840,7 @@

Functions

RewriteImportDataclasses(), RewriteInjectBuiltins(), RewriteDuplicateAssignment(), + RewriteGuaranteedVariables(), # The type inference needs to be run after complex python operations were rewritten AggressiveTypeInferencer(), # Rewrites that circumvent the type inference or use its results diff --git a/docs/hebi/index.html b/docs/hebi/index.html index babdde1..0f89050 100644 --- a/docs/hebi/index.html +++ b/docs/hebi/index.html @@ -241,7 +241,7 @@

Supporters

import warnings -__version__ = "0.1.1.0.12.4" +__version__ = "0.1.1.0.12.5" __author__ = "nielstron" __author_email__ = "n.muendler@web.de" __copyright__ = "Copyright (C) 2023 nielstron" diff --git a/docs/hebi/optimize/optimize_remove_deadvars.html b/docs/hebi/optimize/optimize_remove_deadvars.html index 41dbcea..e793bbf 100644 --- a/docs/hebi/optimize/optimize_remove_deadvars.html +++ b/docs/hebi/optimize/optimize_remove_deadvars.html @@ -74,6 +74,7 @@

Module hebi.optimize.optimize_remove_deadvars

Module hebi.optimize.optimize_remove_deadvarsModule hebi.optimize.optimize_remove_deadvarsMethods loaded_vars = None # names that are guaranteed to be available to the current node # this acts differently to the type inferencer! in particular, ite/while/for all produce their own scope - guaranteed_avail_names = [list(INITIAL_SCOPE.keys())] + guaranteed_avail_names = [ + list(INITIAL_SCOPE.keys()) + ["isinstance", "Union", "Dict", "List"] + ] def guaranteed(self, name: str) -> bool: name = name @@ -512,13 +519,15 @@

Methods

return self.generic_visit(node) return Pass() - def visit_AnnAssign(self, node: AnnAssign): + def visit_AnnAssign(self, node: TypedAnnAssign): if ( not isinstance(node.target, Name) or node.target.id in self.loaded_vars or not SafeOperationVisitor(sum(self.guaranteed_avail_names, [])).visit( node.value ) + # only upcasts are safe! + or not node.target.typ >= node.value.typ ): assert isinstance( node.target, Name @@ -637,7 +646,7 @@

Methods

Visit a node.

-def visit_AnnAssign(self, node: _ast.AnnAssign) +def visit_AnnAssign(self, node: TypedAnnAssign)
@@ -645,13 +654,15 @@

Methods

Expand source code -
def visit_AnnAssign(self, node: AnnAssign):
+
def visit_AnnAssign(self, node: TypedAnnAssign):
     if (
         not isinstance(node.target, Name)
         or node.target.id in self.loaded_vars
         or not SafeOperationVisitor(sum(self.guaranteed_avail_names, [])).visit(
             node.value
         )
+        # only upcasts are safe!
+        or not node.target.typ >= node.value.typ
     ):
         assert isinstance(
             node.target, Name
diff --git a/docs/hebi/prelude.html b/docs/hebi/prelude.html
index 3f6dd5e..e0e739b 100644
--- a/docs/hebi/prelude.html
+++ b/docs/hebi/prelude.html
@@ -139,13 +139,12 @@ 

Module hebi.prelude

""" attached_datum = txout.datum if isinstance(attached_datum, SomeOutputDatumHash): - res = tx_info.data[attached_datum.datum_hash] + return tx_info.data[attached_datum.datum_hash] elif isinstance(attached_datum, SomeOutputDatum): - res = attached_datum.datum + return attached_datum.datum else: # no datum attached assert False, "No datum was attached to the given transaction output" - return res def resolve_datum( @@ -285,13 +284,12 @@

Functions

""" attached_datum = txout.datum if isinstance(attached_datum, SomeOutputDatumHash): - res = tx_info.data[attached_datum.datum_hash] + return tx_info.data[attached_datum.datum_hash] elif isinstance(attached_datum, SomeOutputDatum): - res = attached_datum.datum + return attached_datum.datum else: # no datum attached - assert False, "No datum was attached to the given transaction output" - return res
+ assert False, "No datum was attached to the given transaction output"
diff --git a/docs/hebi/rewrite/index.html b/docs/hebi/rewrite/index.html index dbdb1fc..e66a2eb 100644 --- a/docs/hebi/rewrite/index.html +++ b/docs/hebi/rewrite/index.html @@ -76,6 +76,10 @@

Sub-modules

+
hebi.rewrite.rewrite_guaranteed_variables
+
+
+
hebi.rewrite.rewrite_import
@@ -197,6 +201,7 @@

Index

+
+class RewriteLocation +(orig_node) +
+
+

A :class:NodeVisitor subclass that walks the abstract syntax tree and +allows modification of nodes.

+

The NodeTransformer will walk the AST and use the return value of the +visitor methods to replace or remove the old node. +If the return value of +the visitor method is None, the node will be removed from its location, +otherwise it is replaced with the return value. +The return value may be the +original node in which case no replacement takes place.

+

Here is an example transformer that rewrites all occurrences of name lookups +(foo) to data['foo']::

+

class RewriteName(NodeTransformer):

+
   def visit_Name(self, node):
+       return Subscript(
+           value=Name(id='data', ctx=Load()),
+           slice=Index(value=Str(s=node.id)),
+           ctx=node.ctx
+       )
+
+

Keep in mind that if the node you're operating on has child nodes you must +either transform the child nodes yourself or call the :meth:generic_visit +method for the node first.

+

For nodes that were part of a collection of statements (that applies to all +statement nodes), the visitor may also return a list of nodes rather than +just a single node.

+

Usually you use the transformer like this::

+

node = YourTransformer().visit(node)

+
+ +Expand source code + +
class RewriteLocation(CompilingNodeTransformer):
+    def __init__(self, orig_node):
+        self.orig_node = orig_node
+
+    def visit(self, node):
+        node = ast.copy_location(node, self.orig_node)
+        return super().visit(node)
+
+

Ancestors

+ +

Methods

+
+
+def visit(self, node) +
+
+

+Inherited from: +CompilingNodeTransformer.visit +

+

Visit a node.

+
+ +Expand source code + +
def visit(self, node):
+    node = ast.copy_location(node, self.orig_node)
+    return super().visit(node)
+
+
+
+
@@ -426,6 +522,12 @@

visit_ImportFrom +
  • +

    RewriteLocation

    + +
  • diff --git a/docs/hebi/tests/test_misc.html b/docs/hebi/tests/test_misc.html index 068c4c6..26ca5ae 100644 --- a/docs/hebi/tests/test_misc.html +++ b/docs/hebi/tests/test_misc.html @@ -468,33 +468,6 @@

    Module hebi.tests.test_misc

    failed = True self.assertTrue(failed, "Machine did validate the content") - def test_opt_shared_var(self): - # this tests that errors that are caused by assignments are actually triggered at the time of assigning - source_code = """ -from hebi.prelude import * -def validator(x: Token) -> bool: - if False: - y = x - else: - a = y - return True - """ - ast = compiler.parse(source_code) - code = compiler.compile(ast) - code = code.compile() - f = code.term - # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying - try: - for d in [ - uplc.PlutusConstr(0, []), - ]: - f = uplc.Apply(f, d) - ret = uplc_eval(f) - failed = False - except Exception as e: - failed = True - self.assertTrue(failed, "Machine did validate the content") - def test_list_expr(self): # this tests that the list expression is evaluated correctly source_code = """ @@ -924,7 +897,32 @@

    Module hebi.tests.test_misc

    ast = compiler.parse(source_code) code = compiler.compile(ast).compile() res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, []))) - self.assertEqual(res, uplc.PlutusConstr(0, [])) + self.assertEqual(res, uplc.PlutusConstr(0, [])) + + def test_opt_unsafe_cast(self): + # test that unsafe casts are not optimized away + source_code = """ +from opshin.prelude import * +def validator(x: Token) -> bool: + b: Anything = x + a: int = b + return True + """ + ast = compiler.parse(source_code) + code = compiler.compile(ast) + code = code.compile() + f = code.term + # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying + try: + for d in [ + uplc.PlutusConstr(0, []), + ]: + f = uplc.Apply(f, d) + ret = uplc_eval(f) + failed = False + except Exception as e: + failed = True + self.assertTrue(failed, "Machine did validate the content")
    @@ -1373,33 +1371,6 @@

    Classes

    failed = True self.assertTrue(failed, "Machine did validate the content") - def test_opt_shared_var(self): - # this tests that errors that are caused by assignments are actually triggered at the time of assigning - source_code = """ -from hebi.prelude import * -def validator(x: Token) -> bool: - if False: - y = x - else: - a = y - return True - """ - ast = compiler.parse(source_code) - code = compiler.compile(ast) - code = code.compile() - f = code.term - # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying - try: - for d in [ - uplc.PlutusConstr(0, []), - ]: - f = uplc.Apply(f, d) - ret = uplc_eval(f) - failed = False - except Exception as e: - failed = True - self.assertTrue(failed, "Machine did validate the content") - def test_list_expr(self): # this tests that the list expression is evaluated correctly source_code = """ @@ -1829,7 +1800,32 @@

    Classes

    ast = compiler.parse(source_code) code = compiler.compile(ast).compile() res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, []))) - self.assertEqual(res, uplc.PlutusConstr(0, [])) + self.assertEqual(res, uplc.PlutusConstr(0, [])) + + def test_opt_unsafe_cast(self): + # test that unsafe casts are not optimized away + source_code = """ +from opshin.prelude import * +def validator(x: Token) -> bool: + b: Anything = x + a: int = b + return True + """ + ast = compiler.parse(source_code) + code = compiler.compile(ast) + code = code.compile() + f = code.term + # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying + try: + for d in [ + uplc.PlutusConstr(0, []), + ]: + f = uplc.Apply(f, d) + ret = uplc_eval(f) + failed = False + except Exception as e: + failed = True + self.assertTrue(failed, "Machine did validate the content")

    Ancestors