Example #1
0
/*
 * call-seq:
 *   respond_to?(symbol)
 *
 * Returns <code>true</code> if this JavaScript object responds to the
 * named method.
 */
static VALUE
respond_to_p(int argc, const VALUE* argv, VALUE self)
{
  VALUE sym, priv;

  rb_scan_args(argc, argv, "11", &sym, &priv);

  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);

  JSContext * context = johnson_get_current_context(proxy->runtime);
  PREPARE_RUBY_JROOTS(context, 2);
  
  VALUE stringval = rb_funcall(sym, rb_intern("to_s"), 0);
  char* name = StringValuePtr(stringval);
  
  // assignment is always okay
  if (name[strlen(name) - 1] == '=')
    JRETURN_RUBY(Qtrue);
  
  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);

  JSObject *obj;
  JSBool found;
  
  JCHECK(JS_ValueToObject(context, proxy_value, &obj));
  JROOT(obj);

  JCHECK(JS_HasProperty(context, obj, name, &found));

  JRETURN_RUBY(found ? Qtrue : CALL_RUBY_WRAPPER(rb_call_super, argc, argv));
}
Example #2
0
/*
 * call-seq:
 *   []=(name, value)
 *
 * Sets this JavaScript object's +name+ property to +value+.
 */
static VALUE
set(VALUE self, VALUE name, VALUE value)
{
  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);
  JSContext * context = johnson_get_current_context(proxy->runtime);
  
  PREPARE_RUBY_JROOTS(context, 2);
  
  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);

  jsval js_value;
  JCHECK(convert_to_js(proxy->runtime, value, &js_value));
  
  JROOT(js_value);

  switch(TYPE(name)) {
    case T_FIXNUM:
      JCHECK(JS_SetElement(context,
              JSVAL_TO_OBJECT(proxy_value), (jsint)(NUM2INT(name)), &js_value));
      break;
    case T_SYMBOL:
      name = RB_FUNCALL_0(name, RB_INTERN("to_s"));
    default:
      CALL_RUBY_WRAPPER(rb_string_value_cstr, &name);
      JCHECK(JS_SetProperty(context,
            JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &js_value));
      break;
  }

  JRETURN_RUBY(value);
}
Example #3
0
/*
 * call-seq:
 *   function_property?(name)
 *
 * Returns <code>true</code> if this JavaScript object's +name+ property
 * is a function.
 */
static VALUE
function_property_p(VALUE self, VALUE name)
{
  if (TYPE(name) == T_SYMBOL)
    name = rb_funcall(name, rb_intern("to_s"), 0);

  rb_string_value_cstr(&name);

  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);
  JSContext * context = johnson_get_current_context(proxy->runtime);

  PREPARE_RUBY_JROOTS(context, 2);

  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);

  jsval js_value;  

  JCHECK(JS_GetProperty(context,
      JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &js_value));

  JROOT(js_value);

  JSType type = JS_TypeOfValue(context, js_value);

  JRETURN_RUBY(type == JSTYPE_FUNCTION ? Qtrue : Qfalse);
}
Example #4
0
/*
 * call-seq:
 *   call_function_property(name, arguments)
 *
 * Calls this JavaScript object's +name+ method, passing the given
 * arguments.
 *
 * Equivalent to:
 *    proxy[name].native_call(proxy, *arguments)
 */
static VALUE
call_function_property(int argc, VALUE* argv, VALUE self)
{
  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);
  JSContext * context = johnson_get_current_context(proxy->runtime);

  if (argc < 1)
    rb_raise(rb_eArgError, "Function name required");

  PREPARE_RUBY_JROOTS(context, 2);
  
  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);

  jsval function;

  VALUE name = argv[0];
  CALL_RUBY_WRAPPER(rb_string_value_cstr, &name);
  
  JCHECK(JS_GetProperty(context,
    JSVAL_TO_OBJECT(proxy_value), StringValueCStr(name), &function));

  JROOT(function);

  // should never be anything but a function
  if (!JS_ObjectIsFunction(context, function))
    JERROR("Specified property \"%s\" isn't a function.", StringValueCStr(name));

  REMOVE_JROOTS;

  return call_js_function_value(proxy->runtime, proxy_value, function, argc - 1, &(argv[1]));
}
Example #5
0
/*
 * call-seq:
 *   length()
 *
 * Returns the number of entries in the JavaScript array, or the number
 * of properties on the JavaScript object.
 */
static VALUE
length(VALUE self)
{
  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);
  JSContext * context = johnson_get_current_context(proxy->runtime);

  PREPARE_RUBY_JROOTS(context, 2);
  
  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);

  JSObject* value = JSVAL_TO_OBJECT(proxy_value);
  JROOT(value);
  
  if (JS_IsArrayObject(context, value))
  {
    jsuint length;
    JCHECK(JS_GetArrayLength(context, value, &length));

    JRETURN_RUBY(INT2FIX(length));
  }
  else
  {
    JSIdArray* ids = JS_Enumerate(context, value);
    JCHECK(ids);
    VALUE length = INT2FIX(ids->length);
    
    JS_DestroyIdArray(context, ids);

    JRETURN_RUBY(length);
  }
}
Example #6
0
static VALUE call_js_function_value(JohnsonRuntime* runtime, jsval target, jsval function, int argc, VALUE* argv)
{
  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_RUBY_JROOTS(context, argc + 2);

  JROOT(target);
  JROOT(function);

  assert(JSVAL_IS_OBJECT(target));

  jsval args[argc];
  jsval result;

  int i;
  for(i = 0; i < argc; ++i)
  {
    JCHECK(convert_to_js(runtime, argv[i], &(args[i])));
    JROOT(args[i]);
  }

  JCHECK(JS_CallFunctionValue(context,
    JSVAL_TO_OBJECT(target), function, (unsigned) argc, args, &result));

  JRETURN_RUBY(CONVERT_TO_RUBY(runtime, result));
}
Example #7
0
static bool js_value_is_regexp(JohnsonRuntime* runtime, jsval maybe_regexp)
{
  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_RUBY_JROOTS(context, 1);
  JROOT(maybe_regexp);
  JSBool result = JS_InstanceOf(context, JSVAL_TO_OBJECT(maybe_regexp), &js_RegExpClass, NULL);
  JRETURN_RUBY(result ? true : false);
}
Example #8
0
VALUE convert_js_string_to_ruby(JohnsonRuntime* runtime, JSString* str)
{
  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_RUBY_JROOTS(context, 1);
  JROOT(str);
  char* bytes = JS_GetStringBytes(str);
  JCHECK(bytes);
  JRETURN_RUBY(rb_str_new(bytes, (signed long)JS_GetStringLength(str)));
}
Example #9
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;
}
Example #10
0
static VALUE convert_regexp_to_ruby(JohnsonRuntime* runtime, jsval regexp)
{
  JSContext * context = johnson_get_current_context(runtime);
  PREPARE_RUBY_JROOTS(context, 1);
  JROOT(regexp);
  JSRegExp* re = (JSRegExp*)JS_GetPrivate(context, JSVAL_TO_OBJECT(regexp));

  JRETURN_RUBY(CALL_RUBY_WRAPPER(rb_funcall_2, rb_cRegexp, rb_intern("new"), 2,
    convert_js_string_to_ruby(runtime, re->source),
    INT2NUM((long)re->flags)));
}
Example #11
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;
}
Example #12
0
/*
 * call-seq:
 *   function?()
 *
 * Returns <code>true</code> if this JavaScript object is a function.
 */
static VALUE
function_p(VALUE self)
{
  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);
  JSContext * context = johnson_get_current_context(proxy->runtime);
  PREPARE_RUBY_JROOTS(context, 1);
  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);
  JRETURN_RUBY(JS_TypeOfValue(context, proxy_value) == JSTYPE_FUNCTION ? Qtrue : Qfalse);
}
Example #13
0
static bool js_value_is_symbol(JohnsonRuntime* runtime, jsval maybe_symbol)
{
  jsval nsJohnson, cSymbol;
  JSContext * context = johnson_get_current_context(runtime);

  PREPARE_RUBY_JROOTS(context, 3);
  JROOT(maybe_symbol);

  JCHECK(JS_GetProperty(context, runtime->global, "Johnson", &nsJohnson));
  if (!JSVAL_IS_OBJECT(nsJohnson))
    JERROR("Unable to retrieve Johnson from JSLand");
  JROOT(nsJohnson);

  JCHECK(JS_GetProperty(context, JSVAL_TO_OBJECT(nsJohnson), "Symbol", &cSymbol));
  if (!JSVAL_IS_OBJECT(cSymbol))
    JERROR("Unable to retrieve Johnson.Symbol from JSLand");
  JROOT(cSymbol);

  JSBool is_a_symbol;
  JCHECK(JS_HasInstance(context, JSVAL_TO_OBJECT(cSymbol), maybe_symbol, &is_a_symbol));

  JRETURN_RUBY(is_a_symbol != JS_FALSE);
}
Example #14
0
/*
 * call-seq:
 *   to_s()
 *
 * Converts the JavaScript object to a string, using its toString method
 * if available.
 */
static VALUE to_s(VALUE self)
{
  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);
  JSContext * context = johnson_get_current_context(proxy->runtime);

  PREPARE_RUBY_JROOTS(context, 1);
  
  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);
  
  JSString* str = JS_ValueToString(context, proxy_value);
  JRETURN_RUBY(CONVERT_JS_STRING_TO_RUBY(proxy->runtime, str));
}
Example #15
0
VALUE convert_to_ruby(JohnsonRuntime* runtime, jsval js)
{
  if (JSVAL_NULL == js) return Qnil;

  JSContext * context = johnson_get_current_context(runtime);

  PREPARE_RUBY_JROOTS(context, 1);
  JROOT(js);
  
  switch (JS_TypeOfValue(context, js))
  {
    case JSTYPE_VOID:
      JRETURN_RUBY(Qnil);
      
    case JSTYPE_FUNCTION: 
    case JSTYPE_OBJECT:
      if (OBJECT_TO_JSVAL(runtime->global) == js)
        // global gets special treatment, since the Prelude might not be loaded
        JRETURN_RUBY(make_ruby_land_proxy(runtime, js, "GlobalProxy"));
      
      // this conditional requires the Prelude
      if (js_value_is_symbol(runtime, js))
        JRETURN_RUBY(ID2SYM(rb_intern(JS_GetStringBytes(JS_ValueToString(context, js)))));
    
      if (js_value_is_proxy(runtime, js))
        JRETURN_RUBY(unwrap_js_land_proxy(runtime, js));

      if (js_value_is_regexp(runtime, js))
        JRETURN_RUBY(convert_regexp_to_ruby(runtime, js));
    
      JRETURN_RUBY(make_ruby_land_proxy(runtime, js, "RubyLandProxy"));
        
    case JSTYPE_BOOLEAN:
      JRETURN_RUBY(JSVAL_TRUE == js ? Qtrue : Qfalse);
      
    case JSTYPE_STRING:
      JRETURN_RUBY(convert_js_string_to_ruby(runtime, JSVAL_TO_STRING(js)));
      
    case JSTYPE_NUMBER:
      if (JSVAL_IS_INT(js)) JRETURN_RUBY(INT2FIX(JSVAL_TO_INT(js)));
      else JRETURN_RUBY(rb_float_new(*JSVAL_TO_DOUBLE(js)));

    default:
      JERROR("unknown js type in switch");
  }
  
  JRETURN_RUBY(Qnil);
}
Example #16
0
VALUE make_ruby_land_proxy(JohnsonRuntime* runtime, jsval value, const char const* root_name)
{
  RubyLandProxy * our_proxy = (RubyLandProxy *)JS_HashTableLookup(runtime->jsids, (void *)value);
  
  if (our_proxy)
  {
    // if we already have a proxy, return it
    return apply_conversions(our_proxy->self);
  }
  else
  {    
    // otherwise make one and cache it
    VALUE proxy = Data_Make_Struct((strncmp(root_name, "JSScriptProxy", strlen("JSScriptProxy")) ? proxy_class : script_class), RubyLandProxy, 0, finalize, our_proxy);

    JSContext * context = johnson_get_current_context(runtime);

    PREPARE_RUBY_JROOTS(context, 1);
    JROOT(value);

    VALUE rb_runtime = (VALUE)JS_GetRuntimePrivate(runtime->js);
    rb_iv_set(proxy, "@runtime", rb_runtime);

    our_proxy->runtime = runtime;
    our_proxy->key = (void *)value;
    our_proxy->self = proxy;

    // root the value for JS GC and lookups
    JCHECK(JS_AddNamedRootRT(runtime->js, &(our_proxy->key), root_name));

    // put the proxy OID in the id map
    JCHECK(JS_HashTableAdd(runtime->jsids, (void *)value, (void *)our_proxy));

    VALUE final_proxy = JPROTECT(apply_wrappers, proxy);

    our_proxy->self = final_proxy;

    JRETURN_RUBY(JPROTECT(apply_conversions, final_proxy));
  }
}
Example #17
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;
}
Example #18
0
/*
 * call-seq:
 *   native_compile(script, filename, linenum)
 *
 * Compile +script+ with +filename+ using +linenum+
 */
static VALUE native_compile(VALUE self, VALUE script, VALUE filename, VALUE linenum)
{
  JohnsonRuntime* runtime;
  Data_Get_Struct(self, JohnsonRuntime, runtime);

  JSContext * context = johnson_get_current_context(runtime);
  JohnsonContext * johnson_context = OUR_CONTEXT(context);

  JSScript * compiled_js = JS_CompileScript(
      context,
      runtime->global,
      StringValuePtr(script),
      (size_t)StringValueLen(script),
      StringValueCStr(filename),
      (unsigned)NUM2INT(linenum)
  );
  if(compiled_js == NULL) {
    if (JS_IsExceptionPending(context))
    {
      // If there's an exception pending here, it's a syntax error.
      JS_GetPendingException(context, &johnson_context->ex);
      JS_ClearPendingException(context);
    }

    if (johnson_context->ex) {
      RAISE_JS_ERROR(self, johnson_context->ex);
      return Qnil;
    }
  }

  JSObject * script_object = JS_NewScriptObject(context, compiled_js);

  PREPARE_RUBY_JROOTS(context, 1);
  JROOT(script_object);
  JRETURN_RUBY(make_ruby_land_proxy(runtime, OBJECT_TO_JSVAL(script_object), "JSScriptProxy"));
}
Example #19
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;
}
Example #20
0
/*
 * call-seq:
 *   each {| element | block }
 *   each {| name, value | block }
 *
 * Calls <em>block</em> with each item in this JavaScript array, or with
 * each +name+/+value+ pair (like a Hash) for any other JavaScript
 * object.
 */
static VALUE
each(VALUE self)
{
  RubyLandProxy* proxy;
  Data_Get_Struct(self, RubyLandProxy, proxy);
  JSContext * context = johnson_get_current_context(proxy->runtime);
  
  PREPARE_RUBY_JROOTS(context, 5);
  
  jsval proxy_value;
  JCHECK(get_jsval_for_proxy(proxy, &proxy_value));
  JROOT(proxy_value);

  JSObject* value = JSVAL_TO_OBJECT(proxy_value);
  JROOT(value);
  
  // arrays behave like you'd expect, indexes in order
  if (JS_IsArrayObject(context, value))
  {
    jsuint length;
    JCHECK(JS_GetArrayLength(context, value, &length));
    
    jsuint i = 0;
    for (i = 0; i < length; ++i)
    {
      jsval element;
      JCHECK(JS_GetElement(context, value, (signed) i, &element));
      CALL_RUBY_WRAPPER(rb_yield, CONVERT_TO_RUBY(proxy->runtime, element));
    }
  }
  else
  {
    // not an array? behave like each on Hash; yield [key, value]
    JSIdArray* ids = JS_Enumerate(context, value);
    JCHECK(ids);

    JCLEANUP(destroy_id_array, ids);

    int i;
    for (i = 0; i < ids->length; ++i)
    {
      jsval js_key, js_value;

      JCHECK(JS_IdToValue(context, ids->vector[i], &js_key));
      JROOT(js_key);

      if (JSVAL_IS_STRING(js_key))
      {
        // regular properties have string keys
        JCHECK(JS_GetProperty(context, value,
          JS_GetStringBytes(JSVAL_TO_STRING(js_key)), &js_value));
      }
      else
      {
        // it's a numeric property, use array access
        JCHECK(JS_GetElement(context, value,
          JSVAL_TO_INT(js_key), &js_value));
      }
      JROOT(js_value);

      VALUE key = CONVERT_TO_RUBY(proxy->runtime, js_key);
      VALUE value = CONVERT_TO_RUBY(proxy->runtime, js_value);

      CALL_RUBY_WRAPPER(rb_yield, rb_ary_new3(2L, key, value));

      JUNROOT(js_value);
      JUNROOT(js_key);
    }
  }

  JRETURN_RUBY(self);
}
Example #21
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;
}
Example #22
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;
  }
}