borx 0.0.1.alpha1
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.
- data/lib/borx.rb +6 -0
- data/lib/borx/code.rb +22 -0
- data/lib/borx/environment.rb +108 -0
- data/lib/borx/rewriter.rb +193 -0
- metadata +99 -0
data/lib/borx.rb
ADDED
data/lib/borx/code.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# A simple class to tell the difference between rewritten and raw code.
|
2
|
+
class Borx::Code
|
3
|
+
|
4
|
+
attr :code
|
5
|
+
|
6
|
+
def initialize(code)
|
7
|
+
@code = code
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash
|
11
|
+
code.hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def eql?(other)
|
15
|
+
code.eql?(other.code)
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"#<Borx::Code #{code.inspect}>"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
base = Class.new do
|
2
|
+
|
3
|
+
def get_constant(binding, name, house = nil)
|
4
|
+
raise Borx::Environment::NotImplemented::GetConstant
|
5
|
+
end
|
6
|
+
|
7
|
+
def set_constant(binding, name, house = nil, value)
|
8
|
+
raise Borx::Environment::NotImplemented::SetConstant
|
9
|
+
end
|
10
|
+
|
11
|
+
def call_method(binding, receiver, name, *args, &block)
|
12
|
+
raise Borx::Environment::NotImplemented::CallMethod
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_variable(binding, name)
|
16
|
+
raise Borx::Environment::NotImplemented::GetVariable
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_variable(binding, name, value)
|
20
|
+
raise Borx::Environment::NotImplemented::SetVariable
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class Borx::Environment < base
|
26
|
+
|
27
|
+
Base = superclass
|
28
|
+
|
29
|
+
module GetVariable
|
30
|
+
|
31
|
+
def get_variable(binding, name)
|
32
|
+
binding.eval(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
module SetVariable
|
38
|
+
|
39
|
+
def set_variable(binding, name, value)
|
40
|
+
binding.eval("#{name} = nil ; lambda{|_x| #{name} = _x}").call(value)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
module GetSetVariable
|
46
|
+
include GetVariable
|
47
|
+
include SetVariable
|
48
|
+
end
|
49
|
+
|
50
|
+
module CallPublicMethod
|
51
|
+
def call_method(binding, receiver, name ,*args, &block)
|
52
|
+
receiver.__send__(name, *args, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module CallPrivateMethod
|
57
|
+
def call_private_method(binding, receiver, name ,*args, &block)
|
58
|
+
receiver.__send__(name, *args, &block)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module CallMethod
|
63
|
+
include CallPublicMethod
|
64
|
+
include CallPrivateMethod
|
65
|
+
end
|
66
|
+
|
67
|
+
module GetConstant
|
68
|
+
def get_constant(binding, name, house = Object)
|
69
|
+
house.const_get(name)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module GetSetConstant
|
74
|
+
include GetConstant
|
75
|
+
end
|
76
|
+
|
77
|
+
include GetSetVariable
|
78
|
+
include GetSetConstant
|
79
|
+
include CallMethod
|
80
|
+
|
81
|
+
class NotImplemented < StandardError
|
82
|
+
class GetConstant < self
|
83
|
+
end
|
84
|
+
class SetConstant < self
|
85
|
+
end
|
86
|
+
class CallMethod < self
|
87
|
+
end
|
88
|
+
class GetVariable < self
|
89
|
+
end
|
90
|
+
class SetVariable < self
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def eval(code, bin_ding = nil)
|
95
|
+
bin_ding ||= proc{}.binding
|
96
|
+
eval_code( Borx::Rewriter.rewrite(code), bin_ding)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def eval_code(code, binding, file = '(eval)', line = 0)
|
101
|
+
old_borx, setter = binding.eval('__borx__ ||= nil ; [__borx__, lambda{|v| __borx__ = v}]')
|
102
|
+
setter.call(self)
|
103
|
+
binding.eval(code.code, file, line)
|
104
|
+
ensure
|
105
|
+
setter.call(old_borx) if setter
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
begin
|
2
|
+
require 'ripper'
|
3
|
+
rescue NotFound
|
4
|
+
raise NotFound, "Ripper extension not found. Please add it to your bundle."
|
5
|
+
end
|
6
|
+
require 'sorcerer'
|
7
|
+
require 'borx/code'
|
8
|
+
class Borx::Rewriter < Ripper::SexpBuilder
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
ARGS_NEW = [:args_new].freeze
|
12
|
+
|
13
|
+
def self.rewrite(code, options = {})
|
14
|
+
tree = self.new(code).parse
|
15
|
+
code = Sorcerer.source(tree, options)
|
16
|
+
return Borx::Code.new(code)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def on_top_const_ref(*args)
|
22
|
+
ref = super
|
23
|
+
return call_borx('get_constant',
|
24
|
+
[:args_add,
|
25
|
+
[:args_add, ARGS_NEW, ident_to_string(ref[1])],
|
26
|
+
[:@const, "Object", [0,0]]])
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def on_const_path_ref(*args)
|
31
|
+
path = super
|
32
|
+
return call_borx('get_constant',[:args_add, [:args_add, ARGS_NEW, ident_to_string(path[2])], path[1]])
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_method_add_arg(*x)
|
36
|
+
name, call, args, block = super
|
37
|
+
inject_binding = true
|
38
|
+
if call[0] == :fcall
|
39
|
+
args = args[1][1] unless args == ARGS_NEW
|
40
|
+
brgs = push_args(args,[:var_ref, [:@kw, "self", [0, 0]]],ident_to_string(call[1]))
|
41
|
+
return call_borx('call_private_method', brgs)
|
42
|
+
elsif call[0] == :method_add_arg
|
43
|
+
# this case doesn't show up in the wild
|
44
|
+
# it exists because of the way we rewrite calls
|
45
|
+
inject_binding = false
|
46
|
+
brgs = push_args(args[1][1], call[2][1][1])
|
47
|
+
else
|
48
|
+
# :nocov:
|
49
|
+
raise "Unknown call type #{call}. This is bug. Please report it"
|
50
|
+
# :nocov:
|
51
|
+
end
|
52
|
+
return call_borx('call_method', brgs, binding: inject_binding)
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_call(*args)
|
56
|
+
call = super
|
57
|
+
return call_borx('call_method',
|
58
|
+
[:args_add,
|
59
|
+
[:args_add,[:args_new],call[1]],
|
60
|
+
ident_to_string(call[3])])
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_vcall(vcall)
|
64
|
+
s = super
|
65
|
+
return call_borx('get_variable', [:args_add, [:args_new], ident_to_string(s[1])])
|
66
|
+
end
|
67
|
+
|
68
|
+
def on_var_ref(x)
|
69
|
+
var = super
|
70
|
+
fun = case(var[1][0])
|
71
|
+
when :@const then 'get_constant'
|
72
|
+
when :@gvar then 'get_global_variable'
|
73
|
+
when :@cvar then 'get_class_variable'
|
74
|
+
when :@ivar then 'get_instance_variable'
|
75
|
+
when :@ident then 'get_variable'
|
76
|
+
when :@kw then 'get_magic'
|
77
|
+
# :nocov:
|
78
|
+
else
|
79
|
+
raise "Unknown var_ref type in #{var[1]}, this is a bug, please report it"
|
80
|
+
# :nocov:
|
81
|
+
end
|
82
|
+
return call_borx(fun, [:args_add, [:args_new], ident_to_string(var[1])])
|
83
|
+
end
|
84
|
+
|
85
|
+
def on_assign(field, value)
|
86
|
+
assign, field, value = super
|
87
|
+
if field[0] == :var_field
|
88
|
+
fun = case(field[1][0])
|
89
|
+
when :@const then 'set_constant'
|
90
|
+
when :@gvar then 'set_global_variable'
|
91
|
+
when :@cvar then 'set_class_variable'
|
92
|
+
when :@ivar then 'set_instance_variable'
|
93
|
+
when :@ident then 'set_variable'
|
94
|
+
# :nocov:
|
95
|
+
else
|
96
|
+
raise "Unknown var_field type in #{var[1]}, this is a bug, please report it"
|
97
|
+
# :nocov:
|
98
|
+
end
|
99
|
+
return call_borx(fun,
|
100
|
+
[:args_add,
|
101
|
+
[:args_add, [:args_new],
|
102
|
+
ident_to_string(field[1])], value])
|
103
|
+
elsif field[0] == :field
|
104
|
+
return call_borx('call_method',
|
105
|
+
[:args_add,
|
106
|
+
[:args_add,
|
107
|
+
[:args_add,[:args_new],field[1]],
|
108
|
+
[:string_literal,[:string_add,[:string_content],[:@tstring_content,field[3][1]+"=",field[3][2]]] ]
|
109
|
+
], value
|
110
|
+
])
|
111
|
+
elsif field[0] == :const_path_field
|
112
|
+
return call_borx('set_constant',
|
113
|
+
[:args_add,
|
114
|
+
[:args_add,
|
115
|
+
[:args_add,[:args_new],ident_to_string(field[2])],
|
116
|
+
field[1]
|
117
|
+
], value
|
118
|
+
])
|
119
|
+
elsif field[0] == :top_const_field
|
120
|
+
return call_borx('set_constant',
|
121
|
+
[:args_add,
|
122
|
+
[:args_add,
|
123
|
+
[:args_add, [:args_new],
|
124
|
+
ident_to_string(field[1])],
|
125
|
+
[:@const, 'Object',[0,0]]
|
126
|
+
], value])
|
127
|
+
end
|
128
|
+
# :nocov:
|
129
|
+
raise "Unknown assign type #{field}. This is a bug. Please report it"
|
130
|
+
# :nocov:
|
131
|
+
end
|
132
|
+
|
133
|
+
def on_xstring_literal(x)
|
134
|
+
x = super
|
135
|
+
return call_borx('execute', [:args_add, [:args_new], xstring_to_string(x)])
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_stmts_new
|
139
|
+
return [:stmts_add, [:stmts_new], [:assign,
|
140
|
+
[:var_field, [:@ident, "__borx_binding__", [1, 0]]],
|
141
|
+
[:vcall, [:@ident, "binding", [1, 17]]]]]
|
142
|
+
end
|
143
|
+
|
144
|
+
def xstring_to_string(x)
|
145
|
+
if x[0] == :xstring_literal
|
146
|
+
return [:string_literal, xstring_to_string(x[1]), *x[2..-1]]
|
147
|
+
elsif x[0] == :xstring_add
|
148
|
+
return [:string_add, xstring_to_string(x[1]), *x[2..-1]]
|
149
|
+
elsif x[0] == :xstring_new
|
150
|
+
return [:string_content]
|
151
|
+
else
|
152
|
+
# :nocov:
|
153
|
+
raise "Unknown xstring type #{x}. This is a bug. Please report it."
|
154
|
+
# :nocov:
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def call_borx(name,args, options = {})
|
159
|
+
args = push_args(args, binding_args) unless options[:binding] == false
|
160
|
+
return [:method_add_arg,
|
161
|
+
[:call, [:var_ref, [:@ident, "__borx__", [0, 0]]], :'.', [:@ident, name, [0,0]]],
|
162
|
+
[:arg_paren,
|
163
|
+
[:args_add_block,
|
164
|
+
args,
|
165
|
+
options.fetch(:block, false)
|
166
|
+
]
|
167
|
+
]
|
168
|
+
]
|
169
|
+
end
|
170
|
+
|
171
|
+
def binding_args
|
172
|
+
return [:var_field, [:@ident, "__borx_binding__", [1, 0]]]
|
173
|
+
end
|
174
|
+
|
175
|
+
def ident_to_string(ident)
|
176
|
+
[:string_literal,
|
177
|
+
[:string_add,[:string_content],
|
178
|
+
[:@tstring_content, ident[1], ident[2]]]]
|
179
|
+
end
|
180
|
+
|
181
|
+
def push_args(tree, *args)
|
182
|
+
if tree == [:args_new]
|
183
|
+
if args.any?
|
184
|
+
return [:args_add, push_args(tree, *args[0..-2]), args.last]
|
185
|
+
else
|
186
|
+
return tree
|
187
|
+
end
|
188
|
+
else
|
189
|
+
return [:args_add, push_args(tree[1], *args), tree[2]]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: borx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha1
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Hannes Georg
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sorcerer
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.3.10
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.3.10
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: simplecov
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: |
|
63
|
+
This plugin parses a piece of ruby code with ripper, rewrites it a bit and converts it back to ruby code.
|
64
|
+
This lets a central object decide what the code is actually doing.
|
65
|
+
email: hannes.georg@googlemail.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- lib/borx/environment.rb
|
71
|
+
- lib/borx/code.rb
|
72
|
+
- lib/borx/rewriter.rb
|
73
|
+
- lib/borx.rb
|
74
|
+
homepage: https://github.com/hannesg/borx
|
75
|
+
licenses: []
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - '>'
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 1.3.1
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.8.25
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: rewrites ruby code so you can decide what it actually does
|
98
|
+
test_files: []
|
99
|
+
has_rdoc:
|