sbuilder-eth 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.org +19 -0
- data/VERSION +1 -0
- data/bin/state-explorer.sh +215 -0
- data/lib/eth/al_api.rb +12 -0
- data/lib/eth/ast_sexp.rb +519 -0
- data/lib/eth/constants.rb +354 -0
- data/lib/eth/ethereum.rb +2054 -0
- data/lib/eth/ethereum_expression.rb +476 -0
- data/lib/eth/exception.rb +9 -0
- data/lib/eth/global_scope.rb +100 -0
- data/lib/eth/module.rb +18 -0
- data/lib/eth/sexp_processor.rb +66 -0
- data/lib/eth/sexp_processor_call_separator.rb +163 -0
- data/lib/eth/sexp_processor_getter.rb +125 -0
- data/lib/eth/sexp_processor_resolve.rb +122 -0
- data/lib/eth/sexp_processor_scope.rb +355 -0
- data/lib/eth/sexp_utils.rb +214 -0
- data/lib/eth/solidity_compiler.rb +145 -0
- data/lib/eth/solidity_loader.rb +53 -0
- data/lib/eth/solidity_translator.rb +840 -0
- data/lib/mixer/reference.rb +30 -0
- data/lib/mixer/scope.rb +100 -0
- data/lib/mixer/scoped.rb +18 -0
- data/lib/plugin/controller.rb +267 -0
- data/lib/plugin/module.rb +3 -0
- data/lib/plugin/plugin.rb +33 -0
- data/lib/sbuilder-eth.rb +6 -0
- data/sbuilder-eth.gemspec +38 -0
- metadata +149 -0
@@ -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
|
+
|