Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Cleaned up proxying of callable (Proc, Method) Ruby objects.

- added JSLandCallableProxyClass with a 'call' hook
- removed all the js_function_proxy bits
- no more JSFunctions defined, we do callable objects instead
  • Loading branch information...
commit 73ad6377f9d56b6c2f41519df448d81d7005c309 1 parent cfcba6d
@jbarnette authored
View
17 ext/spidermonkey/conversions.c
@@ -1,6 +1,5 @@
#include "conversions.h"
#include "js_land_proxy.h"
-#include "js_function_proxy.h"
#include "ruby_land_proxy.h"
static jsval convert_float_or_bignum_to_js(OurContext* context, VALUE float_or_bignum)
@@ -79,11 +78,8 @@ jsval convert_to_js(OurContext* context, VALUE ruby)
if (ruby_value_is_proxy(ruby))
return unwrap_ruby_land_proxy(context, ruby);
- if (rb_cProc == CLASS_OF(ruby))
- return make_js_function_proxy(context, ruby);
-
- // UNIMPLEMENTED BELOW THIS LINE
-
+ if (rb_cProc == rb_class_of(ruby) || rb_cMethod == rb_class_of(ruby))
+ return make_js_land_proxy(context, ruby);
default:
Johnson_Error_raise("unknown ruby type in switch");
@@ -92,7 +88,7 @@ jsval convert_to_js(OurContext* context, VALUE ruby)
return JSVAL_NULL;
}
-static VALUE make_ruby_regexp(OurContext* context, jsval regexp)
+static VALUE convert_regexp_to_ruby(OurContext* context, jsval regexp)
{
JSRegExp* re = (JSRegExp*)JS_GetPrivate(context->js, JSVAL_TO_OBJECT(regexp));
@@ -132,11 +128,6 @@ VALUE convert_to_ruby(OurContext* context, jsval js)
return Qnil;
case JSTYPE_FUNCTION:
- if (js_value_is_function_proxy(context, js))
- return unwrap_js_function_proxy(context, js);
-
- // NOTE: intentional fall-through to JSTYPE_OBJECT
-
case JSTYPE_OBJECT:
if (OBJECT_TO_JSVAL(context->global) == js)
// global gets special treatment, since the Prelude might not be loaded
@@ -150,7 +141,7 @@ VALUE convert_to_ruby(OurContext* context, jsval js)
return unwrap_js_land_proxy(context, js);
if (js_value_is_regexp(context, js))
- return make_ruby_regexp(context, js);
+ return convert_regexp_to_ruby(context, js);
return make_ruby_land_proxy(context, js);
View
90 ext/spidermonkey/js_function_proxy.c
@@ -1,90 +0,0 @@
-#include "js_function_proxy.h"
-
-static JSBool call_proc(JSContext *js_context, JSObject* this, uintN argc, jsval* argv, jsval* ret)
-{
- VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
-
- OurContext* context;
- Data_Get_Struct(ruby_context, OurContext, context);
-
- JSFunction* callee = JS_ValueToFunction(js_context, JS_ARGV_CALLEE(argv));
-
- jsval oid;
-
- assert(JS_GetProperty(context->js,
- JS_GetFunctionObject(callee),
- JS_FUNCTION_PROXY_PROPERTY, &oid));
-
- VALUE args = rb_ary_new3(1, INT2NUM(JSVAL_TO_INT(oid)));
-
- int i;
-
- for (i = 0; i < argc; ++i)
- rb_ary_push(args, convert_to_ruby(context, argv[i]));
-
- *ret = convert_to_js(context,
- rb_funcall2(Johnson_SpiderMonkey_JSLandProxy(),
- rb_intern("call_proc_by_oid"), RARRAY_LEN(args), RARRAY_PTR(args)));
-
- return JS_TRUE;
-}
-
-JSBool js_value_is_function_proxy(OurContext* context, jsval maybe_function_proxy)
-{
- JSBool flag;
-
- JS_HasProperty(context->js,
- JSVAL_TO_OBJECT(maybe_function_proxy),
- JS_FUNCTION_PROXY_PROPERTY, &flag);
-
- return flag;
-}
-
-VALUE unwrap_js_function_proxy(OurContext* context, jsval function_proxy)
-{
- VALUE ruby_context = (VALUE)JS_GetContextPrivate(context->js);
- jsval oid;
-
- assert(JS_GetProperty(context->js,
- JS_GetFunctionObject(JS_ValueToFunction(context->js, function_proxy)),
- JS_FUNCTION_PROXY_PROPERTY, &oid));
-
- return rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
- rb_intern("id2ref"), 1, INT2NUM(JSVAL_TO_INT(oid)));
-}
-
-
-jsval make_js_function_proxy(OurContext* context, VALUE proc)
-{
- jsid id = (jsid)JS_HashTableLookup(context->rbids, (void *)rb_obj_id(proc));
- jsval js;
-
- if (id)
- {
- assert(JS_IdToValue(context->js, id, &js));
- }
- else
- {
- JSObject* function = JS_GetFunctionObject(
- JS_NewFunction(context->js, call_proc, 0, 0, NULL, ""));
-
- jsval oid = INT_TO_JSVAL(NUM2INT(rb_obj_id(proc)));
- assert(JS_SetProperty(context->js, function, JS_FUNCTION_PROXY_PROPERTY, &oid));
-
- js = OBJECT_TO_JSVAL(function);
-
- 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(proc), (void *)newid));
-
- // root the ruby value for GC
- // FIXME: this is pretty much copy/paste from js_land_proxy.c
- // FIXME: no custom finalizer on JSFunction, so never freed!
- VALUE ruby_context = (VALUE)JS_GetContextPrivate(context->js);
- rb_funcall(ruby_context, rb_intern("add_gcthing"), 1, proc);
- }
-
- return js;
-}
View
15 ext/spidermonkey/js_function_proxy.h
@@ -1,15 +0,0 @@
-#ifndef JOHNSON_SPIDERMONKEY_JS_FUNCTION_PROXY_H
-#define JOHNSON_SPIDERMONKEY_JS_FUNCTION_PROXY_H
-
-#include "spidermonkey.h"
-#include "context.h"
-
-#ifndef JS_FUNCTION_PROXY_PROPERTY
-#define JS_FUNCTION_PROXY_PROPERTY "__rubyProcOID"
-#endif
-
-JSBool js_value_is_function_proxy(OurContext* context, jsval maybe_function_proxy);
-VALUE unwrap_js_function_proxy(OurContext* context, jsval function_proxy);
-jsval make_js_function_proxy(OurContext* context, VALUE proc);
-
-#endif
View
59 ext/spidermonkey/js_land_proxy.c
@@ -4,6 +4,7 @@ static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
static JSBool set(JSContext* context, JSObject* obj, jsval id, jsval* retval);
static JSBool construct(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval);
static JSBool resolve(JSContext *js_context, JSObject *obj, jsval id, uintN flags, JSObject **objp);
+static JSBool call(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval);
static void finalize(JSContext* context, JSObject* obj);
static JSClass JSLandProxyClass = {
@@ -34,6 +35,21 @@ static JSClass JSLandClassProxyClass = {
construct
};
+static JSClass JSLandCallableProxyClass = {
+ "JSLandCallableProxy", JSCLASS_HAS_PRIVATE,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ finalize,
+ NULL,
+ NULL,
+ call
+};
+
static JSBool autovivified_p(VALUE ruby_context, VALUE self, char* name)
{
return rb_funcall(Johnson_SpiderMonkey_JSLandProxy(), rb_intern("autovivified?"), 2,
@@ -209,8 +225,7 @@ static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
else if (method_p(self, name))
{
*retval = convert_to_js(context,
- rb_funcall(Johnson_SpiderMonkey_JSLandProxy(), rb_intern("wrap"),
- 2, self, rb_str_new2(name)));
+ rb_funcall(self, rb_intern("method"), 1, rb_str_new2(name)));
}
// else it's undefined (JS_VOID) by default
@@ -352,10 +367,37 @@ static JSBool method_missing(JSContext* js_context, JSObject* obj, uintN argc, j
return JS_TRUE;
}
+static JSBool call(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
+{
+ VALUE ruby_context;
+ assert(ruby_context = (VALUE)JS_GetContextPrivate(js_context));
+
+ OurContext* context;
+ Data_Get_Struct(ruby_context, OurContext, context);
+
+ VALUE self;// = convert_to_ruby(context, JS_ARGV_CALLEE(argv));
+ assert(self = (VALUE)JS_GetInstancePrivate(context->js, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), &JSLandCallableProxyClass, NULL));
+
+ VALUE args = rb_ary_new();
+ int i;
+
+ for (i = 0; i < argc; ++i)
+ rb_ary_push(args, convert_to_ruby(context, argv[i]));
+
+ *retval = convert_to_js(context,
+ rb_funcall(Johnson_SpiderMonkey_JSLandProxy(), rb_intern("send_with_possible_block"),
+ 3, self, ID2SYM(rb_intern("call")), args));
+
+ return JS_TRUE;
+}
+
JSBool js_value_is_proxy(OurContext* context, jsval maybe_proxy)
{
JSClass* klass = JS_GET_CLASS(context->js, JSVAL_TO_OBJECT(maybe_proxy));
- return &JSLandProxyClass == klass || &JSLandClassProxyClass == klass;
+
+ return &JSLandProxyClass == klass
+ || &JSLandClassProxyClass == klass
+ || &JSLandCallableProxyClass == klass;
}
VALUE unwrap_js_land_proxy(OurContext* context, jsval proxy)
@@ -406,14 +448,23 @@ jsval make_js_land_proxy(OurContext* context, VALUE value)
JSClass *klass = &JSLandProxyClass;
if (T_CLASS == TYPE(value)) klass = &JSLandClassProxyClass;
+ // FIXME: hack; should happen in Rubyland
if (T_STRUCT == TYPE(value))
rb_funcall(Johnson_SpiderMonkey_JSLandProxy(),
rb_intern("treat_all_properties_as_methods"), 1, value);
+
+ JSBool callable_p = rb_class_of(value) == rb_cMethod
+ || rb_class_of(value) == rb_cProc;
+ if (callable_p)
+ klass = &JSLandCallableProxyClass;
+
assert(jsobj = JS_NewObject(context->js, klass, NULL, NULL));
assert(JS_SetPrivate(context->js, jsobj, (void*)value));
- assert(JS_DefineFunction(context->js, jsobj, "__noSuchMethod__", method_missing, 2, 0));
+ if (!callable_p)
+ assert(JS_DefineFunction(context->js, jsobj,
+ "__noSuchMethod__", method_missing, 2, 0));
js = OBJECT_TO_JSVAL(jsobj);
View
2  test/johnson/conversions/proc_test.rb
@@ -20,7 +20,7 @@ def test_procs_roundtrip
def test_proc_js_function_proxy_gets_reused
@context[:k] = k = lambda { |x| x }
@context[:kk] = k
- assert_js("k == kk")
+ assert_js("k === kk")
end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.