Mercurial > spidermonkey-playground
changeset 1:7444443d2646
added simple script and wrapper.cpp from jetpack
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Thu, 18 Jun 2009 20:35:22 -0700 |
parents | d14fb1d5326c |
children | 1f3e9c8df4f0 |
files | pavement.py spidermonkey-playground.c spidermonkey-playground.cpp wrapper.cpp wrapper.h |
diffstat | 5 files changed, 407 insertions(+), 62 deletions(-) [+] |
line wrap: on
line diff
--- a/pavement.py Thu Jun 18 20:13:37 2009 -0700 +++ b/pavement.py Thu Jun 18 20:35:22 2009 -0700 @@ -11,7 +11,7 @@ incdir = os.path.join(objdir, "include") libdir = os.path.join(objdir, "lib") - cmdline = ["gcc", "spidermonkey-playground.c", + cmdline = ["g++", "spidermonkey-playground.cpp", "wrapper.cpp", "-o", "spidermonkey-playground", "-I%s" % incdir, "-L%s" % libdir, "-lmozjs"]
--- a/spidermonkey-playground.c Thu Jun 18 20:13:37 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -#include "jsapi.h" - -/* The class of the global object. */ -static JSClass global_class = { - "global", JSCLASS_GLOBAL_FLAGS, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -/* The error reporter callback. */ -void reportError(JSContext *cx, const char *message, JSErrorReport *report) -{ - fprintf(stderr, "%s:%u:%s\n", - report->filename ? report->filename : "<no filename>", - (unsigned int) report->lineno, - message); -} - -int main(int argc, const char *argv[]) -{ - /* JS variables. */ - JSRuntime *rt; - JSContext *cx; - JSObject *global; - - /* Create a JS runtime. */ - rt = JS_NewRuntime(8L * 1024L * 1024L); - if (rt == NULL) - return 1; - - /* Create a context. */ - cx = JS_NewContext(rt, 8192); - if (cx == NULL) - return 1; - JS_SetOptions(cx, JSOPTION_VAROBJFIX); - JS_SetVersion(cx, JSVERSION_LATEST); - JS_SetErrorReporter(cx, reportError); - - /* Create the global object. */ - global = JS_NewObject(cx, &global_class, NULL, NULL); - if (global == NULL) - return 1; - - /* Populate the global object with the standard globals, - like Object and Array. */ - if (!JS_InitStandardClasses(cx, global)) - return 1; - - /* Your application code here. This may include JSAPI calls - to create your own custom JS objects and run scripts. */ - printf("JS initialized.\n"); - - /* Cleanup. */ - JS_DestroyContext(cx); - JS_DestroyRuntime(rt); - JS_ShutDown(); - - printf("Farewell.\n"); - return 0; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spidermonkey-playground.cpp Thu Jun 18 20:35:22 2009 -0700 @@ -0,0 +1,111 @@ +#include "string.h" +#include "jsapi.h" + +#include "wrapper.h" + +/* The class of the global object. */ +static JSClass global_class = { + "global", JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +/* The error reporter callback. */ +static void reportError(JSContext *cx, const char *message, + JSErrorReport *report) +{ + fprintf(stderr, "%s:%u:%s\n", + report->filename ? report->filename : "<no filename>", + (unsigned int) report->lineno, + message); +} + +static JSBool print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + char *str; + + if (!JS_ConvertArguments(cx, argc, argv, "s", &str)) + return JS_FALSE; + + printf("%s\n", str); + + return JS_TRUE; +} + +static JSBool wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *wrappee; + JSObject *resolver; + + if (!JS_ConvertArguments(cx, argc, argv, "oo", &wrappee, &resolver)) + return JS_FALSE; + + JSObject *result; + + result = wrapObject(cx, argv[0], argv[1]); + + *rval = OBJECT_TO_JSVAL(result); + return JS_TRUE; +} + +static JSFunctionSpec global_functions[] = { + JS_FS("print", print, 1, 0, 0), + JS_FS("wrap", wrap, 2, 0, 0), + JS_FS_END +}; + +int main(int argc, const char *argv[]) +{ + /* JS variables. */ + JSRuntime *rt; + JSContext *cx; + JSObject *global; + + /* Create a JS runtime. */ + rt = JS_NewRuntime(8L * 1024L * 1024L); + if (rt == NULL) + return 1; + + /* Create a context. */ + cx = JS_NewContext(rt, 8192); + if (cx == NULL) + return 1; + JS_SetOptions(cx, JSOPTION_VAROBJFIX); + JS_SetVersion(cx, JSVERSION_LATEST); + JS_SetErrorReporter(cx, reportError); + + /* Create the global object. */ + global = JS_NewObject(cx, &global_class, NULL, NULL); + if (global == NULL) + return 1; + + /* Populate the global object with the standard globals, + like Object and Array. */ + if (!JS_InitStandardClasses(cx, global)) + return 1; + + if (!JS_DefineFunctions(cx, global, global_functions)) + return 1; + + /* Your application code here. This may include JSAPI calls + to create your own custom JS objects and run scripts. */ + char *source = "print('Hello World.'); wrap({}, {});"; + jsval rval; + if (!JS_EvaluateScript(cx, global, source, + strlen(source), "<string>", 1, + &rval)) { + printf("An error occurred.\n"); + return 1; + } + + /* Cleanup. */ + JS_DestroyContext(cx); + JS_DestroyRuntime(rt); + JS_ShutDown(); + + printf("Farewell.\n"); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wrapper.cpp Thu Jun 18 20:35:22 2009 -0700 @@ -0,0 +1,291 @@ +#include "wrapper.h" + +// Reserved slot ID for the resolver (meta) object +#define SLOT_RESOLVER 0 + +// Reserved slot ID for the object to be wrapped +#define SLOT_WRAPPEE 1 + +static JSBool +resolverHasMethod(JSContext *cx, JSObject *obj, const char *name) +{ + jsval resolver; + if (!JS_GetReservedSlot(cx, obj, SLOT_RESOLVER, &resolver)) + return JS_FALSE; + JSObject *resolverObj = JSVAL_TO_OBJECT(resolver); + + JSBool hasProperty; + if (!JS_HasProperty(cx, resolverObj, name, &hasProperty)) + return JS_FALSE; + return hasProperty; + + // TODO: Check to make sure the property is a function? +} + +static JSBool +delegateToResolver(JSContext *cx, JSObject *obj, const char *name, + uintN argc, jsval *argv, jsval *rval) +{ + jsval resolver; + if (!JS_GetReservedSlot(cx, obj, SLOT_RESOLVER, &resolver)) + return JS_FALSE; + JSObject *resolverObj = JSVAL_TO_OBJECT(resolver); + + uintN allArgc = argc + 2; + jsval allArgv[10]; + if (allArgc > 10) { + JS_ReportError(cx, "Didn't expect so many args!"); + return JS_FALSE; + } + + if (!JS_GetReservedSlot(cx, obj, SLOT_WRAPPEE, allArgv)) + return JS_FALSE; + allArgv[1] = OBJECT_TO_JSVAL(obj); + + for (unsigned int i = 0; i < argc; i++) + allArgv[i + 2] = argv[i]; + + if (!JS_CallFunctionName(cx, resolverObj, name, allArgc, allArgv, rval)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +enumerate(JSContext *cx, JSObject *obj) +{ + if (resolverHasMethod(cx, obj, "enumerate")) { + jsval rval; + if (!delegateToResolver(cx, obj, "enumerate", 0, NULL, &rval)) + return JS_FALSE; + } + return JS_EnumerateStub(cx, obj); +} + +static JSBool +resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + if (resolverHasMethod(cx, obj, "resolve")) { + jsval rval; + jsval args[1]; + args[0] = id; + if (!delegateToResolver(cx, obj, "resolve", 1, args, &rval)) + return JS_FALSE; + + if (JSVAL_IS_OBJECT(rval)) + *objp = JSVAL_TO_OBJECT(rval); + else + *objp = NULL; + + return JS_TRUE; + } + *objp = NULL; + return JS_TRUE; +} + +static JSBool +propertyOp(const char *name, JSContext *cx, JSObject *obj, jsval id, + jsval *vp) +{ + if (resolverHasMethod(cx, obj, name)) { + jsval rval; + jsval args[2]; + args[0] = id; + args[1] = *vp; + if (!delegateToResolver(cx, obj, name, 2, args, &rval)) + return JS_FALSE; + + if (!JSVAL_IS_VOID(rval)) + *vp = rval; + return JS_TRUE; + } + return JS_PropertyStub(cx, obj, id, vp); +} + +static JSBool +addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return propertyOp("addProperty", cx, obj, id, vp); +} + +static JSBool +delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (resolverHasMethod(cx, obj, "delProperty")) { + jsval rval; + jsval args[1]; + args[0] = id; + if (!delegateToResolver(cx, obj, "delProperty", 1, args, &rval)) + return JS_FALSE; + + // TODO: The MDC docs say that setting *vp to JSVAL_FALSE and then + // returning JS_TRUE should indicate that the property can't be + // deleted, but this doesn't seem to actually be the case. + if (!JSVAL_IS_BOOLEAN(rval)) { + JS_ReportError(cx, "delProperty must return a boolean"); + return JS_FALSE; + } + *vp = rval; + return JS_TRUE; + } + return JS_PropertyStub(cx, obj, id, vp); +} + +static JSBool +getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return propertyOp("getProperty", cx, obj, id, vp); +} + +static JSBool +setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return propertyOp("setProperty", cx, obj, id, vp); +} + +static JSBool +checkAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp) +{ + // TODO: This effectively overrides the default JS_CheckAccess() and + // always grants access to any property on the object! + return JS_GetPropertyById(cx, obj, id, vp); +} + +static JSObject * +wrappedObject(JSContext *cx, JSObject *obj) { + jsval wrappee; + if (!JS_GetReservedSlot(cx, obj, SLOT_WRAPPEE, &wrappee)) + return obj; + return JSVAL_TO_OBJECT(wrappee); +} + +static JSBool +equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { + if (resolverHasMethod(cx, obj, "equality")) { + jsval rval; + jsval args[1]; + args[0] = v; + + if (!delegateToResolver(cx, obj, "equality", 1, args, &rval)) + return JS_FALSE; + + if (!JSVAL_IS_BOOLEAN(rval)) { + JS_ReportError(cx, "equality must return a boolean"); + return JS_FALSE; + } + *bp = JSVAL_TO_BOOLEAN(rval); + return JS_TRUE; + } + if (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) == obj) + *bp = JS_TRUE; + else + *bp = JS_FALSE; + return JS_TRUE; +} + +static JSBool +delegateNativeToResolver(const char *name, JSContext *cx, JSObject *thisPtr, + JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + JSObject *array = JS_NewArrayObject(cx, argc, argv); + jsval delegateArgv[2]; + delegateArgv[0] = OBJECT_TO_JSVAL(thisPtr); + delegateArgv[1] = OBJECT_TO_JSVAL(array); + + return delegateToResolver(cx, obj, name, 2, delegateArgv, rval); +} + +static JSBool +call(JSContext *cx, JSObject *thisPtr, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *obj = JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)); + + if (resolverHasMethod(cx, obj, "call")) + return delegateNativeToResolver("call", cx, thisPtr, obj, argc, argv, + rval); + + JS_ReportError(cx, "Either the object isn't callable, or the caller " + "doesn't have permission to call it."); + return JS_FALSE; +} + +static JSBool +construct(JSContext *cx, JSObject *thisPtr, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *obj = JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)); + + if (resolverHasMethod(cx, obj, "construct")) + return delegateNativeToResolver("construct", cx, thisPtr, obj, argc, argv, + rval); + + JS_ReportError(cx, "Either the object can't be used as a constructor, or " + "the caller doesn't have permission to use it."); + return JS_FALSE; +} + +static JSBool +convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + if (resolverHasMethod(cx, obj, "convert")) { + JSString *typeStr = JS_NewStringCopyZ(cx, JS_GetTypeName(cx, type)); + jsval args[1]; + args[0] = STRING_TO_JSVAL(typeStr); + return delegateToResolver(cx, obj, "convert", 1, args, vp); + } + return JS_ConvertStub(cx, obj, type, vp); +} + +static JSObject * +iteratorObject(JSContext *cx, JSObject *obj, JSBool keysonly) +{ + if (resolverHasMethod(cx, obj, "iteratorObject")) { + jsval rval; + jsval args[1]; + args[0] = BOOLEAN_TO_JSVAL(keysonly); + if (!delegateToResolver(cx, obj, "iteratorObject", 1, args, &rval)) + return NULL; + if (!JSVAL_IS_OBJECT(rval)) { + JS_ReportError(cx, "iteratorObject() must return an object."); + return NULL; + } + return JSVAL_TO_OBJECT(rval); + } + JS_ReportError(cx, "iteratorObject() is unimplemented."); + return NULL; +} + +JSExtendedClass sXPC_FlexibleWrapper_JSClass = { + // JSClass (JSExtendedClass.base) initialization + { "XPCFlexibleWrapper", + JSCLASS_NEW_RESOLVE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_RESERVED_SLOTS(2), + addProperty, delProperty, + getProperty, setProperty, + enumerate, (JSResolveOp)resolve, + convert, JS_FinalizeStub, + NULL, checkAccess, + call, construct, + NULL, NULL, + NULL, NULL + }, + // JSExtendedClass initialization + equality, + NULL, // outerObject + NULL, // innerObject + iteratorObject, + wrappedObject, + JSCLASS_NO_RESERVED_MEMBERS +}; + +JSObject *wrapObject(JSContext *cx, jsval object, jsval resolver) +{ + JSObject *obj = JS_NewObjectWithGivenProto( + cx, + &sXPC_FlexibleWrapper_JSClass.base, + NULL, + JS_GetScopeChain(cx) + ); + JS_SetReservedSlot(cx, obj, SLOT_RESOLVER, resolver); + JS_SetReservedSlot(cx, obj, SLOT_WRAPPEE, object); + return obj; +}