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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wrapper.h	Thu Jun 18 20:35:22 2009 -0700
@@ -0,0 +1,4 @@
+#include "jsapi.h"
+
+extern JSExtendedClass sXPC_FlexibleWrapper_JSClass;
+extern JSObject *wrapObject(JSContext *cx, jsval object, jsval resolver);