sbuilder-eth 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|