github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/equity/compiler/checks.go (about)

     1  package compiler
     2  
     3  import "fmt"
     4  
     5  func checkRecursive(contract *Contract) bool {
     6  	for _, clause := range contract.Clauses {
     7  		for _, stmt := range clause.statements {
     8  			if result := checkStatRecursive(stmt, contract.Name); result {
     9  				return true
    10  			}
    11  		}
    12  	}
    13  	return false
    14  }
    15  
    16  func checkStatRecursive(stmt statement, contractName string) bool {
    17  	switch s := stmt.(type) {
    18  	case *ifStatement:
    19  		for _, trueStmt := range s.body.trueBody {
    20  			if result := checkStatRecursive(trueStmt, contractName); result {
    21  				return true
    22  			}
    23  		}
    24  
    25  		for _, falseStmt := range s.body.falseBody {
    26  			if result := checkStatRecursive(falseStmt, contractName); result {
    27  				return true
    28  			}
    29  		}
    30  
    31  	case *lockStatement:
    32  		if c, ok := s.program.(*callExpr); ok {
    33  			if references(c.fn, contractName) {
    34  				return true
    35  			}
    36  		}
    37  	}
    38  
    39  	return false
    40  }
    41  
    42  func prohibitSigParams(contract *Contract) error {
    43  	for _, p := range contract.Params {
    44  		if p.Type == sigType {
    45  			return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name)
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error {
    52  	for _, p := range params {
    53  		used := false
    54  		for _, c := range clauses {
    55  			err := requireAllParamsUsedInClause([]*Param{p}, c)
    56  			if err == nil {
    57  				used = true
    58  				break
    59  			}
    60  		}
    61  
    62  		if !used {
    63  			return fmt.Errorf("parameter \"%s\" is unused", p.Name)
    64  		}
    65  	}
    66  	return nil
    67  }
    68  
    69  func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
    70  	for _, p := range params {
    71  		used := false
    72  		for _, stmt := range clause.statements {
    73  			if used = checkParamUsedInStatement(p, stmt); used {
    74  				break
    75  			}
    76  		}
    77  
    78  		if !used {
    79  			return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  func checkParamUsedInStatement(param *Param, stmt statement) (used bool) {
    86  	switch s := stmt.(type) {
    87  	case *ifStatement:
    88  		if used = references(s.condition, param.Name); used {
    89  			return used
    90  		}
    91  
    92  		for _, st := range s.body.trueBody {
    93  			if used = checkParamUsedInStatement(param, st); used {
    94  				break
    95  			}
    96  		}
    97  
    98  		if !used {
    99  			for _, st := range s.body.falseBody {
   100  				if used = checkParamUsedInStatement(param, st); used {
   101  					break
   102  				}
   103  			}
   104  		}
   105  
   106  	case *defineStatement:
   107  		used = references(s.expr, param.Name)
   108  	case *assignStatement:
   109  		used = references(s.expr, param.Name)
   110  	case *verifyStatement:
   111  		used = references(s.expr, param.Name)
   112  	case *lockStatement:
   113  		used = references(s.lockedAmount, param.Name) || references(s.lockedAsset, param.Name) || references(s.program, param.Name)
   114  	case *unlockStatement:
   115  		used = references(s.unlockedAmount, param.Name) || references(s.unlockedAsset, param.Name)
   116  	}
   117  
   118  	return used
   119  }
   120  
   121  func references(expr expression, name string) bool {
   122  	switch e := expr.(type) {
   123  	case *binaryExpr:
   124  		return references(e.left, name) || references(e.right, name)
   125  	case *unaryExpr:
   126  		return references(e.expr, name)
   127  	case *callExpr:
   128  		if references(e.fn, name) {
   129  			return true
   130  		}
   131  		for _, a := range e.args {
   132  			if references(a, name) {
   133  				return true
   134  			}
   135  		}
   136  		return false
   137  	case varRef:
   138  		return string(e) == name
   139  	case listExpr:
   140  		for _, elt := range []expression(e) {
   141  			if references(elt, name) {
   142  				return true
   143  			}
   144  		}
   145  		return false
   146  	}
   147  	return false
   148  }
   149  
   150  func referencedBuiltin(expr expression) *builtin {
   151  	if v, ok := expr.(varRef); ok {
   152  		for _, b := range builtins {
   153  			if string(v) == b.name {
   154  				return &b
   155  			}
   156  		}
   157  	}
   158  	return nil
   159  }
   160  
   161  func assignIndexes(clause *Clause) error {
   162  	var nextIndex int64
   163  	for i, stmt := range clause.statements {
   164  		if nextIndex = assignStatIndexes(stmt, nextIndex, i != len(clause.statements)-1); nextIndex < 0 {
   165  			return fmt.Errorf("Not support that the number of lock/unlock statement is not equal between ifbody and elsebody when the if-else is not the last statement in clause \"%s\"", clause.Name)
   166  		}
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func assignStatIndexes(stat statement, nextIndex int64, nonFinalFlag bool) int64 {
   173  	switch stmt := stat.(type) {
   174  	case *ifStatement:
   175  		trueIndex := nextIndex
   176  		falseIndex := nextIndex
   177  		for _, trueStmt := range stmt.body.trueBody {
   178  			trueIndex = assignStatIndexes(trueStmt, trueIndex, nonFinalFlag)
   179  		}
   180  
   181  		for _, falseStmt := range stmt.body.falseBody {
   182  			falseIndex = assignStatIndexes(falseStmt, falseIndex, nonFinalFlag)
   183  		}
   184  
   185  		if trueIndex != falseIndex && nonFinalFlag {
   186  			return -1
   187  		} else if trueIndex == falseIndex {
   188  			nextIndex = trueIndex
   189  		}
   190  
   191  	case *lockStatement:
   192  		stmt.index = nextIndex
   193  		nextIndex++
   194  
   195  	case *unlockStatement:
   196  		nextIndex++
   197  	}
   198  
   199  	return nextIndex
   200  }
   201  
   202  func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
   203  	for _, s := range clause.statements {
   204  		if err := typeCheckStatement(s, contract.Value, clause.Name, env); err != nil {
   205  			return err
   206  		}
   207  	}
   208  	return nil
   209  }
   210  
   211  func typeCheckStatement(stat statement, contractValue ValueInfo, clauseName string, env *environ) error {
   212  	switch stmt := stat.(type) {
   213  	case *ifStatement:
   214  		for _, trueStmt := range stmt.body.trueBody {
   215  			if err := typeCheckStatement(trueStmt, contractValue, clauseName, env); err != nil {
   216  				return err
   217  			}
   218  		}
   219  
   220  		for _, falseStmt := range stmt.body.falseBody {
   221  			if err := typeCheckStatement(falseStmt, contractValue, clauseName, env); err != nil {
   222  				return err
   223  			}
   224  		}
   225  
   226  	case *defineStatement:
   227  		if stmt.expr != nil && stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
   228  			return fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
   229  				clauseName, stmt.expr.typ(env), stmt.variable.Type)
   230  		}
   231  
   232  	case *assignStatement:
   233  		if stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
   234  			return fmt.Errorf("expression in assign statement in clause \"%s\" has type \"%s\", must be \"%s\"",
   235  				clauseName, stmt.expr.typ(env), stmt.variable.Type)
   236  		}
   237  
   238  	case *verifyStatement:
   239  		if t := stmt.expr.typ(env); t != boolType {
   240  			return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clauseName, t)
   241  		}
   242  
   243  	case *lockStatement:
   244  		if t := stmt.lockedAmount.typ(env); !(t == intType || t == amountType) {
   245  			return fmt.Errorf("lockedAmount expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Integer", stmt.lockedAmount, clauseName, t)
   246  		}
   247  		if t := stmt.lockedAsset.typ(env); t != assetType {
   248  			return fmt.Errorf("lockedAsset expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Asset", stmt.lockedAsset, clauseName, t)
   249  		}
   250  		if t := stmt.program.typ(env); t != progType {
   251  			return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clauseName, t)
   252  		}
   253  
   254  	case *unlockStatement:
   255  		if t := stmt.unlockedAmount.typ(env); !(t == intType || t == amountType) {
   256  			return fmt.Errorf("unlockedAmount expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Integer", stmt.unlockedAmount, clauseName, t)
   257  		}
   258  		if t := stmt.unlockedAsset.typ(env); t != assetType {
   259  			return fmt.Errorf("unlockedAsset expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Asset", stmt.unlockedAsset, clauseName, t)
   260  		}
   261  		if stmt.unlockedAmount.String() != contractValue.Amount || stmt.unlockedAsset.String() != contractValue.Asset {
   262  			return fmt.Errorf("amount \"%s\" of asset \"%s\" expression in unlock statement of clause \"%s\" must be the contract valueAmount \"%s\" of valueAsset \"%s\"",
   263  				stmt.unlockedAmount.String(), stmt.unlockedAsset.String(), clauseName, contractValue.Amount, contractValue.Asset)
   264  		}
   265  	}
   266  
   267  	return nil
   268  }