sbuilder-ethereum 0.0.6
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/VERSION +1 -0
- data/lib/mixer/decl_ref.rb +19 -0
- data/lib/mixer/domain_ref.rb +18 -0
- data/lib/mixer/scope.rb +92 -0
- data/lib/mixer/scoped.rb +17 -0
- data/lib/mixer/symbol_ref.rb +16 -0
- data/lib/mixer/type_symbol.rb +75 -0
- data/lib/plugin/plugin.rb +332 -0
- data/lib/resources/correctness/accouns_type.tla +1 -0
- data/lib/resources/correctness/accounts_unique.tla +2 -0
- data/lib/resources/correctness/accounts_valid.tla +2 -0
- data/lib/resources/correctness/storage_root_unique.tla +2 -0
- data/lib/resources/correctness/total_value.tla +1 -0
- data/lib/resources/eth/accounts_state.tla +2 -0
- data/lib/resources/eth/accounts_temp.tla +2 -0
- data/lib/resources/eth/address_free.tla +2 -0
- data/lib/resources/eth/mined_state.tla +1 -0
- data/lib/resources/eth/storageRoot.tla +1 -0
- data/lib/resources/eth/storageRoot_temp.tla +1 -0
- data/lib/resources/mine/mine_entry.tla +4 -0
- data/lib/resources/mine/mine_service.tla +22 -0
- data/lib/resources/operators/elementExists.tla +4 -0
- data/lib/resources/operators/gasPrice.tla +2 -0
- data/lib/resources/operators/gasValue.tla +2 -0
- data/lib/resources/operators/getElement.tla +5 -0
- data/lib/resources/operators/intrinsicGas.tla +4 -0
- data/lib/resources/operators/transactionGas.tla +4 -0
- data/lib/resources/operators/upFrontCost.tla +6 -0
- data/lib/resources/personal_newAccount/personal_newAccount_done.tla +14 -0
- data/lib/resources/personal_newAccount/personal_newAccount_entry.tla +10 -0
- data/lib/resources/personal_newAccount/personal_newAccount_service.tla +29 -0
- data/lib/resources/removed/sendTransaction_entry.tla +5 -0
- data/lib/resources/removed/sendTransaction_service.tla +36 -0
- data/lib/resources/removed/tst.tla +1 -0
- data/lib/resources/transaction/ethereum_service_done.tla +24 -0
- data/lib/resources/transaction/ethereum_service_pop.tla +24 -0
- data/lib/resources/transaction/ethereum_service_push.tla +14 -0
- data/lib/resources/transaction/ethereum_service_start.tla +13 -0
- data/lib/resources/transaction/status_fail.tla +1 -0
- data/lib/resources/transaction/status_ok.tla +1 -0
- data/lib/sbuilder-ethereum.rb +52 -0
- data/lib/sbuilder/compile.rb +163 -0
- data/lib/sbuilder/constants.rb +93 -0
- data/lib/sbuilder/exception.rb +22 -0
- data/lib/sbuilder/generate/sexp_processor_tla.rb +2674 -0
- data/lib/sbuilder/generate/tla_element_generator.rb +1206 -0
- data/lib/sbuilder/generate/tla_element_text.rb +703 -0
- data/lib/sbuilder/load.rb +119 -0
- data/lib/sbuilder/mustache/renderer.rb +152 -0
- data/lib/sbuilder/render.rb +141 -0
- data/lib/sbuilder/s.rb +21 -0
- data/lib/sbuilder/sexp_ast.rb +1378 -0
- data/lib/sbuilder/sexp_processor_api.rb +184 -0
- data/lib/sbuilder/sexp_processor_canonize.rb +326 -0
- data/lib/sbuilder/sexp_processor_dataflow.rb +461 -0
- data/lib/sbuilder/sexp_processor_ethereum.rb +127 -0
- data/lib/sbuilder/sexp_processor_need_to_canonize.rb +572 -0
- data/lib/sbuilder/sexp_processor_snippet.rb +154 -0
- data/lib/sbuilder/sexp_processor_symboltable1.rb +296 -0
- data/lib/sbuilder/sexp_processor_symboltable2.rb +175 -0
- data/lib/sbuilder/sexp_utils.rb +417 -0
- data/lib/utils/logger.rb +82 -0
- data/lib/utils/string_inject.rb +11 -0
- data/sbuilder-ethereum.gemspec +39 -0
- metadata +190 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
module Sbuilder
|
2
|
+
module Ethereum
|
3
|
+
|
4
|
+
class Constants
|
5
|
+
|
6
|
+
# ------------------------------------------------------------------
|
7
|
+
# Domains
|
8
|
+
DOMAIN_ADDRESS = "eth_address"
|
9
|
+
DOMAIN_VALUE = "eth_value"
|
10
|
+
DOMAIN_GAS = "eth_gas"
|
11
|
+
DOMAIN_CODE_HASH = "eth_code_hash"
|
12
|
+
DOMAIN_BOOLEAN = "BOOLEAN"
|
13
|
+
DOMAIN_DEFAULT = "default" # when no better candidate not found
|
14
|
+
|
15
|
+
|
16
|
+
# ------------------------------------------------------------------
|
17
|
+
# Symbol table helpers
|
18
|
+
SYMBOL_RETURN_PREFIX="return-"
|
19
|
+
|
20
|
+
|
21
|
+
# ------------------------------------------------------------------
|
22
|
+
# Solidity constans
|
23
|
+
|
24
|
+
SOL_MSG="msg"
|
25
|
+
SOL_NOW="now"
|
26
|
+
SOL_ADDRESS="address"
|
27
|
+
SOL_UINT="uint"
|
28
|
+
SOL_SEND="send"
|
29
|
+
SOL_BOOLEAN_TYPE="bool"
|
30
|
+
|
31
|
+
# ------------------------------------------------------------------
|
32
|
+
# Configuration defaults
|
33
|
+
|
34
|
+
# configuration validation
|
35
|
+
VALIDATION = {
|
36
|
+
:class_configure => {
|
37
|
+
:allowed => %w( solc_command ),
|
38
|
+
:required => %w( ),
|
39
|
+
},
|
40
|
+
:configure => {
|
41
|
+
:allowed => %w( preferences ),
|
42
|
+
:required => %w( ),
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
|
47
|
+
# preferences used - unless otherwise stated
|
48
|
+
DEFAULT_PREFERENCES = {
|
49
|
+
"tla-debug" => false, # debug messages
|
50
|
+
"tla-trace" => false, # framework trace messages
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
# defauls for configuration
|
55
|
+
|
56
|
+
CONFIGURATION_DEFAULTS = {
|
57
|
+
'service_completion' => nil # TLA macro called to complete interface service
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
# ------------------------------------------------------------------
|
62
|
+
# Field names
|
63
|
+
# Contract ID
|
64
|
+
FIELD_NAME_CID = "cid"
|
65
|
+
# Original transactor
|
66
|
+
FIELD_NAME_ORIGINATOR = "originator"
|
67
|
+
# Original transactor
|
68
|
+
FIELD_NAME_SENDER = "sender"
|
69
|
+
# Account ID
|
70
|
+
FIELD_NAME_ADDRESS = "address"
|
71
|
+
# Account ID
|
72
|
+
FIELD_NAME_RECIPIENT = "recipient"
|
73
|
+
# Beneficiary account (in mining)
|
74
|
+
FIELD_NAME_BENEFICIARY = "beneficiary"
|
75
|
+
# Eth VALUE
|
76
|
+
FIELD_NAME_BALANCE = "balance"
|
77
|
+
# Eth VALUE
|
78
|
+
FIELD_NAME_VALUE = "value"
|
79
|
+
# Result
|
80
|
+
FIELD_NAME_RESULT = "result"
|
81
|
+
# Result
|
82
|
+
FIELD_NAME_GAS = "gas"
|
83
|
+
# EVM code (name of class)
|
84
|
+
FIELD_NAME_CODE_HASH = "codeHash"
|
85
|
+
# # Balace on address, inherited to contract
|
86
|
+
# FIELD_NAME_BALANCE = "balance"
|
87
|
+
FIELD_NAME_STATUS = "status"
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sbuilder
|
2
|
+
module Ethereum
|
3
|
+
class SbuilderEtherumException < Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
class SbuilderEtherumScopeException < SbuilderEtherumException
|
7
|
+
end
|
8
|
+
|
9
|
+
class SexpException < SbuilderEtherumException
|
10
|
+
end
|
11
|
+
|
12
|
+
class SexpProcessorDataflowException < SbuilderEtherumException
|
13
|
+
end
|
14
|
+
|
15
|
+
class SbuilderEtherumMustacheException < SbuilderEtherumException
|
16
|
+
end
|
17
|
+
class SbuilderEtherumPluginException < SbuilderEtherumException
|
18
|
+
end
|
19
|
+
class SbuilderEtherumCompileException < SbuilderEtherumException
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,2674 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Sbuilder
|
3
|
+
module Ethereum
|
4
|
+
|
5
|
+
##
|
6
|
+
# Extract snippets and interfaces from SexpAst.
|
7
|
+
#
|
8
|
+
# Assume
|
9
|
+
# - symbols defined && resolved
|
10
|
+
# - dataflows identified
|
11
|
+
#
|
12
|
+
# Actions
|
13
|
+
# - Create Snippets:
|
14
|
+
# -- VariableDeclaration --> TLA_State, :name
|
15
|
+
# -- FunctionDefinition --> TLA_procedure, :name, :body
|
16
|
+
# -- :body:
|
17
|
+
# Return --> :TLA_Return
|
18
|
+
# Assignment --> :TLA_Assignment
|
19
|
+
# Identifier --> :TLA_Identifier
|
20
|
+
|
21
|
+
##
|
22
|
+
# This abstract class manages 'elementGenerator, which knows how
|
23
|
+
# to create TLA language building block (gen_ methods). This class
|
24
|
+
# also manages 'partials' datastructure, which actually know, how
|
25
|
+
# to render s-expressio to TLA building block string. Element
|
26
|
+
# generator is created in entry klass 'SexpProcessorTla', which
|
27
|
+
# passes reference to this one element generator to specialized
|
28
|
+
# klasses e.g. 'FunctionBodyProcessor'.
|
29
|
+
class SexpProcessorTlaRoot < SexpProcessorEthereum
|
30
|
+
|
31
|
+
# ------------------------------------------------------------------
|
32
|
+
# element generator methods
|
33
|
+
|
34
|
+
# @attr [TlaElementGenerator] elementGenerator helper to create
|
35
|
+
# SEXP data elements, and mustache partials to render the SEXP
|
36
|
+
# data elements to TLA language.
|
37
|
+
attr_reader :elementGenerator
|
38
|
+
|
39
|
+
extend Forwardable # for easy delegation
|
40
|
+
|
41
|
+
def_delegators :elementGenerator, # generator methods
|
42
|
+
:requiresLabelAfter, # return true is next statement should have a label
|
43
|
+
:gen_empty_set,
|
44
|
+
:gen_set,
|
45
|
+
:gen_list,
|
46
|
+
:gen_nilElement,
|
47
|
+
:gen_op_set_minus,
|
48
|
+
:gen_operator,
|
49
|
+
:gen_macro_call,
|
50
|
+
:gen_operator_call,
|
51
|
+
:gen_expression2,
|
52
|
+
:gen_identifier,
|
53
|
+
:gen_service_name,
|
54
|
+
:gen_state_variable,
|
55
|
+
:gen_record_definition,
|
56
|
+
:gen_name_op_operand,
|
57
|
+
# names
|
58
|
+
:gen_specname,
|
59
|
+
:gen_interface_input_variable_name,
|
60
|
+
:gen_interface_abort_label,
|
61
|
+
:gen_interface_fail_label,
|
62
|
+
:gen_interface_end_label,
|
63
|
+
:gen_plainname,
|
64
|
+
#
|
65
|
+
:gen_constant,
|
66
|
+
:gen_str,
|
67
|
+
:gen_parenthesis,
|
68
|
+
# :gen_service_entry,
|
69
|
+
:gen_service_procedure,
|
70
|
+
:gen_free_text,
|
71
|
+
:gen_debug_msg,
|
72
|
+
:gen_assert,
|
73
|
+
:gen_commented,
|
74
|
+
:gen_traced_stmt,
|
75
|
+
:gen_labeled_stmt, # add label to stmt
|
76
|
+
:gen_label, # skip which is followed by label
|
77
|
+
:gen_label_it, # wrapts stmt to require label after
|
78
|
+
:gen_goto,
|
79
|
+
:gen_either,
|
80
|
+
:gen_skip,
|
81
|
+
# :gen_check_resume,
|
82
|
+
:gen_assign,
|
83
|
+
:gen_record_field_definition,
|
84
|
+
:gen_record_named_field,
|
85
|
+
:gen_record_field,
|
86
|
+
:gen_stmts,
|
87
|
+
:gen_operator_dummy,
|
88
|
+
:gen_construct_contract,
|
89
|
+
:gen_new_record,
|
90
|
+
:gen_procedure_call,
|
91
|
+
:gen_if,
|
92
|
+
:gen_not,
|
93
|
+
:gen_IF,
|
94
|
+
:gen_TRUE,
|
95
|
+
:gen_FALSE,
|
96
|
+
:gen_EXCEPT,
|
97
|
+
:gen_and,
|
98
|
+
:gen_bin_op,
|
99
|
+
:gen_unary_op,
|
100
|
+
:gen_set_iterate,
|
101
|
+
:gen_set_map,
|
102
|
+
:gen_macro
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
# ------------------------------------------------------------------
|
107
|
+
# Attributes set in configuration
|
108
|
+
|
109
|
+
# @attr [Hash] options set in initialize
|
110
|
+
attr_accessor :options
|
111
|
+
|
112
|
+
|
113
|
+
def initialize( options )
|
114
|
+
super( options )
|
115
|
+
@options = options
|
116
|
+
|
117
|
+
# visitors just recurses - do not shift sexp (as sexp_processor default does)
|
118
|
+
self.default_method = :process_skip
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# ------------------------------------------------------------------
|
123
|
+
# Utitities callable from base klasses
|
124
|
+
|
125
|
+
##
|
126
|
+
# Address of storage root - support transaction
|
127
|
+
def g_storageRoot
|
128
|
+
gen_specname(:eth, TlaElementText::FW_STATE_STORAGE_ROOT)
|
129
|
+
end
|
130
|
+
|
131
|
+
def g_storageRoot_temp
|
132
|
+
gen_specname(:eth, TlaElementText::FW_STATE_STORAGE_ROOT_TEMP)
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# storage root to use in execution
|
137
|
+
def g_storageRoot_exec
|
138
|
+
gen_operator_call( 'Head', g_storageRoot_temp )
|
139
|
+
end
|
140
|
+
|
141
|
+
def g_dispatcherMeta
|
142
|
+
:dispatcher
|
143
|
+
end
|
144
|
+
def g_dispatcherReturnMeta
|
145
|
+
:dispatcher_ret
|
146
|
+
end
|
147
|
+
|
148
|
+
# @return [sTla] for 'dispatcherName' macro
|
149
|
+
def g_dispatcher( dispatcherName )
|
150
|
+
gen_specname( g_dispatcherMeta, dispatcherName )
|
151
|
+
end
|
152
|
+
|
153
|
+
def g_dispatcherReturn( dispatcherName )
|
154
|
+
gen_specname( g_dispatcherReturnMeta, dispatcherName )
|
155
|
+
end
|
156
|
+
|
157
|
+
# def g_ethGasUsed
|
158
|
+
# gen_specname(:eth, TlaElementText::FW_STATE_GAS_USED )
|
159
|
+
# end
|
160
|
+
|
161
|
+
# def g_ethGasUsed_temp
|
162
|
+
# gen_specname(:eth, TlaElementText::FW_STATE_GAS_USED_TEMP )
|
163
|
+
# end
|
164
|
+
|
165
|
+
# def g_ethGasUsed_exec
|
166
|
+
# gen_operator_call( 'Head', g_ethGasUsed_temp )
|
167
|
+
# end
|
168
|
+
|
169
|
+
# def g_ethGasUsed_temp
|
170
|
+
# gen_specname(:eth, TlaElementText::FW_STATE_GAS_USED_TEMP )
|
171
|
+
# end
|
172
|
+
|
173
|
+
def g_ethAccounts
|
174
|
+
gen_specname(:eth, TlaElementText::FW_STATE_ACCOUNTS )
|
175
|
+
end
|
176
|
+
|
177
|
+
def g_ethAccounts_temp
|
178
|
+
gen_specname(:eth, TlaElementText::FW_STATE_ACCOUNTS_TEMP )
|
179
|
+
end
|
180
|
+
|
181
|
+
def g_ethAccounts_exec
|
182
|
+
gen_operator_call( 'Head', g_ethAccounts_temp )
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
# stack = <<value> \o Tail( stack )
|
187
|
+
def gen_assignOnTop( stackElement, value )
|
188
|
+
gen_assign(
|
189
|
+
stackElement,
|
190
|
+
gen_operator_call( TlaElementText::OP_INFRA_UPDATE_TOP, s(stackElement, value ))
|
191
|
+
)
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
##
|
197
|
+
# Utilitiy operator stackVariable := valtoAssing \o Tail(
|
198
|
+
# g_blockStack ). Direct replacement to gen_assign
|
199
|
+
def g_assignOnTop( stackVariable, valtoAssing )
|
200
|
+
gen_assign(
|
201
|
+
stackVariable,
|
202
|
+
gen_operator_call( 'Push', s(gen_operator_call("Tail", stackVariable ), valtoAssing)))
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Make it permanent
|
207
|
+
def g_assignOnStack( stackVariable, valtoAssing )
|
208
|
+
gen_assign(
|
209
|
+
stackVariable,
|
210
|
+
gen_operator_call(
|
211
|
+
'Push',
|
212
|
+
s(
|
213
|
+
gen_operator_call( 'PropageOnStack', s(gen_operator_call("Tail", stackVariable ), valtoAssing)),
|
214
|
+
valtoAssing
|
215
|
+
)))
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
# def g_useIntrinsicGas( interfaceName, service_formal_parameter_name )
|
220
|
+
# sender_account = gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_SENDER)
|
221
|
+
# gasValue = g_gasToValue(gen_operator_call( TlaElementText::ETH_OPEATOR_INTRINSIC_GAS) )
|
222
|
+
# accountsUpdated = gen_set_map(
|
223
|
+
# gen_IF( gen_bin_op(
|
224
|
+
# "=",
|
225
|
+
# gen_record_field( gen_plainname('a'), Constants::FIELD_NAME_ADDRESS),
|
226
|
+
# sender_account
|
227
|
+
# ),
|
228
|
+
# gen_EXCEPT( "a",
|
229
|
+
# gen_bin_op("=",
|
230
|
+
# gen_record_field( "!", Constants::FIELD_NAME_BALANCE),
|
231
|
+
# gen_bin_op(
|
232
|
+
# "-",
|
233
|
+
# gen_record_field( 'a', Constants::FIELD_NAME_BALANCE),
|
234
|
+
# gasValue
|
235
|
+
# ))),
|
236
|
+
# gen_plainname('a')),
|
237
|
+
# gen_set_iterate( "a", g_ethAccounts_exec ))
|
238
|
+
|
239
|
+
# s(
|
240
|
+
# # eth_gasUsed += intrinsicGas
|
241
|
+
# gen_traced_stmt(
|
242
|
+
# g_assignOnStack( g_ethGasUsed_temp,
|
243
|
+
# gen_bin_op( '+', g_ethGasUsed_exec, gasValue )),
|
244
|
+
# s( "SVC-exec@#{interfaceName}: credit #{TlaElementText::FW_STATE_GAS_USED} with value for #{TlaElementText::ETH_OPEATOR_INTRINSIC_GAS}", gasValue )
|
245
|
+
# ),
|
246
|
+
|
247
|
+
|
248
|
+
# # eth_accounts := { IF a.account = i.sender THEN [ a EXCEPT !.balance = a.balance - instrinc ] ELSE a : a \in eth_accounts }
|
249
|
+
# gen_traced_stmt(
|
250
|
+
# g_assignOnStack( g_ethAccounts_temp, accountsUpdated ),
|
251
|
+
# s( "SVC-exec@#{interfaceName}: debit sender account", sender_account, "with gasValue", gasValue )
|
252
|
+
# ),
|
253
|
+
|
254
|
+
# ##
|
255
|
+
# # make changes perament so that they persist rollback
|
256
|
+
|
257
|
+
# gen_traced_stmt(
|
258
|
+
# gen_assign( g_ethGasUsed, g_ethGasUsed_exec ),
|
259
|
+
# s( "SVC-exec@#{interfaceName}: persist credit #{TlaElementText::FW_STATE_GAS_USED}", gasValue )
|
260
|
+
# ),
|
261
|
+
|
262
|
+
# gen_traced_stmt(
|
263
|
+
# gen_assign( g_ethAccounts, g_ethAccounts_exec ),
|
264
|
+
# s( "SVC-exec@#{interfaceName}: persist debit sender account", sender_account )
|
265
|
+
# ),
|
266
|
+
|
267
|
+
# )
|
268
|
+
# end
|
269
|
+
|
270
|
+
def g_persist()
|
271
|
+
|
272
|
+
s(
|
273
|
+
# gen_traced_stmt(
|
274
|
+
# gen_assign( g_ethGasUsed, g_ethGasUsed_exec ),
|
275
|
+
# s( "SVC-exec@#{interfaceName}: persist #{TlaElementText::FW_STATE_GAS_USED}" )
|
276
|
+
# ),
|
277
|
+
|
278
|
+
gen_traced_stmt(
|
279
|
+
gen_assign( g_ethAccounts, g_ethAccounts_exec ),
|
280
|
+
s( "SVC-exec@#{interfaceName}: persist #{TlaElementText::FW_STATE_ACCOUNTS}" )
|
281
|
+
),
|
282
|
+
|
283
|
+
)
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
|
288
|
+
# Call servicve procedure
|
289
|
+
def g_serviceExec( interfaceName, service_formal_parameter_name )
|
290
|
+
s(
|
291
|
+
|
292
|
+
# call procecedure implementing procedule
|
293
|
+
gen_traced_stmt(
|
294
|
+
gen_procedure_call(gen_specname(:solidity_contract_function, interfaceName ), s(gen_identifier(service_formal_parameter_name))),
|
295
|
+
s( "TX-start@#{interfaceName}: call call service implementation", gen_str(gen_specname(:solidity_contract_function, interfaceName )) )
|
296
|
+
),
|
297
|
+
)
|
298
|
+
end
|
299
|
+
|
300
|
+
##
|
301
|
+
# Allways true i.e. TRUE
|
302
|
+
def g_TRUE
|
303
|
+
gen_TRUE
|
304
|
+
end
|
305
|
+
|
306
|
+
##
|
307
|
+
# Operator call to check that recipient account non nil. NOTICE
|
308
|
+
# called from entry macro, and must use 'g_ethAccounts'
|
309
|
+
def g_RecipientAccountExists( service_formal_parameter_name )
|
310
|
+
gen_bin_op(
|
311
|
+
"#",
|
312
|
+
gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_RECIPIENT),
|
313
|
+
gen_nilElement
|
314
|
+
)
|
315
|
+
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# Operator call to check that recipient contract exists
|
320
|
+
def g_RecipientContractExists( bindAddress )
|
321
|
+
bindAddress = gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_RECIPIENT) if bindAddress.nil?
|
322
|
+
gen_operator_call(
|
323
|
+
TlaElementText::FW_OP_ELEMENT_EXISTS,
|
324
|
+
s( g_storageRoot_exec,
|
325
|
+
gen_str( Constants::FIELD_NAME_ADDRESS ),
|
326
|
+
bindAddress,
|
327
|
+
))
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
# Possible to start transaction?
|
332
|
+
#
|
333
|
+
# - if non constror we require that g_RecipientAccountExists
|
334
|
+
# - in any case
|
335
|
+
# -- sender must account must exist
|
336
|
+
# -- senaer account holds enough balance
|
337
|
+
|
338
|
+
def g_serviceExecValidity( service_formal_parameter_name, isConstructor )
|
339
|
+
|
340
|
+
condition =
|
341
|
+
gen_and(
|
342
|
+
# Check account for sender exists
|
343
|
+
gen_operator_call(
|
344
|
+
TlaElementText::FW_OP_ELEMENT_EXISTS,
|
345
|
+
s( g_ethAccounts,
|
346
|
+
gen_str( Constants::FIELD_NAME_ADDRESS ),
|
347
|
+
gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_SENDER),
|
348
|
+
)),
|
349
|
+
# Sender has enough balance in eth_account
|
350
|
+
gen_bin_op(
|
351
|
+
">=",
|
352
|
+
gen_record_field(
|
353
|
+
gen_operator_call(
|
354
|
+
TlaElementText::FW_OP_GET_ELEMENT,
|
355
|
+
s(
|
356
|
+
g_ethAccounts,
|
357
|
+
gen_str( Constants::FIELD_NAME_ADDRESS ),
|
358
|
+
gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_SENDER)
|
359
|
+
)),
|
360
|
+
Constants::FIELD_NAME_BALANCE
|
361
|
+
),
|
362
|
+
# to cover upfront cost
|
363
|
+
gen_operator_call( TlaElementText::ETH_OPEATOR_UP_FRONT_COST, gen_identifier(service_formal_parameter_name))
|
364
|
+
)
|
365
|
+
)
|
366
|
+
|
367
|
+
|
368
|
+
|
369
|
+
# # additional check to ensure that recipent not nil - if non constructor
|
370
|
+
if !isConstructor then
|
371
|
+
condition = gen_and( g_RecipientAccountExists(service_formal_parameter_name), condition )
|
372
|
+
end
|
373
|
+
|
374
|
+
condition
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
# If 'codeHash' of account entry in 'g_ethAccounts_exec'
|
379
|
+
# identified by 'parameterName.recipient' is among
|
380
|
+
# 'contracts.typeName'.
|
381
|
+
#
|
382
|
+
# @param [ContractDefinition:Array] contracts
|
383
|
+
# @param [String] paramterName
|
384
|
+
def g_dispatcherCondition( contracts, parameterName )
|
385
|
+
|
386
|
+
# if_condition:
|
387
|
+
list_of_contracts = contracts.inject( s() ) do |s,contractDefinition|
|
388
|
+
s << gen_str(contractDefinition.typeName)
|
389
|
+
s
|
390
|
+
end
|
391
|
+
|
392
|
+
if_confidition = gen_set_iterate(
|
393
|
+
gen_record_field(
|
394
|
+
gen_operator_call(
|
395
|
+
TlaElementText::FW_OP_GET_ELEMENT,
|
396
|
+
s( g_ethAccounts_exec,
|
397
|
+
gen_str( Constants::FIELD_NAME_ADDRESS ),
|
398
|
+
gen_record_field( parameterName, Constants::FIELD_NAME_RECIPIENT)
|
399
|
+
)),
|
400
|
+
Constants::FIELD_NAME_CODE_HASH
|
401
|
+
),
|
402
|
+
gen_set( gen_list( list_of_contracts ))
|
403
|
+
)
|
404
|
+
|
405
|
+
if_confidition
|
406
|
+
|
407
|
+
end
|
408
|
+
|
409
|
+
|
410
|
+
|
411
|
+
##
|
412
|
+
# Populate address fields for external call
|
413
|
+
def g_procCallAddresses( g_current_formal_input )
|
414
|
+
s(
|
415
|
+
gen_record_field_definition(
|
416
|
+
Constants::FIELD_NAME_SENDER,
|
417
|
+
gen_record_field( g_current_formal_input, Constants::FIELD_NAME_SENDER)),
|
418
|
+
gen_record_field_definition(
|
419
|
+
Constants::FIELD_NAME_ORIGINATOR,
|
420
|
+
gen_record_field( g_current_formal_input, Constants::FIELD_NAME_ORIGINATOR)),
|
421
|
+
)
|
422
|
+
end
|
423
|
+
|
424
|
+
def g_procCallValue( constantValue=0 )
|
425
|
+
s(gen_record_field_definition(
|
426
|
+
Constants::FIELD_NAME_VALUE,
|
427
|
+
gen_constant( constantValue )))
|
428
|
+
end
|
429
|
+
##
|
430
|
+
# create new entry to 'ethStorageRoot'
|
431
|
+
def g_newEntryToStorageRoot( interfaceName, contractFields )
|
432
|
+
s(
|
433
|
+
gen_traced_stmt(
|
434
|
+
gen_assignOnTop(
|
435
|
+
g_storageRoot_temp,
|
436
|
+
gen_bin_op( '\union', gen_set(gen_record_definition(contractFields)), g_storageRoot_exec )
|
437
|
+
),
|
438
|
+
s("SVC-exec@#{interfaceName}: create contract entry to #{TlaElementText::FW_STATE_STORAGE_ROOT_TEMP}")
|
439
|
+
)
|
440
|
+
)
|
441
|
+
end
|
442
|
+
|
443
|
+
# @return [s(record_field_definition)] record field defintion
|
444
|
+
# bind for common message parameters (recipient, sender,
|
445
|
+
# originator)
|
446
|
+
def g_commonMsgParameters( specname_input_variable, recipientTla )
|
447
|
+
s(
|
448
|
+
gen_record_field_definition( Constants::FIELD_NAME_RECIPIENT,
|
449
|
+
recipientTla ),
|
450
|
+
gen_record_field_definition( Constants::FIELD_NAME_SENDER,
|
451
|
+
g_localCurrentAccountId ),
|
452
|
+
gen_record_field_definition( Constants::FIELD_NAME_ORIGINATOR,
|
453
|
+
gen_record_field( specname_input_variable, Constants::FIELD_NAME_ORIGINATOR )),
|
454
|
+
)
|
455
|
+
end
|
456
|
+
|
457
|
+
##
|
458
|
+
# @return InfrastructureServiceGetResponse( "personal_newAccount()" )
|
459
|
+
def g_readNewAccountId
|
460
|
+
gen_operator_call(
|
461
|
+
TlaElementText::OP_INFRA_GET_RESPONSE,
|
462
|
+
gen_str( gen_service_name(TlaElementText::SERVICE_ACCOUNT_NEW))
|
463
|
+
)
|
464
|
+
end
|
465
|
+
|
466
|
+
##
|
467
|
+
# @param [stla] newAddress Nil|address of recipient to create
|
468
|
+
#
|
469
|
+
# @return call 'eth_personal_newAccount( [ codeHash |-> "Demo2", address |-> Nil|address ])'
|
470
|
+
def g_createNewAccount( interfaceName, contractName, newAddress )
|
471
|
+
# s(
|
472
|
+
# gen_procedure_call(
|
473
|
+
# gen_specname( setMetatype(:eth), TlaElementText::INTERFACE_ACCOUNT_NEW),
|
474
|
+
# gen_record_definition(
|
475
|
+
# gen_record_field_definition(
|
476
|
+
# TlaElementText::TLA_RECORD_FIELD_FOR_SINGLE_VALUE,
|
477
|
+
# # use quotes around contractName
|
478
|
+
# gen_str(contractName) ))
|
479
|
+
# )
|
480
|
+
# )
|
481
|
+
s(
|
482
|
+
gen_traced_stmt(
|
483
|
+
gen_procedure_call(
|
484
|
+
gen_specname( setMetatype(:eth), TlaElementText::INTERFACE_ACCOUNT_NEW),
|
485
|
+
gen_record_definition(
|
486
|
+
s(
|
487
|
+
gen_record_field_definition( Constants::FIELD_NAME_CODE_HASH, contractName ),
|
488
|
+
gen_record_field_definition( Constants::FIELD_NAME_ADDRESS, newAddress )
|
489
|
+
))),
|
490
|
+
s("SVC-exec@#{interfaceName}: call '#{TlaElementText::INTERFACE_ACCOUNT_NEW}' to create new account entry")
|
491
|
+
)
|
492
|
+
)
|
493
|
+
end
|
494
|
+
|
495
|
+
##
|
496
|
+
# Put sender address to a well known place stored in stack
|
497
|
+
# locals := 'g_IdRval'
|
498
|
+
def g_copyCurrentAccountIdToLocals( g_IdRval )
|
499
|
+
s(
|
500
|
+
gen_assign( TlaElementText::TLA_LOCALS_VARIABLE, g_IdRval ),
|
501
|
+
)
|
502
|
+
end
|
503
|
+
|
504
|
+
#
|
505
|
+
# @param [String] appLocalName in in application domain
|
506
|
+
# @return [sTla] tlaName for 'appLocalName'
|
507
|
+
def g_locaVariableName( appLocalName )
|
508
|
+
gen_identifier( appLocalName )
|
509
|
+
end
|
510
|
+
|
511
|
+
##
|
512
|
+
# It is in 'locals' variable
|
513
|
+
#
|
514
|
+
# @see g_copyCurrentAccountIdToLocals
|
515
|
+
#
|
516
|
+
def g_localCurrentAccountId
|
517
|
+
# gen_record_field(gen_interface_input_variable_name( interfaceName ), Constants::FIELD_NAME_RECIPIENT )
|
518
|
+
# gen_identifier( TlaElementText::TLA_LOCALS_VARIABLE )
|
519
|
+
g_locaVariableName( TlaElementText::TLA_LOCALS_VARIABLE )
|
520
|
+
end
|
521
|
+
|
522
|
+
# Constant to set to 'responses.status'
|
523
|
+
def g_statusOk
|
524
|
+
gen_operator_call(TlaElementText::TLA_SERVICE_STATUS_OK)
|
525
|
+
end
|
526
|
+
|
527
|
+
# Initial value to set to 'responses.status', something wrong,
|
528
|
+
# if abort seen on return
|
529
|
+
def g_statusAbort
|
530
|
+
gen_operator_call(TlaElementText::TLA_SERVICE_STATUS_ABORT)
|
531
|
+
end
|
532
|
+
|
533
|
+
# Constant to set to 'responses.status'
|
534
|
+
def g_statusFail
|
535
|
+
gen_operator_call(TlaElementText::TLA_SERVICE_STATUS_FAIL)
|
536
|
+
end
|
537
|
+
|
538
|
+
##
|
539
|
+
# SvcReturn( "interfaceName()", status, getRespons("interfaceName()"))
|
540
|
+
def g_updateReturnStatus( interfaceName, status )
|
541
|
+
gen_operator_call(
|
542
|
+
TlaElementText::OP_INFRA_RETURN,
|
543
|
+
s(gen_str(interfaceName ),
|
544
|
+
status,
|
545
|
+
gen_operator_call( TlaElementText::OP_INFRA_GET_RESPONSE, gen_str(interfaceName ))))
|
546
|
+
end
|
547
|
+
|
548
|
+
##
|
549
|
+
# last step in service procesure, just sets status, withoun modifying response value.
|
550
|
+
#
|
551
|
+
# infraReturn( "Demo()", OK, infraRespons( "Demo"))
|
552
|
+
def g_ServiceReturn( interfaceName, status=g_statusOk )
|
553
|
+
s(
|
554
|
+
gen_traced_stmt(
|
555
|
+
g_updateReturnStatus( interfaceName, status ),
|
556
|
+
s( "SVC-exec@#{interfaceName}: set return status", status )
|
557
|
+
)
|
558
|
+
)
|
559
|
+
end
|
560
|
+
|
561
|
+
##
|
562
|
+
# Service procedure starting - push stack
|
563
|
+
def g_svcStackEnter( interfaceName, serviceName )
|
564
|
+
s(
|
565
|
+
gen_label,
|
566
|
+
gen_traced_stmt(
|
567
|
+
# gen_labeled_stmt(
|
568
|
+
gen_macro_call( TlaElementText::TLA_SERVICE_PUSH ),
|
569
|
+
# serviceName, "_push"
|
570
|
+
# ),
|
571
|
+
s( "SVC-exec@#{interfaceName}: init return status", g_statusAbort )))
|
572
|
+
end
|
573
|
+
|
574
|
+
# # E.g. infraReturn( "Demo()", Nil, Nil )
|
575
|
+
# def g_ServiceReturnInit( interfaceName )
|
576
|
+
# s( gen_traced_stmt(
|
577
|
+
# gen_operator_call(
|
578
|
+
# TlaElementText::OP_INFRA_RETURN,
|
579
|
+
# s( gen_str(interfaceName ), g_statusAbort, gen_nilElement )),
|
580
|
+
# s( "SVC-exec@#{interfaceName}: init return status", g_statusAbort )))
|
581
|
+
# end
|
582
|
+
|
583
|
+
##
|
584
|
+
# last step - returs account id created && set status OK
|
585
|
+
def g_constructorReturn( interfaceName )
|
586
|
+
s(
|
587
|
+
# ServiceReturn( "Demo()", 0, new-account-id )
|
588
|
+
gen_traced_stmt(
|
589
|
+
gen_operator_call(
|
590
|
+
TlaElementText::OP_INFRA_RETURN,
|
591
|
+
s(gen_str(interfaceName ), g_statusOk, g_localCurrentAccountId )),
|
592
|
+
s( "SVC-exec@#{interfaceName}: set return status", g_statusOk, "return account id created", g_localCurrentAccountId )
|
593
|
+
)
|
594
|
+
|
595
|
+
)
|
596
|
+
end
|
597
|
+
|
598
|
+
# def g_procedureCall( g_procedure, g_params )
|
599
|
+
# gen_procedure_call(
|
600
|
+
# g_procedure,
|
601
|
+
# gen_record_definition( g_params )
|
602
|
+
# )
|
603
|
+
# end
|
604
|
+
|
605
|
+
# Goto abort label in 'interfaceName' procedure
|
606
|
+
def g_abort( interfaceName, serviceName )
|
607
|
+
gen_goto( gen_interface_abort_label( interfaceName, serviceName ))
|
608
|
+
end
|
609
|
+
|
610
|
+
##
|
611
|
+
# Goto fail label (afte) user code should check for errors
|
612
|
+
def g_fail( interfaceName, serviceName )
|
613
|
+
gen_goto( gen_interface_fail_label( interfaceName ) )
|
614
|
+
end
|
615
|
+
|
616
|
+
def g_end( interfaceName, serviceName, status )
|
617
|
+
s(
|
618
|
+
gen_labeled_stmt(
|
619
|
+
g_updateReturnStatus( interfaceName, status ),
|
620
|
+
gen_specname(:solidity_contract_function, interfaceName ),
|
621
|
+
"call_construct"
|
622
|
+
),
|
623
|
+
gen_goto( gen_interface_end_label( interfaceName )),
|
624
|
+
)
|
625
|
+
end
|
626
|
+
|
627
|
+
# Geneate code to dispatch a call to 'functionCalled' with
|
628
|
+
# 'parameters'. After the call, a jump to abort label in
|
629
|
+
# 'interfaceName'/'serviceName' is called function resulted to
|
630
|
+
# abort.
|
631
|
+
#
|
632
|
+
# Use direct call to 'functionCalled.interfaceName', if
|
633
|
+
# 'findMethodsToDispatch' returns a singleton method, else call
|
634
|
+
# dispatcher macro, also returned by 'findMethodsToDispatch'.
|
635
|
+
def g_dispatch_call( functionCalled, parameters, interfaceName, serviceName )
|
636
|
+
|
637
|
+
dispatcherName, methodsToDispatch = findMethodsToDispatch( functionCalled )
|
638
|
+
|
639
|
+
if methodsToDispatch.length == 1 then
|
640
|
+
# only one function to consider
|
641
|
+
called = gen_specname( :solidity_contract_function, functionCalled.interfaceName )
|
642
|
+
s(
|
643
|
+
gen_procedure_call( called, parameters ),
|
644
|
+
g_abortCheckIncall( interfaceName, serviceName, functionCalled.interfaceName)
|
645
|
+
)
|
646
|
+
else
|
647
|
+
|
648
|
+
# multiple functions - check status of all of the function
|
649
|
+
# (assumes that dispatcher macro resets statuses )
|
650
|
+
if_condition = gen_FALSE
|
651
|
+
|
652
|
+
methodsToDispatch.map do |methodToDispatch|
|
653
|
+
methodToDispatch[:function].interfaceName
|
654
|
+
end.uniq.each do |interfaceName|
|
655
|
+
if_condition =
|
656
|
+
gen_bin_op( '\\/',
|
657
|
+
if_condition,
|
658
|
+
g_abortedInCall( interfaceName )
|
659
|
+
)
|
660
|
+
end
|
661
|
+
if_block = s( g_abort( interfaceName, serviceName ))
|
662
|
+
|
663
|
+
s(
|
664
|
+
gen_label_it(gen_macro_call( g_dispatcher(dispatcherName), parameters )),
|
665
|
+
gen_if( if_condition, if_block )
|
666
|
+
|
667
|
+
)
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
# Geneate code to read response from 'functionCalled'.
|
672
|
+
#
|
673
|
+
# If 'functionCalled' is implemented by just one contract, the
|
674
|
+
# is done using a simple 'InfrastructureServiceGetResponse' call
|
675
|
+
# for functionCalled.interfaceName. If 'functionCalled' is
|
676
|
+
# implemented in serveral contracts, we take the result from
|
677
|
+
# where 'InfrastructureServiceGetStatus' is not Nil.
|
678
|
+
|
679
|
+
|
680
|
+
def g_dispatch_read_return( functionCalled )
|
681
|
+
logger.debug "#{__method__}: functionCall:#{functionCalled}" if logger.debug?
|
682
|
+
|
683
|
+
dispatcherName, methodsToDispatch = findMethodsToDispatch( functionCalled )
|
684
|
+
if methodsToDispatch.length == 1 then
|
685
|
+
|
686
|
+
readReturn = gen_operator_call( TlaElementText::OP_INFRA_GET_RESPONSE, gen_str( functionCalled.interfaceName ))
|
687
|
+
else
|
688
|
+
|
689
|
+
actions = methodsToDispatch.map do |methodToDispatch|
|
690
|
+
methodToDispatch[:function].interfaceName
|
691
|
+
end.uniq.map do |interfaceName|
|
692
|
+
{
|
693
|
+
:condition => gen_bin_op( '#', gen_operator_call( TlaElementText::OP_INFRA_GET_STATUS, gen_str(interfaceName)), gen_nilElement ),
|
694
|
+
:action => gen_operator_call( TlaElementText::OP_INFRA_GET_RESPONSE, gen_str(interfaceName )),
|
695
|
+
}
|
696
|
+
end
|
697
|
+
|
698
|
+
# reverse array - last
|
699
|
+
readReturn = actions.reverse.inject( actions.last[:action] ) do |m,a|
|
700
|
+
r = gen_IF( a[:condition], a[:action], m )
|
701
|
+
r
|
702
|
+
end
|
703
|
+
|
704
|
+
# raise "Not yet implemeteded"
|
705
|
+
end
|
706
|
+
readReturn
|
707
|
+
|
708
|
+
end
|
709
|
+
|
710
|
+
def g_abortedInCall( called_interfaceName )
|
711
|
+
gen_bin_op("=",g_statusAbort, gen_operator_call( TlaElementText::OP_INFRA_GET_STATUS, gen_str(called_interfaceName)))
|
712
|
+
end
|
713
|
+
|
714
|
+
##
|
715
|
+
# Jump to 'g_abort('interfaceName', 'serviceName' )' -label if
|
716
|
+
# 'g_abortedInCall( called_interfaceName )'
|
717
|
+
def g_abortCheckIncall( interfaceName, serviceName, called_interfaceName )
|
718
|
+
gen_if( g_abortedInCall( called_interfaceName ), g_abort(interfaceName, serviceName ))
|
719
|
+
end
|
720
|
+
|
721
|
+
##
|
722
|
+
# @param [stla] newAddress Nil|address of recipient to create
|
723
|
+
#
|
724
|
+
# consrructo - user in constructor and for message call to
|
725
|
+
# undefined recipient
|
726
|
+
def g_construct( interfaceName, contractName, contractExp, newAddress )
|
727
|
+
# call personal_newAccounts( [ dummy |-> 'Demo' ] )
|
728
|
+
g_createNewAccount(interfaceName, contractName, newAddress) +
|
729
|
+
|
730
|
+
# locals := ReadResponse('Demo')
|
731
|
+
g_copyCurrentAccountIdToLocals(g_readNewAccountId) +
|
732
|
+
|
733
|
+
# eth_storageRoot := storage \union [ f1 |-> val1, ...]
|
734
|
+
g_newEntryToStorageRoot( interfaceName, u_initContractFields(contractExp))
|
735
|
+
|
736
|
+
end
|
737
|
+
|
738
|
+
##
|
739
|
+
# Common code for constructor and normal method call
|
740
|
+
#
|
741
|
+
def g_serviceProcedurePreamble( interfaceName, serviceName, specname_input_variable, isContract )
|
742
|
+
|
743
|
+
# specname_input_variable = gen_interface_input_variable_name( interfaceName )
|
744
|
+
|
745
|
+
# ether balance of sender - on block stack
|
746
|
+
sender_balance = gen_record_field(
|
747
|
+
gen_operator_call(
|
748
|
+
TlaElementText::FW_OP_GET_ELEMENT,
|
749
|
+
s( g_ethAccounts_exec,
|
750
|
+
gen_str( Constants::FIELD_NAME_ADDRESS ),
|
751
|
+
gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER )
|
752
|
+
)),
|
753
|
+
Constants::FIELD_NAME_BALANCE)
|
754
|
+
|
755
|
+
# needed value == tx-gas*gas-price + msg.value
|
756
|
+
value_needed_expr =
|
757
|
+
gen_parenthesis(gen_bin_op(
|
758
|
+
'+',
|
759
|
+
g_gasToValue( gen_operator_call( TlaElementText::ETH_OPEATOR_TRANSACTION_GAS)),
|
760
|
+
gen_record_field( specname_input_variable, Constants::FIELD_NAME_VALUE)))
|
761
|
+
|
762
|
+
request_value_expr = gen_record_field( specname_input_variable, Constants::FIELD_NAME_VALUE)
|
763
|
+
|
764
|
+
account_id_sending = gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER )
|
765
|
+
|
766
|
+
|
767
|
+
# Return( "Demo()", Nil, Nil )
|
768
|
+
# TODO remove - unecassary
|
769
|
+
# g_ServiceReturnInit( interfaceName ) +
|
770
|
+
|
771
|
+
# ( isContract ? g_useIntrinsicGas( interfaceName, specname_input_variable ) : s()) +
|
772
|
+
( isContract ? g_persist() : s() ) +
|
773
|
+
|
774
|
+
g_svcStackEnter( interfaceName, serviceName ) +
|
775
|
+
|
776
|
+
# model non-deterministic choice of out of gas
|
777
|
+
s(
|
778
|
+
gen_traced_stmt(
|
779
|
+
gen_either( gen_skip, g_abort( interfaceName, serviceName )),
|
780
|
+
s( "SVC-exec@#{interfaceName}: non-deterministic out-of-gas-check" )
|
781
|
+
)
|
782
|
+
) +
|
783
|
+
|
784
|
+
( isContract ?
|
785
|
+
s(
|
786
|
+
# if eth_account[send].balance < transactionGas goto abort
|
787
|
+
gen_traced_stmt(
|
788
|
+
gen_if(
|
789
|
+
gen_bin_op( "<", sender_balance, value_needed_expr ),
|
790
|
+
gen_traced_stmt(
|
791
|
+
g_abort( interfaceName, serviceName ),
|
792
|
+
s( "SVC-EXEC#{interfaceName}: abort" ) # then
|
793
|
+
)),
|
794
|
+
s(
|
795
|
+
"SVC-exec@#{interfaceName}: check sender balance >= #{TlaElementText::ETH_OPEATOR_GAS_VALUE}(#{TlaElementText::ETH_OPEATOR_TRANSACTION_GAS}) + #{Constants::FIELD_NAME_VALUE}",
|
796
|
+
gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER ),
|
797
|
+
"balance", sender_balance,
|
798
|
+
"value needed", value_needed_expr,
|
799
|
+
) # trace message
|
800
|
+
),
|
801
|
+
|
802
|
+
# goto requires a label here
|
803
|
+
# gen_labeled_stmt( gen_skip, specname_functionName, "_value_checked" ),
|
804
|
+
gen_skip,
|
805
|
+
|
806
|
+
) # s
|
807
|
+
: s()
|
808
|
+
) # isContract?
|
809
|
+
end
|
810
|
+
|
811
|
+
|
812
|
+
##
|
813
|
+
# Move value to 'account_receiving_value' (new account in
|
814
|
+
# constructor, recipient in normal call) from
|
815
|
+
# 'account_id_sending' (=sender).
|
816
|
+
#
|
817
|
+
# Implementation notice: no need to do anything if
|
818
|
+
# 'account_receiving' == 'account_sending'. As a matter of fact,
|
819
|
+
# the code used to do the actual move would result to
|
820
|
+
# duplicating the balance.
|
821
|
+
#
|
822
|
+
# @param [SexpTla] account_id_receiving_value defines account recveiving value
|
823
|
+
#
|
824
|
+
# @return [Sexp:Array] statements to start for the procedure
|
825
|
+
def g_serviceProcedureMoveValue( interfaceName, specname_input_variable, account_id_receiving_value )
|
826
|
+
|
827
|
+
request_value_expr = gen_record_field( specname_input_variable, Constants::FIELD_NAME_VALUE)
|
828
|
+
account_id_sending = gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER )
|
829
|
+
|
830
|
+
s(
|
831
|
+
# eth_accounts[receiver].balance += request.value
|
832
|
+
# eth_accounts[sender].balance -= request.value
|
833
|
+
# move 'value' from account 'input.send' to new account ()
|
834
|
+
gen_if(
|
835
|
+
gen_bin_op( "#", account_id_sending, account_id_receiving_value),
|
836
|
+
# gen_traced_stmt(
|
837
|
+
gen_assignOnTop(
|
838
|
+
g_ethAccounts_temp,
|
839
|
+
gen_set_map(
|
840
|
+
gen_IF(
|
841
|
+
gen_bin_op(
|
842
|
+
"=",
|
843
|
+
gen_record_field( 'a', Constants::FIELD_NAME_ADDRESS),
|
844
|
+
account_id_receiving_value
|
845
|
+
),
|
846
|
+
gen_EXCEPT( "a",
|
847
|
+
gen_bin_op("=",
|
848
|
+
gen_record_field( "!", Constants::FIELD_NAME_BALANCE),
|
849
|
+
gen_bin_op(
|
850
|
+
"+",
|
851
|
+
gen_record_field( "a", Constants::FIELD_NAME_BALANCE),
|
852
|
+
request_value_expr
|
853
|
+
))),
|
854
|
+
gen_IF(
|
855
|
+
gen_bin_op( "=",
|
856
|
+
gen_record_field( 'a', Constants::FIELD_NAME_ADDRESS),
|
857
|
+
account_id_sending) ,
|
858
|
+
gen_EXCEPT( "a",
|
859
|
+
gen_bin_op(
|
860
|
+
"=",
|
861
|
+
gen_record_field( "!", Constants::FIELD_NAME_BALANCE),
|
862
|
+
gen_bin_op(
|
863
|
+
'-',
|
864
|
+
gen_record_field( "a", Constants::FIELD_NAME_BALANCE),
|
865
|
+
request_value_expr
|
866
|
+
)
|
867
|
+
)),
|
868
|
+
'a')),
|
869
|
+
gen_set_iterate( "a", g_ethAccounts_exec ))
|
870
|
+
),
|
871
|
+
# s( "SVC-exec@#{interfaceName}: move value", request_value_expr, "from sender", account_id_sending, "to receiving", account_id_receiving_value ),
|
872
|
+
# ),
|
873
|
+
),
|
874
|
+
gen_traced_stmt(
|
875
|
+
gen_skip, s("SVC-exec@#{interfaceName}: moved value" )
|
876
|
+
)
|
877
|
+
)
|
878
|
+
end
|
879
|
+
|
880
|
+
##
|
881
|
+
#
|
882
|
+
def g_gasToValue( gas )
|
883
|
+
gen_operator_call(TlaElementText::ETH_OPEATOR_GAS_VALUE, gas )
|
884
|
+
end
|
885
|
+
|
886
|
+
|
887
|
+
##
|
888
|
+
# @return [Sexp:Array] statements to end the procedure
|
889
|
+
def g_serviceProcedureEpilogue( interfaceName, serviceName, specname_input_variable )
|
890
|
+
s(
|
891
|
+
# Skip with label so that state changes generated here do
|
892
|
+
# not interfere with rest of the code
|
893
|
+
gen_label,
|
894
|
+
)
|
895
|
+
|
896
|
+
# expr_transaction_gasValue = g_gasToValue(gen_operator_call( TlaElementText::ETH_OPEATOR_TRANSACTION_GAS))
|
897
|
+
|
898
|
+
# # specname_accounts = gen_specname(:eth, TlaElementText::FW_STATE_ACCOUNTS)
|
899
|
+
|
900
|
+
# s(
|
901
|
+
# # Skip with label so that state changes generated here do
|
902
|
+
# # not interfere with rest of the code
|
903
|
+
# gen_label,
|
904
|
+
|
905
|
+
# # increment 'eth_gasUsed' for running the transaction
|
906
|
+
# gen_traced_stmt(
|
907
|
+
# gen_assignOnTop(g_ethGasUsed_temp, gen_bin_op( '+', g_ethGasUsed_exec, expr_transaction_gasValue )),
|
908
|
+
# s( "SVC-exec@#{interfaceName}: credit '#{TlaElementText::ETH_OPEATOR_TRANSACTION_GAS}' for transaction gas value", expr_transaction_gasValue )
|
909
|
+
# ),
|
910
|
+
|
911
|
+
# # decreas gasUsed from sender account
|
912
|
+
# gen_traced_stmt(
|
913
|
+
# gen_assignOnTop(
|
914
|
+
# g_ethAccounts_temp,
|
915
|
+
# gen_set_map(
|
916
|
+
# gen_IF(
|
917
|
+
# gen_bin_op( "=", gen_record_field(specname_input_variable, Constants::FIELD_NAME_SENDER), gen_record_field( "a", Constants::FIELD_NAME_ADDRESS ) ),
|
918
|
+
# gen_EXCEPT(
|
919
|
+
# 'a',
|
920
|
+
# gen_bin_op( "=",
|
921
|
+
# gen_record_field("!", Constants::FIELD_NAME_BALANCE),
|
922
|
+
# gen_bin_op( "-", gen_record_field( "a", Constants::FIELD_NAME_BALANCE ), expr_transaction_gasValue ) )),
|
923
|
+
# 'a'),
|
924
|
+
# gen_set_iterate("a", g_ethAccounts_exec ))
|
925
|
+
# ),
|
926
|
+
# s( "SVC-exec@#{interfaceName}: debit sender", gen_record_field(specname_input_variable, Constants::FIELD_NAME_SENDER), "for transaction gas value", expr_transaction_gasValue )
|
927
|
+
# ),
|
928
|
+
# )
|
929
|
+
end
|
930
|
+
|
931
|
+
# ------------------------------------------------------------------
|
932
|
+
# @!group Utilities
|
933
|
+
|
934
|
+
def g_zero
|
935
|
+
gen_plainname('0')
|
936
|
+
end
|
937
|
+
|
938
|
+
#
|
939
|
+
# @return [Hash:Array] params
|
940
|
+
# @option params [:Symbol] :name
|
941
|
+
def u_functionParameters( functionDecl )
|
942
|
+
functionDecl.parameterList.parameters.map { |param| { :name => param.name } }
|
943
|
+
end
|
944
|
+
|
945
|
+
def u_initValue( typeName )
|
946
|
+
TlaElementText.solIsNumericType(typeName) ? g_zero : gen_nilElement
|
947
|
+
end
|
948
|
+
|
949
|
+
def u_initContractFields( contractExp )
|
950
|
+
contractExp.contractVariables.map do |v|
|
951
|
+
logger.debug "#{__method__}: contract variable=#{v}" if logger.debug?
|
952
|
+
|
953
|
+
# process VariableDeclaration
|
954
|
+
if v.name == Constants::FIELD_NAME_ADDRESS
|
955
|
+
# access return from 'newAccount'
|
956
|
+
{ :name => v.name, :value=> g_readNewAccountId }
|
957
|
+
else
|
958
|
+
if v.isMapping then
|
959
|
+
|
960
|
+
# mapping variable input domain name e.g. d_eth_address
|
961
|
+
inputDomainName = v.typeSexp.mapFrom.domainReference.domainName
|
962
|
+
# numeric solidity types init with 0, default to use Nil
|
963
|
+
# initialValue = TlaElementText.solIsNumericType(v.typeSexp.mapTo.typeName) ? gen_plainname('0') : gen_nilElement
|
964
|
+
initialValue = u_initValue( v.typeSexp.mapTo.typeName )
|
965
|
+
|
966
|
+
|
967
|
+
# e.g. [ i \in d_eth_address |-> Nil ] or [ i \in d_eth_address |-> 0 ]
|
968
|
+
initMapping = gen_record_definition(
|
969
|
+
gen_record_field_definition(
|
970
|
+
gen_set_iterate( 'a',
|
971
|
+
gen_specname( setMetatype(Sbuilder::Facade::META_MODEL_DOMAINS), inputDomainName )),
|
972
|
+
initialValue )
|
973
|
+
)
|
974
|
+
{
|
975
|
+
:name => v.name,
|
976
|
+
:value => initMapping,
|
977
|
+
}
|
978
|
+
else
|
979
|
+
{ :name => v.name, :value => u_initValue( v.typeSexp.typeName ) }
|
980
|
+
end
|
981
|
+
end
|
982
|
+
end.inject( s() ) do |memo,nv|
|
983
|
+
memo << gen_name_op_operand( nv[:name], "|->", nv[:value] )
|
984
|
+
memo
|
985
|
+
end
|
986
|
+
end
|
987
|
+
|
988
|
+
|
989
|
+
|
990
|
+
|
991
|
+
|
992
|
+
|
993
|
+
# @!endgroup
|
994
|
+
|
995
|
+
end
|
996
|
+
|
997
|
+
# This created in plugin
|
998
|
+
class SexpProcessorTla < SexpProcessorTlaRoot
|
999
|
+
|
1000
|
+
|
1001
|
+
# Constant to initialize partials hash. Particularly, add
|
1002
|
+
# mapping for :Snippet TLA generate element sexp_type, because
|
1003
|
+
# snippets are not processed by #elementGenerator, which is
|
1004
|
+
# responsible for adding mapping entries.
|
1005
|
+
PARTIALS = {
|
1006
|
+
|
1007
|
+
:Snippet => <<-EOS.unindent,
|
1008
|
+
(* {|metatype|}:{|appName|}{|# comment |} - {|{ comment }|}{|/ comment |} *)
|
1009
|
+
{|#snippet|}{|#EVAL|}.{|/EVAL|}{|/snippet|}
|
1010
|
+
EOS
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
# @attr [Hash] baseMetatypes implement in 'sbuilder' framework
|
1014
|
+
# can be referenced in TLA translation
|
1015
|
+
attr_reader :baseMetatypes
|
1016
|
+
|
1017
|
+
# ------------------------------------------------------------------
|
1018
|
+
# @!group Recusion parameters
|
1019
|
+
|
1020
|
+
# @attr [STla] specname_input_variable
|
1021
|
+
attr_accessor :specname_input_variable
|
1022
|
+
|
1023
|
+
# @attr [String] interfaceName
|
1024
|
+
attr_accessor :interfaceName
|
1025
|
+
|
1026
|
+
# @attr [String] serviceName
|
1027
|
+
attr_accessor :serviceName
|
1028
|
+
|
1029
|
+
# @!endgroup
|
1030
|
+
|
1031
|
+
# ------------------------------------------------------------------
|
1032
|
+
# @!group Construct & configure
|
1033
|
+
|
1034
|
+
def initialize( metatypes, options={} )
|
1035
|
+
super( options )
|
1036
|
+
|
1037
|
+
# Init partial for snippets , which are not found in SexpAst
|
1038
|
+
@partials = PARTIALS
|
1039
|
+
@collectedSnippetNames = []
|
1040
|
+
@collectedSnippets = []
|
1041
|
+
@collectedInterfaces = []
|
1042
|
+
@collectedDefintions = []
|
1043
|
+
|
1044
|
+
logger.info "#{__method__} init-metatypes #{metatypes}"
|
1045
|
+
# allow access to frame work metatypesa
|
1046
|
+
setBaseMetatypes( metatypes )
|
1047
|
+
|
1048
|
+
# gen_x method + adds partials
|
1049
|
+
initElementGenerator
|
1050
|
+
|
1051
|
+
# load only once
|
1052
|
+
loadFixedSnippets
|
1053
|
+
loadEthereumSnippets
|
1054
|
+
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
# configure +baseMetatypes+ in sbuilder immediatelly after constructuor
|
1058
|
+
#
|
1059
|
+
# @param [Hash] baseMetatypes
|
1060
|
+
# @option baseMetatypes [:Symbol] :name
|
1061
|
+
# @option baseMetatypes [:Symbol] :desc
|
1062
|
+
private def setBaseMetatypes( metatypes )
|
1063
|
+
@baseMetatypes = metatypes
|
1064
|
+
logger.debug( "#{__method__}: baseMetatypes=#{baseMetatypes}" ) if logger.debug?
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
private def initElementGenerator
|
1068
|
+
|
1069
|
+
# gen_* methods delegated here
|
1070
|
+
@elementGenerator = TlaElementGenerator.new( options )
|
1071
|
+
@elementGenerator.setPartials( partials )
|
1072
|
+
@elementGenerator.setValidMetatypes( validMetatypes )
|
1073
|
+
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
|
1077
|
+
# @!endgroup
|
1078
|
+
|
1079
|
+
# ------------------------------------------------------------------
|
1080
|
+
# @!group Scoped
|
1081
|
+
|
1082
|
+
# @attr [Contract] currentContract which currently processing
|
1083
|
+
attr_accessor :currentContract
|
1084
|
+
|
1085
|
+
def contracted( expContract )
|
1086
|
+
|
1087
|
+
self.currentContract = expContract
|
1088
|
+
begin
|
1089
|
+
yield
|
1090
|
+
ensure
|
1091
|
+
self.currentContract = nil
|
1092
|
+
end
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
# @!endgroup
|
1096
|
+
|
1097
|
+
|
1098
|
+
# ------------------------------------------------------------------
|
1099
|
+
# @!group Routines to build module result
|
1100
|
+
|
1101
|
+
# @attr [Hash] partials mapping tla generate sexp to mustache
|
1102
|
+
# template, created in this module
|
1103
|
+
attr_reader :partials
|
1104
|
+
|
1105
|
+
##
|
1106
|
+
# Return partials created in #process -method.
|
1107
|
+
#
|
1108
|
+
# @return [Hash] partials mapping TLA generate element sexp:s to
|
1109
|
+
# mustache partial strings for rendering TLA generate element.
|
1110
|
+
def getPartials
|
1111
|
+
logger.info "#{__method__}: partials.keys=#{partials.keys.join(',')}"
|
1112
|
+
partials
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
# Add 'snippet' to '@collectedSnippets' unless it is already defined in '@collectedSnippetNames'
|
1116
|
+
def addSnippet( snippet )
|
1117
|
+
logger.debug "#{__method__}: snippet=#{snippet}" if logger.debug?
|
1118
|
+
if ! @collectedSnippetNames.include?( snippet.snippetId )
|
1119
|
+
logger.info "#{__method__}: added metatype='#{snippet.metatype}' appName='#{snippet.name}', specName '#{snippet.specName}': #{snippet.comment}"
|
1120
|
+
@collectedSnippets << snippet
|
1121
|
+
@collectedSnippetNames << snippet.snippetId
|
1122
|
+
else
|
1123
|
+
logger.info "#{__method__}: duplitac snippet ='#{snippet.snippetId} - not added"
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
|
1128
|
+
# @return [Snippets] snippets to pass to snippet loader plugin
|
1129
|
+
def snippets
|
1130
|
+
Snippets.new( @collectedSnippets )
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def addInterface( interface )
|
1134
|
+
logger.info "#{__method__}: interfaceDefinition=#{interface.interfaceDefinition}"
|
1135
|
+
@collectedInterfaces << interface
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
# @return [Interface:Array] interfaces to pass to api loader plugin
|
1139
|
+
def interfaces
|
1140
|
+
Interfaces.new( @collectedInterfaces )
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
|
1144
|
+
def addDefinition( definition )
|
1145
|
+
logger.info "#{__method__}: definition=#{definition}"
|
1146
|
+
@collectedDefintions << definition
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
# @return [Definition:Array] definitiosn to pass to api loader plugin
|
1150
|
+
def definitions
|
1151
|
+
Definitions.new( @collectedDefintions )
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
|
1155
|
+
##
|
1156
|
+
# Include +domainName+ to TLA specicification code
|
1157
|
+
def addDomainName( domainName )
|
1158
|
+
logger.info "#{__method__}: domainName=#{domainName}"
|
1159
|
+
@domainNames ||= []
|
1160
|
+
@domainNames << Domain.s(domainName)
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
##
|
1164
|
+
# Add 'domainValue' to 'domainName'
|
1165
|
+
def addDomainValue( domainName, domainValue )
|
1166
|
+
logger.info "#{__method__}: #{domainName} value= #{domainValue}"
|
1167
|
+
domain = domainNames.find { |d| d.domainName == domainName }
|
1168
|
+
raise "Unknow domain #{domainName} - known domains #{domainNames.join(',')}" if domain.nil?
|
1169
|
+
# Just add it to list
|
1170
|
+
domain << domainValue
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
##
|
1174
|
+
# @return [String:Array] domainNames to define in TLA specification code
|
1175
|
+
def domainNames
|
1176
|
+
@domainNames ||= []
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
# @!endgroup
|
1180
|
+
|
1181
|
+
# ------------------------------------------------------------------
|
1182
|
+
# @!group Access specialized sexp processor
|
1183
|
+
|
1184
|
+
def getFunctionBodyProcessor
|
1185
|
+
proc = FunctionBodyProcessor.new( self, elementGenerator, options )
|
1186
|
+
# Move context to processeos
|
1187
|
+
proc.interfaceName = interfaceName
|
1188
|
+
proc.serviceName = serviceName
|
1189
|
+
proc.currentContract = currentContract
|
1190
|
+
proc.specname_input_variable = specname_input_variable
|
1191
|
+
proc
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
def getFunctionLocalsProcessor
|
1195
|
+
proc = FunctionLocalsProcessor.new( elementGenerator, options )
|
1196
|
+
# Move context to processeos
|
1197
|
+
# proc.interfaceName = interfaceName
|
1198
|
+
# proc.serviceName = serviceName
|
1199
|
+
# proc.specname_input_variable = specname_input_variable
|
1200
|
+
proc
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
|
1204
|
+
# @!endgroup
|
1205
|
+
|
1206
|
+
# ------------------------------------------------------------------
|
1207
|
+
# @!group Metatype management and utilities
|
1208
|
+
|
1209
|
+
##
|
1210
|
+
# @return [Hash] metatypes for ethreum loader
|
1211
|
+
def metatypes
|
1212
|
+
TlaElementText::METATYPES
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
##
|
1216
|
+
# Return cached valid metatypes, whica are +baseMetatypes+ and
|
1217
|
+
# +metatypes+ for ethereum loader plugin
|
1218
|
+
#
|
1219
|
+
# @return [Hash] validMetatypes
|
1220
|
+
# @option [Symbol] :name metatype name
|
1221
|
+
# @option [Symbol] :desc metatype description
|
1222
|
+
def validMetatypes
|
1223
|
+
# baseMetatypes :name/:desc array mapped to a hash
|
1224
|
+
raise "Should have called setBaseMetatypes earlier" if baseMetatypes.nil?
|
1225
|
+
@validMetatypes ||= baseMetatypes.inject( metatypes.clone ) {|memo,mt| memo[mt[:name]] = mt[:desc]; memo }
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
|
1229
|
+
##
|
1230
|
+
# This method needs to sync with the name created when api
|
1231
|
+
# loader plugin loads interface for the +function+.
|
1232
|
+
#
|
1233
|
+
# @param [String] function appName (in application domain)
|
1234
|
+
#
|
1235
|
+
# @return [String] intrefaceName +function+ postfixed with '()'
|
1236
|
+
#
|
1237
|
+
##
|
1238
|
+
# Pass +metatype+ trough, and ensure that it exists in
|
1239
|
+
# +validMetatypes+ i.e. in +metatypes+ defined in this module, and
|
1240
|
+
# in +baseMetatypes+ defined by sbuilder framework
|
1241
|
+
|
1242
|
+
def setMetatype( metatype )
|
1243
|
+
raise SbuilderEtherumException, <<-EOS unless validMetatypes.key?( metatype )
|
1244
|
+
Unknown metatype '#{metatype}' type '#{metatype.class}'
|
1245
|
+
|
1246
|
+
Known metatypes: #{validMetatypes.keys.join(',')}
|
1247
|
+
|
1248
|
+
EOS
|
1249
|
+
|
1250
|
+
metatype
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
|
1254
|
+
# @!endgroup
|
1255
|
+
|
1256
|
+
# ------------------------------------------------------------------
|
1257
|
+
# @!group Load once
|
1258
|
+
|
1259
|
+
# Create constant (technical) base snippets, which do not have
|
1260
|
+
# any connection to TLA translation
|
1261
|
+
def loadFixedSnippets
|
1262
|
+
logger.debug "#{__method__}: starts" if logger.debug?
|
1263
|
+
TlaElementText.fixedSnippetSpecs.each do |snippetDef|
|
1264
|
+
|
1265
|
+
# :metatype, :appName, :specName, :body|:file
|
1266
|
+
|
1267
|
+
metatype = snippetDef[:metatype]
|
1268
|
+
appName = snippetDef[:appName]
|
1269
|
+
# nil --> allow sbuilder to generate name
|
1270
|
+
specName = snippetDef[:specName]
|
1271
|
+
logger.debug "#{__method__}: metatype=#{metatype}, appName=#{appName}" if logger.debug?
|
1272
|
+
raise "Must give :body, :file, :specName #{snippetDef}" unless snippetDef[:body] || snippetDef[:file] || snippetDef[:specName]
|
1273
|
+
text = snippetDef[:body] ? snippetDef[:body] : snippetDef[:file] ? File.read( File.join( File.dirname(__FILE__), "../../resources/", snippetDef[:file] )) : nil
|
1274
|
+
bodySpec = gen_free_text( text ) if text
|
1275
|
+
logger.debug "#{__method__}: text=#{text}" if logger.debug?
|
1276
|
+
addSnippet(
|
1277
|
+
Snippet.new( metatype, appName, bodySpec, specName, snippetDef[:comment] )
|
1278
|
+
)
|
1279
|
+
|
1280
|
+
end
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
##
|
1284
|
+
# Load Ethreum environment domains,snippet, and interfaces into
|
1285
|
+
# sbuilder context.
|
1286
|
+
def loadEthereumSnippets
|
1287
|
+
logger.debug "#{__method__}: starts" if logger.debug?
|
1288
|
+
|
1289
|
+
TlaElementText.ethereumSnippetSpecs.each do |spec|
|
1290
|
+
# run lamdba ':snipetType' and pass parameters ':definition'
|
1291
|
+
# instance_exec( spec[:definition], &SNIPPETLOADERS[spec[:snipetType]] )
|
1292
|
+
instance_exec( spec, &TlaElementText.getLoaderLamdba( spec ))
|
1293
|
+
end
|
1294
|
+
end
|
1295
|
+
|
1296
|
+
# ------------------------------------------------------------------
|
1297
|
+
# @!group Entry point & return the generated snippets
|
1298
|
+
|
1299
|
+
|
1300
|
+
# @return [Domains] domains Sexp, with domain -property
|
1301
|
+
def process_SourceUnit( exp )
|
1302
|
+
logger.info "#{__method__}: starting with '#{exp}"
|
1303
|
+
|
1304
|
+
# # gen_* methods delegated here
|
1305
|
+
# @elementGenerator = TlaElementGenerator.new( options )
|
1306
|
+
# @elementGenerator.setPartials( partials )
|
1307
|
+
# @elementGenerator.setValidMetatypes( validMetatypes )
|
1308
|
+
|
1309
|
+
# traverse tree and create Sexps for snippets & interfaces
|
1310
|
+
results = process_rest( exp )
|
1311
|
+
logger.debug "#{__method__}, results=#{results}" if logger.debug?
|
1312
|
+
|
1313
|
+
# add snippets to framework
|
1314
|
+
# loadFixedSnippets
|
1315
|
+
# loadEthereumSnippets
|
1316
|
+
|
1317
|
+
results
|
1318
|
+
end
|
1319
|
+
|
1320
|
+
def process_GlobalScope( exp )
|
1321
|
+
logger.debug "#{__method__}: starting with '#{exp}" if logger.debug?
|
1322
|
+
|
1323
|
+
# # Create snippets and interfaces for all ':FunctionDefinition' in top-scope
|
1324
|
+
# exp.globalScope.symbolDefinitions.
|
1325
|
+
# select { |globalExp| globalExp.sexp_type == :FunctionDefinition }.
|
1326
|
+
# each { |expFunction| loadGlobalFunction( expFunction ) }
|
1327
|
+
process_rest( exp )
|
1328
|
+
|
1329
|
+
s()
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
|
1333
|
+
def process_ContractDefinition( exp )
|
1334
|
+
logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
|
1335
|
+
|
1336
|
+
# Need to know contract names in 'eth_code_hash'. Used for
|
1337
|
+
# example to define type invariant for valid contract types
|
1338
|
+
addDomainValue( Constants::DOMAIN_CODE_HASH, exp.name )
|
1339
|
+
|
1340
|
+
# add a (function) type definition for mapping variables
|
1341
|
+
exp.contractVariables.each do |variable|
|
1342
|
+
if variable.isMapping
|
1343
|
+
name = TlaElementText.compositeDefinitionName( exp, variable )
|
1344
|
+
mappings = variable.typeSexp.maps.inject( s() ) do |memo, elementaryType|
|
1345
|
+
memo << s( :parameter, elementaryType.typeName, elementaryType.domainReference.domainName )
|
1346
|
+
memo
|
1347
|
+
end
|
1348
|
+
addDefinition( Definition.define( name, mappings, :function ) )
|
1349
|
+
end
|
1350
|
+
end
|
1351
|
+
|
1352
|
+
# contract type definition with record fields corresponding contract variables
|
1353
|
+
contractVariableFields =
|
1354
|
+
exp.contractVariables.inject( s() ) do |memo,variable|
|
1355
|
+
if variable.isMapping
|
1356
|
+
# mapping variables refer function type definition created separately
|
1357
|
+
compositeName = TlaElementText.compositeDefinitionName( exp, variable )
|
1358
|
+
memo << s( :reference, variable.name, compositeName )
|
1359
|
+
else
|
1360
|
+
memo << s( :parameter, variable.name, variable.domainReference.domainName )
|
1361
|
+
end
|
1362
|
+
memo
|
1363
|
+
end
|
1364
|
+
addDefinition( Definition.define( exp.name, contractVariableFields) )
|
1365
|
+
|
1366
|
+
# wrap & recurse
|
1367
|
+
contracted( exp ) do
|
1368
|
+
process_rest( exp )
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
|
1374
|
+
# Dummy operation - needed because default `process_skip` expect
|
1375
|
+
# that each 'process_' -function return either valid data or
|
1376
|
+
# empty array.
|
1377
|
+
def process_Mapping( exp )
|
1378
|
+
s()
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
|
1382
|
+
def process_FunctionDefinition( exp )
|
1383
|
+
logger.debug "#{__method__}: start=#{exp}" if logger.debug?
|
1384
|
+
|
1385
|
+
# should set 'constructor'/'method-call'
|
1386
|
+
messageType = "unknow-function-definition"
|
1387
|
+
|
1388
|
+
# extract context
|
1389
|
+
contractExp = exp.enclosing_scope
|
1390
|
+
contractName = contractExp.name
|
1391
|
+
functionName = exp.name
|
1392
|
+
logger.info "#{__method__}: contractExp=#{contractExp}, contractName=#{contractName}, functionName=#{functionName}"
|
1393
|
+
|
1394
|
+
# context helpers
|
1395
|
+
isContract = contractExp.sexp_type == :ContractDefinition
|
1396
|
+
isConstructor = functionName == contractName ? true : false
|
1397
|
+
|
1398
|
+
|
1399
|
+
# processor context
|
1400
|
+
self.interfaceName = exp.interfaceName
|
1401
|
+
self.serviceName = gen_specname(:solidity_contract_function, interfaceName )
|
1402
|
+
# genenerator defined constants
|
1403
|
+
specname_serviceName = gen_service_name( contractName )
|
1404
|
+
self.specname_input_variable = gen_interface_input_variable_name( interfaceName )
|
1405
|
+
|
1406
|
+
# --------------------
|
1407
|
+
# Body block statements
|
1408
|
+
|
1409
|
+
# Contructor & normal function differ
|
1410
|
+
if isConstructor
|
1411
|
+
# constructor: default constructor + any user specific code
|
1412
|
+
messageType = true # 'constructor'
|
1413
|
+
|
1414
|
+
account_id_receiving_value = g_readNewAccountId
|
1415
|
+
|
1416
|
+
stmts =
|
1417
|
+
g_serviceProcedurePreamble( interfaceName, serviceName, specname_input_variable, isContract) +
|
1418
|
+
|
1419
|
+
g_construct( interfaceName, gen_str(contractName), contractExp, gen_nilElement ) +
|
1420
|
+
|
1421
|
+
g_serviceProcedureMoveValue( interfaceName, specname_input_variable, account_id_receiving_value ) +
|
1422
|
+
s(
|
1423
|
+
gen_traced_stmt(
|
1424
|
+
gen_labeled_stmt( gen_skip, gen_specname(:solidity_contract_function, interfaceName ), "_body"),
|
1425
|
+
s("SVC-exec@#{interfaceName}: process constructor body" )
|
1426
|
+
)
|
1427
|
+
) +
|
1428
|
+
getFunctionBodyProcessor.process( exp.block ) +
|
1429
|
+
g_constructorReturn( interfaceName ) +
|
1430
|
+
g_serviceProcedureEpilogue( interfaceName, serviceName, specname_input_variable )
|
1431
|
+
content = "Constructor for contract #{interfaceName}'"
|
1432
|
+
else
|
1433
|
+
# 'normal' public method-call
|
1434
|
+
messageType = false # 'method-call'
|
1435
|
+
account_id_receiving_value = gen_record_field( specname_input_variable, Constants::FIELD_NAME_RECIPIENT )
|
1436
|
+
stmts =
|
1437
|
+
g_serviceProcedurePreamble( interfaceName, serviceName, specname_input_variable, isContract ) +
|
1438
|
+
# locals := x3_demo_input.recipient
|
1439
|
+
g_copyCurrentAccountIdToLocals(gen_record_field(specname_input_variable, Constants::FIELD_NAME_RECIPIENT )) +
|
1440
|
+
(
|
1441
|
+
isContract ?
|
1442
|
+
s(
|
1443
|
+
# conctract function called for non existing contract
|
1444
|
+
gen_if(
|
1445
|
+
gen_not( gen_operator_call(
|
1446
|
+
TlaElementText::FW_OP_ELEMENT_EXISTS,
|
1447
|
+
s(
|
1448
|
+
g_ethAccounts,
|
1449
|
+
gen_str( Constants::FIELD_NAME_ADDRESS),
|
1450
|
+
gen_record_field( specname_input_variable, Constants::FIELD_NAME_RECIPIENT ))
|
1451
|
+
)),
|
1452
|
+
g_construct( interfaceName, gen_nilElement, contractExp, account_id_receiving_value ) +
|
1453
|
+
g_serviceProcedureMoveValue(interfaceName, specname_input_variable, account_id_receiving_value )+
|
1454
|
+
g_end( interfaceName, serviceName, g_statusOk )
|
1455
|
+
)
|
1456
|
+
) :
|
1457
|
+
# non contract ie. GlobalScope function called - no need to create contract
|
1458
|
+
s()
|
1459
|
+
) +
|
1460
|
+
( isContract ?
|
1461
|
+
s(
|
1462
|
+
# correct type
|
1463
|
+
gen_if(
|
1464
|
+
gen_bin_op(
|
1465
|
+
'\/',
|
1466
|
+
gen_not(g_RecipientContractExists(g_localCurrentAccountId)),
|
1467
|
+
gen_bin_op(
|
1468
|
+
"#",
|
1469
|
+
gen_str(contractName),
|
1470
|
+
gen_record_field(
|
1471
|
+
gen_operator_call(
|
1472
|
+
TlaElementText::FW_OP_GET_ELEMENT,
|
1473
|
+
s(
|
1474
|
+
g_ethAccounts_exec,
|
1475
|
+
gen_str( Constants::FIELD_NAME_ADDRESS ),
|
1476
|
+
g_localCurrentAccountId
|
1477
|
+
)
|
1478
|
+
),
|
1479
|
+
Constants::FIELD_NAME_CODE_HASH),
|
1480
|
+
)),
|
1481
|
+
gen_traced_stmt( g_abort( interfaceName, serviceName ), "SVC-exec@#{interfaceName}: wrong type")
|
1482
|
+
)
|
1483
|
+
) :
|
1484
|
+
s(gen_label)
|
1485
|
+
) +
|
1486
|
+
g_serviceProcedureMoveValue( interfaceName, specname_input_variable, account_id_receiving_value ) +
|
1487
|
+
getFunctionBodyProcessor.process( exp.block ) +
|
1488
|
+
# s(process_rest( exp.statements )) +
|
1489
|
+
( isContract ? g_serviceProcedureEpilogue( interfaceName, serviceName, specname_input_variable ) : s( gen_label )) +
|
1490
|
+
g_ServiceReturn( interfaceName )
|
1491
|
+
|
1492
|
+
|
1493
|
+
content = "Service procedure for #{interfaceName}"
|
1494
|
+
end
|
1495
|
+
|
1496
|
+
logger.debug "#{__method__} stmts=#{stmts}" if logger.debug?
|
1497
|
+
|
1498
|
+
# --------------------
|
1499
|
+
# Local variables
|
1500
|
+
|
1501
|
+
localVariablesExps = getFunctionLocalsProcessor.process( exp.block )
|
1502
|
+
localVariables =
|
1503
|
+
s(g_localCurrentAccountId) +
|
1504
|
+
localVariablesExps.map do |varDecl|
|
1505
|
+
g_locaVariableName( varDecl.name )
|
1506
|
+
end
|
1507
|
+
logger.info "#{__method__}: localVariables=#{localVariables} in in function #{exp.name}, localVariablesExps=#{localVariablesExps} "
|
1508
|
+
|
1509
|
+
|
1510
|
+
# --------------------
|
1511
|
+
# Create snippet for function procedure
|
1512
|
+
|
1513
|
+
addSnippet(
|
1514
|
+
Snippet.new(
|
1515
|
+
setMetatype(:solidity_contract_function), interfaceName,
|
1516
|
+
gen_service_procedure(
|
1517
|
+
gen_specname( :solidity_contract_function, interfaceName),
|
1518
|
+
interfaceName,
|
1519
|
+
localVariables,
|
1520
|
+
gen_stmts(serviceProcedureLabelStatements(
|
1521
|
+
stmts,
|
1522
|
+
gen_specname(:solidity_contract_function, interfaceName )
|
1523
|
+
)) # gen_stmts
|
1524
|
+
), nil, content )
|
1525
|
+
)
|
1526
|
+
|
1527
|
+
# --------------------
|
1528
|
+
# Service entry macro for contract function (only if contract function)
|
1529
|
+
|
1530
|
+
if isContract
|
1531
|
+
# Create service entry macro for Solidity contract function (not for global scope)
|
1532
|
+
#
|
1533
|
+
# macro call 'contract_function' procedure if 'serviceExecValidity'
|
1534
|
+
service_formal_parameter_name = 'par'
|
1535
|
+
addSnippet(
|
1536
|
+
Snippet.new(
|
1537
|
+
setMetatype(Sbuilder::Facade::META_MODEL_SERVICE_IMPLEMENTATION), interfaceName,
|
1538
|
+
gen_macro(
|
1539
|
+
gen_specname( Sbuilder::Facade::META_MODEL_SERVICE_IMPLEMENTATION, interfaceName ),
|
1540
|
+
s(gen_plainname( service_formal_parameter_name )),
|
1541
|
+
gen_stmts(
|
1542
|
+
s(
|
1543
|
+
gen_traced_stmt(
|
1544
|
+
|
1545
|
+
# X_temp := <<X>>;
|
1546
|
+
gen_macro_call(
|
1547
|
+
TlaElementText::TLA_SERVICE_START,
|
1548
|
+
),
|
1549
|
+
s( "TX-start@#{interfaceName}: push block state to stack" )
|
1550
|
+
),
|
1551
|
+
|
1552
|
+
gen_traced_stmt(
|
1553
|
+
gen_if(
|
1554
|
+
g_serviceExecValidity(service_formal_parameter_name, isConstructor),
|
1555
|
+
g_serviceExec(interfaceName, service_formal_parameter_name),
|
1556
|
+
),
|
1557
|
+
s( "TX-start@#{interfaceName}: check pre-conditions",
|
1558
|
+
"1) sender account exists",
|
1559
|
+
"2) sender balance>= #{TlaElementText::ETH_OPEATOR_UP_FRONT_COST}",
|
1560
|
+
!isConstructor ? "3) receive account non nil" : ""
|
1561
|
+
)
|
1562
|
+
)
|
1563
|
+
) # s
|
1564
|
+
)
|
1565
|
+
),
|
1566
|
+
nil, # specname
|
1567
|
+
"Service entry for #{interfaceName}"
|
1568
|
+
))
|
1569
|
+
|
1570
|
+
# Map 'service-completion' on interface to fixed specName
|
1571
|
+
# 'TLA_SERVICE_COMPLETION', which implements transaction
|
1572
|
+
# semantics in TLA code.
|
1573
|
+
addSnippet(
|
1574
|
+
Snippet.new(
|
1575
|
+
setMetatype(Sbuilder::Facade::META_MODEL_SERVICE_COMPLETION), interfaceName, nil,
|
1576
|
+
TlaElementText::TLA_SERVICE_COMPLETION, # specname
|
1577
|
+
"Macro called when #{interfaceName} completes"
|
1578
|
+
))
|
1579
|
+
|
1580
|
+
end # if contract
|
1581
|
+
|
1582
|
+
# --------------------
|
1583
|
+
# Function dispatcher
|
1584
|
+
|
1585
|
+
# check inheritance hierarchy && create dispatcher macro
|
1586
|
+
dispatcherName, methodsToDispatch = findMethodsToDispatch( exp )
|
1587
|
+
addDispatcherSnippet( dispatcherName, methodsToDispatch )
|
1588
|
+
|
1589
|
+
# --------------------
|
1590
|
+
# Interface
|
1591
|
+
|
1592
|
+
# Define an interface for a function +functionName+ in
|
1593
|
+
# contract +contractName+. Contructor name f(+contractName+),
|
1594
|
+
# non constructor name f(+contractName+, +functionName+)
|
1595
|
+
#
|
1596
|
+
#
|
1597
|
+
addInterface(
|
1598
|
+
Interface.define(
|
1599
|
+
contractName,
|
1600
|
+
isConstructor ? "" : functionName,
|
1601
|
+
# request parameters + common parameters
|
1602
|
+
exp.parameterList.parameters.map do |param|
|
1603
|
+
raise "Domain reference not resolved on #{param}" unless param.domainReference
|
1604
|
+
s( :parameter, param.name, param.domainReference.name )
|
1605
|
+
end, # + TlaElementText.commonMsgProperties( 'request', messageType ),
|
1606
|
+
# response paremeters + common parameters
|
1607
|
+
exp.returnList.parameters.map do |param|
|
1608
|
+
raise "Domain reference not resolved on #{param}" unless param.domainReference
|
1609
|
+
s( :parameter, param.name, param.domainReference.name )
|
1610
|
+
end + TlaElementText.commonMsgProperties( 'response', messageType ),
|
1611
|
+
)) # addInterface
|
1612
|
+
|
1613
|
+
# clear processor context
|
1614
|
+
self.specname_input_variable = nil
|
1615
|
+
self.interfaceName = nil
|
1616
|
+
self.serviceName = nil
|
1617
|
+
s()
|
1618
|
+
end
|
1619
|
+
|
1620
|
+
# @!endgroup
|
1621
|
+
# ------------------------------------------------------------------
|
1622
|
+
# @!group Helpers to create service procesure
|
1623
|
+
|
1624
|
+
|
1625
|
+
# Create snippet with 'dispatcherName'. Uses addSnippet method
|
1626
|
+
# in top level tla processor
|
1627
|
+
#
|
1628
|
+
#
|
1629
|
+
# @param [Hash:Array] methodsToDispatch
|
1630
|
+
# @option methodsToDispatch [:Symbol] :contracts array of ContractDefinition
|
1631
|
+
# @option methodsToDispatch [:Symbol] :function FunctionDefinition
|
1632
|
+
|
1633
|
+
private def createDispatcherSnippet( dispatcherName, methodsToDispatch )
|
1634
|
+
|
1635
|
+
end
|
1636
|
+
|
1637
|
+
# Find data structure to manage dispatching of 'funtionSexp'.
|
1638
|
+
#
|
1639
|
+
# @param [FunctionDefinition] funtionSexp
|
1640
|
+
#
|
1641
|
+
# @return [String], [Hash:Array] dispatcherName, methodNameImplementations
|
1642
|
+
#
|
1643
|
+
# @option methodNameImplementations [:Symbol] :contracts array
|
1644
|
+
# of ContractDefinition serviced by :function
|
1645
|
+
#
|
1646
|
+
# @option methodNameImplementations [:Symbol] :function
|
1647
|
+
# FunctionDefinition
|
1648
|
+
def findMethodsToDispatch( funtionSexp )
|
1649
|
+
logger.debug "#{__method__}: funtionSexp=#{funtionSexp}" if logger.debug?
|
1650
|
+
raise "Invalid type #{funtionSexp.sexp_type} in #{funtionSexp}" unless funtionSexp.sexp_type == :FunctionDefinition
|
1651
|
+
|
1652
|
+
methodName = funtionSexp.name
|
1653
|
+
currentContract = funtionSexp.contractDefinition
|
1654
|
+
|
1655
|
+
logger.info "#{__method__}: create dispatcher data structure for #{currentContract.name}.#{methodName}"
|
1656
|
+
|
1657
|
+
# inhertitanceHierarchy is a hash mapping typeName ->
|
1658
|
+
# [ContractDefinition:Array] for each contract in inherintance
|
1659
|
+
# hierarhcy of 'currentContract'
|
1660
|
+
inhertitanceHierarchy = currentContract.rootSymbol.allDerivedTypes( InheritanceSort.new )
|
1661
|
+
logger.debug "#{__method__}: inhertitanceHierarchy=#{inhertitanceHierarchy.inject({}) { |m,(k,v)| m[k] = v.map { |a| a.typeName}; m }}" if logger.debug?
|
1662
|
+
|
1663
|
+
# topologically sorted functionNames in inhertitance hierarchy
|
1664
|
+
sortedInheritanceHierarchy = inhertitanceHierarchy.tsort
|
1665
|
+
logger.debug "#{__method__}: sortedInheritanceHierarchy=#{sortedInheritanceHierarchy.join(',')}" if logger.debug?
|
1666
|
+
|
1667
|
+
dispatcherName = "#{currentContract.rootSymbol.name}_#{methodName}"
|
1668
|
+
|
1669
|
+
seenContracts = {}
|
1670
|
+
# iterate 'contractNames' in inhiritance order
|
1671
|
+
methodNameImplementations = sortedInheritanceHierarchy.map do |contractName|
|
1672
|
+
# contract for 'contractName' from 'inhertitanceHierarchy'
|
1673
|
+
contract = inhertitanceHierarchy[contractName].select { |inheritsContractDefintition| inheritsContractDefintition.typeName == contractName }.first
|
1674
|
+
{
|
1675
|
+
:contract => contract,
|
1676
|
+
:contracts => inhertitanceHierarchy[contractName],
|
1677
|
+
:function => contract.resolve( methodName ),
|
1678
|
+
}
|
1679
|
+
end.select do |methodNameImplementation|
|
1680
|
+
# accept only contracts, which implementing 'methodName'
|
1681
|
+
methodNameImplementation[:contract].getSymbol( methodName )
|
1682
|
+
end.map do |methodNameImplementation|
|
1683
|
+
# use first entry for a contract
|
1684
|
+
methodNameImplementation[:contracts] = methodNameImplementation[:contracts].reject { |c| seenContracts.key?( c.name ) }
|
1685
|
+
seenContracts[methodNameImplementation[:contract].name] = true
|
1686
|
+
methodNameImplementation
|
1687
|
+
end
|
1688
|
+
|
1689
|
+
raise "Could not find any implementation for '#{methodName}' in #{sortedInheritanceHierarchy.join(',')}" unless methodNameImplementations.any?
|
1690
|
+
|
1691
|
+
# logger.debug "#{__method__}: methodNameImplementations=#{methodNameImplementations.join(',')}" if logger.debug?
|
1692
|
+
|
1693
|
+
logger.info "#{__method__}: implementing dispatcherName=#{dispatcherName}, methodName '#{methodName}' =#{methodNameImplementations.map { |i| i[:function].interfaceName + '(' + i[:contracts].map{|c| c.name }.join(',')+ ')' }.join('; ')}" if logger.debug?
|
1694
|
+
return dispatcherName, methodNameImplementations
|
1695
|
+
end
|
1696
|
+
|
1697
|
+
# Create macro 'dispatcherName' to dispatch 'methodsToDispatch',
|
1698
|
+
# and pass it to 'addSnippets'. Optimization: if array
|
1699
|
+
# 'methodsToDispatch' length is 0, no need dispatcher macro is
|
1700
|
+
# made (method dispatching is replaced with direct method call,
|
1701
|
+
# and accessing response status/data can are simple calls.
|
1702
|
+
#
|
1703
|
+
# @param [Hash:Array] methodsToDispatch
|
1704
|
+
# @option methodsToDispatch [:Symbol] :contracts array ContractDefinition
|
1705
|
+
# @option methodsToDispatch [:Symbol] :function FunctionDefinition
|
1706
|
+
|
1707
|
+
def addDispatcherSnippet( dispatcherName, methodsToDispatch )
|
1708
|
+
|
1709
|
+
# optimization
|
1710
|
+
return if methodsToDispatch.length == 1
|
1711
|
+
|
1712
|
+
parameterName = 'rec'
|
1713
|
+
|
1714
|
+
|
1715
|
+
bodyStmts =
|
1716
|
+
s(
|
1717
|
+
# init all responses
|
1718
|
+
gen_assign( TlaElementText::OP_INFRA_RESPONSES, TlaElementText::OP_INFRA_STATUS_INIT ),
|
1719
|
+
generateMethodDispatch( methodsToDispatch, parameterName ),
|
1720
|
+
)
|
1721
|
+
|
1722
|
+
body =
|
1723
|
+
gen_macro(
|
1724
|
+
g_dispatcher(dispatcherName), parameterName,
|
1725
|
+
gen_stmts( bodyStmts )
|
1726
|
+
)
|
1727
|
+
|
1728
|
+
# define snippet, and add it main tla processor
|
1729
|
+
snippet = Snippet.new( g_dispatcherMeta, dispatcherName, body )
|
1730
|
+
addSnippet( snippet )
|
1731
|
+
end
|
1732
|
+
|
1733
|
+
#
|
1734
|
+
# @param [Hash:Array] methodsToDispatch
|
1735
|
+
# @option methodsToDispatch [:Symbol] :contracts array ContractDefinition
|
1736
|
+
# @option methodsToDispatch [:Symbol] :function FunctionDefinition
|
1737
|
+
#
|
1738
|
+
# @param [sTla] parameterName name of input parameter to the snippet
|
1739
|
+
#
|
1740
|
+
# @return [sTla:Array] dispatch statement if the
|
1741
|
+
#
|
1742
|
+
private def generateMethodDispatch( methodsToDispatch, parameterName )
|
1743
|
+
|
1744
|
+
# process first here, tail recursion on rest
|
1745
|
+
contracts = methodsToDispatch.first[:contracts]
|
1746
|
+
function = methodsToDispatch.first[:function]
|
1747
|
+
|
1748
|
+
if_condition = g_dispatcherCondition( contracts, parameterName )
|
1749
|
+
|
1750
|
+
if_block = s(
|
1751
|
+
gen_procedure_call( gen_specname( :solidity_contract_function, function.interfaceName ), parameterName )
|
1752
|
+
)
|
1753
|
+
|
1754
|
+
# tail recursion on 'methodsToDispatch'
|
1755
|
+
if methodsToDispatch.length == 1
|
1756
|
+
gen_if( if_condition, if_block, gen_assert( "FALSE", "Unkwon interface" ))
|
1757
|
+
else
|
1758
|
+
gen_if( if_condition, if_block, generateMethodDispatch( methodsToDispatch[1..-1], parameterName))
|
1759
|
+
end
|
1760
|
+
end
|
1761
|
+
|
1762
|
+
|
1763
|
+
##
|
1764
|
+
# Add label #{labelPrefix}_#{index} to statements in 'stmts' array
|
1765
|
+
# recursively in 'labelBlockStatements'
|
1766
|
+
#
|
1767
|
+
# Add label when:
|
1768
|
+
# - for the first statement in the list (start a procedure)
|
1769
|
+
# - previous statement (sexp_type) requires a label
|
1770
|
+
# - recurse down to :labelBlockStatements statements
|
1771
|
+
#
|
1772
|
+
private def serviceProcedureLabelStatements( stmts, labelPrefix )
|
1773
|
+
stmts, index = serviceProcedureLabelStatementsWorker( stmts, labelPrefix, 0, 0, true )
|
1774
|
+
stmts
|
1775
|
+
end
|
1776
|
+
|
1777
|
+
##
|
1778
|
+
# @return [Array, index] stmts as passed as input, index incremented
|
1779
|
+
private def serviceProcedureLabelStatementsWorker( stmts, labelPrefix, index, level, labelFirst )
|
1780
|
+
stmts.each_with_index do |stmt,i|
|
1781
|
+
|
1782
|
+
# prev stamement in current block
|
1783
|
+
prevStmt = stmts[i-1] if i > 0
|
1784
|
+
# puts "#{' ' * level } - #{level}: #{labelPrefix[1][:specname][:appName]}-#{index} #{stmts[i][0]}"
|
1785
|
+
if labelFirst || !prevStmt.nil? && requiresLabelAfter( prevStmt )
|
1786
|
+
stmts[i] = gen_labeled_stmt( stmt, labelPrefix, index )
|
1787
|
+
# puts "#{' ' * level } - #{level}: --> label #{labelPrefix[1][:specname][:appName]}-#{index} #{stmt[0]}"
|
1788
|
+
index += 1
|
1789
|
+
end
|
1790
|
+
labelFirst = false
|
1791
|
+
|
1792
|
+
# then & while>?
|
1793
|
+
if stmt.respond_to?( :labelBlockStatements )
|
1794
|
+
# recursion
|
1795
|
+
s, index = serviceProcedureLabelStatementsWorker( stmt.labelBlockStatements, labelPrefix, index, level+1, false )
|
1796
|
+
end
|
1797
|
+
|
1798
|
+
# else block
|
1799
|
+
if stmt.respond_to?( :labelBlockStatementsElse )
|
1800
|
+
# recursion
|
1801
|
+
s, index = serviceProcedureLabelStatementsWorker( stmt.labelBlockStatementsElse, labelPrefix, index, level+1, false )
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
|
1805
|
+
end
|
1806
|
+
return stmts, index
|
1807
|
+
end
|
1808
|
+
|
1809
|
+
# @!endgroup
|
1810
|
+
|
1811
|
+
|
1812
|
+
# ------------------------------------------------------------------
|
1813
|
+
# @!group Specialized processeros
|
1814
|
+
|
1815
|
+
#
|
1816
|
+
#
|
1817
|
+
class FunctionBodyProcessor < SexpProcessorTlaRoot
|
1818
|
+
|
1819
|
+
def initialize( tlaProcessor, elementGenerator, options )
|
1820
|
+
super( options )
|
1821
|
+
@tlaProcessor = tlaProcessor
|
1822
|
+
@elementGenerator = elementGenerator
|
1823
|
+
|
1824
|
+
# initially no variables assigned within this body
|
1825
|
+
self.variablesAssigned = {}
|
1826
|
+
end
|
1827
|
+
|
1828
|
+
# @attr [SexpProcessorTla] tlaProcessor collecting snippets
|
1829
|
+
attr_accessor :tlaProcessor
|
1830
|
+
|
1831
|
+
# @attr [Contract] currentContract which currently processing
|
1832
|
+
attr_accessor :currentContract
|
1833
|
+
|
1834
|
+
# @attr [STla] specname_input_variable
|
1835
|
+
attr_accessor :specname_input_variable
|
1836
|
+
|
1837
|
+
# @attr [String] interfaceName e.g. Demo(send)
|
1838
|
+
attr_accessor :interfaceName
|
1839
|
+
|
1840
|
+
# @attr [String] serviceName
|
1841
|
+
attr_accessor :serviceName
|
1842
|
+
|
1843
|
+
|
1844
|
+
# ------------------------------------------------------------------
|
1845
|
+
# @!group Access specialized sexp processor
|
1846
|
+
|
1847
|
+
def getFunctionBodyProcessor
|
1848
|
+
proc = FunctionBodyProcessor.new( tlaProcessor, elementGenerator, options )
|
1849
|
+
# Move context to processor
|
1850
|
+
proc.interfaceName = interfaceName
|
1851
|
+
proc.serviceName = serviceName
|
1852
|
+
proc.currentContract = currentContract
|
1853
|
+
proc.specname_input_variable = specname_input_variable
|
1854
|
+
proc
|
1855
|
+
end
|
1856
|
+
|
1857
|
+
def getRvalProcessor
|
1858
|
+
proc = RvalExprProcessor.new( tlaProcessor, elementGenerator,options)
|
1859
|
+
proc.interfaceName = interfaceName
|
1860
|
+
proc.specname_input_variable = specname_input_variable
|
1861
|
+
proc
|
1862
|
+
end
|
1863
|
+
|
1864
|
+
def getFunctionCallProcessor( actualParameters )
|
1865
|
+
proc = FunctionCallProcessor.new( tlaProcessor, elementGenerator, options )
|
1866
|
+
# Move context to processeos
|
1867
|
+
proc.interfaceName = interfaceName
|
1868
|
+
proc.serviceName = serviceName
|
1869
|
+
proc.currentContract = currentContract
|
1870
|
+
proc.actualParameters = actualParameters
|
1871
|
+
proc.specname_input_variable = specname_input_variable
|
1872
|
+
proc
|
1873
|
+
end
|
1874
|
+
|
1875
|
+
def getLvalProcessor( rval )
|
1876
|
+
proc = LvalExprProcessor.new( tlaProcessor, elementGenerator, options )
|
1877
|
+
proc.rval = rval
|
1878
|
+
proc.bodyProcessor = self
|
1879
|
+
proc.interfaceName = interfaceName
|
1880
|
+
proc.specname_input_variable = specname_input_variable
|
1881
|
+
proc
|
1882
|
+
end
|
1883
|
+
|
1884
|
+
# @!endgroup
|
1885
|
+
|
1886
|
+
# ------------------------------------------------------------------
|
1887
|
+
# @!group Lval interface
|
1888
|
+
|
1889
|
+
# @attr [Hash] variablesAssigned
|
1890
|
+
attr_accessor :variablesAssigned
|
1891
|
+
|
1892
|
+
# Maintain list of variables assigned in lval processes. If
|
1893
|
+
# assigining twice to same variable instruct to create a
|
1894
|
+
# label, and reset variables being assigned
|
1895
|
+
#
|
1896
|
+
# @return [Boolean] true if lval processor should label
|
1897
|
+
# assigment statement
|
1898
|
+
def shouldUseLabelToAssignTo( variableName )
|
1899
|
+
if variablesAssigned.key?( variableName )
|
1900
|
+
self.variablesAssigned = {
|
1901
|
+
variableName => true
|
1902
|
+
}
|
1903
|
+
ret = true
|
1904
|
+
else
|
1905
|
+
self.variablesAssigned[variableName] = true
|
1906
|
+
ret = false
|
1907
|
+
end
|
1908
|
+
logger.debug "#{__method__}: variableName: #{variableName} should have label=#{ret}" if logger.debug?
|
1909
|
+
ret
|
1910
|
+
end
|
1911
|
+
|
1912
|
+
# @!endgroup
|
1913
|
+
# ------------------------------------------------------------------
|
1914
|
+
# @!group Process sexp nodes
|
1915
|
+
|
1916
|
+
##
|
1917
|
+
# Concatenate statements together
|
1918
|
+
def process_Block( exp )
|
1919
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
1920
|
+
|
1921
|
+
# s( s(for statement) s())
|
1922
|
+
stmsArra = process_rest( exp )
|
1923
|
+
stmsArra.flatten(1)
|
1924
|
+
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
def process_IfStatement( exp )
|
1928
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
1929
|
+
|
1930
|
+
stmts = s()
|
1931
|
+
|
1932
|
+
# condition
|
1933
|
+
condition = getRvalProcessor.process( exp.condition )
|
1934
|
+
logger.debug "#{__method__}: condition=#{condition}" if logger.debug?
|
1935
|
+
thenBlock = getFunctionBodyProcessor.process( exp.then_branch )
|
1936
|
+
logger.debug "#{__method__}: thenBlock=#{thenBlock}" if logger.debug?
|
1937
|
+
elseBlock = getFunctionBodyProcessor.process( exp.else_branch ) if exp.else_branch
|
1938
|
+
|
1939
|
+
stmts << gen_if( condition, thenBlock, elseBlock )
|
1940
|
+
logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
|
1941
|
+
stmts
|
1942
|
+
end
|
1943
|
+
|
1944
|
+
|
1945
|
+
def process_Throw( exp )
|
1946
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
1947
|
+
stmts = s()
|
1948
|
+
|
1949
|
+
logger.info "#{__method__}: abort-interfaceName=#{interfaceName}"
|
1950
|
+
# stmts << gen_traced_stmt(
|
1951
|
+
# g_updateReturnStatus( interfaceName, g_statusFail ),
|
1952
|
+
# s( "SVC-exec@#{interfaceName}: return status", g_statusFail )
|
1953
|
+
# )
|
1954
|
+
|
1955
|
+
stmts << g_fail( interfaceName, serviceName )
|
1956
|
+
# stmts << gen_traced_stmt(
|
1957
|
+
# g_fail( interfaceName, serviceName ),
|
1958
|
+
# s( "SVC-exec@#{interfaceName}: throw goes to fail label which set FALSE return" )
|
1959
|
+
# )
|
1960
|
+
stmts
|
1961
|
+
end
|
1962
|
+
|
1963
|
+
# Within Block inteprete 'VariableDefinitionStatement' like 'Assignment'
|
1964
|
+
def process_VariableDefinitionStatement( exp )
|
1965
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
1966
|
+
rval = getRvalProcessor.process(exp.rval)
|
1967
|
+
logger.debug "#{__method__}: assigning-rval=#{rval}" if logger.debug?
|
1968
|
+
|
1969
|
+
stmts = getLvalProcessor(rval).process( exp.lval )
|
1970
|
+
|
1971
|
+
logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
|
1972
|
+
stmts
|
1973
|
+
|
1974
|
+
end
|
1975
|
+
|
1976
|
+
# @return [Sexp] s() wrapping statements for assignments
|
1977
|
+
def process_Assignment( exp )
|
1978
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
1979
|
+
raise "Operator '#{exp.operator}' not supported in #{exp}" unless exp.operator == '='
|
1980
|
+
|
1981
|
+
# find rval
|
1982
|
+
rval = getRvalProcessor.process(exp.rval)
|
1983
|
+
logger.debug "#{__method__}: assigning-rval=#{rval}" if logger.debug?
|
1984
|
+
|
1985
|
+
|
1986
|
+
stmts = getLvalProcessor(rval).process( exp.lval )
|
1987
|
+
# stmts << LvalExprProcessor.new(elementGenerator, options).process( exp.rval )
|
1988
|
+
|
1989
|
+
logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
|
1990
|
+
stmts
|
1991
|
+
end
|
1992
|
+
|
1993
|
+
##
|
1994
|
+
# In :Functioncall 'functionExpression' is either
|
1995
|
+
# ':NewExpression' or ':MemberAccess'. tla-statemts for these
|
1996
|
+
# two cases created using FunctionCallProcessor.
|
1997
|
+
def process_FunctionCall( exp )
|
1998
|
+
logger.debug "#{__method__}: start #{exp}" if logger.debug?
|
1999
|
+
|
2000
|
+
# find acual parameters
|
2001
|
+
|
2002
|
+
logger.debug "#{__method__}: exp.actualParameters=#{exp.actualParameters}" if logger.debug?
|
2003
|
+
actualParameters = exp.actualParameters.map.with_index do |expression,i|
|
2004
|
+
# returnVariableDecl = exp.functionReturnParameter( i )
|
2005
|
+
# retExpr = getRvalProcessor.process( expression )
|
2006
|
+
# gen_bin_op( "|->", returnVariableDecl.name, retExpr )
|
2007
|
+
getRvalProcessor.process( expression )
|
2008
|
+
end
|
2009
|
+
logger.debug "#{__method__}: rval-actualParameters=#{actualParameters}" if logger.debug?
|
2010
|
+
|
2011
|
+
|
2012
|
+
# create call
|
2013
|
+
|
2014
|
+
logger.debug "#{__method__}: exp.functionExpression=#{exp.functionExpression}" if logger.debug?
|
2015
|
+
# callStmts = self.process( exp.functionExpression )
|
2016
|
+
callStmts = getFunctionCallProcessor(actualParameters).process( exp.functionExpression )
|
2017
|
+
logger.info "#{__method__}, callStmts=#{callStmts}"
|
2018
|
+
callStmts
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
def process_FunctionCallReadReturn( exp )
|
2022
|
+
logger.debug "#{__method__}: start #{exp}" if logger.debug?
|
2023
|
+
# no operation because return wraps in return statment &&
|
2024
|
+
# expression original function call, which is split up in
|
2025
|
+
# canonization phase
|
2026
|
+
s()
|
2027
|
+
end
|
2028
|
+
|
2029
|
+
# Return 'status' and 'return.expressions' from procedure modeling 'inferfaceName'
|
2030
|
+
def process_Return( exp )
|
2031
|
+
stmts = s()
|
2032
|
+
retRvals = exp.expressions.map.with_index do |expression,i|
|
2033
|
+
# returnVariableDecl = exp.functionReturnParameter( i )
|
2034
|
+
# retExpr = getRvalProcessor.process( expression )
|
2035
|
+
# gen_bin_op( "|->", returnVariableDecl.name, retExpr )
|
2036
|
+
getRvalProcessor.process( expression )
|
2037
|
+
end
|
2038
|
+
# using 'g_statusFail' here because success is flagged only
|
2039
|
+
# after all statements processed successfully
|
2040
|
+
stmts << gen_operator_call(
|
2041
|
+
TlaElementText::OP_INFRA_RETURN,
|
2042
|
+
s( gen_str(interfaceName), g_statusFail, retRvals.first ) )
|
2043
|
+
# assign to function return list
|
2044
|
+
# stmts << gen_debug_msg( "JUST from getter" )
|
2045
|
+
stmts
|
2046
|
+
end
|
2047
|
+
|
2048
|
+
# @!endgroup
|
2049
|
+
|
2050
|
+
|
2051
|
+
end # FunctionBody
|
2052
|
+
|
2053
|
+
# Resolve local variable in function
|
2054
|
+
#
|
2055
|
+
class FunctionLocalsProcessor < SexpProcessorTlaRoot
|
2056
|
+
|
2057
|
+
# @attr [VariableDeclaration:Array] localVariables
|
2058
|
+
attr_accessor :localVariables
|
2059
|
+
|
2060
|
+
# ------------------------------------------------------------------
|
2061
|
+
# @!group Construct & configure
|
2062
|
+
|
2063
|
+
def initialize( elementGenerator, options )
|
2064
|
+
super( options )
|
2065
|
+
@elementGenerator = elementGenerator
|
2066
|
+
|
2067
|
+
# ignore unknown nodes
|
2068
|
+
self.default_method = :process_none
|
2069
|
+
|
2070
|
+
self.localVariables = s()
|
2071
|
+
end
|
2072
|
+
|
2073
|
+
# @!endgroup
|
2074
|
+
|
2075
|
+
|
2076
|
+
# ------------------------------------------------------------------
|
2077
|
+
# @!group Wrap scopes
|
2078
|
+
|
2079
|
+
# @attr [VariableDefinitionStatement] currentDeffi
|
2080
|
+
attr_accessor :currentDeffi
|
2081
|
+
|
2082
|
+
def deffed( expDeffi )
|
2083
|
+
prevDeffi = currentDeffi
|
2084
|
+
self.currentDeffi = expDeffi
|
2085
|
+
begin
|
2086
|
+
yield
|
2087
|
+
ensure
|
2088
|
+
self.currentDeffi = prevDeffi
|
2089
|
+
end
|
2090
|
+
|
2091
|
+
end
|
2092
|
+
|
2093
|
+
# @!endgroup
|
2094
|
+
|
2095
|
+
# ------------------------------------------------------------------
|
2096
|
+
# @!group Process nodes
|
2097
|
+
|
2098
|
+
def process_Block( exp )
|
2099
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2100
|
+
process_rest( exp )
|
2101
|
+
localVariables
|
2102
|
+
end
|
2103
|
+
|
2104
|
+
def process_VariableDefinitionStatement( exp )
|
2105
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2106
|
+
deffed( exp ) do
|
2107
|
+
process_rest( exp )
|
2108
|
+
end
|
2109
|
+
s()
|
2110
|
+
end
|
2111
|
+
|
2112
|
+
def process_VariableDeclaration( exp )
|
2113
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2114
|
+
|
2115
|
+
# localVariable declaration if within VariableDefinitionStatement
|
2116
|
+
localVariables << exp if currentDeffi
|
2117
|
+
|
2118
|
+
s()
|
2119
|
+
end
|
2120
|
+
|
2121
|
+
# @!endgroup
|
2122
|
+
|
2123
|
+
end # FunctionLocalsProcessor
|
2124
|
+
|
2125
|
+
class FunctionCallProcessor < SexpProcessorTlaRoot
|
2126
|
+
|
2127
|
+
# ----------
|
2128
|
+
# set during initialize -phase
|
2129
|
+
|
2130
|
+
# @attr [SexpProcessorTla] tlaProcessor collecting snippets
|
2131
|
+
attr_accessor :tlaProcessor
|
2132
|
+
|
2133
|
+
# @attr [Contract] currentContract which currently processing
|
2134
|
+
attr_accessor :currentContract
|
2135
|
+
|
2136
|
+
# @attr [STla] specname_input_variable
|
2137
|
+
attr_accessor :specname_input_variable
|
2138
|
+
|
2139
|
+
# @attr [String] interfaceName
|
2140
|
+
attr_accessor :interfaceName
|
2141
|
+
|
2142
|
+
# @attr [String] serviceName
|
2143
|
+
attr_accessor :serviceName
|
2144
|
+
|
2145
|
+
# @attr [s(rval, rval)] actualParameters
|
2146
|
+
attr_accessor :actualParameters
|
2147
|
+
|
2148
|
+
# ----------
|
2149
|
+
# properties set process -method in this class
|
2150
|
+
|
2151
|
+
# @attr [String:Array] functionOnFunctionMembers
|
2152
|
+
attr_accessor :functionOnFunctionMembers
|
2153
|
+
|
2154
|
+
# @attr [String:Array] functionOnFunctionMembers
|
2155
|
+
attr_accessor :functionOnFunctionValues
|
2156
|
+
|
2157
|
+
def initialize( tlaProcessor, elementGenerator, options )
|
2158
|
+
super( options )
|
2159
|
+
@tlaProcessor = tlaProcessor
|
2160
|
+
@elementGenerator = elementGenerator
|
2161
|
+
self.functionOnFunctionMembers = []
|
2162
|
+
self.functionOnFunctionValues = []
|
2163
|
+
end
|
2164
|
+
|
2165
|
+
def_delegators :tlaProcessor, # top level tla -processes
|
2166
|
+
:setMetatype,
|
2167
|
+
:findMethodsToDispatch
|
2168
|
+
|
2169
|
+
# ------------------------------------------------------------------
|
2170
|
+
# @!group Access specialized sexp processor
|
2171
|
+
|
2172
|
+
def getRvalProcessor
|
2173
|
+
proc = RvalExprProcessor.new(tlaProcessor, elementGenerator,options)
|
2174
|
+
proc.interfaceName = interfaceName
|
2175
|
+
proc.specname_input_variable = specname_input_variable
|
2176
|
+
proc
|
2177
|
+
end
|
2178
|
+
|
2179
|
+
# @!endgroup
|
2180
|
+
|
2181
|
+
# ------------------------------------------------------------------
|
2182
|
+
# @!group Process sexp nodes
|
2183
|
+
|
2184
|
+
# functionOnFunction case here
|
2185
|
+
def process_FunctionCall( exp )
|
2186
|
+
logger.debug "#{__method__}: #{exp} in functionOnFunction" if logger.debug?
|
2187
|
+
|
2188
|
+
actualParameters = exp.actualParameters.map.with_index do |expression,i|
|
2189
|
+
# returnVariableDecl = exp.functionReturnParameter( i )
|
2190
|
+
# retExpr = getRvalProcessor.process( expression )
|
2191
|
+
# gen_bin_op( "|->", returnVariableDecl.name, retExpr )
|
2192
|
+
getRvalProcessor.process( expression )
|
2193
|
+
end
|
2194
|
+
raise "Expect function on function actual parameters to equal 1, was #{actualParameters.length} in #{actualParameters.join(',')}" unless actualParameters.length == 1
|
2195
|
+
self.functionOnFunctionValues << actualParameters.first
|
2196
|
+
logger.debug "#{__method__}: func-on-func-rval-actualParameters=#{actualParameters}" if logger.debug?
|
2197
|
+
|
2198
|
+
process( exp.functionExpression )
|
2199
|
+
|
2200
|
+
end
|
2201
|
+
|
2202
|
+
##
|
2203
|
+
# We ended here to process the first 'MemberAccess' sexp in
|
2204
|
+
# s(:FunctionCall, "bool", s(:MemberAccess, "send",
|
2205
|
+
# s(:MemberAccess, "sender", s(:Identifier, "msg", "msg"))),
|
2206
|
+
# s(:Literal, "int_const 1", "1"))))
|
2207
|
+
#
|
2208
|
+
# @see process_NewExpression
|
2209
|
+
def process_MemberAccess( exp )
|
2210
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2211
|
+
|
2212
|
+
ret = nil
|
2213
|
+
if exp.isFunctionOnFunction
|
2214
|
+
# AST in 'function on function' case, e.g. demo1.set.value(20)(
|
2215
|
+
# 6 ) like s(:MemberAccess, "value", s(:MemberAccess, "set",
|
2216
|
+
# s(:Identifier, "contract Demo1", "demo1")))
|
2217
|
+
#
|
2218
|
+
# we are processing s(:MemberAccess, "value", collect
|
2219
|
+
# names so that we can consume actualParameters before
|
2220
|
+
# binding formal parameters
|
2221
|
+
self.functionOnFunctionMembers << exp.member_name
|
2222
|
+
logger.info "#{__method__}: collected functionOnFunctionMembers=#{functionOnFunctionMembers.join(',')}"
|
2223
|
+
ret = process( exp.memberStruct )
|
2224
|
+
else
|
2225
|
+
# implement function calll
|
2226
|
+
ret = memberAccessAsFunctionCall( exp )
|
2227
|
+
end
|
2228
|
+
ret
|
2229
|
+
end
|
2230
|
+
|
2231
|
+
# called by process_MemberAccess to create statements to make
|
2232
|
+
# a function call
|
2233
|
+
#
|
2234
|
+
# @param [MemberAccess] exp in process_MemberAccess
|
2235
|
+
private def memberAccessAsFunctionCall( exp )
|
2236
|
+
|
2237
|
+
# context
|
2238
|
+
functionCalled = exp.declationRef
|
2239
|
+
called_interfaceName = functionCalled.interfaceName
|
2240
|
+
# send gets message 'value' as actual parameter
|
2241
|
+
valueAsActualParameter = [Constants::SOL_SEND].include?( functionCalled.name )
|
2242
|
+
|
2243
|
+
recipientSexp = exp.memberStruct
|
2244
|
+
logger.debug "#{__method__}: dispatch a call to called=#{called_interfaceName}->#{functionCalled}/ recipient #{recipientSexp}}" if logger.debug?
|
2245
|
+
logger.debug "#{__method__}: functionOnFunctionMembers=#{functionOnFunctionMembers}" if logger.debug?
|
2246
|
+
|
2247
|
+
# bind formal parameters with actual parameters in
|
2248
|
+
# '@actualParameters'. 1) find [VariableDeclation] of formal
|
2249
|
+
# parameters 2) bind actual parmeters in '@actualParameters'
|
2250
|
+
formalParameters = functionFormalParameters( functionCalled, false, valueAsActualParameter )
|
2251
|
+
|
2252
|
+
# bind formal parameters with '@actualParameters'
|
2253
|
+
formalParameterBinds = formalParameters.any? && formalParameters.map.with_index do |parameter,i|
|
2254
|
+
gen_record_field_definition( parameter.name, actualParameters[i] )
|
2255
|
+
end
|
2256
|
+
formalParameterBinds = s() unless formalParameterBinds
|
2257
|
+
logger.info "#{__method__}: functionCalled: #{functionCalled.name} formal parameters=#{ formalParameters.map.with_index{ |p,i| p.name + '->' + actualParameters[i].inspect } }"
|
2258
|
+
|
2259
|
+
|
2260
|
+
# generate code to access recipient
|
2261
|
+
recipientTla = getRvalProcessor.process( recipientSexp )
|
2262
|
+
logger.debug "#{__method__}: recipientTla=#{recipientTla}" if logger.debug?
|
2263
|
+
|
2264
|
+
# find 'callValue' i.e. what is assigned to 'value' field 1)
|
2265
|
+
# send function gets initial value as actual parameter 2) in
|
2266
|
+
# functionOnFunction e.g. f.gas(2).value(20)() search
|
2267
|
+
# 'functionOnFunctionMembers' and 'functionOnFunctionValues'
|
2268
|
+
# to find value 3) use g_zero
|
2269
|
+
|
2270
|
+
if valueAsActualParameter
|
2271
|
+
# send function
|
2272
|
+
callValue = s()
|
2273
|
+
else
|
2274
|
+
# 3) default g_zero
|
2275
|
+
callValueInit = g_zero
|
2276
|
+
functionOnFunctionMembers.each_with_index do |functionOnFunctionMember,i|
|
2277
|
+
# 2) functionOnFunction case
|
2278
|
+
callValueInit = functionOnFunctionValues[i] if functionOnFunctionMember == Constants::FIELD_NAME_VALUE
|
2279
|
+
end
|
2280
|
+
callValue = s( gen_record_field_definition( Constants::FIELD_NAME_VALUE, callValueInit ))
|
2281
|
+
end
|
2282
|
+
|
2283
|
+
# actual call
|
2284
|
+
logger.debug "#{__method__}: callValue=#{callValue.join(',')}, formalParameterBinds=#{formalParameterBinds.join(',')}" if logger.debug?
|
2285
|
+
|
2286
|
+
# call procedure with formal paremeters bound + common msg parameters set
|
2287
|
+
parameters = gen_record_definition(
|
2288
|
+
formalParameterBinds +
|
2289
|
+
g_commonMsgParameters( specname_input_variable, recipientTla ) +
|
2290
|
+
callValue
|
2291
|
+
)
|
2292
|
+
|
2293
|
+
|
2294
|
+
|
2295
|
+
stmts = s()
|
2296
|
+
# goto abort unless called recipientTla address exist
|
2297
|
+
stmts << gen_if(
|
2298
|
+
gen_not( g_RecipientContractExists( recipientTla )),
|
2299
|
+
gen_traced_stmt(
|
2300
|
+
g_abort(interfaceName, serviceName),
|
2301
|
+
"SVC-exec@#{interfaceName}: abort because called address of contract does not exist" )
|
2302
|
+
|
2303
|
+
)
|
2304
|
+
|
2305
|
+
g_dispatch_call( exp.declationRef, parameters, interfaceName, serviceName ).each { |s| stmts << s }
|
2306
|
+
logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
|
2307
|
+
|
2308
|
+
stmts
|
2309
|
+
|
2310
|
+
end
|
2311
|
+
|
2312
|
+
##
|
2313
|
+
# We ended here to process 'NewExpression' in s(:FunctionCall,
|
2314
|
+
# "contract Demo1", s(:NewExpression, "function () returns
|
2315
|
+
# (contract Demo1)", s(:UserDefinedTypeName, "Demo1")))'
|
2316
|
+
#
|
2317
|
+
# @see process_MemberAccess
|
2318
|
+
def process_NewExpression( exp )
|
2319
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2320
|
+
contructorFunctionDeclaration = exp.declationRef
|
2321
|
+
logger.debug "#{__method__}: contructorFunctionDeclaration=#{contructorFunctionDeclaration} for #{exp.type}" if logger.debug?
|
2322
|
+
|
2323
|
+
called_interfaceName = contructorFunctionDeclaration.interfaceName
|
2324
|
+
called = gen_specname( :solidity_contract_function, called_interfaceName )
|
2325
|
+
logger.debug "#{__method__}: called= #{called}" if logger.debug?
|
2326
|
+
s(
|
2327
|
+
gen_procedure_call(
|
2328
|
+
called,
|
2329
|
+
gen_record_definition(
|
2330
|
+
g_procCallAddresses( specname_input_variable ) +
|
2331
|
+
g_procCallValue)
|
2332
|
+
),
|
2333
|
+
# abort checked without solidity dispatcher
|
2334
|
+
g_abortCheckIncall( interfaceName, serviceName, called_interfaceName )
|
2335
|
+
)
|
2336
|
+
end
|
2337
|
+
|
2338
|
+
# @!endgroup
|
2339
|
+
|
2340
|
+
# ------------------------------------------------------------------
|
2341
|
+
# @!group FunctionCallProcessor utilities
|
2342
|
+
|
2343
|
+
|
2344
|
+
|
2345
|
+
# Abort 'interfaceName'/'serviceName' for if any of
|
2346
|
+
# 'methodsToDispatch' aborts. Actually only one dispatched.
|
2347
|
+
# TODO remove
|
2348
|
+
# def addDispatcherStatusCheck( methodsToDispatch, interfaceName, serviceName )
|
2349
|
+
# if_condition = gen_FALSE
|
2350
|
+
|
2351
|
+
# methodsToDispatch.map do |methodToDispatch|
|
2352
|
+
# methodToDispatch[:function].interfaceName
|
2353
|
+
# end.uniq.each do |interfaceName|
|
2354
|
+
# if_condition =
|
2355
|
+
# gen_bin_op( '\\/',
|
2356
|
+
# if_condition,
|
2357
|
+
# g_abortedInCall( interfaceName )
|
2358
|
+
# )
|
2359
|
+
# end
|
2360
|
+
# if_block = s( g_abort( interfaceName, serviceName ))
|
2361
|
+
# gen_if( if_condition, if_block )
|
2362
|
+
# end
|
2363
|
+
|
2364
|
+
##
|
2365
|
+
# Formal parameters are all request parameters expect 'commonMsgproperties'
|
2366
|
+
# An expection 'send' operation formal parameter include 'value'.
|
2367
|
+
#
|
2368
|
+
# @param [Boolean] isConstructor
|
2369
|
+
#
|
2370
|
+
# @return [s(:VariableDeclaration)] formal parameters in 'functionSexp'
|
2371
|
+
private def functionFormalParameters( functionSexp, isConstructor, valueAsActualParameter=false )
|
2372
|
+
logger.debug "#{__method__}: #{functionSexp}" if logger.debug?
|
2373
|
+
|
2374
|
+
commonMsgParameters =
|
2375
|
+
TlaElementText.commonMsgProperties( 'request', isConstructor ).map { |sexp| sexp[1] }
|
2376
|
+
logger.debug "#{__method__}: commonMsgParameters=#{commonMsgParameters}" if logger.debug?
|
2377
|
+
|
2378
|
+
formalParameters =
|
2379
|
+
functionSexp.parameterList.parameters.
|
2380
|
+
select do |parameter|
|
2381
|
+
# accect request parameter if not in common
|
2382
|
+
!commonMsgParameters.include?( parameter.name ) ||
|
2383
|
+
# OR 'send( value )'
|
2384
|
+
( valueAsActualParameter && parameter.name == Constants::FIELD_NAME_VALUE )
|
2385
|
+
end
|
2386
|
+
|
2387
|
+
logger.debug "#{__method__}: formalParameters=#{formalParameters}" if logger.debug?
|
2388
|
+
formalParameters
|
2389
|
+
end
|
2390
|
+
|
2391
|
+
# @!endgroup
|
2392
|
+
|
2393
|
+
|
2394
|
+
end
|
2395
|
+
|
2396
|
+
class RvalExprProcessor < SexpProcessorTlaRoot
|
2397
|
+
|
2398
|
+
def initialize( tlaProcessor, elementGenerator, options )
|
2399
|
+
super( options )
|
2400
|
+
@tlaProcessor = tlaProcessor
|
2401
|
+
@elementGenerator = elementGenerator
|
2402
|
+
# alert if new unknown nodes added
|
2403
|
+
self.default_method = :process_abort
|
2404
|
+
|
2405
|
+
end
|
2406
|
+
|
2407
|
+
# @attr [String] interfaceName ie. function of the body:
|
2408
|
+
# constructor 'Demo()' or function 'Demo(set)'
|
2409
|
+
attr_accessor :interfaceName
|
2410
|
+
|
2411
|
+
# @attr [STla] specname_input_variable
|
2412
|
+
attr_accessor :specname_input_variable
|
2413
|
+
|
2414
|
+
|
2415
|
+
# @attr [SexpProcessorTla] tlaProcessor collecting snippets
|
2416
|
+
attr_accessor :tlaProcessor
|
2417
|
+
|
2418
|
+
def_delegators :tlaProcessor, # top level tla -processes
|
2419
|
+
:findMethodsToDispatch
|
2420
|
+
|
2421
|
+
# ------------------------------------------------------------------
|
2422
|
+
# @!group Process sexp nodes
|
2423
|
+
|
2424
|
+
|
2425
|
+
##
|
2426
|
+
# Rval which wraps function call - modeled as
|
2427
|
+
# 'InfrastructureServiceGetResponse( "function" ) '
|
2428
|
+
def process_FunctionCallReadReturn( exp )
|
2429
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2430
|
+
functionCall = exp.functionCalled
|
2431
|
+
g_dispatch_read_return( functionCall.functionExpression.declationRef )
|
2432
|
+
# interfaceName = functionCall.functionExpression.declationRef.interfaceName
|
2433
|
+
# logger.info "#{__method__}: functionCall:#{functionCall} -> interfaceName=#{interfaceName}"
|
2434
|
+
# gen_operator_call(
|
2435
|
+
# TlaElementText::OP_INFRA_GET_RESPONSE,
|
2436
|
+
# gen_str( interfaceName )
|
2437
|
+
# )
|
2438
|
+
end
|
2439
|
+
|
2440
|
+
def process_UnaryOperation( exp )
|
2441
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2442
|
+
tlaExpr = process( exp.expr )
|
2443
|
+
opExpr = TlaElementText.solcOperator2Tla(exp.operator)
|
2444
|
+
gen_unary_op( opExpr, tlaExpr )
|
2445
|
+
end
|
2446
|
+
|
2447
|
+
def process_BinaryOperation( exp )
|
2448
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2449
|
+
opExpr = TlaElementText.solcOperator2Tla(exp.operator)
|
2450
|
+
lhsExpr = process( exp.lhs )
|
2451
|
+
rhsExpr = process( exp.rhs )
|
2452
|
+
gen_bin_op( opExpr, lhsExpr, rhsExpr )
|
2453
|
+
end
|
2454
|
+
|
2455
|
+
def process_Literal( exp )
|
2456
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2457
|
+
# TODO: map 'Literl.value' to domain.value in 'exp.domainReference'
|
2458
|
+
gen_plainname( exp.value )
|
2459
|
+
end
|
2460
|
+
|
2461
|
+
def process_MemberAccess( exp )
|
2462
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2463
|
+
variableDeclaration = exp.declationRef
|
2464
|
+
resolvedDeclarationScope = variableDeclaration.scopeDefining
|
2465
|
+
accessVariable( resolvedDeclarationScope, variableDeclaration.name )
|
2466
|
+
end
|
2467
|
+
|
2468
|
+
|
2469
|
+
def process_Identifier( exp )
|
2470
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2471
|
+
variableDeclaration = exp.declationRef
|
2472
|
+
resolvedDeclarationScope = variableDeclaration.scopeDefining
|
2473
|
+
accessVariable( resolvedDeclarationScope, variableDeclaration.name )
|
2474
|
+
end
|
2475
|
+
|
2476
|
+
def process_IndexAccess( exp )
|
2477
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2478
|
+
|
2479
|
+
# variable being indexed
|
2480
|
+
variableDeclaration = exp.identifier.declationRef
|
2481
|
+
resolvedDeclarationScope = variableDeclaration.scopeDefining
|
2482
|
+
|
2483
|
+
# access 'normal' variable
|
2484
|
+
variableToAccess = accessVariable( resolvedDeclarationScope, variableDeclaration.name )
|
2485
|
+
logger.debug "#{__method__}: variableToAccess= #{variableToAccess}" if logger.debug?
|
2486
|
+
|
2487
|
+
# rval indexing the variable
|
2488
|
+
indexerRval = process( exp.indexer )
|
2489
|
+
logger.debug "#{__method__}: exp.indexer #{exp.indexer} index-rval=#{indexerRval}" if logger.debug?
|
2490
|
+
|
2491
|
+
# variableToAccess[indexerRval]
|
2492
|
+
gen_record_named_field( variableToAccess, indexerRval )
|
2493
|
+
|
2494
|
+
end
|
2495
|
+
|
2496
|
+
# @!endgroup
|
2497
|
+
|
2498
|
+
# ------------------------------------------------------------------
|
2499
|
+
# @!group Utilities
|
2500
|
+
|
2501
|
+
private def accessVariable( resolvedDeclarationScope, variableAccess, accountId=g_localCurrentAccountId )
|
2502
|
+
logger.debug "#{__method__}: variableAccess=#{variableAccess}, resolvedDeclarationScope=#{resolvedDeclarationScope[0]}:#{resolvedDeclarationScope[1]}" if logger.debug?
|
2503
|
+
case resolvedDeclarationScope.sexp_type
|
2504
|
+
when :ContractDefinition
|
2505
|
+
# access 'variableAccess' on record in
|
2506
|
+
# 'eth_StorageRoot' with using account id in 'locals',
|
2507
|
+
# where account id is kept for constructor & normal message call
|
2508
|
+
gen_record_field(
|
2509
|
+
gen_operator_call(
|
2510
|
+
TlaElementText::FW_OP_GET_ELEMENT,
|
2511
|
+
s(
|
2512
|
+
g_storageRoot_exec,
|
2513
|
+
gen_str(Constants::FIELD_NAME_ADDRESS),
|
2514
|
+
accountId,
|
2515
|
+
# gen_record_field(gen_interface_input_variable_name( interfaceName ), Constants::FIELD_NAME_RECIPIENT )
|
2516
|
+
)),
|
2517
|
+
variableAccess )
|
2518
|
+
when :FunctionDefinition
|
2519
|
+
# access 'variableAccess' in function local varibales
|
2520
|
+
g_locaVariableName( variableAccess )
|
2521
|
+
when :ParameterList
|
2522
|
+
# # access 'variableAccess' on input variable for 'interfaceName' procedure
|
2523
|
+
gen_record_field( gen_interface_input_variable_name( interfaceName ), variableAccess )
|
2524
|
+
else
|
2525
|
+
raise SbuilderEtherumException, "Missing implemtation for sexp_type #{resolvedDeclarationScope.sexp_type} for rValExpression"
|
2526
|
+
end
|
2527
|
+
|
2528
|
+
end
|
2529
|
+
# @!endgroup
|
2530
|
+
|
2531
|
+
end # RvalExprProcessor
|
2532
|
+
|
2533
|
+
class LvalExprProcessor < SexpProcessorTlaRoot
|
2534
|
+
|
2535
|
+
def initialize( tlaProcessor, elementGenerator, options )
|
2536
|
+
super( options )
|
2537
|
+
@tlaProcessor = tlaProcessor
|
2538
|
+
@elementGenerator = elementGenerator
|
2539
|
+
end
|
2540
|
+
|
2541
|
+
# @attr [SexpProcessorTla] tlaProcessor collecting snippets
|
2542
|
+
attr_accessor :tlaProcessor
|
2543
|
+
|
2544
|
+
# @attr [FunctionBodyProcessor] bodyProcessor keeping tract of variables assigned
|
2545
|
+
attr_accessor :bodyProcessor
|
2546
|
+
|
2547
|
+
# @attr [String] rval generate expression
|
2548
|
+
attr_accessor :rval
|
2549
|
+
|
2550
|
+
# @attr [String] interfaceName ie. function of the body:
|
2551
|
+
# constructor 'Demo()' or function 'Demo(set)'
|
2552
|
+
attr_accessor :interfaceName
|
2553
|
+
|
2554
|
+
# @attr [STla] specname_input_variable
|
2555
|
+
attr_accessor :specname_input_variable
|
2556
|
+
|
2557
|
+
|
2558
|
+
def getRvalProcessor
|
2559
|
+
proc = RvalExprProcessor.new( tlaProcessor, elementGenerator,options)
|
2560
|
+
proc.interfaceName = interfaceName
|
2561
|
+
proc.specname_input_variable = specname_input_variable
|
2562
|
+
proc
|
2563
|
+
end
|
2564
|
+
|
2565
|
+
|
2566
|
+
# ------------------------------------------------------------------
|
2567
|
+
# @!group Process nodes
|
2568
|
+
|
2569
|
+
#
|
2570
|
+
# Assignin to a local variable
|
2571
|
+
def process_VariableDeclaration( exp )
|
2572
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2573
|
+
variableDeclaration = exp
|
2574
|
+
stmts = s()
|
2575
|
+
stmts << gen_assign( g_locaVariableName( exp.name ), rval )
|
2576
|
+
stmts
|
2577
|
+
end
|
2578
|
+
|
2579
|
+
|
2580
|
+
# Shadows identifier!
|
2581
|
+
def process_IndexAccess( exp )
|
2582
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2583
|
+
variableDeclaration = exp.identifier.declationRef
|
2584
|
+
|
2585
|
+
|
2586
|
+
# using 'local' variable to hold contract address
|
2587
|
+
|
2588
|
+
indexerRval = getRvalProcessor.process( exp.indexer )
|
2589
|
+
logger.debug "#{__method__}: exp.lval-indexer #{exp.indexer} index-rval=#{indexerRval}" if logger.debug?
|
2590
|
+
|
2591
|
+
lvalAccess = gen_record_named_field( variableDeclaration.name, indexerRval )
|
2592
|
+
setExcept( lvalAccess, rval )
|
2593
|
+
end
|
2594
|
+
|
2595
|
+
##
|
2596
|
+
# Identifier: find identifier declaration and its scope,
|
2597
|
+
# generate code. Generate code to set 'rval' for element
|
2598
|
+
# identified by 'key'.
|
2599
|
+
#
|
2600
|
+
# Currently supports:
|
2601
|
+
# - contract member variable: update eth_storageRoot with
|
2602
|
+
#
|
2603
|
+
def process_Identifier( exp )
|
2604
|
+
logger.debug "#{__method__}: #{exp}" if logger.debug?
|
2605
|
+
variableDeclaration = exp.declationRef
|
2606
|
+
lvalAccess = gen_identifier(variableDeclaration.name)
|
2607
|
+
setExcept( lvalAccess, rval )
|
2608
|
+
end
|
2609
|
+
|
2610
|
+
# @!endgroup
|
2611
|
+
|
2612
|
+
|
2613
|
+
# ------------------------------------------------------------------
|
2614
|
+
# @!group Utils
|
2615
|
+
|
2616
|
+
##
|
2617
|
+
# Generate code:
|
2618
|
+
#
|
2619
|
+
# var := { IF a.id = 'idValue' THEN [ a EXCEPT = !.a = 'value'] ELSE a: a \in var }
|
2620
|
+
#
|
2621
|
+
# @param [String] expectName name of variable to set
|
2622
|
+
# @param [String] exceptValue to set
|
2623
|
+
private def setExcept( expectName, exceptValue, idKey=g_localCurrentAccountId )
|
2624
|
+
logger.debug "#{__method__}: expectName=#{expectName}, exceptValue=#{exceptValue}" if logger.debug?
|
2625
|
+
# case resolvedDeclarationScope.sexp_type
|
2626
|
+
# when :ContractDefinition
|
2627
|
+
stmts = s()
|
2628
|
+
variableToAssignTo = TlaElementText::FW_STATE_STORAGE_ROOT_TEMP
|
2629
|
+
if bodyProcessor.shouldUseLabelToAssignTo( variableToAssignTo )
|
2630
|
+
stmts << gen_label
|
2631
|
+
end
|
2632
|
+
# Updating contract in process for 'interfaceName'. Access contract address using address in local 'variable '
|
2633
|
+
|
2634
|
+
stmts << gen_assignOnTop(
|
2635
|
+
g_storageRoot_temp,
|
2636
|
+
gen_set_map(
|
2637
|
+
gen_IF( gen_bin_op( "=", gen_record_field( 'a', Constants::FIELD_NAME_ADDRESS), idKey ),
|
2638
|
+
gen_EXCEPT(
|
2639
|
+
'a',
|
2640
|
+
gen_bin_op( '=',
|
2641
|
+
gen_record_field("!", expectName),
|
2642
|
+
exceptValue )), 'a' ),
|
2643
|
+
gen_set_iterate( 'a', g_storageRoot_exec),
|
2644
|
+
))
|
2645
|
+
stmts
|
2646
|
+
end
|
2647
|
+
|
2648
|
+
# @!endgroup
|
2649
|
+
|
2650
|
+
end
|
2651
|
+
|
2652
|
+
# @!endgroup
|
2653
|
+
|
2654
|
+
end # LvalExprProcessor
|
2655
|
+
|
2656
|
+
|
2657
|
+
# ------------------------------------------------------------------
|
2658
|
+
# @!group Helper class to implement tsort#strongly_connected_components
|
2659
|
+
|
2660
|
+
class InheritanceSort < Hash
|
2661
|
+
|
2662
|
+
include TSort
|
2663
|
+
|
2664
|
+
# methods used by tsort
|
2665
|
+
alias tsort_each_node each_key
|
2666
|
+
def tsort_each_child(node, &block)
|
2667
|
+
fetch(node).map { |n| n.typeName }.each(&block)
|
2668
|
+
end
|
2669
|
+
|
2670
|
+
end
|
2671
|
+
# @!endgroup
|
2672
|
+
|
2673
|
+
end
|
2674
|
+
end
|