#ifndef SCRIPTING_H #define SCRIPTING_H #define XP_WIN #include #include #include #include #include extern JSContext* cx; extern JSObject* global; // Initialize scripting, declaring all our JS classes and functions void ScriptingInit(); // Run a script void RunScript(const std::string& filename); // Shortcut for reporting a nice error to the user if an assertion fails #define jsASSERT(expr) if(!(expr)) { JS_ReportError(cx, \ "Assertion failed in %s (%s:%d): " #expr, __FUNCTION__, __FILE__, __LINE__); return JS_FALSE; } // A namespace containing utility functions used internally by the scripting API namespace js { // An interface to a property object (we'll have a subclass below once we have // the conversion functions, but this is needed for Class which is needed for // some of the conversion functions) class AbstractProperty { public: virtual ~AbstractProperty() {}; virtual JSBool set(JSObject *obj, jsval *val) = 0; virtual JSBool get(JSObject *obj, jsval *rval) = 0; }; // An easy way to wrap an entire class into JavaScript; just call add on // the methods and properties, then call Class::init. template class Class { public: static JSClass jsClass; private: static JSNative constructor; static std::vector methodSpecs; static std::vector propertySpecs; static std::vector properties; public: static void addMethod(JSNative func, int numArgs, const char* name) { JSFunctionSpec spec = { name, func, numArgs, 0, 0 }; methodSpecs.push_back(spec); } static void addProperty(AbstractProperty* (*propGen)(), const char* name) { AbstractProperty* prop = propGen(); int index = properties.size(); properties.push_back(prop); JSPropertySpec spec = { name, index, JSPROP_ENUMERATE }; propertySpecs.push_back(spec); } private: static JSBool getProperty(JSContext*, JSObject *obj, jsval id, jsval *vp) { if(!JSVAL_IS_INT(id)) { if(strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "constructor")==0) { // For some reason this is called for the constructor *vp = JSVAL_NULL; return JS_TRUE; } else { return JS_TRUE; } }; int index = JSVAL_TO_INT(id); properties[index]->get(obj, vp); return JS_TRUE; } static JSBool setProperty(JSContext*, JSObject *obj, jsval id, jsval *vp) { if(!JSVAL_IS_INT(id)) return JS_TRUE; int index = JSVAL_TO_INT(id); properties[index]->set(obj, vp); return JS_TRUE; } public: static void init(const char* name, JSNative cons) { constructor = cons; JSFunctionSpec* methods = new JSFunctionSpec[methodSpecs.size() + 1]; for(size_t i=0; i struct PointerClass { typedef int type; }; template struct PointerClass { typedef Class type; }; // Conversion functions from types to Javascript template jsval ToJsval(T v) { // T is really a pointer here (since we want to do ToJsval(pointer)) if(v == 0) { return JSVAL_NULL; } else { JSObject* obj = JS_NewObject(cx, &(PointerClass::type::jsClass), 0, 0); JS_SetPrivate(cx, obj, v); return OBJECT_TO_JSVAL(obj); } } template<> inline jsval ToJsval(bool v) { return v ? JSVAL_TRUE : JSVAL_FALSE; } template<> inline jsval ToJsval(int v) { return INT_TO_JSVAL(v); } template<> inline jsval ToJsval(double v) { jsval ret; JS_NewDoubleValue(cx, v, &ret); return ret; } template<> inline jsval ToJsval(float v) { jsval ret; JS_NewDoubleValue(cx, v, &ret); return ret; } template<> inline jsval ToJsval(const char* v) { int length = strlen(v); char* buf = (char*) JS_malloc(cx, length+1); memcpy(buf, v, length+1); return STRING_TO_JSVAL(JS_NewString(cx, buf, length+1)); } template<> inline jsval ToJsval(const std::string& v) { return ToJsval(v.c_str()); } template<> inline jsval ToJsval(JSFunction* v) { if(v == 0) return JSVAL_NULL; else return OBJECT_TO_JSVAL(JS_GetFunctionObject(v)); } // Conversion functions from Javascript to types template T To(jsval v) { // T is really a pointer here (since we want to do To(jsval)) if(v == JSVAL_NULL) { return 0; } else { JSObject* obj = JSVAL_TO_OBJECT(v); return (T) JS_GetPrivate(cx, obj); } } template<> inline bool To(jsval v) { return (v==JSVAL_TRUE); } template<> inline int To(jsval v) { return JSVAL_TO_INT(v); } template<> inline double To(jsval v) { jsdouble d; JS_ValueToNumber(cx, v, &d); return d; } template<> inline float To(jsval v) { jsdouble d; JS_ValueToNumber(cx, v, &d); return (float) d; } template<> inline std::string To(jsval v) { return std::string(JS_GetStringBytes(JS_ValueToString(cx, v))); } template<> inline JSFunction* To(jsval v) { if(v == JSVAL_NULL) return 0; return JS_ValueToFunction(cx, v); } // A nice way of automatically creating a JS function for any // function or method; could be made shorter by using some kind of // preprocessor loop (boost), but that might get confusing. // Combining member function pointers and templates... fun. // Functions with return type R and up to three arguments A, B, C template JSBool Function (JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==0); *rval = ToJsval(func()); return JS_TRUE; } template JSBool Function (JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==1); *rval = ToJsval(func(To(argv[0]))); return JS_TRUE; } template JSBool Function(JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==2); *rval = ToJsval(func(To(argv[0]), To(argv[1]))); return JS_TRUE; } template JSBool Function(JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==3); *rval = ToJsval(func(To(argv[0]), To(argv[1]), To(argv[2]))); return JS_TRUE; } // Void functions with up to three arguments A, B, C template JSBool VoidFunction (JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==1); func(); *rval = JSVAL_VOID; return JS_TRUE; } template JSBool VoidFunction (JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==1); func(To(argv[0])); *rval = JSVAL_VOID; return JS_TRUE; } template JSBool VoidFunction (JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==2); func(To(argv[0]), To(argv[1])); *rval = JSVAL_VOID; return JS_TRUE; } template JSBool VoidFunction (JSContext *cx, JSObject*, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==3); func(To(argv[0]), To(argv[1]), To(argv[2])); *rval = JSVAL_VOID; return JS_TRUE; } // Methods of O with return type R and up to three arguments A, B, C template JSBool Method (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==0); O* o = (O*) JS_GetPrivate(cx, obj); *rval = ToJsval((o->*func)()); return JS_TRUE; } template JSBool Method (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==1); O* o = (O*) JS_GetPrivate(cx, obj); *rval = ToJsval((o->*func)(To(argv[0]))); return JS_TRUE; } template JSBool Method (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==2); O* o = (O*) JS_GetPrivate(cx, obj); *rval = ToJsval((o->*func)(To(argv[0]), To(argv[1]))); return JS_TRUE; } template JSBool Method (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==3); O* o = (O*) JS_GetPrivate(cx, obj); *rval = ToJsval((o->*func)(To(argv[0]), To(argv[1]), To(argv[2]))); return JS_TRUE; } // Void methods of O with up to three arguments A, B, C template JSBool VoidMethod (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==0); O* o = (O*) JS_GetPrivate(cx, obj); (o->*func)(); *rval = JSVAL_VOID; return JS_TRUE; } template JSBool VoidMethod (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==1); O* o = (O*) JS_GetPrivate(cx, obj); (o->*func)(To(argv[0])); *rval = JSVAL_VOID; return JS_TRUE; } template JSBool VoidMethod (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==2); O* o = (O*) JS_GetPrivate(cx, obj); (o->*func)(To(argv[0]), To(argv[1])); *rval = JSVAL_VOID; return JS_TRUE; } template JSBool VoidMethod (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==3); O* o = (O*) JS_GetPrivate(cx, obj); (o->*func)(To(argv[0]), To(argv[1]), To(argv[2])); *rval = JSVAL_VOID; return JS_TRUE; } // Constructors of O with up to three arguments A, B, C template JSBool Constructor (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsASSERT(argc==0); JS_SetPrivate(cx, obj, new O()); return JS_TRUE; } template JSBool Constructor (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*) { jsASSERT(argc==1); JS_SetPrivate(cx, obj, new O(To(argv[0]))); return JS_TRUE; } template JSBool Constructor (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*) { jsASSERT(argc==2); JS_SetPrivate(cx, obj, new O(To(argv[0]), To(argv[1]))); return JS_TRUE; } template JSBool Constructor (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*) { jsASSERT(argc==3); JS_SetPrivate(cx, obj, new O(To(argv[0]), To(argv[1]), To(argv[2]))); return JS_TRUE; } template JSBool Constructor (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*) { jsASSERT(argc==4); JS_SetPrivate(cx, obj, new O( To(argv[0]), To(argv[1]), To(argv[2]), To(argv[3]))); return JS_TRUE; } // Implementation of AbstractProperty for a given field of an object template class PropertyImpl: public AbstractProperty { public: virtual ~PropertyImpl() {} virtual JSBool set(JSObject *obj, jsval *val) { O* o = (O*) JS_GetPrivate(cx, obj); (o->*field) = To(*val); return JS_TRUE; } virtual JSBool get(JSObject *obj, jsval *rval) { O* o = (O*) JS_GetPrivate(cx, obj); *rval = ToJsval(o->*field); return JS_TRUE; } }; // A nice function to pass to AddProperty so we don't have to type new PropertyImpl() template AbstractProperty* Property() { return new PropertyImpl(); } } // Call a void function with 1 argument template void CallJSFunction(JSFunction* f, T arg) { if(f == 0) { return; } jsval argv[1] = { js::ToJsval(arg) }; jsval rval; JS_CallFunction(cx, global, f, 1, argv, &rval); } #endif