node-marshal 0.2.1 → 0.2.2
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 +4 -4
- data/COPYING +23 -23
- data/README.rdoc +56 -49
- data/bin/noderbc +63 -63
- data/bin/noderbc.bat +0 -0
- data/ext/node-marshal/base85r.c +194 -194
- data/ext/node-marshal/extconf.rb +2 -2
- data/ext/node-marshal/node193.h +321 -321
- data/ext/node-marshal/node220.h +347 -347
- data/ext/node-marshal/node230.h +361 -361
- data/ext/node-marshal/nodedump.c +2356 -2296
- data/ext/node-marshal/nodedump.h +60 -60
- data/ext/node-marshal/nodeinfo.c +619 -615
- data/lib/node-marshal.rb +227 -227
- data/test/lifegame.rb +145 -145
- data/test/test_base.rb +226 -226
- data/test/test_complex.rb +136 -136
- data/test/test_lifegame.rb +68 -68
- data/test/test_namedarg.rb +54 -0
- data/test/test_obfuscator.rb +36 -36
- data/test/test_qcall.rb +52 -52
- data/test/tinytet.rb +79 -79
- metadata +4 -3
data/test/test_complex.rb
CHANGED
@@ -1,137 +1,137 @@
|
|
1
|
-
require_relative '../lib/node-marshal.rb'
|
2
|
-
require 'test/unit'
|
3
|
-
|
4
|
-
# Wrapper for Ruby Proc class (Proc and lambdas) that
|
5
|
-
# saves the text representation of the function
|
6
|
-
class TestFunc
|
7
|
-
def initialize(text)
|
8
|
-
@text = text.to_s
|
9
|
-
@value = eval(text)
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_s
|
13
|
-
@text
|
14
|
-
end
|
15
|
-
|
16
|
-
def inspect
|
17
|
-
@text
|
18
|
-
end
|
19
|
-
|
20
|
-
def call(x,a)
|
21
|
-
@value.(x,a)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Class for generation of the complex structure that includes:
|
26
|
-
# Hashes, Arrays, Floats, Fixnums, Ranges, Strings, Symbols, Procs
|
27
|
-
# It is used for node testing
|
28
|
-
class TestHashTree
|
29
|
-
attr_reader :value, :depth
|
30
|
-
def initialize(*args)
|
31
|
-
if args.length == 2
|
32
|
-
rnd = Random.new
|
33
|
-
lambdas = ['->x,a{a*x**2 + 2*x + 1}', '->x,a{Math::sin(a) + x**2 * a**0.5}']
|
34
|
-
data_size, depth = args[0], args[1]
|
35
|
-
@depth = depth
|
36
|
-
@value = {:depth => depth, :data => [], :leaves =>[] }
|
37
|
-
data_size.times do
|
38
|
-
case rnd.rand(30000) % 7
|
39
|
-
when 0
|
40
|
-
@value[:data] << 10**(rnd.rand(600000) * 0.001 - 300)
|
41
|
-
when 1
|
42
|
-
@value[:data] << rnd.rand(2**31 - 1)
|
43
|
-
when 2
|
44
|
-
rndstr = (Array.new(25) { (rnd.rand(127 - 32) + 32).chr }).join
|
45
|
-
@value[:data] << rndstr
|
46
|
-
when 3
|
47
|
-
@value[:data] << TestFunc.new(lambdas[rnd.rand(30000) % lambdas.length])
|
48
|
-
when 4
|
49
|
-
a, b = rnd.rand(100), rnd.rand(10)
|
50
|
-
@value[:data] << (a..(a + b))
|
51
|
-
when 5
|
52
|
-
a, b = rnd.rand(100), rnd.rand(10)
|
53
|
-
@value[:data] << (a...(a + b))
|
54
|
-
when 6
|
55
|
-
rndsym = (Random.rand(1_000_000_000).to_s(36)).to_sym
|
56
|
-
@value[:data] << rndsym
|
57
|
-
else
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
if depth > 0
|
63
|
-
3.times do
|
64
|
-
@value[:leaves] << TestHashTree.new(data_size, depth - 1)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
elsif args.length == 1
|
68
|
-
h = args[0]
|
69
|
-
raise(ArgumentError, 'Input argument must be a hash') if !h.is_a?(Hash)
|
70
|
-
@depth = h[:depth]
|
71
|
-
@value = {
|
72
|
-
:depth => @depth,
|
73
|
-
:data => h[:data].map {|x| (x.class == String) ? x.dup : x },
|
74
|
-
:leaves => h[:leaves].map {|x| TestHashTree.new(x)}}
|
75
|
-
else
|
76
|
-
raise ArgumentError, 'Invalid numer of arguments'
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def to_s
|
81
|
-
@value.to_s
|
82
|
-
end
|
83
|
-
|
84
|
-
def inspect
|
85
|
-
self.to_s
|
86
|
-
end
|
87
|
-
# Recursive comparison of two complex structures
|
88
|
-
def ==(val)
|
89
|
-
if (self.depth != val.depth) ||
|
90
|
-
(self.value[:data].length != val.value[:data].length)
|
91
|
-
return false
|
92
|
-
else
|
93
|
-
# Compare data
|
94
|
-
res = true
|
95
|
-
v1, v2 = self.value[:data], val.value[:data]
|
96
|
-
v1.each_index do |ind|
|
97
|
-
if (v1[ind].class != Proc && v1[ind].class != TestFunc)
|
98
|
-
res = false if v1[ind] != v2[ind]
|
99
|
-
else
|
100
|
-
x, a = 1.2345, 1.5
|
101
|
-
y1, y2 = v1[ind].call(x,a), v2[ind].call(x,a)
|
102
|
-
res = false if y1 != y2
|
103
|
-
end
|
104
|
-
end
|
105
|
-
# Compare leaves
|
106
|
-
p1, p2 = self.value[:leaves], val.value[:leaves]
|
107
|
-
p1.each_index {|ind| res = false if p1[ind] != p2[ind] }
|
108
|
-
return res
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Tests that check the processing of large and/or complex nodes
|
114
|
-
class TestComplex < Test::Unit::TestCase
|
115
|
-
def test_bruteforce
|
116
|
-
# Create test tree, turn it to node and save it to disk
|
117
|
-
puts 'Creating the tree...'
|
118
|
-
tree_src = TestHashTree.new(20, 7);
|
119
|
-
puts 'Dumping the node...'
|
120
|
-
tree_str = tree_src.to_s
|
121
|
-
node = NodeMarshal.new(:srcmemory, tree_str)
|
122
|
-
tree_bin = node.to_bin
|
123
|
-
puts node.inspect
|
124
|
-
|
125
|
-
File.open('node.bin', 'wb') {|fp| fp << tree_bin }
|
126
|
-
puts " Source code size: %d bytes" % tree_str.length
|
127
|
-
puts " Binary data size: %d bytes" % tree_bin.length
|
128
|
-
# Clear the memory
|
129
|
-
node = nil; GC.enable; GC.start
|
130
|
-
# Load the node from the disk and turn it to tree
|
131
|
-
puts 'Loading the node...'
|
132
|
-
node = NodeMarshal.new(:binfile, 'node.bin')
|
133
|
-
puts node.inspect
|
134
|
-
tree_dest = TestHashTree.new(node.compile.eval)
|
135
|
-
assert_equal(tree_src, tree_dest)
|
136
|
-
end
|
1
|
+
require_relative '../lib/node-marshal.rb'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
# Wrapper for Ruby Proc class (Proc and lambdas) that
|
5
|
+
# saves the text representation of the function
|
6
|
+
class TestFunc
|
7
|
+
def initialize(text)
|
8
|
+
@text = text.to_s
|
9
|
+
@value = eval(text)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@text
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
@text
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(x,a)
|
21
|
+
@value.(x,a)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Class for generation of the complex structure that includes:
|
26
|
+
# Hashes, Arrays, Floats, Fixnums, Ranges, Strings, Symbols, Procs
|
27
|
+
# It is used for node testing
|
28
|
+
class TestHashTree
|
29
|
+
attr_reader :value, :depth
|
30
|
+
def initialize(*args)
|
31
|
+
if args.length == 2
|
32
|
+
rnd = Random.new
|
33
|
+
lambdas = ['->x,a{a*x**2 + 2*x + 1}', '->x,a{Math::sin(a) + x**2 * a**0.5}']
|
34
|
+
data_size, depth = args[0], args[1]
|
35
|
+
@depth = depth
|
36
|
+
@value = {:depth => depth, :data => [], :leaves =>[] }
|
37
|
+
data_size.times do
|
38
|
+
case rnd.rand(30000) % 7
|
39
|
+
when 0
|
40
|
+
@value[:data] << 10**(rnd.rand(600000) * 0.001 - 300)
|
41
|
+
when 1
|
42
|
+
@value[:data] << rnd.rand(2**31 - 1)
|
43
|
+
when 2
|
44
|
+
rndstr = (Array.new(25) { (rnd.rand(127 - 32) + 32).chr }).join
|
45
|
+
@value[:data] << rndstr
|
46
|
+
when 3
|
47
|
+
@value[:data] << TestFunc.new(lambdas[rnd.rand(30000) % lambdas.length])
|
48
|
+
when 4
|
49
|
+
a, b = rnd.rand(100), rnd.rand(10)
|
50
|
+
@value[:data] << (a..(a + b))
|
51
|
+
when 5
|
52
|
+
a, b = rnd.rand(100), rnd.rand(10)
|
53
|
+
@value[:data] << (a...(a + b))
|
54
|
+
when 6
|
55
|
+
rndsym = (Random.rand(1_000_000_000).to_s(36)).to_sym
|
56
|
+
@value[:data] << rndsym
|
57
|
+
else
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if depth > 0
|
63
|
+
3.times do
|
64
|
+
@value[:leaves] << TestHashTree.new(data_size, depth - 1)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
elsif args.length == 1
|
68
|
+
h = args[0]
|
69
|
+
raise(ArgumentError, 'Input argument must be a hash') if !h.is_a?(Hash)
|
70
|
+
@depth = h[:depth]
|
71
|
+
@value = {
|
72
|
+
:depth => @depth,
|
73
|
+
:data => h[:data].map {|x| (x.class == String) ? x.dup : x },
|
74
|
+
:leaves => h[:leaves].map {|x| TestHashTree.new(x)}}
|
75
|
+
else
|
76
|
+
raise ArgumentError, 'Invalid numer of arguments'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
@value.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def inspect
|
85
|
+
self.to_s
|
86
|
+
end
|
87
|
+
# Recursive comparison of two complex structures
|
88
|
+
def ==(val)
|
89
|
+
if (self.depth != val.depth) ||
|
90
|
+
(self.value[:data].length != val.value[:data].length)
|
91
|
+
return false
|
92
|
+
else
|
93
|
+
# Compare data
|
94
|
+
res = true
|
95
|
+
v1, v2 = self.value[:data], val.value[:data]
|
96
|
+
v1.each_index do |ind|
|
97
|
+
if (v1[ind].class != Proc && v1[ind].class != TestFunc)
|
98
|
+
res = false if v1[ind] != v2[ind]
|
99
|
+
else
|
100
|
+
x, a = 1.2345, 1.5
|
101
|
+
y1, y2 = v1[ind].call(x,a), v2[ind].call(x,a)
|
102
|
+
res = false if y1 != y2
|
103
|
+
end
|
104
|
+
end
|
105
|
+
# Compare leaves
|
106
|
+
p1, p2 = self.value[:leaves], val.value[:leaves]
|
107
|
+
p1.each_index {|ind| res = false if p1[ind] != p2[ind] }
|
108
|
+
return res
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Tests that check the processing of large and/or complex nodes
|
114
|
+
class TestComplex < Test::Unit::TestCase
|
115
|
+
def test_bruteforce
|
116
|
+
# Create test tree, turn it to node and save it to disk
|
117
|
+
puts 'Creating the tree...'
|
118
|
+
tree_src = TestHashTree.new(20, 7);
|
119
|
+
puts 'Dumping the node...'
|
120
|
+
tree_str = tree_src.to_s
|
121
|
+
node = NodeMarshal.new(:srcmemory, tree_str)
|
122
|
+
tree_bin = node.to_bin
|
123
|
+
puts node.inspect
|
124
|
+
|
125
|
+
File.open('node.bin', 'wb') {|fp| fp << tree_bin }
|
126
|
+
puts " Source code size: %d bytes" % tree_str.length
|
127
|
+
puts " Binary data size: %d bytes" % tree_bin.length
|
128
|
+
# Clear the memory
|
129
|
+
node = nil; GC.enable; GC.start
|
130
|
+
# Load the node from the disk and turn it to tree
|
131
|
+
puts 'Loading the node...'
|
132
|
+
node = NodeMarshal.new(:binfile, 'node.bin')
|
133
|
+
puts node.inspect
|
134
|
+
tree_dest = TestHashTree.new(node.compile.eval)
|
135
|
+
assert_equal(tree_src, tree_dest)
|
136
|
+
end
|
137
137
|
end
|
data/test/test_lifegame.rb
CHANGED
@@ -1,68 +1,68 @@
|
|
1
|
-
require_relative '../lib/node-marshal.rb'
|
2
|
-
require 'test/unit'
|
3
|
-
|
4
|
-
# Set of tests for Convay's life game running and compilation
|
5
|
-
# (includes tests with binary Ruby files)
|
6
|
-
class TestLifeGame < Test::Unit::TestCase
|
7
|
-
# Compile and launch life game with different
|
8
|
-
# compiler options (requires zlib library)
|
9
|
-
|
10
|
-
def test_compile
|
11
|
-
compile_with_opts(nil)
|
12
|
-
compile_with_opts(:compress=>true)
|
13
|
-
end
|
14
|
-
# Test life game with glider gun configuration
|
15
|
-
def test_glider_gun
|
16
|
-
# Compile the class to the instuction sequence
|
17
|
-
bin = NodeMarshal.new(:srcfile, 'lifegame.rb')
|
18
|
-
puts bin.inspect
|
19
|
-
bin.compile.eval
|
20
|
-
# Calculate the life game
|
21
|
-
res_node = run_game
|
22
|
-
# Play the game of life without NodeMarshal
|
23
|
-
Object.send(:remove_const, :LifeGame)
|
24
|
-
load('lifegame.rb')
|
25
|
-
res_load = run_game
|
26
|
-
# Compare the results
|
27
|
-
assert_equal(res_node, res_load)
|
28
|
-
Object.send(:remove_const, :LifeGame)
|
29
|
-
puts res_load
|
30
|
-
end
|
31
|
-
|
32
|
-
# Runs Life game initialized with "glider gun" configuration
|
33
|
-
# and makes 75 turns (changes of generations)
|
34
|
-
def run_game
|
35
|
-
g = LifeGame::Grid.new(25, 80)
|
36
|
-
g.cfg_glider!
|
37
|
-
g.cfg_glider_gun!
|
38
|
-
75.times {g.make_step!}
|
39
|
-
g.to_ascii
|
40
|
-
end
|
41
|
-
|
42
|
-
# Compiles Convay's life game using proposed options
|
43
|
-
# (opts is a Hash, see NodeMarshal#compile_rb_file method)
|
44
|
-
def compile_with_opts(opts)
|
45
|
-
stub = <<-EOS
|
46
|
-
g = LifeGame::Grid.new(25, 80)
|
47
|
-
g.cfg_glider_gun!
|
48
|
-
75.times {g.make_step!}
|
49
|
-
File.open('life.res', 'w') {|fp| fp << g.to_ascii }
|
50
|
-
EOS
|
51
|
-
# Compile Life game to the file and obtain the result
|
52
|
-
if opts == nil
|
53
|
-
NodeMarshal.compile_rb_file('lifegame_bin.rb', 'lifegame.rb')
|
54
|
-
else
|
55
|
-
NodeMarshal.compile_rb_file('lifegame_bin.rb', 'lifegame.rb', opts)
|
56
|
-
end
|
57
|
-
txt = File.read('lifegame_bin.rb') + stub
|
58
|
-
File.open('lifegame_bin.rb', 'w') {|fp| fp << txt }
|
59
|
-
`ruby lifegame_bin.rb`
|
60
|
-
res_file = File.read('life.res')
|
61
|
-
`rm life.res`
|
62
|
-
# Run the life game without compilation to the file
|
63
|
-
load('lifegame.rb')
|
64
|
-
res_load = run_game
|
65
|
-
Object.send(:remove_const, :LifeGame)
|
66
|
-
assert_equal(res_file, res_load)
|
67
|
-
end
|
68
|
-
end
|
1
|
+
require_relative '../lib/node-marshal.rb'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
# Set of tests for Convay's life game running and compilation
|
5
|
+
# (includes tests with binary Ruby files)
|
6
|
+
class TestLifeGame < Test::Unit::TestCase
|
7
|
+
# Compile and launch life game with different
|
8
|
+
# compiler options (requires zlib library)
|
9
|
+
|
10
|
+
def test_compile
|
11
|
+
compile_with_opts(nil)
|
12
|
+
compile_with_opts(:compress=>true)
|
13
|
+
end
|
14
|
+
# Test life game with glider gun configuration
|
15
|
+
def test_glider_gun
|
16
|
+
# Compile the class to the instuction sequence
|
17
|
+
bin = NodeMarshal.new(:srcfile, 'lifegame.rb')
|
18
|
+
puts bin.inspect
|
19
|
+
bin.compile.eval
|
20
|
+
# Calculate the life game
|
21
|
+
res_node = run_game
|
22
|
+
# Play the game of life without NodeMarshal
|
23
|
+
Object.send(:remove_const, :LifeGame)
|
24
|
+
load('lifegame.rb')
|
25
|
+
res_load = run_game
|
26
|
+
# Compare the results
|
27
|
+
assert_equal(res_node, res_load)
|
28
|
+
Object.send(:remove_const, :LifeGame)
|
29
|
+
puts res_load
|
30
|
+
end
|
31
|
+
|
32
|
+
# Runs Life game initialized with "glider gun" configuration
|
33
|
+
# and makes 75 turns (changes of generations)
|
34
|
+
def run_game
|
35
|
+
g = LifeGame::Grid.new(25, 80)
|
36
|
+
g.cfg_glider!
|
37
|
+
g.cfg_glider_gun!
|
38
|
+
75.times {g.make_step!}
|
39
|
+
g.to_ascii
|
40
|
+
end
|
41
|
+
|
42
|
+
# Compiles Convay's life game using proposed options
|
43
|
+
# (opts is a Hash, see NodeMarshal#compile_rb_file method)
|
44
|
+
def compile_with_opts(opts)
|
45
|
+
stub = <<-EOS
|
46
|
+
g = LifeGame::Grid.new(25, 80)
|
47
|
+
g.cfg_glider_gun!
|
48
|
+
75.times {g.make_step!}
|
49
|
+
File.open('life.res', 'w') {|fp| fp << g.to_ascii }
|
50
|
+
EOS
|
51
|
+
# Compile Life game to the file and obtain the result
|
52
|
+
if opts == nil
|
53
|
+
NodeMarshal.compile_rb_file('lifegame_bin.rb', 'lifegame.rb')
|
54
|
+
else
|
55
|
+
NodeMarshal.compile_rb_file('lifegame_bin.rb', 'lifegame.rb', opts)
|
56
|
+
end
|
57
|
+
txt = File.read('lifegame_bin.rb') + stub
|
58
|
+
File.open('lifegame_bin.rb', 'w') {|fp| fp << txt }
|
59
|
+
`ruby lifegame_bin.rb`
|
60
|
+
res_file = File.read('life.res')
|
61
|
+
`rm life.res`
|
62
|
+
# Run the life game without compilation to the file
|
63
|
+
load('lifegame.rb')
|
64
|
+
res_load = run_game
|
65
|
+
Object.send(:remove_const, :LifeGame)
|
66
|
+
assert_equal(res_file, res_load)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative '../lib/node-marshal.rb'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
|
6
|
+
# This unit tests Ruby 2.0 named arguments support
|
7
|
+
class TestNamedArg < Test::Unit::TestCase
|
8
|
+
def test_namedarg
|
9
|
+
# For Ruby 2.1 and higher
|
10
|
+
program_21 = %q{
|
11
|
+
def testfunc(x, foo:, bar: 'default', stuff:)
|
12
|
+
x.to_s + " " + foo.to_s + " " + bar.to_s + " " + stuff.to_s
|
13
|
+
end
|
14
|
+
def blockfunc(&block)
|
15
|
+
block.call(foo: 'bar')
|
16
|
+
end
|
17
|
+
|
18
|
+
blockstr = blockfunc do |foo:|
|
19
|
+
foo
|
20
|
+
end
|
21
|
+
|
22
|
+
testfunc(1, foo: 'a', stuff: 1e5) + "\n" +
|
23
|
+
testfunc(1, stuff: -50, foo: 'a', bar: 1.23e10) + "\n" + blockstr + "\n"
|
24
|
+
}
|
25
|
+
# For Ruby 2.0
|
26
|
+
program_20 = %q{
|
27
|
+
def testfunc(x, bar: 'default')
|
28
|
+
x.to_s + " " + bar.to_s
|
29
|
+
end
|
30
|
+
testfunc(1) + "\n" + testfunc(1, bar: 'default')
|
31
|
+
}
|
32
|
+
# Detect version of Ruby
|
33
|
+
ver = RUBY_VERSION
|
34
|
+
ver = (ver[0] + ver[2] + ver[4]).to_i
|
35
|
+
# Version-specific stuff
|
36
|
+
program = ""
|
37
|
+
if ver >= 210
|
38
|
+
program = program_21
|
39
|
+
elsif ver >= 210
|
40
|
+
program = program_20
|
41
|
+
else
|
42
|
+
assert_equal(false, true, "Ruby 2.0 or higher is required for named arguments test")
|
43
|
+
end
|
44
|
+
# Test source code compilation
|
45
|
+
node = NodeMarshal.new(:srcmemory, program)
|
46
|
+
node.show_offsets = true
|
47
|
+
#puts node.dump_tree_short
|
48
|
+
bindump = node.to_bin
|
49
|
+
node = NodeMarshal.new(:binmemory, bindump)
|
50
|
+
res_node = node.compile.eval
|
51
|
+
res_text = eval(program)
|
52
|
+
assert_equal(res_text, res_node)
|
53
|
+
end
|
54
|
+
end
|