diff options
Diffstat (limited to 'scripting/engine_spidermonkey.cpp')
-rw-r--r-- | scripting/engine_spidermonkey.cpp | 129 |
1 files changed, 114 insertions, 15 deletions
diff --git a/scripting/engine_spidermonkey.cpp b/scripting/engine_spidermonkey.cpp index aed7b13..64fe21c 100644 --- a/scripting/engine_spidermonkey.cpp +++ b/scripting/engine_spidermonkey.cpp @@ -242,6 +242,10 @@ namespace mongo { return val; } + int toNumberInt( JSObject *o ) { + return (boost::uint32_t)(boost::int32_t) getNumber( o, "floatApprox" ); + } + double toNumber( jsval v ) { double d; uassert( 10214 , "not a number" , JS_ValueToNumber( _context , v , &d ) ); @@ -492,7 +496,6 @@ namespace mongo { return func; } - jsval toval( double d ) { jsval val; assert( JS_NewNumberValue( _context, d , &val ) ); @@ -531,7 +534,7 @@ namespace mongo { JSObject * toJSObject( const BSONObj * obj , bool readOnly=false ) { static string ref = "$ref"; - if ( ref == obj->firstElement().fieldName() ) { + if ( ref == obj->firstElementFieldName() ) { JSObject * o = JS_NewObject( _context , &dbref_class , NULL, NULL); CHECKNEWOBJECT(o,_context,"toJSObject1"); assert( JS_SetPrivate( _context , o , (void*)(new BSONHolder( obj->getOwned() ) ) ) ); @@ -551,8 +554,9 @@ namespace mongo { void makeLongObj( long long n, JSObject * o ) { boost::uint64_t val = (boost::uint64_t)n; CHECKNEWOBJECT(o,_context,"NumberLong1"); - setProperty( o , "floatApprox" , toval( (double)(boost::int64_t)( val ) ) ); - if ( (boost::int64_t)val != (boost::int64_t)(double)(boost::int64_t)( val ) ) { + double floatApprox = (double)(boost::int64_t)val; + setProperty( o , "floatApprox" , toval( floatApprox ) ); + if ( (boost::int64_t)val != (boost::int64_t)floatApprox ) { // using 2 doubles here instead of a single double because certain double // bit patterns represent undefined values and sm might trash them setProperty( o , "top" , toval( (double)(boost::uint32_t)( val >> 32 ) ) ); @@ -566,6 +570,19 @@ namespace mongo { return OBJECT_TO_JSVAL( o ); } + void makeIntObj( int n, JSObject * o ) { + boost::uint32_t val = (boost::uint32_t)n; + CHECKNEWOBJECT(o,_context,"NumberInt1"); + double floatApprox = (double)(boost::int32_t)val; + setProperty( o , "floatApprox" , toval( floatApprox ) ); + } + + jsval toval( int n ) { + JSObject * o = JS_NewObject( _context , &numberint_class , 0 , 0 ); + makeIntObj( n, o ); + return OBJECT_TO_JSVAL( o ); + } + jsval toval( const BSONElement& e ) { switch( e.type() ) { @@ -576,6 +593,8 @@ namespace mongo { case NumberDouble: case NumberInt: return toval( e.number() ); +// case NumberInt: +// return toval( e.numberInt() ); case Symbol: // TODO: should we make a special class for this case String: return toval( e.valuestr() ); @@ -651,7 +670,7 @@ namespace mongo { return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) ); } case Date: - return OBJECT_TO_JSVAL( js_NewDateObjectMsec( _context , (jsdouble) e.date().millis ) ); + return OBJECT_TO_JSVAL( js_NewDateObjectMsec( _context , (jsdouble) ((long long)e.date().millis) ) ); case MinKey: return OBJECT_TO_JSVAL( JS_NewObject( _context , &minkey_class , 0 , 0 ) ); @@ -903,6 +922,68 @@ namespace mongo { // --- global helpers --- + JSBool hexToBinData(JSContext * cx, jsval *rval, int subtype, string s) { + JSObject * o = JS_NewObject( cx , &bindata_class , 0 , 0 ); + CHECKNEWOBJECT(o,_context,"Bindata_BinData1"); + int len = s.size() / 2; + char * data = new char[len]; + char *p = data; + const char *src = s.c_str(); + for( size_t i = 0; i+1 < s.size(); i += 2 ) { + *p++ = fromHex(src + i); + } + assert( JS_SetPrivate( cx , o , new BinDataHolder( data , len ) ) ); + Convertor c(cx); + c.setProperty( o, "len", c.toval((double)len) ); + c.setProperty( o, "type", c.toval((double)subtype) ); + *rval = OBJECT_TO_JSVAL( o ); + delete data; + return JS_TRUE; + } + + JSBool _HexData( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { + Convertor c( cx ); + if ( argc != 2 ) { + JS_ReportError( cx , "HexData needs 2 arguments -- HexData(subtype,hexstring)" ); + return JS_FALSE; + } + int type = (int)c.toNumber( argv[ 0 ] ); + if ( type == 2 ) { + JS_ReportError( cx , "BinData subtype 2 is deprecated" ); + return JS_FALSE; + } + string s = c.toString(argv[1]); + return hexToBinData(cx, rval, type, s); + } + + JSBool _UUID( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { + Convertor c( cx ); + if ( argc != 1 ) { + JS_ReportError( cx , "UUID needs argument -- UUID(hexstring)" ); + return JS_FALSE; + } + string s = c.toString(argv[0]); + if( s.size() != 32 ) { + JS_ReportError( cx , "bad UUID hex string len" ); + return JS_FALSE; + } + return hexToBinData(cx, rval, 3, s); + } + + JSBool _MD5( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { + Convertor c( cx ); + if ( argc != 1 ) { + JS_ReportError( cx , "MD5 needs argument -- MD5(hexstring)" ); + return JS_FALSE; + } + string s = c.toString(argv[0]); + if( s.size() != 32 ) { + JS_ReportError( cx , "bad MD5 hex string len" ); + return JS_FALSE; + } + return hexToBinData(cx, rval, 5, s); + } + JSBool native_print( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { stringstream ss; Convertor c( cx ); @@ -920,6 +1001,7 @@ namespace mongo { Convertor c(cx); NativeFunction func = (NativeFunction)((long long)c.getNumber( obj , "x" ) ); + void* data = (void*)((long long)c.getNumber( obj , "y" ) ); assert( func ); BSONObj a; @@ -934,7 +1016,7 @@ namespace mongo { BSONObj out; try { - out = func( a ); + out = func( a, data ); } catch ( std::exception& e ) { JS_ReportError( cx , e.what() ); @@ -963,6 +1045,9 @@ namespace mongo { { "nativeHelper" , &native_helper , 1 , 0 , 0 } , { "load" , &native_load , 1 , 0 , 0 } , { "gc" , &native_gc , 1 , 0 , 0 } , + { "UUID", &_UUID, 0, 0, 0 } , + { "MD5", &_MD5, 0, 0, 0 } , + { "HexData", &_HexData, 0, 0, 0 } , { 0 , 0 , 0 , 0 , 0 } }; @@ -1050,6 +1135,7 @@ namespace mongo { if ( val != JSVAL_NULL && val != JSVAL_VOID && JSVAL_IS_OBJECT( val ) ) { // TODO: this is a hack to get around sub objects being modified + // basically right now whenever a sub object is read we mark whole obj as possibly modified JSObject * oo = JSVAL_TO_OBJECT( val ); if ( JS_InstanceOf( cx , oo , &bson_class , 0 ) || JS_IsArrayObject( cx , oo ) ) { @@ -1346,6 +1432,12 @@ namespace mongo { } } + void setFunction( const char *field , const char * code ) { + smlock; + jsval v = OBJECT_TO_JSVAL(JS_GetFunctionObject(_convertor->compileFunction(code))); + JS_SetProperty( _context , _global , field , &v ); + } + void rename( const char * from , const char * to ) { smlock; jsval v; @@ -1462,33 +1554,35 @@ namespace mongo { return worked; } - int invoke( JSFunction * func , const BSONObj& args, int timeoutMs , bool ignoreReturn ) { + int invoke( JSFunction * func , const BSONObj* args, const BSONObj* recv, int timeoutMs , bool ignoreReturn, bool readOnlyArgs, bool readOnlyRecv ) { smlock; precall(); assert( JS_EnterLocalRootScope( _context ) ); - int nargs = args.nFields(); + int nargs = args ? args->nFields() : 0; scoped_array<jsval> smargsPtr( new jsval[nargs] ); if ( nargs ) { - BSONObjIterator it( args ); + BSONObjIterator it( *args ); for ( int i=0; i<nargs; i++ ) { smargsPtr[i] = _convertor->toval( it.next() ); } } - if ( args.isEmpty() ) { + if ( !args ) { _convertor->setProperty( _global , "args" , JSVAL_NULL ); } else { - setObject( "args" , args , true ); // this is for backwards compatability + setObject( "args" , *args , true ); // this is for backwards compatability } JS_LeaveLocalRootScope( _context ); installInterrupt( timeoutMs ); jsval rval; + setThis(recv); JSBool ret = JS_CallFunction( _context , _this ? _this : _global , func , nargs , smargsPtr.get() , &rval ); + setThis(0); uninstallInterrupt( timeoutMs ); if ( !ret ) { @@ -1502,8 +1596,8 @@ namespace mongo { return 0; } - int invoke( ScriptingFunction funcAddr , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = 0 ) { - return invoke( (JSFunction*)funcAddr , args , timeoutMs , ignoreReturn ); + int invoke( ScriptingFunction funcAddr , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = 0, bool readOnlyArgs = false, bool readOnlyRecv = false ) { + return invoke( (JSFunction*)funcAddr , args , recv, timeoutMs , ignoreReturn, readOnlyArgs, readOnlyRecv); } void gotError( string s ) { @@ -1514,13 +1608,18 @@ namespace mongo { return _error; } - void injectNative( const char *field, NativeFunction func ) { + void injectNative( const char *field, NativeFunction func, void* data ) { smlock; string name = field; _convertor->setProperty( _global , (name + "_").c_str() , _convertor->toval( (double)(long long)func ) ); stringstream code; - code << field << "_" << " = { x : " << field << "_ }; "; + if (data) { + _convertor->setProperty( _global , (name + "_data_").c_str() , _convertor->toval( (double)(long long)data ) ); + code << field << "_" << " = { x : " << field << "_ , y: " << field << "_data_ }; "; + } else { + code << field << "_" << " = { x : " << field << "_ }; "; + } code << field << " = function(){ return nativeHelper.apply( " << field << "_ , arguments ); }"; exec( code.str() ); } |