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
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'sexp_processor'
|
2
|
+
|
3
|
+
module Sbuilder
|
4
|
+
module Eth
|
5
|
+
class SexpProcessorResolve < SexpProcessorEth
|
6
|
+
# ------------------------------------------------------------------
|
7
|
+
# @!group Constructor
|
8
|
+
|
9
|
+
def initialize( options={} )
|
10
|
+
super( options )
|
11
|
+
# Allow Sexp nodes to remain intact, see 'process_rest'
|
12
|
+
self.require_empty = false
|
13
|
+
|
14
|
+
# visitors just recurses - do not shift sexp (as sexp_processor default does)
|
15
|
+
self.default_method = :process_rest
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
# @!endgroup
|
20
|
+
|
21
|
+
# ------------------------------------------------------------------
|
22
|
+
# @!group Resolvers for reference node types
|
23
|
+
|
24
|
+
# Resolve 'reference' on 'NewExpression' to point to constructor
|
25
|
+
# in contract pointed by 'classConstructed'
|
26
|
+
def process_NewExpression( sexp )
|
27
|
+
|
28
|
+
# resolve child particulary 'UserDefinedTypeName'
|
29
|
+
process_rest( sexp )
|
30
|
+
|
31
|
+
# Navigate to resolved contract
|
32
|
+
contract = sexp.classConstructed.reference
|
33
|
+
|
34
|
+
# resolve for constructor method
|
35
|
+
sexp.reference = contract.resolve( sexp.classConstructed["name"])
|
36
|
+
|
37
|
+
s()
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_UserDefinedTypeName( sexp )
|
41
|
+
logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
|
42
|
+
|
43
|
+
raise "Unscoped identifier '#{sexp['name']}' for #{sexp}" if sexp.enclosingScope.nil?
|
44
|
+
# Resolve user defined type name in 'enclosingScope'
|
45
|
+
sexp.reference = sexp.enclosingScope.resolve(sexp['name'])
|
46
|
+
|
47
|
+
s()
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_MemberAccess( sexp )
|
51
|
+
logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
|
52
|
+
|
53
|
+
# resolve childs (ie. :Identifier, :MemberAccess) NB: can also
|
54
|
+
# be FunctionCall as is case for f.gas(1).value(2)(3,5,6)
|
55
|
+
process_rest( sexp )
|
56
|
+
|
57
|
+
# 'struct' should have been resolved by process_rest.
|
58
|
+
# No need to resolve function call modifiers
|
59
|
+
|
60
|
+
if sexp.struct.sexp_type == :FunctionCall
|
61
|
+
|
62
|
+
# sexp.reference = scope.resolve( sexp["member_name"] ) unless sexp["member_name"] == Constants::FIELD_NAME_VALUE ||
|
63
|
+
# # sexp["member_name"] == Constants::FIELD_NAME_GAS
|
64
|
+
|
65
|
+
else
|
66
|
+
# scope is pointed by reference
|
67
|
+
scope = sexp.struct.reference
|
68
|
+
# Indirection for :VariableDeclaration: scope is actually the
|
69
|
+
# type of the :VariableDeclaration
|
70
|
+
scope = scope.variableType.reference if scope.sexp_type == :VariableDeclaration
|
71
|
+
|
72
|
+
if scope.sexp_type == :FunctionDefinition
|
73
|
+
#
|
74
|
+
# Case f.value(1).gas(1)(3,4) - value and gas modifiers of
|
75
|
+
# the function CALL are NOT defined in 'FunctionDefinition'
|
76
|
+
#
|
77
|
+
# Gas, value - No need to resolve - raise exception for surprieses if
|
78
|
+
# 'member_name' is something else that 'value' or 'gas'.
|
79
|
+
#
|
80
|
+
# raise "Unexpected sexp #{sexp["member_name"]} of in #{sexp}" unless sexp["member_name"] == Constants::FIELD_NAME_VALUE ||
|
81
|
+
# sexp["member_name"] == Constants::FIELD_NAME_GAS
|
82
|
+
# sexp.reference = sexp.functionCalled.reference
|
83
|
+
if sexp["member_name"] == Constants::FIELD_NAME_VALUE || sexp["member_name"] == Constants::FIELD_NAME_GAS
|
84
|
+
# point to called function
|
85
|
+
sexp.reference = sexp.struct.reference
|
86
|
+
else
|
87
|
+
sexp.reference = scope.resolve( sexp["member_name"] )
|
88
|
+
end
|
89
|
+
|
90
|
+
else
|
91
|
+
# easy part
|
92
|
+
sexp.reference = scope.resolve( sexp["member_name"] )
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
s()
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def process_Identifier( sexp )
|
102
|
+
logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
|
103
|
+
|
104
|
+
raise "Unscoped identifier '#{sexp['value']}' for #{sexp}" if sexp.enclosingScope.nil?
|
105
|
+
# Resolve identifier 'value' in 'enclosingScope' of 'sexp'
|
106
|
+
sexp.reference = sexp.enclosingScope.resolve(sexp['value'])
|
107
|
+
|
108
|
+
# continue with tree traversal
|
109
|
+
# process_rest( sexp )
|
110
|
+
s()
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
# @!endgroup
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
@@ -0,0 +1,355 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'sexp_processor'
|
3
|
+
|
4
|
+
module Sbuilder
|
5
|
+
module Eth
|
6
|
+
|
7
|
+
# Visit AstSexp, 1) create hierarchical symbol tables 2) define
|
8
|
+
# symbols, and 3) add pointer to encosing scope to expression
|
9
|
+
# needed to be resolved.
|
10
|
+
#
|
11
|
+
# Techinically:
|
12
|
+
# - 1) Scope#enclosingScope : build scope hierarchy
|
13
|
+
# - 2) Scope#define( name, sexp ): build symbol table defines
|
14
|
+
# - 3) Reference#enclosingScope : set scope for use for Scope#resolve
|
15
|
+
#
|
16
|
+
# Scope hierarchy built:
|
17
|
+
# - contract
|
18
|
+
# - function
|
19
|
+
# - input parameter = request message (key name "msg"), scope where expression
|
20
|
+
# references are resolved
|
21
|
+
# - return parameters = response message ( key name "return-")
|
22
|
+
#
|
23
|
+
# TODO:
|
24
|
+
# - global scope
|
25
|
+
# - structure types
|
26
|
+
# - enums
|
27
|
+
#
|
28
|
+
#
|
29
|
+
#
|
30
|
+
|
31
|
+
class SexpProcessorScope < SexpProcessorEth
|
32
|
+
|
33
|
+
# ------------------------------------------------------------------
|
34
|
+
# @!group Object state
|
35
|
+
|
36
|
+
# @!attribute [Scope] currentScope which currently opened
|
37
|
+
attr_accessor :currentScope
|
38
|
+
|
39
|
+
# @!attribute [Hash] options
|
40
|
+
attr_accessor :options
|
41
|
+
|
42
|
+
# @!endgroup
|
43
|
+
|
44
|
+
|
45
|
+
# ------------------------------------------------------------------
|
46
|
+
# @!group Constructor
|
47
|
+
|
48
|
+
def initialize( options={} )
|
49
|
+
super( options )
|
50
|
+
|
51
|
+
self.options = options
|
52
|
+
|
53
|
+
initGlobalScope
|
54
|
+
end
|
55
|
+
|
56
|
+
def initGlobalScope
|
57
|
+
self.currentScope = GlobalScope.start( options )
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!endgroup
|
61
|
+
|
62
|
+
# ------------------------------------------------------------------
|
63
|
+
# @!group Recursion helpers
|
64
|
+
|
65
|
+
def scoped( ethScope )
|
66
|
+
raise "Scope support missing in #{ethScope}" unless ethScope.respond_to?( :isScope ) && ethScope.isScope
|
67
|
+
|
68
|
+
# remember 'prevScope', update enclosing scope, switch '@currentScope',
|
69
|
+
prevScope = currentScope
|
70
|
+
ethScope.enclosingScope = currentScope
|
71
|
+
self.currentScope = ethScope
|
72
|
+
|
73
|
+
begin
|
74
|
+
yield
|
75
|
+
ensure
|
76
|
+
# logger.debug "#{__method__}: pop scope for '#{ethScope.sexp_type}' - resume #{prevScope ? prevScope.sexp_type : 'global scope'}" if logger.debug?
|
77
|
+
self.currentScope = prevScope
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def getScopeExpressionProcessor( scope )
|
82
|
+
proc = ScopeExpressionProcessor.new( options )
|
83
|
+
proc.currentScope = scope
|
84
|
+
proc
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
# @!endgroup
|
89
|
+
|
90
|
+
|
91
|
+
# ------------------------------------------------------------------
|
92
|
+
# @!group Node rules to recurse down
|
93
|
+
|
94
|
+
def process_ContractDefinition( sexp )
|
95
|
+
|
96
|
+
# add contract to global scope
|
97
|
+
currentScope.define( sexp['name'], sexp )
|
98
|
+
|
99
|
+
scoped( sexp ) do
|
100
|
+
getScopeExpressionProcessor( currentScope ).process( sexp )
|
101
|
+
process_rest( sexp )
|
102
|
+
end
|
103
|
+
s()
|
104
|
+
end
|
105
|
+
|
106
|
+
# Scope ModifierDefinitionReferences
|
107
|
+
# to 'msg' and :ParameterList parameters in
|
108
|
+
# :ModifierDefinition must be resolved dynamically during
|
109
|
+
# :ModifierInvocation. Global references can be resolved
|
110
|
+
# "normally" (=statically by sexp_processor_scope and
|
111
|
+
# sexp_processor_resolve), as well as :Idenfier references in
|
112
|
+
# sexp_processor_resolve.
|
113
|
+
#
|
114
|
+
#
|
115
|
+
# Solution: we define 'msg' scope as :ParameterListWrapper
|
116
|
+
# encapsulating :ParameterList of :ModifierDefinition. Method
|
117
|
+
# 'scopeDefining', which would otherwise return :ParameterList
|
118
|
+
# or :ModifierDefinition, is changed to return :ParameterList of
|
119
|
+
# :FunctionDefintion from a value set dynamically into
|
120
|
+
# :ParameterListWrapper, when scope of modifier is known (=when
|
121
|
+
# processing :ModifierInvocation in :FunctionDefinition
|
122
|
+
# statements).
|
123
|
+
#
|
124
|
+
def process_ModifierDefinition( sexp )
|
125
|
+
logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
|
126
|
+
|
127
|
+
currentScope.define( sexp['name'], sexp )
|
128
|
+
|
129
|
+
# Wrap :ModifierDefinition 'requestParameters' ParameterListWrapper
|
130
|
+
# sexp.wrapRequestParameters
|
131
|
+
|
132
|
+
blockScope = sexp
|
133
|
+
scoped( sexp ) do
|
134
|
+
|
135
|
+
|
136
|
+
# Define msg
|
137
|
+
currentScope.define( Constants::SOL_MSG, sexp.requestParameters)
|
138
|
+
|
139
|
+
# Add common params
|
140
|
+
addCommonRequestParameters( sexp.requestParameters, sexp)
|
141
|
+
|
142
|
+
# Scope 'normal' request paramers in () ModifierDefinition scope
|
143
|
+
sexp.requestParameters.parameters.each { |paramDecl| process( paramDecl ) }
|
144
|
+
scoped( sexp.requestParameters ) do
|
145
|
+
# scope request params within contract:func:input:output
|
146
|
+
process( sexp.requestParameters )
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# currentScope.define( Constants::SYMBOL_RESPONSE_MSG, sexp.responseParameters )
|
151
|
+
# process( sexp.responseParameters )
|
152
|
+
# local variables
|
153
|
+
process( sexp.block )
|
154
|
+
end
|
155
|
+
|
156
|
+
# process expression on block with requestParameteres in scope context
|
157
|
+
getScopeExpressionProcessor( blockScope ).process( sexp.block )
|
158
|
+
|
159
|
+
s()
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
# Create scope hierarchy for :FunctionDefintion, define symbols
|
164
|
+
# for declarations in a scope, and add pointers to scope for
|
165
|
+
# identfiers.
|
166
|
+
#
|
167
|
+
# Implementation:
|
168
|
+
#
|
169
|
+
# - assumes preprocessing ensures return parameters have name
|
170
|
+
#
|
171
|
+
# - scope hieararchy: :FunctionDefintion, requestParameters,
|
172
|
+
# responseParameters
|
173
|
+
#
|
174
|
+
# -symbol definitions:
|
175
|
+
# - FunctionDefintion
|
176
|
+
# - msg --> requestParameters
|
177
|
+
# - local variables
|
178
|
+
# - requestParameters
|
179
|
+
# - request parameter name -> :VarDecl
|
180
|
+
# - responseParameters
|
181
|
+
# - response parameter name -> :VarDecl
|
182
|
+
#
|
183
|
+
# - scope identifiers
|
184
|
+
# - requestParameters
|
185
|
+
# -- modifierInvocations
|
186
|
+
# - responseParameters
|
187
|
+
# -- function body block
|
188
|
+
#
|
189
|
+
#
|
190
|
+
|
191
|
+
def process_FunctionDefinition( sexp )
|
192
|
+
logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
|
193
|
+
|
194
|
+
|
195
|
+
currentScope.define( sexp['name'], sexp )
|
196
|
+
currentScope.define( Constants::SOL_THIS, sexp )
|
197
|
+
|
198
|
+
scoped( sexp ) do
|
199
|
+
|
200
|
+
# validate existing names for return value
|
201
|
+
logger.debug "#{__method__}: sexp.responseParameters=#{sexp.responseParameters}" if logger.debug?
|
202
|
+
# assume preprocessing ensures return parameters have name
|
203
|
+
sexp.responseParameters.parameters.each { |parameter| raise "Unnamed return parameters are not supported yet #{parameter} in #{sexp}" if parameter["name"].empty? }
|
204
|
+
|
205
|
+
# Support msg.X, where X=sender, value, ...
|
206
|
+
currentScope.define( Constants::SOL_MSG, sexp.requestParameters )
|
207
|
+
|
208
|
+
# TODO: remove, currently used to create response data types
|
209
|
+
# for this function
|
210
|
+
currentScope.define( Constants::SYMBOL_RESPONSE_MSG, sexp.responseParameters )
|
211
|
+
|
212
|
+
# add msg.sender, msg.recipient, msg.value
|
213
|
+
addCommonRequestParameters( sexp.requestParameters, sexp)
|
214
|
+
|
215
|
+
scoped( sexp.requestParameters ) do
|
216
|
+
|
217
|
+
# scope request params within contract:func:input:output
|
218
|
+
process( sexp.requestParameters )
|
219
|
+
|
220
|
+
# scope for modfifiers: contract:func:input
|
221
|
+
sexp.modifierInvocations.each do |modifier|
|
222
|
+
logger.debug "#{__method__}: scope-modifierInvocation:#{modifier}" if logger.debug?
|
223
|
+
# getScopeExpressionProcessor( sexp.requestParameters ).process( modifier )
|
224
|
+
getScopeExpressionProcessor( currentScope ).process( modifier )
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
# declare return parametes within 'responseParameters' scope
|
229
|
+
scoped( sexp.responseParameters ) do
|
230
|
+
process( sexp.responseParameters )
|
231
|
+
# add scope for identifier references
|
232
|
+
getScopeExpressionProcessor( currentScope ).process( sexp.block )
|
233
|
+
end # scoped( sexp.responseParameters )
|
234
|
+
|
235
|
+
end # request parameters
|
236
|
+
|
237
|
+
# local variables
|
238
|
+
process( sexp.block )
|
239
|
+
|
240
|
+
end # scoped(sexp): function
|
241
|
+
|
242
|
+
s()
|
243
|
+
end
|
244
|
+
|
245
|
+
# def process_ParameterList( sexp )
|
246
|
+
# # logger.info "#{__method__}: #{sexp} entered"
|
247
|
+
# scoped( sexp ) do
|
248
|
+
# process_rest( sexp )
|
249
|
+
# end
|
250
|
+
# end
|
251
|
+
|
252
|
+
# @!endgroup
|
253
|
+
|
254
|
+
# ------------------------------------------------------------------
|
255
|
+
# @!group Node rules for scoped ie. nodes using scope
|
256
|
+
|
257
|
+
def process_VariableDeclaration( sexp )
|
258
|
+
logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
|
259
|
+
logger.info "#{__method__}: define variable name=#{sexp['name']}, type=#{sexp.sexp_type} within #{currentScope}"
|
260
|
+
|
261
|
+
currentScope.define( sexp['name'], sexp )
|
262
|
+
process_rest( sexp )
|
263
|
+
end
|
264
|
+
|
265
|
+
def process_VariableDeclarationWithDomain( sexp )
|
266
|
+
logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
|
267
|
+
logger.info "#{__method__}: define variable name=#{sexp['name']}, type=#{sexp.sexp_type} within #{currentScope}"
|
268
|
+
|
269
|
+
currentScope.define( sexp['name'], sexp )
|
270
|
+
|
271
|
+
# No need to go deeper
|
272
|
+
# process_rest( sexp )
|
273
|
+
|
274
|
+
s()
|
275
|
+
end
|
276
|
+
|
277
|
+
|
278
|
+
|
279
|
+
# @!endgroup
|
280
|
+
|
281
|
+
# ------------------------------------------------------------------
|
282
|
+
# @!group AST modifiers
|
283
|
+
|
284
|
+
# Add common request parameters (e.g. sender, value, recipient)
|
285
|
+
# to 'sexpRequestParameters' of a constructor or a normal
|
286
|
+
# contract function 'sexpFunctionDefinition'.
|
287
|
+
#
|
288
|
+
# @param sexpFuncOrModifier
|
289
|
+
# [:FunctionDefinition,:ModifierDefinition] to add common
|
290
|
+
# parametes rto
|
291
|
+
#
|
292
|
+
#
|
293
|
+
def addCommonRequestParameters( sexpRequestParameters, sexpFuncOrModifier )
|
294
|
+
(sexpFuncOrModifier.sexp_type != :ModifierDefinition && sexpFuncOrModifier["isConstructor"] ?
|
295
|
+
Constants::COMMON_REQ_CONSTRUCTOR :
|
296
|
+
Constants::COMMON_REQ_MSG).each do |name_domain|
|
297
|
+
sexpRequestParameters.addChild( VariableDeclarationWithDomain.create( name_domain))
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
# @!endgroup
|
303
|
+
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
class ScopeExpressionProcessor < SexpProcessorEth
|
308
|
+
|
309
|
+
# @!attribute [Scope] currentScope which currently opened
|
310
|
+
attr_accessor :currentScope
|
311
|
+
|
312
|
+
|
313
|
+
def initialize( options={} )
|
314
|
+
super( options )
|
315
|
+
|
316
|
+
# self.options = options
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
# ------------------------------------------------------------------
|
321
|
+
# @!group Individual expressions - set enclosingScope
|
322
|
+
|
323
|
+
def process_UserDefinedTypeName( sexp )
|
324
|
+
|
325
|
+
logger.debug "#{__method__}: sexp==#{sexp}" if logger.debug?
|
326
|
+
raise "No current scope for #{sexp}" if currentScope.nil?
|
327
|
+
|
328
|
+
# save 'currentScope' to use in resolve declaration reference
|
329
|
+
sexp.enclosingScope = currentScope
|
330
|
+
|
331
|
+
process_rest(sexp)
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
def process_Identifier( sexp )
|
337
|
+
|
338
|
+
logger.debug "#{__method__}: sexp==#{sexp}" if logger.debug?
|
339
|
+
logger.info "#{__method__}: sexp=#{sexp['value']}"
|
340
|
+
|
341
|
+
# save 'currentScope' to use in resolve declaration reference
|
342
|
+
sexp.enclosingScope = currentScope
|
343
|
+
|
344
|
+
process_rest(sexp)
|
345
|
+
end
|
346
|
+
|
347
|
+
# @!endgroup
|
348
|
+
|
349
|
+
|
350
|
+
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
end
|
355
|
+
|