therubyracer 0.8.2 → 0.9.0beta1

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.

Files changed (53) hide show
  1. data/Changelog.md +1 -1
  2. data/ext/v8/rr.cpp +14 -7
  3. data/ext/v8/rr.h +1 -0
  4. data/ext/v8/v8.cpp +27 -25
  5. data/ext/v8/v8_array.cpp +7 -9
  6. data/ext/v8/v8_callbacks.cpp +1 -1
  7. data/ext/v8/{v8_cxt.cpp → v8_context.cpp} +11 -11
  8. data/ext/v8/{v8_cxt.h → v8_context.h} +1 -1
  9. data/ext/v8/v8_date.cpp +6 -6
  10. data/ext/v8/v8_exception.cpp +10 -11
  11. data/ext/v8/v8_external.cpp +7 -24
  12. data/ext/v8/v8_external.h +0 -1
  13. data/ext/v8/{v8_func.cpp → v8_function.cpp} +14 -14
  14. data/ext/v8/{v8_func.h → v8_function.h} +1 -2
  15. data/ext/v8/v8_handle.cpp +119 -0
  16. data/ext/v8/v8_handle.h +27 -0
  17. data/ext/v8/{v8_msg.cpp → v8_message.cpp} +8 -9
  18. data/ext/v8/{v8_msg.h → v8_message.h} +1 -1
  19. data/ext/v8/{v8_obj.cpp → v8_object.cpp} +51 -29
  20. data/ext/v8/{v8_obj.h → v8_object.h} +3 -4
  21. data/ext/v8/v8_script.cpp +5 -5
  22. data/ext/v8/{v8_str.cpp → v8_string.cpp} +9 -11
  23. data/ext/v8/{v8_str.h → v8_string.h} +1 -1
  24. data/ext/v8/v8_template.cpp +113 -98
  25. data/ext/v8/v8_try_catch.cpp +1 -1
  26. data/ext/v8/v8_v8.cpp +7 -0
  27. data/ext/v8/v8_value.cpp +44 -36
  28. data/ext/v8/v8_value.h +2 -2
  29. data/ext/v8/v8_weakref.cpp +51 -0
  30. data/ext/v8/v8_weakref.h +30 -0
  31. data/lib/v8.rb +6 -1
  32. data/lib/v8/context.rb +13 -3
  33. data/lib/v8/error.rb +1 -1
  34. data/lib/v8/portal.rb +26 -277
  35. data/lib/v8/portal/caller.rb +36 -0
  36. data/lib/v8/portal/constructor.rb +98 -0
  37. data/lib/v8/portal/function.rb +48 -0
  38. data/lib/v8/portal/interceptors.rb +153 -0
  39. data/lib/v8/portal/proxies.rb +102 -0
  40. data/lib/v8/portal/templates.rb +73 -0
  41. data/lib/v8/version.rb +1 -1
  42. data/spec/ext/array_spec.rb +15 -0
  43. data/spec/ext/cxt_spec.rb +4 -4
  44. data/spec/ext/ext_spec_helper.rb +43 -0
  45. data/spec/ext/mem_spec.rb +42 -0
  46. data/spec/ext/object_spec.rb +22 -0
  47. data/spec/redjs/jsapi_spec.rb +4 -4
  48. data/spec/spec_helper.rb +1 -1
  49. data/spec/v8/portal/proxies_spec.rb +189 -0
  50. metadata +38 -42
  51. data/ext/v8/v8_ref.cpp +0 -37
  52. data/ext/v8/v8_ref.h +0 -28
  53. data/lib/v8/portal/functions.rb +0 -45
@@ -0,0 +1,36 @@
1
+
2
+ module V8
3
+ class Portal
4
+ class Caller
5
+
6
+ def initialize(portal)
7
+ @portal = portal
8
+ end
9
+
10
+ def raw
11
+ begin
12
+ yield
13
+ rescue Exception => e
14
+ case e
15
+ when SystemExit, NoMemoryError
16
+ raise e
17
+ else
18
+ error = V8::C::Exception::Error(V8::C::String::New(e.message))
19
+ error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
20
+ V8::C::ThrowException(error)
21
+ end
22
+ end
23
+ end
24
+
25
+ def protect(*args, &block)
26
+ @portal.v8 raw(*args, &block)
27
+ end
28
+
29
+ def invoke(code, *args, &block)
30
+ protect do
31
+ code.call(*args, &block)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,98 @@
1
+
2
+ module V8
3
+ class Portal
4
+ class ConstructorAdapter
5
+ attr_reader :template, :function, :exposed
6
+ alias_method :exposed?, :exposed
7
+
8
+ def initialize(templates, class_id)
9
+ @exposed = false
10
+ @class_id = class_id
11
+ @templates = templates
12
+ @invoke = method(:invoke)
13
+ @template = C::FunctionTemplate::New(@invoke)
14
+ portal.interceptors.setup(@template.InstanceTemplate())
15
+ cls = self.ruby_class
16
+ superclass = cls.superclass
17
+ if cls != ::Object && superclass != ::Object && superclass != ::Class
18
+ @template.Inherit(templates.to_constructor(superclass).template)
19
+ end
20
+ if cls.name && cls.name =~ /(::)?(\w+?)$/
21
+ template.SetClassName(C::String::NewSymbol("rb::" + $2))
22
+ else
23
+ template.SetClassName("Ruby")
24
+ end
25
+ end
26
+
27
+ def function
28
+ @template.GetFunction()
29
+ end
30
+
31
+ def allocate(object)
32
+ arguments = C::Array::New(1)
33
+ arguments.Set(0, C::External::New(object))
34
+ function.NewInstance(arguments)
35
+ end
36
+
37
+ def disable
38
+ @disabled = true
39
+ end
40
+
41
+ def enable
42
+ @disabled = nil
43
+ end
44
+
45
+ def invoke(arguments)
46
+ return if @disabled
47
+ if !@exposed
48
+ unless arguments.Length() == 1 && arguments[0].kind_of?(C::External)
49
+ C::ThrowException(C::Exception::Error(C::String::New("cannot call native constructor from javascript")))
50
+ else
51
+ object = arguments[0].Value()
52
+ proxies.register_javascript_proxy arguments.This(), :for => object
53
+ end
54
+ else
55
+ instance = nil
56
+ if arguments.Length() > 0 && arguments[0].kind_of?(C::External)
57
+ instance = arguments[0].Value()
58
+ else
59
+ rbargs = []
60
+ for i in 0..arguments.Length() - 1
61
+ rbargs << @templates.portal.rb(arguments[i])
62
+ end
63
+ instance = portal.caller.raw do
64
+ self.ruby_class.new(*rbargs)
65
+ end
66
+ end
67
+ proxies.register_javascript_proxy arguments.This(), :for => instance
68
+ end
69
+ end
70
+
71
+ def exposed=(exposed)
72
+ if exposed && !@augmented
73
+ #create a prototype so that this constructor also acts like a ruby object
74
+ prototype_template = C::ObjectTemplate::New()
75
+ portal.interceptors.setup(prototype_template)
76
+ prototype = prototype_template.NewInstance()
77
+ #set *that* object's prototype to an empty function so that it will look and behave like a function.
78
+ prototype.SetPrototype(C::FunctionTemplate::New().GetFunction())
79
+ template.GetFunction().SetPrototype(prototype)
80
+ @augmented = true
81
+ end
82
+ @exposed = exposed
83
+ end
84
+
85
+ def ruby_class
86
+ ObjectSpace._id2ref(@class_id)
87
+ end
88
+
89
+ def proxies
90
+ @templates.proxies
91
+ end
92
+
93
+ def portal
94
+ @templates.portal
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,48 @@
1
+ module V8
2
+ class Portal
3
+ class FunctionAdapter
4
+
5
+ attr_reader :template, :function
6
+
7
+ def initialize(portal, code)
8
+ @portal = portal
9
+ @caller = code.respond_to?(:call) ? Call.new(portal) : BindAndCall.new(portal)
10
+ @code = code
11
+ @template = V8::C::FunctionTemplate::New(@caller, @code)
12
+ end
13
+
14
+ def function
15
+ @template.GetFunction()
16
+ end
17
+
18
+ class Call
19
+ def initialize(portal)
20
+ @portal = portal
21
+ end
22
+
23
+ def call(arguments)
24
+ proc = arguments.Data()
25
+ rbargs = []
26
+ for i in 0..arguments.Length() - 1
27
+ rbargs << @portal.rb(arguments[i])
28
+ end
29
+ @portal.caller.invoke(proc, *rbargs)
30
+ end
31
+ end
32
+
33
+ class BindAndCall < Call
34
+ def call(arguments)
35
+ method = arguments.Data()
36
+ rbargs = []
37
+ for i in 0..arguments.Length() - 1
38
+ rbargs << @portal.rb(arguments[i])
39
+ end
40
+ this = @portal.rb(arguments.This())
41
+ @portal.caller.protect do
42
+ method.bind(this).call(*rbargs)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,153 @@
1
+ module V8
2
+ class Portal
3
+ class Interceptors
4
+ def initialize(portal)
5
+ @portal
6
+ @getter = NamedPropertyGetter.new(portal)
7
+ @setter = NamedPropertySetter.new(portal)
8
+ @query = nil
9
+ @deleter = nil
10
+ @enumerator = NamedPropertyEnumerator.new(portal)
11
+ @igetter = IndexedPropertyGetter.new(portal)
12
+ @isetter = IndexedPropertySetter.new(portal)
13
+ @iquery = nil
14
+ @ideleter = nil
15
+ @ienumerator = IndexedPropertyEnumerator.new(portal)
16
+ end
17
+
18
+ def setup(template)
19
+ template.SetNamedPropertyHandler(@getter,@setter,@query,@deleter,@enumerator, nil)
20
+ template.SetIndexedPropertyHandler(@igetter,@isetter,@iquery,@ideleter,@ienumerator, nil)
21
+ end
22
+
23
+ class PropertyAttributes
24
+ attr_reader :flags
25
+ def initialize
26
+ @flags = 0
27
+ end
28
+
29
+ def read_only
30
+ tap do
31
+ @flags |= V8::C::ReadOnly
32
+ end
33
+ end
34
+
35
+ def dont_enum
36
+ tap do
37
+ @flags |= V8::C::DontEnum
38
+ end
39
+ end
40
+
41
+ def dont_delete
42
+ tap do
43
+ @flags |= V8::C::DontDelete
44
+ end
45
+ end
46
+ end
47
+
48
+ class Interceptor
49
+
50
+ def initialize(portal)
51
+ @to, @access = portal, portal.access
52
+ end
53
+
54
+ def intercept(info, retval = nil, &code)
55
+ obj = @to.rb(info.This())
56
+ intercepts = true
57
+ result = @to.caller.protect do
58
+ dontintercept = proc do
59
+ intercepts = false
60
+ end
61
+ code.call(obj, dontintercept)
62
+ end
63
+ intercepts ? (retval || result) : C::Empty
64
+ end
65
+ end
66
+
67
+ class NamedPropertyGetter < Interceptor
68
+ def call(property, info)
69
+ intercept(info) do |obj, dontintercept|
70
+ @access.get(obj, @to.rb(property), &dontintercept)
71
+ end
72
+ end
73
+ end
74
+
75
+ class NamedPropertySetter < Interceptor
76
+ def call(property, value, info)
77
+ intercept(info, value) do |obj, dontintercept|
78
+ @access.set(obj, @to.rb(property), @to.rb(value), &dontintercept)
79
+ end
80
+ end
81
+ end
82
+
83
+ class NamedPropertyQuery
84
+ def call(property, info)
85
+ attributes = PropertyAttributes.new
86
+ result = intercept(info) do |obj, dontintercept|
87
+ @access.query(obj, @to.rb(property), attributes, &dontintercept)
88
+ end
89
+ return result == C::Empty ? result : C::Integer::New(attributes.flags)
90
+ end
91
+ end
92
+
93
+ class NamedPropertyEnumerator < Interceptor
94
+ def call(info)
95
+ intercept(info) do |obj, dontintercept|
96
+ @access.names(obj, &dontintercept).to_a
97
+ end
98
+ end
99
+ end
100
+
101
+ class NamedPropertyDeleter < Interceptor
102
+ def call(property, info)
103
+ intercept(info) do |obj, dontintercept|
104
+ @access.delete(obj, property, &dontintercept)
105
+ end
106
+ end
107
+ end
108
+
109
+ class IndexedPropertyGetter < Interceptor
110
+ def call(index, info)
111
+ intercept(info) do |obj, dontintercept|
112
+ @access.iget(obj, index, &dontintercept)
113
+ end
114
+ end
115
+ end
116
+
117
+ class IndexedPropertySetter < Interceptor
118
+ def call(index, value, info)
119
+ intercept(info, value) do |obj, dontintercept|
120
+ @access.iset(obj, index, @to.rb(value), &dontintercept)
121
+ end
122
+ end
123
+ end
124
+
125
+ class IndexedPropertyQuery < Interceptor
126
+ def call(property, info)
127
+ attributes = PropertyAttributes.new
128
+ result = intercept(info) do |obj, dontintercept|
129
+ @access.indices(obj, &dontintercept)
130
+ end
131
+ result == C::Empty ? C::Empty : C::Integer::New(attributes.flags)
132
+ end
133
+ end
134
+
135
+ class IndexedPropertyDeleter < Interceptor
136
+ def call(index, info)
137
+ intercept(info) do |obj, dontintercept|
138
+ @access.idelete(obj, index, &dontintercept)
139
+ end
140
+ end
141
+ end
142
+
143
+ class IndexedPropertyEnumerator < Interceptor
144
+ def call(info)
145
+ intercept(info) do |obj, dontintercept|
146
+ @access.indices(obj, &dontintercept)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+
@@ -0,0 +1,102 @@
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
+ def rb_object_2_js_proxy(object)
49
+ @js_proxies_rb2js[object]
50
+ end
51
+
52
+ def js_proxy_2_rb_object(proxy)
53
+ @js_proxies_js2rb[proxy]
54
+ end
55
+
56
+ def register_ruby_proxy(proxy, options = {})
57
+ target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param"
58
+ fail ArgumentError, "'#{proxy.inspect}' is not a Handle to an actual V8 object" unless target.kind_of?(V8::C::Handle)
59
+ @rb_proxies_rb2js[proxy.object_id] = target
60
+ @rb_proxies_js2rb[target] = proxy.object_id
61
+ ObjectSpace.define_finalizer(proxy, @clear_rb_proxy)
62
+ end
63
+
64
+ def js_object_2_rb_proxy(object)
65
+ if id = @rb_proxies_js2rb[object]
66
+ ObjectSpace._id2ref id
67
+ end
68
+ end
69
+
70
+ def rb_proxy_2_js_object(proxy)
71
+ @rb_proxies_rb2js[proxy.object_id]
72
+ end
73
+
74
+ DoubleProxyError = Class.new(StandardError)
75
+
76
+ class ClearJSProxy
77
+
78
+ def initialize(rb2js, js2rb)
79
+ @rb2js, @js2rb = rb2js, js2rb
80
+ end
81
+
82
+ def call(proxy, parameter)
83
+ rb = @js2rb[proxy]
84
+ @js2rb.delete(proxy)
85
+ @rb2js.delete(rb)
86
+ end
87
+ end
88
+
89
+ class ClearRubyProxy
90
+ def initialize(rb2js, js2rb)
91
+ @rb2js, @js2rb = rb2js, js2rb
92
+ end
93
+
94
+ def call(proxy_id)
95
+ js = @rb2js[proxy_id]
96
+ @rb2js.delete(proxy_id)
97
+ @js2rb.delete(js)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ 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