JSBool report_ruby_error_in_js(JohnsonRuntime* runtime, int state, VALUE old_errinfo) { JSContext * context = johnson_get_current_context(runtime); assert(state); switch (state) { case TAG_RAISE: { VALUE local_error = ruby_errinfo; jsval js_err; ruby_errinfo = old_errinfo; if (!convert_to_js(runtime, local_error, &js_err)) return JS_FALSE; JS_SetPendingException(context, js_err); return JS_FALSE; } case TAG_THROW: // FIXME: This should be propagated to JS... as an exception? default: { JSString* str = JS_NewStringCopyZ(context, "Unexpected longjmp from ruby!"); if (str) JS_SetPendingException(context, STRING_TO_JSVAL(str)); return JS_FALSE; } } }
/* * 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); }
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)); }
/* * call-seq: * native_call(this, *args) * * Call this Ruby Object as a function, with the given +this+ object and * arguments. Equivalent to the call method in JavaScript. */ static VALUE native_call(int argc, VALUE* argv, VALUE self) { if (!function_p(self)) rb_raise(rb_eRuntimeError, "This Johnson::SpiderMonkey::RubyLandProxy isn't a function."); if (argc < 1) rb_raise(rb_eArgError, "Target object required"); RubyLandProxy* proxy; Data_Get_Struct(self, RubyLandProxy, proxy); jsval proxy_value; if (!get_jsval_for_proxy(proxy, &proxy_value)) raise_js_error_in_ruby(proxy->runtime); jsval global; if (!convert_to_js(proxy->runtime, argv[0], &global)) raise_js_error_in_ruby(proxy->runtime); return call_js_function_value(proxy->runtime, global, proxy_value, argc - 1, &(argv[1])); }
JSBool call_ruby_from_js(JohnsonRuntime* runtime, jsval* retval, VALUE self, ID id, int argc, ...) { VALUE result; va_list va; va_start(va, argc); JSBool okay = call_ruby_from_js_va(runtime, &result, self, id, argc, va); va_end(va); if (!okay) return JS_FALSE; return retval ? convert_to_js(runtime, result, retval) : JS_TRUE; }
/* * call-seq: * set_trap(script, parsecode, block) * * Set the trap at +script+ and +parsecode+ to +block+ */ static VALUE set_trap(VALUE self, VALUE script, VALUE linenum, VALUE block) { JohnsonRuntime* runtime; Data_Get_Struct(self, JohnsonRuntime, runtime); JSContext * context = johnson_get_current_context(runtime); jsval compiled_js; if(!convert_to_js(runtime, script, &compiled_js)) rb_raise(rb_eRuntimeError, "Couldn't get compiled script."); JSScript * js_script = (JSScript *)JS_GetPrivate(context, JSVAL_TO_OBJECT(compiled_js)); jsbytecode * pc = JS_LineNumberToPC(context, js_script, (uintN)NUM2INT(linenum)); return JS_SetTrap(context, js_script, pc, trap_handler, (void*)block) ? Qtrue : Qfalse; }
/* * call-seq: * evaluate_compiled_script(script) * * Evaluate +script+ */ static VALUE evaluate_compiled_script(VALUE self, VALUE compiled_script) { JohnsonRuntime* runtime; Data_Get_Struct(self, JohnsonRuntime, runtime); JSContext * context = johnson_get_current_context(runtime); JohnsonContext * johnson_context = OUR_CONTEXT(context); // clean things up first johnson_context->ex = 0; memset(johnson_context->msg, 0, MAX_EXCEPTION_MESSAGE_SIZE); jsval compiled_js; if(!convert_to_js(runtime, compiled_script, &compiled_js)) rb_raise(rb_eRuntimeError, "Script compilation failed"); JSScript * js_script = (JSScript *)JS_GetPrivate(context, JSVAL_TO_OBJECT(compiled_js)); jsval js; JSBool ok = JS_ExecuteScript(context, runtime->global, js_script, &js); if (!ok) { 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; } } return convert_to_ruby(runtime, js); }
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; }