sbuilder-eth 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.org +19 -0
- data/VERSION +1 -0
- data/bin/state-explorer.sh +215 -0
- data/lib/eth/al_api.rb +12 -0
- data/lib/eth/ast_sexp.rb +519 -0
- data/lib/eth/constants.rb +354 -0
- data/lib/eth/ethereum.rb +2054 -0
- data/lib/eth/ethereum_expression.rb +476 -0
- data/lib/eth/exception.rb +9 -0
- data/lib/eth/global_scope.rb +100 -0
- data/lib/eth/module.rb +18 -0
- data/lib/eth/sexp_processor.rb +66 -0
- data/lib/eth/sexp_processor_call_separator.rb +163 -0
- data/lib/eth/sexp_processor_getter.rb +125 -0
- data/lib/eth/sexp_processor_resolve.rb +122 -0
- data/lib/eth/sexp_processor_scope.rb +355 -0
- data/lib/eth/sexp_utils.rb +214 -0
- data/lib/eth/solidity_compiler.rb +145 -0
- data/lib/eth/solidity_loader.rb +53 -0
- data/lib/eth/solidity_translator.rb +840 -0
- data/lib/mixer/reference.rb +30 -0
- data/lib/mixer/scope.rb +100 -0
- data/lib/mixer/scoped.rb +18 -0
- data/lib/plugin/controller.rb +267 -0
- data/lib/plugin/module.rb +3 -0
- data/lib/plugin/plugin.rb +33 -0
- data/lib/sbuilder-eth.rb +6 -0
- data/sbuilder-eth.gemspec +38 -0
- metadata +149 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
module Sbuilder
|
3
|
+
module Eth
|
4
|
+
|
5
|
+
module Test
|
6
|
+
def hei
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class GlobalScopeEntry < AstSexp
|
11
|
+
|
12
|
+
include Sbuilder::Eth::Scoped
|
13
|
+
def initialize
|
14
|
+
super( self.class.name.split('::').last.to_sym )
|
15
|
+
end
|
16
|
+
# @return [Symbol] class basename (as a symbol)
|
17
|
+
# def sexp_type
|
18
|
+
# self.class.name.split('::').last.to_sym
|
19
|
+
# end
|
20
|
+
end
|
21
|
+
|
22
|
+
class SolidityFunction < GlobalScopeEntry
|
23
|
+
# @return [Symbol] :reservedFunctionType to indicate that
|
24
|
+
# specaial Solidity function
|
25
|
+
def functionType
|
26
|
+
:reservedFunctionType
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Solidity function pushed in global scope
|
31
|
+
class SelfDestruct < SolidityFunction; end
|
32
|
+
class Revert < SolidityFunction; end
|
33
|
+
class Require < SolidityFunction; end
|
34
|
+
|
35
|
+
|
36
|
+
# # SexpExtry for keyword 'this'
|
37
|
+
# class This < GlobalScopeEntry; end
|
38
|
+
|
39
|
+
class GlobalScope < AstSexp
|
40
|
+
|
41
|
+
|
42
|
+
include Sbuilder::Eth::Scope
|
43
|
+
|
44
|
+
GLOBAL_SCOPE_DEFS =
|
45
|
+
[
|
46
|
+
{
|
47
|
+
:name => Constants::SOL_SELF_DESTRUCT,
|
48
|
+
:astSexp => SelfDestruct.new,
|
49
|
+
},
|
50
|
+
{
|
51
|
+
:name => Constants::SOL_REVERT,
|
52
|
+
:astSexp => Revert.new,
|
53
|
+
},
|
54
|
+
{
|
55
|
+
:name => Constants::SOL_REQUIRE,
|
56
|
+
:astSexp => Require.new,
|
57
|
+
},
|
58
|
+
# {
|
59
|
+
# :name => Constants::SOL_THIS,
|
60
|
+
# :astSexp => This.new,
|
61
|
+
# }
|
62
|
+
]
|
63
|
+
|
64
|
+
# ------------------------------------------------------------------
|
65
|
+
# @!group Constructor
|
66
|
+
|
67
|
+
# @return [GlobalScope] initialized (defined scope) GlobalScope
|
68
|
+
# instance
|
69
|
+
def initialize( options={})
|
70
|
+
super( self.class.name.split('::').last.to_sym )
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def self.start( options={} )
|
75
|
+
globalScope = GlobalScope.new( options )
|
76
|
+
|
77
|
+
globalScopeInit.each do |scopeDef|
|
78
|
+
globalScope.define( scopeDef[:name], scopeDef[:astSexp])
|
79
|
+
end
|
80
|
+
|
81
|
+
globalScope
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [Array<Hash>] initHash array of hashes defining
|
85
|
+
# initial gloabl scope, hash defines properties ':name' and
|
86
|
+
# ':astSexp'
|
87
|
+
# @option initHash [:Symbol] :name
|
88
|
+
# @option initHash [:Symbol] :astSexp
|
89
|
+
def self.globalScopeInit
|
90
|
+
GLOBAL_SCOPE_DEFS
|
91
|
+
end
|
92
|
+
|
93
|
+
# @!endgroup
|
94
|
+
|
95
|
+
extend Test
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
data/lib/eth/module.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'constants'
|
2
|
+
require_relative 'exception'
|
3
|
+
require_relative 'al_api'
|
4
|
+
require_relative 'ethereum_expression'
|
5
|
+
require_relative 'ethereum'
|
6
|
+
require_relative 'ast_sexp'
|
7
|
+
require_relative 'global_scope'
|
8
|
+
require_relative "solidity_compiler"
|
9
|
+
require_relative "solidity_loader"
|
10
|
+
require_relative "solidity_translator"
|
11
|
+
|
12
|
+
require_relative "sexp_utils"
|
13
|
+
require_relative "sexp_processor"
|
14
|
+
# require_relative "sexp_processor_constructors"
|
15
|
+
require_relative "sexp_processor_getter"
|
16
|
+
require_relative "sexp_processor_call_separator"
|
17
|
+
require_relative "sexp_processor_scope"
|
18
|
+
require_relative "sexp_processor_resolve"
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'sexp_processor'
|
2
|
+
|
3
|
+
module Sbuilder
|
4
|
+
module Eth
|
5
|
+
class SexpProcessorEth < SexpProcessor
|
6
|
+
|
7
|
+
# include logger
|
8
|
+
include Sbuilder::Al::Util::MyLogger # mix logger
|
9
|
+
|
10
|
+
# @# @!attribute [Hash] options
|
11
|
+
attr_accessor :options
|
12
|
+
|
13
|
+
# @# @!attribute [Logger] logger
|
14
|
+
attr_reader :logger
|
15
|
+
|
16
|
+
def initialize( options={})
|
17
|
+
super()
|
18
|
+
|
19
|
+
self.options = options
|
20
|
+
@logger = getLogger( nil, options )
|
21
|
+
|
22
|
+
# Override defaults in parent class
|
23
|
+
self.strict = false
|
24
|
+
|
25
|
+
# Allow Sexp nodes to remain intact, see 'process_rest'
|
26
|
+
self.require_empty = false
|
27
|
+
|
28
|
+
# visitors just recurses - do not shift sexp (as sexp_processor default does)
|
29
|
+
self.default_method = :process_rest
|
30
|
+
|
31
|
+
# no warning needed when using default method
|
32
|
+
self.warn_on_default = false
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# Use for default processor when we need to control what gets
|
37
|
+
# processed.
|
38
|
+
def process_skip exp
|
39
|
+
s()
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def process_rest exp
|
44
|
+
pos = 0
|
45
|
+
ret = s()
|
46
|
+
until pos >= exp.length
|
47
|
+
sexp = exp[pos]
|
48
|
+
pos += 1
|
49
|
+
psexps = process( sexp ) if Sexp === sexp
|
50
|
+
if !psexps.nil? && psexps.sexp_type == :dummy
|
51
|
+
# :dummy get flattened
|
52
|
+
psexps.rest.each do |psexp|
|
53
|
+
ret << psexp
|
54
|
+
end
|
55
|
+
else # non-dummy sexp || nil sexp
|
56
|
+
ret << psexps unless psexps.nil?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
ret
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'sexp_processor'
|
2
|
+
|
3
|
+
module Sbuilder
|
4
|
+
module Eth
|
5
|
+
|
6
|
+
# Sexp preprocessor find call statements in expression and add a
|
7
|
+
# separate call immediately before statement containing the
|
8
|
+
# expression
|
9
|
+
#
|
10
|
+
class SexpProcessorCallSeparator < SexpProcessorEth
|
11
|
+
|
12
|
+
# ------------------------------------------------------------------
|
13
|
+
# @!group Object state
|
14
|
+
|
15
|
+
|
16
|
+
# @!attribute [Ast::Statement] current statement
|
17
|
+
attr_accessor :currentStatement
|
18
|
+
|
19
|
+
|
20
|
+
# @!endgroup
|
21
|
+
|
22
|
+
|
23
|
+
# ------------------------------------------------------------------
|
24
|
+
# @!group Constructor
|
25
|
+
|
26
|
+
def initialize( options={} )
|
27
|
+
super( options )
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!endgroup
|
31
|
+
|
32
|
+
# ------------------------------------------------------------------
|
33
|
+
# @!group Scope hierarch utils
|
34
|
+
|
35
|
+
def wrapStatement( stmtSexp )
|
36
|
+
|
37
|
+
# remember 'prevScope', update enclosing scope, switch '@currentScope',
|
38
|
+
prevStatement = currentStatement
|
39
|
+
self.currentStatement = stmtSexp
|
40
|
+
|
41
|
+
begin
|
42
|
+
yield
|
43
|
+
ensure
|
44
|
+
self.currentStatement = prevStatement
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!endgroup
|
50
|
+
|
51
|
+
|
52
|
+
# ------------------------------------------------------------------
|
53
|
+
# @!group AST-node processors
|
54
|
+
|
55
|
+
|
56
|
+
# Add :FunctionCalls immediately before a statement, which has
|
57
|
+
# expressions with requiring :FunctionCall
|
58
|
+
#
|
59
|
+
# Implementation visits block statements AST nodes, and finds
|
60
|
+
# FunctionCalls expect for "calls without assignment"
|
61
|
+
# i.e. :ExpressionStatement with direct child[0] as
|
62
|
+
# :FunctionCall.
|
63
|
+
|
64
|
+
def process_Block( sexp )
|
65
|
+
|
66
|
+
logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
|
67
|
+
|
68
|
+
# Array of [Int, Array<:FunctionCall>]
|
69
|
+
callsToAdd = []
|
70
|
+
|
71
|
+
sexp.children.each_with_index do |stmt,i|
|
72
|
+
|
73
|
+
calls = process( stmt )
|
74
|
+
@logger.debug "\n\nstmt=#{stmt}\ncalls=#{calls}" if @logger.debug?
|
75
|
+
|
76
|
+
# collect index && :FunctionCalls lValueRequested
|
77
|
+
calls = unwrap(calls).select{ |s| s.sexp_type == :FunctionCall } || []
|
78
|
+
@logger.debug "#{__method__}: calls=#{calls}" if @logger.debug?
|
79
|
+
|
80
|
+
# build [ 0, [f1,f2] ], [ 1, [f3,f4, ..] ], ...
|
81
|
+
if calls.length
|
82
|
+
# normal function/constructor!!
|
83
|
+
logger.info "#{__method__}: add #{calls.length} function calls #{calls.map { |call| call.callTarget["name"] } }"
|
84
|
+
callsToAdd << [ i, calls ]
|
85
|
+
end
|
86
|
+
|
87
|
+
end # each with index
|
88
|
+
|
89
|
+
# Process in reverse order to keep index valid
|
90
|
+
callsToAdd.reverse.each do |posToAdd|
|
91
|
+
pos = posToAdd[0]
|
92
|
+
calls = posToAdd[1]
|
93
|
+
calls.reverse.each do |call|
|
94
|
+
logger.info "#{__method__}: insert pos #{pos}, call #{call}"
|
95
|
+
sexp.insertChild( pos, call )
|
96
|
+
logger.debug "#{__method__}: sexp.children #{sexp.children}" if logger.debug?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
s()
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# @!endgroup
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
# ------------------------------------------------------------------
|
110
|
+
# @!group Function found
|
111
|
+
|
112
|
+
|
113
|
+
def process_ExpressionStatement( sexp )
|
114
|
+
return s() if sexp.children[0].sexp_type == :FunctionCall
|
115
|
+
process_rest( sexp )
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_FunctionCall( sexp )
|
119
|
+
sexp
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
# @!endgroup
|
124
|
+
|
125
|
+
# ------------------------------------------------------------------
|
126
|
+
# @!group Helpers
|
127
|
+
|
128
|
+
# Recursively flatten 1 level until sArray is Array<Sexp> (with
|
129
|
+
# non-empty Sexps)
|
130
|
+
#
|
131
|
+
# @param sArray [Array<Array*<Sexp>>] Array
|
132
|
+
#
|
133
|
+
# @return [Array<Sexp>]
|
134
|
+
def unwrap( sArray )
|
135
|
+
|
136
|
+
# not an array - no change
|
137
|
+
return sArray unless sArray.is_a?(Array)
|
138
|
+
|
139
|
+
sArray = sArray.
|
140
|
+
# unwrap non sexps
|
141
|
+
map { |e| e.is_a?( Sexp) && e.sexp_type.is_a?( Symbol ) ? e : unwrap(e) }.
|
142
|
+
# reject empty elements
|
143
|
+
select { |e| e != [] }
|
144
|
+
|
145
|
+
# peel off proper outer arrays
|
146
|
+
sArray = sArray[0] while sArray.is_a?(Array) &&
|
147
|
+
sArray.length == 1 &&
|
148
|
+
sArray[0].is_a?(Array) && sArray[0].length == 1 && sArray[0][0].is_a?( Array)
|
149
|
+
|
150
|
+
|
151
|
+
sArray
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# @!endgroup
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
end # class
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'sexp_processor'
|
2
|
+
|
3
|
+
module Sbuilder
|
4
|
+
module Eth
|
5
|
+
|
6
|
+
# Sexp preprocessor, which adds getters for public variable
|
7
|
+
# declarations
|
8
|
+
#
|
9
|
+
class SexpProcessorGetter < SexpProcessorEth
|
10
|
+
|
11
|
+
def initialize( options={} )
|
12
|
+
super( options )
|
13
|
+
end
|
14
|
+
|
15
|
+
# Find public variable declarations without getter method
|
16
|
+
# {Sbuilder::Eth::SexpUtils.getterName}, add default getter to
|
17
|
+
# the contract using {Sbuilder::Eth::SexpUtils.sexpGetter}.
|
18
|
+
#
|
19
|
+
# Uses {#SexpProcessorGetterFinder} to get an array
|
20
|
+
# ':VariableDeclaration' and ':FunctionDefinition' sexps. Array
|
21
|
+
# contains also sexp_types :NONE for variable declaration which
|
22
|
+
# are not public.
|
23
|
+
|
24
|
+
def process_ContractDefinition( sexp )
|
25
|
+
logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
|
26
|
+
|
27
|
+
# find public variable declarations without getter
|
28
|
+
gettersToImplemt =
|
29
|
+
SexpProcessorGetterFinder.new(options).
|
30
|
+
process( sexp ).
|
31
|
+
# s() generated by sexp-processor default-method e.g modifiers
|
32
|
+
select { |sexp| sexp.length > 0 }.
|
33
|
+
# sieve :VariableDeclaration before :FunctionDefinition
|
34
|
+
sort { |s1,s2| -1 * (s1.sexp_type.to_s <=> s2.sexp_type.to_s) }.
|
35
|
+
# sieve builds a hash getter name -> VariableDeclaration
|
36
|
+
reduce( {} ) do |getters,sexp|
|
37
|
+
case sexp.sexp_type
|
38
|
+
when :VariableDeclaration
|
39
|
+
# should implement getter with 'getterName'
|
40
|
+
getters[ SexpUtils.getterName( sexp['name']) ] = sexp
|
41
|
+
when :FunctionDefinition
|
42
|
+
# getter already implemented - remove
|
43
|
+
getters[ sexp["name"] ] = nil
|
44
|
+
when :NONE
|
45
|
+
# getter not needed for a variable declation
|
46
|
+
else
|
47
|
+
raise "Unexppected sexp-type #{sexp.sexp_type} for #{sexp}"
|
48
|
+
end
|
49
|
+
getters
|
50
|
+
end.
|
51
|
+
# create getter only for var-decls without getter
|
52
|
+
select do |k,sexp|
|
53
|
+
!sexp.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
logger.info "#{__method__}:gettersToImplemt=#{gettersToImplemt.keys.join(',')}"
|
57
|
+
|
58
|
+
# add getter method to contract 'sexp'
|
59
|
+
gettersToImplemt.each do |name, variableDeclarationSexp|
|
60
|
+
# create getter for contract 'sexp'
|
61
|
+
sexp.addChild( SexpUtils.sexpGetter( variableDeclarationSexp["name"]) )
|
62
|
+
end
|
63
|
+
sexp
|
64
|
+
end # process_ContractDefinition( sexp )
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get an array ':VariableDeclaration' and ':FunctionDefinition'
|
69
|
+
# sexps. Array contains also sexp_types :NONE for variable
|
70
|
+
# declaration which are not public.
|
71
|
+
|
72
|
+
class SexpProcessorGetterFinder < SexpProcessorEth
|
73
|
+
# ------------------------------------------------------------------
|
74
|
+
# @!group Constructor
|
75
|
+
|
76
|
+
def initialize( options={} )
|
77
|
+
super( options )
|
78
|
+
|
79
|
+
# Process known sexp
|
80
|
+
self.default_method = :process_skip
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# @!endgroup
|
86
|
+
|
87
|
+
# ------------------------------------------------------------------
|
88
|
+
# @!group Resolvers for reference node types
|
89
|
+
|
90
|
+
def process_ContractDefinition( sexp )
|
91
|
+
process_rest( sexp )
|
92
|
+
end
|
93
|
+
|
94
|
+
# We know that we are processing 'VariableDeclaration' for a
|
95
|
+
# contrcact (because we have intercepted FunctionDefinition in
|
96
|
+
# {#process_FunctionDefinition}). Create getter for public
|
97
|
+
# variable declaration.
|
98
|
+
#
|
99
|
+
# @return [AstSexp(:VariableDeclaration), AstSexp(:NONE)] :NONE
|
100
|
+
# if no getter needed
|
101
|
+
def process_VariableDeclaration( sexp )
|
102
|
+
logger.debug "#{__method__}: sexp=#{sexp}" if @logger.debug?
|
103
|
+
logger.info "#{__method__}: variable name =#{sexp['name']}, visibility #{sexp['visibility']}"
|
104
|
+
if sexp["visibility"] == Constants::SOL_VISIBILITY_PUBLIC
|
105
|
+
return sexp
|
106
|
+
end
|
107
|
+
# not public --> no getter needed
|
108
|
+
s(:NONE)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Intercept 'FunctionDefinition' to prevent recursion into
|
112
|
+
# variable declaration for function parameters and locals. just
|
113
|
+
# return so that we can check, whether some getter already
|
114
|
+
# defined
|
115
|
+
#
|
116
|
+
def process_FunctionDefinition( sexp )
|
117
|
+
sexp
|
118
|
+
end
|
119
|
+
|
120
|
+
# @!endgroup
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|