Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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