johnson 1.1.2 → 1.2.0

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 (41) hide show
  1. data/CHANGELOG.rdoc +12 -0
  2. data/Manifest.txt +6 -4
  3. data/README.rdoc +2 -8
  4. data/Rakefile +11 -11
  5. data/bin/johnson +1 -3
  6. data/ext/spidermonkey/context.c +3 -2
  7. data/ext/spidermonkey/conversions.c +61 -27
  8. data/ext/spidermonkey/conversions.h +13 -0
  9. data/ext/spidermonkey/debugger.c +13 -5
  10. data/ext/spidermonkey/debugger.h +1 -0
  11. data/ext/spidermonkey/extconf.rb +2 -3
  12. data/ext/spidermonkey/jroot.h +11 -1
  13. data/ext/spidermonkey/js_land_proxy.c +21 -11
  14. data/ext/spidermonkey/ruby_land_proxy.c +116 -41
  15. data/ext/spidermonkey/ruby_land_proxy.h +21 -0
  16. data/ext/spidermonkey/runtime.c +85 -19
  17. data/ext/spidermonkey/runtime.h +2 -0
  18. data/ext/spidermonkey/spidermonkey.c +3 -1
  19. data/lib/johnson.rb +19 -27
  20. data/lib/johnson/cli.rb +2 -1
  21. data/{js/johnson → lib/johnson/js}/cli.js +0 -0
  22. data/lib/johnson/js/core.js +34 -0
  23. data/lib/johnson/js/prelude.js +149 -0
  24. data/lib/johnson/ruby_land_proxy.rb +113 -0
  25. data/lib/johnson/runtime.rb +92 -33
  26. data/lib/johnson/spidermonkey.rb +12 -0
  27. data/lib/johnson/spidermonkey/js_land_proxy.rb +10 -8
  28. data/lib/johnson/spidermonkey/ruby_land_proxy.rb +10 -47
  29. data/lib/johnson/spidermonkey/runtime.rb +11 -31
  30. data/test/johnson/conversions/array_test.rb +41 -3
  31. data/test/johnson/conversions/string_test.rb +12 -0
  32. data/test/johnson/custom_conversions_test.rb +50 -0
  33. data/test/johnson/prelude_test.rb +23 -0
  34. data/test/johnson/runtime_test.rb +82 -2
  35. data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +17 -1
  36. data/test/johnson/spidermonkey/runtime_test.rb +24 -0
  37. data/vendor/spidermonkey/jsprf.c +2 -0
  38. metadata +22 -9
  39. data/js/johnson/prelude.js +0 -80
  40. data/lib/johnson/version.rb +0 -3
  41. data/lib/rails/init.rb +0 -37
@@ -559,17 +559,22 @@ static void finalize(JSContext* js_context, JSObject* obj)
559
559
 
560
560
  JSBool make_js_land_proxy(JohnsonRuntime* runtime, VALUE value, jsval* retval)
561
561
  {
562
- *retval = (jsval)JS_HashTableLookup(runtime->rbids, (void *)value);
563
-
564
- if (*retval)
562
+ jsval base_value = (jsval)JS_HashTableLookup(runtime->rbids, (void *)value);
563
+
564
+ JSContext * context = johnson_get_current_context(runtime);
565
+ PREPARE_JROOTS(context, 2);
566
+
567
+ jsval johnson = JSVAL_NULL;
568
+ JCHECK(evaluate_js_property_expression(runtime, "Johnson", &johnson));
569
+ JROOT(johnson);
570
+
571
+ if (base_value)
565
572
  {
566
- return JS_TRUE;
573
+ JCHECK(JS_CallFunctionName(context, johnson, "applyConversions", 1, &base_value, retval));
574
+ JRETURN;
567
575
  }
568
576
  else
569
577
  {
570
- JSContext * context = johnson_get_current_context(runtime);
571
- PREPARE_JROOTS(context, 1);
572
-
573
578
  JSObject *jsobj;
574
579
 
575
580
  JSClass *klass = &JSLandProxyClass;
@@ -596,15 +601,20 @@ JSBool make_js_land_proxy(JohnsonRuntime* runtime, VALUE value, jsval* retval)
596
601
  JCHECK(JS_DefineFunction(context, jsobj, "toArray", to_array, 0, 0));
597
602
  JCHECK(JS_DefineFunction(context, jsobj, "toString", to_string, 0, 0));
598
603
 
599
- *retval = OBJECT_TO_JSVAL(jsobj);
604
+ base_value = OBJECT_TO_JSVAL(jsobj);
600
605
 
601
- // put the proxy OID in the id map
602
- JCHECK(JS_HashTableAdd(runtime->rbids, (void *)value, (void *)(*retval)));
603
-
604
606
  // root the ruby value for GC
605
607
  VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(runtime->js);
606
608
  rb_funcall(ruby_runtime, rb_intern("add_gcthing"), 1, value);
607
609
 
610
+ jsval wrapped_value = JSVAL_NULL;
611
+ JCHECK(JS_CallFunctionName(context, johnson, "applyWrappers", 1, &base_value, &wrapped_value));
612
+
613
+ // put the proxy OID in the id map
614
+ JCHECK(JS_HashTableAdd(runtime->rbids, (void *)value, (void *)(wrapped_value)));
615
+
616
+ JCHECK(JS_CallFunctionName(context, johnson, "applyConversions", 1, &wrapped_value, retval));
617
+
608
618
  JRETURN;
609
619
  }
610
620
  }
@@ -7,7 +7,16 @@ DEFINE_RUBY_WRAPPER(rb_call_super, rb_call_super, ARGLIST2(argc, argv))
7
7
  DECLARE_RUBY_WRAPPER(rb_yield, VALUE v)
8
8
  DEFINE_RUBY_WRAPPER(rb_yield, rb_yield, ARGLIST1(v))
9
9
 
10
+ DECLARE_RUBY_WRAPPER(rb_check_type, VALUE o; int t)
11
+ DEFINE_VOID_RUBY_WRAPPER(rb_check_type, rb_check_type, ARGLIST2(o, t))
12
+
13
+ DEFINE_RUBY_WRAPPER(rb_string_value, rb_string_value, ARGLIST1(v))
14
+ DEFINE_VOID_RUBY_WRAPPER(rb_string_value_cstr, rb_string_value_cstr, ARGLIST1(v))
15
+
16
+ DEFINE_RUBY_WRAPPER(make_ruby_land_proxy, make_ruby_land_proxy, ARGLIST3(runtime, value, root_name))
17
+
10
18
  static VALUE proxy_class = Qnil;
19
+ static VALUE script_class = Qnil;
11
20
 
12
21
  static inline JSBool get_jsval_for_proxy(RubyLandProxy* proxy, jsval* jv)
13
22
  {
@@ -45,7 +54,8 @@ static VALUE call_js_function_value(JohnsonRuntime* runtime, jsval target, jsval
45
54
  * call-seq:
46
55
  * [](name)
47
56
  *
48
- * Returns the property with +name+.
57
+ * Retrieves the current value of the +name+ property of this JavaScript
58
+ * object.
49
59
  */
50
60
  static VALUE
51
61
  get(VALUE self, VALUE name)
@@ -67,8 +77,10 @@ get(VALUE self, VALUE name)
67
77
  JCHECK(JS_GetElement(context,
68
78
  JSVAL_TO_OBJECT(proxy_value), (jsint)(NUM2INT(name)), &js_value));
69
79
  break;
80
+ case T_SYMBOL:
81
+ name = RB_FUNCALL_0(name, RB_INTERN("to_s"));
70
82
  default:
71
- Check_Type(name, T_STRING);
83
+ CALL_RUBY_WRAPPER(rb_string_value_cstr, &name);
72
84
  JCHECK(JS_GetProperty(context,
73
85
  JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &js_value));
74
86
  break;
@@ -79,9 +91,9 @@ get(VALUE self, VALUE name)
79
91
 
80
92
  /*
81
93
  * call-seq:
82
- * []=(name,value)
94
+ * []=(name, value)
83
95
  *
84
- * Sets the property with +name+ to +value+.
96
+ * Sets this JavaScript object's +name+ property to +value+.
85
97
  */
86
98
  static VALUE
87
99
  set(VALUE self, VALUE name, VALUE value)
@@ -106,8 +118,10 @@ set(VALUE self, VALUE name, VALUE value)
106
118
  JCHECK(JS_SetElement(context,
107
119
  JSVAL_TO_OBJECT(proxy_value), (jsint)(NUM2INT(name)), &js_value));
108
120
  break;
121
+ case T_SYMBOL:
122
+ name = RB_FUNCALL_0(name, RB_INTERN("to_s"));
109
123
  default:
110
- Check_Type(name, T_STRING);
124
+ CALL_RUBY_WRAPPER(rb_string_value_cstr, &name);
111
125
  JCHECK(JS_SetProperty(context,
112
126
  JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &js_value));
113
127
  break;
@@ -118,9 +132,9 @@ set(VALUE self, VALUE name, VALUE value)
118
132
 
119
133
  /*
120
134
  * call-seq:
121
- * function?
135
+ * function?()
122
136
  *
123
- * Returns <code>true</code> if this is a function.
137
+ * Returns <code>true</code> if this JavaScript object is a function.
124
138
  */
125
139
  static VALUE
126
140
  function_p(VALUE self)
@@ -135,11 +149,18 @@ function_p(VALUE self)
135
149
  JRETURN_RUBY(JS_TypeOfValue(context, proxy_value) == JSTYPE_FUNCTION ? Qtrue : Qfalse);
136
150
  }
137
151
 
152
+ static VALUE
153
+ callable_test_p(VALUE self, VALUE proxy)
154
+ {
155
+ return function_p(proxy);
156
+ }
157
+
138
158
  /*
139
159
  * call-seq:
140
160
  * respond_to?(symbol)
141
161
  *
142
- * Returns <code>true</code> if _obj_ responds to given method.
162
+ * Returns <code>true</code> if this JavaScript object responds to the
163
+ * named method.
143
164
  */
144
165
  static VALUE
145
166
  respond_to_p(int argc, const VALUE* argv, VALUE self)
@@ -178,9 +199,10 @@ respond_to_p(int argc, const VALUE* argv, VALUE self)
178
199
 
179
200
  /*
180
201
  * call-seq:
181
- * native_call(global, *args)
202
+ * native_call(this, *args)
182
203
  *
183
- * Call as a function with given +global+ using *args.
204
+ * Call this Ruby Object as a function, with the given +this+ object and
205
+ * arguments. Equivalent to the call method in JavaScript.
184
206
  */
185
207
  static VALUE
186
208
  native_call(int argc, VALUE* argv, VALUE self)
@@ -217,9 +239,12 @@ destroy_id_array(JSContext* context, void* data)
217
239
 
218
240
  /*
219
241
  * call-seq:
220
- * each { |obj| block }
242
+ * each {| element | block }
243
+ * each {| name, value | block }
221
244
  *
222
- * Calls <em>block</em> with each item in the collection.
245
+ * Calls <em>block</em> with each item in this JavaScript array, or with
246
+ * each +name+/+value+ pair (like a Hash) for any other JavaScript
247
+ * object.
223
248
  */
224
249
  static VALUE
225
250
  each(VALUE self)
@@ -248,7 +273,7 @@ each(VALUE self)
248
273
  {
249
274
  jsval element;
250
275
  JCHECK(JS_GetElement(context, value, (signed) i, &element));
251
- CALL_RUBY_WRAPPER(rb_yield, convert_to_ruby(proxy->runtime, element));
276
+ CALL_RUBY_WRAPPER(rb_yield, CONVERT_TO_RUBY(proxy->runtime, element));
252
277
  }
253
278
  }
254
279
  else
@@ -296,9 +321,10 @@ each(VALUE self)
296
321
 
297
322
  /*
298
323
  * call-seq:
299
- * length
324
+ * length()
300
325
  *
301
- * Returns the length of the collection.
326
+ * Returns the number of entries in the JavaScript array, or the number
327
+ * of properties on the JavaScript object.
302
328
  */
303
329
  static VALUE
304
330
  length(VALUE self)
@@ -337,9 +363,10 @@ length(VALUE self)
337
363
 
338
364
  /*
339
365
  * call-seq:
340
- * runtime
366
+ * runtime()
341
367
  *
342
- * Returns runtime.
368
+ * Returns the Johnson::SpiderMonkey::Runtime against which this object
369
+ * is registered.
343
370
  */
344
371
  static VALUE
345
372
  runtime(VALUE self)
@@ -353,13 +380,17 @@ runtime(VALUE self)
353
380
  * call-seq:
354
381
  * function_property?(name)
355
382
  *
356
- * Returns <code>true</code> if +name+ is a function property.
383
+ * Returns <code>true</code> if this JavaScript object's +name+ property
384
+ * is a function.
357
385
  */
358
386
  static VALUE
359
387
  function_property_p(VALUE self, VALUE name)
360
388
  {
361
- Check_Type(name, T_STRING);
362
-
389
+ if (TYPE(name) == T_SYMBOL)
390
+ name = rb_funcall(name, rb_intern("to_s"), 0);
391
+
392
+ rb_string_value_cstr(&name);
393
+
363
394
  RubyLandProxy* proxy;
364
395
  Data_Get_Struct(self, RubyLandProxy, proxy);
365
396
  JSContext * context = johnson_get_current_context(proxy->runtime);
@@ -386,7 +417,11 @@ function_property_p(VALUE self, VALUE name)
386
417
  * call-seq:
387
418
  * call_function_property(name, arguments)
388
419
  *
389
- * Calls function +name+ with +arguments+.
420
+ * Calls this JavaScript object's +name+ method, passing the given
421
+ * arguments.
422
+ *
423
+ * Equivalent to:
424
+ * proxy[name].native_call(proxy, *arguments)
390
425
  */
391
426
  static VALUE
392
427
  call_function_property(int argc, VALUE* argv, VALUE self)
@@ -405,26 +440,30 @@ call_function_property(int argc, VALUE* argv, VALUE self)
405
440
  JROOT(proxy_value);
406
441
 
407
442
  jsval function;
443
+
444
+ VALUE name = argv[0];
445
+ CALL_RUBY_WRAPPER(rb_string_value_cstr, &name);
408
446
 
409
447
  JCHECK(JS_GetProperty(context,
410
- JSVAL_TO_OBJECT(proxy_value), StringValueCStr(argv[0]), &function));
448
+ JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &function));
411
449
 
412
450
  JROOT(function);
413
451
 
414
- JSType funtype = JS_TypeOfValue(context, function);
415
-
416
452
  // should never be anything but a function
417
- if (funtype != JSTYPE_FUNCTION)
418
- JERROR("Specified property \"%s\" isn't a function.", StringValueCStr(argv[0]));
453
+ if (!JS_ObjectIsFunction(context, function))
454
+ JERROR("Specified property \"%s\" isn't a function.", StringValueCStr(name));
455
+
456
+ REMOVE_JROOTS;
419
457
 
420
- JRETURN_RUBY(call_js_function_value(proxy->runtime, proxy_value, function, argc - 1, &(argv[1])));
458
+ return call_js_function_value(proxy->runtime, proxy_value, function, argc - 1, &(argv[1]));
421
459
  }
422
460
 
423
461
  /*
424
462
  * call-seq:
425
- * to_s
463
+ * to_s()
426
464
  *
427
- * Converts object to a string.
465
+ * Converts the JavaScript object to a string, using its toString method
466
+ * if available.
428
467
  */
429
468
  static VALUE to_s(VALUE self)
430
469
  {
@@ -439,7 +478,7 @@ static VALUE to_s(VALUE self)
439
478
  JROOT(proxy_value);
440
479
 
441
480
  JSString* str = JS_ValueToString(context, proxy_value);
442
- JRETURN_RUBY(convert_js_string_to_ruby(proxy->runtime, str));
481
+ JRETURN_RUBY(CONVERT_JS_STRING_TO_RUBY(proxy->runtime, str));
443
482
  }
444
483
 
445
484
  ///////////////////////////////////////////////////////////////////////////
@@ -468,7 +507,26 @@ static void finalize(RubyLandProxy* proxy)
468
507
 
469
508
  bool ruby_value_is_proxy(VALUE maybe_proxy)
470
509
  {
471
- return proxy_class == CLASS_OF(maybe_proxy);
510
+ return rb_obj_is_kind_of(maybe_proxy, proxy_class);
511
+ }
512
+
513
+ bool ruby_value_is_script_proxy(VALUE maybe_proxy)
514
+ {
515
+ return rb_obj_is_kind_of(maybe_proxy, script_class);
516
+ }
517
+
518
+ VALUE apply_wrappers(VALUE proxy)
519
+ {
520
+ VALUE johnson = rb_const_get(rb_mKernel, rb_intern("Johnson"));
521
+ VALUE johnson_proxy = rb_const_get(johnson, rb_intern("RubyLandProxy"));
522
+ return rb_funcall(johnson_proxy, rb_intern("apply_wrappers"), 1, proxy);
523
+ }
524
+
525
+ VALUE apply_conversions(VALUE proxy)
526
+ {
527
+ VALUE johnson = rb_const_get(rb_mKernel, rb_intern("Johnson"));
528
+ VALUE johnson_proxy = rb_const_get(johnson, rb_intern("RubyLandProxy"));
529
+ return rb_funcall(johnson_proxy, rb_intern("apply_conversions"), 1, proxy);
472
530
  }
473
531
 
474
532
  JSBool unwrap_ruby_land_proxy(JohnsonRuntime* runtime, VALUE wrapped, jsval* retval)
@@ -487,34 +545,41 @@ JSBool unwrap_ruby_land_proxy(JohnsonRuntime* runtime, VALUE wrapped, jsval* ret
487
545
 
488
546
  VALUE make_ruby_land_proxy(JohnsonRuntime* runtime, jsval value, const char const* root_name)
489
547
  {
490
- VALUE id = (VALUE)JS_HashTableLookup(runtime->jsids, (void *)value);
548
+ RubyLandProxy * our_proxy = (RubyLandProxy *)JS_HashTableLookup(runtime->jsids, (void *)value);
491
549
 
492
- if (id)
550
+ if (our_proxy)
493
551
  {
494
552
  // if we already have a proxy, return it
495
- return id;
553
+ return apply_conversions(our_proxy->self);
496
554
  }
497
555
  else
498
556
  {
499
557
  // otherwise make one and cache it
500
- RubyLandProxy* our_proxy;
501
- VALUE proxy = Data_Make_Struct(proxy_class, RubyLandProxy, 0, finalize, our_proxy);
558
+ VALUE proxy = Data_Make_Struct((strncmp(root_name, "JSScriptProxy", strlen("JSScriptProxy")) ? proxy_class : script_class), RubyLandProxy, 0, finalize, our_proxy);
502
559
 
503
560
  JSContext * context = johnson_get_current_context(runtime);
504
561
 
505
562
  PREPARE_RUBY_JROOTS(context, 1);
506
563
  JROOT(value);
507
564
 
565
+ VALUE rb_runtime = (VALUE)JS_GetRuntimePrivate(runtime->js);
566
+ rb_iv_set(proxy, "@runtime", rb_runtime);
567
+
508
568
  our_proxy->runtime = runtime;
509
569
  our_proxy->key = (void *)value;
570
+ our_proxy->self = proxy;
510
571
 
511
572
  // root the value for JS GC and lookups
512
573
  JCHECK(JS_AddNamedRootRT(runtime->js, &(our_proxy->key), root_name));
513
574
 
514
575
  // put the proxy OID in the id map
515
- JCHECK(JS_HashTableAdd(runtime->jsids, (void *)value, (void *)proxy));
516
-
517
- JRETURN_RUBY(proxy);
576
+ JCHECK(JS_HashTableAdd(runtime->jsids, (void *)value, (void *)our_proxy));
577
+
578
+ VALUE final_proxy = JPROTECT(apply_wrappers, proxy);
579
+
580
+ our_proxy->self = final_proxy;
581
+
582
+ JRETURN_RUBY(JPROTECT(apply_conversions, final_proxy));
518
583
  }
519
584
  }
520
585
 
@@ -525,8 +590,11 @@ void init_Johnson_SpiderMonkey_Proxy(VALUE spidermonkey)
525
590
  VALUE spidermonkey = rb_define_module_under(johnson, "SpiderMonkey");
526
591
  */
527
592
 
593
+ VALUE johnson = rb_const_get(rb_mKernel, rb_intern("Johnson"));
594
+ VALUE johnson_proxy = rb_const_get(johnson, rb_intern("RubyLandProxy"));
595
+
528
596
  /* RubyLandProxy class. */
529
- proxy_class = rb_define_class_under(spidermonkey, "RubyLandProxy", rb_cObject);
597
+ proxy_class = rb_define_class_under(spidermonkey, "RubyLandProxy", johnson_proxy);
530
598
 
531
599
  rb_define_method(proxy_class, "[]", get, 1);
532
600
  rb_define_method(proxy_class, "[]=", set, 2);
@@ -536,8 +604,15 @@ void init_Johnson_SpiderMonkey_Proxy(VALUE spidermonkey)
536
604
  rb_define_method(proxy_class, "length", length, 0);
537
605
  rb_define_method(proxy_class, "to_s", to_s, 0);
538
606
 
539
- rb_define_private_method(proxy_class, "native_call", native_call, -1);
540
607
  rb_define_private_method(proxy_class, "runtime", runtime, 0);
541
608
  rb_define_private_method(proxy_class, "function_property?", function_property_p, 1);
542
609
  rb_define_private_method(proxy_class, "call_function_property", call_function_property, -1);
610
+
611
+ VALUE callable = rb_define_module_under(proxy_class, "Callable");
612
+ rb_define_singleton_method(callable, "test?", callable_test_p, 1);
613
+ rb_define_private_method(callable, "native_call", native_call, -1);
614
+
615
+ rb_funcall(johnson_proxy, rb_intern("insert_wrapper"), 1, callable);
616
+
617
+ script_class = rb_define_class_under(spidermonkey, "RubyLandScript", proxy_class);
543
618
  }
@@ -4,12 +4,33 @@
4
4
  #include "spidermonkey.h"
5
5
  #include "runtime.h"
6
6
 
7
+ #ifdef LEAK_ROOT_NAMES
8
+ #define LEAKY_ROOT_NAME(static_string, dynamic_detail) \
9
+ ({\
10
+ const char * const _leaky_root__detail = (dynamic_detail);\
11
+ char * _leaky_root__leaked = malloc(strlen(static_string) + strlen(_leaky_root__detail) + 2);\
12
+ strcpy(_leaky_root__leaked, static_string);\
13
+ _leaky_root__leaked[strlen(static_string)] = ':';\
14
+ strcpy(_leaky_root__leaked + strlen(static_string) + 1, _leaky_root__detail);\
15
+ _leaky_root__leaked;\
16
+ })
17
+ #else
18
+ #define LEAKY_ROOT_NAME(static_string, dynamic_detail) (static_string)
19
+ #endif
20
+
21
+ DECLARE_RUBY_WRAPPER(make_ruby_land_proxy, JohnsonRuntime* runtime; jsval value; const char const* root_name)
22
+
23
+ DECLARE_RUBY_WRAPPER(rb_string_value, VALUE v)
24
+ DECLARE_RUBY_WRAPPER(rb_string_value_cstr, VALUE v)
25
+
7
26
  typedef struct {
8
27
  void* key;
9
28
  JohnsonRuntime* runtime;
29
+ VALUE self;
10
30
  } RubyLandProxy;
11
31
 
12
32
  bool ruby_value_is_proxy(VALUE maybe_proxy);
33
+ bool ruby_value_is_script_proxy(VALUE maybe_proxy);
13
34
  JSBool unwrap_ruby_land_proxy(JohnsonRuntime* runtime, VALUE proxy, jsval* retval);
14
35
  VALUE make_ruby_land_proxy(JohnsonRuntime* runtime, jsval value, const char const* root_name);
15
36
  void init_Johnson_SpiderMonkey_Proxy(VALUE spidermonkey);
@@ -2,13 +2,13 @@
2
2
  #include "global.h"
3
3
  #include "idhash.h"
4
4
  #include "conversions.h"
5
- #include "jsdbgapi.h"
5
+ #include "debugger.h"
6
6
  #include "jroot.h"
7
7
  #include "ruby_land_proxy.h"
8
8
 
9
9
  /*
10
10
  * call-seq:
11
- * global
11
+ * global()
12
12
  *
13
13
  * Returns the global object used for this context.
14
14
  */
@@ -19,14 +19,15 @@ static VALUE global(VALUE self)
19
19
  return convert_to_ruby(runtime, OBJECT_TO_JSVAL(runtime->global));
20
20
  }
21
21
 
22
- static JSTrapStatus trap_handler( JSContext *UNUSED(context),
22
+ static JSTrapStatus trap_handler( JSContext *context,
23
23
  JSScript *UNUSED(script),
24
24
  jsbytecode *UNUSED(pc),
25
25
  jsval *UNUSED(rval),
26
26
  void *block_closure )
27
27
  {
28
+ PREPARE_JROOTS(context, 0);
28
29
  VALUE block = (VALUE)block_closure;
29
- rb_funcall(block, rb_intern("call"), 0);
30
+ RB_FUNCALL_0(block, rb_intern("call"));
30
31
  return JSTRAP_CONTINUE;
31
32
  }
32
33
 
@@ -34,7 +35,7 @@ static JSTrapStatus trap_handler( JSContext *UNUSED(context),
34
35
  * call-seq:
35
36
  * clear_trap(script, line_num)
36
37
  *
37
- * Set the trap at +script+ and +line_num+ to +block+
38
+ * Clear the trap previously set at +line_num+ of +script+.
38
39
  */
39
40
  static VALUE clear_trap(VALUE self, VALUE script, VALUE linenum)
40
41
  {
@@ -56,9 +57,10 @@ static VALUE clear_trap(VALUE self, VALUE script, VALUE linenum)
56
57
 
57
58
  /*
58
59
  * call-seq:
59
- * set_trap(script, parsecode, block)
60
+ * set_trap(script, line_num, block)
60
61
  *
61
- * Set the trap at +script+ and +parsecode+ to +block+
62
+ * Set a trap to invoke +block+ when execution of +script+ reaches
63
+ * +line_num+.
62
64
  */
63
65
  static VALUE set_trap(VALUE self, VALUE script, VALUE linenum, VALUE block)
64
66
  {
@@ -77,9 +79,10 @@ static VALUE set_trap(VALUE self, VALUE script, VALUE linenum, VALUE block)
77
79
 
78
80
  /*
79
81
  * call-seq:
80
- * native_compile(script, filename, linenum)
82
+ * native_compile(script_string, filename, linenum)
81
83
  *
82
- * Compile +script+ with +filename+ using +linenum+
84
+ * Compile the JavaScript code in +script_string+, marked as originating
85
+ * from +filename+ starting at +linenum+.
83
86
  */
84
87
  static VALUE native_compile(VALUE self, VALUE script, VALUE filename, VALUE linenum)
85
88
  {
@@ -113,20 +116,23 @@ static VALUE native_compile(VALUE self, VALUE script, VALUE filename, VALUE line
113
116
 
114
117
  JSObject * script_object = JS_NewScriptObject(context, compiled_js);
115
118
 
116
- PREPARE_RUBY_JROOTS(context, 1);
117
- JROOT(script_object);
118
- JRETURN_RUBY(make_ruby_land_proxy(runtime, OBJECT_TO_JSVAL(script_object), "JSScriptProxy"));
119
+ return make_ruby_land_proxy(runtime, OBJECT_TO_JSVAL(script_object), LEAKY_ROOT_NAME("JSScriptProxy", RTEST(filename) ? RSTRING(rb_inspect(filename))->ptr : "(?)"));
119
120
  }
120
121
 
121
122
  /*
122
123
  * call-seq:
123
- * evaluate_compiled_script(script)
124
+ * evaluate_compiled_script(script_proxy)
124
125
  *
125
- * Evaluate +script+
126
+ * Evaluate previously compiled +script_proxy+, returning the final
127
+ * result from that script.
126
128
  */
127
129
  static VALUE evaluate_compiled_script(VALUE self, VALUE compiled_script)
128
130
  {
129
131
  JohnsonRuntime* runtime;
132
+
133
+ if (!ruby_value_is_script_proxy(compiled_script))
134
+ rb_raise(rb_eArgError, "Compiled JS Script expected");
135
+
130
136
  Data_Get_Struct(self, JohnsonRuntime, runtime);
131
137
 
132
138
  JSContext * context = johnson_get_current_context(runtime);
@@ -163,12 +169,16 @@ static VALUE evaluate_compiled_script(VALUE self, VALUE compiled_script)
163
169
  return convert_to_ruby(runtime, js);
164
170
  }
165
171
 
172
+ #ifdef JS_GC_ZEAL
166
173
  /*
167
174
  * call-seq:
168
175
  * gc_zeal=(level)
169
176
  *
170
177
  * Sets the GC zeal.
171
- * 0 = normal, 1 = Very Frequent, 2 = Extremely Frequent
178
+ *
179
+ * 0:: Normal
180
+ * 1:: Very Frequent
181
+ * 2:: Extremely Frequent
172
182
  */
173
183
  static VALUE
174
184
  set_gc_zeal(VALUE self, VALUE zeal)
@@ -182,12 +192,33 @@ set_gc_zeal(VALUE self, VALUE zeal)
182
192
 
183
193
  return zeal;
184
194
  }
195
+ #endif
196
+
197
+ /*
198
+ * call-seq:
199
+ * gc()
200
+ *
201
+ * Manually initiates a SpiderMonkey Garbage Collection run.
202
+ */
203
+ static VALUE
204
+ gc(VALUE self)
205
+ {
206
+ JohnsonRuntime* runtime;
207
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
208
+
209
+ JSContext* context = johnson_get_current_context(runtime);
210
+
211
+ JS_GC(context);
212
+
213
+ return Qnil;
214
+ }
185
215
 
186
216
  /*
187
217
  * call-seq:
188
218
  * debugger=(debugger)
189
219
  *
190
- * Sets a debugger object
220
+ * Directs the runtime to install a full set of debug hooks, using the
221
+ * given +debugger+, which must be a Johnson::SpiderMonkey::Debugger.
191
222
  */
192
223
  static VALUE
193
224
  set_debugger(VALUE self, VALUE debugger)
@@ -195,6 +226,9 @@ set_debugger(VALUE self, VALUE debugger)
195
226
  JohnsonRuntime* runtime;
196
227
  JSDebugHooks* debug_hooks;
197
228
 
229
+ if (!ruby_value_is_debugger(debugger))
230
+ rb_raise(rb_eTypeError, "Expected Johnson::SpiderMonkey::Debugger instance");
231
+
198
232
  rb_iv_set(self, "@debugger", debugger);
199
233
  Data_Get_Struct(self, JohnsonRuntime, runtime);
200
234
  Data_Get_Struct(debugger, JSDebugHooks, debug_hooks);
@@ -247,6 +281,13 @@ JSBool gc_callback(JSContext *context, JSGCStatus status)
247
281
  return JS_FALSE;
248
282
  }
249
283
 
284
+ /**
285
+ * call-seq:
286
+ * initialize_native(options)
287
+ *
288
+ * Create the underlying SpiderMonkey runtime. This must be called
289
+ * first, and only once. Called by +initialize+ by default.
290
+ */
250
291
  static VALUE
251
292
  initialize_native(VALUE self, VALUE UNUSED(options))
252
293
  {
@@ -262,7 +303,7 @@ initialize_native(VALUE self, VALUE UNUSED(options))
262
303
 
263
304
  JSContext* context = johnson_get_current_context(runtime);
264
305
 
265
- if (runtime->global = JS_GetGlobalObject(context))
306
+ if ((runtime->global = JS_GetGlobalObject(context)))
266
307
  return self;
267
308
  }
268
309
 
@@ -289,6 +330,16 @@ JSContext* johnson_get_current_context(JohnsonRuntime * runtime)
289
330
  return context->js;
290
331
  }
291
332
 
333
+ static int proxy_cleanup_enumerator(JSHashEntry *entry, int i, void* arg)
334
+ {
335
+ JohnsonRuntime *runtime = (JohnsonRuntime*)(arg);
336
+ // entry->key is jsval; entry->value is RubyLandProxy*
337
+ RubyLandProxy * proxy = (RubyLandProxy *)(entry->value);
338
+ JS_RemoveRootRT(runtime->js, &(proxy->key));
339
+ proxy->runtime = NULL;
340
+ return 0;
341
+ }
342
+
292
343
  static void deallocate(JohnsonRuntime* runtime)
293
344
  {
294
345
  // our gc callback can create ruby objects, so disable it
@@ -303,6 +354,10 @@ static void deallocate(JohnsonRuntime* runtime)
303
354
  iterator = NULL;
304
355
  }
305
356
 
357
+ JSContext* cleanup = JS_NewContext(runtime->js, 8192L);
358
+ JS_HashTableEnumerateEntries(runtime->jsids, proxy_cleanup_enumerator, runtime);
359
+ JS_DestroyContext(cleanup);
360
+
306
361
  JS_DestroyRuntime(runtime->js);
307
362
  free(runtime);
308
363
  }
@@ -315,16 +370,27 @@ static VALUE allocate(VALUE klass)
315
370
 
316
371
  void init_Johnson_SpiderMonkey_Runtime(VALUE spidermonkey)
317
372
  {
318
- VALUE klass = rb_define_class_under(spidermonkey, "Runtime", rb_cObject);
373
+ /* HACK: These comments are *only* to make RDoc happy.
374
+ VALUE johnson = rb_define_module("Johnson");
375
+ VALUE spidermonkey = rb_define_module_under(johnson, "SpiderMonkey");
376
+ */
377
+
378
+ VALUE johnson = rb_const_get(rb_mKernel, rb_intern("Johnson"));
379
+ VALUE johnson_runtime = rb_const_get(johnson, rb_intern("Runtime"));
380
+
381
+ VALUE klass = rb_define_class_under(spidermonkey, "Runtime", johnson_runtime);
319
382
 
320
383
  rb_define_alloc_func(klass, allocate);
321
384
  rb_define_private_method(klass, "initialize_native", initialize_native, 1);
322
385
 
323
386
  rb_define_method(klass, "global", global, 0);
324
387
  rb_define_method(klass, "debugger=", set_debugger, 1);
388
+ rb_define_method(klass, "gc", gc, 0);
389
+ #ifdef JS_GC_ZEAL
325
390
  rb_define_method(klass, "gc_zeal=", set_gc_zeal, 1);
391
+ #endif
326
392
  rb_define_method(klass, "evaluate_compiled_script", evaluate_compiled_script, 1);
327
393
  rb_define_private_method(klass, "native_compile", native_compile, 3);
328
- rb_define_private_method(klass, "set_trap", set_trap, 3);
394
+ rb_define_method(klass, "set_trap", set_trap, 3);
329
395
  rb_define_private_method(klass, "clear_trap", clear_trap, 2);
330
396
  }