Permalink
Browse files

Fixed Ruby value tracking/GC rooting.

Narrowed visibility for jsend.
Cleaned up some conversion stuff.


git-svn-id: svn+ssh://rubyforge.org/var/svn/johnson/trunk@120 54575175-8111-4fdf-a583-07ff49f40e23
  • Loading branch information...
1 parent 34f64d5 commit 3c24fe99307ba54772d8524bbb3191fedd270919 jbarnette committed Apr 2, 2008
@@ -25,34 +25,6 @@ static jsval convert_symbol_to_js(OurContext* context, VALUE symbol)
return js;
}
-static jsval convert_object_to_js(OurContext* context, VALUE object)
-{
- jsid id = (jsid)JS_HashTableLookup(context->rbids, (void *)rb_obj_id(object));
-
- if (id)
- {
- // if we already have a proxy, return it
- jsval js;
- assert(JS_IdToValue(context->js, id, &js));
- return js;
- }
- else
- {
- // otherwise make one and cache it
- jsval proxy = make_js_proxy(context, object);
-
- jsval newid;
- assert(JS_ValueToId(context->js, proxy, &newid));
-
- // put the proxy OID in the id map
- assert(JS_HashTableAdd(context->rbids, (void *)rb_obj_id(object), (void *)newid));
-
- // FIXME: root for GC
-
- return proxy;
- }
-}
-
jsval convert_to_js(OurContext* context, VALUE ruby)
{
switch(TYPE(ruby))
@@ -81,7 +53,7 @@ jsval convert_to_js(OurContext* context, VALUE ruby)
case T_CLASS:
case T_OBJECT:
- return convert_object_to_js(context, ruby);
+ return make_js_proxy(context, ruby);
case T_DATA: // keep T_DATA last for fall-through
if (ruby_value_is_proxy(ruby))
@@ -119,41 +91,6 @@ static JSBool jsval_is_a_symbol(OurContext* context, jsval maybe_symbol)
return is_a_symbol;
}
-VALUE convert_object_to_ruby(OurContext* context, jsval object)
-{
- if (JSVAL_NULL == object) return Qnil;
-
- if (jsval_is_a_symbol(context, object))
- return ID2SYM(rb_intern(JS_GetStringBytes(JS_ValueToString(context->js, object))));
-
- if (js_value_is_proxy(context, object))
- return unwrap_js_proxy(context, object);
-
- VALUE id = (VALUE)JS_HashTableLookup(context->jsids, (void *)object);
-
- if (id)
- {
- // if we already have a proxy, return it
- return rb_funcall(rb_const_get(rb_cObject,
- rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, id);
- }
- else
- {
- // otherwise make one and cache it
- VALUE proxy = make_ruby_proxy(context, object);
-
- // put the proxy OID in the id map
- assert(JS_HashTableAdd(context->jsids, (void *)object, (void *)rb_obj_id(proxy)));
-
- // root the value for JS GC
- char key[10];
- sprintf(key, "%x", (int)object);
- JS_SetProperty(context->js, context->gcthings, key, &object);
-
- return proxy;
- }
-}
-
VALUE convert_to_ruby(OurContext* context, jsval js)
{
switch (JS_TypeOfValue(context->js, js))
@@ -163,7 +100,16 @@ VALUE convert_to_ruby(OurContext* context, jsval js)
case JSTYPE_FUNCTION:
case JSTYPE_OBJECT:
- return convert_object_to_ruby(context, js);
+ if (JSVAL_NULL == js)
+ return Qnil;
+
+ if (jsval_is_a_symbol(context, js))
+ return ID2SYM(rb_intern(JS_GetStringBytes(JS_ValueToString(context->js, js))));
+
+ if (js_value_is_proxy(context, js))
+ return unwrap_js_proxy(context, js);
+
+ return make_ruby_proxy(context, js);
case JSTYPE_BOOLEAN:
return JSVAL_TRUE == js ? Qtrue : Qfalse;
@@ -16,27 +16,6 @@ static JSClass JSProxyClass = {
finalize
};
-static void finalize(JSContext* context, JSObject* obj)
-{
- // FIXME
-}
-// void Johnson_RubyProxy_finalize(JSContext *js_context, JSObject *obj)
-// {
-// VALUE ruby;
-// VALUE self = (VALUE)JS_GetContextPrivate(js_context);
-//
-// ruby = (VALUE)JS_GetInstancePrivate(js_context, obj, &gRubyProxyClass, NULL);
-// VALUE c_name = rb_funcall(
-// rb_funcall(self, rb_intern("class"), 0),
-// rb_intern("to_s"), 0);
-//
-// if(rb_ivar_defined(self, rb_intern("@converted_objects"))) {
-// rb_hash_delete( rb_iv_get(self, "@converted_objects"),
-// rb_funcall(ruby, rb_intern("object_id"), 0));
-// }
-// }
-//
-
static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
{
// pull out our Ruby object, which is embedded in js_context
@@ -164,36 +143,59 @@ JSBool js_value_is_proxy(OurContext* context, jsval maybe_proxy)
VALUE unwrap_js_proxy(OurContext* context, jsval proxy)
{
VALUE value;
- assert(value = (VALUE)JS_GetInstancePrivate(context->js, proxy, &JSProxyClass, NULL));
+ assert(value = (VALUE)JS_GetInstancePrivate(context->js, JSVAL_TO_OBJECT(proxy), &JSProxyClass, NULL));
return value;
}
-// static jsval convert_ruby_object_to_jsval(CombinedContext* context, VALUE ruby)
-// {
-// VALUE self = (VALUE)JS_GetContextPrivate(context->js);
-// JSObject * js = JS_NewObject(context->js, &gRubyProxyClass, NULL, NULL);
-// if(!js) Johnson_Error_raise("failed JS_NewObject");
-//
-// rb_hash_aset( rb_iv_get(self, "@converted_objects"),
-// rb_funcall(ruby, rb_intern("object_id"), 0),
-// ruby );
-//
-// if(JS_SetPrivate(context->js, js, (void*)ruby) == JS_FALSE)
-// Johnson_Error_raise("failed JS_SetPrivate");
-//
-// JS_DefineFunction(context->js, js, "__noSuchMethod__",
-// Johnson_RubyProxy_method_missing, 2, 0);
-//
-// return OBJECT_TO_JSVAL(js);
-// }
+static void finalize(JSContext* js_context, JSObject* obj)
+{
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
+
+ if (ruby_context)
+ {
+ OurContext* context;
+ Data_Get_Struct(ruby_context, OurContext, context);
+
+ VALUE self;
+ assert(self = (VALUE)JS_GetInstancePrivate(context->js, obj, &JSProxyClass, NULL));
+
+ // remove the proxy OID from the id map
+ JS_HashTableRemove(context->rbids, (void *)rb_obj_id(self));
+
+ // free up the ruby value for GC
+ rb_funcall(ruby_context, rb_intern("remove_gcthing"), 1, self);
+ }
+}
jsval make_js_proxy(OurContext* context, VALUE value)
{
- JSObject *js;
+ jsid id = (jsid)JS_HashTableLookup(context->rbids, (void *)rb_obj_id(value));
+ jsval js;
- assert(js = JS_NewObject(context->js, &JSProxyClass, NULL, NULL));
- assert(JS_SetPrivate(context->js, js, (void*)value));
- assert(JS_DefineFunction(context->js, js, "__noSuchMethod__", method_missing, 2, 0));
+ if (id)
+ {
+ assert(JS_IdToValue(context->js, id, &js));
+ }
+ else
+ {
+ JSObject *jsobj;
+
+ assert(jsobj = JS_NewObject(context->js, &JSProxyClass, NULL, NULL));
+ assert(JS_SetPrivate(context->js, jsobj, (void*)value));
+ assert(JS_DefineFunction(context->js, jsobj, "__noSuchMethod__", method_missing, 2, 0));
+
+ js = OBJECT_TO_JSVAL(jsobj);
+
+ jsval newid;
+ assert(JS_ValueToId(context->js, js, &newid));
+
+ // put the proxy OID in the id map
+ assert(JS_HashTableAdd(context->rbids, (void *)rb_obj_id(value), (void *)newid));
+
+ // root the ruby value for GC
+ VALUE ruby_context = (VALUE)JS_GetContextPrivate(context->js);
+ rb_funcall(ruby_context, rb_intern("add_gcthing"), 1, value);
+ }
- return OBJECT_TO_JSVAL(js);
+ return js;
}
@@ -220,7 +220,7 @@ call_function_property(int argc, VALUE* argv, VALUE self)
//// INFRASTRUCTURE BELOW HERE ////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
-static void deallocate(OurRubyProxy* proxy)
+static void finalize(OurRubyProxy* proxy)
{
// could get finalized after the context has been freed
if (proxy->context)
@@ -245,17 +245,6 @@ JSBool ruby_value_is_proxy(VALUE maybe_proxy)
return proxy_class == CLASS_OF(maybe_proxy);
}
-VALUE make_ruby_proxy(OurContext* context, jsval value)
-{
- OurRubyProxy* proxy;
- VALUE rbproxy = Data_Make_Struct(proxy_class, OurRubyProxy, 0, deallocate, proxy);
-
- proxy->value = value;
- proxy->context = context;
-
- return rbproxy;
-}
-
jsval unwrap_ruby_proxy(OurContext* context, VALUE wrapped)
{
assert(ruby_value_is_proxy(wrapped));
@@ -266,6 +255,38 @@ jsval unwrap_ruby_proxy(OurContext* context, VALUE wrapped)
return proxy->value;
}
+VALUE make_ruby_proxy(OurContext* context, jsval value)
+{
+ VALUE id = (VALUE)JS_HashTableLookup(context->jsids, (void *)value);
+
+ if (id)
+ {
+ // if we already have a proxy, return it
+ // FIXME: don't depend on ObjectSpace!
+ return rb_funcall(rb_const_get(rb_cObject,
+ rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, id);
+ }
+ else
+ {
+ // otherwise make one and cache it
+ OurRubyProxy* our_proxy;
+ VALUE proxy = Data_Make_Struct(proxy_class, OurRubyProxy, 0, finalize, our_proxy);
+
+ our_proxy->value = value;
+ our_proxy->context = context;
+
+ // put the proxy OID in the id map
+ assert(JS_HashTableAdd(context->jsids, (void *)value, (void *)rb_obj_id(proxy)));
+
+ // root the value for JS GC
+ char key[10];
+ sprintf(key, "%x", (int)value);
+ JS_SetProperty(context->js, context->gcthings, key, &value);
+
+ return proxy;
+ }
+}
+
void init_Johnson_SpiderMonkey_Proxy(VALUE spidermonkey)
{
proxy_class = rb_define_class_under(spidermonkey, "RubyProxy", rb_cObject);
@@ -4,11 +4,24 @@ class Context # native
def initialize
@gcthings = {}
end
+
+ protected
+ # called from js_proxy.c:method_missing
def jsend(target, symbol, args)
block = args.pop if args.last.is_a?(RubyProxy) && args.last.function?
target.__send__(symbol, *args, &block)
end
+
+ # called from js_proxy.c:make_js_proxy
+ def add_gcthing(thing)
+ @gcthings[thing.object_id] = thing
+ end
+
+ # called from js_proxy.c:finalize
+ def remove_gcthing(thing)
+ @gcthings.delete(thing.object_id)
+ end
end
end
end
@@ -5,34 +5,13 @@ module SpiderMonkey
class ContextTest < Johnson::TestCase
def setup
@context = Johnson::SpiderMonkey::Context.new
- @context.evaluate(Johnson::PRELUDE)
end
def test_provides_basic_context_interface
assert(@context.respond_to?(:evaluate))
assert(@context.respond_to?(:[]))
assert(@context.respond_to?(:[]=))
end
-
- class Foo
- def gets_an_unspecified_block
- block_given?
- end
-
- def runs_block(arg, &block)
- yield(arg)
- end
- end
-
- def test_jsend_deals_with_blocks
- func = @context.evaluate("function() {}")
- assert(@context.jsend(Foo.new, :gets_an_unspecified_block, [func]))
- end
-
- def test_jsend_deals_with_specified_blocks
- func = @context.evaluate("function(x) { return x * 2 }")
- assert_equal(4, @context.jsend(Foo.new, :runs_block, [2, func]))
- end
end
end
end

0 comments on commit 3c24fe9

Please sign in to comment.