live_ast 0.2.0

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.
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