sbuilder-eth 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.org +19 -0
- data/VERSION +1 -0
- data/bin/state-explorer.sh +215 -0
- data/lib/eth/al_api.rb +12 -0
- data/lib/eth/ast_sexp.rb +519 -0
- data/lib/eth/constants.rb +354 -0
- data/lib/eth/ethereum.rb +2054 -0
- data/lib/eth/ethereum_expression.rb +476 -0
- data/lib/eth/exception.rb +9 -0
- data/lib/eth/global_scope.rb +100 -0
- data/lib/eth/module.rb +18 -0
- data/lib/eth/sexp_processor.rb +66 -0
- data/lib/eth/sexp_processor_call_separator.rb +163 -0
- data/lib/eth/sexp_processor_getter.rb +125 -0
- data/lib/eth/sexp_processor_resolve.rb +122 -0
- data/lib/eth/sexp_processor_scope.rb +355 -0
- data/lib/eth/sexp_utils.rb +214 -0
- data/lib/eth/solidity_compiler.rb +145 -0
- data/lib/eth/solidity_loader.rb +53 -0
- data/lib/eth/solidity_translator.rb +840 -0
- data/lib/mixer/reference.rb +30 -0
- data/lib/mixer/scope.rb +100 -0
- data/lib/mixer/scoped.rb +18 -0
- data/lib/plugin/controller.rb +267 -0
- data/lib/plugin/module.rb +3 -0
- data/lib/plugin/plugin.rb +33 -0
- data/lib/sbuilder-eth.rb +6 -0
- data/sbuilder-eth.gemspec +38 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b62782275cc9f06d62897fda5dd8066bbd13f30a
|
4
|
+
data.tar.gz: ba81ab05d6ff401f4502c7fc6083f534030c45ec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e440647e8d019f30fcea31dab54eb23ae4fc7db7fe02ded360eac0a5bbf2d09ab28f870a2e41ca82b3a97ea58427e8191b0e9463df4daecccc47e96a0c0d3aea
|
7
|
+
data.tar.gz: 1dafec9250e8e950cc1053a8745829cc43f311ed45a09adf56cd03454bb26a40bde9022a4170c595bad9e8783f9ae21606224cd5e7c9536dfe6aa23f79137cf2
|
data/README.org
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- mode: flyspell; mode: org- *-
|
2
|
+
|
3
|
+
* sbuilder-eth - Ethereum Plugin for =tla-sbuilder=
|
4
|
+
|
5
|
+
[[https://github.com/jarjuk/tla-sbuilder][tla-sbuilder]] is tool to generate runnable formal models in [[http://research.microsoft.com/en-us/um/people/lamport/tla/book.html][TLA+
|
6
|
+
language]] for [[https://github.com/jarjuk/tla-sbuilder#TARGET-SYSTEM][business IT systems]]. A formal model can be [[https://en.wikipedia.org/wiki/Model_checking][model
|
7
|
+
checked]] using [[http://research.microsoft.com/en-us/um/people/lamport/tla/tools.html][TLA+ Tools]].
|
8
|
+
|
9
|
+
[[https://github.com/jarjuk/sbuilder-eth][sbuillder-eth]] loads Ethreum Solidity implementations into
|
10
|
+
=tla-sbuilder= context using API Language (AL) provided by
|
11
|
+
[[https://github.com/jarjuk/sbuilder-al][sbuillder-al]] GEM.
|
12
|
+
|
13
|
+
*Notice* replaces a previous plugin [[https://github.com/jarjuk/sbuilder-ethereum][sbuilder-ethereum]].
|
14
|
+
|
15
|
+
|
16
|
+
* Solidity language support
|
17
|
+
|
18
|
+
|
19
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
@@ -0,0 +1,215 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
|
4
|
+
usage() {
|
5
|
+
|
6
|
+
|
7
|
+
cat <<EOF
|
8
|
+
Usage $0:
|
9
|
+
|
10
|
+
$0 file ([STATE][-STATE]|last) VARIBALE[:VARIABLE]* [VARIABLE=[!]value,[VARIABLE=[-]value]*]"
|
11
|
+
|
12
|
+
where
|
13
|
+
- STATE is number 1 or string 'last'
|
14
|
+
- VARIABLE is shown for commnad $0 file 1
|
15
|
+
- query is comma ',' separated list of variable queries
|
16
|
+
'variable=value' or 'variable=!value"
|
17
|
+
|
18
|
+
Exmpale
|
19
|
+
$0 DUMP 1-99999 x1_addressPool now=1
|
20
|
+
EOF
|
21
|
+
|
22
|
+
}
|
23
|
+
|
24
|
+
if [ $# -lt 2 ]; then
|
25
|
+
usage
|
26
|
+
exit
|
27
|
+
fi
|
28
|
+
|
29
|
+
DUMP=$1; shift
|
30
|
+
|
31
|
+
STATE=$1; shift
|
32
|
+
KEY=$1; shift
|
33
|
+
QUERY=$1;shift
|
34
|
+
|
35
|
+
# echo gawk -vrange="$STATE" -vkey=$KEY -vquerystr=$QUERY
|
36
|
+
|
37
|
+
gawk -vrange="$STATE" -vkey=$KEY -vquerystr=$QUERY '
|
38
|
+
BEGIN {
|
39
|
+
empty_lines = 0
|
40
|
+
max_state = 0
|
41
|
+
|
42
|
+
# printf( "Starting state=%s, variable=%s\n", state, variable )
|
43
|
+
|
44
|
+
# manage state
|
45
|
+
# in_state - processing state
|
46
|
+
# in_variable - processing variable
|
47
|
+
# collect hash[in_state][variable]= ...state
|
48
|
+
delete states
|
49
|
+
}
|
50
|
+
|
51
|
+
# qs: var1=val1,var2=!val2
|
52
|
+
# @return 1 true if match all
|
53
|
+
function checkquery( qs, states, state ) {
|
54
|
+
|
55
|
+
# no state to explore
|
56
|
+
if ( length( states[state]) == 0 ) {
|
57
|
+
return( 0 )
|
58
|
+
}
|
59
|
+
|
60
|
+
# printf( "DBG: qs=%s\n", qs )
|
61
|
+
split( qs, queries, "," )
|
62
|
+
for ( query in queries ) {
|
63
|
+
# printf( "DBG: query=%s--> %s\n", query, queries[query] )
|
64
|
+
split( queries[query], q, "=" )
|
65
|
+
query_variable=q[1]
|
66
|
+
if ( substr(q[2],1,1) == "~" ) {
|
67
|
+
# regexp matach
|
68
|
+
query_value= substr(q[2],2)
|
69
|
+
match_equal = 0
|
70
|
+
negate = 0
|
71
|
+
}
|
72
|
+
# else if ( substr(q[2],1,1) == "-" ) {
|
73
|
+
# # negated match
|
74
|
+
# query_value= substr(q[2],2)
|
75
|
+
# match_equal = 0
|
76
|
+
# negate = 1
|
77
|
+
# }
|
78
|
+
else if ( substr(q[2],1,1) == "-" ) {
|
79
|
+
# negated match
|
80
|
+
query_value= substr(q[2],2)
|
81
|
+
match_equal = 1
|
82
|
+
negate = 1
|
83
|
+
}
|
84
|
+
else {
|
85
|
+
# match equal
|
86
|
+
query_value=q[2]
|
87
|
+
match_equal = 1
|
88
|
+
negate = 0
|
89
|
+
}
|
90
|
+
|
91
|
+
# printf( "DBG: match-equal=%d, var=%s, qv=%s, state=%s match=%s\n", match_equal, query_variable, query_value, state, match( states[state][query_variable], query_value) )
|
92
|
+
check = (match_equal && states[state][query_variable] == query_value) || (!match_equal && match( states[state][query_variable], query_value))
|
93
|
+
if ( !check && !negate ) {
|
94
|
+
# query failed: because no match made
|
95
|
+
return( 0 )
|
96
|
+
}
|
97
|
+
if ( check && negate ) {
|
98
|
+
# query failed: becaue match made - but should not have
|
99
|
+
return( 0 )
|
100
|
+
}
|
101
|
+
|
102
|
+
} # for q in queries
|
103
|
+
|
104
|
+
# all match
|
105
|
+
return( 1 )
|
106
|
+
|
107
|
+
}
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
END {
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
if ( key ) {
|
116
|
+
|
117
|
+
if ( range == "last" ) {
|
118
|
+
# last
|
119
|
+
start=0+max_state
|
120
|
+
end=0+max_state
|
121
|
+
}
|
122
|
+
else {
|
123
|
+
|
124
|
+
# 1-2
|
125
|
+
# 1-
|
126
|
+
# -2
|
127
|
+
# -
|
128
|
+
|
129
|
+
split( range, s, "-" )
|
130
|
+
start = 0+s[1]
|
131
|
+
# end = (0+s[2]<0+start)?0+start:0+s[2]
|
132
|
+
end = 0+s[2]
|
133
|
+
if (start == "" ){
|
134
|
+
start=1
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
split( key, variables, ":" )
|
139
|
+
|
140
|
+
# printf ("range=%s %d-%d querystr=%s\n", range, start, end, querystr )
|
141
|
+
|
142
|
+
|
143
|
+
for ( state=start; (end!= 0 && state <= end) || state<=max_state; state +=1 ) {
|
144
|
+
if ( !querystr || checkquery( querystr, states, state )) {
|
145
|
+
# no querystr or query match
|
146
|
+
for ( i in variables ) {
|
147
|
+
variable = variables[i]
|
148
|
+
# show timetick - instead of state number
|
149
|
+
now = states[state]["now"]
|
150
|
+
# printf( "%s@%s=%s\n", variable, state, states[state][variable] )
|
151
|
+
printf( "%s@%s=%s\n§\n", variable, now, states[state][variable] )
|
152
|
+
}
|
153
|
+
print ""
|
154
|
+
} # query
|
155
|
+
}
|
156
|
+
}
|
157
|
+
else if ( !key ) {
|
158
|
+
|
159
|
+
min=1
|
160
|
+
max=0
|
161
|
+
|
162
|
+
for ( key in states ) {
|
163
|
+
max=(0+max>0+key)?max:key
|
164
|
+
min=(0+min<0+key)?min:key
|
165
|
+
}
|
166
|
+
print "States: " min " - " max
|
167
|
+
|
168
|
+
|
169
|
+
print "variables:"
|
170
|
+
for ( key in states[1] ) {
|
171
|
+
printf( " %s\n", key )
|
172
|
+
}
|
173
|
+
|
174
|
+
print "Usage:"
|
175
|
+
print
|
176
|
+
print "'$0' file ([STATE][-STATE]|last) VARIBALE[:VARIABLE]* [VARIABLE=[!]value,[VARIABLE=[-]value]*]"
|
177
|
+
}
|
178
|
+
|
179
|
+
}
|
180
|
+
|
181
|
+
# State 9999:
|
182
|
+
match( $0, /State\s([0-9]+)/, ary) {
|
183
|
+
in_state = ary[1]
|
184
|
+
in_variable = 0
|
185
|
+
if ( in_state > max_state ) {
|
186
|
+
max_state = in_state
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
# collect variable. Notice before setting in_variable
|
191
|
+
in_variable && !/^\/\\/ {
|
192
|
+
# print "invariable " in_variable $0
|
193
|
+
states[in_state][in_variable]=states[in_state][in_variable] $0
|
194
|
+
# print "invariable " states[in_state][in_variable]
|
195
|
+
}
|
196
|
+
|
197
|
+
# /\ variabale = state info
|
198
|
+
match( $0, /^\/\\ ([^ ]*) = (.*)/, ary) {
|
199
|
+
# printf( "and *%s*\n", ary[1] )
|
200
|
+
# printf( "AND *%s*\n", ary[2] )
|
201
|
+
in_variable = ary[1]
|
202
|
+
# first variable content
|
203
|
+
states[in_state][in_variable] = ary[2]
|
204
|
+
}
|
205
|
+
|
206
|
+
# Emtpy line - discard && reset
|
207
|
+
/^\s*$/ {
|
208
|
+
in_variable = 0
|
209
|
+
empty_lines = empty_lines + 1
|
210
|
+
}
|
211
|
+
|
212
|
+
|
213
|
+
' $DUMP
|
214
|
+
|
215
|
+
# | fold -w 80 -s
|
data/lib/eth/al_api.rb
ADDED
data/lib/eth/ast_sexp.rb
ADDED
@@ -0,0 +1,519 @@
|
|
1
|
+
require 'sexp_processor'
|
2
|
+
|
3
|
+
module Sbuilder
|
4
|
+
module Eth
|
5
|
+
|
6
|
+
##
|
7
|
+
# AstSexp subclasses Sexps, which furher subclasses Array.
|
8
|
+
#
|
9
|
+
# First element is a symbol defining sexp type. AstSexp uses hash
|
10
|
+
# as the second array element for storing attributes. Elements
|
11
|
+
# starting with position are AST tree children.
|
12
|
+
#
|
13
|
+
# Method {.from_hash} creates AstSexp object for AST structure
|
14
|
+
# from solc Solidity compiler. It recurses AST nodes along
|
15
|
+
# 'children' property, and constructs AstSexp -object using 'name'
|
16
|
+
# (for sexp type), and 'attribute' properties.
|
17
|
+
#
|
18
|
+
|
19
|
+
class AstSexp < Sexp
|
20
|
+
|
21
|
+
|
22
|
+
# ------------------------------------------------------------------
|
23
|
+
# @!group Constructor
|
24
|
+
|
25
|
+
# Create a new Sexp containing +args+.
|
26
|
+
def initialize(*args)
|
27
|
+
super(*args)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create AstSexp tree from 'hash' with 'name' property,
|
31
|
+
# 'attributes' -property and 'children' -property
|
32
|
+
#
|
33
|
+
# @param hash [Hash ] Hash to convert to sexp
|
34
|
+
#
|
35
|
+
|
36
|
+
def self.from_hash( hash )
|
37
|
+
|
38
|
+
# create sexp Node based on json['name']
|
39
|
+
result = new_node( hash )
|
40
|
+
|
41
|
+
# add children from hash['children']
|
42
|
+
hash['children'] && hash['children'].each do |c|
|
43
|
+
chld = self.from_hash(c)
|
44
|
+
# chld.setParent( result )
|
45
|
+
# result << chld
|
46
|
+
result.addChild( chld )
|
47
|
+
end
|
48
|
+
|
49
|
+
# rename childs if needed
|
50
|
+
result.finalizeChilds
|
51
|
+
|
52
|
+
result
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add 'chld' to Sexp
|
57
|
+
def addChild( chld )
|
58
|
+
self << chld
|
59
|
+
end
|
60
|
+
|
61
|
+
# Insert 'child' in 'pos'
|
62
|
+
def insertChild( pos, child )
|
63
|
+
# children start counting from 2,...
|
64
|
+
self.insert(2+pos, child)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# After all childs processed
|
69
|
+
def finalizeChilds
|
70
|
+
# no-operation
|
71
|
+
end
|
72
|
+
|
73
|
+
# Find class to build for 'hash#name' and use constructor
|
74
|
+
# '.start' to construct an instance for dispatched class.
|
75
|
+
def self.new_node( hash )
|
76
|
+
|
77
|
+
|
78
|
+
# map solc node type to Sexp-class. This allows us to create
|
79
|
+
# AST nodes with better semantic information.
|
80
|
+
node2class = {
|
81
|
+
:ContractDefinition => ContractDefinition,
|
82
|
+
:FunctionDefinition => FunctionDefinition,
|
83
|
+
:ModifierDefinition => ModifierDefinition,
|
84
|
+
:ModifierInvocation => ModifierInvocation,
|
85
|
+
:ParameterList => ParameterList,
|
86
|
+
:VariableDeclaration => VariableDeclaration,
|
87
|
+
:Identifier => Identifier,
|
88
|
+
:UserDefinedTypeName => UserDefinedTypeName,
|
89
|
+
:NewExpression => NewExpression,
|
90
|
+
:IndexAccess => IndexAccess,
|
91
|
+
:Assignment => Assignment,
|
92
|
+
:MemberAccess => MemberAccess,
|
93
|
+
:IfStatement => IfStatement,
|
94
|
+
:FunctionCall => FunctionCall,
|
95
|
+
}
|
96
|
+
|
97
|
+
#
|
98
|
+
raise "Missing property 'name' on hash #{hash}" if hash['name'].nil?
|
99
|
+
sexp_type = hash['name'].to_sym
|
100
|
+
klass = node2class[sexp_type]
|
101
|
+
|
102
|
+
# default klass
|
103
|
+
klass = AstSexp if klass.nil?
|
104
|
+
|
105
|
+
ret = klass.start( klass, hash )
|
106
|
+
ret
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.start( klass, hash )
|
110
|
+
|
111
|
+
attributes = hash['attributes'] && hash['attributes'].keys.reduce( {} ) do |memo,key|
|
112
|
+
# memo << [key,hash['attributes'][key]]
|
113
|
+
memo[key] = hash['attributes'][key]
|
114
|
+
memo
|
115
|
+
end || {}
|
116
|
+
# link to sourceLine from a string 'line:col:lenggth' e.g. 102:6:1
|
117
|
+
attributes['sourceLine'] = hash['src'] ? hash['src'].split(':')[0].to_i : nil
|
118
|
+
ary = [ hash['name'].to_sym ] + [ attributes ]
|
119
|
+
klass.new( *ary )
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# @!endgroup
|
125
|
+
|
126
|
+
# ------------------------------------------------------------------
|
127
|
+
# @!group traverse support
|
128
|
+
|
129
|
+
# @return [s(AstSexp) nodes matching 'sym' or 'blk' returning
|
130
|
+
# true anywhere in sexp-tree
|
131
|
+
def nodes_with_match( sym=nil, &blk )
|
132
|
+
blk = ->( chldSexp ) { chldSexp.sexp_type == sym } unless sym.nil?
|
133
|
+
sexpAstArray = s() # []
|
134
|
+
deep_each { |n|
|
135
|
+
sexpAstArray << n if blk[n]
|
136
|
+
}
|
137
|
+
sexpAstArray
|
138
|
+
end
|
139
|
+
|
140
|
+
# Restore behaviour missing methods
|
141
|
+
def method_missing meth, *args
|
142
|
+
raise "Unknwon method '#{meth}' called on #{self}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def respond_to? msg, private = false # :nodoc:
|
146
|
+
# why do I need this? Because ruby 2.0 is broken. That's why.
|
147
|
+
super
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# @!endgroup
|
152
|
+
|
153
|
+
# ------------------------------------------------------------------
|
154
|
+
# @!group Access sexp instance properties
|
155
|
+
|
156
|
+
# @return [Hash] hash in position 1 for attribute mapping
|
157
|
+
def attributes
|
158
|
+
self[1] || {}
|
159
|
+
end
|
160
|
+
|
161
|
+
# @return [value] value of first attribute with key == 'attrName'
|
162
|
+
def attribute( attrName )
|
163
|
+
# attributes.select{ |a| a[0] == attrName }.map{ |a| a[1] }.first
|
164
|
+
attributes[attrName]
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
# String/symbols index access attributes, all other are
|
169
|
+
# delegated to parent (array class)
|
170
|
+
def [](index)
|
171
|
+
if index.is_a?(String) || index.is_a?(Symbol)
|
172
|
+
ret = attribute(index)
|
173
|
+
raise "Accessing non existing attribute '#{index}' in #{self}" if ret.nil?
|
174
|
+
return ret
|
175
|
+
end
|
176
|
+
super(index)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Override square bracket assignment to work on strings/symbols
|
180
|
+
# on 'attributes' hash in pos 1 of the sexp
|
181
|
+
def []=(index, val)
|
182
|
+
if index.is_a?(String) || index.is_a?(Symbol)
|
183
|
+
self[1] = attributes.merge( { index => val })
|
184
|
+
return self
|
185
|
+
end
|
186
|
+
super(index,val)
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
# @return [Boolean] true if 'key' is present in '#attributes'
|
191
|
+
def key?( key )
|
192
|
+
attributes.key?( key )
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
# @param sym [Symbol] create 'blk' c.sexpt_type == 'sym' if
|
197
|
+
# 'sym' given
|
198
|
+
#
|
199
|
+
# @param blk [Block] to filter children element to return
|
200
|
+
#
|
201
|
+
# @return [Array] array of childs (starting from position 2)
|
202
|
+
# where &blk evaluates true, return all childs if no block given
|
203
|
+
def children( sym=nil, &blk )
|
204
|
+
blk = ->( chldSexp ) { chldSexp.sexp_type == sym } unless sym.nil?
|
205
|
+
blk.nil? ? self[2..-1] : self[2..-1].select{ |e| blk[e] }
|
206
|
+
end
|
207
|
+
|
208
|
+
def hasChild( sym=nil )
|
209
|
+
children( sym ).any?
|
210
|
+
end
|
211
|
+
|
212
|
+
# @!endgroup
|
213
|
+
end # class AstSexp < Sexp
|
214
|
+
|
215
|
+
|
216
|
+
# Class defining context Solidity expression 'expressionAst'
|
217
|
+
class ExpressionContext < AstSexp;
|
218
|
+
# Create ExpressionContext for 'expressionAst' in context 'ctx'
|
219
|
+
def self.createExpressionContext( expressionAst, ctx={} )
|
220
|
+
expressionContext = ExpressionContext.new( :ExpressionContext )
|
221
|
+
expressionContext["expressionAst"] = expressionAst
|
222
|
+
expressionContext["context"] = ctx
|
223
|
+
expressionContext
|
224
|
+
end
|
225
|
+
# @return [SexpAst] expressionAst wrapped within ExpressionContext
|
226
|
+
def expressionAst
|
227
|
+
self["expressionAst"]
|
228
|
+
end
|
229
|
+
def setContextContract( contract )
|
230
|
+
self["context"][:ContractDefinition] = contract
|
231
|
+
end
|
232
|
+
def context
|
233
|
+
self["context"]
|
234
|
+
end
|
235
|
+
|
236
|
+
# supports :ContractDefinition
|
237
|
+
def getContext( contextType )
|
238
|
+
# supportContextTypes = [:ContractDefinition]
|
239
|
+
# raise "Unsupportted contextType:#{contextType}, supported #{supportContextTypes.join(',')} " unless supportContextTypes.include?( contextType )
|
240
|
+
context[contextType]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class ContractDefinition < AstSexp;
|
245
|
+
include Scope;
|
246
|
+
include Scoped;
|
247
|
+
# @return [Array<AstSexp>] member :VariableDeclaration of contract
|
248
|
+
def memberVariables
|
249
|
+
children(:VariableDeclaration )
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class FunctionOrModifierDefinition < AstSexp
|
254
|
+
include Scope
|
255
|
+
include Scoped
|
256
|
+
# @return [:ParameterList] input to Function/Modifier
|
257
|
+
def requestParameters
|
258
|
+
children[0]
|
259
|
+
end
|
260
|
+
def block
|
261
|
+
children(:Block).first
|
262
|
+
end
|
263
|
+
# statements are children of :Block sexp entry
|
264
|
+
def statements
|
265
|
+
block.children
|
266
|
+
end
|
267
|
+
end
|
268
|
+
class FunctionDefinition < FunctionOrModifierDefinition
|
269
|
+
# Make a difference between (Request)ParameterList and
|
270
|
+
# ResponseParameterList
|
271
|
+
def finalizeChilds
|
272
|
+
# change sexp-type
|
273
|
+
responseParameters[0] = :ResponseParameterList
|
274
|
+
end
|
275
|
+
def responseParameters
|
276
|
+
children[1]
|
277
|
+
end
|
278
|
+
def modifierInvocations
|
279
|
+
children(:ModifierInvocation)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
class ModifierDefinition < FunctionOrModifierDefinition
|
283
|
+
# Statements until 'PlaceholderStatement'
|
284
|
+
def preambleStatements
|
285
|
+
statements.take_while{ |stmt| stmt.sexp_type != :PlaceholderStatement }
|
286
|
+
end
|
287
|
+
|
288
|
+
# Enable access to translated actual parameters
|
289
|
+
# for :ModifierInvocation during :FunctionDefintion
|
290
|
+
# translation.
|
291
|
+
# @# @!attribute [Hash] map formal parameter name to TlaSexp
|
292
|
+
attr_accessor :invocationCtx
|
293
|
+
|
294
|
+
def define(name, astSexp)
|
295
|
+
super( name, astSexp )
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
# @return [Scoped,ScopedWrapper] wraper if 'name' is symbol in
|
300
|
+
# :ModifierDefinition, else propagate resolve to scope
|
301
|
+
# hierarchy (to return Scoped)
|
302
|
+
def resolve( name )
|
303
|
+
return ScopedWrapper.wrap( self, name, getSymbol( name ) ) if getSymbol( name )
|
304
|
+
super( name )
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
# Wrap 'Scoped' (e.g. VariabeDeclation) 'astSexp' to allow access
|
310
|
+
# to evaluated arguments of a :ModifierInvcation in
|
311
|
+
# 'invocationCtx' -attribute of 'scopeDefining'.
|
312
|
+
class ScopedWrapper < AstSexp
|
313
|
+
|
314
|
+
def resolve( name )
|
315
|
+
scoped.resolve(name)
|
316
|
+
end
|
317
|
+
|
318
|
+
# @return [ScopedWrapper] constructor wrapping 'astSexp' with 'name' in 'scope'
|
319
|
+
def self.wrap( scope, name, astSexp )
|
320
|
+
wrapper = ScopedWrapper.new( :ScopedWrapper, astSexp.attributes, *astSexp.children )
|
321
|
+
wrapper.scopeDefining = scope
|
322
|
+
wrapper.scoped = astSexp
|
323
|
+
wrapper.name = name
|
324
|
+
wrapper
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
|
329
|
+
# @# @!attribute [Scoped] scoped being wrapped
|
330
|
+
attr_accessor :scoped
|
331
|
+
|
332
|
+
# @# @!attribute [String] name of scoped used in define
|
333
|
+
attr_accessor :name
|
334
|
+
|
335
|
+
# @# @!attribute [Scope] scopeDefining i.e. scope defining {#scoped}
|
336
|
+
attr_writer :scopeDefining
|
337
|
+
|
338
|
+
# @return [TlaExpression,Scope] invocationCtx return expression
|
339
|
+
# evaluated in @scopeDefining.invocationCtx, else return
|
340
|
+
# scope {#scopeDefining} for {#scoped} (=wrapped) object
|
341
|
+
def scopeDefining
|
342
|
+
# return evaluated value from context
|
343
|
+
if @scopeDefining.invocationCtx
|
344
|
+
ctxElement = @scopeDefining.invocationCtx[name]
|
345
|
+
raise "Missing evaluation for key '#{name}' of class #{scoped.class}, known keys in invocationCtx=#{@scopeDefining.invocationCtx.keys.join(',')}" if ctxElement.nil?
|
346
|
+
return ctxElement
|
347
|
+
end
|
348
|
+
|
349
|
+
@scopeDefining
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
|
354
|
+
class ModifierInvocation < AstSexp
|
355
|
+
# @return [:ModifierDefinition] :ModifierDefinition resolved for
|
356
|
+
# :ModifierInvocation :Identifier
|
357
|
+
def modifierDefinition
|
358
|
+
children(:Identifier).first.reference
|
359
|
+
end
|
360
|
+
# Actual parametes used in :ModifierInvocation
|
361
|
+
def arguments
|
362
|
+
children[1..-1]
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
|
367
|
+
|
368
|
+
class Assignment < AstSexp
|
369
|
+
def lval
|
370
|
+
children[0]
|
371
|
+
end
|
372
|
+
def rval
|
373
|
+
children[1]
|
374
|
+
end
|
375
|
+
end
|
376
|
+
class ParameterList < AstSexp;
|
377
|
+
include Scope
|
378
|
+
include Scoped
|
379
|
+
|
380
|
+
def parameters
|
381
|
+
children
|
382
|
+
end
|
383
|
+
|
384
|
+
# @return [VariableDeclaration] index-th parameter declaration
|
385
|
+
def parameterByIndex( index )
|
386
|
+
children[index]
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
class VariableDeclarationWithDomain < AstSexp
|
391
|
+
include Scoped
|
392
|
+
# @param name_domain [Array] array of length with name and
|
393
|
+
# domain
|
394
|
+
def self.create( name_domain)
|
395
|
+
VariableDeclarationWithDomain.new(:VariableDeclarationWithDomain,
|
396
|
+
{"name" => name_domain[0],
|
397
|
+
"domain" => name_domain[1]})
|
398
|
+
end
|
399
|
+
end
|
400
|
+
class VariableDeclaration < AstSexp
|
401
|
+
include Scoped
|
402
|
+
def variableType
|
403
|
+
children[0]
|
404
|
+
end
|
405
|
+
end
|
406
|
+
class Identifier < AstSexp; include Reference; end
|
407
|
+
class MemberAccess < AstSexp;
|
408
|
+
include Reference;
|
409
|
+
def memberName
|
410
|
+
self['member_name']
|
411
|
+
end
|
412
|
+
def struct
|
413
|
+
children[0]
|
414
|
+
end
|
415
|
+
# Identifier for the MemberAccess
|
416
|
+
def identifierAst
|
417
|
+
return struct if struct.sexp_type == :Identifier
|
418
|
+
struct.identifierAst
|
419
|
+
end
|
420
|
+
end
|
421
|
+
class UserDefinedTypeName < AstSexp; include Reference; end
|
422
|
+
class NewExpression < AstSexp
|
423
|
+
include Reference
|
424
|
+
def classConstructed
|
425
|
+
children(:UserDefinedTypeName).first
|
426
|
+
end
|
427
|
+
end
|
428
|
+
class IndexAccess < AstSexp;
|
429
|
+
# @return [Identifier] identifier indexed
|
430
|
+
def identifier
|
431
|
+
children[0]
|
432
|
+
end
|
433
|
+
def indexer
|
434
|
+
children[1]
|
435
|
+
end
|
436
|
+
end
|
437
|
+
class FunctionCall < AstSexp
|
438
|
+
# AST nodes
|
439
|
+
def functionCalled
|
440
|
+
children[0]
|
441
|
+
end
|
442
|
+
def arguments
|
443
|
+
children[1..-1]
|
444
|
+
end
|
445
|
+
|
446
|
+
# @return [true] for selfdestruct, revert, require etc
|
447
|
+
def isCallToReservedFunctionType
|
448
|
+
# case this.f.value(1).gas(2)(3,4,5)
|
449
|
+
return false if functionCalled.sexp_type == :FunctionCall
|
450
|
+
# case f(3,4,5) is selfdestruct, require etc.
|
451
|
+
return functionCalled.reference.respond_to?(:functionType) &&
|
452
|
+
functionCalled.reference.functionType == :reservedFunctionType
|
453
|
+
end
|
454
|
+
# @return [Boolean] true if functionCalled.sexp_type == :NewExpression
|
455
|
+
def isConstructorCall
|
456
|
+
functionCalled.sexp_type == :NewExpression
|
457
|
+
end
|
458
|
+
|
459
|
+
# @return [nil,FunctionCall] if functionCalled contains :MemberAccess to value
|
460
|
+
def valueModified
|
461
|
+
(nodes_with_match { |n| n.sexp_type == :FunctionCall && n.functionCalled.sexp_type == :MemberAccess && n.functionCalled["member_name"] == "value" } || []).first
|
462
|
+
end
|
463
|
+
|
464
|
+
# @return [Identifier,:MemberAccess] Solidity expression AST in
|
465
|
+
# :FunctionCall to dispatch the calle = functionCalled
|
466
|
+
# minus gas/value modifiers
|
467
|
+
def callExpression
|
468
|
+
functionCalled.nodes_with_match(:Identifier).first
|
469
|
+
|
470
|
+
# nodes_with_match do |n|
|
471
|
+
# n.sexp_type != :FunctionCall &&
|
472
|
+
# ( n.sexp_type == :Identifier ||
|
473
|
+
# ( [:MemberAccess].include?( n.sexp_type ) &&
|
474
|
+
# ! [Constants::FIELD_NAME_GAS, Constants::FIELD_NAME_VALUE ].include?( n["member_name"] ))
|
475
|
+
# )
|
476
|
+
# end.first
|
477
|
+
end
|
478
|
+
|
479
|
+
def callTarget
|
480
|
+
case functionCalled.sexp_type
|
481
|
+
when :MemberAccess
|
482
|
+
if functionCalled.struct.respond_to?( :callTarget )
|
483
|
+
# recurse FunctionCall (while value/gas modifier)
|
484
|
+
functionCalled.struct.callTarget
|
485
|
+
else
|
486
|
+
# if value/gas then ptr to modify fun else resolved-func
|
487
|
+
[Constants::FIELD_NAME_GAS, Constants::FIELD_NAME_VALUE].include?( functionCalled["member_name"] ) ?
|
488
|
+
functionCalled.struct.reference :
|
489
|
+
functionCalled.reference
|
490
|
+
end
|
491
|
+
when :Identifier
|
492
|
+
functionCalled.reference
|
493
|
+
when :NewExpression
|
494
|
+
functionCalled.reference
|
495
|
+
when :FunctionCall
|
496
|
+
# recursion :FunctionCall modifiers
|
497
|
+
functionCalled.callTarget
|
498
|
+
else
|
499
|
+
raise "Do know how to process '#{functionCalled.sexp_type}' to get 'callTarget' in #{self}"
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
end
|
504
|
+
|
505
|
+
class IfStatement < AstSexp
|
506
|
+
def condition
|
507
|
+
children[0]
|
508
|
+
end
|
509
|
+
def ifThen
|
510
|
+
children[1]
|
511
|
+
end
|
512
|
+
def elseThen
|
513
|
+
children[2]
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|