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,56 @@
1
+ require_relative 'shared/main'
2
+
3
+ class LambdaTest < RegularTest
4
+ def test_block_braces_multiline
5
+ block = return_block { |x, y|
6
+ x + y
7
+ }
8
+
9
+ expected = binop_block(:return_block, :+)
10
+ assert_equal expected, block.to_ast
11
+ end
12
+
13
+ def test_block_do_end_multiline
14
+ block = return_block do |x, y|
15
+ x * y
16
+ end
17
+
18
+ expected = binop_block(:return_block, :*)
19
+ assert_equal expected, block.to_ast
20
+ end
21
+
22
+ def test_lambda
23
+ a = lambda { |x, y| x - y }
24
+
25
+ expected = binop_block(:lambda, :-)
26
+ assert_equal expected, a.to_ast
27
+ end
28
+
29
+ def test_proc
30
+ a = proc { |x, y| x / y }
31
+
32
+ expected = binop_block(:proc, :/)
33
+ assert_equal expected, a.to_ast
34
+ end
35
+
36
+ def test_proc_new
37
+ a = Proc.new { |x, y| x + y }
38
+
39
+ expected = binop_proc_new(:+)
40
+ assert_equal expected, a.to_ast
41
+ end
42
+
43
+ def test_block_braces_one_line
44
+ block = return_block { |x, y| x * y }
45
+
46
+ expected = binop_block(:return_block, :*)
47
+ assert_equal expected, block.to_ast
48
+ end
49
+
50
+ def test_block_do_end_one_line
51
+ block = return_block do |x, y| x - y end
52
+
53
+ expected = binop_block(:return_block, :-)
54
+ assert_equal expected, block.to_ast
55
+ end
56
+ end
@@ -0,0 +1,84 @@
1
+ require_relative 'shared/main'
2
+
3
+ class AAA_LoadPathTest < BaseTest
4
+ include FileUtils
5
+
6
+ def test_load_path
7
+ $LOAD_PATH.unshift DATA_DIR
8
+ begin
9
+ check_load
10
+ check_errors
11
+ temp_file "foo.rb" do
12
+ Dir.chdir(DATA_DIR) do
13
+ compare_load_errors("/foo.rb")
14
+ end
15
+ end
16
+ ensure
17
+ $LOAD_PATH.shift
18
+ end
19
+ end
20
+
21
+ def test_chdir
22
+ mkdir DATA_DIR, :verbose => false rescue nil
23
+ Dir.chdir(DATA_DIR) do
24
+ check_load
25
+ check_errors
26
+ end
27
+ end
28
+
29
+ def check_load
30
+ code_1 = %{
31
+ def hello
32
+ "password"
33
+ end
34
+ }
35
+
36
+ code_2 = %{
37
+ def goodbye
38
+ "bubbleboy"
39
+ end
40
+ }
41
+
42
+ temp_file "foo.rb" do |path|
43
+ write_file(path, code_1)
44
+
45
+ Object.send(:remove_method, :hello) rescue nil
46
+ load "foo.rb"
47
+ assert_equal "password", hello
48
+
49
+ write_file path, code_2
50
+
51
+ Object.send(:remove_method, :goodbye) rescue nil
52
+ LiveAST.load "foo.rb"
53
+ assert_equal "bubbleboy", goodbye
54
+ end
55
+ ensure
56
+ Object.send(:remove_method, :hello) rescue nil
57
+ Object.send(:remove_method, :goodbye) rescue nil
58
+ end
59
+
60
+ def compare_load_errors(file)
61
+ orig = assert_raise LoadError do
62
+ load file
63
+ end
64
+ live = assert_raise LoadError do
65
+ LiveAST.load file
66
+ end
67
+ assert_equal orig.message, live.message
68
+ end
69
+
70
+ def check_errors
71
+ temp_file "foo.rb" do |path|
72
+ touch path, :verbose => false
73
+ [
74
+ "foo",
75
+ "",
76
+ "/usr",
77
+ ".",
78
+ "..",
79
+ ].each do |file|
80
+ compare_load_errors(file)
81
+ end
82
+ end
83
+ end
84
+ end
data/test/load_test.rb ADDED
@@ -0,0 +1,85 @@
1
+ require_relative 'shared/main'
2
+
3
+ class AAA_LoadFileTest < BaseTest
4
+ class << self
5
+ attr_accessor :flag
6
+ end
7
+
8
+ def test_a_no_locals_created
9
+ code = %{
10
+ AAA_LoadFileTest.flag = :code_a
11
+ FOO = 77
12
+ x = 33
13
+ y = 99
14
+ }
15
+
16
+ temp_file do |file|
17
+ write_file file, code
18
+
19
+ ret = LiveAST.load file
20
+ assert_equal true, ret
21
+ assert_equal :code_a, AAA_LoadFileTest.flag
22
+
23
+ assert_raise NameError do
24
+ eval("x", TOPLEVEL_BINDING)
25
+ end
26
+
27
+ assert_equal 77, ::FOO
28
+ end
29
+ end
30
+
31
+ def test_b_no_locals_modified
32
+ code = %{
33
+ AAA_LoadFileTest.flag = :code_b
34
+ r = 55
35
+ }
36
+
37
+ temp_file do |file|
38
+ eval("r = 66", TOPLEVEL_BINDING)
39
+
40
+ write_file file, code
41
+
42
+ ret = LiveAST.load file
43
+ assert_equal true, ret
44
+ assert_equal :code_b, AAA_LoadFileTest.flag
45
+
46
+ actual = eval("r", TOPLEVEL_BINDING)
47
+ assert_equal 66, actual
48
+ end
49
+ end
50
+
51
+ def test_c_wrap
52
+ code = %{
53
+ AAA_LoadFileTest.flag = :code_c
54
+ ZOOM = 111
55
+ }
56
+
57
+ temp_file do |file|
58
+ write_file file, code
59
+
60
+ ret = LiveAST.load file, true
61
+ assert_equal true, ret
62
+ assert_equal :code_c, AAA_LoadFileTest.flag
63
+
64
+ assert_raises NameError do
65
+ ZOOM
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.from_d
71
+ self.flag = :code_d
72
+ end
73
+
74
+ def test_d_empty_locals_list
75
+ code = %{
76
+ AAA_LoadFileTest.from_d
77
+ }
78
+
79
+ temp_file do |file|
80
+ write_file file, code
81
+ LiveAST.load file
82
+ assert_equal :code_d, AAA_LoadFileTest.flag
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,51 @@
1
+ require_relative 'shared/main'
2
+
3
+ class AAA_NoninvasiveTest < BaseTest
4
+ def test_no_clutter
5
+ [Method, UnboundMethod, Proc].each do |klass|
6
+ assert !klass.instance_methods.include?(:to_ast)
7
+ assert !klass.instance_methods.include?(:to_ruby)
8
+ end
9
+
10
+ assert !respond_to?(:ast_eval)
11
+ assert !Kernel.respond_to?(:ast_eval)
12
+ assert !respond_to?(:ast_load)
13
+ assert !Kernel.respond_to?(:ast_load)
14
+ end
15
+
16
+ DEFINE_A = lambda do
17
+ class A
18
+ def f
19
+ "A#f"
20
+ end
21
+ end
22
+ end
23
+
24
+ def test_method
25
+ DEFINE_A.call
26
+
27
+ expected = no_arg_def(:f, "A#f")
28
+ assert_equal expected, LiveAST.ast(A.instance_method(:f))
29
+ assert_equal expected, LiveAST.ast(A.new.method(:f))
30
+ end
31
+
32
+ def test_lambda
33
+ a = lambda { |x, y| x ** y }
34
+
35
+ assert_equal binop_block(:lambda, :**), LiveAST.ast(a)
36
+ end
37
+
38
+ def test_ast_eval
39
+ code = %{ lambda { |x, y| x / y } }
40
+
41
+ expected = binop_block(:lambda, :/)
42
+ result = LiveAST.ast(LiveAST.eval(code, binding))
43
+ assert_equal expected, result
44
+ end
45
+
46
+ def test_bogus
47
+ assert_raises TypeError do
48
+ LiveAST.ast(99)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'shared/main'
2
+ require_relative '../devel/jumpstart'
3
+
4
+ sections = [
5
+ "Synopsis",
6
+ "Loading Source",
7
+ "Noninvasive Interface",
8
+ "+to_ruby+",
9
+ ]
10
+
11
+ Jumpstart.doc_to_test("README.rdoc", *sections)
@@ -0,0 +1,52 @@
1
+ require_relative 'shared/main'
2
+
3
+ class RecursiveEvalTest < RegularTest
4
+ DEFINE = lambda do
5
+ ast_eval %{
6
+ class A
7
+ ast_eval %{
8
+ def f
9
+ "A#f"
10
+ end
11
+ }, binding
12
+
13
+ ast_eval %{
14
+ ast_eval %{
15
+ remove_method :f
16
+ def f(x, y)
17
+ x + y
18
+ end
19
+
20
+ def g
21
+ "A#g"
22
+ end
23
+ }, binding
24
+
25
+ LAMBDA = ast_eval %{
26
+ lambda { |x, y| x * y }
27
+ }, binding
28
+ }, binding
29
+ end
30
+ }, binding
31
+ end
32
+
33
+ def test_method
34
+ defined?(A) or DEFINE.call
35
+ assert_equal "#{self.class}::A", A.name
36
+
37
+ assert_equal binop_def(:f, :+),
38
+ A.instance_method(:f).to_ast
39
+
40
+ assert_equal no_arg_def(:g, "A#g"),
41
+ A.instance_method(:g).to_ast
42
+ end
43
+
44
+ def test_lambda
45
+ defined?(A) or DEFINE.call
46
+ assert_equal "#{self.class}::A", A.name
47
+
48
+ assert_equal binop_block(:lambda, :*),
49
+ A::LAMBDA.to_ast
50
+ end
51
+ end
52
+
@@ -0,0 +1,83 @@
1
+ require_relative 'shared/main'
2
+
3
+ class RedefineMethodTest < RegularTest
4
+ DEFINE_A = lambda do
5
+ class A
6
+ def f
7
+ "old A#f"
8
+ end
9
+
10
+ PREVIOUS_Af = instance_method(:f)
11
+ remove_method :f
12
+
13
+ def f(x, y)
14
+ x * y
15
+ end
16
+ end
17
+ end
18
+
19
+ def test_inclass_redef
20
+ DEFINE_A.call
21
+
22
+ assert_equal binop_def(:f, :*),
23
+ A.instance_method(:f).to_ast
24
+
25
+ assert_equal no_arg_def(:f, "old A#f"),
26
+ A::PREVIOUS_Af.to_ast
27
+ end
28
+
29
+ DEFINE_B = lambda do
30
+ class B
31
+ def f
32
+ "old B#f"
33
+ end
34
+ end
35
+ end
36
+
37
+ def test_dynamic_redef
38
+ DEFINE_B.call
39
+
40
+ assert_equal "old B#f", B.new.f
41
+ assert_equal no_arg_def(:f, "old B#f"), B.instance_method(:f).to_ast
42
+
43
+ B.class_eval do
44
+ remove_method :f
45
+ define_method :f do |x, y|
46
+ x - y
47
+ end
48
+ end
49
+
50
+ assert_equal 11, B.new.f(44, 33)
51
+
52
+ assert_equal binop_define_method(:f, :-),
53
+ B.instance_method(:f).to_ast
54
+ end
55
+
56
+ DEFINE_C = lambda do
57
+ class C
58
+ def f
59
+ "old C#f"
60
+ end
61
+ end
62
+ end
63
+
64
+ def test_dynamic_redef_with_eval
65
+ DEFINE_C.call
66
+
67
+ assert_equal "old C#f", C.new.f
68
+
69
+ C.class_eval do
70
+ ast_eval %{
71
+ remove_method :f
72
+ define_method :f do |x, y|
73
+ x * y
74
+ end
75
+ }, binding
76
+ end
77
+
78
+ assert_equal 12, C.new.f(3, 4)
79
+
80
+ assert_equal binop_define_method(:f, :*),
81
+ C.instance_method(:f).to_ast
82
+ end
83
+ end
@@ -0,0 +1,108 @@
1
+ require_relative 'shared/main'
2
+
3
+ class AAB_ReloadTest < BaseTest
4
+ include FileUtils
5
+
6
+ def test_reloading
7
+ raw_reload
8
+ require 'live_ast/ast_load'
9
+ noninvasive_ast_reload
10
+ require 'live_ast/replace_load'
11
+ ast_reload
12
+ end
13
+
14
+ def raw_reload
15
+ code_1 = %{
16
+ class AAB_ReloadTest::A
17
+ def f
18
+ "first A#f"
19
+ end
20
+ end
21
+ }
22
+
23
+ code_2 = %{
24
+ class AAB_ReloadTest::A
25
+ def f
26
+ "second A#f"
27
+ end
28
+ end
29
+ }
30
+
31
+ temp_file do |file|
32
+ write_file file, code_1
33
+ load file
34
+
35
+ LiveAST.ast(A.instance_method(:f))
36
+
37
+ write_file file, code_2
38
+ load file
39
+
40
+ # forced a raw-reload inconsistency -- verify bogus
41
+
42
+ assert_equal no_arg_def(:f, "first A#f"),
43
+ LiveAST.ast(A.instance_method(:f))
44
+ end
45
+ end
46
+
47
+ def noninvasive_ast_reload
48
+ code_1 = %{
49
+ class AAB_ReloadTest::B
50
+ def f
51
+ "first B#f"
52
+ end
53
+ end
54
+ }
55
+
56
+ code_2 = %{
57
+ class AAB_ReloadTest::B
58
+ def f
59
+ "second B#f"
60
+ end
61
+ end
62
+ }
63
+
64
+ temp_file do |file|
65
+ write_file file, code_1
66
+ load file
67
+
68
+ LiveAST.ast(B.instance_method(:f))
69
+
70
+ write_file file, code_2
71
+ ast_load file
72
+
73
+ assert_equal no_arg_def(:f, "second B#f"),
74
+ LiveAST.ast(B.instance_method(:f))
75
+ end
76
+ end
77
+
78
+ def ast_reload
79
+ code_1 = %{
80
+ class AAB_ReloadTest::C
81
+ def f
82
+ "first C#f"
83
+ end
84
+ end
85
+ }
86
+
87
+ code_2 = %{
88
+ class AAB_ReloadTest::C
89
+ def f
90
+ "second C#f"
91
+ end
92
+ end
93
+ }
94
+
95
+ temp_file do |file|
96
+ write_file file, code_1
97
+ load file
98
+
99
+ LiveAST.ast(C.instance_method(:f))
100
+
101
+ write_file file, code_2
102
+ load file
103
+
104
+ assert_equal no_arg_def(:f, "second C#f"),
105
+ LiveAST.ast(C.instance_method(:f))
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,124 @@
1
+ module ASTGenerators
2
+ #
3
+ # no_arg_def(:f, "A#f") returns the ast of
4
+ #
5
+ # def f
6
+ # "A#f"
7
+ # end
8
+ #
9
+ def no_arg_def(name, ret)
10
+ s(:defn, name, s(:args), s(:scope, s(:block, s(:str, ret))))
11
+ end
12
+
13
+ #
14
+ # no_arg_def_return(no_arg_def(:f, "A#f")) == "A#f"
15
+ #
16
+ def no_arg_def_return(ast)
17
+ ast[3][1][1][1]
18
+ end
19
+
20
+ #
21
+ # binop_def(:f, :+) returns the ast of
22
+ #
23
+ # def f(x, y)
24
+ # x + y
25
+ # end
26
+ #
27
+ def binop_def(name, op)
28
+ s(:defn,
29
+ name,
30
+ s(:args, :x, :y),
31
+ s(:scope,
32
+ s(:block, s(:call, s(:lvar, :x), op, s(:arglist, s(:lvar, :y))))))
33
+ end
34
+
35
+ #
36
+ # binop_define_method(:f, :*) returns the ast of
37
+ #
38
+ # define_method :f do |x, y|
39
+ # x * y
40
+ # end
41
+ #
42
+ def binop_define_method(name, op)
43
+ s(:iter,
44
+ s(:call, nil, :define_method, s(:arglist, s(:lit, name))),
45
+ s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))),
46
+ s(:call, s(:lvar, :x), op, s(:arglist, s(:lvar, :y))))
47
+ end
48
+
49
+ #
50
+ # binop_covert_define_method(:f, :-, :my_def) returns the ast of
51
+ #
52
+ # my_def :f do |x, y|
53
+ # x - y
54
+ # end
55
+ #
56
+ def binop_covert_define_method(name, op, covert_name)
57
+ s(:iter,
58
+ s(:call, nil, covert_name, s(:arglist, s(:lit, name))),
59
+ s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))),
60
+ s(:call, s(:lvar, :x), op, s(:arglist, s(:lvar, :y))))
61
+ end
62
+
63
+ #
64
+ # binop_define_method_with_var(:method_name, :/) returns the ast of
65
+ #
66
+ # define_method method_name do |x, y|
67
+ # x / y
68
+ # end
69
+ #
70
+ def binop_define_method_with_var(name, op)
71
+ s(:iter,
72
+ s(:call, nil, :define_method, s(:arglist, s(:lvar, name))),
73
+ s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))),
74
+ s(:call, s(:lvar, :x), op, s(:arglist, s(:lvar, :y))))
75
+ end
76
+
77
+ #
78
+ # binop_define_singleton_method(:f, :+, :a) returns the ast of
79
+ #
80
+ # a.define_singleton_method :f do |x, y|
81
+ # x + y
82
+ # end
83
+ #
84
+ def binop_define_singleton_method(name, op, receiver)
85
+ s(:iter,
86
+ s(:call, s(:lvar, receiver), :define_singleton_method,
87
+ s(:arglist, s(:lit, name))),
88
+ s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))),
89
+ s(:call, s(:lvar, :x), op, s(:arglist, s(:lvar, :y))))
90
+ end
91
+
92
+ #
93
+ # no_arg_block(:foo, "bar") returns the ast of
94
+ #
95
+ # foo { "bar" }
96
+ #
97
+ def no_arg_block(name, ret)
98
+ s(:iter, s(:call, nil, name, s(:arglist)), nil, s(:str, ret))
99
+ end
100
+
101
+ #
102
+ # binop_block(:foo, :+) returns the ast of
103
+ #
104
+ # foo { |x, y| x + y }
105
+ #
106
+ def binop_block(name, op, receiver = nil, args = [])
107
+ s(:iter,
108
+ s(:call, receiver, name, s(:arglist, *args)),
109
+ s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))),
110
+ s(:call, s(:lvar, :x), op, s(:arglist, s(:lvar, :y))))
111
+ end
112
+
113
+ #
114
+ # binop_proc_new(:*) returns the ast of
115
+ #
116
+ # Proc.new { |x, y| x * y }
117
+ #
118
+ def binop_proc_new(op)
119
+ s(:iter,
120
+ s(:call, s(:const, :Proc), :new, s(:arglist)),
121
+ s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))),
122
+ s(:call, s(:lvar, :x), op, s(:arglist, s(:lvar, :y))))
123
+ end
124
+ end