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.
- data/CHANGELOG.rdoc +12 -0
- data/Manifest.txt +6 -4
- data/README.rdoc +2 -8
- data/Rakefile +11 -11
- data/bin/johnson +1 -3
- data/ext/spidermonkey/context.c +3 -2
- data/ext/spidermonkey/conversions.c +61 -27
- data/ext/spidermonkey/conversions.h +13 -0
- data/ext/spidermonkey/debugger.c +13 -5
- data/ext/spidermonkey/debugger.h +1 -0
- data/ext/spidermonkey/extconf.rb +2 -3
- data/ext/spidermonkey/jroot.h +11 -1
- data/ext/spidermonkey/js_land_proxy.c +21 -11
- data/ext/spidermonkey/ruby_land_proxy.c +116 -41
- data/ext/spidermonkey/ruby_land_proxy.h +21 -0
- data/ext/spidermonkey/runtime.c +85 -19
- data/ext/spidermonkey/runtime.h +2 -0
- data/ext/spidermonkey/spidermonkey.c +3 -1
- data/lib/johnson.rb +19 -27
- data/lib/johnson/cli.rb +2 -1
- data/{js/johnson → lib/johnson/js}/cli.js +0 -0
- data/lib/johnson/js/core.js +34 -0
- data/lib/johnson/js/prelude.js +149 -0
- data/lib/johnson/ruby_land_proxy.rb +113 -0
- data/lib/johnson/runtime.rb +92 -33
- data/lib/johnson/spidermonkey.rb +12 -0
- data/lib/johnson/spidermonkey/js_land_proxy.rb +10 -8
- data/lib/johnson/spidermonkey/ruby_land_proxy.rb +10 -47
- data/lib/johnson/spidermonkey/runtime.rb +11 -31
- data/test/johnson/conversions/array_test.rb +41 -3
- data/test/johnson/conversions/string_test.rb +12 -0
- data/test/johnson/custom_conversions_test.rb +50 -0
- data/test/johnson/prelude_test.rb +23 -0
- data/test/johnson/runtime_test.rb +82 -2
- data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +17 -1
- data/test/johnson/spidermonkey/runtime_test.rb +24 -0
- data/vendor/spidermonkey/jsprf.c +2 -0
- metadata +22 -9
- data/js/johnson/prelude.js +0 -80
- data/lib/johnson/version.rb +0 -3
- 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
|
-
|
563
|
-
|
564
|
-
|
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
|
-
|
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
|
-
|
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
|
-
*
|
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
|
-
|
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
|
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
|
-
|
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
|
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(
|
202
|
+
* native_call(this, *args)
|
182
203
|
*
|
183
|
-
* Call as a function with given +
|
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 { |
|
242
|
+
* each {| element | block }
|
243
|
+
* each {| name, value | block }
|
221
244
|
*
|
222
|
-
* Calls <em>block</em> with each item in
|
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,
|
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
|
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
|
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+
|
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
|
-
|
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
|
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(
|
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 (
|
418
|
-
JERROR("Specified property \"%s\" isn't a function.", StringValueCStr(
|
453
|
+
if (!JS_ObjectIsFunction(context, function))
|
454
|
+
JERROR("Specified property \"%s\" isn't a function.", StringValueCStr(name));
|
455
|
+
|
456
|
+
REMOVE_JROOTS;
|
419
457
|
|
420
|
-
|
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(
|
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
|
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
|
-
|
548
|
+
RubyLandProxy * our_proxy = (RubyLandProxy *)JS_HashTableLookup(runtime->jsids, (void *)value);
|
491
549
|
|
492
|
-
if (
|
550
|
+
if (our_proxy)
|
493
551
|
{
|
494
552
|
// if we already have a proxy, return it
|
495
|
-
return
|
553
|
+
return apply_conversions(our_proxy->self);
|
496
554
|
}
|
497
555
|
else
|
498
556
|
{
|
499
557
|
// otherwise make one and cache it
|
500
|
-
RubyLandProxy
|
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 *)
|
516
|
-
|
517
|
-
|
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",
|
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);
|
data/ext/spidermonkey/runtime.c
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
#include "global.h"
|
3
3
|
#include "idhash.h"
|
4
4
|
#include "conversions.h"
|
5
|
-
#include "
|
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 *
|
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
|
-
|
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
|
-
*
|
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,
|
60
|
+
* set_trap(script, line_num, block)
|
60
61
|
*
|
61
|
-
* Set
|
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(
|
82
|
+
* native_compile(script_string, filename, linenum)
|
81
83
|
*
|
82
|
-
* Compile
|
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
|
-
|
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(
|
124
|
+
* evaluate_compiled_script(script_proxy)
|
124
125
|
*
|
125
|
-
* Evaluate +
|
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
|
-
*
|
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
|
-
*
|
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
|
-
|
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
|
-
|
394
|
+
rb_define_method(klass, "set_trap", set_trap, 3);
|
329
395
|
rb_define_private_method(klass, "clear_trap", clear_trap, 2);
|
330
396
|
}
|