示例#1
0
static JSBool call(JSContext* js_context, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* retval)
{
  VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
  
  JohnsonContext* context;
  JohnsonRuntime* runtime;
  Data_Get_Struct(ruby_context, JohnsonContext, context);

  VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
  Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);

  PREPARE_JROOTS(js_context, 0);
  
  VALUE self = (VALUE)JS_GetInstancePrivate(context->js, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), &JSLandCallableProxyClass, NULL);
  
  VALUE args = rb_ary_new();  

  uintN i;
  for (i = 0; i < argc; ++i)
    rb_ary_push(args, CONVERT_TO_RUBY(runtime, argv[i]));
  
  JCHECK(call_ruby_from_js(runtime, retval, Johnson_SpiderMonkey_JSLandProxy(),
    rb_intern("send_with_possible_block"), 3, self, ID2SYM(rb_intern("call")), args));
  JRETURN;
}
示例#2
0
static JSBool method_missing(JSContext* js_context, JSObject* obj, uintN argc, jsval* argv, jsval* retval)
{
  VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
  
  JohnsonContext* context;
  JohnsonRuntime* runtime;
  Data_Get_Struct(ruby_context, JohnsonContext, context);

  VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
  Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);

  PREPARE_JROOTS(js_context, 0);
    
  VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
  
  assert(argc >= 2);

  char* key = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
  VALUE ruby_id = rb_intern(key);
  
  // FIXME: this is horrible and lazy, to_a comes from enumerable on proxy (argv[1] is a JSArray)
  VALUE args;
  JCHECK(call_ruby_from_js2(runtime, &args, CONVERT_TO_RUBY(runtime, argv[1]), rb_intern("to_a"), 0));

  JCHECK(call_ruby_from_js(runtime, retval, Johnson_SpiderMonkey_JSLandProxy(),
    rb_intern("send_with_possible_block"), 3, self, ID2SYM(ruby_id), args));

  JRETURN;
}
示例#3
0
// called for lazily resolved properties, which should go away
static JSBool get_and_destroy_resolved_property(
  JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
{
  PREPARE_JROOTS(js_context, 1);
  JROOT(id);
  char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  JCHECK(JS_DeleteProperty(js_context, obj, name));
  JCHECK(get(js_context, obj, id, retval));
  JRETURN;
}
示例#4
0
JSBool unwrap_ruby_land_proxy(JohnsonRuntime* runtime, VALUE wrapped, jsval* retval)
{
  JSContext * context = johnson_get_current_context(runtime);
  assert(ruby_value_is_proxy(wrapped));
  
  PREPARE_JROOTS(context, 0);

  RubyLandProxy* proxy;
  Data_Get_Struct(wrapped, RubyLandProxy, proxy);
  
  JCHECK(get_jsval_for_proxy(proxy, retval));
  JRETURN;
}
示例#5
0
static JSBool convert_regexp_to_js(JohnsonRuntime* runtime, VALUE regexp, jsval* retval)
{
  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_JROOTS(context, 0);
  VALUE source = rb_funcall(regexp, rb_intern("source"), 0);
  jsint options = (jsint)(NUM2INT(rb_funcall(regexp, rb_intern("options"), 0)));

  JSObject* obj = JS_NewRegExpObject(context,
        StringValuePtr(source),
        (size_t) StringValueLen(source),
        (unsigned) options);

  JCHECK(obj);
  *retval = OBJECT_TO_JSVAL(obj);
  JRETURN;
}
示例#6
0
static JSBool to_array(JSContext* js_context, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* retval)
{
  VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);

  JohnsonContext* context;
  JohnsonRuntime* runtime;
  Data_Get_Struct(ruby_context, JohnsonContext, context);

  VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
  Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);

  PREPARE_JROOTS(js_context, 0);

  VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);

  JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("to_a"), 0));
  JRETURN;
}
示例#7
0
static JSBool convert_symbol_to_js(JohnsonRuntime* runtime, VALUE symbol, jsval* retval)
{
  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_JROOTS(context, 2);

  VALUE to_s = CALL_RUBY_WRAPPER(rb_funcall_0, symbol, rb_intern("to_s"), 0);
  jsval name = STRING_TO_JSVAL(JS_NewStringCopyN(context, StringValuePtr(to_s), (size_t) StringValueLen(to_s)));

  JROOT(name);

  // calls Johnson.symbolize(name) in JS-land. See lib/prelude.js

  jsval nsJohnson;    
  JCHECK(JS_GetProperty(context, runtime->global, "Johnson", &nsJohnson));
  JROOT(nsJohnson);

  JCHECK(JS_CallFunctionName(context, JSVAL_TO_OBJECT(nsJohnson), "symbolize", 1, &name, retval));

  JRETURN;
}
示例#8
0
static JSBool resolve(JSContext *js_context, JSObject *obj, jsval id, uintN UNUSED(flags), JSObject **objp)
{
  VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
  
  JohnsonContext* context;
  Data_Get_Struct(ruby_context, JohnsonContext, context);

  PREPARE_JROOTS(js_context, 1);
  JROOT(id);
  
  char* name = JS_GetStringBytes(JS_ValueToString(js_context, id));

  if (respond_to_p(js_context, obj, name))
  {
    JCHECK(JS_DefineProperty(js_context, obj, name, JSVAL_VOID,
        get_and_destroy_resolved_property, set, JSPROP_ENUMERATE));

    *objp = obj;
  }

  JRETURN;
}
示例#9
0
JSBool convert_to_js(JohnsonRuntime* runtime, VALUE ruby, jsval* retval)
{
  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_JROOTS(context, 0);
  switch(TYPE(ruby))
  {
    case T_NONE:
      JERROR("I don't know how to handle T_NONE.");
      JRETURN;

    case T_ICLASS:
      JERROR("I don't know how to handle T_ICLASS.");
      JRETURN;

    case T_MATCH:
      JERROR("I don't know how to handle T_MATCH.");
      JRETURN;

    case T_BLKTAG:
      JERROR("I don't know how to handle T_BLKTAG.");
      JRETURN;

    case T_NODE:
      JERROR("I don't know how to handle T_NODE | T_MASK.");
      JRETURN;

    case T_UNDEF:
      JERROR("I don't know how to handle T_UNDEF.");
      JRETURN;

    case T_VARMAP:
      JERROR("I don't know how to handle T_VARMAP.");
      JRETURN;

    case T_NIL:
      *retval = JSVAL_NULL;
      JRETURN;

    case T_TRUE:
      *retval = JSVAL_TRUE;
      JRETURN;
    
    case T_FALSE:
      *retval = JSVAL_FALSE;
      JRETURN;

    case T_STRING:
      {
        JSString* str = JS_NewStringCopyN(context, StringValuePtr(ruby), (size_t) StringValueLen(ruby));
        JCHECK(str);
        *retval = STRING_TO_JSVAL(str);
        JRETURN;
      }

    case T_FIXNUM:
      {
        long val = NUM2LONG(ruby);
        if (val >= JSVAL_INT_MIN && val <= JSVAL_INT_MAX)
        {
          *retval = INT_TO_JSVAL((jsint)val);
          JRETURN;
        }
      }

    case T_FLOAT:
    case T_BIGNUM:
      JCHECK(convert_float_or_bignum_to_js(runtime, ruby, retval));
      JRETURN;

    case T_SYMBOL:
      JCHECK(convert_symbol_to_js(runtime, ruby, retval));
      JRETURN;

    case T_CLASS:
    case T_ARRAY:
    case T_HASH:
    case T_MODULE:
    case T_FILE:
    case T_STRUCT:
    case T_OBJECT:
      JCHECK(make_js_land_proxy(runtime, ruby, retval));
      JRETURN;
      
    case T_REGEXP:
      JCHECK(convert_regexp_to_js(runtime, ruby, retval));
      JRETURN;

    case T_DATA: // HEY! keep T_DATA last for fall-through
      if (ruby_value_is_proxy(ruby))
        JCHECK(unwrap_ruby_land_proxy(runtime, ruby, retval));
      else // If we can't identify the object, just wrap it
        JCHECK(make_js_land_proxy(runtime, ruby, retval));
      JRETURN;

    default:
      JERROR("unknown ruby type in switch");
  }
  
  *retval = JSVAL_NULL;
  JRETURN;
}
示例#10
0
JSBool make_js_land_proxy(JohnsonRuntime* runtime, VALUE value, jsval* retval)
{
  jsval base_value = (jsval)JS_HashTableLookup(runtime->rbids, (void *)value);

  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_JROOTS(context, 2);

  jsval johnson = JSVAL_NULL;
  JCHECK(evaluate_js_property_expression(runtime, "Johnson", &johnson));
  JROOT(johnson);

  if (base_value)
  {
    JCHECK(JS_CallFunctionName(context, johnson, "applyConversions", 1, &base_value, retval));
    JRETURN;
  }
  else
  {
    JSObject *jsobj;
    
    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);

    bool callable_p = Qtrue == rb_funcall(value,
      rb_intern("respond_to?"), 1, rb_str_new2("call"));
      
    if (callable_p)
      klass = &JSLandCallableProxyClass;
        
    JCHECK((jsobj = JS_NewObject(context, klass, NULL, NULL)));
    JROOT(jsobj);
    
    JCHECK(JS_SetPrivate(context, jsobj, (void*)value));

    JCHECK(JS_DefineFunction(context, jsobj, "__noSuchMethod__", method_missing, 2, 0));

    JCHECK(JS_DefineFunction(context, jsobj, "toArray", to_array, 0, 0));
    JCHECK(JS_DefineFunction(context, jsobj, "toString", to_string, 0, 0));

    base_value = OBJECT_TO_JSVAL(jsobj);

    // root the ruby value for GC
    VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(runtime->js);
    rb_funcall(ruby_runtime, rb_intern("add_gcthing"), 1, value);

    jsval wrapped_value = JSVAL_NULL;
    JCHECK(JS_CallFunctionName(context, johnson, "applyWrappers", 1, &base_value, &wrapped_value));

    // put the proxy OID in the id map
    JCHECK(JS_HashTableAdd(runtime->rbids, (void *)value, (void *)(wrapped_value)));
    
    JCHECK(JS_CallFunctionName(context, johnson, "applyConversions", 1, &wrapped_value, retval));

    JRETURN;
  }
}
示例#11
0
static JSBool set(JSContext* js_context, JSObject* obj, jsval id, jsval* value)
{
  VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
  
  JohnsonContext* context;
  JohnsonRuntime* runtime;
  Data_Get_Struct(ruby_context, JohnsonContext, context);

  VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
  Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);

  PREPARE_JROOTS(js_context, 2);
  JROOT(id);
  JROOT_PTR(value);
    
  VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);

  // Short-circuit for numeric indexes
  
  if (JSVAL_IS_INT(id))
  {
    if (indexable_p(self))
    {
      VALUE idx = INT2FIX(JSVAL_TO_INT(id));
      VALUE val = CONVERT_TO_RUBY(runtime, *value);

      JCHECK(call_ruby_from_js(runtime, NULL, self, rb_intern("[]="), 2, idx, val));
    }

    JRETURN;
  }
  
  VALUE ruby_key = CONVERT_TO_RUBY(runtime, id);
  VALUE ruby_value = CONVERT_TO_RUBY(runtime, *value);

  VALUE setter = rb_str_append(rb_str_new3(ruby_key), rb_str_new2("="));
  VALUE setter_id = rb_intern(StringValueCStr(setter));
  
  VALUE settable_p, indexable_p;
  JCHECK(call_ruby_from_js2(runtime, &settable_p, self, rb_intern("respond_to?"), 1, ID2SYM(setter_id)));
  JCHECK(call_ruby_from_js2(runtime, &indexable_p, self, rb_intern("respond_to?"), 1, ID2SYM(rb_intern("[]="))));
  
  if (settable_p)
  {
    VALUE method, arity;
    JCHECK(call_ruby_from_js2(runtime, &method, self, rb_intern("method"), 1, ID2SYM(setter_id)));
    JCHECK(call_ruby_from_js2(runtime, &arity, method, rb_intern("arity"), 0));

    // if the Ruby object has a 1-arity method named "property=",
    // call it with the converted value
    
    if (NUM2INT(arity) == 1)
      JCHECK(call_ruby_from_js(runtime, NULL, self, setter_id, 1, ruby_value));
  }
  else if(indexable_p)
  {
    // otherwise, if the Ruby object quacks sorta like a hash for assignment
    // (it responds to "[]="), assign it by key
    
    JCHECK(call_ruby_from_js(runtime, NULL, self, rb_intern("[]="), 2, ruby_key, ruby_value));
  }
  else
  {
    JCHECK(call_ruby_from_js(runtime, NULL, Johnson_SpiderMonkey_JSLandProxy(), rb_intern("autovivify"), 
      3, self, ruby_key, ruby_value));
  }

  JRETURN;
}
示例#12
0
static JSBool get(JSContext* js_context, JSObject* obj, jsval id, jsval* retval)
{
  // pull out our Ruby context, which is embedded in js_context
  
  VALUE ruby_context = (VALUE)JS_GetContextPrivate(js_context);
  
  // get our struct, which is embedded in ruby_context
  
  JohnsonContext* context;
  JohnsonRuntime* runtime;
  Data_Get_Struct(ruby_context, JohnsonContext, context);

  VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(js_context));
  Data_Get_Struct(ruby_runtime, JohnsonRuntime, runtime);

  PREPARE_JROOTS(js_context, 1);
  JROOT(id);
    
  // get the Ruby object that backs this proxy
  
  VALUE self = (VALUE)JS_GetInstancePrivate(context->js, obj, JS_GET_CLASS(context->js, obj), NULL);
  
  // Short-circuit for numeric indexes
  
  if (JSVAL_IS_INT(id))
  {
    if (indexable_p(self)) {
      VALUE idx = INT2FIX(JSVAL_TO_INT(id));
      JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("[]"), 1, idx));
    }
    
    JRETURN;
  }
  
  char* name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  VALUE ruby_id = rb_intern(name);

  // FIXME: we should probably just JS_DefineProperty this, and it shouldn't be enumerable
  
  if (!strcasecmp("__iterator__", name)) {
    JCHECK(evaluate_js_property_expression(runtime, "Johnson.Generator.create", retval));
  }
  
  // if the Ruby object has a dynamic js property with a key
  // matching the property we're looking for, pull the value out of
  // that map.
  
  else if (autovivified_p(ruby_context, self, name))
  {
    JCHECK(call_ruby_from_js(runtime, retval, Johnson_SpiderMonkey_JSLandProxy(),
      rb_intern("autovivified"), 2, self, rb_str_new2(name)));
  }

  // if the Ruby object is a Module or Class and has a matching
  // const defined, return the converted result of const_get
  
  else if (const_p(self, name))
  {
    JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("const_get"),
      1, ID2SYM(ruby_id)));
  }  

  // otherwise, if it's a global, return the global
  else if (global_p(name))
  {
    JCHECK(convert_to_js(runtime, rb_gv_get(name), retval));
  }
  
  // otherwise, if the Ruby object has a an attribute method matching
  // the property we're trying to get, call it and return the converted result
  
  else if (attribute_p(self, name))
  {
    JCHECK(call_ruby_from_js(runtime, retval, self, ruby_id, 0));
  }

  // otherwise, if the Ruby object quacks sorta like a hash (it responds to
  // "[]" and "key?"), index it by key and return the converted result
  
  else if (has_key_p(self, name))
  {
    JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("[]"), 1, rb_str_new2(name)));
  }
  
  // otherwise, it's a method being accessed as a property, which means
  // we need to return a lambda
  
  // FIXME: this should really wrap the Method  for 'name' in a JS class
  // rather than generating a wrapper Proc
  
  else if (method_p(self, name))
  {
    JCHECK(call_ruby_from_js(runtime, retval, self, rb_intern("method"), 1, rb_str_new2(name)));
  }

  // else it's undefined (JS_VOID) by default
  JRETURN;
}