ruby-perl 0.99.15j

Sign up to get free protection for your applications and to get access to all the features.
data/lib/perl/value.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Perl
2
+ class Value
3
+ end
4
+ end
@@ -0,0 +1,54 @@
1
+ require 'perl/value'
2
+ require 'ffi'
3
+
4
+ class Perl::Value::Array
5
+ class Av < FFI::Struct
6
+ class Xnv_u < FFI::Union
7
+ layout :xnv_nv, :long,
8
+ :xgv_stash, :pointer,
9
+ :xpad_cop_seq, :long,
10
+ :xbm_s, :long
11
+ end
12
+
13
+ layout :xnv_u, Xnv_u,
14
+ :xav_fill, :long,
15
+ :xav_max, :long
16
+
17
+ def inspect
18
+ "<#{self.class.name} @xav_fill=#{self[:xav_fill].inspect} @xav_max=#{self[:xav_max].inspect}>"
19
+ end
20
+ end
21
+
22
+ def initialize(args)
23
+ @perl = Perl.PL_curinterp
24
+ @value = nil
25
+ @array = nil
26
+ @av = nil
27
+
28
+ case args
29
+ when Perl::Value::Scalar::SV
30
+ @array = args[:sv_u][:svu_array]
31
+ @av = Av.new(args[:sv_any])
32
+ when nil
33
+ else
34
+ raise "Don't know how to handle #{args.class} (#{args.inspect})"
35
+ end
36
+ end
37
+
38
+ def value
39
+ return @value if @value
40
+ if @array
41
+ @value = @array.get_array_of_pointer(0, @av[:xav_fill]+1).map do |ptr|
42
+ Perl::Value::Scalar.new(ptr)
43
+ end
44
+ else
45
+ return nil
46
+ end
47
+ end
48
+
49
+ def freeze!
50
+ self.value.each { |v| v.freeze! }
51
+ @array = nil
52
+ @av = nil
53
+ end
54
+ end
@@ -0,0 +1,73 @@
1
+ require 'perl/value'
2
+ require 'perl/value/scalar'
3
+ require 'ffi'
4
+
5
+ require 'stringio'
6
+
7
+ class Perl::Value::Hash
8
+ HV_DISABLE_UVAR_XKEY = 0x01
9
+ HV_FETCH_ISSTORE = 0x04
10
+ HV_FETCH_ISEXISTS = 0x08
11
+ HV_FETCH_LVALUE = 0x10
12
+ HV_FETCH_JUST_SV = 0x20
13
+ HV_DELETE = 0x40
14
+
15
+ class << self
16
+ def to_perl(hash)
17
+ build_hv(Perl.PL_curinterp, hash)
18
+ end
19
+
20
+ protected
21
+ def build_hv(perl, hash)
22
+ new_hv(perl).tap do |hv|
23
+ hash.each do |k,v|
24
+ value = value_to_sv(k, v)
25
+ add_key_value(perl, hv, k.to_s, value) if value
26
+ end
27
+ end
28
+ end
29
+
30
+ def new_hv(perl)
31
+ Perl.Perl_newHV(perl)
32
+ end
33
+
34
+ def value_to_sv(key, value)
35
+ case value
36
+ when Array, IO
37
+ puts "Don't know how to handle #{value.class} (#{key} => #{value.inspect})"
38
+ when FalseClass
39
+ Perl::Value::Scalar.to_perl(value.to_s) # FIXME
40
+ when Method
41
+ puts "Cannot handle value with class=#{value.class} (#{key} => #{value.inspect}), skipping"
42
+ when String, StringIO
43
+ Perl::Value::Scalar.to_perl(value)
44
+ when TrueClass
45
+ Perl::Value::Scalar.to_perl(value.to_s) # FIXME
46
+ else
47
+ raise "Don't know how to handle #{value.class} (#{key} => #{value.inspect})"
48
+ end
49
+ end
50
+
51
+ def add_key_value(perl, hv, key, value)
52
+ Perl.Perl_hv_common_key_len(perl, hv, key, key.length, (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), value, 0)
53
+ end
54
+ end
55
+
56
+ def initialize(args = nil)
57
+ @perl = Perl.PL_curinterp
58
+ @hash = nil
59
+ @hv = nil
60
+
61
+ case args
62
+ when Hash
63
+ @hash = args
64
+ when nil
65
+ else
66
+ raise "Don't know how to handle #{args.class} (#{args.inspect})"
67
+ end
68
+ end
69
+
70
+ def to_perl
71
+ @hv ||= self.class.send(:build_hv, @perl, @hash)
72
+ end
73
+ end
@@ -0,0 +1,130 @@
1
+ require 'perl/value'
2
+ require 'perl/value/array'
3
+ require 'ffi'
4
+
5
+ class Perl::Value::Scalar
6
+ class SV < FFI::Struct
7
+ class SvU < FFI::Union
8
+ layout :svu_iv, :long,
9
+ :svu_uv, :long, # XXX ?
10
+ :svu_rv, :pointer,
11
+ :svu_pv, :string,
12
+ :svu_array, :pointer,
13
+ :svu_hash, :pointer,
14
+ :svu_gp, :pointer
15
+ end
16
+
17
+ layout :sv_any, :pointer,
18
+ :sv_refcnt, :int32,
19
+ :sv_flags, :int32,
20
+ :sv_u, SvU
21
+
22
+ SVt_NULL = 0
23
+ SVt_BIND = 1
24
+ SVt_IV = 2
25
+ SVt_NV = 3
26
+ SVt_RV = 4
27
+ SVt_PV = 5
28
+ SVt_PVIV = 6
29
+ SVt_PVNV = 7
30
+ SVt_PVMG = 8
31
+ SVt_PVGV = 9
32
+ SVt_PVLV = 10
33
+ SVt_PVAV = 11
34
+ SVt_PVHV = 12
35
+ SVt_PVCV = 13
36
+ SVt_PVFM = 14
37
+ SVt_PVIO = 15
38
+ SVTYPEMASK = 0xff
39
+
40
+ SVf_IOK = 0x00000100
41
+ SVf_NOK = 0x00000200
42
+ SVf_POK = 0x00000400
43
+ SVf_ROK = 0x00000800
44
+
45
+ def value
46
+ case
47
+ when (self[:sv_flags] & SVf_POK) == SVf_POK
48
+ self[:sv_u][:svu_pv]
49
+ when (self[:sv_flags] & SVf_ROK) == SVf_ROK
50
+ Perl::Value::Scalar.new(self[:sv_u][:svu_rv])
51
+ when (self[:sv_flags] & SVTYPEMASK) == SVt_PVAV
52
+ Perl::Value::Array.new(self)
53
+ else
54
+ raise "Don't know how to handle #{self[:sv_u]} (#{self.inspect})"
55
+ end
56
+ end
57
+
58
+ def reference?
59
+ (self[:sv_flags] & SVf_ROK) == SVf_ROK
60
+ end
61
+
62
+ def deref
63
+ raise "Not a reference!" unless (self[:sv_flags] & SVf_ROK) == SVf_ROK
64
+
65
+ SV.new(self[:sv_u][:svu_rv]).value
66
+ end
67
+
68
+ def inspect
69
+ "<#{self.class.name} @pointer=#{self.pointer} @sv_any=#{self[:sv_any].inspect} @sv_refcnt=#{self[:sv_refcnt].inspect} @sv_flags=0x#{self[:sv_flags].to_s(16)} @sv_u=#{self[:sv_u].inspect}>"
70
+ end
71
+ end
72
+
73
+ class << self
74
+ def to_perl(value)
75
+ case value
76
+ when String
77
+ Perl.Perl_newSVpv(Perl.PL_curinterp, value, value.length)
78
+ when StringIO
79
+ value = value.string
80
+ Perl.Perl_newSVpv(Perl.PL_curinterp, value, value.length)
81
+ when nil
82
+ else
83
+ raise "Don't know how to handle #{value.class} (#{value.inspect})"
84
+ end
85
+ end
86
+ end
87
+
88
+ def initialize(args = nil)
89
+ @perl = Perl.PL_curinterp
90
+ @scalar = nil
91
+ @sv = nil
92
+ @deref = nil
93
+ @is_ref = nil
94
+
95
+ case args
96
+ when FFI::Pointer
97
+ @sv = SV.new(args)
98
+ when String
99
+ @scalar = args
100
+ when nil
101
+ else
102
+ raise "Don't know how to handle #{args.class} (#{args.inspect})"
103
+ end
104
+ end
105
+
106
+ def to_perl
107
+ @sv ||= Perl.Perl_newSVpv(@perl, @scalar, @scalar.length)
108
+ end
109
+
110
+ def value
111
+ @scalar ||= @sv ? @sv.value : nil
112
+ end
113
+
114
+ def reference?
115
+ if @is_ref.nil?
116
+ @is_ref = @sv.reference?
117
+ else
118
+ @is_ref
119
+ end
120
+ end
121
+
122
+ def deref
123
+ @deref ||= to_perl.deref
124
+ end
125
+
126
+ def freeze!
127
+ reference? ? self.deref.freeze! : self.value
128
+ @sv = nil
129
+ end
130
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ require 'perl/ext/hash'
4
+ require 'perl/interpreter'
5
+
6
+ describe Hash do
7
+ describe "#to_perl" do
8
+ before(:all) do
9
+ @interpreter = Perl::Interpreter.new
10
+ end
11
+ after(:all) do
12
+ @interpreter.stop
13
+ end
14
+
15
+ describe "on an empty Hash" do
16
+ let(:subject) { {} }
17
+
18
+ it "should return the expected result" do
19
+ ptr = subject.to_perl
20
+ ptr.should be_kind_of(FFI::Pointer)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ require 'perl/ext/object'
4
+
5
+ class Object
6
+ def perl_interpreter
7
+ @_perl
8
+ end
9
+ end
10
+
11
+ describe Object do
12
+ after(:each) do
13
+ perl_interpreter.stop
14
+ end
15
+
16
+ describe "#Perl" do
17
+ it "should instance an interpreter" do
18
+ perl_interpreter.should be_nil
19
+ Perl("$_")
20
+ perl_interpreter.should_not be_nil
21
+ perl_interpreter.should be_kind_of(Perl::Interpreter)
22
+ end
23
+
24
+ # it "should run the provided code" do
25
+ # ret = capture_stdout_descriptor do
26
+ # Perl("print \"hi there\";")
27
+ # end
28
+ # ret.should == "hi there"
29
+ # end
30
+ #
31
+ # it "should run the provided code block" do
32
+ # ret = capture_stdout_descriptor do
33
+ # Perl do
34
+ # run "print \"hi there\";"
35
+ # end
36
+ # end
37
+ # ret.should == "hi there"
38
+ # end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ require 'perl/ext/string'
4
+ require 'perl/interpreter'
5
+
6
+ describe String do
7
+ describe "#to_perl" do
8
+ before(:all) do
9
+ @interpreter = Perl::Interpreter.new
10
+ end
11
+ after(:all) do
12
+ @interpreter.stop
13
+ end
14
+
15
+ describe "on an empty String" do
16
+ let(:subject) { "" }
17
+
18
+ it "should return the expected result" do
19
+ ptr = subject.to_perl
20
+ ptr.should be_kind_of(FFI::Pointer)
21
+ value = Perl::Value::Scalar.new(ptr)
22
+ value.value.should == ""
23
+ end
24
+ end
25
+
26
+ describe "on a String" do
27
+ let(:subject) { "something" }
28
+
29
+ it "should return the expected result" do
30
+ ptr = subject.to_perl
31
+ ptr.should be_kind_of(FFI::Pointer)
32
+ value = Perl::Value::Scalar.new(ptr)
33
+ value.value.should == "something"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ require 'perl/value/hash'
4
+ require 'perl/interpreter'
5
+
6
+ describe Perl::Value::Hash do
7
+ include PerlValueHelpers
8
+
9
+ context "built without arguments" do
10
+ its(:perl) { should_not be_nil }
11
+ its(:hash) { should be_nil }
12
+ its(:hv) { should be_nil }
13
+ end
14
+
15
+ context "built from a String" do
16
+ let(:input) { "pippo" }
17
+ it do
18
+ lambda { described_class.new(input) }.should raise_error
19
+ end
20
+ end
21
+
22
+ context "built from a Hash" do
23
+ before(:all) do
24
+ @interpreter = Perl::Interpreter.new
25
+ end
26
+ after(:all) do
27
+ @interpreter.stop
28
+ end
29
+
30
+ let(:input) { {:a => "b", :c => "d"} }
31
+ subject { described_class.new(input) }
32
+ its(:perl) { should_not be_nil }
33
+ its(:hash) { should_not be_nil }
34
+ its(:hash) { should eq(input) }
35
+ its(:hv) { should be_nil }
36
+
37
+ describe "when #to_perl is called" do
38
+ it "should return the expected object" do
39
+ subject.to_perl.should_not be_nil
40
+ end
41
+
42
+ it "should cache the returned object" do
43
+ subject.to_perl.should == subject.hv
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,220 @@
1
+ require 'spec_helper'
2
+
3
+ require 'perl/interpreter'
4
+ require 'perl/value/hash'
5
+ require 'ffi'
6
+
7
+ class PerlCommon
8
+ extend Perl::Common
9
+ end
10
+
11
+ describe Perl::Interpreter do
12
+ describe "#initialize" do
13
+ it "should call setup" do
14
+ original_setup = Perl.setup
15
+ Perl.should_receive(:setup) do
16
+ @called = true
17
+ original_setup
18
+ end
19
+
20
+ described_class.new
21
+ @called.should be_true
22
+ end
23
+
24
+ it "should call perl_alloc and pass its return value to more methods" do
25
+ Perl.should_receive(:perl_alloc).and_return(42)
26
+ Perl.should_receive(:perl_construct).with(42)
27
+ Perl.should_receive(:perl_parse) do |*args|
28
+ @args = args
29
+ end
30
+ Perl.should_receive(:perl_run).with(42)
31
+
32
+ described_class.new
33
+ @args[0].should == 42
34
+ end
35
+
36
+ # XXX this is a bad spec, it should be a spec to fake_args itself
37
+ it "should call perl_parse with the expected arguments" do
38
+ argc, argv = PerlCommon.embedded_argv_to_ffi
39
+ Perl.should_receive(:perl_parse) do |*args|
40
+ @args = args
41
+ end
42
+
43
+ described_class.new
44
+ @args[2].should == argc
45
+ @args[3].should be_kind_of(FFI::MemoryPointer)
46
+ @args[3].size.should == FFI.type_size(:pointer) * (argc + 1)
47
+
48
+ (0..@args[2]).each do |i|
49
+ entry = @args[3][i].get_pointer(0)
50
+ entry.should be_kind_of(FFI::Pointer)
51
+ end
52
+ @args[3][0].get_pointer(0).read_string.should == ""
53
+ @args[3][1].get_pointer(0).read_string.should == "-e"
54
+ @args[3][2].get_pointer(0).read_string.should == "0"
55
+ @args[3][3].get_pointer(0).address.should == 0
56
+ end
57
+ end
58
+
59
+ describe "#call" do
60
+ after(:each) do
61
+ subject.stop
62
+ end
63
+
64
+ it "should prepare a Perl stack" do
65
+ Perl::Stack.should_receive(:function_stack)
66
+
67
+ subject.call("something", [], :void)
68
+ end
69
+
70
+ it "should call args_on_stack" do
71
+ Perl::Stack::Function.should_receive(:new).any_number_of_times do |stack, args|
72
+ @stack = stack
73
+ @args = args
74
+ end
75
+
76
+ subject.call("something", [], :void)
77
+ @args.should == []
78
+ subject.call("something", [{}, :hash], :void)
79
+ @args.should == [{}, :hash]
80
+ end
81
+
82
+ # XXX this should be a spec for Perl::Stack::Function
83
+ # it "should push all the arguments onto the stack" do
84
+ # @stack = []
85
+ #
86
+ # fs = mock(:function_stack).tap do |stack|
87
+ # stack.should_receive(:push).any_number_of_times do |arg|
88
+ # @stack << arg
89
+ # end
90
+ # end
91
+ # Perl::Stack::Function.should_receive(:new).and_yield(fs)
92
+ # Perl.should_receive(:Perl_call_pv)
93
+ #
94
+ # subject.call("something", [{}, :hash], :void)
95
+ # @stack.length.should == 1
96
+ # @stack[0].should be_kind_of(FFI::Pointer)
97
+ # end
98
+
99
+ it "should pass a String to a Perl function" do
100
+ func = subject.eval("require 'dumpvar.pl'; sub { dumpValue(\\@_); };")
101
+
102
+ ret = capture_stdout_descriptor do
103
+ subject.call(func, "42", :void)
104
+ end
105
+ ret = ret.split(/\n/)
106
+ ret.length.should == 1
107
+ ret[0].should =~ /^0\s+42/
108
+ end
109
+
110
+ it "should pass a String reference to a Perl function" do
111
+ func = subject.eval("require 'dumpvar.pl'; sub { dumpValue(\\@_); };")
112
+
113
+ ret = capture_stdout_descriptor do
114
+ subject.call(func, {:ref => "42"}, :void)
115
+ end
116
+ ret = ret.split(/\n/)
117
+ ret.length.should == 2
118
+ ret[0].should =~ /^0\s+SCALAR/
119
+ ret[1].should =~ /^\s+-> 42/
120
+ end
121
+
122
+ it "should pass a Hash reference to a Perl function" do
123
+ func = subject.eval("require 'dumpvar.pl'; sub { dumpValue(\\@_); };")
124
+
125
+ ret = capture_stdout_descriptor do
126
+ subject.call(func, {:ref => {'a' => 'b'}}, :void)
127
+ end
128
+ ret = ret.split(/\n/)
129
+ ret.length.should == 2
130
+ ret[0].should =~ /^0\s+HASH/
131
+ ret[1].should =~ /^\s+'a' => 'b'/
132
+ end
133
+
134
+ context "in a void context" do
135
+ context "when passed a string" do
136
+ it "should yield nil" do
137
+ func = subject.eval("sub { return \"string\"; };")
138
+
139
+ subject.call(func, [], :void) do |ret|
140
+ ret.should be_nil
141
+ end
142
+ end
143
+
144
+ it "the return value should be nil" do
145
+ func = subject.eval("sub { return \"string\"; };")
146
+
147
+ subject.call(func, [], :void).should be_nil
148
+ end
149
+ end
150
+
151
+ context "when passed an array" do
152
+ it "should yield nil" do
153
+ func = subject.eval("sub { return [\"1\", \"2\"]; };")
154
+
155
+ subject.call(func, [], :void) do |ret|
156
+ ret.should be_nil
157
+ end
158
+ end
159
+
160
+ it "the return value should be nil" do
161
+ func = subject.eval("sub { return [\"1\", \"2\"]; };")
162
+
163
+ subject.call(func, [], :void).should be_nil
164
+ end
165
+ end
166
+ end
167
+
168
+ context "in a scalar context" do
169
+ context "when passed a string" do
170
+ it "should yield a scalar that acts as a string" do
171
+ func = subject.eval("sub { return \"string\"; };")
172
+
173
+ subject.call(func, [], :scalar) do |ret|
174
+ ret.value.should == "string"
175
+ end
176
+ end
177
+
178
+ it "should return a scalar that acts as a string" do
179
+ func = subject.eval("sub { return \"string\"; };")
180
+
181
+ ret = subject.call(func, [], :scalar)
182
+ ret.value.should == "string"
183
+ end
184
+ end
185
+
186
+ context "when passed an array" do
187
+ it "should yield an array reference" do
188
+ func = subject.eval("sub { return [\"1\", \"2\"]; };")
189
+
190
+ subject.call(func, [], :scalar) do |ret|
191
+ ret.reference?.should be_true
192
+ ret.deref.should be_kind_of(Perl::Value::Array)
193
+
194
+ array = ret.deref.value
195
+ array.length.should == 2
196
+ array[0].should be_kind_of(Perl::Value::Scalar)
197
+ array[0].value.should == "1"
198
+ array[1].should be_kind_of(Perl::Value::Scalar)
199
+ array[1].value.should == "2"
200
+ end
201
+ end
202
+
203
+ it "the return value should be an array reference" do
204
+ func = subject.eval("sub { return [\"1\", \"2\"]; };")
205
+
206
+ ret = subject.call(func, [], :scalar)
207
+ ret.reference?.should be_true
208
+ ret.deref.should be_kind_of(Perl::Value::Array)
209
+
210
+ array = ret.deref.value
211
+ array.length.should == 2
212
+ array[0].should be_kind_of(Perl::Value::Scalar)
213
+ array[0].value.should == "1"
214
+ array[1].should be_kind_of(Perl::Value::Scalar)
215
+ array[1].value.should == "2"
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end