Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 617 lines (477 sloc) 18.601 kb
e18f74d @jbarnette Renaming proxies for clarity.
authored
1 #include "js_land_proxy.h"
dc42336 @matthewd Deal with a couple of minor warnings.
matthewd authored
2 #include "conversions.h"
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
3
4 static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval);
5 static JSBool set(JSContext* context, JSObject* obj, jsval id, jsval* retval);
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
6 static JSBool construct(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval);
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
7 static JSBool resolve(JSContext *js_context, JSObject *obj, jsval id, uintN flags, JSObject **objp);
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
8 static JSBool call(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval);
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
9 static void finalize(JSContext* context, JSObject* obj);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
10
510f1b5 @jbarnette Fix some vestigal renaming issues.
authored
11 static JSClass JSLandProxyClass = {
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
12 "JSLandProxy", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
13 JS_PropertyStub,
14 JS_PropertyStub,
15 get,
16 set,
17 JS_EnumerateStub,
d3286d8 @jbarnette Fixing compiler warnings.
authored
18 (JSResolveOp) resolve,
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
19 JS_ConvertStub,
20 finalize
21 };
22
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
23 static JSClass JSLandClassProxyClass = {
24 "JSLandClassProxy", JSCLASS_HAS_PRIVATE,
25 JS_PropertyStub,
26 JS_PropertyStub,
27 get,
28 set,
29 JS_EnumerateStub,
30 JS_ResolveStub,
31 JS_ConvertStub,
32 finalize,
33 NULL,
34 NULL,
35 NULL,
36 construct
37 };
38
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
39 static JSClass JSLandCallableProxyClass = {
40 "JSLandCallableProxy", JSCLASS_HAS_PRIVATE,
41 JS_PropertyStub,
42 JS_PropertyStub,
f518879 @jbarnette Any Rubyland object that responds to call can be called as a function in...
authored
43 get,
44 set,
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
45 JS_EnumerateStub,
46 JS_ResolveStub,
47 JS_ConvertStub,
48 finalize,
49 NULL,
50 NULL,
51 call
52 };
53
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
54 #define TAG_RAISE 0x6
55 #define TAG_THROW 0x7
56
57 static VALUE call_ruby_from_js_invoke(VALUE args)
58 {
59 VALUE self = rb_ary_pop(args);
60 VALUE id = rb_ary_pop(args);
61 return rb_apply(self, SYM2ID(id), args);
62 }
63
64 JSBool call_ruby_from_js_va(OurContext* context, VALUE* result, VALUE self, ID id, int argc, va_list va)
65 {
66 VALUE old_errinfo = ruby_errinfo;
67 VALUE args = rb_ary_new2(argc + 2);
68
69 int i;
70 for(i = 0; i < argc; i++)
71 rb_ary_store(args, i, va_arg(va, VALUE));
72
73 rb_ary_store(args, argc, ID2SYM(id));
74 rb_ary_store(args, argc + 1, self);
75
76 int state;
77 *result = rb_protect(call_ruby_from_js_invoke, args, &state);
78
79 switch (state)
80 {
81 case 0:
82 return JS_TRUE;
83
84 case TAG_RAISE:
85 {
86 VALUE local_error = ruby_errinfo;
87 jsval js_err;
88 ruby_errinfo = old_errinfo;
89 if (!convert_to_js(context, local_error, &js_err))
90 return JS_FALSE;
91 JS_SetPendingException(context->js, js_err);
92 return JS_FALSE;
93 }
94
95 case TAG_THROW:
96 // FIXME: This should be propagated to JS... as an exception?
97
98 default:
99 {
100 JSString* str = JS_NewStringCopyZ(context->js, "Unexpected longjmp from ruby!");
101 if (str)
102 JS_SetPendingException(context->js, STRING_TO_JSVAL(str));
103 return JS_FALSE;
104 }
105 }
106 }
107
108 JSBool call_ruby_from_js(OurContext* context, jsval* retval, VALUE self, ID id, int argc, ...)
109 {
110 VALUE result;
111 va_list va;
112 va_start(va, argc);
113 JSBool okay = call_ruby_from_js_va(context, &result, self, id, argc, va);
114 va_end(va);
115 if (!okay) return JS_FALSE;
116 return retval ? convert_to_js(context, result, retval) : JS_TRUE;
117 }
118
119 JSBool call_ruby_from_js2(OurContext* context, VALUE* retval, VALUE self, ID id, int argc, ...)
120 {
121 va_list va;
122 va_start(va, argc);
123 JSBool okay = call_ruby_from_js_va(context, retval, self, id, argc, va);
124 va_end(va);
125 return okay;
126 }
127
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
128 static bool autovivified_p(VALUE UNUSED(ruby_context), VALUE self, char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
129 {
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
130 return RTEST(rb_funcall(Johnson_SpiderMonkey_JSLandProxy(), rb_intern("autovivified?"), 2,
131 self, rb_str_new2(name)));
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
132 }
133
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
134 static bool const_p(VALUE self, char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
135 {
136 return rb_obj_is_kind_of(self, rb_cModule)
137 && rb_is_const_id(rb_intern(name))
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
138 && RTEST( rb_funcall(self, rb_intern("const_defined?"), 1, ID2SYM(rb_intern(name))) );
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
139 }
140
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
141 static bool global_p(char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
142 {
143 return rb_ary_includes(rb_f_global_variables(), rb_str_new2(name));
144 }
145
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
146 static bool method_p(VALUE self, char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
147 {
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
148 return RTEST( rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern(name))) );
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
149 }
150
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
151 static bool attribute_p(VALUE self, char* name)
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
152 {
153 if (!method_p(self, name))
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
154 return false;
5447b86 @matthewd Be a little more certain that we've got a METHOD* before we poke it.
matthewd authored
155
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
156 VALUE rb_id = rb_intern(name);
157 VALUE rb_method = rb_funcall(self, rb_intern("method"), 1, ID2SYM(rb_id));
158
5447b86 @matthewd Be a little more certain that we've got a METHOD* before we poke it.
matthewd authored
159 if (TYPE(rb_method) == T_DATA)
160 {
161 VALUE klass = CLASS_OF(rb_method);
162 if (klass == rb_cMethod)
163 {
164 METHOD* method;
165 Data_Get_Struct(rb_method, METHOD, method);
166
167 if (method && nd_type(method->body) == NODE_IVAR)
168 return true;
169 }
170 }
171
172 return RTEST(rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
173 rb_intern("js_property?"), 2, self, ID2SYM(rb_id)));
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
174 }
175
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
176 static bool indexable_p(VALUE self)
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
177 {
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
178 return RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]"))));
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
179 }
180
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
181 static bool has_key_p(VALUE self, char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
182 {
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
183 return RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]"))))
184 && RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("key?"))))
185 && RTEST(rb_funcall(self, rb_intern("key?"), 1, rb_str_new2(name)));
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
186 }
187
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
188 static bool respond_to_p(JSContext* js_context, JSObject* obj, char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
189 {
97b8244 @matthewd Root everything in sight.
matthewd authored
190 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
191
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
192 OurContext* context;
193 Data_Get_Struct(ruby_context, OurContext, context);
97b8244 @matthewd Root everything in sight.
matthewd authored
194
195 VALUE self = (VALUE)JS_GetInstancePrivate(
196 context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
197
198 if (!self) return false;
199
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
200 return autovivified_p(ruby_context, self, name)
201 || const_p(self, name)
202 || global_p(name)
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
203 || attribute_p(self, name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
204 || method_p(self, name)
205 || has_key_p(self, name);
206 }
207
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
208 static jsval evaluate_js_property_expression(OurContext * context, const char * property, jsval* retval) {
bda2acd @matthewd Merged up to removal of vendor/spidermonkey.
matthewd authored
209 return JS_EvaluateScript(context->js, context->global,
210 property, strlen(property), "johnson:evaluate_js_property_expression", 1,
211 retval);
63b6e8d @tenderlove reducing the iterator code
tenderlove authored
212 }
213
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
214 static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
215 {
97b8244 @matthewd Root everything in sight.
matthewd authored
216 JS_AddNamedRoot(js_context, &id, "JSLandProxy#get");
217
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
218 // pull out our Ruby context, which is embedded in js_context
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
219
97b8244 @matthewd Root everything in sight.
matthewd authored
220 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
221
222 // get our struct, which is embedded in ruby_context
223
224 OurContext* context;
225 Data_Get_Struct(ruby_context, OurContext, context);
226
227 // get the Ruby object that backs this proxy
228
97b8244 @matthewd Root everything in sight.
matthewd authored
229 VALUE 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
230
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
231 // Short-circuit for numeric indexes
232
233 if (JSVAL_IS_INT(id))
234 {
97b8244 @matthewd Root everything in sight.
matthewd authored
235 if (indexable_p(self)) {
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
236 VALUE idx = INT2FIX(JSVAL_TO_INT(id));
97b8244 @matthewd Root everything in sight.
matthewd authored
237 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
238 return call_ruby_from_js(context, retval, self, rb_intern("[]"), 1, idx);
97b8244 @matthewd Root everything in sight.
matthewd authored
239 }
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
240
97b8244 @matthewd Root everything in sight.
matthewd authored
241 JS_RemoveRoot(js_context, &id);
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
242 return JS_TRUE;
243 }
244
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
245 char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
246 VALUE ruby_id = rb_intern(name);
97b8244 @matthewd Root everything in sight.
matthewd authored
247
d45a2d5 @jbarnette Added some notes for the future.
authored
248 // FIXME: we should probably just JS_DefineProperty this, and it shouldn't be enumerable
249
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
250 if (!strcasecmp("__iterator__", name)) {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
251 JS_RemoveRoot(js_context, &id);
bda2acd @matthewd Merged up to removal of vendor/spidermonkey.
matthewd authored
252 return evaluate_js_property_expression(context, "Johnson.Generator.create", retval);
576e1fe #28 #23 arrays work in js land and so does for .. in
Aaron Patterson authored
253 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
254
204c021 switching to hash based dynamic properties
Aaron Patterson authored
255 // if the Ruby object has a dynamic js property with a key
256 // matching the property we're looking for, pull the value out of
257 // that map.
78f8bf9 @jbarnette Naming and formatting nitpicks.
authored
258
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
259 else if (autovivified_p(ruby_context, self, name))
204c021 switching to hash based dynamic properties
Aaron Patterson authored
260 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
261 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
262 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
263 rb_intern("autovivified"), 2, self, rb_str_new2(name));
204c021 switching to hash based dynamic properties
Aaron Patterson authored
264 }
265
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
266 // if the Ruby object is a Module or Class and has a matching
267 // const defined, return the converted result of const_get
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
268
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
269 else if (const_p(self, name))
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
270 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
271 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
272 return call_ruby_from_js(context, retval, self, rb_intern("const_get"),
273 1, ID2SYM(ruby_id));
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
274 }
1f9f150 @tenderlove giving spidermonkey access to ruby constants
tenderlove authored
275
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
276 // otherwise, if it's a global, return the global
277 else if (global_p(name))
1f9f150 @tenderlove giving spidermonkey access to ruby constants
tenderlove authored
278 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
279 JS_RemoveRoot(js_context, &id);
a5d745e @matthewd Make convert_to_js() return a JSBool, to allow error reporting.
matthewd authored
280 return convert_to_js(context, rb_gv_get(name), retval);
1f9f150 @tenderlove giving spidermonkey access to ruby constants
tenderlove authored
281 }
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
282
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
283 // otherwise, if the Ruby object has a an attribute method matching
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
284 // the property we're trying to get, call it and return the converted result
285
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
286 else if (attribute_p(self, name))
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
287 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
288 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
289 return call_ruby_from_js(context, retval, self, ruby_id, 0);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
290 }
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
291
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
292 // otherwise, if the Ruby object quacks sorta like a hash (it responds to
293 // "[]" and "key?"), index it by key and return the converted result
294
295 else if (has_key_p(self, name))
296 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
297 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
298 return call_ruby_from_js(context, retval, self, rb_intern("[]"), 1, rb_str_new2(name));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
299 }
300
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
301 // otherwise, it's a method being accessed as a property, which means
302 // we need to return a lambda
303
cfcba6d @jbarnette Added a few more FIXMEs.
authored
304 // FIXME: this should really wrap the Method for 'name' in a JS class
305 // rather than generating a wrapper Proc
306
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
307 else if (method_p(self, name))
308 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
309 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
310 return call_ruby_from_js(context, retval, self, rb_intern("method"), 1, rb_str_new2(name));
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
311 }
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
312
313 JS_RemoveRoot(js_context, &id);
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
314
315 // else it's undefined (JS_VOID) by default
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
316 return JS_TRUE;
317 }
318
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
319 // called for lazily resolved properties, which should go away
320 static JSBool get_and_destroy_resolved_property(
321 JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
322 {
97b8244 @matthewd Root everything in sight.
matthewd authored
323 JS_AddNamedRoot(js_context, &id, "JSLandProxy#get_and_destroy_resolved_property");
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
324 char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
325 JS_DeleteProperty(js_context, obj, name);
97b8244 @matthewd Root everything in sight.
matthewd authored
326 JS_RemoveRoot(js_context, &id);
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
327 return get(js_context, obj, id, retval);
328 }
329
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
330 static JSBool set(JSContext* js_context, JSObject* obj, jsval id, jsval* value)
331 {
97b8244 @matthewd Root everything in sight.
matthewd authored
332 JS_AddNamedRoot(js_context, &id, "JSLandProxy#set");
333 JS_AddNamedRoot(js_context, value, "JSLandProxy#set");
334
335 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
336
337 OurContext* context;
338 Data_Get_Struct(ruby_context, OurContext, context);
339
97b8244 @matthewd Root everything in sight.
matthewd authored
340 VALUE 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
341
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
342 // Short-circuit for numeric indexes
343
344 if (JSVAL_IS_INT(id))
345 {
346 if (indexable_p(self))
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
347 {
348 VALUE idx = INT2FIX(JSVAL_TO_INT(id));
349 VALUE val = convert_to_ruby(context, *value);
350 JS_RemoveRoot(js_context, value);
351 JS_RemoveRoot(js_context, &id);
352
353 return call_ruby_from_js(context, NULL, self, rb_intern("[]="), 2, idx, val);
354 }
97b8244 @matthewd Root everything in sight.
matthewd authored
355
356 JS_RemoveRoot(js_context, value);
357 JS_RemoveRoot(js_context, &id);
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
358
359 return JS_TRUE;
360 }
361
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
362 VALUE ruby_key = convert_to_ruby(context, id);
363 VALUE ruby_value = convert_to_ruby(context, *value);
97b8244 @matthewd Root everything in sight.
matthewd authored
364
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
365 JS_RemoveRoot(js_context, value);
97b8244 @matthewd Root everything in sight.
matthewd authored
366 JS_RemoveRoot(js_context, &id);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
367
368 VALUE setter = rb_str_append(rb_str_new3(ruby_key), rb_str_new2("="));
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
369 VALUE setter_id = rb_intern(StringValueCStr(setter));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
370
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
371 VALUE settable_p, indexable_p;
372 if (!call_ruby_from_js2(context, &settable_p, self, rb_intern("respond_to?"), 1, ID2SYM(setter_id)))
373 return JS_FALSE;
374 if (!call_ruby_from_js2(context, &indexable_p, self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]="))))
375 return JS_FALSE;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
376
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
377 if (settable_p)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
378 {
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
379 VALUE method, arity;
380 if (!call_ruby_from_js2(context, &method, self, rb_intern("method"), 1, ID2SYM(setter_id)))
381 return JS_FALSE;
382 if (!call_ruby_from_js2(context, &arity, method, rb_intern("arity"), 0))
383 return JS_FALSE;
384
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
385 // if the Ruby object has a 1-arity method named "property=",
386 // call it with the converted value
387
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
388 if (NUM2INT(arity) == 1)
389 return call_ruby_from_js(context, NULL, self, setter_id, 1, ruby_value);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
390 }
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
391 else if(indexable_p)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
392 {
393 // otherwise, if the Ruby object quacks sorta like a hash for assignment
394 // (it responds to "[]="), assign it by key
395
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
396 return call_ruby_from_js(context, NULL, self, rb_intern("[]="), 2, ruby_key, ruby_value);
78f8bf9 @jbarnette Naming and formatting nitpicks.
authored
397 }
398 else
399 {
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
400 return call_ruby_from_js(context, NULL, Johnson_SpiderMonkey_JSLandProxy(), rb_intern("autovivify"),
401 3, self, ruby_key, ruby_value);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
402 }
97b8244 @matthewd Root everything in sight.
matthewd authored
403
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
404 return JS_TRUE;
405 }
406
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
407 static JSBool construct(JSContext* js_context, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* retval)
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
408 {
97b8244 @matthewd Root everything in sight.
matthewd authored
409 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
410
411 OurContext* context;
412 Data_Get_Struct(ruby_context, OurContext, context);
413
414 VALUE klass = convert_to_ruby(context, JS_ARGV_CALLEE(argv));
415 VALUE args = rb_ary_new();
416
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
417 uintN i;
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
418 for (i = 0; i < argc; ++i)
419 rb_ary_push(args, convert_to_ruby(context, argv[i]));
420
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
421 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
422 rb_intern("send_with_possible_block"), 3, klass, ID2SYM(rb_intern("new")), args);
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
423 }
424
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
425 static JSBool resolve(JSContext *js_context, JSObject *obj, jsval id, uintN UNUSED(flags), JSObject **objp)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
426 {
97b8244 @matthewd Root everything in sight.
matthewd authored
427 JS_AddNamedRoot(js_context, &id, "JSLandProxy#resolve");
428
429 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
430
431 OurContext* context;
432 Data_Get_Struct(ruby_context, OurContext, context);
433
434 char* name = JS_GetStringBytes(JS_ValueToString(js_context, id));
97b8244 @matthewd Root everything in sight.
matthewd authored
435
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
436 if (respond_to_p(js_context, obj, name))
437 {
97b8244 @matthewd Root everything in sight.
matthewd authored
438 if(!(JS_DefineProperty(js_context, obj, name, JSVAL_VOID,
439 get_and_destroy_resolved_property, set, JSPROP_ENUMERATE))) {
440 JS_RemoveRoot(js_context, &id);
441 return JS_FALSE;
442 }
443
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
444 *objp = obj;
445 }
97b8244 @matthewd Root everything in sight.
matthewd authored
446
447 JS_RemoveRoot(js_context, &id);
448
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
449 return JS_TRUE;
450 }
451
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
452 static JSBool to_string(JSContext* js_context, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* retval)
453 {
454 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
455
456 OurContext* context;
457 Data_Get_Struct(ruby_context, OurContext, context);
458
459 VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
460
461 return call_ruby_from_js(context, retval, self, rb_intern("to_s"), 0);
462 }
463
464 static JSBool to_array(JSContext* js_context, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* retval)
465 {
466 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
467
468 OurContext* context;
469 Data_Get_Struct(ruby_context, OurContext, context);
470
471 VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
472
473 return call_ruby_from_js(context, retval, self, rb_intern("to_a"), 0);
474 }
475
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
476 static JSBool method_missing(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
477 {
97b8244 @matthewd Root everything in sight.
matthewd authored
478 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
479
480 OurContext* context;
481 Data_Get_Struct(ruby_context, OurContext, context);
482
97b8244 @matthewd Root everything in sight.
matthewd authored
483 VALUE 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
484
97b8244 @matthewd Root everything in sight.
matthewd authored
485 assert(argc >= 2);
486
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
487 char* key = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
488 VALUE ruby_id = rb_intern(key);
489
78f8bf9 @jbarnette Naming and formatting nitpicks.
authored
490 // FIXME: this is horrible and lazy, to_a comes from enumerable on proxy (argv[1] is a JSArray)
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
491 VALUE args;
d40fe3e @matthewd argv[] values are already rooted by SpiderMonkey.
matthewd authored
492 if (!call_ruby_from_js2(context, &args, convert_to_ruby(context, argv[1]), rb_intern("to_a"), 0))
493 return JS_FALSE;
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
494
495 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
496 rb_intern("send_with_possible_block"), 3, self, ID2SYM(ruby_id), args);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
497 }
498
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
499 static JSBool call(JSContext* js_context, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* retval)
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
500 {
97b8244 @matthewd Root everything in sight.
matthewd authored
501 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
502
503 OurContext* context;
504 Data_Get_Struct(ruby_context, OurContext, context);
505
97b8244 @matthewd Root everything in sight.
matthewd authored
506 VALUE self = (VALUE)JS_GetInstancePrivate(context->js, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), &JSLandCallableProxyClass, NULL);
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
507
508 VALUE args = rb_ary_new();
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
509
510 uintN i;
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
511 for (i = 0; i < argc; ++i)
512 rb_ary_push(args, convert_to_ruby(context, argv[i]));
513
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
514 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
515 rb_intern("send_with_possible_block"), 3, self, ID2SYM(rb_intern("call")), args);
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
516 }
517
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
518 bool js_value_is_proxy(OurContext* MAYBE_UNUSED(context), jsval maybe_proxy)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
519 {
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
520 JSClass* klass = JS_GET_CLASS(context->js, JSVAL_TO_OBJECT(maybe_proxy));
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
521
522 return &JSLandProxyClass == klass
523 || &JSLandClassProxyClass == klass
524 || &JSLandCallableProxyClass == klass;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
525 }
526
e18f74d @jbarnette Renaming proxies for clarity.
authored
527 VALUE unwrap_js_land_proxy(OurContext* context, jsval proxy)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
528 {
01b0682 @jbarnette JS proxies roundtrip.
authored
529 VALUE value;
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
530 JSObject *proxy_object = JSVAL_TO_OBJECT(proxy);
531
97b8244 @matthewd Root everything in sight.
matthewd authored
532 value = (VALUE)JS_GetInstancePrivate(context->js, proxy_object,
533 JS_GET_CLASS(context->js, proxy_object), NULL);
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
534
f0cc336 js proxies roundtrip correctly. still no gc root.
jbarnette authored
535 return value;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
536 }
537
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
538 static void finalize(JSContext* js_context, JSObject* obj)
539 {
540 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
541
542 if (ruby_context)
543 {
544 OurContext* context;
545 Data_Get_Struct(ruby_context, OurContext, context);
546
97b8244 @matthewd Root everything in sight.
matthewd authored
547 VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj,
548 JS_GET_CLASS(context->js, obj), NULL);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
549
550 // remove the proxy OID from the id map
551 JS_HashTableRemove(context->rbids, (void *)rb_obj_id(self));
552
553 // free up the ruby value for GC
554 rb_funcall(ruby_context, rb_intern("remove_gcthing"), 1, self);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
555 call_ruby_from_js(context, NULL, ruby_context, rb_intern("remove_gcthing"), 1, self);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
556 }
557 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
558
9149af0 @matthewd More JSBool conversions.
matthewd authored
559 JSBool make_js_land_proxy(OurContext* context, VALUE value, jsval* retval)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
560 {
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
561 jsid id = (jsid)JS_HashTableLookup(context->rbids, (void *)rb_obj_id(value));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
562
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
563 if (id)
564 {
9149af0 @matthewd More JSBool conversions.
matthewd authored
565 return JS_IdToValue(context->js, id, retval);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
566 }
567 else
568 {
569 JSObject *jsobj;
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
570
571 JSClass *klass = &JSLandProxyClass;
572 if (T_CLASS == TYPE(value)) klass = &JSLandClassProxyClass;
573
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
574 // FIXME: hack; should happen in Rubyland
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
575 if (T_STRUCT == TYPE(value))
576 rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
577 rb_intern("treat_all_properties_as_methods"), 1, value);
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
578
f518879 @jbarnette Any Rubyland object that responds to call can be called as a function in...
authored
579 bool callable_p = Qtrue == rb_funcall(value,
580 rb_intern("respond_to?"), 1, rb_str_new2("call"));
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
581
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
582 if (callable_p)
583 klass = &JSLandCallableProxyClass;
584
97b8244 @matthewd Root everything in sight.
matthewd authored
585 if(!(jsobj = JS_NewObject(context->js, klass, NULL, NULL)))
586 return JS_FALSE;
f518879 @jbarnette Any Rubyland object that responds to call can be called as a function in...
authored
587
97b8244 @matthewd Root everything in sight.
matthewd authored
588 if(!(JS_SetPrivate(context->js, jsobj, (void*)value)))
589 return JS_FALSE;
576e1fe #28 #23 arrays work in js land and so does for .. in
Aaron Patterson authored
590
f518879 @jbarnette Any Rubyland object that responds to call can be called as a function in...
authored
591 if(!(JS_DefineFunction(context->js, jsobj, "__noSuchMethod__", method_missing, 2, 0)))
592 return JS_FALSE;
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
593
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
594 if(!(JS_DefineFunction(context->js, jsobj, "toArray", to_array, 0, 0)))
595 return JS_FALSE;
596
597 if(!(JS_DefineFunction(context->js, jsobj, "toString", to_string, 0, 0)))
598 return JS_FALSE;
599
9149af0 @matthewd More JSBool conversions.
matthewd authored
600 *retval = OBJECT_TO_JSVAL(jsobj);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
601
602 jsval newid;
97b8244 @matthewd Root everything in sight.
matthewd authored
603 if(!(JS_ValueToId(context->js, *retval, &newid)))
604 return JS_FALSE;
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
605
606 // put the proxy OID in the id map
97b8244 @matthewd Root everything in sight.
matthewd authored
607 if(!(JS_HashTableAdd(context->rbids, (void *)rb_obj_id(value), (void *)newid)))
608 return JS_FALSE;
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
609
610 // root the ruby value for GC
611 VALUE ruby_context = (VALUE)JS_GetContextPrivate(context->js);
612 rb_funcall(ruby_context, rb_intern("add_gcthing"), 1, value);
9149af0 @matthewd More JSBool conversions.
matthewd authored
613
614 return JS_TRUE;
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
615 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
616 }
Something went wrong with that request. Please try again.