live_ast 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/CHANGES.rdoc +6 -0
  2. data/MANIFEST +54 -0
  3. data/README.rdoc +388 -0
  4. data/Rakefile +19 -0
  5. data/devel/jumpstart.rb +983 -0
  6. data/lib/live_ast/ast_eval.rb +13 -0
  7. data/lib/live_ast/ast_load.rb +15 -0
  8. data/lib/live_ast/base.rb +56 -0
  9. data/lib/live_ast/cache.rb +14 -0
  10. data/lib/live_ast/error.rb +30 -0
  11. data/lib/live_ast/evaler.rb +66 -0
  12. data/lib/live_ast/linker.rb +107 -0
  13. data/lib/live_ast/loader.rb +69 -0
  14. data/lib/live_ast/parser.rb +48 -0
  15. data/lib/live_ast/replace_load.rb +14 -0
  16. data/lib/live_ast/replace_raise.rb +21 -0
  17. data/lib/live_ast/to_ast.rb +17 -0
  18. data/lib/live_ast/to_ruby.rb +12 -0
  19. data/lib/live_ast/version.rb +3 -0
  20. data/lib/live_ast.rb +4 -0
  21. data/test/ast_eval_feature_test.rb +11 -0
  22. data/test/ast_load_feature_test.rb +11 -0
  23. data/test/backtrace_test.rb +159 -0
  24. data/test/covert_define_method_test.rb +23 -0
  25. data/test/def_test.rb +35 -0
  26. data/test/define_method_test.rb +41 -0
  27. data/test/define_singleton_method_test.rb +15 -0
  28. data/test/encoding_test/bad.rb +1 -0
  29. data/test/encoding_test/cp932.rb +6 -0
  30. data/test/encoding_test/default.rb +5 -0
  31. data/test/encoding_test/eucjp.rb +6 -0
  32. data/test/encoding_test/koi8.rb +6 -0
  33. data/test/encoding_test/koi8_shebang.rb +7 -0
  34. data/test/encoding_test/usascii.rb +6 -0
  35. data/test/encoding_test/utf8.rb +6 -0
  36. data/test/encoding_test.rb +51 -0
  37. data/test/error_test.rb +115 -0
  38. data/test/eval_test.rb +269 -0
  39. data/test/flush_cache_test.rb +98 -0
  40. data/test/lambda_test.rb +56 -0
  41. data/test/load_path_test.rb +84 -0
  42. data/test/load_test.rb +85 -0
  43. data/test/noninvasive_test.rb +51 -0
  44. data/test/readme_test.rb +11 -0
  45. data/test/recursive_eval_test.rb +52 -0
  46. data/test/redefine_method_test.rb +83 -0
  47. data/test/reload_test.rb +108 -0
  48. data/test/shared/ast_generators.rb +124 -0
  49. data/test/shared/main.rb +110 -0
  50. data/test/stdlib_test.rb +11 -0
  51. data/test/thread_test.rb +44 -0
  52. data/test/to_ast_feature_test.rb +15 -0
  53. data/test/to_ruby_feature_test.rb +15 -0
  54. data/test/to_ruby_test.rb +86 -0
  55. metadata +223 -0
@@ -0,0 +1,23 @@
1
+ require_relative 'shared/main'
2
+
3
+ class CovertDefineMethodTest < RegularTest
4
+ DEFINE = lambda do
5
+ class A
6
+ def A.my_def(*args, &block)
7
+ define_method(*args, &block)
8
+ end
9
+
10
+ my_def :h do |x, y|
11
+ x + y
12
+ end
13
+ end
14
+ end
15
+
16
+ def test_covert_define_method
17
+ DEFINE.call
18
+ assert_equal 77, A.new.h(33, 44)
19
+
20
+ assert_equal binop_covert_define_method(:h, :+, :my_def),
21
+ A.instance_method(:h).to_ast
22
+ end
23
+ end
data/test/def_test.rb ADDED
@@ -0,0 +1,35 @@
1
+ require_relative 'shared/main'
2
+
3
+ class DefTest < RegularTest
4
+ class A
5
+ def f
6
+ "A#f"
7
+ end
8
+ end
9
+
10
+ def test_def_unbound_method_a
11
+ expected = no_arg_def(:f, "A#f")
12
+ assert_equal expected, A.instance_method(:f).to_ast
13
+ end
14
+
15
+ def test_def_method_a
16
+ expected = no_arg_def(:f, "A#f")
17
+ assert_equal expected, A.new.method(:f).to_ast
18
+ end
19
+
20
+ class B
21
+ def f(x, y)
22
+ x + y
23
+ end
24
+ end
25
+
26
+ def test_def_unbound_method_b
27
+ expected = binop_def(:f, :+)
28
+ assert_equal expected, B.instance_method(:f).to_ast
29
+ end
30
+
31
+ def test_def_instance_method_b
32
+ expected = binop_def(:f, :+)
33
+ assert_equal expected, B.new.method(:f).to_ast
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'shared/main'
2
+
3
+ class DefineMethodTest < RegularTest
4
+ DEFINE = lambda do
5
+ class A
6
+ {
7
+ :f => :+,
8
+ :g => :*,
9
+ :h => :-,
10
+ }.each_pair do |name, op|
11
+ case op
12
+ when :+
13
+ define_method name do |x, y|
14
+ x + y
15
+ end
16
+ when :*
17
+ define_method name do |x, y|
18
+ x * y
19
+ end
20
+ when :-
21
+ define_method name do |x, y|
22
+ x - y
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def test_define_method
30
+ DEFINE.call
31
+
32
+ assert_equal binop_define_method_with_var(:name, :+),
33
+ A.instance_method(:f).to_ast
34
+
35
+ assert_equal binop_define_method_with_var(:name, :*),
36
+ A.instance_method(:g).to_ast
37
+
38
+ assert_equal binop_define_method_with_var(:name, :-),
39
+ A.instance_method(:h).to_ast
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'shared/main'
2
+
3
+ class DefineSingletonMethodTest < RegularTest
4
+ def test_define_singleton_method
5
+ a = Object.new
6
+ a.define_singleton_method :f do |x, y|
7
+ x + y
8
+ end
9
+
10
+ assert_equal 77, a.f(33, 44)
11
+
12
+ assert_equal binop_define_singleton_method(:f, :+, :a),
13
+ a.singleton_class.instance_method(:f).to_ast
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ # encoding: feynman-diagram
@@ -0,0 +1,6 @@
1
+ # -*- coding: cp932 -*-
2
+ module EncodingTest
3
+ def cp932_string
4
+ "�傫�Ȕ��Ƃ˂��B"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module EncodingTest
2
+ def default_string
3
+ "Cats and large boxes." # google translate
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: euc-jp -*-
2
+ module EncodingTest
3
+ def eucjp_string
4
+ "�礭��Ȣ�Ȥͤ���"
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: koi8-r -*-
2
+ module EncodingTest
3
+ def koi8_string
4
+ "� ���������� ������� ���������� �� 20 ��������"
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: koi8-r -*-
3
+ module EncodingTest
4
+ def koi8_shebang_string
5
+ "� ���������� ������� ���������� �� 20 ��������"
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: us-ascii
2
+ module EncodingTest
3
+ def usascii_string
4
+ "Cats and large boxes." # google translate
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ module EncodingTest
3
+ def utf8_string
4
+ "大きな箱とねこ。"
5
+ end
6
+ end
@@ -0,0 +1,51 @@
1
+ require_relative 'shared/main'
2
+
3
+ require_relative 'encoding_test/default.rb'
4
+ require_relative 'encoding_test/usascii.rb'
5
+ require_relative 'encoding_test/utf8.rb'
6
+ require_relative 'encoding_test/cp932.rb'
7
+ require_relative 'encoding_test/eucjp.rb'
8
+ require_relative 'encoding_test/koi8.rb'
9
+ require_relative 'encoding_test/koi8_shebang.rb'
10
+
11
+ class AllEncodingTest < RegularTest
12
+ include EncodingTest
13
+
14
+ %w[
15
+
16
+ default US-ASCII
17
+ usascii US-ASCII
18
+ utf8 UTF-8
19
+ cp932 Windows-31J
20
+ eucjp EUC-JP
21
+ koi8 KOI8-R
22
+ koi8_shebang KOI8-R
23
+
24
+ ].each_slice(2) do |abbr, name|
25
+ define_method "test_#{abbr}" do
26
+ str = send("#{abbr}_string")
27
+ assert_equal name, str.encoding.to_s
28
+
29
+ ast = EncodingTest.instance_method("#{abbr}_string").to_ast
30
+ assert_equal name, no_arg_def_return(ast).encoding.to_s
31
+
32
+ LiveAST.load "./test/encoding_test/#{abbr}.rb"
33
+
34
+ ast = EncodingTest.instance_method("#{abbr}_string").to_ast
35
+ assert_equal name, no_arg_def_return(ast).encoding.to_s
36
+ end
37
+ end
38
+
39
+ def test_bad
40
+ orig = assert_raise ArgumentError do
41
+ require "./test/encoding_test/bad.rb"
42
+ end
43
+ live = assert_raise ArgumentError do
44
+ LiveAST.load "./test/encoding_test/bad.rb"
45
+ end
46
+ # inconsistent punctuation from Ruby
47
+ re = %r!\Aunknown encoding name\s*[-:]\s*feynman-diagram\Z!
48
+ assert_match re, orig.message
49
+ assert_match re, live.message
50
+ end
51
+ end
@@ -0,0 +1,115 @@
1
+ require_relative 'shared/main'
2
+
3
+ class ErrorTest < RegularTest
4
+ def test_multiple_lambda_same_line
5
+ a = lambda { } ; b = lambda { }
6
+
7
+ assert_raise LiveAST::MultipleDefinitionsOnSameLineError do
8
+ a.to_ast
9
+ end
10
+ end
11
+
12
+ DEFINE_A = lambda do
13
+ class A
14
+ def f ; end ; def g ; end
15
+ end
16
+ end
17
+
18
+ def test_multi_defs
19
+ DEFINE_A.call
20
+ assert_raise LiveAST::MultipleDefinitionsOnSameLineError do
21
+ A.instance_method(:f).to_ast
22
+ end
23
+ end
24
+
25
+ def test_ast_not_found
26
+ assert_raise LiveAST::NoSourceError do
27
+ File.method(:open).to_ast
28
+ end
29
+ end
30
+
31
+ def test_arg_error_too_many
32
+ orig = assert_raises ArgumentError do
33
+ eval("s", binding, "f", 99, nil)
34
+ end
35
+
36
+ live = assert_raises ArgumentError do
37
+ ast_eval("s", binding, "f", 99, nil)
38
+ end
39
+
40
+ assert_equal orig.message.sub("1..4", "2..4"), live.message
41
+ end
42
+
43
+ def test_bad_args
44
+ [99, Object.new, File].each do |bad|
45
+ orig = assert_raises TypeError do
46
+ eval(bad, binding)
47
+ end
48
+ live = assert_raises TypeError do
49
+ ast_eval(bad, binding)
50
+ end
51
+ assert_equal orig.message, live.message
52
+
53
+ orig = assert_raises TypeError do
54
+ eval("3 + 4", binding, bad)
55
+ end
56
+ live = assert_raises TypeError do
57
+ ast_eval("3 + 4", binding, bad)
58
+ end
59
+ assert_equal orig.message, live.message
60
+ end
61
+ end
62
+
63
+ def test_raw_eval
64
+ f = eval("lambda { }")
65
+ assert_raises LiveAST::RawEvalError do
66
+ f.to_ast
67
+ end
68
+ end
69
+
70
+ def test_reload_with_raw_eval_1
71
+ ast_eval("lambda { }", binding)
72
+ eval("lambda { }")
73
+ end
74
+
75
+ def test_reload_with_raw_eval_2
76
+ c = ast_eval %{
77
+ Class.new do
78
+ def f
79
+ end
80
+ end
81
+ }, binding
82
+ c.module_eval do
83
+ eval %{
84
+ remove_method :f
85
+ def f(x, y)
86
+ x + y
87
+ end
88
+ }
89
+ nil
90
+ end
91
+
92
+ assert_raises LiveAST::RawEvalError do
93
+ c.instance_method(:f).to_ast
94
+ end
95
+ end
96
+
97
+ def test_bad_binding
98
+ orig = assert_raises TypeError do
99
+ eval("", "bogus")
100
+ end
101
+
102
+ live = assert_raises TypeError do
103
+ ast_eval("", "bogus")
104
+ end
105
+
106
+ assert_equal orig.message, live.message
107
+ end
108
+
109
+ def test_shenanigans
110
+ error = assert_raises RuntimeError do
111
+ LiveAST.load "foo.rb|ast@4"
112
+ end
113
+ assert_match(/revision token/, error.message)
114
+ end
115
+ end
data/test/eval_test.rb ADDED
@@ -0,0 +1,269 @@
1
+ require_relative 'shared/main'
2
+
3
+ class EvalTest < RegularTest
4
+ DEFINE_A = lambda do
5
+ class A
6
+ ast_eval %{
7
+ def f(x, y)
8
+ x + y
9
+ end
10
+ }, binding
11
+ end
12
+ end
13
+
14
+ def test_eval_method
15
+ DEFINE_A.call
16
+ assert_equal "#{self.class}::A", A.name
17
+ assert_equal A, A.instance_method(:f).owner
18
+
19
+ assert_equal binop_def(:f, :+),
20
+ A.instance_method(:f).to_ast
21
+
22
+ assert_equal binop_def(:f, :+),
23
+ A.new.method(:f).to_ast
24
+ end
25
+
26
+ DEFINE_B = lambda do
27
+ ast_eval %{
28
+ class B
29
+ def f(x, y)
30
+ x * y
31
+ end
32
+ end
33
+ }, binding
34
+ end
35
+
36
+ def test_eval_class
37
+ DEFINE_B.call
38
+ assert_equal "#{self.class}::B", B.name
39
+ assert_equal B, B.instance_method(:f).owner
40
+
41
+ assert_equal binop_def(:f, :*),
42
+ B.instance_method(:f).to_ast
43
+
44
+ assert_equal binop_def(:f, :*),
45
+ B.new.method(:f).to_ast
46
+ end
47
+
48
+ def test_eval_method_anon
49
+ klass = Class.new do
50
+ ast_eval %{
51
+ def f(x, y)
52
+ x - y
53
+ end
54
+ }, binding
55
+ end
56
+
57
+ assert_nil klass.name
58
+ assert_equal klass, klass.instance_method(:f).owner
59
+
60
+ assert_equal binop_def(:f, :-),
61
+ klass.instance_method(:f).to_ast
62
+
63
+ assert_equal binop_def(:f, :-),
64
+ klass.new.method(:f).to_ast
65
+ end
66
+
67
+ def test_eval_class_anon
68
+ klass = ast_eval %{
69
+ Class.new do
70
+ def f(x, y)
71
+ x / y
72
+ end
73
+ end
74
+ }, binding
75
+
76
+ assert_nil klass.name
77
+ assert_equal klass, klass.instance_method(:f).owner
78
+
79
+ assert_equal binop_def(:f, :/),
80
+ klass.instance_method(:f).to_ast
81
+
82
+ assert_equal binop_def(:f, :/),
83
+ klass.new.method(:f).to_ast
84
+ end
85
+
86
+ DEFINE_C = lambda do
87
+ class C
88
+ ast_eval %{
89
+ define_method :g do |x, y|
90
+ x + y
91
+ end
92
+ }, binding
93
+ end
94
+ end
95
+
96
+ def test_eval_method_dynamic
97
+ DEFINE_C.call
98
+ assert_equal "#{self.class}::C", C.name
99
+ assert_equal C, C.instance_method(:g).owner
100
+
101
+ assert_equal binop_define_method(:g, :+),
102
+ C.instance_method(:g).to_ast
103
+
104
+ assert_equal binop_define_method(:g, :+),
105
+ C.new.method(:g).to_ast
106
+ end
107
+
108
+ DEFINE_D = lambda do
109
+ ast_eval %{
110
+ class D
111
+ define_method :g do |x, y|
112
+ x * y
113
+ end
114
+ end
115
+ }, binding
116
+ end
117
+
118
+ def test_eval_class_dynamic
119
+ DEFINE_D.call
120
+ assert_equal "#{self.class}::D", D.name
121
+ assert_equal D, D.instance_method(:g).owner
122
+
123
+ assert_equal binop_define_method(:g, :*),
124
+ D.instance_method(:g).to_ast
125
+
126
+ assert_equal binop_define_method(:g, :*),
127
+ D.new.method(:g).to_ast
128
+ end
129
+
130
+ def test_eval_method_anon_dynamic
131
+ klass = Class.new do
132
+ ast_eval %{
133
+ define_method :g do |x, y|
134
+ x - y
135
+ end
136
+ }, binding
137
+ end
138
+
139
+ assert_nil klass.name
140
+ assert_equal klass, klass.instance_method(:g).owner
141
+
142
+ assert_equal binop_define_method(:g, :-),
143
+ klass.instance_method(:g).to_ast
144
+
145
+ assert_equal binop_define_method(:g, :-),
146
+ klass.new.method(:g).to_ast
147
+ end
148
+
149
+ def test_eval_class_anon_dynamic
150
+ klass = ast_eval %{
151
+ Class.new do
152
+ define_method :g do |x, y|
153
+ x / y
154
+ end
155
+ end
156
+ }, binding
157
+
158
+ assert_nil klass.name
159
+ assert_equal klass, klass.instance_method(:g).owner
160
+
161
+ assert_equal binop_define_method(:g, :/),
162
+ klass.instance_method(:g).to_ast
163
+
164
+ assert_equal binop_define_method(:g, :/),
165
+ klass.new.method(:g).to_ast
166
+ end
167
+
168
+ DEFINE_GH = lambda do
169
+ ast_eval %{
170
+ class G
171
+ def f
172
+ "G#f"
173
+ end
174
+ end
175
+
176
+ class H
177
+ def g
178
+ "H#g"
179
+ end
180
+ end
181
+ }, binding
182
+ end
183
+
184
+ def test_reuse_string
185
+ DEFINE_GH.call
186
+ assert_equal "#{self.class}::G", G.name
187
+ assert_equal "#{self.class}::H", H.name
188
+
189
+ assert_equal no_arg_def(:f, "G#f"),
190
+ G.instance_method(:f).to_ast
191
+
192
+ assert_equal no_arg_def(:f, "G#f"),
193
+ G.new.method(:f).to_ast
194
+
195
+ assert_equal no_arg_def(:g, "H#g"),
196
+ H.instance_method(:g).to_ast
197
+
198
+ assert_equal no_arg_def(:g, "H#g"),
199
+ H.new.method(:g).to_ast
200
+ end
201
+
202
+ def test_module_eval
203
+ klass = Class.new
204
+ klass.module_eval do
205
+ ast_eval %{
206
+ def f
207
+ "z"
208
+ end
209
+ }, binding
210
+ end
211
+
212
+ assert_equal klass, klass.instance_method(:f).owner
213
+
214
+ assert_equal no_arg_def(:f, "z"),
215
+ klass.instance_method(:f).to_ast
216
+
217
+ assert_equal no_arg_def(:f, "z"),
218
+ klass.new.method(:f).to_ast
219
+ end
220
+
221
+ def test_singleton_class
222
+ obj = Object.new
223
+ obj.singleton_class.module_eval do
224
+ ast_eval %{
225
+ def f
226
+ "singleton"
227
+ end
228
+ }, binding
229
+ end
230
+
231
+ assert_equal no_arg_def(:f, "singleton"),
232
+ obj.method(:f).to_ast
233
+ end
234
+
235
+ def test_proc_in_eval
236
+ a = ast_eval %{ proc { |x, y| x + y } }, binding
237
+
238
+ assert_equal binop_block(:proc, :+), a.to_ast
239
+ end
240
+
241
+ def test_proc_new_in_eval
242
+ a = ast_eval %{ Proc.new { |x, y| x * y } }, binding
243
+
244
+ assert_equal binop_proc_new(:*), a.to_ast
245
+ end
246
+
247
+ def test_method_block_in_eval
248
+ a = ast_eval %{ return_block { |x, y| x - y } }, binding
249
+
250
+ assert_equal binop_block(:return_block, :-), a.to_ast
251
+ end
252
+
253
+ def test_lambda_in_eval
254
+ a = ast_eval %{ lambda { |x, y| x / y } }, binding
255
+
256
+ assert_equal binop_block(:lambda, :/), a.to_ast
257
+
258
+ # sanity check
259
+ assert_not_equal binop_block(:lambda, :+), a.to_ast
260
+ end
261
+
262
+ # from rubyspec
263
+ def test_to_str_on_file
264
+ file = MiniTest::Mock.new
265
+ file.expect(:to_str, "zebra.rb")
266
+ ast_eval "33 + 44", binding, file
267
+ file.verify
268
+ end
269
+ end
@@ -0,0 +1,98 @@
1
+ require_relative 'shared/main'
2
+
3
+ # test for flushing side-effects: unsort this TestCase from other
4
+ # TestCases.
5
+
6
+ define_unsorted_test_case "FlushCacheTest", RegularTest do
7
+ def test_cache
8
+ # testing global state of cache -- must be sequential
9
+ uncached_method_from_require
10
+ uncached_method_from_eval
11
+ cached_method_from_eval
12
+ lost_method_from_require
13
+ flush_lambda
14
+ end
15
+
16
+ def uncached_method_from_require
17
+ klass = Class.new do
18
+ def f ; end
19
+ def g ; end
20
+ end
21
+
22
+ LiveAST.flush_cache
23
+
24
+ #
25
+ # file never made it into the cache; unaffected by flush
26
+ #
27
+ assert_nothing_raised do
28
+ klass.instance_method(:g).to_ast
29
+ end
30
+ end
31
+
32
+ def uncached_method_from_eval
33
+ klass = Class.new do
34
+ ast_eval %{
35
+ def f ; end
36
+ def g ; end
37
+ }, binding
38
+ end
39
+
40
+ LiveAST.flush_cache
41
+
42
+ assert_raise LiveAST::FlushedError do
43
+ klass.instance_method(:g).to_ast
44
+ end
45
+ end
46
+
47
+ def cached_method_from_eval
48
+ klass = Class.new do
49
+ ast_eval %{
50
+ def f ; end
51
+ def g ; end
52
+ }, binding
53
+ end
54
+
55
+ f_ast = klass.instance_method(:f).to_ast
56
+
57
+ LiveAST.flush_cache
58
+
59
+ assert_equal f_ast.object_id,
60
+ klass.instance_method(:f).to_ast.object_id
61
+
62
+ assert_raise LiveAST::FlushedError do
63
+ klass.instance_method(:g).to_ast
64
+ end
65
+ end
66
+
67
+ def lost_method_from_require
68
+ klass = Class.new do
69
+ def f ; end
70
+ def g ; end
71
+ end
72
+
73
+ # check that previous flushing did not cause side effect
74
+ assert_nothing_raised do
75
+ klass.instance_method(:f).to_ast
76
+ end
77
+ end
78
+
79
+ def flush_lambda
80
+ a, b = ast_eval %{
81
+ [
82
+ lambda { "aaa" },
83
+ lambda { "bbb" },
84
+ ]
85
+ }, binding
86
+
87
+ a_ast = a.to_ast
88
+ assert_equal no_arg_block(:lambda, "aaa"), a_ast
89
+
90
+ LiveAST.flush_cache
91
+
92
+ assert_equal a_ast.object_id, a.to_ast.object_id
93
+
94
+ assert_raise LiveAST::FlushedError do
95
+ b.to_ast
96
+ end
97
+ end
98
+ end