Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 270 lines (203 sloc) 8.483 kb
e18f74d @jbarnette Renaming proxies for clarity.
authored
1 #include "js_land_proxy.h"
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
2
3 static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval);
4 static JSBool set(JSContext* context, JSObject* obj, jsval id, jsval* retval);
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
5 static JSBool construct(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval);
6 static void finalize(JSContext* context, JSObject* obj);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
7
510f1b5 @jbarnette Fix some vestigal renaming issues.
authored
8 static JSClass JSLandProxyClass = {
9 "JSLandProxy", JSCLASS_HAS_PRIVATE,
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
10 JS_PropertyStub,
11 JS_PropertyStub,
12 get,
13 set,
14 JS_EnumerateStub,
15 JS_ResolveStub,
16 JS_ConvertStub,
17 finalize
18 };
19
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
20 static JSClass JSLandClassProxyClass = {
21 "JSLandClassProxy", JSCLASS_HAS_PRIVATE,
22 JS_PropertyStub,
23 JS_PropertyStub,
24 get,
25 set,
26 JS_EnumerateStub,
27 JS_ResolveStub,
28 JS_ConvertStub,
29 finalize,
30 NULL,
31 NULL,
32 NULL,
33 construct
34 };
35
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
36 static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
37 {
38 // pull out our Ruby object, which is embedded in js_context
39
40 VALUE ruby_context;
41 assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
42
43 // get our struct, which is embedded in ruby_context
44
45 OurContext* context;
46 Data_Get_Struct(ruby_context, OurContext, context);
47
48 // get the Ruby object that backs this proxy
49
50 VALUE self;
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
51 assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
52
53 char* key = JS_GetStringBytes(JSVAL_TO_STRING(id));
54 VALUE ruby_id = rb_intern(key);
55
204c021 switching to hash based dynamic properties
Aaron Patterson authored
56 // if the Ruby object has a dynamic js property with a key
57 // matching the property we're looking for, pull the value out of
58 // that map.
59 if (rb_funcall(ruby_context, rb_intern("autovivified?"), 2, self, ID2SYM(ruby_id)))
60 {
61
62 *retval = convert_to_js(context,
63 rb_funcall(ruby_context, rb_intern("autovivified"), 2, self, ID2SYM(ruby_id)));
64 }
65
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
66 // if the Ruby object is a Module or Class and has a matching
67 // const defined, return the converted result of const_get
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
68
204c021 switching to hash based dynamic properties
Aaron Patterson authored
69 else if (rb_obj_is_kind_of(self, rb_cModule)
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
70 && rb_is_const_id(ruby_id)
71 && rb_funcall(self, rb_intern("const_defined?"), 1, ID2SYM(ruby_id)))
72 {
73 *retval = convert_to_js(context,
74 rb_funcall(self, rb_intern("const_get"), 1, ID2SYM(ruby_id)));
75 }
76
77 // otherwise, if the Ruby object has a 0-arity method named the same as
78 // the property we're trying to get, call it and return the converted result
79
80 else if (rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(ruby_id)))
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
81 {
82 VALUE method = rb_funcall(self, rb_intern("method"), 1, ID2SYM(ruby_id));
83 int arity = NUM2INT(rb_funcall(method, rb_intern("arity"), 0));
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
84
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
85 if (arity == 0)
86 *retval = convert_to_js(context, rb_funcall(self, ruby_id, 0));
87 }
88 else
89 {
90 // otherwise, if the Ruby object quacks sorta like a hash (it responds to
91 // "[]" and "key?"), index it by key and return the converted result
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
92
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
93 VALUE indexable_p = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]")));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
94 VALUE has_key_p = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("key?")));
95
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
96 if (indexable_p && has_key_p)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
97 *retval = convert_to_js(context, rb_funcall(self, rb_intern("[]"), 1, rb_str_new2(key)));
98 }
99
100 return JS_TRUE;
101 }
102
103 static JSBool set(JSContext* js_context, JSObject* obj, jsval id, jsval* value)
104 {
105 VALUE ruby_context;
106 assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
107
108 OurContext* context;
109 Data_Get_Struct(ruby_context, OurContext, context);
110
111 VALUE self;
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
112 assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
113
114 char* key = JS_GetStringBytes(JSVAL_TO_STRING(id));
115 VALUE ruby_key = rb_str_new2(key);
116
117 VALUE setter = rb_str_append(rb_str_new3(ruby_key), rb_str_new2("="));
118 VALUE setter_id = rb_intern(StringValuePtr(setter));
119
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
120 VALUE settable_p = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(setter_id));
121 VALUE indexable_p = rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]=")));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
122
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
123 if (settable_p)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
124 {
125 VALUE method = rb_funcall(self, rb_intern("method"), 1, ID2SYM(setter_id));
126 int arity = NUM2INT(rb_funcall(method, rb_intern("arity"), 0));
127
128 // if the Ruby object has a 1-arity method named "property=",
129 // call it with the converted value
130
131 if (arity == 1)
132 rb_funcall(self, setter_id, 1, convert_to_ruby(context, *value));
133 }
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
134 else if(indexable_p)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
135 {
136 // otherwise, if the Ruby object quacks sorta like a hash for assignment
137 // (it responds to "[]="), assign it by key
138
7329bb5 autovivifying attributes from js land
Aaron Patterson authored
139 rb_funcall(self, rb_intern("[]="), 2, ruby_key, convert_to_ruby(context, *value));
140 } else {
141 rb_funcall(ruby_context, rb_intern("autovivify"), 3, self, ruby_key, convert_to_ruby(context, *value));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
142 }
143
144 return JS_TRUE;
145 }
146
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
147 static JSBool construct(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
148 {
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
149 VALUE ruby_context;
150 assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
151
152 OurContext* context;
153 Data_Get_Struct(ruby_context, OurContext, context);
154
155 VALUE klass = convert_to_ruby(context, JS_ARGV_CALLEE(argv));
156 VALUE args = rb_ary_new();
157
158 int i;
159
160 for (i = 0; i < argc; ++i)
161 rb_ary_push(args, convert_to_ruby(context, argv[i]));
162
163 // Context#jsend: if the last arg is a function, it'll get passed along as a &block
164
165 *retval = convert_to_js(context,
166 rb_funcall(ruby_context, rb_intern("jsend"), 3, klass, ID2SYM(rb_intern("new")), args));
167
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
168 return JS_TRUE;
169 }
170
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
171 static JSBool method_missing(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
172 {
173 VALUE ruby_context;
174 assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
175
176 OurContext* context;
177 Data_Get_Struct(ruby_context, OurContext, context);
178
179 VALUE self;
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
180 assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
181
182 char* key = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
183
184 VALUE ruby_id = rb_intern(key);
185
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
186 // FIXME: this is horrible and lazy, to_a comes from enumerable on proxy
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
187 VALUE args = rb_funcall(convert_to_ruby(context, argv[1]), rb_intern("to_a"), 0);
188
189 // Context#jsend: if the last arg is a function, it'll get passed along as a &block
190
191 *retval = convert_to_js(context,
192 rb_funcall(ruby_context, rb_intern("jsend"), 3, self, ID2SYM(ruby_id), args));
193
194 return JS_TRUE;
195 }
196
01b0682 @jbarnette JS proxies roundtrip.
authored
197 JSBool js_value_is_proxy(OurContext* context, jsval maybe_proxy)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
198 {
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
199 JSClass* klass = JS_GET_CLASS(context->js, JSVAL_TO_OBJECT(maybe_proxy));
200 return &JSLandProxyClass == klass || &JSLandClassProxyClass == klass;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
201 }
202
e18f74d @jbarnette Renaming proxies for clarity.
authored
203 VALUE unwrap_js_land_proxy(OurContext* context, jsval proxy)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
204 {
01b0682 @jbarnette JS proxies roundtrip.
authored
205 VALUE value;
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
206 JSObject *proxy_object = JSVAL_TO_OBJECT(proxy);
207
208 assert(value = (VALUE)JS_GetInstancePrivate(context->js, proxy_object,
209 JS_GET_CLASS(context->js, proxy_object), NULL));
210
f0cc336 js proxies roundtrip correctly. still no gc root.
jbarnette authored
211 return value;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
212 }
213
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
214 static void finalize(JSContext* js_context, JSObject* obj)
215 {
216 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
217
218 if (ruby_context)
219 {
220 OurContext* context;
221 Data_Get_Struct(ruby_context, OurContext, context);
222
223 VALUE self;
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
224 assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj,
225 JS_GET_CLASS(context->js, obj), NULL));
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
226
227 // remove the proxy OID from the id map
228 JS_HashTableRemove(context->rbids, (void *)rb_obj_id(self));
229
230 // free up the ruby value for GC
231 rb_funcall(ruby_context, rb_intern("remove_gcthing"), 1, self);
232 }
233 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
234
e18f74d @jbarnette Renaming proxies for clarity.
authored
235 jsval make_js_land_proxy(OurContext* context, VALUE value)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
236 {
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
237 jsid id = (jsid)JS_HashTableLookup(context->rbids, (void *)rb_obj_id(value));
238 jsval js;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
239
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
240 if (id)
241 {
242 assert(JS_IdToValue(context->js, id, &js));
243 }
244 else
245 {
246 JSObject *jsobj;
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
247
248 JSClass *klass = &JSLandProxyClass;
249 if (T_CLASS == TYPE(value)) klass = &JSLandClassProxyClass;
250
251 assert(jsobj = JS_NewObject(context->js, klass, NULL, NULL));
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
252 assert(JS_SetPrivate(context->js, jsobj, (void*)value));
253 assert(JS_DefineFunction(context->js, jsobj, "__noSuchMethod__", method_missing, 2, 0));
254
255 js = OBJECT_TO_JSVAL(jsobj);
256
257 jsval newid;
258 assert(JS_ValueToId(context->js, js, &newid));
259
260 // put the proxy OID in the id map
261 assert(JS_HashTableAdd(context->rbids, (void *)rb_obj_id(value), (void *)newid));
262
263 // root the ruby value for GC
264 VALUE ruby_context = (VALUE)JS_GetContextPrivate(context->js);
265 rb_funcall(ruby_context, rb_intern("add_gcthing"), 1, value);
266 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
267
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
268 return js;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
269 }
Something went wrong with that request. Please try again.