therubyracer-freebsd 0.10.1
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/.gitignore +13 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/.yardopts +1 -0
- data/Changelog.md +231 -0
- data/Gemfile +3 -0
- data/README.md +167 -0
- data/Rakefile +26 -0
- data/bin/therubyracer +11 -0
- data/ext/v8/extconf.rb +26 -0
- data/ext/v8/rr.cpp +189 -0
- data/ext/v8/rr.h +41 -0
- data/ext/v8/v8.cpp +48 -0
- data/ext/v8/v8_array.cpp +48 -0
- data/ext/v8/v8_array.h +8 -0
- data/ext/v8/v8_callbacks.cpp +81 -0
- data/ext/v8/v8_callbacks.h +8 -0
- data/ext/v8/v8_context.cpp +92 -0
- data/ext/v8/v8_context.h +6 -0
- data/ext/v8/v8_date.cpp +40 -0
- data/ext/v8/v8_date.h +6 -0
- data/ext/v8/v8_debug.cpp +17 -0
- data/ext/v8/v8_debug.h +6 -0
- data/ext/v8/v8_exception.cpp +133 -0
- data/ext/v8/v8_exception.h +11 -0
- data/ext/v8/v8_external.cpp +70 -0
- data/ext/v8/v8_external.h +8 -0
- data/ext/v8/v8_function.cpp +69 -0
- data/ext/v8/v8_function.h +11 -0
- data/ext/v8/v8_handle.cpp +186 -0
- data/ext/v8/v8_handle.h +48 -0
- data/ext/v8/v8_locker.cpp +139 -0
- data/ext/v8/v8_locker.h +6 -0
- data/ext/v8/v8_message.cpp +67 -0
- data/ext/v8/v8_message.h +10 -0
- data/ext/v8/v8_object.cpp +122 -0
- data/ext/v8/v8_object.h +10 -0
- data/ext/v8/v8_script.cpp +36 -0
- data/ext/v8/v8_script.h +8 -0
- data/ext/v8/v8_string.cpp +52 -0
- data/ext/v8/v8_string.h +9 -0
- data/ext/v8/v8_template.cpp +344 -0
- data/ext/v8/v8_template.h +8 -0
- data/ext/v8/v8_try_catch.cpp +70 -0
- data/ext/v8/v8_try_catch.h +5 -0
- data/ext/v8/v8_v8.cpp +34 -0
- data/ext/v8/v8_v8.h +6 -0
- data/ext/v8/v8_value.cpp +175 -0
- data/ext/v8/v8_value.h +10 -0
- data/ext/v8/v8_weakref.cpp +61 -0
- data/ext/v8/v8_weakref.h +29 -0
- data/lib/v8.rb +23 -0
- data/lib/v8/access.rb +92 -0
- data/lib/v8/array.rb +17 -0
- data/lib/v8/c/locker.rb +18 -0
- data/lib/v8/cli.rb +133 -0
- data/lib/v8/context.rb +111 -0
- data/lib/v8/error.rb +130 -0
- data/lib/v8/function.rb +44 -0
- data/lib/v8/object.rb +69 -0
- data/lib/v8/portal.rb +86 -0
- data/lib/v8/portal/caller.rb +37 -0
- data/lib/v8/portal/constructor.rb +98 -0
- data/lib/v8/portal/function.rb +63 -0
- data/lib/v8/portal/interceptors.rb +152 -0
- data/lib/v8/portal/proxies.rb +151 -0
- data/lib/v8/portal/templates.rb +73 -0
- data/lib/v8/stack.rb +66 -0
- data/lib/v8/tap.rb +9 -0
- data/lib/v8/version.rb +3 -0
- data/spec/ext/array_spec.rb +15 -0
- data/spec/ext/cxt_spec.rb +57 -0
- data/spec/ext/ext_spec_helper.rb +27 -0
- data/spec/ext/func_spec.rb +64 -0
- data/spec/ext/object_spec.rb +10 -0
- data/spec/ext/string_spec.rb +11 -0
- data/spec/ext/try_catch_spec.rb +60 -0
- data/spec/redjs_spec.rb +9 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/v8/error_spec.rb +131 -0
- data/spec/v8/portal/proxies_spec.rb +106 -0
- data/specmem/handle_memspec.rb +41 -0
- data/specmem/object_memspec.rb +14 -0
- data/specmem/proxies_memspec.rb +49 -0
- data/specmem/spec_helper.rb +24 -0
- data/specthread/spec_helper.rb +2 -0
- data/specthread/threading_spec.rb +13 -0
- data/thefrontside.png +0 -0
- data/therubyracer.gemspec +27 -0
- metadata +183 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
module V8
|
2
|
+
class Portal
|
3
|
+
class Proxies
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@js_proxies_rb2js = {}
|
7
|
+
@js_proxies_js2rb = {}
|
8
|
+
@rb_proxies_rb2js = {}
|
9
|
+
@rb_proxies_js2rb = {}
|
10
|
+
@clear_js_proxy = ClearJSProxy.new(@js_proxies_rb2js, @js_proxies_js2rb)
|
11
|
+
@clear_rb_proxy = ClearRubyProxy.new(@rb_proxies_rb2js, @rb_proxies_js2rb)
|
12
|
+
end
|
13
|
+
|
14
|
+
def js2rb(js)
|
15
|
+
if rb = js_proxy_2_rb_object(js)
|
16
|
+
return rb
|
17
|
+
elsif rb = js_object_2_rb_proxy(js)
|
18
|
+
return rb
|
19
|
+
else
|
20
|
+
proxy = block_given? ? yield(js) : Object.new
|
21
|
+
register_ruby_proxy proxy, :for => js if proxy && js && js.kind_of?(V8::C::Handle)
|
22
|
+
return proxy
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def rb2js(rb)
|
27
|
+
if js = rb_proxy_2_js_object(rb)
|
28
|
+
return js
|
29
|
+
elsif js = rb_object_2_js_proxy(rb)
|
30
|
+
return js
|
31
|
+
else
|
32
|
+
proxy = block_given? ? yield(rb) : V8::C::Object::New()
|
33
|
+
register_javascript_proxy proxy, :for => rb unless @js_proxies_rb2js[rb]
|
34
|
+
return proxy
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def register_javascript_proxy(proxy, options = {})
|
39
|
+
target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param"
|
40
|
+
fail ArgumentError, "javascript proxy must be a V8::C::Handle, not #{proxy.class}" unless proxy.kind_of?(V8::C::Handle)
|
41
|
+
fail DoubleProxyError, "target already has a registered proxy" if @js_proxies_rb2js[target]
|
42
|
+
|
43
|
+
@js_proxies_js2rb[proxy] = target
|
44
|
+
@js_proxies_rb2js[target] = proxy
|
45
|
+
proxy.MakeWeak(nil, @clear_js_proxy)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Lookup the JavaScript proxy for a natively Ruby object
|
49
|
+
# @param [Object] object the Ruby object
|
50
|
+
# @return [V8::C::Handle] the JavaScript proxy representing `object`
|
51
|
+
def rb_object_2_js_proxy(object)
|
52
|
+
@js_proxies_rb2js[object]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Look up a natively Ruby object given its JavaScript proxy
|
56
|
+
# @param [V8::C::Handle] proxy the JavaScript proxy
|
57
|
+
# @return [Object] the Ruby object represented by `proxy`
|
58
|
+
def js_proxy_2_rb_object(proxy)
|
59
|
+
@js_proxies_js2rb[proxy]
|
60
|
+
end
|
61
|
+
|
62
|
+
def register_ruby_proxy(proxy, options = {})
|
63
|
+
target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param"
|
64
|
+
fail ArgumentError, "'#{proxy.inspect}' is not a Handle to an actual V8 object" unless target.kind_of?(V8::C::Handle)
|
65
|
+
@rb_proxies_rb2js[proxy.object_id] = target
|
66
|
+
@rb_proxies_js2rb[target] = proxy.object_id
|
67
|
+
ObjectSpace.define_finalizer(proxy, @clear_rb_proxy)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Looks up the Ruby proxy for an object that is natively JavaScript
|
71
|
+
# @param [V8::C::Handle] object the JavaScript whose proxy we want
|
72
|
+
# @return [Object] the Ruby proxy for `object`
|
73
|
+
def js_object_2_rb_proxy(object)
|
74
|
+
if id = @rb_proxies_js2rb[object]
|
75
|
+
ObjectSpace._id2ref id
|
76
|
+
end
|
77
|
+
rescue RangeError
|
78
|
+
# sometimes, the Ruby proxy has been garbage collected, but
|
79
|
+
# the finalizer which runs has not been called. That's OK
|
80
|
+
# we just clear out the entry, and return nil so that a new
|
81
|
+
# proxy can be created.
|
82
|
+
@clear_rb_proxy.call(id)
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# Looks up a native JavaScript object by its Ruby proxy
|
87
|
+
# @param [Object] proxy the Ruby proxy
|
88
|
+
# @return [V8::C::Handle] the native JavaScript object
|
89
|
+
def rb_proxy_2_js_object(proxy)
|
90
|
+
@rb_proxies_rb2js[proxy.object_id]
|
91
|
+
end
|
92
|
+
|
93
|
+
def js_empty?
|
94
|
+
@js_proxies_rb2js.empty? && @js_proxies_js2rb.empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
def rb_empty?
|
98
|
+
@rb_proxies_rb2js.empty? && @rb_proxies_js2rb.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
def empty?
|
102
|
+
js_empty? && rb_empty?
|
103
|
+
end
|
104
|
+
DoubleProxyError = Class.new(StandardError)
|
105
|
+
|
106
|
+
class ClearJSProxy
|
107
|
+
|
108
|
+
def initialize(rb2js, js2rb)
|
109
|
+
@rb2js, @js2rb = rb2js, js2rb
|
110
|
+
end
|
111
|
+
|
112
|
+
def call(proxy, parameter)
|
113
|
+
rb = @js2rb[proxy]
|
114
|
+
@js2rb.delete(proxy)
|
115
|
+
@rb2js.delete(rb)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# @Private
|
120
|
+
# Remove the linkage between a Ruby proxy and a native
|
121
|
+
# JavaScript object. In general, this object is registered
|
122
|
+
# as a finalizer on the Ruby proxy itself, so that when it is
|
123
|
+
# garbage collected, it releases the back reference to the
|
124
|
+
# native JavaScript object.
|
125
|
+
#
|
126
|
+
# It is important to do this as soon as is reasonably possible
|
127
|
+
# so that the native JavaScript object can itself be garbage
|
128
|
+
# collected (provided there are no other references to it)
|
129
|
+
class ClearRubyProxy
|
130
|
+
def initialize(rb2js, js2rb)
|
131
|
+
@rb2js, @js2rb = rb2js, js2rb
|
132
|
+
end
|
133
|
+
|
134
|
+
# takes the object id of a Ruby proxy that has been garbage collected
|
135
|
+
# and releases the reference to the native JavaScript object that
|
136
|
+
# it was bound to.
|
137
|
+
# @param[Fixnum] proxy_id the proxy id of the garbage collected Ruby proxy
|
138
|
+
def call(proxy_id)
|
139
|
+
# TODO: this if-check should be synchronized, so that if called manually
|
140
|
+
# it will not conflict with the finalization thread. It's not so heinous
|
141
|
+
# if the refererence gets cleared twice, but we definiteily dont't want
|
142
|
+
# to double-decrement the v8 GC hint.
|
143
|
+
if js = @rb2js[proxy_id]
|
144
|
+
@rb2js.delete(proxy_id)
|
145
|
+
@js2rb.delete(js)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
module V8
|
3
|
+
class Portal
|
4
|
+
class Templates
|
5
|
+
|
6
|
+
attr_reader :portal
|
7
|
+
|
8
|
+
def initialize(portal)
|
9
|
+
@portal = portal
|
10
|
+
@constructors = {}
|
11
|
+
@methods = {}
|
12
|
+
@procs = {}
|
13
|
+
@releases = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_constructor(ruby_class)
|
17
|
+
class_id = ruby_class.object_id
|
18
|
+
if constructor = @constructors[class_id]
|
19
|
+
return constructor
|
20
|
+
else
|
21
|
+
constructor = @constructors[class_id] = ConstructorAdapter.new(self, class_id)
|
22
|
+
ObjectSpace.define_finalizer(ruby_class, release(@constructors, class_id))
|
23
|
+
return constructor
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_function(code)
|
28
|
+
case code
|
29
|
+
when Method, UnboundMethod
|
30
|
+
if fn = @methods[code.to_s]
|
31
|
+
return fn
|
32
|
+
else
|
33
|
+
function = @methods[code.to_s] = FunctionAdapter.new(@portal, code)
|
34
|
+
#TODO: test this weak behavior
|
35
|
+
function.template.MakeWeak(0, release(@methods, code.to_s))
|
36
|
+
return function
|
37
|
+
end
|
38
|
+
else
|
39
|
+
if fn = @procs[code]
|
40
|
+
return fn
|
41
|
+
else
|
42
|
+
function = @procs[code] = FunctionAdapter.new(@portal, code)
|
43
|
+
#TODO: test this weak behavior
|
44
|
+
function.template.MakeWeak(0, release(@procs, code))
|
45
|
+
return function
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def proxies
|
51
|
+
@portal.proxies
|
52
|
+
end
|
53
|
+
|
54
|
+
def release(refs, id)
|
55
|
+
release = Release.new(@releases, refs, id)
|
56
|
+
@releases[release] = true
|
57
|
+
return release
|
58
|
+
end
|
59
|
+
|
60
|
+
class Release
|
61
|
+
def initialize(releases, refs, id)
|
62
|
+
@releases, @refs, @id = releases, refs, id
|
63
|
+
end
|
64
|
+
|
65
|
+
def call(*args)
|
66
|
+
@refs.delete(@id)
|
67
|
+
@releases.delete(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/v8/stack.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
module V8
|
3
|
+
|
4
|
+
class StackTrace
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(to, native)
|
8
|
+
@to = to
|
9
|
+
@native = native
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
@native.GetFrameCount()
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
for i in 0..length - 1
|
18
|
+
yield V8::StackFrame.new(@to, @native.GetFrame(i))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
map {|f|"at #{f}"}.join("\n")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class StackFrame
|
28
|
+
|
29
|
+
def initialize(portal, native)
|
30
|
+
@to = portal
|
31
|
+
@native = native
|
32
|
+
end
|
33
|
+
|
34
|
+
def script_name
|
35
|
+
@to.rb(@native.GetScriptName())
|
36
|
+
end
|
37
|
+
|
38
|
+
def function_name
|
39
|
+
@to.rb(@native.GetFunctionName())
|
40
|
+
end
|
41
|
+
|
42
|
+
def line_number
|
43
|
+
@native.GetLineNumber()
|
44
|
+
end
|
45
|
+
|
46
|
+
def column
|
47
|
+
@native.GetColumn()
|
48
|
+
end
|
49
|
+
|
50
|
+
def eval?
|
51
|
+
@native.IsEval()
|
52
|
+
end
|
53
|
+
|
54
|
+
def constructor?
|
55
|
+
@native.IsConstructor()
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
if @native.GetFunctionName()
|
60
|
+
"#{function_name} (#{script_name}:#{line_number}:#{column})"
|
61
|
+
else
|
62
|
+
"#{script_name}:#{line_number}:#{column}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/v8/tap.rb
ADDED
data/lib/v8/version.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe V8::C::Array do
|
4
|
+
include V8::ExtSpec
|
5
|
+
|
6
|
+
it "can be instantiated" do
|
7
|
+
a = c::Array::New()
|
8
|
+
a.Length().should eql(0)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "maintains referential integrity" do
|
12
|
+
v8_eval('a = []')
|
13
|
+
v8_eval('a').should be(v8_eval('a'))
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
require "#{File.dirname(__FILE__)}/../spec_helper.rb"
|
3
|
+
|
4
|
+
include V8
|
5
|
+
|
6
|
+
describe C::Context do
|
7
|
+
|
8
|
+
before {@lock = C::Locker.new}
|
9
|
+
after {@lock.delete}
|
10
|
+
|
11
|
+
it "should not have a current context if no context is open" do
|
12
|
+
C::Context::GetEntered().should be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can javascript properties on the global scope via ruby when the default scope is a ruby object" do
|
16
|
+
V8::Context.new(:with => Object.new) do |cxt|
|
17
|
+
cxt['foo'] = 'bar'
|
18
|
+
cxt.eval('foo').should eql('bar')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can get the current javascript execution stack" do
|
23
|
+
V8::Context.new do |cxt|
|
24
|
+
trace = nil
|
25
|
+
cxt['getTrace'] = lambda do
|
26
|
+
trace = V8::Context.stack
|
27
|
+
end
|
28
|
+
cxt.eval(<<-JS, 'trace.js')
|
29
|
+
function one() {
|
30
|
+
return two();
|
31
|
+
}
|
32
|
+
|
33
|
+
function two() {
|
34
|
+
return three();
|
35
|
+
}
|
36
|
+
|
37
|
+
function three() {
|
38
|
+
return getTrace()
|
39
|
+
}
|
40
|
+
one();
|
41
|
+
JS
|
42
|
+
trace.length.should be(4)
|
43
|
+
trace.to_a[0].tap do |frame|
|
44
|
+
frame.line_number.should == 10
|
45
|
+
frame.column.should == 16
|
46
|
+
frame.script_name.should == 'trace.js'
|
47
|
+
frame.function_name.should == 'three'
|
48
|
+
frame.should_not be_eval
|
49
|
+
frame.should_not be_constructor
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "has an empty stack if there is no enterned context" do
|
55
|
+
V8::Context.stack.should be_empty
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
module V8::ExtSpec
|
3
|
+
|
4
|
+
def self.included(object)
|
5
|
+
object.class_eval do
|
6
|
+
before do
|
7
|
+
@lock = c::Locker.new
|
8
|
+
@cxt = c::Context::New()
|
9
|
+
@cxt.Enter()
|
10
|
+
end
|
11
|
+
after do
|
12
|
+
@cxt.Exit()
|
13
|
+
@cxt.Dispose()
|
14
|
+
@lock.delete
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def v8_eval(script, sourcename = "<eval>")
|
20
|
+
c::Script::New(c::String::New(script), c::String::New(sourcename)).Run()
|
21
|
+
end
|
22
|
+
|
23
|
+
def c
|
24
|
+
V8::C
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../spec_helper.rb"
|
2
|
+
|
3
|
+
include V8
|
4
|
+
|
5
|
+
describe C::Function do
|
6
|
+
it "is callable" do
|
7
|
+
Context.new do |cxt|
|
8
|
+
f = cxt.eval('(function() {return "Hello World"})', '<eval>');
|
9
|
+
f.call().should == "Hello World"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "receives proper argument length from ruby" do
|
14
|
+
Context.new do |cxt|
|
15
|
+
f = cxt.eval('(function() {return arguments.length})', 'eval')
|
16
|
+
f.call(1, 2, 3).should == 3
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "maps all arguments from ruby" do
|
21
|
+
Context.new do |cxt|
|
22
|
+
f = cxt.eval('(function(one, two, three) {return one + two + three})', 'eval')
|
23
|
+
f.call(1,2,3).should == 6
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "properly maps ruby objects back and forth from arguments to return value" do
|
28
|
+
Context.new do |cxt|
|
29
|
+
Object.new.tap do |this|
|
30
|
+
f = cxt.eval('(function() {return this})', 'eval')
|
31
|
+
f.methodcall(this).should be(this)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can be called outside of a context" do
|
37
|
+
Context.new do |cxt|
|
38
|
+
@f = cxt.eval('(function() {return "Call Me"})', 'eval')
|
39
|
+
end
|
40
|
+
@f.call().should == "Call Me"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "is reflected properly" do
|
44
|
+
Context.new do |cxt|
|
45
|
+
cxt['say'] = lambda {|this, word, times| word * times}
|
46
|
+
cxt.eval('say("Hello", 3)').should == "HelloHelloHello"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "has a name" do
|
51
|
+
Context.new do |cxt|
|
52
|
+
f = cxt.eval('(function hi() {return "Hello World"})', '<eval>')
|
53
|
+
f.name.should == "hi"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "can have its name set" do
|
58
|
+
Context.new do |cxt|
|
59
|
+
f = cxt.eval('(function () {return "Goodbye World"})', '<eval>')
|
60
|
+
f.name = 'bye'
|
61
|
+
f.name.should == 'bye'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|