Skip to content
This repository has been archived by the owner on Aug 18, 2018. It is now read-only.

Commit

Permalink
Part 1 of cleaning up initialize_native, thanks to zenspider.
Browse files Browse the repository at this point in the history
  • Loading branch information
jbarnette committed Apr 23, 2008
1 parent 27a28d8 commit cb2938d
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 79 deletions.
121 changes: 66 additions & 55 deletions ext/spidermonkey/context.c
Expand Up @@ -42,7 +42,7 @@ static VALUE global(VALUE self)
{
OurContext* context;
Data_Get_Struct(self, OurContext, context);
return convert_to_ruby(context, OBJECT_TO_JSVAL(context->global));
return convert_to_ruby(context, OBJECT_TO_JSVAL(context->global));
}

static VALUE evaluate(int argc, VALUE* argv, VALUE self)
Expand All @@ -51,18 +51,18 @@ static VALUE evaluate(int argc, VALUE* argv, VALUE self)

OurContext* context;
Data_Get_Struct(self, OurContext, context);

rb_scan_args( argc, argv, "12", &script, &filename, &linenum );

// clean things up first
context->ex = 0;
memset(context->msg, 0, MAX_EXCEPTION_MESSAGE_SIZE);
memset(context->msg, 0, MAX_EXCEPTION_MESSAGE_SIZE);

char* filenamez = RTEST(filename) ? StringValueCStr(filename) : NULL;
int linenumi = RTEST(linenum) ? NUM2INT(linenum) : 1;

jsval js;

// FIXME: should be able to pass in the 'file' name
JSBool ok = JS_EvaluateScript(context->js, context->global,
StringValuePtr(script), StringValueLen(script), filenamez, linenumi, &js);
Expand All @@ -77,10 +77,10 @@ static VALUE evaluate(int argc, VALUE* argv, VALUE self)
}

char* msg = context->msg;

// toString() whatever the exception object is (if we have one)
if (context->ex) msg = JS_GetStringBytes(JS_ValueToString(context->js, context->ex));

return Johnson_Error_raise(msg);
}

Expand All @@ -91,35 +91,35 @@ static void error(JSContext* js, const char* message, JSErrorReport* report)
{
// first we find ourselves
VALUE self = (VALUE)JS_GetContextPrivate(js);

// then we find our bridge
OurContext* context;
Data_Get_Struct(self, OurContext, context);

// NOTE: SpiderMonkey REALLY doesn't like being interrupted. If we
// jump over to Ruby and raise here, segfaults and such ensue.
// Instead, we store the exception (if any) and the error message
// on the context. They're dealt with in the if (!ok) block of evaluate().

strncpy(context->msg, message, MAX_EXCEPTION_MESSAGE_SIZE);
JS_GetPendingException(context->js, &context->ex);
}

static void deallocate(OurContext* context)
{
JS_SetContextPrivate(context->js, 0);

JS_RemoveRoot(context->js, &(context->global));
JS_RemoveRoot(context->js, &(context->gcthings));
JS_HashTableDestroy(context->rbids);
JS_HashTableDestroy(context->jsids);

context->jsids = 0;
context->rbids = 0;

JS_DestroyContext(context->js);
JS_DestroyRuntime(context->runtime);

free(context);
}

Expand All @@ -129,60 +129,71 @@ static VALUE allocate(VALUE klass)
return Data_Wrap_Struct(klass, 0, deallocate, context);
}

static VALUE initialize_native(VALUE self, VALUE options)
{
static VALUE initialize_native(VALUE self, VALUE options) {
OurContext* context;
bool gthings_rooted_p = false;

Data_Get_Struct(self, OurContext, context);

// FIXME: Surely there's a less ugly way of doing this...
if((context->runtime = JS_NewRuntime(0x100000))) {
if((context->js = JS_NewContext(context->runtime, 8192))) {
if ((context->jsids = create_id_hash())) {
if ((context->rbids = create_id_hash())) {
if ((context->gcthings = JS_NewObject(context->js, NULL, 0, 0))) {
if (JS_AddNamedRoot(context->js, &(context->gcthings), "context->gcthings")) {
if ((context->global = JS_NewObject(context->js, &OurGlobalClass, NULL, NULL))) {
if (JS_AddNamedRoot(context->js, &(context->global), "context->global")) {
JS_SetErrorReporter(context->js, error);
JS_SetContextPrivate(context->js, (void *)self);
jsval js_cObject;
if (JS_GetProperty(context->js, context->global, "Object", &js_cObject)
&& JS_AddNamedRoot(context->js, &js_cObject, "context.Object")) {
if (JS_DefineFunction(context->js, JSVAL_TO_OBJECT(js_cObject), "defineProperty", define_property, 4, 0)
&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(js_cObject), "READ_ONLY",
INT_TO_JSVAL(0x02), NULL, NULL, JSPROP_READONLY)
&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(js_cObject), "ITERABLE",
INT_TO_JSVAL(0x01), NULL, NULL, JSPROP_READONLY)
&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(js_cObject), "NON_DELETABLE",
INT_TO_JSVAL(0x04), NULL, NULL, JSPROP_READONLY)) {
JS_RemoveRoot(context->js, &js_cObject);
return self;
}
JS_RemoveRoot(context->js, &js_cObject);
}
JS_RemoveRoot(context->js, &(context->global));
}
}
JS_RemoveRoot(context->js, &(context->gcthings));
}
}
JS_HashTableDestroy(context->rbids);
}
JS_HashTableDestroy(context->jsids);
if ((context->runtime = JS_NewRuntime(0x100000))
&& (context->js = JS_NewContext(context->runtime, 8192))
&& (context->jsids = create_id_hash())
&& (context->rbids = create_id_hash())
&& (context->gcthings = JS_NewObject(context->js, NULL, 0, 0))
&& (gthings_rooted_p = JS_AddNamedRoot(context->js, &(context->gcthings), "context->gcthings"))
&& (context->global = JS_NewObject(context->js, &OurGlobalClass, NULL, NULL))
&& (JS_AddNamedRoot(context->js, &(context->global), "context->global")))
{
JS_SetErrorReporter(context->js, error);
JS_SetContextPrivate(context->js, (void *)self);

// FIXME: move this to extensions.c or something
jsval js_cObject;

if (JS_GetProperty(context->js, context->global, "Object", &js_cObject)
&& JS_AddNamedRoot(context->js, &js_cObject, "context.Object")) {
if (JS_DefineFunction(context->js, JSVAL_TO_OBJECT(js_cObject), "defineProperty", define_property, 4, 0)
&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(js_cObject), "READ_ONLY",
INT_TO_JSVAL(0x02), NULL, NULL, JSPROP_READONLY)
&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(js_cObject), "ITERABLE",
INT_TO_JSVAL(0x01), NULL, NULL, JSPROP_READONLY)
&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(js_cObject), "NON_DELETABLE",
INT_TO_JSVAL(0x04), NULL, NULL, JSPROP_READONLY)) {
JS_RemoveRoot(context->js, &js_cObject);

return self;
}
JS_DestroyContext(context->js);

JS_RemoveRoot(context->js, &js_cObject);
}
JS_DestroyRuntime(context->runtime);

JS_RemoveRoot(context->js, &(context->global));
}

if (gthings_rooted_p)
JS_RemoveRoot(context->js, &(context->gcthings));

if (context->rbids)
JS_HashTableDestroy(context->rbids);

if (context->jsids)
JS_HashTableDestroy(context->jsids);

if (context->js)
JS_DestroyContext(context->js);

if (context->runtime)
JS_DestroyRuntime(context->runtime);

rb_raise(rb_eRuntimeError, "Failed to initialize SpiderMonkey context");
}

// FIXME: move this to extensions.c or something
// Argv is [ object, name, value, READ_ONLY | ITERABLE | NON_DELETABLE ]
static JSBool define_property(JSContext *js_context, JSObject *obj, uintN argc, jsval *argv, jsval *retval) {
char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
int flags = JSVAL_TO_INT(argv[3]);

return JS_DefineProperty(js_context, JSVAL_TO_OBJECT(argv[0]), name, argv[2], NULL, NULL, flags);
}

Expand All @@ -193,11 +204,11 @@ void init_Johnson_SpiderMonkey_Context(VALUE spidermonkey)
rb_define_alloc_func(context, allocate);
rb_define_private_method(context, "initialize_native", initialize_native, 1);

rb_define_method(context, "global", global, 0);
rb_define_method(context, "global", global, 0);
rb_define_method(context, "evaluate", evaluate, -1);
}

VALUE Johnson_SpiderMonkey_JSLandProxy()
{
return rb_eval_string("Johnson::SpiderMonkey::JSLandProxy");
return rb_eval_string("Johnson::SpiderMonkey::JSLandProxy");
}
1 change: 0 additions & 1 deletion test/helper.rb
@@ -1,6 +1,5 @@
require "rubygems"
require "test/unit"
require "mocha"

%w(../lib ../ext/spidermonkey).each do |path|
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), path)))
Expand Down
24 changes: 1 addition & 23 deletions test/johnson/context_test.rb
Expand Up @@ -5,35 +5,13 @@ class ContextTest < Johnson::TestCase
def setup
@context = Johnson::Context.new
end

def test_default_delegate_is_spidermonkey
assert_equal(Johnson::SpiderMonkey::Context, @context.delegate.class)
end

def test_delegates_public_ops
delegate = mock(:evaluate => nil)
delegate.expects(:[]).with("key").returns("value")
delegate.expects(:[]=).with("key", "value").returns("value")

ctx = Johnson::Context.new(delegate)

assert_equal("value", ctx[:key])
assert_equal("value", ctx[:key] = "value")
end

def test_evaluate_returns_nil_for_nil_expression
assert_nil(@context.evaluate(nil))
end

def test_converts_keys_to_strings_for_get_and_set
delegate = mock(:evaluate => nil)
delegate.expects(:[]).with("key")
delegate.expects(:[]=).with("key", "value")

ctx = Johnson::Context.new(delegate)

value = ctx[:key]
ctx[:key] = "value"
end
end
end

0 comments on commit cb2938d

Please sign in to comment.