boc 0.4.2-java
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/CHANGES.rdoc +21 -0
- data/README.rdoc +119 -0
- data/Rakefile +139 -0
- data/devel/levitate.rb +852 -0
- data/ext/boc/boc.c +136 -0
- data/ext/boc/extconf.rb +2 -0
- data/jext/boc/BocService.java +111 -0
- data/lib/boc.rb +108 -0
- data/lib/boc/binding_of_caller.rb +11 -0
- data/lib/boc/boc.jar +0 -0
- data/lib/boc/version.rb +3 -0
- data/test/basic_test.rb +209 -0
- data/test/jruby_trial.rb +20 -0
- data/test/main.rb +56 -0
- data/test/readme_test.rb +9 -0
- data/test/shim_test.rb +26 -0
- metadata +88 -0
data/ext/boc/boc.c
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
VALUE
|
4
|
+
rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv) ;
|
5
|
+
|
6
|
+
static VALUE cBoc ;
|
7
|
+
|
8
|
+
static VALUE basic_object_method_sym ;
|
9
|
+
|
10
|
+
struct dispatch_args
|
11
|
+
{
|
12
|
+
int argc ;
|
13
|
+
VALUE* argv ;
|
14
|
+
VALUE self ;
|
15
|
+
ID method_id ;
|
16
|
+
} ;
|
17
|
+
|
18
|
+
static VALUE
|
19
|
+
begin_section( VALUE data )
|
20
|
+
{
|
21
|
+
struct dispatch_args* dargs = (struct dispatch_args*)data ;
|
22
|
+
|
23
|
+
return rb_funcall_passing_block(
|
24
|
+
dargs->self,
|
25
|
+
dargs->method_id,
|
26
|
+
dargs->argc,
|
27
|
+
dargs->argv) ;
|
28
|
+
}
|
29
|
+
|
30
|
+
static VALUE
|
31
|
+
ensure_section( VALUE unused )
|
32
|
+
{
|
33
|
+
rb_ary_pop(
|
34
|
+
rb_funcall(
|
35
|
+
cBoc,
|
36
|
+
rb_intern("stack"),
|
37
|
+
0)) ;
|
38
|
+
|
39
|
+
return Qnil ;
|
40
|
+
}
|
41
|
+
|
42
|
+
static VALUE
|
43
|
+
dispatch_common( VALUE method_sym, int argc, VALUE *argv, VALUE self )
|
44
|
+
{
|
45
|
+
struct dispatch_args dargs ;
|
46
|
+
|
47
|
+
dargs.argc = argc ;
|
48
|
+
dargs.argv = argv ;
|
49
|
+
dargs.self = self ;
|
50
|
+
|
51
|
+
dargs.method_id =
|
52
|
+
rb_to_id(
|
53
|
+
rb_str_plus(
|
54
|
+
rb_sym_to_s(method_sym),
|
55
|
+
rb_str_new2("__impl"))) ;
|
56
|
+
|
57
|
+
rb_ary_push(
|
58
|
+
rb_funcall(
|
59
|
+
cBoc,
|
60
|
+
rb_intern("stack"),
|
61
|
+
0),
|
62
|
+
rb_binding_new()) ;
|
63
|
+
|
64
|
+
return rb_ensure(
|
65
|
+
begin_section,
|
66
|
+
(VALUE)&dargs,
|
67
|
+
ensure_section,
|
68
|
+
Qnil) ;
|
69
|
+
}
|
70
|
+
|
71
|
+
static VALUE
|
72
|
+
dispatch_normal( int argc, VALUE *argv, VALUE self )
|
73
|
+
{
|
74
|
+
return dispatch_common(
|
75
|
+
rb_funcall(
|
76
|
+
self,
|
77
|
+
rb_intern("__method__"),
|
78
|
+
0),
|
79
|
+
argc,
|
80
|
+
argv,
|
81
|
+
self) ;
|
82
|
+
}
|
83
|
+
|
84
|
+
static VALUE
|
85
|
+
dispatch_basic_object(int argc, VALUE *argv, VALUE self)
|
86
|
+
{
|
87
|
+
return dispatch_common(
|
88
|
+
basic_object_method_sym,
|
89
|
+
argc,
|
90
|
+
argv,
|
91
|
+
self) ;
|
92
|
+
}
|
93
|
+
|
94
|
+
static VALUE
|
95
|
+
enable_ext( VALUE self, VALUE klass, VALUE method_sym )
|
96
|
+
{
|
97
|
+
rb_define_method(
|
98
|
+
klass,
|
99
|
+
RSTRING_PTR(rb_sym_to_s(method_sym)),
|
100
|
+
dispatch_normal,
|
101
|
+
-1) ;
|
102
|
+
|
103
|
+
return Qnil ;
|
104
|
+
}
|
105
|
+
|
106
|
+
static VALUE
|
107
|
+
enable_basic_object_ext( VALUE self, VALUE klass, VALUE method_sym )
|
108
|
+
{
|
109
|
+
basic_object_method_sym = method_sym ;
|
110
|
+
|
111
|
+
rb_define_method(
|
112
|
+
klass,
|
113
|
+
RSTRING_PTR(rb_sym_to_s(method_sym)),
|
114
|
+
dispatch_basic_object,
|
115
|
+
-1) ;
|
116
|
+
|
117
|
+
return Qnil ;
|
118
|
+
}
|
119
|
+
|
120
|
+
void
|
121
|
+
Init_boc()
|
122
|
+
{
|
123
|
+
cBoc = rb_define_module("Boc") ;
|
124
|
+
|
125
|
+
rb_define_singleton_method(
|
126
|
+
cBoc,
|
127
|
+
"enable_ext",
|
128
|
+
enable_ext,
|
129
|
+
2) ;
|
130
|
+
|
131
|
+
rb_define_singleton_method(
|
132
|
+
cBoc,
|
133
|
+
"enable_basic_object_ext",
|
134
|
+
enable_basic_object_ext,
|
135
|
+
2) ;
|
136
|
+
}
|
data/ext/boc/extconf.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
package boc ;
|
2
|
+
|
3
|
+
import org.jruby.Ruby ;
|
4
|
+
import org.jruby.RubyModule ;
|
5
|
+
import org.jruby.RubyBinding ;
|
6
|
+
import org.jruby.RubySymbol ;
|
7
|
+
import org.jruby.RubyClass ;
|
8
|
+
import org.jruby.anno.JRubyMethod ;
|
9
|
+
import org.jruby.exceptions.RaiseException ;
|
10
|
+
import org.jruby.runtime.Arity ;
|
11
|
+
import org.jruby.runtime.Block ;
|
12
|
+
import org.jruby.runtime.ThreadContext ;
|
13
|
+
import org.jruby.runtime.callback.Callback ;
|
14
|
+
import org.jruby.runtime.builtin.IRubyObject ;
|
15
|
+
import org.jruby.runtime.load.BasicLibraryService ;
|
16
|
+
|
17
|
+
public class BocService implements BasicLibraryService
|
18
|
+
{
|
19
|
+
static RubyModule s_boc ;
|
20
|
+
static RubyClass s_removed_error ;
|
21
|
+
|
22
|
+
public boolean basicLoad( Ruby ruby )
|
23
|
+
{
|
24
|
+
s_boc = ruby.defineModule("Boc") ;
|
25
|
+
|
26
|
+
s_boc.getSingletonClass().
|
27
|
+
defineAnnotatedMethods(SingletonMethods.class) ;
|
28
|
+
|
29
|
+
s_removed_error = ruby.defineClassUnder(
|
30
|
+
"InformationRemovedError",
|
31
|
+
ruby.getException(),
|
32
|
+
ruby.getException().getAllocator(),
|
33
|
+
s_boc) ;
|
34
|
+
|
35
|
+
return true ;
|
36
|
+
}
|
37
|
+
|
38
|
+
public static class SingletonMethods
|
39
|
+
{
|
40
|
+
@JRubyMethod( name = "enable_ext", alias = {"enable_basic_object_ext"} )
|
41
|
+
public static IRubyObject
|
42
|
+
s_enable_ext( IRubyObject recv, IRubyObject klass, IRubyObject sym )
|
43
|
+
{
|
44
|
+
String method_name = ((RubySymbol)sym).toString() ;
|
45
|
+
|
46
|
+
((RubyModule)klass).defineMethod(
|
47
|
+
method_name,
|
48
|
+
new Dispatcher(method_name + "__impl")) ;
|
49
|
+
|
50
|
+
return recv.getRuntime().getNil() ;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
public static class Dispatcher implements Callback
|
55
|
+
{
|
56
|
+
String m_impl ;
|
57
|
+
|
58
|
+
public Dispatcher( String impl )
|
59
|
+
{
|
60
|
+
m_impl = impl ;
|
61
|
+
}
|
62
|
+
|
63
|
+
public IRubyObject
|
64
|
+
execute( IRubyObject recv, IRubyObject[] args, Block block )
|
65
|
+
{
|
66
|
+
Ruby ruby = recv.getRuntime() ;
|
67
|
+
ThreadContext context = ruby.getCurrentContext() ;
|
68
|
+
IRubyObject stack = s_boc.callMethod(context, "stack") ;
|
69
|
+
|
70
|
+
stack.callMethod(
|
71
|
+
context,
|
72
|
+
"push",
|
73
|
+
RubyBinding.newBinding(ruby, context.previousBinding())) ;
|
74
|
+
|
75
|
+
try
|
76
|
+
{
|
77
|
+
return recv.callMethod(context, m_impl, args, block) ;
|
78
|
+
}
|
79
|
+
catch( java.lang.NullPointerException e )
|
80
|
+
{
|
81
|
+
throw new RaiseException(
|
82
|
+
ruby, s_removed_error, s_error_msg, true) ;
|
83
|
+
}
|
84
|
+
finally
|
85
|
+
{
|
86
|
+
stack.callMethod(context, "pop") ;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
public Arity getArity()
|
91
|
+
{
|
92
|
+
return Arity.OPTIONAL ;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
static String s_error_msg = "\n" +
|
97
|
+
"\n" +
|
98
|
+
"__________________________________________________________________\n" +
|
99
|
+
"Boc (binding of caller) failed because JRuby removed the necessary\n" +
|
100
|
+
"information. To prevent this from happening, pass the following\n" +
|
101
|
+
"command-line flag to jruby:\n" +
|
102
|
+
"\n" +
|
103
|
+
" -J-Djruby.astInspector.enabled=false\n" +
|
104
|
+
"\n" +
|
105
|
+
"Alternatively, place a do-nothing block somewhere in the caller:\n" +
|
106
|
+
"\n" +
|
107
|
+
" p { }\n" +
|
108
|
+
" # ...your code...\n" +
|
109
|
+
"\n" +
|
110
|
+
"==================================================================\n" ;
|
111
|
+
}
|
data/lib/boc.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'boc/boc'
|
2
|
+
|
3
|
+
module Boc
|
4
|
+
#
|
5
|
+
# :singleton-method: enable
|
6
|
+
# :call-seq: enable(klass, method_name)
|
7
|
+
#
|
8
|
+
# Enable <code>Boc.value</code> for the given instance method.
|
9
|
+
#
|
10
|
+
# class A
|
11
|
+
# def f
|
12
|
+
# p eval("x", Boc.value)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def self.g
|
16
|
+
# p eval("x", Boc.value)
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Boc.enable A, :f
|
21
|
+
# Boc.enable A.singleton_class, :g
|
22
|
+
#
|
23
|
+
# x = 33
|
24
|
+
# A.new.f # => 33
|
25
|
+
# A.g # => 33
|
26
|
+
#
|
27
|
+
|
28
|
+
#
|
29
|
+
# <code>Boc.value</code> was called outside of an
|
30
|
+
# <code>enable</code>d method.
|
31
|
+
#
|
32
|
+
class NotEnabledError < StandardError
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# The method given to <code>Boc.enable</code> appears to have been
|
37
|
+
# already enabled.
|
38
|
+
#
|
39
|
+
class AlreadyEnabledError < StandardError
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
#
|
44
|
+
# Returns the binding of the caller. May only be used within an
|
45
|
+
# <code>enable</code>d method.
|
46
|
+
#
|
47
|
+
def value
|
48
|
+
if stack.empty?
|
49
|
+
raise NotEnabledError, "Boc.value called outside of an enabled method"
|
50
|
+
end
|
51
|
+
stack.last
|
52
|
+
end
|
53
|
+
|
54
|
+
[:enable, :enable_basic_object].each do |def_method|
|
55
|
+
define_method def_method do |klass, method_name|
|
56
|
+
MODULE_EVAL.bind(klass).call do
|
57
|
+
visibility = Boc.visibility klass, method_name
|
58
|
+
|
59
|
+
impl = "#{method_name}__impl"
|
60
|
+
Boc.check_enabled klass, method_name, impl
|
61
|
+
|
62
|
+
Boc.no_warn { alias_method impl, method_name }
|
63
|
+
Boc.send "#{def_method}_ext", klass, method_name
|
64
|
+
|
65
|
+
send visibility, method_name
|
66
|
+
public impl # needs to work with rb_funcall_passing_block
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def stack #:nodoc:
|
72
|
+
Thread.current[:_boc_stack] ||= []
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# squelch alias warnings
|
77
|
+
#
|
78
|
+
def no_warn #:nodoc:
|
79
|
+
prev = $VERBOSE
|
80
|
+
$VERBOSE = nil
|
81
|
+
begin
|
82
|
+
yield
|
83
|
+
ensure
|
84
|
+
$VERBOSE = prev
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def visibility(klass, method_name) #:nodoc:
|
89
|
+
if klass.public_instance_methods.include?(method_name)
|
90
|
+
:public
|
91
|
+
elsif klass.protected_instance_methods.include?(method_name)
|
92
|
+
:protected
|
93
|
+
else
|
94
|
+
:private
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def check_enabled(klass, method_name, impl) #:nodoc:
|
99
|
+
if klass.method_defined?(impl) or klass.private_method_defined?(impl)
|
100
|
+
raise AlreadyEnabledError,
|
101
|
+
"Boc.enable: refusing to overwrite `#{impl}' -- " <<
|
102
|
+
"method `#{method_name}' appears to be already enabled"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
MODULE_EVAL = Module.instance_method(:module_eval) #:nodoc:
|
108
|
+
end
|
data/lib/boc/boc.jar
ADDED
Binary file
|
data/lib/boc/version.rb
ADDED
data/test/basic_test.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require_relative 'main'
|
2
|
+
|
3
|
+
class BasicTest < BocTest
|
4
|
+
MEMO = {}
|
5
|
+
|
6
|
+
def setup
|
7
|
+
MEMO.clear
|
8
|
+
end
|
9
|
+
|
10
|
+
class A
|
11
|
+
def f(y, z)
|
12
|
+
MEMO[:x] = eval("x", Boc.value)
|
13
|
+
MEMO[:y] = y
|
14
|
+
MEMO[:z] = z
|
15
|
+
66
|
16
|
+
end
|
17
|
+
|
18
|
+
def g
|
19
|
+
x = 33
|
20
|
+
f(44, 55)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_basic
|
25
|
+
assert_raises Boc::NotEnabledError do
|
26
|
+
Boc.value
|
27
|
+
end
|
28
|
+
|
29
|
+
Boc.enable A, :f
|
30
|
+
|
31
|
+
assert_nil MEMO[:x]
|
32
|
+
assert_nil MEMO[:y]
|
33
|
+
assert_nil MEMO[:z]
|
34
|
+
|
35
|
+
assert_equal 66, A.new.g
|
36
|
+
|
37
|
+
assert_equal 33, MEMO[:x]
|
38
|
+
assert_equal 44, MEMO[:y]
|
39
|
+
assert_equal 55, MEMO[:z]
|
40
|
+
|
41
|
+
assert_raises Boc::NotEnabledError do
|
42
|
+
Boc.value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class B
|
47
|
+
def foo(*args, &block)
|
48
|
+
MEMO[:args] = args
|
49
|
+
MEMO[:block] = block
|
50
|
+
eval("u", Boc.value) ;
|
51
|
+
end
|
52
|
+
|
53
|
+
def bar
|
54
|
+
u = 66
|
55
|
+
foo(77, 88) { |s| s + "zzz" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_explicit_block
|
60
|
+
Boc.enable B, :foo
|
61
|
+
|
62
|
+
assert_nil MEMO[:args]
|
63
|
+
assert_nil MEMO[:block]
|
64
|
+
|
65
|
+
assert_equal 66, B.new.bar
|
66
|
+
assert_equal [77, 88], MEMO[:args]
|
67
|
+
assert_equal "zoozzz", MEMO[:block].call("zoo")
|
68
|
+
end
|
69
|
+
|
70
|
+
class C
|
71
|
+
def foo(*args)
|
72
|
+
MEMO[:args] = args
|
73
|
+
MEMO[:yield_result] = yield "moo"
|
74
|
+
eval("u", Boc.value) ;
|
75
|
+
end
|
76
|
+
|
77
|
+
def bar
|
78
|
+
u = 66
|
79
|
+
foo(77, 88) { |s| s + "zzz" }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_implicit_block
|
84
|
+
Boc.enable C, :foo
|
85
|
+
|
86
|
+
assert_nil MEMO[:args]
|
87
|
+
assert_nil MEMO[:yield_result]
|
88
|
+
|
89
|
+
assert_equal 66, C.new.bar
|
90
|
+
assert_equal [77, 88], MEMO[:args]
|
91
|
+
assert_equal "moozzz", MEMO[:yield_result]
|
92
|
+
end
|
93
|
+
|
94
|
+
module R
|
95
|
+
def self.factorial_of_x
|
96
|
+
x = eval("x", Boc.value) -
|
97
|
+
if caller.grep(/#{__method__}/).size == (RUBY_ENGINE == "jruby" ? 0 : 1)
|
98
|
+
0
|
99
|
+
else
|
100
|
+
1
|
101
|
+
end
|
102
|
+
|
103
|
+
if x == 0
|
104
|
+
1
|
105
|
+
else
|
106
|
+
x*factorial_of_x
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_recursive
|
112
|
+
Boc.enable R.singleton_class, :factorial_of_x
|
113
|
+
|
114
|
+
x = 5
|
115
|
+
assert_equal 120, R.factorial_of_x
|
116
|
+
x = 4
|
117
|
+
assert_equal 24, R.factorial_of_x
|
118
|
+
x = 1
|
119
|
+
assert_equal 1, R.factorial_of_x
|
120
|
+
x = 0
|
121
|
+
assert_equal 1, R.factorial_of_x
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_basic_object
|
125
|
+
begin
|
126
|
+
BasicObject.module_eval do
|
127
|
+
def zoofoo
|
128
|
+
::Kernel.eval("z", ::Boc.value)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
Boc.enable_basic_object BasicObject, :zoofoo
|
133
|
+
z = 77
|
134
|
+
assert_equal 77, BasicObject.new.zoofoo
|
135
|
+
ensure
|
136
|
+
BasicObject.module_eval do
|
137
|
+
remove_method :zoofoo
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class D
|
143
|
+
public
|
144
|
+
def f ; end
|
145
|
+
|
146
|
+
protected
|
147
|
+
def g ; end
|
148
|
+
|
149
|
+
private
|
150
|
+
def h ; end
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_visibility
|
154
|
+
Boc.enable D, :f
|
155
|
+
Boc.enable D, :g
|
156
|
+
Boc.enable D, :h
|
157
|
+
|
158
|
+
assert D.public_instance_methods.include?(:f)
|
159
|
+
assert D.protected_instance_methods.include?(:g)
|
160
|
+
assert D.private_instance_methods.include?(:h)
|
161
|
+
|
162
|
+
D.new.f
|
163
|
+
D.new.instance_eval { g }
|
164
|
+
D.new.instance_eval { h }
|
165
|
+
end
|
166
|
+
|
167
|
+
class K
|
168
|
+
def f(bind)
|
169
|
+
eval("self", bind)
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.g
|
173
|
+
self.new.f(binding)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_self_control
|
178
|
+
Boc.enable K, :f
|
179
|
+
assert_equal K, K.g
|
180
|
+
end
|
181
|
+
|
182
|
+
class L
|
183
|
+
def f
|
184
|
+
eval("self", Boc.value)
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.g
|
188
|
+
self.new.f
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_self
|
193
|
+
Boc.enable L, :f
|
194
|
+
assert_equal L, L.g
|
195
|
+
end
|
196
|
+
|
197
|
+
class K
|
198
|
+
def k
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_double_enable
|
203
|
+
Boc.enable K, :k
|
204
|
+
error = assert_raises Boc::AlreadyEnabledError do
|
205
|
+
Boc.enable K, :k
|
206
|
+
end
|
207
|
+
assert_match(/method `k'.*already/, error.message)
|
208
|
+
end
|
209
|
+
end
|