Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 610 lines (472 sloc) 18.498 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,
43 JS_PropertyStub,
44 JS_PropertyStub,
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;
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
155
156 VALUE rb_id = rb_intern(name);
157 VALUE rb_method = rb_funcall(self, rb_intern("method"), 1, ID2SYM(rb_id));
158
159 METHOD* method;
160 Data_Get_Struct(rb_method, METHOD, method);
161
162 return nd_type(method->body) == NODE_IVAR
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
163 || RTEST(rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
164 rb_intern("js_property?"), 2, self, ID2SYM(rb_id)));
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
165 }
166
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
167 static bool indexable_p(VALUE self)
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
168 {
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
169 return RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]"))));
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
170 }
171
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
172 static bool has_key_p(VALUE self, char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
173 {
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
174 return RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]"))))
175 && RTEST(rb_funcall(self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("key?"))))
176 && RTEST(rb_funcall(self, rb_intern("key?"), 1, rb_str_new2(name)));
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
177 }
178
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
179 static bool respond_to_p(JSContext* js_context, JSObject* obj, char* name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
180 {
97b8244 @matthewd Root everything in sight.
matthewd authored
181 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
182
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
183 OurContext* context;
184 Data_Get_Struct(ruby_context, OurContext, context);
97b8244 @matthewd Root everything in sight.
matthewd authored
185
186 VALUE self = (VALUE)JS_GetInstancePrivate(
187 context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
188
189 if (!self) return false;
190
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
191 return autovivified_p(ruby_context, self, name)
192 || const_p(self, name)
193 || global_p(name)
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
194 || attribute_p(self, name)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
195 || method_p(self, name)
196 || has_key_p(self, name);
197 }
198
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
199 static jsval evaluate_js_property_expression(OurContext * context, const char * property, jsval* retval) {
bda2acd @matthewd Merged up to removal of vendor/spidermonkey.
matthewd authored
200 return JS_EvaluateScript(context->js, context->global,
201 property, strlen(property), "johnson:evaluate_js_property_expression", 1,
202 retval);
63b6e8d @tenderlove reducing the iterator code
tenderlove authored
203 }
204
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
205 static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
206 {
97b8244 @matthewd Root everything in sight.
matthewd authored
207 JS_AddNamedRoot(js_context, &id, "JSLandProxy#get");
208
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
209 // 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
210
97b8244 @matthewd Root everything in sight.
matthewd authored
211 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
212
213 // get our struct, which is embedded in ruby_context
214
215 OurContext* context;
216 Data_Get_Struct(ruby_context, OurContext, context);
217
218 // get the Ruby object that backs this proxy
219
97b8244 @matthewd Root everything in sight.
matthewd authored
220 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
221
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
222 // Short-circuit for numeric indexes
223
224 if (JSVAL_IS_INT(id))
225 {
97b8244 @matthewd Root everything in sight.
matthewd authored
226 if (indexable_p(self)) {
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
227 VALUE idx = INT2FIX(JSVAL_TO_INT(id));
97b8244 @matthewd Root everything in sight.
matthewd authored
228 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
229 return call_ruby_from_js(context, retval, self, rb_intern("[]"), 1, idx);
97b8244 @matthewd Root everything in sight.
matthewd authored
230 }
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
231
97b8244 @matthewd Root everything in sight.
matthewd authored
232 JS_RemoveRoot(js_context, &id);
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
233 return JS_TRUE;
234 }
235
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
236 char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
237 VALUE ruby_id = rb_intern(name);
97b8244 @matthewd Root everything in sight.
matthewd authored
238
d45a2d5 @jbarnette Added some notes for the future.
authored
239 // FIXME: we should probably just JS_DefineProperty this, and it shouldn't be enumerable
240
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
241 if (!strcasecmp("__iterator__", name)) {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
242 JS_RemoveRoot(js_context, &id);
bda2acd @matthewd Merged up to removal of vendor/spidermonkey.
matthewd authored
243 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
244 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
245
204c021 switching to hash based dynamic properties
Aaron Patterson authored
246 // if the Ruby object has a dynamic js property with a key
247 // matching the property we're looking for, pull the value out of
248 // that map.
78f8bf9 @jbarnette Naming and formatting nitpicks.
authored
249
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
250 else if (autovivified_p(ruby_context, self, name))
204c021 switching to hash based dynamic properties
Aaron Patterson authored
251 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
252 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
253 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
254 rb_intern("autovivified"), 2, self, rb_str_new2(name));
204c021 switching to hash based dynamic properties
Aaron Patterson authored
255 }
256
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
257 // if the Ruby object is a Module or Class and has a matching
258 // const defined, return the converted result of const_get
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
259
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
260 else if (const_p(self, name))
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
261 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
262 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
263 return call_ruby_from_js(context, retval, self, rb_intern("const_get"),
264 1, ID2SYM(ruby_id));
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
265 }
1f9f150 @tenderlove giving spidermonkey access to ruby constants
tenderlove authored
266
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
267 // otherwise, if it's a global, return the global
268 else if (global_p(name))
1f9f150 @tenderlove giving spidermonkey access to ruby constants
tenderlove authored
269 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
270 JS_RemoveRoot(js_context, &id);
a5d745e @matthewd Make convert_to_js() return a JSBool, to allow error reporting.
matthewd authored
271 return convert_to_js(context, rb_gv_get(name), retval);
1f9f150 @tenderlove giving spidermonkey access to ruby constants
tenderlove authored
272 }
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
273
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
274 // otherwise, if the Ruby object has a an attribute method matching
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
275 // the property we're trying to get, call it and return the converted result
276
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
277 else if (attribute_p(self, name))
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette 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);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
280 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
281 }
5ccbbfa Constants can be accessed as properties in JSLand.
jbarnette authored
282
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
283 // otherwise, if the Ruby object quacks sorta like a hash (it responds to
284 // "[]" and "key?"), index it by key and return the converted result
285
286 else if (has_key_p(self, name))
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, rb_intern("[]"), 1, rb_str_new2(name));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
290 }
291
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
292 // otherwise, it's a method being accessed as a property, which means
293 // we need to return a lambda
294
cfcba6d @jbarnette Added a few more FIXMEs.
authored
295 // FIXME: this should really wrap the Method for 'name' in a JS class
296 // rather than generating a wrapper Proc
297
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
298 else if (method_p(self, name))
299 {
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
300 JS_RemoveRoot(js_context, &id);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
301 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
302 }
5a4b5f0 @matthewd Do a slightly better job of keeping +id+ rooted as long as we need +name...
matthewd authored
303
304 JS_RemoveRoot(js_context, &id);
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
305
306 // else it's undefined (JS_VOID) by default
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
307 return JS_TRUE;
308 }
309
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
310 // called for lazily resolved properties, which should go away
311 static JSBool get_and_destroy_resolved_property(
312 JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
313 {
97b8244 @matthewd Root everything in sight.
matthewd authored
314 JS_AddNamedRoot(js_context, &id, "JSLandProxy#get_and_destroy_resolved_property");
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
315 char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
316 JS_DeleteProperty(js_context, obj, name);
97b8244 @matthewd Root everything in sight.
matthewd authored
317 JS_RemoveRoot(js_context, &id);
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
318 return get(js_context, obj, id, retval);
319 }
320
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
321 static JSBool set(JSContext* js_context, JSObject* obj, jsval id, jsval* value)
322 {
97b8244 @matthewd Root everything in sight.
matthewd authored
323 JS_AddNamedRoot(js_context, &id, "JSLandProxy#set");
324 JS_AddNamedRoot(js_context, value, "JSLandProxy#set");
325
326 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
327
328 OurContext* context;
329 Data_Get_Struct(ruby_context, OurContext, context);
330
97b8244 @matthewd Root everything in sight.
matthewd authored
331 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
332
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
333 // Short-circuit for numeric indexes
334
335 if (JSVAL_IS_INT(id))
336 {
337 if (indexable_p(self))
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
338 {
339 VALUE idx = INT2FIX(JSVAL_TO_INT(id));
340 VALUE val = convert_to_ruby(context, *value);
341 JS_RemoveRoot(js_context, value);
342 JS_RemoveRoot(js_context, &id);
343
344 return call_ruby_from_js(context, NULL, self, rb_intern("[]="), 2, idx, val);
345 }
97b8244 @matthewd Root everything in sight.
matthewd authored
346
347 JS_RemoveRoot(js_context, value);
348 JS_RemoveRoot(js_context, &id);
e0d5bf2 @jbarnette Simple implementation for working numeric array indexes.
authored
349
350 return JS_TRUE;
351 }
352
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
353 VALUE ruby_key = convert_to_ruby(context, id);
354 VALUE ruby_value = convert_to_ruby(context, *value);
97b8244 @matthewd Root everything in sight.
matthewd authored
355
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
356 JS_RemoveRoot(js_context, value);
97b8244 @matthewd Root everything in sight.
matthewd authored
357 JS_RemoveRoot(js_context, &id);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
358
359 VALUE setter = rb_str_append(rb_str_new3(ruby_key), rb_str_new2("="));
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
360 VALUE setter_id = rb_intern(StringValueCStr(setter));
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
361
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
362 VALUE settable_p, indexable_p;
363 if (!call_ruby_from_js2(context, &settable_p, self, rb_intern("respond_to?"), 1, ID2SYM(setter_id)))
364 return JS_FALSE;
365 if (!call_ruby_from_js2(context, &indexable_p, self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]="))))
366 return JS_FALSE;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
367
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
368 if (settable_p)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
369 {
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
370 VALUE method, arity;
371 if (!call_ruby_from_js2(context, &method, self, rb_intern("method"), 1, ID2SYM(setter_id)))
372 return JS_FALSE;
373 if (!call_ruby_from_js2(context, &arity, method, rb_intern("arity"), 0))
374 return JS_FALSE;
375
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
376 // if the Ruby object has a 1-arity method named "property=",
377 // call it with the converted value
378
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
379 if (NUM2INT(arity) == 1)
380 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
381 }
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
382 else if(indexable_p)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
383 {
384 // otherwise, if the Ruby object quacks sorta like a hash for assignment
385 // (it responds to "[]="), assign it by key
386
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
387 return call_ruby_from_js(context, NULL, self, rb_intern("[]="), 2, ruby_key, ruby_value);
78f8bf9 @jbarnette Naming and formatting nitpicks.
authored
388 }
389 else
390 {
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
391 return call_ruby_from_js(context, NULL, Johnson_SpiderMonkey_JSLandProxy(), rb_intern("autovivify"),
392 3, self, ruby_key, ruby_value);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
393 }
97b8244 @matthewd Root everything in sight.
matthewd authored
394
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
395 return JS_TRUE;
396 }
397
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
398 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
399 {
97b8244 @matthewd Root everything in sight.
matthewd authored
400 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
401
402 OurContext* context;
403 Data_Get_Struct(ruby_context, OurContext, context);
404
405 VALUE klass = convert_to_ruby(context, JS_ARGV_CALLEE(argv));
406 VALUE args = rb_ary_new();
407
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
408 uintN i;
46ffbc1 @jbarnette JSland proxies of Ruby classes have working constructors. [#29 state:res...
authored
409 for (i = 0; i < argc; ++i)
410 rb_ary_push(args, convert_to_ruby(context, argv[i]));
411
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
412 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
413 rb_intern("send_with_possible_block"), 3, klass, ID2SYM(rb_intern("new")), args);
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
414 }
415
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
416 static JSBool resolve(JSContext *js_context, JSObject *obj, jsval id, uintN UNUSED(flags), JSObject **objp)
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
417 {
97b8244 @matthewd Root everything in sight.
matthewd authored
418 JS_AddNamedRoot(js_context, &id, "JSLandProxy#resolve");
419
420 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
421
422 OurContext* context;
423 Data_Get_Struct(ruby_context, OurContext, context);
424
425 char* name = JS_GetStringBytes(JS_ValueToString(js_context, id));
97b8244 @matthewd Root everything in sight.
matthewd authored
426
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
427 if (respond_to_p(js_context, obj, name))
428 {
97b8244 @matthewd Root everything in sight.
matthewd authored
429 if(!(JS_DefineProperty(js_context, obj, name, JSVAL_VOID,
430 get_and_destroy_resolved_property, set, JSPROP_ENUMERATE))) {
431 JS_RemoveRoot(js_context, &id);
432 return JS_FALSE;
433 }
434
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
435 *objp = obj;
436 }
97b8244 @matthewd Root everything in sight.
matthewd authored
437
438 JS_RemoveRoot(js_context, &id);
439
6b3683d @jbarnette Make with() work. [#31 state:resolved]
authored
440 return JS_TRUE;
441 }
442
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
443 static JSBool to_string(JSContext* js_context, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* retval)
444 {
445 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
446
447 OurContext* context;
448 Data_Get_Struct(ruby_context, OurContext, context);
449
450 VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
451
452 return call_ruby_from_js(context, retval, self, rb_intern("to_s"), 0);
453 }
454
455 static JSBool to_array(JSContext* js_context, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* retval)
456 {
457 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
458
459 OurContext* context;
460 Data_Get_Struct(ruby_context, OurContext, context);
461
462 VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
463
464 return call_ruby_from_js(context, retval, self, rb_intern("to_a"), 0);
465 }
466
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
467 static JSBool method_missing(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
468 {
97b8244 @matthewd Root everything in sight.
matthewd authored
469 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
470
471 OurContext* context;
472 Data_Get_Struct(ruby_context, OurContext, context);
473
97b8244 @matthewd Root everything in sight.
matthewd authored
474 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
475
97b8244 @matthewd Root everything in sight.
matthewd authored
476 assert(argc >= 2);
477
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
478 char* key = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
479 VALUE ruby_id = rb_intern(key);
480
78f8bf9 @jbarnette Naming and formatting nitpicks.
authored
481 // 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
482 VALUE args;
d40fe3e @matthewd argv[] values are already rooted by SpiderMonkey.
matthewd authored
483 if (!call_ruby_from_js2(context, &args, convert_to_ruby(context, argv[1]), rb_intern("to_a"), 0))
484 return JS_FALSE;
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
485
486 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
487 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
488 }
489
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
490 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
491 {
97b8244 @matthewd Root everything in sight.
matthewd authored
492 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
493
494 OurContext* context;
495 Data_Get_Struct(ruby_context, OurContext, context);
496
97b8244 @matthewd Root everything in sight.
matthewd authored
497 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
498
499 VALUE args = rb_ary_new();
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
500
501 uintN i;
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
502 for (i = 0; i < argc; ++i)
503 rb_ary_push(args, convert_to_ruby(context, argv[i]));
504
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
505 return call_ruby_from_js(context, retval, Johnson_SpiderMonkey_JSLandProxy(),
506 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
507 }
508
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
509 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
510 {
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
511 JSClass* klass = JS_GET_CLASS(context->js, JSVAL_TO_OBJECT(maybe_proxy));
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
512
513 return &JSLandProxyClass == klass
514 || &JSLandClassProxyClass == klass
515 || &JSLandCallableProxyClass == klass;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
516 }
517
e18f74d @jbarnette Renaming proxies for clarity.
authored
518 VALUE unwrap_js_land_proxy(OurContext* context, jsval proxy)
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
519 {
01b0682 @jbarnette JS proxies roundtrip.
authored
520 VALUE value;
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
521 JSObject *proxy_object = JSVAL_TO_OBJECT(proxy);
522
97b8244 @matthewd Root everything in sight.
matthewd authored
523 value = (VALUE)JS_GetInstancePrivate(context->js, proxy_object,
524 JS_GET_CLASS(context->js, proxy_object), NULL);
109858e @jbarnette Make JSLandProxyClass more easily parameterizable.
authored
525
f0cc336 js proxies roundtrip correctly. still no gc root.
jbarnette authored
526 return value;
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
527 }
528
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
529 static void finalize(JSContext* js_context, JSObject* obj)
530 {
531 VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
532
533 if (ruby_context)
534 {
535 OurContext* context;
536 Data_Get_Struct(ruby_context, OurContext, context);
537
97b8244 @matthewd Root everything in sight.
matthewd authored
538 VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj,
539 JS_GET_CLASS(context->js, obj), NULL);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
540
541 // remove the proxy OID from the id map
542 JS_HashTableRemove(context->rbids, (void *)rb_obj_id(self));
543
544 // free up the ruby value for GC
545 rb_funcall(ruby_context, rb_intern("remove_gcthing"), 1, self);
c1a7fbf @matthewd Added a bunch of exception propagation handling.
matthewd authored
546 call_ruby_from_js(context, NULL, ruby_context, rb_intern("remove_gcthing"), 1, self);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
547 }
548 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
549
9149af0 @matthewd More JSBool conversions.
matthewd authored
550 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
551 {
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
552 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
553
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
554 if (id)
555 {
9149af0 @matthewd More JSBool conversions.
matthewd authored
556 return JS_IdToValue(context->js, id, retval);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
557 }
558 else
559 {
560 JSObject *jsobj;
b77b3ad @jbarnette Make sure multiple proxy classes are supported.
authored
561
562 JSClass *klass = &JSLandProxyClass;
563 if (T_CLASS == TYPE(value)) klass = &JSLandClassProxyClass;
564
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
565 // FIXME: hack; should happen in Rubyland
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
566 if (T_STRUCT == TYPE(value))
567 rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
568 rb_intern("treat_all_properties_as_methods"), 1, value);
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
569
0746d92 @matthewd Use C99 bool for simple conditionals, and JSBool only for JS engine.
matthewd authored
570 bool callable_p = rb_class_of(value) == rb_cMethod
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
571 || rb_class_of(value) == rb_cProc;
b7616dd @jbarnette Reimplement property and method access. Mostly.
authored
572
73ad637 @jbarnette Cleaned up proxying of callable (Proc, Method) Ruby objects.
authored
573 if (callable_p)
574 klass = &JSLandCallableProxyClass;
575
97b8244 @matthewd Root everything in sight.
matthewd authored
576 if(!(jsobj = JS_NewObject(context->js, klass, NULL, NULL)))
577 return JS_FALSE;
578 if(!(JS_SetPrivate(context->js, jsobj, (void*)value)))
579 return JS_FALSE;
576e1fe #28 #23 arrays work in js land and so does for .. in
Aaron Patterson authored
580
97b8244 @matthewd Root everything in sight.
matthewd authored
581 if (!callable_p) {
582 if(!(JS_DefineFunction(context->js, jsobj,
583 "__noSuchMethod__", method_missing, 2, 0)))
584 return JS_FALSE;
585 }
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
586
70adb2d @matthewd Enabled a bunch of warnings, and added .toString/.toArray/.to_s
matthewd authored
587 if(!(JS_DefineFunction(context->js, jsobj, "toArray", to_array, 0, 0)))
588 return JS_FALSE;
589
590 if(!(JS_DefineFunction(context->js, jsobj, "toString", to_string, 0, 0)))
591 return JS_FALSE;
592
9149af0 @matthewd More JSBool conversions.
matthewd authored
593 *retval = OBJECT_TO_JSVAL(jsobj);
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
594
595 jsval newid;
97b8244 @matthewd Root everything in sight.
matthewd authored
596 if(!(JS_ValueToId(context->js, *retval, &newid)))
597 return JS_FALSE;
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
598
599 // put the proxy OID in the id map
97b8244 @matthewd Root everything in sight.
matthewd authored
600 if(!(JS_HashTableAdd(context->rbids, (void *)rb_obj_id(value), (void *)newid)))
601 return JS_FALSE;
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
602
603 // root the ruby value for GC
604 VALUE ruby_context = (VALUE)JS_GetContextPrivate(context->js);
605 rb_funcall(ruby_context, rb_intern("add_gcthing"), 1, value);
9149af0 @matthewd More JSBool conversions.
matthewd authored
606
607 return JS_TRUE;
3c24fe9 Fixed Ruby value tracking/GC rooting.
jbarnette authored
608 }
69ace87 JS-land proxies for Ruby objects. No support for GC or roundtripping.
jbarnette authored
609 }
Something went wrong with that request. Please try again.