therubyracer-freebsd 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|