jbarnette-johnson 1.0.0.200806240111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. data/CHANGELOG +5 -0
  2. data/MANIFEST +385 -0
  3. data/MINGW32.mk +124 -0
  4. data/README.rdoc +51 -0
  5. data/Rakefile +166 -0
  6. data/bin/johnson +107 -0
  7. data/cross-compile.txt +38 -0
  8. data/ext/spidermonkey/context.c +122 -0
  9. data/ext/spidermonkey/context.h +19 -0
  10. data/ext/spidermonkey/conversions.c +286 -0
  11. data/ext/spidermonkey/conversions.h +18 -0
  12. data/ext/spidermonkey/debugger.c +208 -0
  13. data/ext/spidermonkey/debugger.h +9 -0
  14. data/ext/spidermonkey/extconf.rb +25 -0
  15. data/ext/spidermonkey/extensions.c +37 -0
  16. data/ext/spidermonkey/extensions.h +12 -0
  17. data/ext/spidermonkey/global.c +40 -0
  18. data/ext/spidermonkey/global.h +11 -0
  19. data/ext/spidermonkey/idhash.c +16 -0
  20. data/ext/spidermonkey/idhash.h +8 -0
  21. data/ext/spidermonkey/immutable_node.c.erb +522 -0
  22. data/ext/spidermonkey/immutable_node.h +22 -0
  23. data/ext/spidermonkey/jroot.h +187 -0
  24. data/ext/spidermonkey/js_land_proxy.c +609 -0
  25. data/ext/spidermonkey/js_land_proxy.h +20 -0
  26. data/ext/spidermonkey/ruby_land_proxy.c +537 -0
  27. data/ext/spidermonkey/ruby_land_proxy.h +17 -0
  28. data/ext/spidermonkey/runtime.c +304 -0
  29. data/ext/spidermonkey/runtime.h +25 -0
  30. data/ext/spidermonkey/spidermonkey.c +20 -0
  31. data/ext/spidermonkey/spidermonkey.h +29 -0
  32. data/js/johnson/browser.js +9 -0
  33. data/js/johnson/browser/env.js +687 -0
  34. data/js/johnson/browser/jquery.js +3444 -0
  35. data/js/johnson/browser/xmlsax.js +1564 -0
  36. data/js/johnson/browser/xmlw3cdom.js +4189 -0
  37. data/js/johnson/cli.js +30 -0
  38. data/js/johnson/prelude.js +80 -0
  39. data/js/johnson/template.js +29 -0
  40. data/lib/hoe.rb +748 -0
  41. data/lib/johnson.rb +46 -0
  42. data/lib/johnson/cli.rb +7 -0
  43. data/lib/johnson/cli/options.rb +56 -0
  44. data/lib/johnson/error.rb +4 -0
  45. data/lib/johnson/nodes.rb +7 -0
  46. data/lib/johnson/nodes/binary_node.rb +64 -0
  47. data/lib/johnson/nodes/for.rb +14 -0
  48. data/lib/johnson/nodes/for_in.rb +12 -0
  49. data/lib/johnson/nodes/function.rb +13 -0
  50. data/lib/johnson/nodes/list.rb +27 -0
  51. data/lib/johnson/nodes/node.rb +68 -0
  52. data/lib/johnson/nodes/ternary_node.rb +20 -0
  53. data/lib/johnson/parser.rb +21 -0
  54. data/lib/johnson/parser/syntax_error.rb +13 -0
  55. data/lib/johnson/runtime.rb +55 -0
  56. data/lib/johnson/spidermonkey/context.rb +10 -0
  57. data/lib/johnson/spidermonkey/debugger.rb +67 -0
  58. data/lib/johnson/spidermonkey/immutable_node.rb +280 -0
  59. data/lib/johnson/spidermonkey/js_land_proxy.rb +62 -0
  60. data/lib/johnson/spidermonkey/mutable_tree_visitor.rb +233 -0
  61. data/lib/johnson/spidermonkey/ruby_land_proxy.rb +52 -0
  62. data/lib/johnson/spidermonkey/runtime.rb +94 -0
  63. data/lib/johnson/version.rb +4 -0
  64. data/lib/johnson/visitable.rb +16 -0
  65. data/lib/johnson/visitors.rb +4 -0
  66. data/lib/johnson/visitors/dot_visitor.rb +167 -0
  67. data/lib/johnson/visitors/ecma_visitor.rb +315 -0
  68. data/lib/johnson/visitors/enumerating_visitor.rb +115 -0
  69. data/lib/johnson/visitors/sexp_visitor.rb +172 -0
  70. data/lib/rails/init.rb +37 -0
  71. data/test/assets/index.html +38 -0
  72. data/test/assets/jquery_test.html +186 -0
  73. data/test/helper.rb +58 -0
  74. data/test/johnson/browser_test.rb +38 -0
  75. data/test/johnson/conversions/array_test.rb +32 -0
  76. data/test/johnson/conversions/boolean_test.rb +17 -0
  77. data/test/johnson/conversions/callable_test.rb +34 -0
  78. data/test/johnson/conversions/file_test.rb +15 -0
  79. data/test/johnson/conversions/nil_test.rb +20 -0
  80. data/test/johnson/conversions/number_test.rb +34 -0
  81. data/test/johnson/conversions/regexp_test.rb +24 -0
  82. data/test/johnson/conversions/string_test.rb +26 -0
  83. data/test/johnson/conversions/struct_test.rb +15 -0
  84. data/test/johnson/conversions/symbol_test.rb +19 -0
  85. data/test/johnson/conversions/thread_test.rb +24 -0
  86. data/test/johnson/error_test.rb +9 -0
  87. data/test/johnson/extensions_test.rb +56 -0
  88. data/test/johnson/nodes/array_literal_test.rb +57 -0
  89. data/test/johnson/nodes/array_node_test.rb +26 -0
  90. data/test/johnson/nodes/binary_node_test.rb +61 -0
  91. data/test/johnson/nodes/bracket_access_test.rb +16 -0
  92. data/test/johnson/nodes/delete_test.rb +11 -0
  93. data/test/johnson/nodes/do_while_test.rb +12 -0
  94. data/test/johnson/nodes/dot_accessor_test.rb +15 -0
  95. data/test/johnson/nodes/export_test.rb +9 -0
  96. data/test/johnson/nodes/for_test.rb +54 -0
  97. data/test/johnson/nodes/function_test.rb +71 -0
  98. data/test/johnson/nodes/if_test.rb +41 -0
  99. data/test/johnson/nodes/import_test.rb +13 -0
  100. data/test/johnson/nodes/label_test.rb +19 -0
  101. data/test/johnson/nodes/object_literal_test.rb +110 -0
  102. data/test/johnson/nodes/return_test.rb +16 -0
  103. data/test/johnson/nodes/semi_test.rb +8 -0
  104. data/test/johnson/nodes/switch_test.rb +55 -0
  105. data/test/johnson/nodes/ternary_test.rb +25 -0
  106. data/test/johnson/nodes/throw_test.rb +9 -0
  107. data/test/johnson/nodes/try_node_test.rb +59 -0
  108. data/test/johnson/nodes/typeof_test.rb +11 -0
  109. data/test/johnson/nodes/unary_node_test.rb +23 -0
  110. data/test/johnson/nodes/void_test.rb +11 -0
  111. data/test/johnson/nodes/while_test.rb +26 -0
  112. data/test/johnson/nodes/with_test.rb +10 -0
  113. data/test/johnson/prelude_test.rb +56 -0
  114. data/test/johnson/runtime_test.rb +46 -0
  115. data/test/johnson/spidermonkey/context_test.rb +21 -0
  116. data/test/johnson/spidermonkey/immutable_node_test.rb +34 -0
  117. data/test/johnson/spidermonkey/js_land_proxy_test.rb +236 -0
  118. data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +225 -0
  119. data/test/johnson/spidermonkey/runtime_test.rb +17 -0
  120. data/test/johnson/version_test.rb +13 -0
  121. data/test/johnson/visitors/dot_visitor_test.rb +39 -0
  122. data/test/johnson/visitors/enumerating_visitor_test.rb +12 -0
  123. data/test/johnson_test.rb +16 -0
  124. data/test/jquery_units/test.js +27 -0
  125. data/test/jquery_units/test_helper.js +197 -0
  126. data/test/jquery_units/units/ajax.js +795 -0
  127. data/test/jquery_units/units/core.js +1563 -0
  128. data/test/jquery_units/units/event.js +299 -0
  129. data/test/jquery_units/units/fx.js +427 -0
  130. data/test/jquery_units/units/offset.js +112 -0
  131. data/test/jquery_units/units/selector.js +224 -0
  132. data/test/jspec/helper.js +7 -0
  133. data/test/jspec/jspec.js +192 -0
  134. data/test/jspec/simple_spec.js +68 -0
  135. data/test/parser_test.rb +276 -0
  136. data/todo/.keep +0 -0
  137. metadata +501 -0
@@ -0,0 +1,20 @@
1
+ #ifndef JOHNSON_SPIDERMONKEY_JS_LAND_PROXY_H
2
+ #define JOHNSON_SPIDERMONKEY_JS_LAND_PROXY_H
3
+
4
+ #include "spidermonkey.h"
5
+ #include "runtime.h"
6
+
7
+ bool js_value_is_proxy(JohnsonRuntime* runtime, jsval maybe_proxy);
8
+ VALUE unwrap_js_land_proxy(JohnsonRuntime* runtime, jsval proxy);
9
+ JSBool make_js_land_proxy(JohnsonRuntime* runtime, VALUE value, jsval* retval);
10
+
11
+ #include "node.h"
12
+ typedef struct {
13
+ VALUE klass, rklass;
14
+ VALUE recv;
15
+ ID id, oid;
16
+ int safe_level;
17
+ NODE *body;
18
+ } METHOD;
19
+
20
+ #endif
@@ -0,0 +1,537 @@
1
+ #include "ruby_land_proxy.h"
2
+ #include "conversions.h"
3
+
4
+ DECLARE_RUBY_WRAPPER(rb_call_super, int argc; const VALUE* argv)
5
+ DEFINE_RUBY_WRAPPER(rb_call_super, rb_call_super, ARGLIST2(argc, argv))
6
+
7
+ DECLARE_RUBY_WRAPPER(rb_yield, VALUE v)
8
+ DEFINE_RUBY_WRAPPER(rb_yield, rb_yield, ARGLIST1(v))
9
+
10
+ static VALUE proxy_class = Qnil;
11
+
12
+ static inline JSBool get_jsval_for_proxy(RubyLandProxy* proxy, jsval* jv)
13
+ {
14
+ *jv = (jsval)(proxy->key);
15
+ return JS_TRUE;
16
+ }
17
+
18
+ static VALUE call_js_function_value(JohnsonRuntime* runtime, jsval target, jsval function, int argc, VALUE* argv)
19
+ {
20
+ JSContext * context = johnson_get_current_context(runtime);
21
+ PREPARE_RUBY_JROOTS(context, argc + 2);
22
+
23
+ JROOT(target);
24
+ JROOT(function);
25
+
26
+ assert(JSVAL_IS_OBJECT(target));
27
+
28
+ jsval args[argc];
29
+ jsval result;
30
+
31
+ int i;
32
+ for(i = 0; i < argc; ++i)
33
+ {
34
+ JCHECK(convert_to_js(runtime, argv[i], &(args[i])));
35
+ JROOT(args[i]);
36
+ }
37
+
38
+ JCHECK(JS_CallFunctionValue(context,
39
+ JSVAL_TO_OBJECT(target), function, (unsigned) argc, args, &result));
40
+
41
+ JRETURN_RUBY(CONVERT_TO_RUBY(runtime, result));
42
+ }
43
+
44
+ /*
45
+ * call-seq:
46
+ * [](name)
47
+ *
48
+ * Returns the property with +name+.
49
+ */
50
+ static VALUE
51
+ get(VALUE self, VALUE name)
52
+ {
53
+ RubyLandProxy* proxy;
54
+ Data_Get_Struct(self, RubyLandProxy, proxy);
55
+
56
+ JSContext * context = johnson_get_current_context(proxy->runtime);
57
+ PREPARE_RUBY_JROOTS(context, 1);
58
+
59
+ jsval proxy_value;
60
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
61
+ JROOT(proxy_value);
62
+
63
+ jsval js_value;
64
+
65
+ switch(TYPE(name)) {
66
+ case T_FIXNUM:
67
+ JCHECK(JS_GetElement(context,
68
+ JSVAL_TO_OBJECT(proxy_value), NUM2INT(name), &js_value));
69
+ break;
70
+ default:
71
+ Check_Type(name, T_STRING);
72
+ JCHECK(JS_GetProperty(context,
73
+ JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &js_value));
74
+ break;
75
+ }
76
+
77
+ JRETURN_RUBY(CONVERT_TO_RUBY(proxy->runtime, js_value));
78
+ }
79
+
80
+ /*
81
+ * call-seq:
82
+ * []=(name,value)
83
+ *
84
+ * Sets the property with +name+ to +value+.
85
+ */
86
+ static VALUE
87
+ set(VALUE self, VALUE name, VALUE value)
88
+ {
89
+ RubyLandProxy* proxy;
90
+ Data_Get_Struct(self, RubyLandProxy, proxy);
91
+ JSContext * context = johnson_get_current_context(proxy->runtime);
92
+
93
+ PREPARE_RUBY_JROOTS(context, 2);
94
+
95
+ jsval proxy_value;
96
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
97
+ JROOT(proxy_value);
98
+
99
+ jsval js_value;
100
+ JCHECK(convert_to_js(proxy->runtime, value, &js_value));
101
+
102
+ JROOT(js_value);
103
+
104
+ switch(TYPE(name)) {
105
+ case T_FIXNUM:
106
+ JCHECK(JS_SetElement(context,
107
+ JSVAL_TO_OBJECT(proxy_value), NUM2INT(name), &js_value));
108
+ break;
109
+ default:
110
+ Check_Type(name, T_STRING);
111
+ JCHECK(JS_SetProperty(context,
112
+ JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &js_value));
113
+ break;
114
+ }
115
+
116
+ JRETURN_RUBY(value);
117
+ }
118
+
119
+ /*
120
+ * call-seq:
121
+ * function?
122
+ *
123
+ * Returns <code>true</code> if this is a function.
124
+ */
125
+ static VALUE
126
+ function_p(VALUE self)
127
+ {
128
+ RubyLandProxy* proxy;
129
+ Data_Get_Struct(self, RubyLandProxy, proxy);
130
+ JSContext * context = johnson_get_current_context(proxy->runtime);
131
+ PREPARE_RUBY_JROOTS(context, 1);
132
+ jsval proxy_value;
133
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
134
+ JROOT(proxy_value);
135
+ JRETURN_RUBY(JS_TypeOfValue(context, proxy_value) == JSTYPE_FUNCTION ? Qtrue : Qfalse);
136
+ }
137
+
138
+ /*
139
+ * call-seq:
140
+ * respond_to?(symbol)
141
+ *
142
+ * Returns <code>true</code> if _obj_ responds to given method.
143
+ */
144
+ static VALUE
145
+ respond_to_p(VALUE self, VALUE sym)
146
+ {
147
+ RubyLandProxy* proxy;
148
+ Data_Get_Struct(self, RubyLandProxy, proxy);
149
+
150
+ JSContext * context = johnson_get_current_context(proxy->runtime);
151
+ PREPARE_RUBY_JROOTS(context, 2);
152
+
153
+ char* name = rb_id2name(SYM2ID(sym));
154
+
155
+ // assignment is always okay
156
+ if (name[strlen(name) - 1] == '=')
157
+ JRETURN_RUBY(Qtrue);
158
+
159
+ jsval proxy_value;
160
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
161
+ JROOT(proxy_value);
162
+
163
+ JSObject *obj;
164
+ JSBool found;
165
+
166
+ JCHECK(JS_ValueToObject(context, proxy_value, &obj));
167
+ JROOT(obj);
168
+
169
+ JCHECK(JS_HasProperty(context, obj, name, &found));
170
+
171
+ JRETURN_RUBY(found ? Qtrue : CALL_RUBY_WRAPPER(rb_call_super, 1, &sym));
172
+ }
173
+
174
+ /*
175
+ * call-seq:
176
+ * native_call(global, *args)
177
+ *
178
+ * Call as a function with given +global+ using *args.
179
+ */
180
+ static VALUE
181
+ native_call(int argc, VALUE* argv, VALUE self)
182
+ {
183
+ // FIXME: This should really call super#native_call.
184
+ if (!function_p(self))
185
+ rb_raise(rb_eRuntimeError, "This Johnson::SpiderMonkey::RubyLandProxy isn't a function.");
186
+
187
+ if (argc < 1)
188
+ rb_raise(rb_eArgError, "Target object required");
189
+
190
+ RubyLandProxy* proxy;
191
+ Data_Get_Struct(self, RubyLandProxy, proxy);
192
+ JSContext * context = johnson_get_current_context(proxy->runtime);
193
+
194
+ PREPARE_RUBY_JROOTS(context, 1);
195
+
196
+ jsval proxy_value;
197
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
198
+ JROOT(proxy_value);
199
+
200
+ jsval global;
201
+ JCHECK(convert_to_js(proxy->runtime, argv[0], &global));
202
+
203
+ JRETURN_RUBY(call_js_function_value(proxy->runtime, global, proxy_value, argc - 1, &(argv[1])));
204
+ }
205
+
206
+ static void
207
+ destroy_id_array(JSContext* context, void* data)
208
+ {
209
+ JS_DestroyIdArray(context, (JSIdArray*)data);
210
+ }
211
+
212
+ /*
213
+ * call-seq:
214
+ * each { |obj| block }
215
+ *
216
+ * Calls <em>block</em> with each item in the collection.
217
+ */
218
+ static VALUE
219
+ each(VALUE self)
220
+ {
221
+ RubyLandProxy* proxy;
222
+ Data_Get_Struct(self, RubyLandProxy, proxy);
223
+ JSContext * context = johnson_get_current_context(proxy->runtime);
224
+
225
+ PREPARE_RUBY_JROOTS(context, 5);
226
+
227
+ jsval proxy_value;
228
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
229
+ JROOT(proxy_value);
230
+
231
+ JSObject* value = JSVAL_TO_OBJECT(proxy_value);
232
+ JROOT(value);
233
+
234
+ // arrays behave like you'd expect, indexes in order
235
+ if (JS_IsArrayObject(context, value))
236
+ {
237
+ jsuint length;
238
+ JCHECK(JS_GetArrayLength(context, value, &length));
239
+
240
+ jsuint i = 0;
241
+ for (i = 0; i < length; ++i)
242
+ {
243
+ jsval element;
244
+ JCHECK(JS_GetElement(context, value, (signed) i, &element));
245
+ CALL_RUBY_WRAPPER(rb_yield, convert_to_ruby(proxy->runtime, element));
246
+ }
247
+ }
248
+ else
249
+ {
250
+ // not an array? behave like each on Hash; yield [key, value]
251
+ JSIdArray* ids = JS_Enumerate(context, value);
252
+ JCHECK(ids);
253
+
254
+ JCLEANUP(destroy_id_array, ids);
255
+
256
+ int i;
257
+ for (i = 0; i < ids->length; ++i)
258
+ {
259
+ jsval js_key, js_value;
260
+
261
+ JCHECK(JS_IdToValue(context, ids->vector[i], &js_key));
262
+ JROOT(js_key);
263
+
264
+ if (JSVAL_IS_STRING(js_key))
265
+ {
266
+ // regular properties have string keys
267
+ JCHECK(JS_GetProperty(context, value,
268
+ JS_GetStringBytes(JSVAL_TO_STRING(js_key)), &js_value));
269
+ }
270
+ else
271
+ {
272
+ // it's a numeric property, use array access
273
+ JCHECK(JS_GetElement(context, value,
274
+ JSVAL_TO_INT(js_key), &js_value));
275
+ }
276
+ JROOT(js_value);
277
+
278
+ VALUE key = CONVERT_TO_RUBY(proxy->runtime, js_key);
279
+ VALUE value = CONVERT_TO_RUBY(proxy->runtime, js_value);
280
+
281
+ CALL_RUBY_WRAPPER(rb_yield, rb_ary_new3(2, key, value));
282
+
283
+ JUNROOT(js_value);
284
+ JUNROOT(js_key);
285
+ }
286
+ }
287
+
288
+ JRETURN_RUBY(self);
289
+ }
290
+
291
+ /*
292
+ * call-seq:
293
+ * length
294
+ *
295
+ * Returns the length of the collection.
296
+ */
297
+ static VALUE
298
+ length(VALUE self)
299
+ {
300
+ RubyLandProxy* proxy;
301
+ Data_Get_Struct(self, RubyLandProxy, proxy);
302
+ JSContext * context = johnson_get_current_context(proxy->runtime);
303
+
304
+ PREPARE_RUBY_JROOTS(context, 2);
305
+
306
+ jsval proxy_value;
307
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
308
+ JROOT(proxy_value);
309
+
310
+ JSObject* value = JSVAL_TO_OBJECT(proxy_value);
311
+ JROOT(value);
312
+
313
+ if (JS_IsArrayObject(context, value))
314
+ {
315
+ jsuint length;
316
+ JCHECK(JS_GetArrayLength(context, value, &length));
317
+
318
+ JRETURN_RUBY(INT2FIX(length));
319
+ }
320
+ else
321
+ {
322
+ JSIdArray* ids = JS_Enumerate(context, value);
323
+ JCHECK(ids);
324
+ VALUE length = INT2FIX(ids->length);
325
+
326
+ JS_DestroyIdArray(context, ids);
327
+
328
+ JRETURN_RUBY(length);
329
+ }
330
+ }
331
+
332
+ /*
333
+ * call-seq:
334
+ * runtime
335
+ *
336
+ * Returns runtime.
337
+ */
338
+ static VALUE
339
+ runtime(VALUE self)
340
+ {
341
+ RubyLandProxy* proxy;
342
+ Data_Get_Struct(self, RubyLandProxy, proxy);
343
+ return (VALUE)JS_GetRuntimePrivate(proxy->runtime->js);
344
+ }
345
+
346
+ /*
347
+ * call-seq:
348
+ * function_property?(name)
349
+ *
350
+ * Returns <code>true</code> if +name+ is a function property.
351
+ */
352
+ static VALUE
353
+ function_property_p(VALUE self, VALUE name)
354
+ {
355
+ Check_Type(name, T_STRING);
356
+
357
+ RubyLandProxy* proxy;
358
+ Data_Get_Struct(self, RubyLandProxy, proxy);
359
+ JSContext * context = johnson_get_current_context(proxy->runtime);
360
+
361
+ PREPARE_RUBY_JROOTS(context, 2);
362
+
363
+ jsval proxy_value;
364
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
365
+ JROOT(proxy_value);
366
+
367
+ jsval js_value;
368
+
369
+ JCHECK(JS_GetProperty(context,
370
+ JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &js_value));
371
+
372
+ JROOT(js_value);
373
+
374
+ JSType type = JS_TypeOfValue(context, js_value);
375
+
376
+ JRETURN_RUBY(type == JSTYPE_FUNCTION ? Qtrue : Qfalse);
377
+ }
378
+
379
+ /*
380
+ * call-seq:
381
+ * call_function_property(name, arguments)
382
+ *
383
+ * Calls function +name+ with +arguments+.
384
+ */
385
+ static VALUE
386
+ call_function_property(int argc, VALUE* argv, VALUE self)
387
+ {
388
+ RubyLandProxy* proxy;
389
+ Data_Get_Struct(self, RubyLandProxy, proxy);
390
+ JSContext * context = johnson_get_current_context(proxy->runtime);
391
+
392
+ if (argc < 1)
393
+ rb_raise(rb_eArgError, "Function name required");
394
+
395
+ PREPARE_RUBY_JROOTS(context, 2);
396
+
397
+ jsval proxy_value;
398
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
399
+ JROOT(proxy_value);
400
+
401
+ jsval function;
402
+
403
+ JCHECK(JS_GetProperty(context,
404
+ JSVAL_TO_OBJECT(proxy_value), StringValueCStr(argv[0]), &function));
405
+
406
+ JROOT(function);
407
+
408
+ JSType funtype = JS_TypeOfValue(context, function);
409
+
410
+ // should never be anything but a function
411
+ if (funtype != JSTYPE_FUNCTION)
412
+ JERROR("Specified property \"%s\" isn't a function.", StringValueCStr(argv[0]));
413
+
414
+ JRETURN_RUBY(call_js_function_value(proxy->runtime, proxy_value, function, argc - 1, &(argv[1])));
415
+ }
416
+
417
+ /*
418
+ * call-seq:
419
+ * to_s
420
+ *
421
+ * Converts object to a string.
422
+ */
423
+ static VALUE to_s(VALUE self)
424
+ {
425
+ RubyLandProxy* proxy;
426
+ Data_Get_Struct(self, RubyLandProxy, proxy);
427
+ JSContext * context = johnson_get_current_context(proxy->runtime);
428
+
429
+ PREPARE_RUBY_JROOTS(context, 1);
430
+
431
+ jsval proxy_value;
432
+ JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
433
+ JROOT(proxy_value);
434
+
435
+ JSString* str = JS_ValueToString(context, proxy_value);
436
+ JRETURN_RUBY(convert_js_string_to_ruby(proxy->runtime, str));
437
+ }
438
+
439
+ ///////////////////////////////////////////////////////////////////////////
440
+ //// INFRASTRUCTURE BELOW HERE ////////////////////////////////////////////
441
+ ///////////////////////////////////////////////////////////////////////////
442
+
443
+ static void finalize(RubyLandProxy* proxy)
444
+ {
445
+ // could get finalized after the context has been freed
446
+ if (proxy->runtime && proxy->runtime->jsids)
447
+ {
448
+ // remove this proxy from the OID map
449
+ jsval proxy_value;
450
+ get_jsval_for_proxy(proxy, &proxy_value);
451
+ JS_HashTableRemove(proxy->runtime->jsids, (void *)proxy_value);
452
+ }
453
+
454
+ if (proxy->runtime)
455
+ {
456
+ // remove our GC handle on the JS value
457
+ JS_RemoveRootRT(proxy->runtime->js, &(proxy->key));
458
+ }
459
+
460
+ free(proxy);
461
+ }
462
+
463
+ bool ruby_value_is_proxy(VALUE maybe_proxy)
464
+ {
465
+ return proxy_class == CLASS_OF(maybe_proxy);
466
+ }
467
+
468
+ JSBool unwrap_ruby_land_proxy(JohnsonRuntime* runtime, VALUE wrapped, jsval* retval)
469
+ {
470
+ JSContext * context = johnson_get_current_context(runtime);
471
+ assert(ruby_value_is_proxy(wrapped));
472
+
473
+ PREPARE_JROOTS(context, 0);
474
+
475
+ RubyLandProxy* proxy;
476
+ Data_Get_Struct(wrapped, RubyLandProxy, proxy);
477
+
478
+ JCHECK(get_jsval_for_proxy(proxy, retval));
479
+ JRETURN;
480
+ }
481
+
482
+ VALUE make_ruby_land_proxy(JohnsonRuntime* runtime, jsval value, const char const* root_name)
483
+ {
484
+ VALUE id = (VALUE)JS_HashTableLookup(runtime->jsids, (void *)value);
485
+
486
+ if (id)
487
+ {
488
+ // if we already have a proxy, return it
489
+ return id;
490
+ }
491
+ else
492
+ {
493
+ // otherwise make one and cache it
494
+ RubyLandProxy* our_proxy;
495
+ VALUE proxy = Data_Make_Struct(proxy_class, RubyLandProxy, 0, finalize, our_proxy);
496
+
497
+ JSContext * context = johnson_get_current_context(runtime);
498
+
499
+ PREPARE_RUBY_JROOTS(context, 1);
500
+ JROOT(value);
501
+
502
+ our_proxy->runtime = runtime;
503
+ our_proxy->key = (void *)value;
504
+
505
+ // root the value for JS GC and lookups
506
+ JCHECK(JS_AddNamedRootRT(runtime->js, &(our_proxy->key), root_name));
507
+
508
+ // put the proxy OID in the id map
509
+ JCHECK(JS_HashTableAdd(runtime->jsids, (void *)value, (void *)proxy));
510
+
511
+ JRETURN_RUBY(proxy);
512
+ }
513
+ }
514
+
515
+ void init_Johnson_SpiderMonkey_Proxy(VALUE spidermonkey)
516
+ {
517
+ /* HACK: These comments are *only* to make RDoc happy.
518
+ VALUE johnson = rb_define_module("Johnson");
519
+ VALUE spidermonkey = rb_define_module_under(johnson, "SpiderMonkey");
520
+ */
521
+
522
+ /* RubyLandProxy class. */
523
+ proxy_class = rb_define_class_under(spidermonkey, "RubyLandProxy", rb_cObject);
524
+
525
+ rb_define_method(proxy_class, "[]", get, 1);
526
+ rb_define_method(proxy_class, "[]=", set, 2);
527
+ rb_define_method(proxy_class, "function?", function_p, 0);
528
+ rb_define_method(proxy_class, "respond_to?", respond_to_p, 1);
529
+ rb_define_method(proxy_class, "each", each, 0);
530
+ rb_define_method(proxy_class, "length", length, 0);
531
+ rb_define_method(proxy_class, "to_s", to_s, 0);
532
+
533
+ rb_define_private_method(proxy_class, "native_call", native_call, -1);
534
+ rb_define_private_method(proxy_class, "runtime", runtime, 0);
535
+ rb_define_private_method(proxy_class, "function_property?", function_property_p, 1);
536
+ rb_define_private_method(proxy_class, "call_function_property", call_function_property, -1);
537
+ }