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