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

Commit

Permalink
Merge branch 'master' of git@github.com:jbarnette/johnson
Browse files Browse the repository at this point in the history
* 'master' of git@github.com:jbarnette/johnson:
  Basic exception handling for Context#evaluate.
  Moved our global object creation, etc into a separate file.
  Motherfriendly MANIFEST.
  Fix some indentation.
  Move our SpiderMonkey extensions to extensions.c.
  Part 1 of cleaning up initialize_native, thanks to zenspider.
  fixing warnings, accepting *args
  populating file name and line number on parse errors
  asking the file stream for its name
  • Loading branch information
wycats committed Apr 23, 2008
2 parents 1a2ffe6 + 12809c7 commit bd37eeb
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 151 deletions.
4 changes: 4 additions & 0 deletions Manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ ext/spidermonkey/conversions.h
ext/spidermonkey/error.c
ext/spidermonkey/error.h
ext/spidermonkey/extconf.rb
ext/spidermonkey/extensions.c
ext/spidermonkey/extensions.h
ext/spidermonkey/global.c
ext/spidermonkey/global.h
ext/spidermonkey/idhash.c
ext/spidermonkey/idhash.h
ext/spidermonkey/immutable_node.c
Expand Down
2 changes: 1 addition & 1 deletion bin/johnson
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ rescue Object => e
end

def eval_in_js(expression)
rescued { puts "=> " + CONTEXT.evaluate(expression).inspect }
rescued { puts "=> " + CONTEXT.evaluate(expression, "(console)").inspect }
end

def eval_in_ruby(expression, bind_to)
Expand Down
208 changes: 98 additions & 110 deletions ext/spidermonkey/context.c
Original file line number Diff line number Diff line change
@@ -1,68 +1,37 @@
#include "context.h"
#include "conversions.h"
#include "global.h"
#include "error.h"
#include "extensions.h"
#include "idhash.h"

static JSBool define_property(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *retval);

static JSBool global_enumerate(JSContext *js_context, JSObject *obj)
{
return JS_EnumerateStandardClasses(js_context, obj);
}

static JSBool global_resolve(
JSContext *js_context, JSObject *obj, jsval id, uintN flags, JSObject **objp)
{
if ((flags & JSRESOLVE_ASSIGNING) == 0) {
JSBool resolved_p;

if (!JS_ResolveStandardClass(js_context, obj, id, &resolved_p))
return JS_FALSE;
if (resolved_p) *objp = obj;
}

return JS_TRUE;
}


static JSClass OurGlobalClass = {
"global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, // addProperty
JS_PropertyStub, // delProperty
JS_PropertyStub, // getProperty
JS_PropertyStub, // setProperty
global_enumerate,
(JSResolveOp) global_resolve,
JS_ConvertStub,
JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};

static VALUE global(VALUE self)
static VALUE /* Context#global */
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)
static VALUE /* Context#evaluate(script, filename=nil, linenum=nil) */
evaluate(int argc, VALUE* argv, VALUE self)
{
VALUE script, filename, linenum;

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 @@ -76,50 +45,126 @@ static VALUE evaluate(int argc, VALUE* argv, VALUE self)
JS_ClearPendingException(context->js);
}

if (context->ex)
{
return rb_funcall(self, rb_intern("handle_js_exception"),
1, convert_to_ruby(context, context->ex));

// VALUE message, file, line, stack;
//
// jsval js_message;
// assert(JS_GetProperty(context->js, JSVAL_TO_OBJECT(context->ex), "message", &js_message));
// message = convert_to_ruby(context, js_message);
//
// jsval js_file;
// assert(JS_GetProperty(context->js, JSVAL_TO_OBJECT(context->ex), "fileName", &js_file));
// file = convert_to_ruby(context, js_file);
//
// jsval js_line;
// assert(JS_GetProperty(context->js, JSVAL_TO_OBJECT(context->ex), "lineNumber", &js_line));
// line = convert_to_ruby(context, js_line);
//
// jsval js_stack;
// assert(JS_GetProperty(context->js, JSVAL_TO_OBJECT(context->ex), "stack", &js_stack));
// stack = convert_to_ruby(context, js_stack);
//
// return rb_funcall(self, rb_intern("handle_js_exception"),
// 4, message, file, line, stack);
}

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);
}

return convert_to_ruby(context, js);
}

static void error(JSContext* js, const char* message, JSErrorReport* report)
// callback for JS_SetErrorReporter
static void report_js_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);
}

/* private */ static VALUE /* Context#initialize_native(options={}) */
initialize_native(VALUE self, VALUE options)
{
OurContext* context;
bool gthings_rooted_p = false;

Data_Get_Struct(self, OurContext, context);

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 = create_global_object(context))
&& (JS_AddNamedRoot(context->js, &(context->global), "context->global")))
{
JS_SetErrorReporter(context->js, report_js_error);
JS_SetContextPrivate(context->js, (void *)self);

if (init_spidermonkey_extensions(context))
return self;

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");
}

///////////////////////////////////////////////////////////////////////////
//// INFRASTRUCTURE BELOW HERE ////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

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,75 +174,18 @@ static VALUE allocate(VALUE klass)
return Data_Wrap_Struct(klass, 0, deallocate, context);
}

static VALUE initialize_native(VALUE self, VALUE options)
{
OurContext* context;
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);
}
JS_DestroyContext(context->js);
}
JS_DestroyRuntime(context->runtime);
}

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

// 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);
}

void init_Johnson_SpiderMonkey_Context(VALUE spidermonkey)
{
VALUE context = rb_define_class_under(spidermonkey, "Context", rb_cObject);

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");
}
42 changes: 42 additions & 0 deletions ext/spidermonkey/extensions.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "extensions.h"

static JSBool /* Object.defineProperty(target, name, value, flags) */
define_property(JSContext *js_context, JSObject *obj, uintN argc, jsval *argv, jsval *retval) {
char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));

// READ_ONLY | ITERABLE | NON_DELETABLE
int flags = JSVAL_TO_INT(argv[3]);

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

bool init_spidermonkey_extensions(OurContext* context)
{
jsval Object;

if (JS_GetProperty(context->js, context->global, "Object", &Object))
{
if (JS_AddNamedRoot(context->js, &Object, "context.Object"))
{
if (JS_DefineFunction(context->js, JSVAL_TO_OBJECT(Object),
"defineProperty", define_property, 4, 0)

&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(Object), "READ_ONLY",
INT_TO_JSVAL(0x02), NULL, NULL, JSPROP_READONLY)

&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(Object), "ITERABLE",
INT_TO_JSVAL(0x01), NULL, NULL, JSPROP_READONLY)

&& JS_DefineProperty(context->js, JSVAL_TO_OBJECT(Object), "NON_DELETABLE",
INT_TO_JSVAL(0x04), NULL, NULL, JSPROP_READONLY))
{
JS_RemoveRoot(context->js, &Object);
return true;
}

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

return false;
}
9 changes: 9 additions & 0 deletions ext/spidermonkey/extensions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef JOHNSON_SPIDERMONKEY_EXTENSIONS_H
#define JOHNSON_SPIDERMONKEY_EXTENSIONS_H

#include "spidermonkey.h"
#include "context.h"

bool init_spidermonkey_extensions(OurContext* context);

#endif
Loading

0 comments on commit bd37eeb

Please sign in to comment.