borx 0.0.1.alpha1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/borx.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Borx
2
+
3
+ end
4
+
5
+ require 'borx/environment'
6
+ require 'borx/rewriter'
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: