summaryrefslogtreecommitdiff
path: root/scripting/engine_spidermonkey.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scripting/engine_spidermonkey.cpp')
-rw-r--r--scripting/engine_spidermonkey.cpp129
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() );
}