mvz-live_ast 1.1.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.
- checksums.yaml +7 -0
- data/CHANGES.rdoc +93 -0
- data/README.rdoc +419 -0
- data/Rakefile +21 -0
- data/devel/levitate.rb +853 -0
- data/devel/levitate_config.rb +4 -0
- data/lib/live_ast.rb +4 -0
- data/lib/live_ast/ast_eval.rb +11 -0
- data/lib/live_ast/ast_load.rb +15 -0
- data/lib/live_ast/base.rb +73 -0
- data/lib/live_ast/common.rb +48 -0
- data/lib/live_ast/error.rb +20 -0
- data/lib/live_ast/evaler.rb +32 -0
- data/lib/live_ast/full.rb +2 -0
- data/lib/live_ast/irb_spy.rb +43 -0
- data/lib/live_ast/linker.rb +122 -0
- data/lib/live_ast/loader.rb +60 -0
- data/lib/live_ast/reader.rb +26 -0
- data/lib/live_ast/replace_eval.rb +121 -0
- data/lib/live_ast/replace_load.rb +14 -0
- data/lib/live_ast/replace_raise.rb +18 -0
- data/lib/live_ast/ruby_parser.rb +36 -0
- data/lib/live_ast/ruby_parser/test.rb +197 -0
- data/lib/live_ast/ruby_parser/unparser.rb +13 -0
- data/lib/live_ast/to_ast.rb +26 -0
- data/lib/live_ast/to_ruby.rb +24 -0
- data/lib/live_ast/version.rb +3 -0
- data/test/ast_eval_feature_test.rb +11 -0
- data/test/ast_load_feature_test.rb +11 -0
- data/test/attr_test.rb +24 -0
- data/test/backtrace_test.rb +158 -0
- data/test/covert_define_method_test.rb +23 -0
- data/test/def_test.rb +35 -0
- data/test/define_method_test.rb +67 -0
- data/test/define_singleton_method_test.rb +15 -0
- data/test/encoding_test.rb +52 -0
- data/test/encoding_test/bad.rb +1 -0
- data/test/encoding_test/cp932.rb +6 -0
- data/test/encoding_test/default.rb +5 -0
- data/test/encoding_test/eucjp.rb +6 -0
- data/test/encoding_test/koi8.rb +6 -0
- data/test/encoding_test/koi8_shebang.rb +7 -0
- data/test/encoding_test/koi8_with_utf8bom.rb +6 -0
- data/test/encoding_test/usascii.rb +6 -0
- data/test/encoding_test/usascii_with_utf8bom.rb +6 -0
- data/test/encoding_test/utf8.rb +6 -0
- data/test/encoding_test/utf8bom.rb +6 -0
- data/test/encoding_test/utf8bom_only.rb +5 -0
- data/test/encoding_test/utf8dos.rb +6 -0
- data/test/encoding_test/utf8mac.rb +6 -0
- data/test/encoding_test/utf8mac_alt.rb +6 -0
- data/test/encoding_test/utf8unix.rb +6 -0
- data/test/error_test.rb +116 -0
- data/test/eval_test.rb +269 -0
- data/test/flush_cache_test.rb +98 -0
- data/test/irb_test.rb +25 -0
- data/test/lambda_test.rb +56 -0
- data/test/load_path_test.rb +78 -0
- data/test/load_test.rb +123 -0
- data/test/main.rb +140 -0
- data/test/nested_test.rb +29 -0
- data/test/noninvasive_test.rb +51 -0
- data/test/readme_test.rb +16 -0
- data/test/recursive_eval_test.rb +52 -0
- data/test/redefine_method_test.rb +83 -0
- data/test/reload_test.rb +105 -0
- data/test/replace_eval_test.rb +405 -0
- data/test/rubygems_test.rb +25 -0
- data/test/rubyspec_test.rb +39 -0
- data/test/singleton_test.rb +25 -0
- data/test/stdlib_test.rb +13 -0
- data/test/thread_test.rb +44 -0
- data/test/to_ast_feature_test.rb +15 -0
- data/test/to_ruby_feature_test.rb +15 -0
- data/test/to_ruby_test.rb +87 -0
- metadata +275 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
require_relative '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_raises LiveAST::ASTNotFoundError 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_raises LiveAST::ASTNotFoundError 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_raises LiveAST::ASTNotFoundError do
|
95
|
+
b.to_ast
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/test/irb_test.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'main'
|
2
|
+
|
3
|
+
class IRBTest < RegularTest
|
4
|
+
def with_module(parent, child)
|
5
|
+
parent.const_set child, Module.new
|
6
|
+
begin
|
7
|
+
yield
|
8
|
+
ensure
|
9
|
+
parent.send :remove_const, child
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_irb
|
14
|
+
with_module(Object, :IRB) do
|
15
|
+
with_module(LiveAST, :IRBSpy) do
|
16
|
+
LiveAST::IRBSpy.class_eval do
|
17
|
+
def self.code_at(line)
|
18
|
+
"def f ; end"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
LiveAST::Linker.fetch_from_cache("(irb)", 1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/test/lambda_test.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative '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,78 @@
|
|
1
|
+
require_relative '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 nil, "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 code_1, "foo.rb" do |path|
|
43
|
+
Object.send(:remove_method, :hello) rescue nil
|
44
|
+
load "foo.rb"
|
45
|
+
assert_equal "password", hello
|
46
|
+
|
47
|
+
write_file path, code_2
|
48
|
+
|
49
|
+
Object.send(:remove_method, :goodbye) rescue nil
|
50
|
+
LiveAST.load "foo.rb"
|
51
|
+
assert_equal "bubbleboy", goodbye
|
52
|
+
end
|
53
|
+
ensure
|
54
|
+
Object.send(:remove_method, :hello) rescue nil
|
55
|
+
Object.send(:remove_method, :goodbye) rescue nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def compare_load_errors(file)
|
59
|
+
error = assert_raises LoadError do
|
60
|
+
LiveAST.load file
|
61
|
+
end
|
62
|
+
assert_equal "cannot load such file -- #{file}", error.message
|
63
|
+
end
|
64
|
+
|
65
|
+
def check_errors
|
66
|
+
temp_file "# do nothing", "foo.rb" do |path|
|
67
|
+
[
|
68
|
+
"foo",
|
69
|
+
"",
|
70
|
+
"/usr",
|
71
|
+
".",
|
72
|
+
"..",
|
73
|
+
].each do |file|
|
74
|
+
compare_load_errors(file)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/test/load_test.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require_relative 'main'
|
2
|
+
require_relative '../devel/levitate'
|
3
|
+
|
4
|
+
class AAA_LoadFileTest < BaseTest
|
5
|
+
class << self
|
6
|
+
attr_accessor :flag
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_a_no_locals_created
|
10
|
+
code = %{
|
11
|
+
AAA_LoadFileTest.flag = :code_a
|
12
|
+
FOO = 77
|
13
|
+
x = 33
|
14
|
+
y = 99
|
15
|
+
}
|
16
|
+
|
17
|
+
temp_file code do |file|
|
18
|
+
ret = LiveAST.load file
|
19
|
+
assert_equal true, ret
|
20
|
+
assert_equal :code_a, AAA_LoadFileTest.flag
|
21
|
+
|
22
|
+
assert_raises NameError do
|
23
|
+
eval("x", TOPLEVEL_BINDING)
|
24
|
+
end
|
25
|
+
|
26
|
+
assert_equal 77, ::FOO
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_b_no_locals_modified
|
31
|
+
code = %{
|
32
|
+
AAA_LoadFileTest.flag = :code_b
|
33
|
+
r = 55
|
34
|
+
}
|
35
|
+
|
36
|
+
temp_file code do |file|
|
37
|
+
eval("r = 66", TOPLEVEL_BINDING)
|
38
|
+
|
39
|
+
ret = LiveAST.load file
|
40
|
+
assert_equal true, ret
|
41
|
+
assert_equal :code_b, AAA_LoadFileTest.flag
|
42
|
+
|
43
|
+
actual = eval("r", TOPLEVEL_BINDING)
|
44
|
+
assert_equal 66, actual
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_c_wrap
|
49
|
+
code = %{
|
50
|
+
AAA_LoadFileTest.flag = :code_c
|
51
|
+
ZOOM = 111
|
52
|
+
}
|
53
|
+
|
54
|
+
temp_file code do |file|
|
55
|
+
ret = LiveAST.load file, true
|
56
|
+
assert_equal true, ret
|
57
|
+
assert_equal :code_c, AAA_LoadFileTest.flag
|
58
|
+
|
59
|
+
assert_raises NameError do
|
60
|
+
ZOOM
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.from_d
|
66
|
+
self.flag = :code_d
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_d_empty_locals_list
|
70
|
+
code = %{
|
71
|
+
AAA_LoadFileTest.from_d
|
72
|
+
}
|
73
|
+
|
74
|
+
temp_file code do |file|
|
75
|
+
LiveAST.load file
|
76
|
+
assert_equal :code_d, AAA_LoadFileTest.flag
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_verbose_respected
|
81
|
+
lib = File.expand_path(File.dirname(__FILE__) + "/../lib")
|
82
|
+
|
83
|
+
[
|
84
|
+
# respects a loaded file setting $VERBOSE = true
|
85
|
+
[
|
86
|
+
"false",
|
87
|
+
"true",
|
88
|
+
lambda { |file|
|
89
|
+
Levitate.run file
|
90
|
+
}
|
91
|
+
],
|
92
|
+
|
93
|
+
# unfixable: does not respect a loaded file setting $VERBOSE = nil
|
94
|
+
[
|
95
|
+
"true",
|
96
|
+
"false",
|
97
|
+
lambda { |file|
|
98
|
+
unfixable do
|
99
|
+
assert_nothing_raised do
|
100
|
+
Levitate.run file
|
101
|
+
end
|
102
|
+
end
|
103
|
+
}
|
104
|
+
]
|
105
|
+
].each do |main_value, loaded_value, action|
|
106
|
+
loaded_code = %{
|
107
|
+
$VERBOSE = #{loaded_value}
|
108
|
+
}
|
109
|
+
|
110
|
+
temp_file loaded_code do |loaded_file|
|
111
|
+
main_code = %{
|
112
|
+
$LOAD_PATH.unshift '#{lib}'
|
113
|
+
require 'live_ast/base'
|
114
|
+
toplevel_local = 444
|
115
|
+
$VERBOSE = #{main_value}
|
116
|
+
LiveAST.load '#{loaded_file}'
|
117
|
+
$VERBOSE == #{loaded_value} or exit(1)
|
118
|
+
}
|
119
|
+
temp_file main_code, &action
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/test/main.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
# require first for stdlib_test
|
4
|
+
require 'pp'
|
5
|
+
require 'find'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
require 'minitest/unit'
|
9
|
+
require 'minitest/mock'
|
10
|
+
require 'minitest/autorun'
|
11
|
+
|
12
|
+
$VERBOSE = true
|
13
|
+
|
14
|
+
require 'live_ast/base'
|
15
|
+
|
16
|
+
def define_unsorted_test_case(name, superclass, &block)
|
17
|
+
klass = Class.new superclass, &block
|
18
|
+
letter = ('A'..'Z').to_a[rand(26)]
|
19
|
+
Object.const_set "#{letter}#{name}", klass
|
20
|
+
end
|
21
|
+
|
22
|
+
class JLMiniTest < MiniTest::Unit::TestCase
|
23
|
+
def self.test_methods
|
24
|
+
default = super
|
25
|
+
onlies = default.select { |m| m =~ %r!__only\Z! }
|
26
|
+
if onlies.empty?
|
27
|
+
default
|
28
|
+
else
|
29
|
+
puts "\nNOTE: running ONLY *__only tests for #{self}"
|
30
|
+
onlies
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def delim(char)
|
35
|
+
"\n" << (char*72) << "\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
def mu_pp(obj)
|
39
|
+
delim("_") <<
|
40
|
+
obj.pretty_inspect.chomp <<
|
41
|
+
delim("=")
|
42
|
+
end
|
43
|
+
|
44
|
+
def unfixable
|
45
|
+
begin
|
46
|
+
yield
|
47
|
+
raise "claimed to be unfixable, but assertion succeeded"
|
48
|
+
rescue MiniTest::Assertion
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def assert_nothing_raised
|
53
|
+
yield
|
54
|
+
assert_nil nil
|
55
|
+
rescue => ex
|
56
|
+
raise MiniTest::Assertion,
|
57
|
+
exception_details(ex, "Expected nothing raised, but got:")
|
58
|
+
end
|
59
|
+
|
60
|
+
%w[
|
61
|
+
empty equal in_delta in_epsilon includes instance_of
|
62
|
+
kind_of match nil operator respond_to same
|
63
|
+
].each { |name|
|
64
|
+
alias_method "assert_not_#{name}", "refute_#{name}"
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
class BaseTest < JLMiniTest
|
69
|
+
include LiveAST.parser::Test
|
70
|
+
|
71
|
+
DATA_DIR = File.expand_path(File.dirname(__FILE__) + "/data")
|
72
|
+
|
73
|
+
def self.stdlib_has_source?
|
74
|
+
RUBY_ENGINE != "jruby"
|
75
|
+
end
|
76
|
+
|
77
|
+
def temp_file(code, basename = nil)
|
78
|
+
basename ||= ('a'..'z').to_a.shuffle.join + ".rb"
|
79
|
+
path = DATA_DIR + "/" + basename
|
80
|
+
FileUtils.mkdir DATA_DIR unless File.directory? DATA_DIR
|
81
|
+
|
82
|
+
write_file path, code if code
|
83
|
+
begin
|
84
|
+
yield path
|
85
|
+
ensure
|
86
|
+
FileUtils.rm_f path
|
87
|
+
FileUtils.rmdir DATA_DIR rescue nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_file(file, contents)
|
92
|
+
File.open(file, "w") { |f| f.print contents }
|
93
|
+
end
|
94
|
+
|
95
|
+
def return_block(&block)
|
96
|
+
block
|
97
|
+
end
|
98
|
+
|
99
|
+
def exception_backtrace
|
100
|
+
begin
|
101
|
+
yield
|
102
|
+
rescue Exception => e
|
103
|
+
e.backtrace
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def ignore(*args)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class RegularTest < BaseTest
|
112
|
+
def setup
|
113
|
+
super
|
114
|
+
require 'live_ast'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class ReplaceEvalTest < BaseTest
|
119
|
+
def initialize(*args)
|
120
|
+
super
|
121
|
+
ok = begin
|
122
|
+
require 'live_ast/full'
|
123
|
+
true
|
124
|
+
rescue LoadError
|
125
|
+
if RUBY_ENGINE == "ruby"
|
126
|
+
raise "need: gem install boc"
|
127
|
+
end
|
128
|
+
false
|
129
|
+
end
|
130
|
+
|
131
|
+
unless ok
|
132
|
+
self.class.class_eval do
|
133
|
+
instance_methods(false).each do |m|
|
134
|
+
remove_method(m)
|
135
|
+
define_method(m) { }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|