therubyracer 0.7.5 → 0.8.0.pre
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of therubyracer might be problematic. Click here for more details.
- data/History.txt +2 -2
- data/README.rdoc +1 -1
- data/Rakefile +4 -2
- data/ext/v8/rr.cpp +9 -2
- data/ext/v8/rr.h +1 -0
- data/ext/v8/v8.cpp +2 -0
- data/ext/v8/v8_cxt.cpp +11 -0
- data/ext/v8/v8_locker.cpp +41 -0
- data/ext/v8/v8_locker.h +6 -0
- data/ext/v8/v8_obj.cpp +12 -0
- data/ext/v8/v8_script.cpp +2 -0
- data/ext/v8/v8_template.cpp +17 -7
- data/lib/v8.rb +3 -2
- data/lib/v8/access.rb +53 -168
- data/lib/v8/array.rb +2 -2
- data/lib/v8/context.rb +9 -7
- data/lib/v8/error.rb +5 -4
- data/lib/v8/function.rb +7 -34
- data/lib/v8/object.rb +10 -12
- data/lib/v8/portal.rb +325 -0
- data/lib/v8/portal/functions.rb +45 -0
- data/spec/redjs/jsapi_spec.rb +154 -50
- data/therubyracer.gemspec +4 -4
- metadata +17 -13
- data/ext/v8/upstream/2.3.3/tools/v8.xcodeproj/project.pbxproj +0 -1855
- data/lib/v8/to.rb +0 -82
- data/spec/v8/to_spec.rb +0 -14
data/lib/v8/array.rb
CHANGED
data/lib/v8/context.rb
CHANGED
@@ -2,12 +2,14 @@ require 'stringio'
|
|
2
2
|
|
3
3
|
module V8
|
4
4
|
class Context
|
5
|
-
attr_reader :native, :scope
|
5
|
+
attr_reader :native, :scope, :access
|
6
6
|
def initialize(opts = {})
|
7
|
-
@
|
7
|
+
@access = Access.new
|
8
|
+
@to = Portal.new(self, @access)
|
9
|
+
@native = opts[:with] ? C::Context::New(@to.rubytemplate) : C::Context::New()
|
8
10
|
@native.enter do
|
11
|
+
@scope = @to.rb(@native.Global())
|
9
12
|
@native.Global().SetHiddenValue(C::String::New("TheRubyRacer::RubyObject"), C::External::New(opts[:with])) if opts[:with]
|
10
|
-
@scope = To.rb(@native.Global())
|
11
13
|
end
|
12
14
|
yield(self) if block_given?
|
13
15
|
end
|
@@ -20,15 +22,15 @@ module V8
|
|
20
22
|
value = nil
|
21
23
|
C::TryCatch.try do |try|
|
22
24
|
@native.enter do
|
23
|
-
script = C::Script::Compile(
|
25
|
+
script = C::Script::Compile(@to.v8(javascript.to_s), @to.v8(filename.to_s))
|
24
26
|
if try.HasCaught()
|
25
|
-
err = JSError.new(try)
|
27
|
+
err = JSError.new(try, @to)
|
26
28
|
else
|
27
29
|
result = script.Run()
|
28
30
|
if try.HasCaught()
|
29
|
-
err = JSError.new(try)
|
31
|
+
err = JSError.new(try, @to)
|
30
32
|
else
|
31
|
-
value =
|
33
|
+
value = @to.rb(result)
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
data/lib/v8/error.rb
CHANGED
@@ -3,7 +3,8 @@ module V8
|
|
3
3
|
class JSError < StandardError
|
4
4
|
attr_reader :value, :boundaries
|
5
5
|
|
6
|
-
def initialize(try)
|
6
|
+
def initialize(try, to)
|
7
|
+
@to = to
|
7
8
|
begin
|
8
9
|
super(initialize_unsafe(try))
|
9
10
|
rescue Exception => e
|
@@ -15,7 +16,7 @@ module V8
|
|
15
16
|
|
16
17
|
def initialize_unsafe(try)
|
17
18
|
message = nil
|
18
|
-
ex =
|
19
|
+
ex = @to.rb(try.Exception())
|
19
20
|
@boundaries = [Boundary.new(:rbframes => caller(3), :jsframes => parse_js_frames(try))]
|
20
21
|
if V8::Object === ex
|
21
22
|
if msg = ex['message']
|
@@ -79,7 +80,7 @@ module V8
|
|
79
80
|
end if trace_javascript
|
80
81
|
rbcontext = b.rbframes
|
81
82
|
jscontext = b.jsframes
|
82
|
-
rbframes.reject! {|f| f =~ /lib\/v8
|
83
|
+
rbframes.reject! {|f| f =~ /lib\/v8\/.*\.rb/} unless trace_framework
|
83
84
|
mixed.unshift(*rbframes) if trace_ruby
|
84
85
|
mixed.unshift(*jsframes) if trace_javascript
|
85
86
|
end
|
@@ -87,7 +88,7 @@ module V8
|
|
87
88
|
end
|
88
89
|
|
89
90
|
def parse_js_frames(try)
|
90
|
-
raw =
|
91
|
+
raw = @to.rb(try.StackTrace())
|
91
92
|
if raw && !raw.empty?
|
92
93
|
raw.split("\n")[1..-1].tap do |frames|
|
93
94
|
frames.each {|frame| frame.strip!.chomp!(",")}
|
data/lib/v8/function.rb
CHANGED
@@ -5,10 +5,10 @@ module V8
|
|
5
5
|
err = nil
|
6
6
|
return_value = nil
|
7
7
|
C::TryCatch.try do |try|
|
8
|
-
@
|
9
|
-
this =
|
10
|
-
return_value =
|
11
|
-
err = JSError.new(try) if try.HasCaught()
|
8
|
+
@portal.open do |to|
|
9
|
+
this = to.v8(thisObject)
|
10
|
+
return_value = to.rb(@native.Call(this, to.v8(args)))
|
11
|
+
err = JSError.new(try, to) if try.HasCaught()
|
12
12
|
end
|
13
13
|
end
|
14
14
|
raise err if err
|
@@ -16,39 +16,12 @@ module V8
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def call(*args)
|
19
|
-
self.methodcall(@context.Global(), *args)
|
19
|
+
self.methodcall(@portal.context.native.Global(), *args)
|
20
20
|
end
|
21
21
|
|
22
22
|
def new(*args)
|
23
|
-
@
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.rubyprotect
|
29
|
-
begin
|
30
|
-
To.v8(yield)
|
31
|
-
rescue Exception => e
|
32
|
-
case e
|
33
|
-
when SystemExit, NoMemoryError
|
34
|
-
raise e
|
35
|
-
else
|
36
|
-
error = V8::C::Exception::Error(V8::C::String::New(e.message))
|
37
|
-
error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
|
38
|
-
V8::C::ThrowException(error)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.rubycall(rubycode, *args)
|
44
|
-
rubyprotect do
|
45
|
-
rubycode.call(*args)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.rubysend(obj, message, *args)
|
50
|
-
rubyprotect do
|
51
|
-
obj.send(message, *args)
|
23
|
+
@portal.open do |to|
|
24
|
+
to.rb(@native.NewInstance(to.v8(args)))
|
52
25
|
end
|
53
26
|
end
|
54
27
|
end
|
data/lib/v8/object.rb
CHANGED
@@ -3,35 +3,33 @@ module V8
|
|
3
3
|
class Object
|
4
4
|
include Enumerable
|
5
5
|
|
6
|
-
def initialize(native,
|
7
|
-
@native = native
|
8
|
-
@context = context || C::Context::GetEntered()
|
9
|
-
raise ScriptError, "V8::Object.new called without an open V8 context" unless @context
|
6
|
+
def initialize(native, portal)
|
7
|
+
@native, @portal = native, portal
|
10
8
|
end
|
11
9
|
|
12
10
|
def [](key)
|
13
|
-
@
|
14
|
-
|
11
|
+
@portal.open do |to|
|
12
|
+
to.rb(@native.Get(to.v8(key)))
|
15
13
|
end
|
16
14
|
end
|
17
15
|
|
18
16
|
def []=(key, value)
|
19
17
|
value.tap do
|
20
|
-
@
|
21
|
-
@native.Set(
|
18
|
+
@portal.open do |to|
|
19
|
+
@native.Set(to.v8(key), to.v8(value))
|
22
20
|
end
|
23
21
|
end
|
24
22
|
end
|
25
23
|
|
26
24
|
def to_s
|
27
|
-
@
|
28
|
-
|
25
|
+
@portal.open do |to|
|
26
|
+
to.rb(@native.ToString())
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
30
|
def each
|
33
|
-
@
|
34
|
-
for prop in
|
31
|
+
@portal.open do |to|
|
32
|
+
for prop in to.rb(@native.GetPropertyNames())
|
35
33
|
yield prop, self[prop]
|
36
34
|
end
|
37
35
|
end
|
data/lib/v8/portal.rb
ADDED
@@ -0,0 +1,325 @@
|
|
1
|
+
|
2
|
+
module V8
|
3
|
+
class Portal
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def initialize(context, access)
|
7
|
+
@context, @access = context, access
|
8
|
+
@named_property_getter = Interceptor(NamedPropertyGetter)
|
9
|
+
@named_property_setter = Interceptor(NamedPropertySetter)
|
10
|
+
@named_property_query = nil
|
11
|
+
@named_property_deleter = nil
|
12
|
+
@named_property_enumerator = Interceptor(NamedPropertyEnumerator)
|
13
|
+
|
14
|
+
@indexed_property_getter = Interceptor(IndexedPropertyGetter)
|
15
|
+
@indexed_property_setter = Interceptor(IndexedPropertySetter)
|
16
|
+
@indexed_property_query = nil
|
17
|
+
@indexed_property_deleter = nil
|
18
|
+
@indexed_property_enumerator = Interceptor(IndexedPropertyEnumerator)
|
19
|
+
|
20
|
+
@constructors = Hash.new do |h, cls|
|
21
|
+
h[cls] = template = C::FunctionTemplate::New() do |arguments|
|
22
|
+
unless arguments.Length() == 1 && arguments[0].kind_of?(C::External)
|
23
|
+
C::ThrowException(C::Exception::Error(C::String::New("cannot call native constructor from javascript")))
|
24
|
+
else
|
25
|
+
arguments.This().tap do |this|
|
26
|
+
this.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), arguments[0])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
template.tap do
|
31
|
+
setuptemplate(template.InstanceTemplate())
|
32
|
+
if cls != ::Object && cls.superclass != ::Object && cls.superclass != ::Class
|
33
|
+
template.Inherit(@constructors[cls.superclass])
|
34
|
+
end
|
35
|
+
if cls.name && cls.name =~ /(::)?(\w+?)$/
|
36
|
+
template.SetClassName(C::String::NewSymbol("rb::" + $2))
|
37
|
+
else
|
38
|
+
template.SetClassName("Ruby")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
@instances = Hash.new do |h, obj|
|
44
|
+
args = C::Array::New(1)
|
45
|
+
args.Set(0, C::External::New(obj))
|
46
|
+
h[obj] = @constructors[obj.class].GetFunction().NewInstance(args)
|
47
|
+
end
|
48
|
+
|
49
|
+
@functions = Functions.new(self)
|
50
|
+
|
51
|
+
@embedded_constructors = Hash.new do |h, cls|
|
52
|
+
template = @constructors[cls]
|
53
|
+
template.SetCallHandler() do |arguments|
|
54
|
+
wrap = nil
|
55
|
+
if arguments.Length() > 0 && arguments[0].kind_of?(C::External)
|
56
|
+
wrap = arguments[0]
|
57
|
+
else
|
58
|
+
rbargs = []
|
59
|
+
for i in 0..arguments.Length() - 1
|
60
|
+
rbargs << rb(arguments[i])
|
61
|
+
end
|
62
|
+
instance = rubysend(cls, :new, *rbargs)
|
63
|
+
wrap = C::External::New(instance)
|
64
|
+
end
|
65
|
+
arguments.This().tap do |this|
|
66
|
+
this.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), wrap)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
h[cls] = template
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def open
|
74
|
+
@context.native.enter do
|
75
|
+
yield(self)
|
76
|
+
end if block_given?
|
77
|
+
end
|
78
|
+
|
79
|
+
def rb(value)
|
80
|
+
case value
|
81
|
+
when V8::C::Function then peer(value) {V8::Function}
|
82
|
+
when V8::C::Array then peer(value) {V8::Array}
|
83
|
+
when V8::C::Object then peer(value) {V8::Object}
|
84
|
+
when V8::C::String then value.Utf8Value()
|
85
|
+
when V8::C::Date then Time.at(value.NumberValue())
|
86
|
+
when V8::C::Value then nil if value.IsEmpty()
|
87
|
+
else
|
88
|
+
value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def v8(value)
|
93
|
+
case value
|
94
|
+
when V8::Object
|
95
|
+
value.instance_eval {@native}
|
96
|
+
when String
|
97
|
+
C::String::New(value.to_s)
|
98
|
+
when Symbol
|
99
|
+
C::String::NewSymbol(value.to_s)
|
100
|
+
when Proc,Method,UnboundMethod
|
101
|
+
@functions[value]
|
102
|
+
when ::Array
|
103
|
+
C::Array::New(value.length).tap do |a|
|
104
|
+
value.each_with_index do |item, i|
|
105
|
+
a.Set(i, v8(item))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
when ::Hash
|
109
|
+
C::Object::New().tap do |o|
|
110
|
+
value.each do |key, value|
|
111
|
+
o.Set(v8(key), v8(value))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
when ::Time
|
115
|
+
C::Date::New(value)
|
116
|
+
when ::Class
|
117
|
+
@embedded_constructors[value].GetFunction().tap do |f|
|
118
|
+
f.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), C::External::New(value))
|
119
|
+
#set the function's prototype object to the object that will have the named property handlers
|
120
|
+
prototype = rubytemplate.NewInstance()
|
121
|
+
#set *that* object's prototype to an empty function so that it will look and behave like a function.
|
122
|
+
prototype.SetPrototype(C::FunctionTemplate::New() {}.GetFunction())
|
123
|
+
f.SetPrototype(prototype)
|
124
|
+
end
|
125
|
+
when nil,Numeric,TrueClass,FalseClass, C::Value
|
126
|
+
value
|
127
|
+
else
|
128
|
+
@instances[value]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def rubyprotect
|
133
|
+
begin
|
134
|
+
v8 yield
|
135
|
+
rescue Exception => e
|
136
|
+
case e
|
137
|
+
when SystemExit, NoMemoryError
|
138
|
+
raise e
|
139
|
+
else
|
140
|
+
error = V8::C::Exception::Error(V8::C::String::New(e.message))
|
141
|
+
error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
|
142
|
+
V8::C::ThrowException(error)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def rubycall(rubycode, *args, &block)
|
148
|
+
rubyprotect do
|
149
|
+
rubycode.call(*args, &block)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def rubysend(obj, message, *args, &block)
|
154
|
+
rubyprotect do
|
155
|
+
obj.send(message, *args, &block)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def rubytemplate
|
160
|
+
C::ObjectTemplate::New().tap do |t|
|
161
|
+
setuptemplate(t)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def setuptemplate(t)
|
166
|
+
t.SetNamedPropertyHandler(
|
167
|
+
@named_property_getter,
|
168
|
+
@named_property_setter,
|
169
|
+
nil,
|
170
|
+
nil,
|
171
|
+
@named_property_enumerator
|
172
|
+
)
|
173
|
+
t.SetIndexedPropertyHandler(
|
174
|
+
@indexed_property_getter,
|
175
|
+
@indexed_property_setter,
|
176
|
+
nil,
|
177
|
+
nil,
|
178
|
+
@indexed_property_enumerator
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def peer(value)
|
185
|
+
external = value.GetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"))
|
186
|
+
if external && !external.IsEmpty()
|
187
|
+
external.Value()
|
188
|
+
else
|
189
|
+
yield.new(value, self)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class Interceptor
|
194
|
+
def initialize(portal, access)
|
195
|
+
@to, @access = portal, access
|
196
|
+
end
|
197
|
+
|
198
|
+
def intercept(info, retval = nil, &code)
|
199
|
+
obj = @to.rb(info.This())
|
200
|
+
intercepts = true
|
201
|
+
result = @to.rubyprotect do
|
202
|
+
dontintercept = proc do
|
203
|
+
intercepts = false
|
204
|
+
end
|
205
|
+
code.call(obj, dontintercept)
|
206
|
+
end
|
207
|
+
intercepts ? (retval || result) : C::Empty
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
def Interceptor(cls)
|
213
|
+
cls.new self, @access
|
214
|
+
end
|
215
|
+
|
216
|
+
class PropertyAttributes
|
217
|
+
attr_reader :flags
|
218
|
+
def initialize
|
219
|
+
@flags = 0
|
220
|
+
end
|
221
|
+
|
222
|
+
def read_only
|
223
|
+
tap do
|
224
|
+
@flags |= V8::C::ReadOnly
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def dont_enum
|
229
|
+
tap do
|
230
|
+
@flags |= V8::C::DontEnum
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def dont_delete
|
235
|
+
tap do
|
236
|
+
@flags |= V8::C::DontDelete
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class NamedPropertyGetter < Interceptor
|
242
|
+
def call(property, info)
|
243
|
+
intercept(info) do |obj, dontintercept|
|
244
|
+
@access.get(obj, @to.rb(property), &dontintercept)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class NamedPropertySetter < Interceptor
|
250
|
+
def call(property, value, info)
|
251
|
+
intercept(info, value) do |obj, dontintercept|
|
252
|
+
@access.set(obj, @to.rb(property), @to.rb(value), &dontintercept)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class NamedPropertyQuery
|
258
|
+
def call(property, info)
|
259
|
+
attributes = PropertyAttributes.new
|
260
|
+
result = intercept(info) do |obj, dontintercept|
|
261
|
+
@access.query(obj, @to.rb(property), attributes, &dontintercept)
|
262
|
+
end
|
263
|
+
return result == C::Empty ? result : C::Integer::New(attributes.flags)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class NamedPropertyEnumerator < Interceptor
|
268
|
+
def call(info)
|
269
|
+
intercept(info) do |obj, dontintercept|
|
270
|
+
@access.names(obj, &dontintercept).to_a
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class NamedPropertyDeleter < Interceptor
|
276
|
+
def call(property, info)
|
277
|
+
intercept(info) do |obj, dontintercept|
|
278
|
+
@access.delete(obj, property, &dontintercept)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class IndexedPropertyGetter < Interceptor
|
284
|
+
def call(index, info)
|
285
|
+
intercept(info) do |obj, dontintercept|
|
286
|
+
@access.iget(obj, index, &dontintercept)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class IndexedPropertySetter < Interceptor
|
292
|
+
def call(index, value, info)
|
293
|
+
intercept(info, value) do |obj, dontintercept|
|
294
|
+
@access.iset(obj, index, @to.rb(value), &dontintercept)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class IndexedPropertyQuery < Interceptor
|
300
|
+
def call(property, info)
|
301
|
+
attributes = PropertyAttributes.new
|
302
|
+
result = intercept(info) do |obj, dontintercept|
|
303
|
+
@access.indices(obj, &dontintercept)
|
304
|
+
end
|
305
|
+
result == C::Empty ? C::Empty : C::Integer::New(attributes.flags)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
class IndexedPropertyDeleter < Interceptor
|
310
|
+
def call(index, info)
|
311
|
+
intercept(info) do |obj, dontintercept|
|
312
|
+
@access.idelete(obj, index, &dontintercept)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
class IndexedPropertyEnumerator < Interceptor
|
318
|
+
def call(info)
|
319
|
+
intercept(info) do |obj, dontintercept|
|
320
|
+
@access.indices(obj, &dontintercept)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|