ruby-perl 0.99.15j
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.
- data/.autotest +1 -0
- data/.rspec +0 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +49 -0
- data/README.md +123 -0
- data/Rakefile +5 -0
- data/autotest/discover.rb +1 -0
- data/bin/rperl +23 -0
- data/examples/hello.pl +1 -0
- data/examples/hello.rb +9 -0
- data/examples/hello_block.rb +5 -0
- data/examples/hello_here.rb +5 -0
- data/examples/passenger/config.ru +3 -0
- data/examples/passenger/log/.keep +0 -0
- data/examples/passenger/public/.keep +0 -0
- data/examples/passenger/tmp/.keep +0 -0
- data/examples/passenger/webapp.psgi +16 -0
- data/examples/perl.ru +4 -0
- data/examples/webapp.psgi +16 -0
- data/lib/perl.rb +43 -0
- data/lib/perl/common.rb +64 -0
- data/lib/perl/ext/hash.rb +10 -0
- data/lib/perl/ext/object.rb +30 -0
- data/lib/perl/ext/string.rb +10 -0
- data/lib/perl/ffi_lib.rb +65 -0
- data/lib/perl/internal.rb +73 -0
- data/lib/perl/interpreter.rb +89 -0
- data/lib/perl/rack.rb +45 -0
- data/lib/perl/shell.rb +28 -0
- data/lib/perl/stack.rb +99 -0
- data/lib/perl/stack/function.rb +67 -0
- data/lib/perl/value.rb +4 -0
- data/lib/perl/value/array.rb +54 -0
- data/lib/perl/value/hash.rb +73 -0
- data/lib/perl/value/scalar.rb +130 -0
- data/spec/ext/hash_spec.rb +24 -0
- data/spec/ext/object_spec.rb +40 -0
- data/spec/ext/string_spec.rb +37 -0
- data/spec/hash_value_spec.rb +47 -0
- data/spec/interpreter_spec.rb +220 -0
- data/spec/scalar_value_spec.rb +82 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/perl_value_helpers.rb +13 -0
- metadata +109 -0
data/lib/perl/ffi_lib.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module Perl
|
2
|
+
module FFILib
|
3
|
+
def self.included(klass)
|
4
|
+
klass.instance_eval do
|
5
|
+
require 'perl/internal'
|
6
|
+
extend FFI::Library
|
7
|
+
|
8
|
+
klass.ffi_lib '/System/Library/Perl/5.10.0/darwin-thread-multi-2level/CORE/libperl.dylib'
|
9
|
+
|
10
|
+
# PERL_SYS_INIT3()
|
11
|
+
attach_function 'Perl_sys_init3', [:int, :pointer, :pointer], :void
|
12
|
+
# PERL_SYS_TERM()
|
13
|
+
attach_function 'Perl_sys_term', [], :void
|
14
|
+
|
15
|
+
klass.attach_function 'perl_alloc', [], :pointer
|
16
|
+
attach_function 'perl_construct', [:pointer], :void
|
17
|
+
# PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
|
18
|
+
attach_function 'perl_parse', [:pointer, :pointer, :int, :pointer, :pointer], :void
|
19
|
+
attach_function 'perl_run', [:pointer], :void
|
20
|
+
attach_function 'perl_destruct', [:pointer], :void
|
21
|
+
attach_function 'perl_free', [:pointer], :void
|
22
|
+
|
23
|
+
# eval_pv()
|
24
|
+
attach_function 'Perl_eval_pv', [:pointer, :string, :int], :pointer
|
25
|
+
# call_pv()
|
26
|
+
attach_function 'Perl_call_pv', [:pointer, :string, :int], :int
|
27
|
+
# call_sv()
|
28
|
+
attach_function 'Perl_call_sv', [:pointer, :pointer, :int], :int
|
29
|
+
|
30
|
+
# ENTER()
|
31
|
+
attach_function 'Perl_push_scope', [:pointer], :void
|
32
|
+
# SAVETMPS()
|
33
|
+
attach_function 'Perl_save_int', [:pointer, :pointer], :void
|
34
|
+
# FREETMPS()
|
35
|
+
attach_function 'Perl_free_tmps', [:pointer], :void
|
36
|
+
# LEAVE()
|
37
|
+
attach_function 'Perl_pop_scope', [:pointer], :void
|
38
|
+
# PUSHMARK()
|
39
|
+
attach_function 'Perl_markstack_grow', [:pointer], :void
|
40
|
+
|
41
|
+
# newSV()
|
42
|
+
attach_function 'Perl_newSVpv', [:pointer, :string, :int], :pointer
|
43
|
+
|
44
|
+
attach_function 'Perl_newRV_noinc', [:pointer, :pointer], :pointer
|
45
|
+
attach_function 'Perl_sv_2mortal', [:pointer, :pointer], :pointer
|
46
|
+
|
47
|
+
attach_function 'Perl_newHV', [:pointer], :pointer
|
48
|
+
# hv_store()
|
49
|
+
attach_function 'Perl_hv_common_key_len', [:pointer, :pointer, :string, :int32, :int, :pointer, :uint32], :pointer
|
50
|
+
|
51
|
+
attach_variable 'PL_curinterp', :pointer
|
52
|
+
|
53
|
+
#
|
54
|
+
# Returns a reference to internal information on the current
|
55
|
+
# interpreter. Much of the public Perl API is in fact a thin
|
56
|
+
# wrapper over data contained in here. A lot of damage can be
|
57
|
+
# done my manipulating it incorrectly, so be careful.
|
58
|
+
#
|
59
|
+
def curinterp
|
60
|
+
Perl::Internal.new(Perl.PL_curinterp)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Perl
|
2
|
+
class Internal < FFI::Struct
|
3
|
+
class Stat < FFI::Struct
|
4
|
+
size = 144
|
5
|
+
layout :a, :pointer
|
6
|
+
end
|
7
|
+
|
8
|
+
class Tms < FFI::Struct
|
9
|
+
size = 32
|
10
|
+
layout :a, :pointer
|
11
|
+
end
|
12
|
+
|
13
|
+
class Jmpenv < FFI::Struct
|
14
|
+
size = 168
|
15
|
+
layout :a, :pointer
|
16
|
+
end
|
17
|
+
|
18
|
+
layout :Istack_sp, :pointer,
|
19
|
+
:Iopsave, :pointer,
|
20
|
+
:Icurpad, :pointer,
|
21
|
+
:Istack_base, :pointer,
|
22
|
+
:Istack_max, :pointer,
|
23
|
+
:Iscopestack, :pointer,
|
24
|
+
:Iscopestack_ix, :int32,
|
25
|
+
:Iscopestack_max, :int32,
|
26
|
+
:Isavestack, :pointer,
|
27
|
+
:Isavestack_ix, :int32,
|
28
|
+
:Isavestack_max, :int32,
|
29
|
+
:Itmps_stack, :pointer,
|
30
|
+
:Itmps_ix, :int32,
|
31
|
+
:Itmps_floor, :int32,
|
32
|
+
:Itmps_max, :int32,
|
33
|
+
:Imodcount, :int32,
|
34
|
+
:Imarkstack, :pointer,
|
35
|
+
:Imarkstack_ptr, :pointer,
|
36
|
+
:Imarkstack_max, :pointer,
|
37
|
+
:ISv, :pointer,
|
38
|
+
:IXpv, :pointer,
|
39
|
+
:Ina, :int,
|
40
|
+
:Istatbuf, Stat, # FIXME that's just wrong
|
41
|
+
:Istatcache, Stat, 288, # FIXME that's just wrong
|
42
|
+
:Istatgv, :pointer, 432,
|
43
|
+
:Istatname, :pointer,
|
44
|
+
:Itimesbuf, Tms, # FIXME that's just wrong
|
45
|
+
:Icurpm, :pointer, 480,
|
46
|
+
:Irs, :pointer,
|
47
|
+
:Ilast_in_gv, :pointer,
|
48
|
+
:Iofs_sv, :pointer,
|
49
|
+
:Idefoutgv, :pointer,
|
50
|
+
:Ichopset, :string,
|
51
|
+
:Iformtarget, :pointer,
|
52
|
+
:Ibodytarget, :pointer,
|
53
|
+
:Itoptarget, :pointer,
|
54
|
+
:Idefstash, :pointer,
|
55
|
+
:Icurstash, :pointer,
|
56
|
+
:Irestartop, :pointer,
|
57
|
+
:Icurcop, :pointer,
|
58
|
+
:Icurstack, :pointer,
|
59
|
+
:Icurstackinfo, :pointer,
|
60
|
+
:Imainstack, :pointer,
|
61
|
+
:Itop_env, :pointer,
|
62
|
+
:Istart_env, Jmpenv, # FIXME that's just wrong
|
63
|
+
:Ierrors, :pointer, 784,
|
64
|
+
:Ihv_fetch_ent_mh, :pointer,
|
65
|
+
:Ilastgotoprobe, :pointer,
|
66
|
+
:Isortcop, :pointer,
|
67
|
+
:Isortstash, :pointer,
|
68
|
+
:Ifirstgv, :pointer,
|
69
|
+
:Isecondgv, :pointer,
|
70
|
+
# TODO more stuff here
|
71
|
+
:Iexit_flags, :uint8, 1245
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'perl'
|
2
|
+
require 'perl/common'
|
3
|
+
require 'perl/stack'
|
4
|
+
|
5
|
+
module Perl
|
6
|
+
class Interpreter
|
7
|
+
include Perl::Common
|
8
|
+
|
9
|
+
G_SCALAR = 0
|
10
|
+
G_ARRAY = 1
|
11
|
+
G_DISCARD = 2
|
12
|
+
G_EVAL = 4
|
13
|
+
G_NOARGS = 8
|
14
|
+
G_KEEPERR = 16
|
15
|
+
G_NODEBUG = 32
|
16
|
+
G_METHOD = 64
|
17
|
+
G_VOID = 128
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
start
|
21
|
+
end
|
22
|
+
|
23
|
+
def eval(str=nil, &block)
|
24
|
+
return Perl.Perl_eval_pv(Perl.PL_curinterp, str, 1) if str
|
25
|
+
|
26
|
+
if block_given?
|
27
|
+
yield(Statement.new)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
alias_method :run, :eval
|
31
|
+
|
32
|
+
def load(filename)
|
33
|
+
file = File.read(filename)
|
34
|
+
eval(file)
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(method_name, args, return_type)
|
38
|
+
Perl::Stack.function_stack(args) do |stack|
|
39
|
+
rc = do_call(method_name, options_for_call(args, return_type))
|
40
|
+
ret = handle_return(rc, return_type, stack)
|
41
|
+
|
42
|
+
if return_type == :void
|
43
|
+
return nil
|
44
|
+
else
|
45
|
+
if block_given?
|
46
|
+
return yield(ret)
|
47
|
+
else
|
48
|
+
ret.freeze!
|
49
|
+
return ret
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def do_call(method_name, options)
|
57
|
+
method = method_name.is_a?(String) ? :Perl_call_pv : :Perl_call_sv
|
58
|
+
Perl.send(method, Perl.PL_curinterp, method_name, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
# XXX should we have G_DISCARD ?
|
62
|
+
def options_for_call(args, return_type)
|
63
|
+
options = G_EVAL
|
64
|
+
options |= G_NOARGS if args.empty?
|
65
|
+
|
66
|
+
case return_type
|
67
|
+
when :list
|
68
|
+
options |= G_ARRAY
|
69
|
+
when :scalar
|
70
|
+
options |= G_SCALAR
|
71
|
+
when :void
|
72
|
+
options |= G_VOID
|
73
|
+
else
|
74
|
+
raise "Unknown return type #{return_type}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_return(ret, return_type, stack)
|
79
|
+
case return_type
|
80
|
+
when :list
|
81
|
+
when :scalar
|
82
|
+
raise "Unexpected ret=#{ret}" unless ret == 1
|
83
|
+
Perl::Value::Scalar.new(stack.pop_scalar)
|
84
|
+
when :void
|
85
|
+
raise "Unexpected ret=#{ret}" unless ret == 0
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/perl/rack.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'perl'
|
2
|
+
require 'perl/interpreter'
|
3
|
+
|
4
|
+
module Perl
|
5
|
+
class Rack
|
6
|
+
def initialize(filename)
|
7
|
+
@interpreter = Perl::Interpreter.new
|
8
|
+
@app = @interpreter.load(filename)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@interpreter.call(@app, {:ref => clean_env(env)}, :scalar) do |ret|
|
13
|
+
value = ret.deref.value # Array
|
14
|
+
|
15
|
+
status = value[0].value
|
16
|
+
|
17
|
+
v1 = value[1].deref.value
|
18
|
+
headers = Hash[*v1.map { |v| v.value }]
|
19
|
+
|
20
|
+
body = value[2].deref.value.map { |v| v.value }
|
21
|
+
|
22
|
+
[status, headers, body]
|
23
|
+
end
|
24
|
+
rescue => e
|
25
|
+
puts "e: #{e.inspect}\n#{e.backtrace.join("\n")}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def clean_env(hash)
|
29
|
+
ret = hash.dup
|
30
|
+
if defined?(PhusionPassenger)
|
31
|
+
ret = ret.reject do |k,v|
|
32
|
+
v.is_a?(PhusionPassenger::Utils::RewindableInput)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
ret.tap do |h|
|
36
|
+
["async.close"].each do |k|
|
37
|
+
if h.has_key?(k)
|
38
|
+
puts "Cannot handle env['#{k}'] (#{k} => #{h[k].inspect}), skipping"
|
39
|
+
h.delete("async.close")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/perl/shell.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'perl'
|
2
|
+
require 'perl/common'
|
3
|
+
|
4
|
+
module Perl
|
5
|
+
class Shell
|
6
|
+
include Perl::Common
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def run
|
10
|
+
new.run
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
Perl.setup
|
16
|
+
|
17
|
+
@my_perl = Perl.perl_alloc
|
18
|
+
Perl.perl_construct(@my_perl)
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
argc, argv = argv_to_ffi
|
23
|
+
|
24
|
+
Perl.perl_parse(@my_perl, nil, argc, argv, nil)
|
25
|
+
Perl.perl_run(@my_perl)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/perl/stack.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
module Perl; class Stack; end; end
|
2
|
+
|
3
|
+
require 'perl/stack/function'
|
4
|
+
|
5
|
+
module Perl
|
6
|
+
class Stack
|
7
|
+
class << self
|
8
|
+
def function_stack(args, &block)
|
9
|
+
Function.new(self.new, args, &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@sp = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def dSP
|
18
|
+
@sp = Perl.curinterp[:Istack_sp].tap { |sp| trace("dSP: @sp=#{sp}") }
|
19
|
+
end
|
20
|
+
alias_method :spagain, :dSP
|
21
|
+
|
22
|
+
def enter
|
23
|
+
Perl.Perl_push_scope(Perl.PL_curinterp)
|
24
|
+
end
|
25
|
+
|
26
|
+
def savetmps
|
27
|
+
curinterp = Perl.curinterp
|
28
|
+
trace("savetmps: curinterp=#{curinterp.to_ptr.inspect}")
|
29
|
+
|
30
|
+
trace("savetmps: tmps_floor=#{curinterp[:Itmps_floor].inspect}")
|
31
|
+
addr = Perl.PL_curinterp + curinterp.offset_of(:Itmps_floor)
|
32
|
+
trace("addr=#{addr.inspect}")
|
33
|
+
|
34
|
+
Perl.Perl_save_int(Perl.PL_curinterp, addr)
|
35
|
+
trace("savetmps: tmps_floor now #{curinterp[:Itmps_floor].inspect}")
|
36
|
+
|
37
|
+
trace("curinterp[:Itmps_ix] was #{curinterp[:Itmps_ix].inspect}")
|
38
|
+
curinterp[:Itmps_floor] = curinterp[:Itmps_ix]
|
39
|
+
trace("savetmps: tmps_floor now #{curinterp[:Itmps_floor].inspect}")
|
40
|
+
end
|
41
|
+
|
42
|
+
def pushmark
|
43
|
+
trace("pushmark: @sp=#{@sp}")
|
44
|
+
|
45
|
+
curinterp = Perl.curinterp
|
46
|
+
trace("curinterp[:Imarkstack_ptr] was #{curinterp[:Imarkstack_ptr].inspect} == #{curinterp[:Imarkstack_max].inspect}")
|
47
|
+
curinterp[:Imarkstack_ptr] += FFI.type_size(:int32)
|
48
|
+
trace("curinterp[:Imarkstack_ptr] now #{curinterp[:Imarkstack_ptr].inspect}")
|
49
|
+
if curinterp[:Imarkstack_ptr] == curinterp[:Imarkstack_max]
|
50
|
+
Perl.Perl_markstack_grow(Perl.PL_curinterp)
|
51
|
+
end
|
52
|
+
trace("curinterp[:Imarkstack_ptr] <= #{@sp.address - curinterp[:Istack_base].address}")
|
53
|
+
curinterp[:Imarkstack_ptr].put_pointer(0, @sp.address - curinterp[:Istack_base].address)
|
54
|
+
trace("curinterp[:Imarkstack_ptr] now #{curinterp[:Imarkstack_ptr].inspect}")
|
55
|
+
end
|
56
|
+
|
57
|
+
def push(sv)
|
58
|
+
trace("push: @sp=#{@sp}")
|
59
|
+
@sp += FFI.type_size(:pointer)
|
60
|
+
@sp.put_pointer(0, sv)
|
61
|
+
trace("push: is now @sp=#{@sp}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def putback
|
65
|
+
trace("putback: @sp=#{@sp}")
|
66
|
+
|
67
|
+
curinterp = Perl.curinterp
|
68
|
+
curinterp[:Istack_sp] = @sp
|
69
|
+
end
|
70
|
+
|
71
|
+
def pops
|
72
|
+
trace("pops: @sp=#{@sp}")
|
73
|
+
|
74
|
+
ret = @sp.get_pointer(0)
|
75
|
+
@sp = FFI::Pointer.new(@sp.address - FFI.type_size(:pointer))
|
76
|
+
trace("pops: now @sp=#{@sp}")
|
77
|
+
ret
|
78
|
+
end
|
79
|
+
|
80
|
+
def freetmps
|
81
|
+
curinterp = Perl.curinterp
|
82
|
+
trace("#{curinterp[:Itmps_ix].inspect} > #{curinterp[:Itmps_floor].inspect}")
|
83
|
+
|
84
|
+
if curinterp[:Itmps_ix] > curinterp[:Itmps_floor]
|
85
|
+
Perl.Perl_free_tmps(Perl.PL_curinterp)
|
86
|
+
trace("#{curinterp[:Itmps_ix].inspect} > #{curinterp[:Itmps_floor].inspect}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def leave
|
91
|
+
Perl.Perl_pop_scope(Perl.PL_curinterp)
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
def trace(msg)
|
96
|
+
$stderr.puts(msg) if false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Perl::Stack::Function
|
2
|
+
def initialize(stack, args, &block)
|
3
|
+
@stack = stack
|
4
|
+
@stack.dSP
|
5
|
+
@stack.enter
|
6
|
+
@stack.savetmps
|
7
|
+
|
8
|
+
case args
|
9
|
+
when nil
|
10
|
+
# nothing
|
11
|
+
when Hash
|
12
|
+
init_with_hash(args)
|
13
|
+
else
|
14
|
+
init_with_array(Array(args))
|
15
|
+
end
|
16
|
+
|
17
|
+
yield(self)
|
18
|
+
ensure
|
19
|
+
if @stack
|
20
|
+
@stack.freetmps
|
21
|
+
@stack.leave
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def push(value)
|
26
|
+
value = Perl.Perl_sv_2mortal(Perl.PL_curinterp, value)
|
27
|
+
@stack.push(value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def pop_scalar
|
31
|
+
@stack.spagain
|
32
|
+
return @stack.pops
|
33
|
+
ensure
|
34
|
+
@stack.putback
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def init_with_hash(args)
|
39
|
+
return unless args.any?
|
40
|
+
|
41
|
+
begin
|
42
|
+
@stack.pushmark
|
43
|
+
args.each_pair do |type, arg|
|
44
|
+
value = arg.to_perl
|
45
|
+
value = Perl.Perl_newRV_noinc(Perl.PL_curinterp, value) if type == :ref
|
46
|
+
push(value)
|
47
|
+
end
|
48
|
+
ensure
|
49
|
+
@stack.putback
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def init_with_array(args)
|
54
|
+
return unless args.any?
|
55
|
+
|
56
|
+
args = Array(args)
|
57
|
+
begin
|
58
|
+
@stack.pushmark
|
59
|
+
args.each do |arg|
|
60
|
+
value = arg.to_perl
|
61
|
+
push(value)
|
62
|
+
end
|
63
|
+
ensure
|
64
|
+
@stack.putback
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|