summaryrefslogtreecommitdiff
path: root/scripting
diff options
context:
space:
mode:
Diffstat (limited to 'scripting')
-rw-r--r--scripting/bench.cpp105
-rw-r--r--scripting/engine.cpp45
-rw-r--r--scripting/engine.h43
-rw-r--r--scripting/engine_java.cpp10
-rw-r--r--scripting/engine_spidermonkey.cpp129
-rw-r--r--scripting/engine_spidermonkey.h34
-rw-r--r--scripting/engine_v8.cpp1166
-rw-r--r--scripting/engine_v8.h122
-rw-r--r--scripting/sm_db.cpp120
-rw-r--r--scripting/utils.cpp19
-rw-r--r--scripting/v8_db.cpp605
-rw-r--r--scripting/v8_db.h132
-rw-r--r--scripting/v8_utils.cpp78
-rw-r--r--scripting/v8_utils.h6
-rw-r--r--scripting/v8_wrapper.cpp569
-rw-r--r--scripting/v8_wrapper.h15
16 files changed, 2071 insertions, 1127 deletions
diff --git a/scripting/bench.cpp b/scripting/bench.cpp
index 2723985..9ada7d6 100644
--- a/scripting/bench.cpp
+++ b/scripting/bench.cpp
@@ -30,13 +30,6 @@
namespace mongo {
- /**
- * benchQuery( "foo" , { _id : 1 } )
- */
- BSONObj benchQuery( const BSONObj& args ) {
- return BSONObj();
- }
-
struct BenchRunConfig {
BenchRunConfig() {
host = "localhost";
@@ -54,7 +47,7 @@ namespace mongo {
string db;
unsigned parallel;
- int seconds;
+ double seconds;
BSONObj ops;
@@ -64,6 +57,73 @@ namespace mongo {
bool error;
};
+ static bool _hasSpecial( const BSONObj& obj ) {
+ BSONObjIterator i( obj );
+ while ( i.more() ) {
+ BSONElement e = i.next();
+ if ( e.fieldName()[0] == '#' )
+ return true;
+
+ if ( ! e.isABSONObj() )
+ continue;
+
+ if ( _hasSpecial( e.Obj() ) )
+ return true;
+ }
+ return false;
+ }
+
+ static void _fixField( BSONObjBuilder& b , const BSONElement& e ) {
+ assert( e.type() == Object );
+
+ BSONObj sub = e.Obj();
+ assert( sub.nFields() == 1 );
+
+ BSONElement f = sub.firstElement();
+ if ( str::equals( "#RAND_INT" , f.fieldName() ) ) {
+ BSONObjIterator i( f.Obj() );
+ int min = i.next().numberInt();
+ int max = i.next().numberInt();
+
+ int x = min + ( rand() % ( max - min ) );
+ b.append( e.fieldName() , x );
+ }
+ else {
+ uasserted( 14811 , str::stream() << "invalid bench dynamic piece: " << f.fieldName() );
+ }
+
+ }
+
+ static void fixQuery( BSONObjBuilder& b , const BSONObj& obj ) {
+ BSONObjIterator i( obj );
+ while ( i.more() ) {
+ BSONElement e = i.next();
+
+ if ( e.type() != Object ) {
+ b.append( e );
+ continue;
+ }
+
+ BSONObj sub = e.Obj();
+ if ( sub.firstElement().fieldName()[0] != '#' ) {
+ b.append( e );
+ continue;
+ }
+
+ _fixField( b , e );
+ }
+ }
+
+
+ static BSONObj fixQuery( const BSONObj& obj ) {
+ if ( ! _hasSpecial( obj ) )
+ return obj;
+
+ BSONObjBuilder b( obj.objsize() + 128 );
+ fixQuery( b , obj );
+ return b.obj();
+ }
+
static void benchThread( BenchRunConfig * config ) {
ScopedDbConnection conn( config->host );
config->threadsReady++;
@@ -76,14 +136,20 @@ namespace mongo {
string op = e["op"].String();
if ( op == "findOne" ) {
- conn->findOne( ns , e["query"].Obj() );
+ conn->findOne( ns , fixQuery( e["query"].Obj() ) );
+ }
+ else if ( op == "remove" ) {
+ conn->remove( ns , fixQuery( e["query"].Obj() ) );
+ }
+ else if ( op == "update" ) {
+ conn->update( ns , fixQuery( e["query"].Obj() ) , e["update"].Obj() , e["upsert"].trueValue() );
}
else {
log() << "don't understand op: " << op << endl;
config->error = true;
return;
}
-
+
}
}
@@ -93,7 +159,7 @@ namespace mongo {
/**
* benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 }
*/
- BSONObj benchRun( const BSONObj& argsFake ) {
+ BSONObj benchRun( const BSONObj& argsFake, void* data ) {
assert( argsFake.firstElement().isABSONObj() );
BSONObj args = argsFake.firstElement().Obj();
@@ -109,7 +175,7 @@ namespace mongo {
if ( args["parallel"].isNumber() )
config.parallel = args["parallel"].numberInt();
if ( args["seconds"].isNumber() )
- config.seconds = args["seconds"].numberInt();
+ config.seconds = args["seconds"].number();
config.ops = args["ops"].Obj();
@@ -130,7 +196,7 @@ namespace mongo {
BSONObj before;
conn->simpleCommand( "admin" , &before , "serverStatus" );
- sleepsecs( config.seconds );
+ sleepmillis( (int)(1000.0 * config.seconds) );
BSONObj after;
conn->simpleCommand( "admin" , &after , "serverStatus" );
@@ -147,11 +213,14 @@ namespace mongo {
// compute actual ops/sec
- before = before["opcounters"].Obj();
- after = after["opcounters"].Obj();
+ before = before["opcounters"].Obj().copy();
+ after = after["opcounters"].Obj().copy();
+
+ bool totals = args["totals"].trueValue();
BSONObjBuilder buf;
- buf.append( "note" , "values per second" );
+ if ( ! totals )
+ buf.append( "note" , "values per second" );
{
BSONObjIterator i( after );
@@ -159,7 +228,9 @@ namespace mongo {
BSONElement e = i.next();
double x = e.number();
x = x - before[e.fieldName()].number();
- buf.append( e.fieldName() , x / config.seconds );
+ if ( ! totals )
+ x = x / config.seconds;
+ buf.append( e.fieldName() , x );
}
}
BSONObj zoo = buf.obj();
diff --git a/scripting/engine.cpp b/scripting/engine.cpp
index f9be639..1982940 100644
--- a/scripting/engine.cpp
+++ b/scripting/engine.cpp
@@ -85,10 +85,10 @@ namespace mongo {
}
- int Scope::invoke( const char* code , const BSONObj& args, int timeoutMs ) {
+ int Scope::invoke( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs ) {
ScriptingFunction func = createFunction( code );
uassert( 10207 , "compile failed" , func );
- return invoke( func , args, timeoutMs );
+ return invoke( func , args, recv, timeoutMs );
}
bool Scope::execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs ) {
@@ -241,6 +241,27 @@ namespace mongo {
return f;
}
+ namespace JSFiles {
+ extern const JSFile collection;
+ extern const JSFile db;
+ extern const JSFile mongo;
+ extern const JSFile mr;
+ extern const JSFile query;
+ extern const JSFile utils;
+ extern const JSFile utils_sh;
+ }
+
+ void Scope::execCoreFiles() {
+ // keeping same order as in SConstruct
+ execSetup(JSFiles::utils);
+ execSetup(JSFiles::utils_sh);
+ execSetup(JSFiles::db);
+ execSetup(JSFiles::mongo);
+ execSetup(JSFiles::mr);
+ execSetup(JSFiles::query);
+ execSetup(JSFiles::collection);
+ }
+
typedef map< string , list<Scope*> > PoolToScopes;
class ScopeCache {
@@ -373,8 +394,12 @@ namespace mongo {
void setBoolean( const char *field , bool val ) {
_real->setBoolean( field , val );
}
- void setThis( const BSONObj * obj ) {
- _real->setThis( obj );
+// void setThis( const BSONObj * obj ) {
+// _real->setThis( obj );
+// }
+
+ void setFunction( const char *field , const char * code ) {
+ _real->setFunction(field, code);
}
ScriptingFunction createFunction( const char * code ) {
@@ -392,8 +417,8 @@ namespace mongo {
/**
* @return 0 on success
*/
- int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs , bool ignoreReturn ) {
- return _real->invoke( func , args , timeoutMs , ignoreReturn );
+ int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs , bool ignoreReturn, bool readOnlyArgs, bool readOnlyRecv ) {
+ return _real->invoke( func , args , recv, timeoutMs , ignoreReturn );
}
string getError() {
@@ -407,14 +432,18 @@ namespace mongo {
return _real->execFile( filename , printResult , reportError , assertOnError , timeoutMs );
}
- void injectNative( const char *field, NativeFunction func ) {
- _real->injectNative( field , func );
+ void injectNative( const char *field, NativeFunction func, void* data ) {
+ _real->injectNative( field , func, data );
}
void gc() {
_real->gc();
}
+ void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ) {
+ _real->append(builder, fieldName, scopeName);
+ }
+
private:
string _pool;
Scope * _real;
diff --git a/scripting/engine.h b/scripting/engine.h
index 62afd77..1f9f1f5 100644
--- a/scripting/engine.h
+++ b/scripting/engine.h
@@ -27,18 +27,8 @@ namespace mongo {
const StringData& source;
};
- namespace JSFiles {
- extern const JSFile collection;
- extern const JSFile db;
- extern const JSFile mongo;
- extern const JSFile mr;
- extern const JSFile query;
- extern const JSFile servers;
- extern const JSFile utils;
- }
-
typedef unsigned long long ScriptingFunction;
- typedef BSONObj (*NativeFunction) ( const BSONObj &args );
+ typedef BSONObj (*NativeFunction) ( const BSONObj &args, void* data );
class Scope : boost::noncopyable {
public:
@@ -48,7 +38,7 @@ namespace mongo {
virtual void reset() = 0;
virtual void init( const BSONObj * data ) = 0;
void init( const char * data ) {
- BSONObj o( data , 0 );
+ BSONObj o( data );
init( &o );
}
@@ -79,14 +69,15 @@ namespace mongo {
virtual int type( const char *field ) = 0;
- void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName );
+ virtual void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName );
virtual void setElement( const char *field , const BSONElement& e ) = 0;
virtual void setNumber( const char *field , double val ) = 0;
virtual void setString( const char *field , const char * val ) = 0;
virtual void setObject( const char *field , const BSONObj& obj , bool readOnly=true ) = 0;
virtual void setBoolean( const char *field , bool val ) = 0;
- virtual void setThis( const BSONObj * obj ) = 0;
+ virtual void setFunction( const char *field , const char * code ) = 0;
+// virtual void setThis( const BSONObj * obj ) = 0;
virtual ScriptingFunction createFunction( const char * code );
@@ -94,18 +85,18 @@ namespace mongo {
/**
* @return 0 on success
*/
- virtual int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = false ) = 0;
- void invokeSafe( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 ) {
- int res = invoke( func , args , timeoutMs );
+ virtual int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool readOnlyArgs = false, bool readOnlyRecv = false ) = 0;
+ void invokeSafe( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0, bool readOnlyArgs = false, bool readOnlyRecv = false ) {
+ int res = invoke( func , args , recv, timeoutMs, readOnlyArgs, readOnlyRecv );
if ( res == 0 )
return;
throw UserException( 9004 , (string)"invoke failed: " + getError() );
}
virtual string getError() = 0;
- int invoke( const char* code , const BSONObj& args, int timeoutMs = 0 );
- void invokeSafe( const char* code , const BSONObj& args, int timeoutMs = 0 ) {
- if ( invoke( code , args , timeoutMs ) == 0 )
+ int invoke( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 );
+ void invokeSafe( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 ) {
+ if ( invoke( code , args , recv, timeoutMs ) == 0 )
return;
throw UserException( 9005 , (string)"invoke failed: " + getError() );
}
@@ -119,19 +110,11 @@ namespace mongo {
execSetup(file.source, file.name);
}
- void execCoreFiles() {
- // keeping same order as in SConstruct
- execSetup(JSFiles::utils);
- execSetup(JSFiles::db);
- execSetup(JSFiles::mongo);
- execSetup(JSFiles::mr);
- execSetup(JSFiles::query);
- execSetup(JSFiles::collection);
- }
+ void execCoreFiles();
virtual bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 );
- virtual void injectNative( const char *field, NativeFunction func ) = 0;
+ virtual void injectNative( const char *field, NativeFunction func, void* data = 0 ) = 0;
virtual void gc() = 0;
diff --git a/scripting/engine_java.cpp b/scripting/engine_java.cpp
index fc8945f..5738816 100644
--- a/scripting/engine_java.cpp
+++ b/scripting/engine_java.cpp
@@ -43,7 +43,7 @@ namespace mongo {
-#include "../util/message.h"
+#include "../util/net/message.h"
#include "../db/db.h"
using namespace std;
@@ -405,15 +405,17 @@ namespace mongo {
if ( guess == 0 )
return BSONObj();
- char * buf = (char *) malloc(guess);
- jobject bb = _getEnv()->NewDirectByteBuffer( (void*)buf , guess );
+ BSONObj::Holder* holder = (BSONObj::Holder*) malloc(guess + sizeof(unsigned));
+ holder->zero()
+
+ jobject bb = _getEnv()->NewDirectByteBuffer( (void*)holder->data , guess );
jassert( bb );
int len = _getEnv()->CallStaticIntMethod( _dbhook , _scopeGetObject , id , _getEnv()->NewStringUTF( field ) , bb );
_getEnv()->DeleteLocalRef( bb );
jassert( len > 0 && len < guess );
- BSONObj obj(buf, true);
+ BSONObj obj(holder);
assert( obj.objsize() <= guess );
return obj;
}
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() );
}
diff --git a/scripting/engine_spidermonkey.h b/scripting/engine_spidermonkey.h
index 3ee7495..9fd430d 100644
--- a/scripting/engine_spidermonkey.h
+++ b/scripting/engine_spidermonkey.h
@@ -21,18 +21,6 @@
// START inc hacking
-#if defined( MOZJS )
-
-#define MOZILLA_1_8_BRANCH
-
-#include "mozjs/jsapi.h"
-#include "mozjs/jsdate.h"
-#include "mozjs/jsregexp.h"
-
-#warning if you are using an ubuntu version of spider monkey, we recommend installing spider monkey from source
-
-#elif defined( OLDJS )
-
#ifdef WIN32
#include "jstypes.h"
#undef JS_PUBLIC_API
@@ -46,30 +34,11 @@
#include "jsdate.h"
#include "jsregexp.h"
-#else
-
-#include "js/jsapi.h"
-#include "js/jsobj.h"
-#include "js/jsdate.h"
-#include "js/jsregexp.h"
-
-#endif
-
// END inc hacking
// -- SM 1.6 hacks ---
#ifndef JSCLASS_GLOBAL_FLAGS
-
-#warning old version of spider monkey ( probably 1.6 ) you should upgrade to at least 1.7
-
-#define JSCLASS_GLOBAL_FLAGS 0
-
-JSBool JS_CStringsAreUTF8() {
- return false;
-}
-
-#define SM16
-
+#error old version of spider monkey ( probably 1.6 ) you should upgrade to at least 1.7
#endif
// -- END SM 1.6 hacks ---
@@ -95,6 +64,7 @@ namespace mongo {
extern JSClass bindata_class;
extern JSClass timestamp_class;
extern JSClass numberlong_class;
+ extern JSClass numberint_class;
extern JSClass minkey_class;
extern JSClass maxkey_class;
diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp
index cd186b4..fd69d66 100644
--- a/scripting/engine_v8.cpp
+++ b/scripting/engine_v8.cpp
@@ -15,6 +15,14 @@
* limitations under the License.
*/
+#if defined(_WIN32)
+/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with
+ our usage of boost */
+#include "boost/cstdint.hpp"
+using namespace boost;
+#define V8STDINT_H_
+#endif
+
#include "engine_v8.h"
#include "v8_wrapper.h"
@@ -28,9 +36,239 @@ namespace mongo {
// guarded by v8 mutex
map< unsigned, int > __interruptSpecToThreadId;
+ /**
+ * Unwraps a BSONObj from the JS wrapper
+ */
+ static BSONObj* unwrapBSONObj(const Handle<v8::Object>& obj) {
+ Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+ if (field.IsEmpty() || !field->IsExternal())
+ return 0;
+ void* ptr = field->Value();
+ return (BSONObj*)ptr;
+ }
+
+ static void weakRefBSONCallback(v8::Persistent<v8::Value> p, void* scope) {
+ // should we lock here? no idea, and no doc from v8 of course
+ HandleScope handle_scope;
+ if (!p.IsNearDeath())
+ return;
+ Handle<External> field = Handle<External>::Cast(p->ToObject()->GetInternalField(0));
+ BSONObj* data = (BSONObj*) field->Value();
+ delete data;
+ p.Dispose();
+ }
+
+ Persistent<v8::Object> V8Scope::wrapBSONObject(Local<v8::Object> obj, BSONObj* data) {
+ obj->SetInternalField(0, v8::External::New(data));
+ Persistent<v8::Object> p = Persistent<v8::Object>::New(obj);
+ p.MakeWeak(this, weakRefBSONCallback);
+ return p;
+ }
+
+ static void weakRefArrayCallback(v8::Persistent<v8::Value> p, void* scope) {
+ // should we lock here? no idea, and no doc from v8 of course
+ HandleScope handle_scope;
+ if (!p.IsNearDeath())
+ return;
+ Handle<External> field = Handle<External>::Cast(p->ToObject()->GetInternalField(0));
+ char* data = (char*) field->Value();
+ delete [] data;
+ p.Dispose();
+ }
+
+ Persistent<v8::Object> V8Scope::wrapArrayObject(Local<v8::Object> obj, char* data) {
+ obj->SetInternalField(0, v8::External::New(data));
+ Persistent<v8::Object> p = Persistent<v8::Object>::New(obj);
+ p.MakeWeak(this, weakRefArrayCallback);
+ return p;
+ }
+
+ static Handle<v8::Value> namedGet(Local<v8::String> name, const v8::AccessorInfo &info) {
+ // all properties should be set, otherwise means builtin or deleted
+ if (!(info.This()->HasRealNamedProperty(name)))
+ return v8::Handle<v8::Value>();
+
+ Handle<v8::Value> val = info.This()->GetRealNamedProperty(name);
+ if (!val->IsUndefined()) {
+ // value already cached
+ return val;
+ }
+
+ string key = toSTLString(name);
+ BSONObj *obj = unwrapBSONObj(info.Holder());
+ BSONElement elmt = obj->getField(key.c_str());
+ if (elmt.eoo())
+ return Handle<Value>();
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ val = scope->mongoToV8Element(elmt, false);
+ info.This()->ForceSet(name, val);
+
+ if (elmt.type() == mongo::Object || elmt.type() == mongo::Array) {
+ // if accessing a subobject, it may get modified and base obj would not know
+ // have to set base as modified, which means some optim is lost
+ info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true));
+ }
+ return val;
+ }
+
+ static Handle<v8::Value> namedGetRO(Local<v8::String> name, const v8::AccessorInfo &info) {
+ string key = toSTLString(name);
+ BSONObj *obj = unwrapBSONObj(info.Holder());
+ BSONElement elmt = obj->getField(key.c_str());
+ if (elmt.eoo())
+ return Handle<Value>();
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ Handle<v8::Value> val = scope->mongoToV8Element(elmt, true);
+ return val;
+ }
+
+ static Handle<v8::Value> namedSet(Local<v8::String> name, Local<v8::Value> value_obj, const v8::AccessorInfo& info) {
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true));
+ return Handle<Value>();
+ }
+
+ static Handle<v8::Array> namedEnumerator(const AccessorInfo &info) {
+ BSONObj *obj = unwrapBSONObj(info.Holder());
+ Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields()));
+ int i = 0;
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ // note here that if keys are parseable number, v8 will access them using index
+ for ( BSONObjIterator it(*obj); it.more(); ++i) {
+ const BSONElement& f = it.next();
+// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName())));
+ Handle<v8::String> name = scope->getV8Str(f.fieldName());
+ arr->Set(i, name);
+ }
+ return arr;
+ }
+
+ Handle<Boolean> namedDelete( Local<v8::String> property, const AccessorInfo& info ) {
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true));
+ return Handle<Boolean>();
+ }
+
+// v8::Handle<v8::Integer> namedQuery(Local<v8::String> property, const AccessorInfo& info) {
+// string key = ToString(property);
+// return v8::Integer::New(None);
+// }
+
+ static Handle<v8::Value> indexedGet(uint32_t index, const v8::AccessorInfo &info) {
+ // all properties should be set, otherwise means builtin or deleted
+ if (!(info.This()->HasRealIndexedProperty(index)))
+ return v8::Handle<v8::Value>();
+
+ StringBuilder ss;
+ ss << index;
+ string key = ss.str();
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ // cannot get v8 to properly cache the indexed val in the js object
+// Handle<v8::String> name = scope->getV8Str(key);
+// // v8 API really confusing here, must check existence on index, but then fetch with name
+// if (info.This()->HasRealIndexedProperty(index)) {
+// Handle<v8::Value> val = info.This()->GetRealNamedProperty(name);
+// if (!val.IsEmpty() && !val->IsNull())
+// return val;
+// }
+ BSONObj *obj = unwrapBSONObj(info.Holder());
+ BSONElement elmt = obj->getField(key);
+ if (elmt.eoo())
+ return Handle<Value>();
+ Handle<Value> val = scope->mongoToV8Element(elmt, false);
+// info.This()->ForceSet(name, val);
+
+ if (elmt.type() == mongo::Object || elmt.type() == mongo::Array) {
+ // if accessing a subobject, it may get modified and base obj would not know
+ // have to set base as modified, which means some optim is lost
+ info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true));
+ }
+ return val;
+ }
+
+ Handle<Boolean> indexedDelete( uint32_t index, const AccessorInfo& info ) {
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true));
+ return Handle<Boolean>();
+ }
+
+ static Handle<v8::Value> indexedGetRO(uint32_t index, const v8::AccessorInfo &info) {
+ StringBuilder ss;
+ ss << index;
+ string key = ss.str();
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ // cannot get v8 to properly cache the indexed val in the js object
+// Handle<v8::String> name = scope->getV8Str(key);
+// // v8 API really confusing here, must check existence on index, but then fetch with name
+// if (info.This()->HasRealIndexedProperty(index)) {
+// Handle<v8::Value> val = info.This()->GetRealNamedProperty(name);
+// if (!val.IsEmpty() && !val->IsNull())
+// return val;
+// }
+ BSONObj *obj = unwrapBSONObj(info.Holder());
+ BSONElement elmt = obj->getField(key);
+ if (elmt.eoo())
+ return Handle<Value>();
+ Handle<Value> val = scope->mongoToV8Element(elmt, true);
+// info.This()->ForceSet(name, val);
+ return val;
+ }
+
+ static Handle<v8::Value> indexedSet(uint32_t index, Local<v8::Value> value_obj, const v8::AccessorInfo& info) {
+ Local< External > scp = External::Cast( *info.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+ info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true));
+ return Handle<Value>();
+ }
+
+// static Handle<v8::Array> indexedEnumerator(const AccessorInfo &info) {
+// BSONObj *obj = unwrapBSONObj(info.Holder());
+// Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields()));
+// Local< External > scp = External::Cast( *info.Data() );
+// V8Scope* scope = (V8Scope*)(scp->Value());
+// int i = 0;
+// for ( BSONObjIterator it(*obj); it.more(); ++i) {
+// const BSONElement& f = it.next();
+//// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName())));
+// arr->Set(i, scope->getV8Str(f.fieldName()));
+// }
+// return arr;
+// }
+
+ Handle<Value> NamedReadOnlySet( Local<v8::String> property, Local<Value> value, const AccessorInfo& info ) {
+ string key = toSTLString(property);
+ cout << "cannot write property " << key << " to read-only object" << endl;
+ return value;
+ }
+
+ Handle<Boolean> NamedReadOnlyDelete( Local<v8::String> property, const AccessorInfo& info ) {
+ string key = toSTLString(property);
+ cout << "cannot delete property " << key << " from read-only object" << endl;
+ return Boolean::New( false );
+ }
+
+ Handle<Value> IndexedReadOnlySet( uint32_t index, Local<Value> value, const AccessorInfo& info ) {
+ cout << "cannot write property " << index << " to read-only array" << endl;
+ return value;
+ }
+
+ Handle<Boolean> IndexedReadOnlyDelete( uint32_t index, const AccessorInfo& info ) {
+ cout << "cannot delete property " << index << " from read-only array" << endl;
+ return Boolean::New( false );
+ }
+
// --- engine ---
- V8ScriptEngine::V8ScriptEngine() {}
+ V8ScriptEngine::V8ScriptEngine() {
+ }
V8ScriptEngine::~V8ScriptEngine() {
}
@@ -69,50 +307,105 @@ namespace mongo {
_context = Context::New();
Context::Scope context_scope( _context );
_global = Persistent< v8::Object >::New( _context->Global() );
-
- _this = Persistent< v8::Object >::New( v8::Object::New() );
-
- _global->Set(v8::String::New("print"), newV8Function< Print >()->GetFunction() );
- _global->Set(v8::String::New("version"), newV8Function< Version >()->GetFunction() );
-
- _global->Set(v8::String::New("load"),
- v8::FunctionTemplate::New( v8Callback< loadCallback >, v8::External::New(this))->GetFunction() );
-
- _wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate()->GetFunction() );
-
- _global->Set(v8::String::New("gc"), newV8Function< GCV8 >()->GetFunction() );
-
-
- installDBTypes( _global );
+ _emptyObj = Persistent< v8::Object >::New( v8::Object::New() );
+
+ // initialize lazy object template
+ lzObjectTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+ lzObjectTemplate->SetInternalFieldCount( 1 );
+ lzObjectTemplate->SetNamedPropertyHandler(namedGet, namedSet, 0, namedDelete, 0, v8::External::New(this));
+ lzObjectTemplate->SetIndexedPropertyHandler(indexedGet, indexedSet, 0, indexedDelete, 0, v8::External::New(this));
+
+ roObjectTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+ roObjectTemplate->SetInternalFieldCount( 1 );
+ roObjectTemplate->SetNamedPropertyHandler(namedGetRO, NamedReadOnlySet, 0, NamedReadOnlyDelete, namedEnumerator, v8::External::New(this));
+ roObjectTemplate->SetIndexedPropertyHandler(indexedGetRO, IndexedReadOnlySet, 0, IndexedReadOnlyDelete, 0, v8::External::New(this));
+
+ // initialize lazy array template
+ // unfortunately it is not possible to create true v8 array from a template
+ // this means we use an object template and copy methods over
+ // this it creates issues when calling certain methods that check array type
+ lzArrayTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+ lzArrayTemplate->SetInternalFieldCount( 1 );
+ lzArrayTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0, v8::External::New(this));
+
+ internalFieldObjects = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+ internalFieldObjects->SetInternalFieldCount( 1 );
+
+ V8STR_CONN = getV8Str( "_conn" );
+ V8STR_ID = getV8Str( "_id" );
+ V8STR_LENGTH = getV8Str( "length" );
+ V8STR_LEN = getV8Str( "len" );
+ V8STR_TYPE = getV8Str( "type" );
+ V8STR_ISOBJECTID = getV8Str( "isObjectId" );
+ V8STR_RETURN = getV8Str( "return" );
+ V8STR_ARGS = getV8Str( "args" );
+ V8STR_T = getV8Str( "t" );
+ V8STR_I = getV8Str( "i" );
+ V8STR_EMPTY = getV8Str( "" );
+ V8STR_MINKEY = getV8Str( "$MinKey" );
+ V8STR_MAXKEY = getV8Str( "$MaxKey" );
+ V8STR_NUMBERLONG = getV8Str( "__NumberLong" );
+ V8STR_NUMBERINT = getV8Str( "__NumberInt" );
+ V8STR_DBPTR = getV8Str( "__DBPointer" );
+ V8STR_BINDATA = getV8Str( "__BinData" );
+ V8STR_NATIVE_FUNC = getV8Str( "_native_function" );
+ V8STR_NATIVE_DATA = getV8Str( "_native_data" );
+ V8STR_V8_FUNC = getV8Str( "_v8_function" );
+ V8STR_RO = getV8Str( "_ro" );
+ V8STR_MODIFIED = getV8Str( "_mod" );
+
+ injectV8Function("print", Print);
+ injectV8Function("version", Version);
+ injectV8Function("load", load);
+
+ _wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate(this)->GetFunction() );
+
+ injectV8Function("gc", GCV8);
+
+ installDBTypes( this, _global );
}
V8Scope::~V8Scope() {
V8Lock l;
Context::Scope context_scope( _context );
_wrapper.Dispose();
- _this.Dispose();
+ _emptyObj.Dispose();
for( unsigned i = 0; i < _funcs.size(); ++i )
_funcs[ i ].Dispose();
_funcs.clear();
_global.Dispose();
_context.Dispose();
+ std::map <string, v8::Persistent <v8::String> >::iterator it = _strCache.begin();
+ std::map <string, v8::Persistent <v8::String> >::iterator end = _strCache.end();
+ while (it != end) {
+ it->second.Dispose();
+ ++it;
+ }
+ lzObjectTemplate.Dispose();
+ lzArrayTemplate.Dispose();
+ roObjectTemplate.Dispose();
+ internalFieldObjects.Dispose();
}
- Handle< Value > V8Scope::nativeCallback( const Arguments &args ) {
+ /**
+ * JS Callback that will call a c++ function with BSON arguments.
+ */
+ Handle< Value > V8Scope::nativeCallback( V8Scope* scope, const Arguments &args ) {
V8Lock l;
HandleScope handle_scope;
- Local< External > f = External::Cast( *args.Callee()->Get( v8::String::New( "_native_function" ) ) );
+ Local< External > f = External::Cast( *args.Callee()->Get( scope->V8STR_NATIVE_FUNC ) );
NativeFunction function = (NativeFunction)(f->Value());
+ Local< External > data = External::Cast( *args.Callee()->Get( scope->V8STR_NATIVE_DATA ) );
BSONObjBuilder b;
for( int i = 0; i < args.Length(); ++i ) {
stringstream ss;
ss << i;
- v8ToMongoElement( b, v8::String::New( "foo" ), ss.str(), args[ i ] );
+ scope->v8ToMongoElement( b, scope->V8STR_EMPTY, ss.str(), args[ i ] );
}
BSONObj nativeArgs = b.obj();
BSONObj ret;
try {
- ret = function( nativeArgs );
+ ret = function( nativeArgs, data->Value() );
}
catch( const std::exception &e ) {
return v8::ThrowException(v8::String::New(e.what()));
@@ -120,26 +413,63 @@ namespace mongo {
catch( ... ) {
return v8::ThrowException(v8::String::New("unknown exception"));
}
- return handle_scope.Close( mongoToV8Element( ret.firstElement() ) );
+ return handle_scope.Close( scope->mongoToV8Element( ret.firstElement() ) );
}
- Handle< Value > V8Scope::loadCallback( const Arguments &args ) {
- V8Lock l;
- HandleScope handle_scope;
- Handle<External> field = Handle<External>::Cast(args.Data());
- void* ptr = field->Value();
- V8Scope* self = static_cast<V8Scope*>(ptr);
-
- Context::Scope context_scope(self->_context);
+ Handle< Value > V8Scope::load( V8Scope* scope, const Arguments &args ) {
+ Context::Scope context_scope(scope->_context);
for (int i = 0; i < args.Length(); ++i) {
std::string filename(toSTLString(args[i]));
- if (!self->execFile(filename, false , true , false)) {
+ if (!scope->execFile(filename, false , true , false)) {
return v8::ThrowException(v8::String::New((std::string("error loading file: ") + filename).c_str()));
}
}
return v8::True();
}
+ /**
+ * JS Callback that will call a c++ function with the v8 scope and v8 arguments.
+ * Handles interrupts, exception handling, etc
+ *
+ * The implementation below assumes that SERVER-1816 has been fixed - in
+ * particular, interrupted() must return true if an interrupt was ever
+ * sent; currently that is not the case if a new killop overwrites the data
+ * for an old one
+ */
+ v8::Handle< v8::Value > V8Scope::v8Callback( const v8::Arguments &args ) {
+ disableV8Interrupt(); // we don't want to have to audit all v8 calls for termination exceptions, so we don't allow these exceptions during the callback
+ if ( globalScriptEngine->interrupted() ) {
+ v8::V8::TerminateExecution(); // experimentally it seems that TerminateExecution() will override the return value
+ return v8::Undefined();
+ }
+ Local< External > f = External::Cast( *args.Callee()->Get( v8::String::New( "_v8_function" ) ) );
+ v8Function function = (v8Function)(f->Value());
+ Local< External > scp = External::Cast( *args.Data() );
+ V8Scope* scope = (V8Scope*)(scp->Value());
+
+ v8::Handle< v8::Value > ret;
+ string exception;
+ try {
+ ret = function( scope, args );
+ }
+ catch( const std::exception &e ) {
+ exception = e.what();
+ }
+ catch( ... ) {
+ exception = "unknown exception";
+ }
+ enableV8Interrupt();
+ if ( globalScriptEngine->interrupted() ) {
+ v8::V8::TerminateExecution();
+ return v8::Undefined();
+ }
+ if ( !exception.empty() ) {
+ // technically, ThrowException is supposed to be the last v8 call before returning
+ ret = v8::ThrowException( v8::String::New( exception.c_str() ) );
+ }
+ return ret;
+ }
+
// ---- global stuff ----
void V8Scope::init( const BSONObj * data ) {
@@ -156,29 +486,29 @@ namespace mongo {
void V8Scope::setNumber( const char * field , double val ) {
V8_SIMPLE_HEADER
- _global->Set( v8::String::New( field ) , v8::Number::New( val ) );
+ _global->Set( getV8Str( field ) , v8::Number::New( val ) );
}
void V8Scope::setString( const char * field , const char * val ) {
V8_SIMPLE_HEADER
- _global->Set( v8::String::New( field ) , v8::String::New( val ) );
+ _global->Set( getV8Str( field ) , v8::String::New( val ) );
}
void V8Scope::setBoolean( const char * field , bool val ) {
V8_SIMPLE_HEADER
- _global->Set( v8::String::New( field ) , v8::Boolean::New( val ) );
+ _global->Set( getV8Str( field ) , v8::Boolean::New( val ) );
}
void V8Scope::setElement( const char *field , const BSONElement& e ) {
V8_SIMPLE_HEADER
- _global->Set( v8::String::New( field ) , mongoToV8Element( e ) );
+ _global->Set( getV8Str( field ) , mongoToV8Element( e ) );
}
void V8Scope::setObject( const char *field , const BSONObj& obj , bool readOnly) {
V8_SIMPLE_HEADER
// Set() accepts a ReadOnly parameter, but this just prevents the field itself
// from being overwritten and doesn't protect the object stored in 'field'.
- _global->Set( v8::String::New( field ) , mongoToV8( obj, false, readOnly) );
+ _global->Set( getV8Str( field ) , mongoToLZV8( obj, false, readOnly) );
}
int V8Scope::type( const char *field ) {
@@ -196,8 +526,9 @@ namespace mongo {
return Array;
if ( v->IsBoolean() )
return Bool;
- if ( v->IsInt32() )
- return NumberInt;
+ // needs to be explicit NumberInt to use integer
+// if ( v->IsInt32() )
+// return NumberInt;
if ( v->IsNumber() )
return NumberDouble;
if ( v->IsExternal() ) {
@@ -213,7 +544,7 @@ namespace mongo {
}
v8::Handle<v8::Value> V8Scope::get( const char * field ) {
- return _global->Get( v8::String::New( field ) );
+ return _global->Get( getV8Str( field ) );
}
double V8Scope::getNumber( const char *field ) {
@@ -314,45 +645,50 @@ namespace mongo {
return num;
}
- void V8Scope::setThis( const BSONObj * obj ) {
+ void V8Scope::setFunction( const char *field , const char * code ) {
V8_SIMPLE_HEADER
- if ( ! obj ) {
- _this = Persistent< v8::Object >::New( v8::Object::New() );
- return;
- }
-
- //_this = mongoToV8( *obj );
- v8::Handle<v8::Value> argv[1];
- argv[0] = v8::External::New( createWrapperHolder( obj , true , false ) );
- _this = Persistent< v8::Object >::New( _wrapper->NewInstance( 1, argv ) );
+ _global->Set( getV8Str( field ) , __createFunction(code) );
}
+// void V8Scope::setThis( const BSONObj * obj ) {
+// V8_SIMPLE_HEADER
+// if ( ! obj ) {
+// _this = Persistent< v8::Object >::New( v8::Object::New() );
+// return;
+// }
+//
+// //_this = mongoToV8( *obj );
+// v8::Handle<v8::Value> argv[1];
+// argv[0] = v8::External::New( createWrapperHolder( this, obj , true , false ) );
+// _this = Persistent< v8::Object >::New( _wrapper->NewInstance( 1, argv ) );
+// }
+
void V8Scope::rename( const char * from , const char * to ) {
V8_SIMPLE_HEADER;
- v8::Local<v8::String> f = v8::String::New( from );
- v8::Local<v8::String> t = v8::String::New( to );
+ Handle<v8::String> f = getV8Str( from );
+ Handle<v8::String> t = getV8Str( to );
_global->Set( t , _global->Get( f ) );
_global->Set( f , v8::Undefined() );
}
- int V8Scope::invoke( ScriptingFunction func , const BSONObj& argsObject, int timeoutMs , bool ignoreReturn ) {
+ int V8Scope::invoke( ScriptingFunction func , const BSONObj* argsObject, const BSONObj* recv, int timeoutMs , bool ignoreReturn, bool readOnlyArgs, bool readOnlyRecv ) {
V8_SIMPLE_HEADER
Handle<Value> funcValue = _funcs[func-1];
TryCatch try_catch;
- int nargs = argsObject.nFields();
+ int nargs = argsObject ? argsObject->nFields() : 0;
scoped_array< Handle<Value> > args;
if ( nargs ) {
args.reset( new Handle<Value>[nargs] );
- BSONObjIterator it( argsObject );
+ BSONObjIterator it( *argsObject );
for ( int i=0; i<nargs; i++ ) {
BSONElement next = it.next();
- args[i] = mongoToV8Element( next );
+ args[i] = mongoToV8Element( next, readOnlyArgs );
}
- setObject( "args", argsObject, true ); // for backwards compatibility
+ setObject( "args", *argsObject, readOnlyArgs); // for backwards compatibility
}
else {
- _global->Set( v8::String::New( "args" ), v8::Undefined() );
+ _global->Set( V8STR_ARGS, v8::Undefined() );
}
if ( globalScriptEngine->interrupted() ) {
stringstream ss;
@@ -361,8 +697,14 @@ namespace mongo {
log() << _error << endl;
return 1;
}
+ Handle<v8::Object> v8recv;
+ if (recv != 0)
+ v8recv = mongoToLZV8(*recv, false, readOnlyRecv);
+ else
+ v8recv = _emptyObj;
+
enableV8Interrupt(); // because of v8 locker we can check interrupted, then enable
- Local<Value> result = ((v8::Function*)(*funcValue))->Call( _this , nargs , args.get() );
+ Local<Value> result = ((v8::Function*)(*funcValue))->Call( v8recv , nargs , nargs ? args.get() : 0 );
disableV8Interrupt();
if ( result.IsEmpty() ) {
@@ -379,7 +721,7 @@ namespace mongo {
}
if ( ! ignoreReturn ) {
- _global->Set( v8::String::New( "return" ) , result );
+ _global->Set( V8STR_RETURN , result );
}
return 0;
@@ -438,7 +780,7 @@ namespace mongo {
return false;
}
- _global->Set( v8::String::New( "__lastres__" ) , result );
+ _global->Set( getV8Str( "__lastres__" ) , result );
if ( printResult && ! result->IsUndefined() ) {
cout << toSTLString( result ) << endl;
@@ -447,12 +789,43 @@ namespace mongo {
return true;
}
- void V8Scope::injectNative( const char *field, NativeFunction func ) {
+ void V8Scope::injectNative( const char *field, NativeFunction func, void* data ) {
+ injectNative(field, func, _global, data);
+ }
+
+ void V8Scope::injectNative( const char *field, NativeFunction func, Handle<v8::Object>& obj, void* data ) {
+ V8_SIMPLE_HEADER
+
+ Handle< FunctionTemplate > ft = createV8Function(nativeCallback);
+ ft->Set( this->V8STR_NATIVE_FUNC, External::New( (void*)func ) );
+ ft->Set( this->V8STR_NATIVE_DATA, External::New( data ) );
+ obj->Set( getV8Str( field ), ft->GetFunction() );
+ }
+
+ void V8Scope::injectV8Function( const char *field, v8Function func ) {
+ injectV8Function(field, func, _global);
+ }
+
+ void V8Scope::injectV8Function( const char *field, v8Function func, Handle<v8::Object>& obj ) {
+ V8_SIMPLE_HEADER
+
+ Handle< FunctionTemplate > ft = createV8Function(func);
+ Handle<v8::Function> f = ft->GetFunction();
+ obj->Set( getV8Str( field ), f );
+ }
+
+ void V8Scope::injectV8Function( const char *field, v8Function func, Handle<v8::Template>& t ) {
V8_SIMPLE_HEADER
- Handle< FunctionTemplate > f( newV8Function< nativeCallback >() );
- f->Set( v8::String::New( "_native_function" ), External::New( (void*)func ) );
- _global->Set( v8::String::New( field ), f->GetFunction() );
+ Handle< FunctionTemplate > ft = createV8Function(func);
+ Handle<v8::Function> f = ft->GetFunction();
+ t->Set( getV8Str( field ), f );
+ }
+
+ Handle<FunctionTemplate> V8Scope::createV8Function( v8Function func ) {
+ Handle< FunctionTemplate > ft = v8::FunctionTemplate::New(v8Callback, External::New( this ));
+ ft->Set( this->V8STR_V8_FUNC, External::New( (void*)func ) );
+ return ft;
}
void V8Scope::gc() {
@@ -479,7 +852,7 @@ namespace mongo {
v8::Locker::StartPreemption( 50 );
//_global->Set( v8::String::New( "Mongo" ) , _engine->_externalTemplate->GetFunction() );
- _global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( true )->GetFunction() );
+ _global->Set( getV8Str( "Mongo" ) , getMongoFunctionTemplate( this, true )->GetFunction() );
execCoreFiles();
exec( "_mongo = new Mongo();" , "local connect 2" , false , true , true , 0 );
exec( (string)"db = _mongo.getDB(\"" + dbName + "\");" , "local connect 3" , false , true , true , 0 );
@@ -496,8 +869,8 @@ namespace mongo {
if ( _connectState == LOCAL )
throw UserException( 12512, "localConnect already called, can't call externalSetup" );
- installFork( _global, _context );
- _global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( false )->GetFunction() );
+ installFork( this, _global, _context );
+ _global->Set( getV8Str( "Mongo" ) , getMongoFunctionTemplate( this, false )->GetFunction() );
execCoreFiles();
_connectState = EXTERNAL;
}
@@ -512,4 +885,663 @@ namespace mongo {
_error = "";
}
+ Local< v8::Value > newFunction( const char *code ) {
+ stringstream codeSS;
+ codeSS << "____MontoToV8_newFunction_temp = " << code;
+ string codeStr = codeSS.str();
+ Local< Script > compiled = Script::New( v8::String::New( codeStr.c_str() ) );
+ Local< Value > ret = compiled->Run();
+ return ret;
+ }
+
+ Local< v8::Value > V8Scope::newId( const OID &id ) {
+ v8::Function * idCons = this->getObjectIdCons();
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::String::New( id.str().c_str() );
+ return idCons->NewInstance( 1 , argv );
+ }
+
+ Local<v8::Object> V8Scope::mongoToV8( const BSONObj& m , bool array, bool readOnly ) {
+
+ Local<v8::Object> o;
+
+ // handle DBRef. needs to come first. isn't it? (metagoto)
+ static string ref = "$ref";
+ if ( ref == m.firstElement().fieldName() ) {
+ const BSONElement& id = m["$id"];
+ if (!id.eoo()) { // there's no check on $id exitence in sm implementation. risky ?
+ v8::Function* dbRef = getNamedCons( "DBRef" );
+ o = dbRef->NewInstance();
+ }
+ }
+
+ Local< v8::ObjectTemplate > readOnlyObjects;
+
+ if ( !o.IsEmpty() ) {
+ readOnly = false;
+ }
+ else if ( array ) {
+ // NOTE Looks like it's impossible to add interceptors to v8 arrays.
+ readOnly = false;
+ o = v8::Array::New();
+ }
+ else if ( !readOnly ) {
+ o = v8::Object::New();
+ }
+ else {
+ // NOTE Our readOnly implemention relies on undocumented ObjectTemplate
+ // functionality that may be fragile, but it still seems like the best option
+ // for now -- fwiw, the v8 docs are pretty sparse. I've determined experimentally
+ // that when property handlers are set for an object template, they will attach
+ // to objects previously created by that template. To get this to work, though,
+ // it is necessary to initialize the template's property handlers before
+ // creating objects from the template (as I have in the following few lines
+ // of code).
+ // NOTE In my first attempt, I configured the permanent property handlers before
+ // constructiong the object and replaced the Set() calls below with ForceSet().
+ // However, it turns out that ForceSet() only bypasses handlers for named
+ // properties and not for indexed properties.
+ readOnlyObjects = v8::ObjectTemplate::New();
+ // NOTE This internal field will store type info for special db types. For
+ // regular objects the field is unnecessary - for simplicity I'm creating just
+ // one readOnlyObjects template for objects where the field is & isn't necessary,
+ // assuming that the overhead of an internal field is slight.
+ readOnlyObjects->SetInternalFieldCount( 1 );
+ readOnlyObjects->SetNamedPropertyHandler( 0 );
+ readOnlyObjects->SetIndexedPropertyHandler( 0 );
+ o = readOnlyObjects->NewInstance();
+ }
+
+ mongo::BSONObj sub;
+
+ for ( BSONObjIterator i(m); i.more(); ) {
+ const BSONElement& f = i.next();
+
+ Local<Value> v;
+ Handle<v8::String> name = getV8Str(f.fieldName());
+
+ switch ( f.type() ) {
+
+ case mongo::Code:
+ o->Set( name, newFunction( f.valuestr() ) );
+ break;
+
+ case CodeWScope:
+ if ( f.codeWScopeObject().isEmpty() )
+ log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
+ o->Set( name, newFunction( f.codeWScopeCode() ) );
+ break;
+
+ case mongo::String:
+ o->Set( name , v8::String::New( f.valuestr() ) );
+ break;
+
+ case mongo::jstOID: {
+ v8::Function * idCons = getObjectIdCons();
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::String::New( f.__oid().str().c_str() );
+ o->Set( name ,
+ idCons->NewInstance( 1 , argv ) );
+ break;
+ }
+
+ case mongo::NumberDouble:
+ case mongo::NumberInt:
+ o->Set( name , v8::Number::New( f.number() ) );
+ break;
+
+// case mongo::NumberInt: {
+// Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
+// int val = f.numberInt();
+// v8::Function* numberInt = getNamedCons( "NumberInt" );
+// v8::Handle<v8::Value> argv[1];
+// argv[0] = v8::Int32::New( val );
+// o->Set( name, numberInt->NewInstance( 1, argv ) );
+// break;
+// }
+
+ case mongo::Array:
+ sub = f.embeddedObject();
+ o->Set( name , mongoToV8( sub , true, readOnly ) );
+ break;
+ case mongo::Object:
+ sub = f.embeddedObject();
+ o->Set( name , mongoToLZV8( sub , false, readOnly ) );
+ break;
+
+ case mongo::Date:
+ o->Set( name , v8::Date::New( (double) ((long long)f.date().millis) ));
+ break;
+
+ case mongo::Bool:
+ o->Set( name , v8::Boolean::New( f.boolean() ) );
+ break;
+
+ case mongo::jstNULL:
+ case mongo::Undefined: // duplicate sm behavior
+ o->Set( name , v8::Null() );
+ break;
+
+ case mongo::RegEx: {
+ v8::Function * regex = getNamedCons( "RegExp" );
+
+ v8::Handle<v8::Value> argv[2];
+ argv[0] = v8::String::New( f.regex() );
+ argv[1] = v8::String::New( f.regexFlags() );
+
+ o->Set( name , regex->NewInstance( 2 , argv ) );
+ break;
+ }
+
+ case mongo::BinData: {
+ Local<v8::Object> b = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
+
+ int len;
+ const char *data = f.binData( len );
+
+ v8::Function* binData = getNamedCons( "BinData" );
+ v8::Handle<v8::Value> argv[3];
+ argv[0] = v8::Number::New( len );
+ argv[1] = v8::Number::New( f.binDataType() );
+ argv[2] = v8::String::New( data, len );
+ o->Set( name, binData->NewInstance(3, argv) );
+ break;
+ }
+
+ case mongo::Timestamp: {
+ Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
+
+ sub->Set( V8STR_T , v8::Number::New( f.timestampTime() ) );
+ sub->Set( V8STR_I , v8::Number::New( f.timestampInc() ) );
+ sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
+
+ o->Set( name , sub );
+ break;
+ }
+
+ case mongo::NumberLong: {
+ Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
+ unsigned long long val = f.numberLong();
+ v8::Function* numberLong = getNamedCons( "NumberLong" );
+ double floatApprox = (double)(long long)val;
+ if ( (long long)val == (long long)floatApprox ) {
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::Number::New( floatApprox );
+ o->Set( name, numberLong->NewInstance( 1, argv ) );
+ }
+ else {
+ v8::Handle<v8::Value> argv[3];
+ argv[0] = v8::Number::New( floatApprox );
+ argv[1] = v8::Integer::New( val >> 32 );
+ argv[2] = v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) );
+ o->Set( name, numberLong->NewInstance(3, argv) );
+ }
+ break;
+ }
+
+ case mongo::MinKey: {
+ Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
+ sub->Set( V8STR_MINKEY, v8::Boolean::New( true ) );
+ sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
+ o->Set( name , sub );
+ break;
+ }
+
+ case mongo::MaxKey: {
+ Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
+ sub->Set( V8STR_MAXKEY, v8::Boolean::New( true ) );
+ sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
+ o->Set( name , sub );
+ break;
+ }
+
+ case mongo::DBRef: {
+ v8::Function* dbPointer = getNamedCons( "DBPointer" );
+ v8::Handle<v8::Value> argv[2];
+ argv[0] = getV8Str( f.dbrefNS() );
+ argv[1] = newId( f.dbrefOID() );
+ o->Set( name, dbPointer->NewInstance(2, argv) );
+ break;
+ }
+
+ default:
+ cout << "can't handle type: ";
+ cout << f.type() << " ";
+ cout << f.toString();
+ cout << endl;
+ break;
+ }
+
+ }
+
+ if ( readOnly ) {
+ readOnlyObjects->SetNamedPropertyHandler( 0, NamedReadOnlySet, 0, NamedReadOnlyDelete );
+ readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete );
+ }
+
+ return o;
+ }
+
+ /**
+ * converts a BSONObj to a Lazy V8 object
+ */
+ Handle<v8::Object> V8Scope::mongoToLZV8( const BSONObj& m , bool array, bool readOnly ) {
+ Local<v8::Object> o;
+
+ if (readOnly) {
+ o = roObjectTemplate->NewInstance();
+ o->SetHiddenValue(V8STR_RO, v8::Boolean::New(true));
+ } else {
+ if (array) {
+ o = lzArrayTemplate->NewInstance();
+ o->SetPrototype(v8::Array::New(1)->GetPrototype());
+ o->Set(V8STR_LENGTH, v8::Integer::New(m.nFields()), DontEnum);
+ // o->Set(ARRAY_STRING, v8::Boolean::New(true), DontEnum);
+ } else {
+ o = lzObjectTemplate->NewInstance();
+
+ static string ref = "$ref";
+ if ( ref == m.firstElement().fieldName() ) {
+ const BSONElement& id = m["$id"];
+ if (!id.eoo()) {
+ v8::Function* dbRef = getNamedCons( "DBRef" );
+ o->SetPrototype(dbRef->NewInstance()->GetPrototype());
+ }
+ }
+ }
+
+ // need to set all keys with dummy values, so that order of keys is correct during enumeration
+ // otherwise v8 will list any newly set property in JS before the ones of underlying BSON obj.
+ for (BSONObjIterator it(m); it.more();) {
+ const BSONElement& f = it.next();
+ o->ForceSet(getV8Str(f.fieldName()), v8::Undefined());
+ }
+ }
+
+ BSONObj* own = new BSONObj(m.getOwned());
+// BSONObj* own = new BSONObj(m);
+ Persistent<v8::Object> p = wrapBSONObject(o, own);
+ return p;
+ }
+
+ Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f, bool readOnly ) {
+// Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New();
+// internalFieldObjects->SetInternalFieldCount( 1 );
+
+ switch ( f.type() ) {
+
+ case mongo::Code:
+ return newFunction( f.valuestr() );
+
+ case CodeWScope:
+ if ( f.codeWScopeObject().isEmpty() )
+ log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
+ return newFunction( f.codeWScopeCode() );
+
+ case mongo::String:
+// return v8::String::NewExternal( new ExternalString( f.valuestr() ));
+ return v8::String::New( f.valuestr() );
+// return getV8Str( f.valuestr() );
+
+ case mongo::jstOID:
+ return newId( f.__oid() );
+
+ case mongo::NumberDouble:
+ case mongo::NumberInt:
+ return v8::Number::New( f.number() );
+
+ case mongo::Array:
+ // for arrays it's better to use non lazy object because:
+ // - the lazy array is not a true v8 array and requires some v8 src change for all methods to work
+ // - it made several tests about 1.5x slower
+ // - most times when an array is accessed, all its values will be used
+ return mongoToV8( f.embeddedObject() , true, readOnly );
+ case mongo::Object:
+ return mongoToLZV8( f.embeddedObject() , false, readOnly);
+
+ case mongo::Date:
+ return v8::Date::New( (double) ((long long)f.date().millis) );
+
+ case mongo::Bool:
+ return v8::Boolean::New( f.boolean() );
+
+ case mongo::EOO:
+ case mongo::jstNULL:
+ case mongo::Undefined: // duplicate sm behavior
+ return v8::Null();
+
+ case mongo::RegEx: {
+ v8::Function * regex = getNamedCons( "RegExp" );
+
+ v8::Handle<v8::Value> argv[2];
+ argv[0] = v8::String::New( f.regex() );
+ argv[1] = v8::String::New( f.regexFlags() );
+
+ return regex->NewInstance( 2 , argv );
+ break;
+ }
+
+ case mongo::BinData: {
+ int len;
+ const char *data = f.binData( len );
+
+ v8::Function* binData = getNamedCons( "BinData" );
+ v8::Handle<v8::Value> argv[3];
+ argv[0] = v8::Number::New( len );
+ argv[1] = v8::Number::New( f.binDataType() );
+ argv[2] = v8::String::New( data, len );
+ return binData->NewInstance( 3, argv );
+ };
+
+ case mongo::Timestamp: {
+ Local<v8::Object> sub = internalFieldObjects->NewInstance();
+
+ sub->Set( V8STR_T , v8::Number::New( f.timestampTime() ) );
+ sub->Set( V8STR_I , v8::Number::New( f.timestampInc() ) );
+ sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
+
+ return sub;
+ }
+
+ case mongo::NumberLong: {
+ Local<v8::Object> sub = internalFieldObjects->NewInstance();
+ unsigned long long val = f.numberLong();
+ v8::Function* numberLong = getNamedCons( "NumberLong" );
+ if ( (long long)val == (long long)(double)(long long)(val) ) {
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::Number::New( (double)(long long)( val ) );
+ return numberLong->NewInstance( 1, argv );
+ }
+ else {
+ v8::Handle<v8::Value> argv[3];
+ argv[0] = v8::Number::New( (double)(long long)( val ) );
+ argv[1] = v8::Integer::New( val >> 32 );
+ argv[2] = v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) );
+ return numberLong->NewInstance( 3, argv );
+ }
+ }
+
+// case mongo::NumberInt: {
+// Local<v8::Object> sub = internalFieldObjects->NewInstance();
+// int val = f.numberInt();
+// v8::Function* numberInt = getNamedCons( "NumberInt" );
+// v8::Handle<v8::Value> argv[1];
+// argv[0] = v8::Int32::New(val);
+// return numberInt->NewInstance( 1, argv );
+// }
+
+ case mongo::MinKey: {
+ Local<v8::Object> sub = internalFieldObjects->NewInstance();
+ sub->Set( V8STR_MINKEY, v8::Boolean::New( true ) );
+ sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
+ return sub;
+ }
+
+ case mongo::MaxKey: {
+ Local<v8::Object> sub = internalFieldObjects->NewInstance();
+ sub->Set( V8STR_MAXKEY, v8::Boolean::New( true ) );
+ sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
+ return sub;
+ }
+
+ case mongo::DBRef: {
+ v8::Function* dbPointer = getNamedCons( "DBPointer" );
+ v8::Handle<v8::Value> argv[2];
+ argv[0] = getV8Str( f.dbrefNS() );
+ argv[1] = newId( f.dbrefOID() );
+ return dbPointer->NewInstance(2, argv);
+ }
+
+ default:
+ cout << "can't handle type: ";
+ cout << f.type() << " ";
+ cout << f.toString();
+ cout << endl;
+ break;
+ }
+
+ return v8::Undefined();
+ }
+
+ void V8Scope::append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ) {
+ V8_SIMPLE_HEADER
+ Handle<v8::String> v8name = getV8Str(scopeName);
+ Handle<Value> value = _global->Get( v8name );
+ v8ToMongoElement(builder, v8name, fieldName, value);
+ }
+
+ void V8Scope::v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth, BSONObj* originalParent ) {
+
+ if ( value->IsString() ) {
+// Handle<v8::String> str = Handle<v8::String>::Cast(value);
+// ExternalString* es = (ExternalString*) (str->GetExternalAsciiStringResource());
+// b.append( sname , es->data() );
+ b.append( sname , toSTLString( value ).c_str() );
+ return;
+ }
+
+ if ( value->IsFunction() ) {
+ b.appendCode( sname , toSTLString( value ) );
+ return;
+ }
+
+ if ( value->IsNumber() ) {
+ double val = value->ToNumber()->Value();
+ // if previous type was integer, keep it
+ int intval = (int)val;
+ if (val == intval && originalParent) {
+ BSONElement elmt = originalParent->getField(sname);
+ if (elmt.type() == mongo::NumberInt) {
+ b.append( sname , intval );
+ return;
+ }
+ }
+
+ b.append( sname , val );
+ return;
+ }
+
+ if ( value->IsArray() ) {
+ BSONObj sub = v8ToMongo( value->ToObject() , depth );
+ b.appendArray( sname , sub );
+ return;
+ }
+
+ if ( value->IsDate() ) {
+ long long dateval = (long long)(v8::Date::Cast( *value )->NumberValue());
+ b.appendDate( sname , Date_t( (unsigned long long) dateval ) );
+ return;
+ }
+
+ if ( value->IsExternal() )
+ return;
+
+ if ( value->IsObject() ) {
+ // The user could potentially modify the fields of these special objects,
+ // wreaking havoc when we attempt to reinterpret them. Not doing any validation
+ // for now...
+ Local< v8::Object > obj = value->ToObject();
+ if ( obj->InternalFieldCount() && obj->GetInternalField( 0 )->IsNumber() ) {
+ switch( obj->GetInternalField( 0 )->ToInt32()->Value() ) { // NOTE Uint32's Value() gave me a linking error, so going with this instead
+ case Timestamp:
+ b.appendTimestamp( sname,
+ Date_t( (unsigned long long)(obj->Get( V8STR_T )->ToNumber()->Value() )),
+ obj->Get( V8STR_I )->ToInt32()->Value() );
+ return;
+ case MinKey:
+ b.appendMinKey( sname );
+ return;
+ case MaxKey:
+ b.appendMaxKey( sname );
+ return;
+ default:
+ assert( "invalid internal field" == 0 );
+ }
+ }
+ string s = toSTLString( value );
+ if ( s.size() && s[0] == '/' ) {
+ s = s.substr( 1 );
+ string r = s.substr( 0 , s.rfind( "/" ) );
+ string o = s.substr( s.rfind( "/" ) + 1 );
+ b.appendRegex( sname , r , o );
+ }
+ else if ( value->ToObject()->GetPrototype()->IsObject() &&
+ value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( V8STR_ISOBJECTID ) ) {
+ OID oid;
+ oid.init( toSTLString( value ) );
+ b.appendOID( sname , &oid );
+ }
+ else if ( !value->ToObject()->GetHiddenValue( V8STR_NUMBERLONG ).IsEmpty() ) {
+ // TODO might be nice to potentially speed this up with an indexed internal
+ // field, but I don't yet know how to use an ObjectTemplate with a
+ // constructor.
+ v8::Handle< v8::Object > it = value->ToObject();
+ long long val;
+ if ( !it->Has( getV8Str( "top" ) ) ) {
+ val = (long long)( it->Get( getV8Str( "floatApprox" ) )->NumberValue() );
+ }
+ else {
+ val = (long long)
+ ( (unsigned long long)( it->Get( getV8Str( "top" ) )->ToInt32()->Value() ) << 32 ) +
+ (unsigned)( it->Get( getV8Str( "bottom" ) )->ToInt32()->Value() );
+ }
+
+ b.append( sname, val );
+ }
+ else if ( !value->ToObject()->GetHiddenValue( V8STR_NUMBERINT ).IsEmpty() ) {
+ v8::Handle< v8::Object > it = value->ToObject();
+ b.append(sname, it->GetHiddenValue(V8STR_NUMBERINT)->Int32Value());
+ }
+ else if ( !value->ToObject()->GetHiddenValue( V8STR_DBPTR ).IsEmpty() ) {
+ OID oid;
+ oid.init( toSTLString( value->ToObject()->Get( getV8Str( "id" ) ) ) );
+ string ns = toSTLString( value->ToObject()->Get( getV8Str( "ns" ) ) );
+ b.appendDBRef( sname, ns, oid );
+ }
+ else if ( !value->ToObject()->GetHiddenValue( V8STR_BINDATA ).IsEmpty() ) {
+ int len = obj->Get( getV8Str( "len" ) )->ToInt32()->Value();
+ Local<External> c = External::Cast( *(obj->GetInternalField( 0 )) );
+ const char* dataArray = (char*)(c->Value());;
+ b.appendBinData( sname,
+ len,
+ mongo::BinDataType( obj->Get( getV8Str( "type" ) )->ToInt32()->Value() ),
+ dataArray );
+ }
+ else {
+ BSONObj sub = v8ToMongo( value->ToObject() , depth );
+ b.append( sname , sub );
+ }
+ return;
+ }
+
+ if ( value->IsBoolean() ) {
+ b.appendBool( sname , value->ToBoolean()->Value() );
+ return;
+ }
+
+ else if ( value->IsUndefined() ) {
+ b.appendUndefined( sname );
+ return;
+ }
+
+ else if ( value->IsNull() ) {
+ b.appendNull( sname );
+ return;
+ }
+
+ cout << "don't know how to convert to mongo field [" << name << "]\t" << value << endl;
+ }
+
+ BSONObj V8Scope::v8ToMongo( v8::Handle<v8::Object> o , int depth ) {
+ BSONObj* originalBSON = 0;
+ if (o->HasNamedLookupInterceptor()) {
+ originalBSON = unwrapBSONObj(o);
+ }
+
+ if ( !o->GetHiddenValue( V8STR_RO ).IsEmpty() ||
+ (o->HasNamedLookupInterceptor() && o->GetHiddenValue( V8STR_MODIFIED ).IsEmpty()) ) {
+ // object was readonly, use bson as is
+ if (originalBSON)
+ return *originalBSON;
+ }
+
+ BSONObjBuilder b;
+
+ if ( depth == 0 ) {
+ if ( o->HasRealNamedProperty( V8STR_ID ) ) {
+ v8ToMongoElement( b , V8STR_ID , "_id" , o->Get( V8STR_ID ), 0, originalBSON );
+ }
+ }
+
+ Local<v8::Array> names = o->GetPropertyNames();
+ for ( unsigned int i=0; i<names->Length(); i++ ) {
+ v8::Local<v8::String> name = names->Get( i )->ToString();
+
+// if ( o->GetPrototype()->IsObject() &&
+// o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) )
+// continue;
+
+ v8::Local<v8::Value> value = o->Get( name );
+
+ const string sname = toSTLString( name );
+ if ( depth == 0 && sname == "_id" )
+ continue;
+
+ v8ToMongoElement( b , name , sname , value , depth + 1, originalBSON );
+ }
+ return b.obj();
+ }
+
+ // --- random utils ----
+
+ v8::Function * V8Scope::getNamedCons( const char * name ) {
+ return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( getV8Str( name ) ) ) );
+ }
+
+ v8::Function * V8Scope::getObjectIdCons() {
+ return getNamedCons( "ObjectId" );
+ }
+
+ Handle<v8::Value> V8Scope::Print(V8Scope* scope, const Arguments& args) {
+ bool first = true;
+ for (int i = 0; i < args.Length(); i++) {
+ HandleScope handle_scope;
+ if (first) {
+ first = false;
+ }
+ else {
+ printf(" ");
+ }
+ v8::String::Utf8Value str(args[i]);
+ printf("%s", *str);
+ }
+ printf("\n");
+ return v8::Undefined();
+ }
+
+ Handle<v8::Value> V8Scope::Version(V8Scope* scope, const Arguments& args) {
+ HandleScope handle_scope;
+ return handle_scope.Close( v8::String::New(v8::V8::GetVersion()) );
+ }
+
+ Handle<v8::Value> V8Scope::GCV8(V8Scope* scope, const Arguments& args) {
+ V8Lock l;
+ while( !V8::IdleNotification() );
+ return v8::Undefined();
+ }
+
+ /**
+ * Gets a V8 strings from the scope's cache, creating one if needed
+ */
+ v8::Handle<v8::String> V8Scope::getV8Str(string str) {
+ Persistent<v8::String> ptr = _strCache[str];
+ if (ptr.IsEmpty()) {
+ ptr = Persistent<v8::String>::New(v8::String::New(str.c_str()));
+ _strCache[str] = ptr;
+// cout << "Adding str " + str << endl;
+ }
+// cout << "Returning str " + str << endl;
+ return ptr;
+ }
+
} // namespace mongo
diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h
index c770955..3f116c9 100644
--- a/scripting/engine_v8.h
+++ b/scripting/engine_v8.h
@@ -19,7 +19,6 @@
#include <vector>
#include "engine.h"
-#include "v8_db.h"
#include <v8.h>
using namespace v8;
@@ -27,6 +26,36 @@ using namespace v8;
namespace mongo {
class V8ScriptEngine;
+ class V8Scope;
+
+ typedef Handle< Value > (*v8Function) ( V8Scope* scope, const v8::Arguments& args );
+
+ // Preemption is going to be allowed for the v8 mutex, and some of our v8
+ // usage is not preemption safe. So we are using an additional mutex that
+ // will not be preempted. The V8Lock should be used in place of v8::Locker
+ // except in certain special cases involving interrupts.
+ namespace v8Locks {
+ // the implementations are quite simple - objects must be destroyed in
+ // reverse of the order created, and should not be shared between threads
+ struct RecursiveLock {
+ RecursiveLock();
+ ~RecursiveLock();
+ bool _unlock;
+ };
+ struct RecursiveUnlock {
+ RecursiveUnlock();
+ ~RecursiveUnlock();
+ bool _lock;
+ };
+ } // namespace v8Locks
+ class V8Lock {
+ v8Locks::RecursiveLock _noPreemptionLock;
+ v8::Locker _preemptionLock;
+ };
+ struct V8Unlock {
+ v8::Unlocker _preemptionUnlock;
+ v8Locks::RecursiveUnlock _noPreemptionUnlock;
+ };
class V8Scope : public Scope {
public:
@@ -47,6 +76,7 @@ namespace mongo {
virtual string getString( const char *field );
virtual bool getBoolean( const char *field );
virtual BSONObj getObject( const char *field );
+ Handle<v8::Object> getGlobalObject() { return _global; };
virtual int type( const char *field );
@@ -55,28 +85,82 @@ namespace mongo {
virtual void setBoolean( const char *field , bool val );
virtual void setElement( const char *field , const BSONElement& e );
virtual void setObject( const char *field , const BSONObj& obj , bool readOnly);
- virtual void setThis( const BSONObj * obj );
+ virtual void setFunction( const char *field , const char * code );
+// virtual void setThis( const BSONObj * obj );
virtual void rename( const char * from , const char * to );
virtual ScriptingFunction _createFunction( const char * code );
Local< v8::Function > __createFunction( const char * code );
- virtual int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = false );
+ virtual int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool readOnlyArgs = false, bool readOnlyRecv = false );
virtual bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs );
virtual string getError() { return _error; }
- virtual void injectNative( const char *field, NativeFunction func );
+ virtual void injectNative( const char *field, NativeFunction func, void* data = 0 );
+ void injectNative( const char *field, NativeFunction func, Handle<v8::Object>& obj, void* data = 0 );
+ void injectV8Function( const char *field, v8Function func );
+ void injectV8Function( const char *field, v8Function func, Handle<v8::Object>& obj );
+ void injectV8Function( const char *field, v8Function func, Handle<v8::Template>& t );
+ Handle<v8::FunctionTemplate> createV8Function( v8Function func );
void gc();
Handle< Context > context() const { return _context; }
+ v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false );
+ v8::Handle<v8::Object> mongoToLZV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false );
+ mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0 );
+
+ void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name ,
+ const string sname , v8::Handle<v8::Value> value , int depth = 0, BSONObj* originalParent=0 );
+ v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool readOnly = false );
+ virtual void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName );
+
+ v8::Function * getNamedCons( const char * name );
+ v8::Function * getObjectIdCons();
+ Local< v8::Value > newId( const OID &id );
+
+ Persistent<v8::Object> wrapBSONObject(Local<v8::Object> obj, BSONObj* data);
+ Persistent<v8::Object> wrapArrayObject(Local<v8::Object> obj, char* data);
+
+ v8::Handle<v8::String> getV8Str(string str);
+// inline v8::Handle<v8::String> getV8Str(string str) { return v8::String::New(str.c_str()); }
+ inline v8::Handle<v8::String> getLocalV8Str(string str) { return v8::String::New(str.c_str()); }
+
+ Handle<v8::String> V8STR_CONN;
+ Handle<v8::String> V8STR_ID;
+ Handle<v8::String> V8STR_LENGTH;
+ Handle<v8::String> V8STR_LEN;
+ Handle<v8::String> V8STR_TYPE;
+ Handle<v8::String> V8STR_ISOBJECTID;
+ Handle<v8::String> V8STR_NATIVE_FUNC;
+ Handle<v8::String> V8STR_NATIVE_DATA;
+ Handle<v8::String> V8STR_V8_FUNC;
+ Handle<v8::String> V8STR_RETURN;
+ Handle<v8::String> V8STR_ARGS;
+ Handle<v8::String> V8STR_T;
+ Handle<v8::String> V8STR_I;
+ Handle<v8::String> V8STR_EMPTY;
+ Handle<v8::String> V8STR_MINKEY;
+ Handle<v8::String> V8STR_MAXKEY;
+ Handle<v8::String> V8STR_NUMBERLONG;
+ Handle<v8::String> V8STR_NUMBERINT;
+ Handle<v8::String> V8STR_DBPTR;
+ Handle<v8::String> V8STR_BINDATA;
+ Handle<v8::String> V8STR_WRAPPER;
+ Handle<v8::String> V8STR_RO;
+ Handle<v8::String> V8STR_MODIFIED;
+
private:
void _startCall();
- static Handle< Value > nativeCallback( const Arguments &args );
+ static Handle< Value > nativeCallback( V8Scope* scope, const Arguments &args );
+ static v8::Handle< v8::Value > v8Callback( const v8::Arguments &args );
+ static Handle< Value > load( V8Scope* scope, const Arguments &args );
+ static Handle< Value > Print(V8Scope* scope, const v8::Arguments& args);
+ static Handle< Value > Version(V8Scope* scope, const v8::Arguments& args);
+ static Handle< Value > GCV8(V8Scope* scope, const v8::Arguments& args);
- static Handle< Value > loadCallback( const Arguments &args );
V8ScriptEngine * _engine;
@@ -85,12 +169,19 @@ namespace mongo {
string _error;
vector< Persistent<Value> > _funcs;
- v8::Persistent<v8::Object> _this;
+ v8::Persistent<v8::Object> _emptyObj;
v8::Persistent<v8::Function> _wrapper;
enum ConnectState { NOT , LOCAL , EXTERNAL };
ConnectState _connectState;
+
+ std::map <string, v8::Persistent <v8::String> > _strCache;
+
+ Persistent<v8::ObjectTemplate> lzObjectTemplate;
+ Persistent<v8::ObjectTemplate> roObjectTemplate;
+ Persistent<v8::ObjectTemplate> lzArrayTemplate;
+ Persistent<v8::ObjectTemplate> internalFieldObjects;
};
class V8ScriptEngine : public ScriptEngine {
@@ -117,7 +208,24 @@ namespace mongo {
friend class V8Scope;
};
+ class ExternalString : public v8::String::ExternalAsciiStringResource {
+ public:
+ ExternalString(std::string str) : _data(str) {
+ }
+
+ ~ExternalString() {
+ }
+
+ const char* data () const { return _data.c_str(); }
+ size_t length () const { return _data.length(); }
+ private:
+// string _str;
+// const char* _data;
+ std::string _data;
+// size_t _len;
+ };
extern ScriptEngine * globalScriptEngine;
extern map< unsigned, int > __interruptSpecToThreadId;
+
}
diff --git a/scripting/sm_db.cpp b/scripting/sm_db.cpp
index 4c9d541..2a9169b 100644
--- a/scripting/sm_db.cpp
+++ b/scripting/sm_db.cpp
@@ -192,7 +192,15 @@ namespace mongo {
return JS_FALSE;
}
- ScriptEngine::runConnectCallback( *conn );
+ try{
+ ScriptEngine::runConnectCallback( *conn );
+ }
+ catch( std::exception& e ){
+ // Can happen if connection goes down while we're starting up here
+ // Catch so that we don't get a hard-to-trace segfault from SM
+ JS_ReportError( cx, ((string)( str::stream() << "Error during mongo startup." << causedBy( e ) )).c_str() );
+ return JS_FALSE;
+ }
assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( conn ) ) ) );
jsval host_val = c.toval( host.c_str() );
@@ -607,6 +615,7 @@ namespace mongo {
// UUID **************************
+#if 0
JSBool uuid_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
@@ -631,6 +640,8 @@ namespace mongo {
buf[i] = fromHex(encoded.c_str() + i * 2);
}
+zzz
+
assert( JS_SetPrivate( cx, obj, new BinDataHolder( buf, 16 ) ) );
c.setProperty( obj, "len", c.toval( (double)16 ) );
c.setProperty( obj, "type", c.toval( (double)3 ) );
@@ -676,6 +687,8 @@ namespace mongo {
{ 0 }
};
+#endif
+
// BinData **************************
JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
@@ -744,27 +757,17 @@ namespace mongo {
assert( holder );
const char *data = ( ( BinDataHolder* )( holder ) )->c_;
stringstream ss;
- ss << hex;
+ ss.setf (ios_base::hex , ios_base::basefield);
+ ss.fill ('0');
+ ss.setf (ios_base::right , ios_base::adjustfield);
for( int i = 0; i < len; i++ ) {
unsigned v = (unsigned char) data[i];
- ss << v;
+ ss << setw(2) << v;
}
string ret = ss.str();
return *rval = c.toval( ret.c_str() );
}
- JSBool bindataLength(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- Convertor c(cx);
- int len = (int)c.getNumber( obj, "len" );
- return *rval = c.toval((double) len);
- }
-
- JSBool bindataSubtype(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- Convertor c(cx);
- int t = (int)c.getNumber( obj, "type" );
- return *rval = c.toval((double) t);
- }
-
void bindata_finalize( JSContext * cx , JSObject * obj ) {
Convertor c(cx);
void *holder = JS_GetPrivate( cx, obj );
@@ -785,8 +788,6 @@ namespace mongo {
{ "toString" , bindata_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "hex", bindataAsHex, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "base64", bindataBase64, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
- { "length", bindataLength, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
- { "subtype", bindataSubtype, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
@@ -937,6 +938,79 @@ namespace mongo {
{ 0 }
};
+ JSClass numberint_class = {
+ "NumberInt" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ JSBool numberint_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ smuassert( cx , "NumberInt needs 0 or 1 args" , argc == 0 || argc == 1 );
+
+ if ( ! JS_InstanceOf( cx , obj , &numberint_class , 0 ) ) {
+ obj = JS_NewObject( cx , &numberint_class , 0 , 0 );
+ CHECKNEWOBJECT( obj, cx, "numberint_constructor" );
+ *rval = OBJECT_TO_JSVAL( obj );
+ }
+
+ Convertor c( cx );
+ if ( argc == 0 ) {
+ c.setProperty( obj, "floatApprox", c.toval( 0.0 ) );
+ }
+ else if ( JSVAL_IS_NUMBER( argv[ 0 ] ) ) {
+ c.setProperty( obj, "floatApprox", argv[ 0 ] );
+ }
+ else {
+ string num = c.toString( argv[ 0 ] );
+ //PRINT(num);
+ const char *numStr = num.c_str();
+ int n;
+ try {
+ n = (int) parseLL( numStr );
+ //PRINT(n);
+ }
+ catch ( const AssertionException & ) {
+ smuassert( cx , "could not convert string to integer" , false );
+ }
+ c.makeIntObj( n, obj );
+ }
+
+ return JS_TRUE;
+ }
+
+ JSBool numberint_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ Convertor c(cx);
+ return *rval = c.toval( double( c.toNumberInt( obj ) ) );
+ }
+
+ JSBool numberint_tonumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ return numberint_valueof( cx, obj, argc, argv, rval );
+ }
+
+ JSBool numberint_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ Convertor c(cx);
+ int val = c.toNumberInt( obj );
+ string ret = str::stream() << "NumberInt(" << val << ")";
+ return *rval = c.toval( ret.c_str() );
+ }
+
+ JSBool numberint_tojson(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ Convertor c(cx);
+ int val = c.toNumberInt( obj );
+ string ret = str::stream() << val;
+ return *rval = c.toval( ret.c_str() );
+ }
+
+
+ JSFunctionSpec numberint_functions[] = {
+ { "valueOf" , numberint_valueof , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "toNumber" , numberint_tonumber , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "toString" , numberint_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "tojson" , numberint_tojson , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { 0 }
+ };
+
JSClass minkey_class = {
"MinKey" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
@@ -1039,10 +1113,11 @@ namespace mongo {
assert( JS_InitClass( cx , global , 0 , &dbquery_class , dbquery_constructor , 0 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &dbpointer_class , dbpointer_constructor , 0 , 0 , dbpointer_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) );
- assert( JS_InitClass( cx , global , 0 , &uuid_class , uuid_constructor , 0 , 0 , uuid_functions , 0 , 0 ) );
+// assert( JS_InitClass( cx , global , 0 , &uuid_class , uuid_constructor , 0 , 0 , uuid_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &timestamp_class , timestamp_constructor , 0 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &numberlong_class , numberlong_constructor , 0 , 0 , numberlong_functions , 0 , 0 ) );
+ assert( JS_InitClass( cx , global , 0 , &numberint_class , numberint_constructor , 0 , 0 , numberint_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
@@ -1087,6 +1162,11 @@ namespace mongo {
return true;
}
+ if ( JS_InstanceOf( c->_context , o , &numberint_class , 0 ) ) {
+ b.append( name , c->toNumberInt( o ) );
+ return true;
+ }
+
if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ) {
b.appendDBRef( name , c->getString( o , "ns" ) , c->toOID( c->getProperty( o , "id" ) ) );
return true;
@@ -1120,8 +1200,8 @@ namespace mongo {
#else
if ( JS_InstanceOf( c->_context , o, &js_DateClass , 0 ) ) {
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
- //TODO: make signed
- b.appendDate( name , Date_t((unsigned long long)d) );
+ long long d2 = (long long)d;
+ b.appendDate( name , Date_t((unsigned long long)d2) );
return true;
}
#endif
diff --git a/scripting/utils.cpp b/scripting/utils.cpp
index 97eea10..612b173 100644
--- a/scripting/utils.cpp
+++ b/scripting/utils.cpp
@@ -25,7 +25,7 @@ namespace mongo {
void installBenchmarkSystem( Scope& scope );
- BSONObj jsmd5( const BSONObj &a ) {
+ BSONObj jsmd5( const BSONObj &a, void* data ) {
uassert( 10261 , "js md5 needs a string" , a.firstElement().type() == String );
const char * s = a.firstElement().valuestrsafe();
@@ -38,7 +38,7 @@ namespace mongo {
return BSON( "" << digestToString( d ) );
}
- BSONObj JSVersion( const BSONObj& args ) {
+ BSONObj JSVersion( const BSONObj& args, void* data ) {
cout << "version: " << versionString << endl;
if ( strstr( versionString , "+" ) )
printGitVersion();
@@ -46,6 +46,20 @@ namespace mongo {
}
+ BSONObj JSSleep(const mongo::BSONObj &args, void* data) {
+ assert( args.nFields() == 1 );
+ assert( args.firstElement().isNumber() );
+ int ms = int( args.firstElement().number() );
+ {
+ auto_ptr< ScriptEngine::Unlocker > u = globalScriptEngine->newThreadUnlocker();
+ sleepmillis( ms );
+ }
+
+ BSONObjBuilder b;
+ b.appendUndefined( "" );
+ return b.obj();
+ }
+
// ---------------------------------
// ---- installer --------
// ---------------------------------
@@ -53,6 +67,7 @@ namespace mongo {
void installGlobalUtils( Scope& scope ) {
scope.injectNative( "hex_md5" , jsmd5 );
scope.injectNative( "version" , JSVersion );
+ scope.injectNative( "sleep" , JSSleep );
installBenchmarkSystem( scope );
}
diff --git a/scripting/v8_db.cpp b/scripting/v8_db.cpp
index 4d12454..bda549c 100644
--- a/scripting/v8_db.cpp
+++ b/scripting/v8_db.cpp
@@ -15,10 +15,18 @@
* limitations under the License.
*/
+#if defined(_WIN32)
+/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with
+ our usage of boost */
+#include "boost/cstdint.hpp"
+using namespace boost;
+#define V8STDINT_H_
+#endif
+
#include "v8_wrapper.h"
#include "v8_utils.h"
-#include "v8_db.h"
#include "engine_v8.h"
+#include "v8_db.h"
#include "util/base64.h"
#include "util/text.h"
#include "../client/syncclusterconnection.h"
@@ -32,127 +40,161 @@ namespace mongo {
#define DDD(x)
- v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( bool local ) {
- v8::Local<v8::FunctionTemplate> mongo;
+ v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( V8Scope* scope, bool local ) {
+ v8::Handle<v8::FunctionTemplate> mongo;
if ( local ) {
- mongo = newV8Function< mongoConsLocal >();
+ mongo = scope->createV8Function(mongoConsLocal);
}
else {
- mongo = newV8Function< mongoConsExternal >();
+ mongo = scope->createV8Function(mongoConsExternal);
}
mongo->InstanceTemplate()->SetInternalFieldCount( 1 );
+ v8::Handle<v8::Template> proto = mongo->PrototypeTemplate();
+ scope->injectV8Function("find", mongoFind, proto);
+ scope->injectV8Function("insert", mongoInsert, proto);
+ scope->injectV8Function("remove", mongoRemove, proto);
+ scope->injectV8Function("update", mongoUpdate, proto);
- v8::Local<v8::Template> proto = mongo->PrototypeTemplate();
-
- proto->Set( v8::String::New( "find" ) , newV8Function< mongoFind >() );
- proto->Set( v8::String::New( "insert" ) , newV8Function< mongoInsert >() );
- proto->Set( v8::String::New( "remove" ) , newV8Function< mongoRemove >() );
- proto->Set( v8::String::New( "update" ) , newV8Function< mongoUpdate >() );
-
- Local<FunctionTemplate> ic = newV8Function< internalCursorCons >();
+ v8::Handle<FunctionTemplate> ic = scope->createV8Function(internalCursorCons);
ic->InstanceTemplate()->SetInternalFieldCount( 1 );
- ic->PrototypeTemplate()->Set( v8::String::New("next") , newV8Function< internalCursorNext >() );
- ic->PrototypeTemplate()->Set( v8::String::New("hasNext") , newV8Function< internalCursorHasNext >() );
- ic->PrototypeTemplate()->Set( v8::String::New("objsLeftInBatch") , newV8Function< internalCursorObjsLeftInBatch >() );
- proto->Set( v8::String::New( "internalCursor" ) , ic );
-
-
+ v8::Handle<v8::Template> icproto = ic->PrototypeTemplate();
+ scope->injectV8Function("next", internalCursorNext, icproto);
+ scope->injectV8Function("hasNext", internalCursorHasNext, icproto);
+ scope->injectV8Function("objsLeftInBatch", internalCursorObjsLeftInBatch, icproto);
+ proto->Set( scope->getV8Str( "internalCursor" ) , ic );
return mongo;
}
- v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate() {
- v8::Local<v8::FunctionTemplate> numberLong = newV8Function< numberLongInit >();
+ v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit);
v8::Local<v8::Template> proto = numberLong->PrototypeTemplate();
-
- proto->Set( v8::String::New( "valueOf" ) , newV8Function< numberLongValueOf >() );
- proto->Set( v8::String::New( "toNumber" ) , newV8Function< numberLongToNumber >() );
- proto->Set( v8::String::New( "toString" ) , newV8Function< numberLongToString >() );
+ scope->injectV8Function("valueOf", numberLongValueOf, proto);
+ scope->injectV8Function("toNumber", numberLongToNumber, proto);
+ scope->injectV8Function("toString", numberLongToString, proto);
return numberLong;
}
- v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate() {
- v8::Local<v8::FunctionTemplate> binData = newV8Function< binDataInit >();
- v8::Local<v8::Template> proto = binData->PrototypeTemplate();
+ v8::Handle<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> numberInt = scope->createV8Function(numberIntInit);
+ v8::Local<v8::Template> proto = numberInt->PrototypeTemplate();
+ scope->injectV8Function("valueOf", numberIntValueOf, proto);
+ scope->injectV8Function("toNumber", numberIntToNumber, proto);
+ scope->injectV8Function("toString", numberIntToString, proto);
- proto->Set( v8::String::New( "toString" ) , newV8Function< binDataToString >() );
+ return numberInt;
+ }
+ v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit);
+ binData->InstanceTemplate()->SetInternalFieldCount(1);
+ v8::Local<v8::Template> proto = binData->PrototypeTemplate();
+ scope->injectV8Function("toString", binDataToString, proto);
+ scope->injectV8Function("base64", binDataToBase64, proto);
+ scope->injectV8Function("hex", binDataToHex, proto);
return binData;
}
- v8::Handle<v8::FunctionTemplate> getTimestampFunctionTemplate() {
- v8::Local<v8::FunctionTemplate> ts = newV8Function< dbTimestampInit >();
- v8::Local<v8::Template> proto = ts->PrototypeTemplate();
-
- ts->InstanceTemplate()->SetInternalFieldCount( 1 );
-
- return ts;
+ v8::Handle<v8::FunctionTemplate> getUUIDFunctionTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(uuidInit);
+ templ->InstanceTemplate()->SetInternalFieldCount(1);
+ v8::Local<v8::Template> proto = templ->PrototypeTemplate();
+ scope->injectV8Function("toString", binDataToString, proto);
+ scope->injectV8Function("base64", binDataToBase64, proto);
+ scope->injectV8Function("hex", binDataToHex, proto);
+ return templ;
}
+ v8::Handle<v8::FunctionTemplate> getMD5FunctionTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(md5Init);
+ templ->InstanceTemplate()->SetInternalFieldCount(1);
+ v8::Local<v8::Template> proto = templ->PrototypeTemplate();
+ scope->injectV8Function("toString", binDataToString, proto);
+ scope->injectV8Function("base64", binDataToBase64, proto);
+ scope->injectV8Function("hex", binDataToHex, proto);
+ return templ;
+ }
- void installDBTypes( Handle<ObjectTemplate>& global ) {
- v8::Local<v8::FunctionTemplate> db = newV8Function< dbInit >();
- db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
- global->Set(v8::String::New("DB") , db );
-
- v8::Local<v8::FunctionTemplate> dbCollection = newV8Function< collectionInit >();
- dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
- global->Set(v8::String::New("DBCollection") , dbCollection );
-
-
- v8::Local<v8::FunctionTemplate> dbQuery = newV8Function< dbQueryInit >();
- dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess );
- global->Set(v8::String::New("DBQuery") , dbQuery );
-
- global->Set( v8::String::New("ObjectId") , newV8Function< objectIdInit >() );
-
- global->Set( v8::String::New("DBRef") , newV8Function< dbRefInit >() );
-
- global->Set( v8::String::New("DBPointer") , newV8Function< dbPointerInit >() );
-
- global->Set( v8::String::New("BinData") , getBinDataFunctionTemplate() );
+ v8::Handle<v8::FunctionTemplate> getHexDataFunctionTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(hexDataInit);
+ templ->InstanceTemplate()->SetInternalFieldCount(1);
+ v8::Local<v8::Template> proto = templ->PrototypeTemplate();
+ scope->injectV8Function("toString", binDataToString, proto);
+ scope->injectV8Function("base64", binDataToBase64, proto);
+ scope->injectV8Function("hex", binDataToHex, proto);
+ return templ;
+ }
- global->Set( v8::String::New("NumberLong") , getNumberLongFunctionTemplate() );
+ v8::Handle<v8::FunctionTemplate> getTimestampFunctionTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> ts = scope->createV8Function(dbTimestampInit);
+ v8::Local<v8::Template> proto = ts->PrototypeTemplate();
+ ts->InstanceTemplate()->SetInternalFieldCount( 1 );
- global->Set( v8::String::New("Timestamp") , getTimestampFunctionTemplate() );
+ return ts;
}
- void installDBTypes( Handle<v8::Object>& global ) {
- v8::Local<v8::FunctionTemplate> db = newV8Function< dbInit >();
+// void installDBTypes( V8Scope* scope, Handle<ObjectTemplate>& global ) {
+// v8::Handle<v8::FunctionTemplate> db = scope->createV8Function(dbInit);
+// db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
+// global->Set(v8::String::New("DB") , db );
+//
+// v8::Handle<v8::FunctionTemplate> dbCollection = scope->createV8Function(collectionInit);
+// dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
+// global->Set(v8::String::New("DBCollection") , dbCollection );
+//
+//
+// v8::Handle<v8::FunctionTemplate> dbQuery = scope->createV8Function(dbQueryInit);
+// dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess );
+// global->Set(v8::String::New("DBQuery") , dbQuery );
+//
+// global->Set( v8::String::New("ObjectId") , newV8Function< objectIdInit >(scope) );
+//
+// global->Set( v8::String::New("DBRef") , newV8Function< dbRefInit >(scope) );
+//
+// global->Set( v8::String::New("DBPointer") , newV8Function< dbPointerInit >(scope) );
+//
+// global->Set( v8::String::New("BinData") , getBinDataFunctionTemplate(scope) );
+//
+// global->Set( v8::String::New("NumberLong") , getNumberLongFunctionTemplate(scope) );
+//
+// global->Set( v8::String::New("Timestamp") , getTimestampFunctionTemplate(scope) );
+// }
+
+ void installDBTypes( V8Scope* scope, v8::Handle<v8::Object>& global ) {
+ v8::Handle<v8::FunctionTemplate> db = scope->createV8Function(dbInit);
db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
- global->Set(v8::String::New("DB") , db->GetFunction() );
-
- v8::Local<v8::FunctionTemplate> dbCollection = newV8Function< collectionInit >();
+ global->Set(scope->getV8Str("DB") , db->GetFunction() );
+ v8::Handle<v8::FunctionTemplate> dbCollection = scope->createV8Function(collectionInit);
dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
- global->Set(v8::String::New("DBCollection") , dbCollection->GetFunction() );
+ global->Set(scope->getV8Str("DBCollection") , dbCollection->GetFunction() );
- v8::Local<v8::FunctionTemplate> dbQuery = newV8Function< dbQueryInit >();
+ v8::Handle<v8::FunctionTemplate> dbQuery = scope->createV8Function(dbQueryInit);
dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess );
- global->Set(v8::String::New("DBQuery") , dbQuery->GetFunction() );
-
- global->Set( v8::String::New("ObjectId") , newV8Function< objectIdInit >()->GetFunction() );
-
- global->Set( v8::String::New("DBRef") , newV8Function< dbRefInit >()->GetFunction() );
+ global->Set(scope->getV8Str("DBQuery") , dbQuery->GetFunction() );
- global->Set( v8::String::New("DBPointer") , newV8Function< dbPointerInit >()->GetFunction() );
+ scope->injectV8Function("ObjectId", objectIdInit, global);
+ scope->injectV8Function("DBRef", dbRefInit, global);
+ scope->injectV8Function("DBPointer", dbPointerInit, global);
- global->Set( v8::String::New("BinData") , getBinDataFunctionTemplate()->GetFunction() );
-
- global->Set( v8::String::New("NumberLong") , getNumberLongFunctionTemplate()->GetFunction() );
-
- global->Set( v8::String::New("Timestamp") , getTimestampFunctionTemplate()->GetFunction() );
+ global->Set( scope->getV8Str("BinData") , getBinDataFunctionTemplate(scope)->GetFunction() );
+ global->Set( scope->getV8Str("UUID") , getUUIDFunctionTemplate(scope)->GetFunction() );
+ global->Set( scope->getV8Str("MD5") , getMD5FunctionTemplate(scope)->GetFunction() );
+ global->Set( scope->getV8Str("HexData") , getHexDataFunctionTemplate(scope)->GetFunction() );
+ global->Set( scope->getV8Str("NumberLong") , getNumberLongFunctionTemplate(scope)->GetFunction() );
+ global->Set( scope->getV8Str("NumberInt") , getNumberIntFunctionTemplate(scope)->GetFunction() );
+ global->Set( scope->getV8Str("Timestamp") , getTimestampFunctionTemplate(scope)->GetFunction() );
BSONObjBuilder b;
b.appendMaxKey( "" );
b.appendMinKey( "" );
BSONObj o = b.obj();
BSONObjIterator i( o );
- global->Set( v8::String::New("MaxKey"), mongoToV8Element( i.next() ) );
- global->Set( v8::String::New("MinKey"), mongoToV8Element( i.next() ) );
+ global->Set( scope->getV8Str("MaxKey"), scope->mongoToV8Element( i.next() ) );
+ global->Set( scope->getV8Str("MinKey"), scope->mongoToV8Element( i.next() ) );
- global->Get( v8::String::New( "Object" ) )->ToObject()->Set( v8::String::New("bsonsize") , newV8Function< bsonsize >()->GetFunction() );
+ global->Get( scope->getV8Str( "Object" ) )->ToObject()->Set( scope->getV8Str("bsonsize") , scope->createV8Function(bsonsize)->GetFunction() );
}
void destroyConnection( Persistent<Value> self, void* parameter) {
@@ -161,7 +203,7 @@ namespace mongo {
self.Clear();
}
- Handle<Value> mongoConsExternal(const Arguments& args) {
+ Handle<Value> mongoConsExternal(V8Scope* scope, const Arguments& args) {
char host[255];
@@ -196,13 +238,13 @@ namespace mongo {
}
args.This()->SetInternalField( 0 , External::New( conn ) );
- args.This()->Set( v8::String::New( "slaveOk" ) , Boolean::New( false ) );
- args.This()->Set( v8::String::New( "host" ) , v8::String::New( host ) );
+ args.This()->Set( scope->getV8Str( "slaveOk" ) , Boolean::New( false ) );
+ args.This()->Set( scope->getV8Str( "host" ) , scope->getV8Str( host ) );
return v8::Undefined();
}
- Handle<Value> mongoConsLocal(const Arguments& args) {
+ Handle<Value> mongoConsLocal(V8Scope* scope, const Arguments& args) {
if ( args.Length() > 0 )
return v8::ThrowException( v8::String::New( "local Mongo constructor takes no args" ) );
@@ -218,8 +260,8 @@ namespace mongo {
// NOTE I don't believe the conn object will ever be freed.
args.This()->SetInternalField( 0 , External::New( conn ) );
- args.This()->Set( v8::String::New( "slaveOk" ) , Boolean::New( false ) );
- args.This()->Set( v8::String::New( "host" ) , v8::String::New( "EMBEDDED" ) );
+ args.This()->Set( scope->getV8Str( "slaveOk" ) , Boolean::New( false ) );
+ args.This()->Set( scope->getV8Str( "host" ) , scope->getV8Str( "EMBEDDED" ) );
return v8::Undefined();
}
@@ -255,7 +297,7 @@ namespace mongo {
3 - limit
4 - skip
*/
- Handle<Value> mongoFind(const Arguments& args) {
+ Handle<Value> mongoFind(V8Scope* scope, const Arguments& args) {
HandleScope handle_scope;
jsassert( args.Length() == 7 , "find needs 7 args" );
@@ -263,16 +305,16 @@ namespace mongo {
DBClientBase * conn = getConnection( args );
GETNS;
- BSONObj q = v8ToMongo( args[1]->ToObject() );
+ BSONObj q = scope->v8ToMongo( args[1]->ToObject() );
DDD( "query:" << q );
BSONObj fields;
bool haveFields = args[2]->IsObject() && args[2]->ToObject()->GetPropertyNames()->Length() > 0;
if ( haveFields )
- fields = v8ToMongo( args[2]->ToObject() );
+ fields = scope->v8ToMongo( args[2]->ToObject() );
Local<v8::Object> mongo = args.This();
- Local<v8::Value> slaveOkVal = mongo->Get( v8::String::New( "slaveOk" ) );
+ Local<v8::Value> slaveOkVal = mongo->Get( scope->getV8Str( "slaveOk" ) );
jsassert( slaveOkVal->IsBoolean(), "slaveOk member invalid" );
bool slaveOk = slaveOkVal->BooleanValue();
@@ -285,8 +327,10 @@ namespace mongo {
{
V8Unlock u;
cursor = conn->query( ns, q , nToReturn , nToSkip , haveFields ? &fields : 0, options | ( slaveOk ? QueryOption_SlaveOk : 0 ) , batchSize );
+ if ( ! cursor.get() )
+ return v8::ThrowException( v8::String::New( "error doing query: failed" ) );
}
- v8::Function * cons = (v8::Function*)( *( mongo->Get( v8::String::New( "internalCursor" ) ) ) );
+ v8::Function * cons = (v8::Function*)( *( mongo->Get( scope->getV8Str( "internalCursor" ) ) ) );
assert( cons );
Persistent<v8::Object> c = Persistent<v8::Object>::New( cons->NewInstance() );
@@ -300,11 +344,11 @@ namespace mongo {
}
}
- v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args) {
+ v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments& args) {
jsassert( args.Length() == 2 , "insert needs 2 args" );
jsassert( args[1]->IsObject() , "have to insert an object" );
- if ( args.This()->Get( v8::String::New( "readOnly" ) )->BooleanValue() )
+ if ( args.This()->Get( scope->getV8Str( "readOnly" ) )->BooleanValue() )
return v8::ThrowException( v8::String::New( "js db in read only mode" ) );
DBClientBase * conn = getConnection( args );
@@ -312,12 +356,12 @@ namespace mongo {
v8::Handle<v8::Object> in = args[1]->ToObject();
- if ( ! in->Has( v8::String::New( "_id" ) ) ) {
+ if ( ! in->Has( scope->getV8Str( "_id" ) ) ) {
v8::Handle<v8::Value> argv[1];
- in->Set( v8::String::New( "_id" ) , getObjectIdCons()->NewInstance( 0 , argv ) );
+ in->Set( scope->getV8Str( "_id" ) , scope->getObjectIdCons()->NewInstance( 0 , argv ) );
}
- BSONObj o = v8ToMongo( in );
+ BSONObj o = scope->v8ToMongo( in );
DDD( "want to save : " << o.jsonString() );
try {
@@ -331,18 +375,18 @@ namespace mongo {
return v8::Undefined();
}
- v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args) {
+ v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments& args) {
jsassert( args.Length() == 2 || args.Length() == 3 , "remove needs 2 args" );
jsassert( args[1]->IsObject() , "have to remove an object template" );
- if ( args.This()->Get( v8::String::New( "readOnly" ) )->BooleanValue() )
+ if ( args.This()->Get( scope->getV8Str( "readOnly" ) )->BooleanValue() )
return v8::ThrowException( v8::String::New( "js db in read only mode" ) );
DBClientBase * conn = getConnection( args );
GETNS;
v8::Handle<v8::Object> in = args[1]->ToObject();
- BSONObj o = v8ToMongo( in );
+ BSONObj o = scope->v8ToMongo( in );
bool justOne = false;
if ( args.Length() > 2 ) {
@@ -361,12 +405,12 @@ namespace mongo {
return v8::Undefined();
}
- v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args) {
+ v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments& args) {
jsassert( args.Length() >= 3 , "update needs at least 3 args" );
jsassert( args[1]->IsObject() , "1st param to update has to be an object" );
jsassert( args[2]->IsObject() , "2nd param to update has to be an object" );
- if ( args.This()->Get( v8::String::New( "readOnly" ) )->BooleanValue() )
+ if ( args.This()->Get( scope->getV8Str( "readOnly" ) )->BooleanValue() )
return v8::ThrowException( v8::String::New( "js db in read only mode" ) );
DBClientBase * conn = getConnection( args );
@@ -379,8 +423,8 @@ namespace mongo {
bool multi = args.Length() > 4 && args[4]->IsBoolean() && args[4]->ToBoolean()->Value();
try {
- BSONObj q1 = v8ToMongo( q );
- BSONObj o1 = v8ToMongo( o );
+ BSONObj q1 = scope->v8ToMongo( q );
+ BSONObj o1 = scope->v8ToMongo( o );
V8Unlock u;
conn->update( ns , q1 , o1 , upsert, multi );
}
@@ -403,11 +447,11 @@ namespace mongo {
return cursor;
}
- v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args) {
+ v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Arguments& args) {
return v8::Undefined();
}
- v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args) {
+ v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Arguments& args) {
mongo::DBClientCursor * cursor = getCursor( args );
if ( ! cursor )
return v8::Undefined();
@@ -416,10 +460,13 @@ namespace mongo {
V8Unlock u;
o = cursor->next();
}
- return mongoToV8( o );
+ bool ro = false;
+ if (args.This()->Has(scope->V8STR_RO))
+ ro = args.This()->Get(scope->V8STR_RO)->BooleanValue();
+ return scope->mongoToLZV8( o, false, ro );
}
- v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args) {
+ v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args) {
mongo::DBClientCursor * cursor = getCursor( args );
if ( ! cursor )
return Boolean::New( false );
@@ -431,7 +478,7 @@ namespace mongo {
return Boolean::New( ret );
}
- v8::Handle<v8::Value> internalCursorObjsLeftInBatch(const v8::Arguments& args) {
+ v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, const v8::Arguments& args) {
mongo::DBClientCursor * cursor = getCursor( args );
if ( ! cursor )
return v8::Number::New( (double) 0 );
@@ -443,14 +490,19 @@ namespace mongo {
return v8::Number::New( (double) ret );
}
+// v8::Handle<v8::Value> internalCursorReadOnly(V8Scope* scope, const v8::Arguments& args) {
+// Local<v8::Object> cursor = args.This();
+// cursor->Set(scope->V8STR_RO, v8::Undefined());
+// return cursor;
+// }
// --- DB ----
- v8::Handle<v8::Value> dbInit(const v8::Arguments& args) {
+ v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args) {
assert( args.Length() == 2 );
- args.This()->Set( v8::String::New( "_mongo" ) , args[0] );
- args.This()->Set( v8::String::New( "_name" ) , args[1] );
+ args.This()->Set( scope->getV8Str( "_mongo" ) , args[0] );
+ args.This()->Set( scope->getV8Str( "_name" ) , args[1] );
for ( int i=0; i<args.Length(); i++ )
assert( ! args[i]->IsUndefined() );
@@ -458,13 +510,13 @@ namespace mongo {
return v8::Undefined();
}
- v8::Handle<v8::Value> collectionInit( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> collectionInit( V8Scope* scope, const v8::Arguments& args ) {
assert( args.Length() == 4 );
- args.This()->Set( v8::String::New( "_mongo" ) , args[0] );
- args.This()->Set( v8::String::New( "_db" ) , args[1] );
- args.This()->Set( v8::String::New( "_shortName" ) , args[2] );
- args.This()->Set( v8::String::New( "_fullName" ) , args[3] );
+ args.This()->Set( scope->getV8Str( "_mongo" ) , args[0] );
+ args.This()->Set( scope->getV8Str( "_db" ) , args[1] );
+ args.This()->Set( scope->getV8Str( "_shortName" ) , args[2] );
+ args.This()->Set( scope->getV8Str( "_fullName" ) , args[3] );
if ( haveLocalShardingInfo( toSTLString( args[3] ) ) )
return v8::ThrowException( v8::String::New( "can't use sharded collection from db.eval" ) );
@@ -475,52 +527,52 @@ namespace mongo {
return v8::Undefined();
}
- v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> dbQueryInit( V8Scope* scope, const v8::Arguments& args ) {
v8::Handle<v8::Object> t = args.This();
assert( args.Length() >= 4 );
- t->Set( v8::String::New( "_mongo" ) , args[0] );
- t->Set( v8::String::New( "_db" ) , args[1] );
- t->Set( v8::String::New( "_collection" ) , args[2] );
- t->Set( v8::String::New( "_ns" ) , args[3] );
+ t->Set( scope->getV8Str( "_mongo" ) , args[0] );
+ t->Set( scope->getV8Str( "_db" ) , args[1] );
+ t->Set( scope->getV8Str( "_collection" ) , args[2] );
+ t->Set( scope->getV8Str( "_ns" ) , args[3] );
if ( args.Length() > 4 && args[4]->IsObject() )
- t->Set( v8::String::New( "_query" ) , args[4] );
+ t->Set( scope->getV8Str( "_query" ) , args[4] );
else
- t->Set( v8::String::New( "_query" ) , v8::Object::New() );
+ t->Set( scope->getV8Str( "_query" ) , v8::Object::New() );
if ( args.Length() > 5 && args[5]->IsObject() )
- t->Set( v8::String::New( "_fields" ) , args[5] );
+ t->Set( scope->getV8Str( "_fields" ) , args[5] );
else
- t->Set( v8::String::New( "_fields" ) , v8::Null() );
+ t->Set( scope->getV8Str( "_fields" ) , v8::Null() );
if ( args.Length() > 6 && args[6]->IsNumber() )
- t->Set( v8::String::New( "_limit" ) , args[6] );
+ t->Set( scope->getV8Str( "_limit" ) , args[6] );
else
- t->Set( v8::String::New( "_limit" ) , Number::New( 0 ) );
+ t->Set( scope->getV8Str( "_limit" ) , Number::New( 0 ) );
if ( args.Length() > 7 && args[7]->IsNumber() )
- t->Set( v8::String::New( "_skip" ) , args[7] );
+ t->Set( scope->getV8Str( "_skip" ) , args[7] );
else
- t->Set( v8::String::New( "_skip" ) , Number::New( 0 ) );
+ t->Set( scope->getV8Str( "_skip" ) , Number::New( 0 ) );
if ( args.Length() > 8 && args[8]->IsNumber() )
- t->Set( v8::String::New( "_batchSize" ) , args[8] );
+ t->Set( scope->getV8Str( "_batchSize" ) , args[8] );
else
- t->Set( v8::String::New( "_batchSize" ) , Number::New( 0 ) );
+ t->Set( scope->getV8Str( "_batchSize" ) , Number::New( 0 ) );
if ( args.Length() > 9 && args[9]->IsNumber() )
- t->Set( v8::String::New( "_options" ) , args[9] );
+ t->Set( scope->getV8Str( "_options" ) , args[9] );
else
- t->Set( v8::String::New( "_options" ) , Number::New( 0 ) );
+ t->Set( scope->getV8Str( "_options" ) , Number::New( 0 ) );
- t->Set( v8::String::New( "_cursor" ) , v8::Null() );
- t->Set( v8::String::New( "_numReturned" ) , v8::Number::New(0) );
- t->Set( v8::String::New( "_special" ) , Boolean::New(false) );
+ t->Set( scope->getV8Str( "_cursor" ) , v8::Null() );
+ t->Set( scope->getV8Str( "_numReturned" ) , v8::Number::New(0) );
+ t->Set( scope->getV8Str( "_special" ) , Boolean::New(false) );
return v8::Undefined();
}
@@ -560,11 +612,11 @@ namespace mongo {
return f->Call( info.This() , 1 , argv );
}
- v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> objectIdInit( V8Scope* scope, const v8::Arguments& args ) {
v8::Handle<v8::Object> it = args.This();
if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
- v8::Function * f = getObjectIdCons();
+ v8::Function * f = scope->getObjectIdCons();
it = f->NewInstance();
}
@@ -585,12 +637,12 @@ namespace mongo {
oid.init( s );
}
- it->Set( v8::String::New( "str" ) , v8::String::New( oid.str().c_str() ) );
+ it->Set( scope->getV8Str( "str" ) , v8::String::New( oid.str().c_str() ) );
return it;
}
- v8::Handle<v8::Value> dbRefInit( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> dbRefInit( V8Scope* scope, const v8::Arguments& args ) {
if (args.Length() != 2 && args.Length() != 0) {
return v8::ThrowException( v8::String::New( "DBRef needs 2 arguments" ) );
@@ -599,19 +651,19 @@ namespace mongo {
v8::Handle<v8::Object> it = args.This();
if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
- v8::Function* f = getNamedCons( "DBRef" );
+ v8::Function* f = scope->getNamedCons( "DBRef" );
it = f->NewInstance();
}
if ( args.Length() == 2 ) {
- it->Set( v8::String::New( "$ref" ) , args[0] );
- it->Set( v8::String::New( "$id" ) , args[1] );
+ it->Set( scope->getV8Str( "$ref" ) , args[0] );
+ it->Set( scope->getV8Str( "$id" ) , args[1] );
}
return it;
}
- v8::Handle<v8::Value> dbPointerInit( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> dbPointerInit( V8Scope* scope, const v8::Arguments& args ) {
if (args.Length() != 2) {
return v8::ThrowException( v8::String::New( "DBPointer needs 2 arguments" ) );
@@ -620,28 +672,28 @@ namespace mongo {
v8::Handle<v8::Object> it = args.This();
if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
- v8::Function* f = getNamedCons( "DBPointer" );
+ v8::Function* f = scope->getNamedCons( "DBPointer" );
it = f->NewInstance();
}
- it->Set( v8::String::New( "ns" ) , args[0] );
- it->Set( v8::String::New( "id" ) , args[1] );
- it->SetHiddenValue( v8::String::New( "__DBPointer" ), v8::Number::New( 1 ) );
+ it->Set( scope->getV8Str( "ns" ) , args[0] );
+ it->Set( scope->getV8Str( "id" ) , args[1] );
+ it->SetHiddenValue( scope->getV8Str( "__DBPointer" ), v8::Number::New( 1 ) );
return it;
}
- v8::Handle<v8::Value> dbTimestampInit( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> dbTimestampInit( V8Scope* scope, const v8::Arguments& args ) {
v8::Handle<v8::Object> it = args.This();
if ( args.Length() == 0 ) {
- it->Set( v8::String::New( "t" ) , v8::Number::New( 0 ) );
- it->Set( v8::String::New( "i" ) , v8::Number::New( 0 ) );
+ it->Set( scope->getV8Str( "t" ) , v8::Number::New( 0 ) );
+ it->Set( scope->getV8Str( "i" ) , v8::Number::New( 0 ) );
}
else if ( args.Length() == 2 ) {
- it->Set( v8::String::New( "t" ) , args[0] );
- it->Set( v8::String::New( "i" ) , args[1] );
+ it->Set( scope->getV8Str( "t" ) , args[0] );
+ it->Set( scope->getV8Str( "i" ) , args[1] );
}
else {
return v8::ThrowException( v8::String::New( "Timestamp needs 0 or 2 arguments" ) );
@@ -653,66 +705,157 @@ namespace mongo {
}
- v8::Handle<v8::Value> binDataInit( const v8::Arguments& args ) {
- v8::Handle<v8::Object> it = args.This();
+ v8::Handle<v8::Value> binDataInit( V8Scope* scope, const v8::Arguments& args ) {
+ v8::Local<v8::Object> it = args.This();
- // 3 args: len, type, data
+ Handle<Value> type;
+ Handle<Value> len;
+ int rlen;
+ char* data;
if (args.Length() == 3) {
+ // 3 args: len, type, data
if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
- v8::Function* f = getNamedCons( "BinData" );
+ v8::Function* f = scope->getNamedCons( "BinData" );
it = f->NewInstance();
}
- it->Set( v8::String::New( "len" ) , args[0] );
- it->Set( v8::String::New( "type" ) , args[1] );
- it->Set( v8::String::New( "data" ), args[2] );
- it->SetHiddenValue( v8::String::New( "__BinData" ), v8::Number::New( 1 ) );
-
- // 2 args: type, base64 string
+ len = args[0];
+ rlen = len->IntegerValue();
+ type = args[1];
+ v8::String::Utf8Value utf( args[ 2 ] );
+ char* tmp = *utf;
+ data = new char[rlen];
+ memcpy(data, tmp, rlen);
}
else if ( args.Length() == 2 ) {
+ // 2 args: type, base64 string
if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
- v8::Function* f = getNamedCons( "BinData" );
+ v8::Function* f = scope->getNamedCons( "BinData" );
it = f->NewInstance();
}
- v8::String::Utf8Value data( args[ 1 ] );
- string decoded = base64::decode( *data );
- it->Set( v8::String::New( "len" ) , v8::Number::New( decoded.length() ) );
- it->Set( v8::String::New( "type" ) , args[ 0 ] );
- it->Set( v8::String::New( "data" ), v8::String::New( decoded.data(), decoded.length() ) );
- it->SetHiddenValue( v8::String::New( "__BinData" ), v8::Number::New( 1 ) );
-
+ type = args[0];
+ v8::String::Utf8Value utf( args[ 1 ] );
+ string decoded = base64::decode( *utf );
+ const char* tmp = decoded.data();
+ rlen = decoded.length();
+ data = new char[rlen];
+ memcpy(data, tmp, rlen);
+ len = v8::Number::New(rlen);
+// it->Set( scope->getV8Str( "data" ), v8::String::New( decoded.data(), decoded.length() ) );
}
else {
- return v8::ThrowException( v8::String::New( "BinData needs 3 arguments" ) );
+ return v8::ThrowException( v8::String::New( "BinData needs 2 or 3 arguments" ) );
}
- return it;
+ it->Set( scope->getV8Str( "len" ) , len );
+ it->Set( scope->getV8Str( "type" ) , type );
+ it->SetHiddenValue( scope->V8STR_BINDATA, v8::Number::New( 1 ) );
+ Persistent<v8::Object> res = scope->wrapArrayObject(it, data);
+ return res;
}
- v8::Handle<v8::Value> binDataToString( const v8::Arguments& args ) {
-
- if (args.Length() != 0) {
- return v8::ThrowException( v8::String::New( "toString needs 0 arguments" ) );
- }
-
+ v8::Handle<v8::Value> binDataToString( V8Scope* scope, const v8::Arguments& args ) {
v8::Handle<v8::Object> it = args.This();
- int len = it->Get( v8::String::New( "len" ) )->ToInt32()->Value();
- int type = it->Get( v8::String::New( "type" ) )->ToInt32()->Value();
- v8::String::Utf8Value data( it->Get( v8::String::New( "data" ) ) );
+ int len = it->Get( scope->V8STR_LEN )->Int32Value();
+ int type = it->Get( scope->V8STR_TYPE )->Int32Value();
+ Local<External> c = External::Cast( *(it->GetInternalField( 0 )) );
+ char* data = (char*)(c->Value());
stringstream ss;
ss << "BinData(" << type << ",\"";
- base64::encode( ss, *data, len );
+ base64::encode( ss, data, len );
ss << "\")";
string ret = ss.str();
return v8::String::New( ret.c_str() );
}
- v8::Handle<v8::Value> numberLongInit( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> binDataToBase64( V8Scope* scope, const v8::Arguments& args ) {
+ v8::Handle<v8::Object> it = args.This();
+ int len = Handle<v8::Number>::Cast(it->Get(scope->V8STR_LEN))->Int32Value();
+ Local<External> c = External::Cast( *(it->GetInternalField( 0 )) );
+ char* data = (char*)(c->Value());
+ stringstream ss;
+ base64::encode( ss, (const char *)data, len );
+ return v8::String::New(ss.str().c_str());
+ }
+
+ v8::Handle<v8::Value> binDataToHex( V8Scope* scope, const v8::Arguments& args ) {
+ v8::Handle<v8::Object> it = args.This();
+ int len = Handle<v8::Number>::Cast(it->Get(scope->V8STR_LEN))->Int32Value();
+ Local<External> c = External::Cast( *(it->GetInternalField( 0 )) );
+ char* data = (char*)(c->Value());
+ stringstream ss;
+ ss.setf (ios_base::hex , ios_base::basefield);
+ ss.fill ('0');
+ ss.setf (ios_base::right , ios_base::adjustfield);
+ for( int i = 0; i < len; i++ ) {
+ unsigned v = (unsigned char) data[i];
+ ss << setw(2) << v;
+ }
+ return v8::String::New(ss.str().c_str());
+ }
+
+ static v8::Handle<v8::Value> hexToBinData( V8Scope* scope, v8::Local<v8::Object> it, int type, string hexstr ) {
+ int len = hexstr.length() / 2;
+ char* data = new char[len];
+ const char* src = hexstr.c_str();
+ for( int i = 0; i < 16; i++ ) {
+ data[i] = fromHex(src + i * 2);
+ }
+
+ it->Set( scope->V8STR_LEN , v8::Number::New(len) );
+ it->Set( scope->V8STR_TYPE , v8::Number::New(type) );
+ it->SetHiddenValue( scope->V8STR_BINDATA, v8::Number::New( 1 ) );
+ Persistent<v8::Object> res = scope->wrapArrayObject(it, data);
+ return res;
+ }
+
+ v8::Handle<v8::Value> uuidInit( V8Scope* scope, const v8::Arguments& args ) {
+ if (args.Length() != 1) {
+ return v8::ThrowException( v8::String::New( "UUIS needs 1 argument" ) );
+ }
+ v8::String::Utf8Value utf( args[ 0 ] );
+ if( utf.length() != 32 ) {
+ return v8::ThrowException( v8::String::New( "UUIS string must have 32 characters" ) );
+ }
+
+ return hexToBinData(scope, args.This(), bdtUUID, *utf);
+ }
+
+// v8::Handle<v8::Value> uuidToString( V8Scope* scope, const v8::Arguments& args ) {
+// v8::Handle<v8::Object> it = args.This();
+// Local<External> c = External::Cast( *(it->GetInternalField( 0 )) );
+// char* data = (char*)(c->Value());
+//
+// stringstream ss;
+// ss << "UUID(\"" << toHex(data, 16) << "\")";
+// return v8::String::New( ss.str().c_str() );
+// }
+
+ v8::Handle<v8::Value> md5Init( V8Scope* scope, const v8::Arguments& args ) {
+ if (args.Length() != 1) {
+ return v8::ThrowException( v8::String::New( "MD5 needs 1 argument" ) );
+ }
+ v8::String::Utf8Value utf( args[ 0 ] );
+ if( utf.length() != 32 ) {
+ return v8::ThrowException( v8::String::New( "MD5 string must have 32 characters" ) );
+ }
+
+ return hexToBinData(scope, args.This(), MD5Type, *utf);
+ }
+
+ v8::Handle<v8::Value> hexDataInit( V8Scope* scope, const v8::Arguments& args ) {
+ if (args.Length() != 2) {
+ return v8::ThrowException( v8::String::New( "HexData needs 2 arguments" ) );
+ }
+ v8::String::Utf8Value utf( args[ 1 ] );
+ return hexToBinData(scope, args.This(), args[0]->IntegerValue(), *utf);
+ }
+
+ v8::Handle<v8::Value> numberLongInit( V8Scope* scope, const v8::Arguments& args ) {
if (args.Length() != 0 && args.Length() != 1 && args.Length() != 3) {
return v8::ThrowException( v8::String::New( "NumberLong needs 0, 1 or 3 arguments" ) );
@@ -721,16 +864,16 @@ namespace mongo {
v8::Handle<v8::Object> it = args.This();
if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
- v8::Function* f = getNamedCons( "NumberLong" );
+ v8::Function* f = scope->getNamedCons( "NumberLong" );
it = f->NewInstance();
}
if ( args.Length() == 0 ) {
- it->Set( v8::String::New( "floatApprox" ), v8::Number::New( 0 ) );
+ it->Set( scope->getV8Str( "floatApprox" ), v8::Number::New( 0 ) );
}
else if ( args.Length() == 1 ) {
if ( args[ 0 ]->IsNumber() ) {
- it->Set( v8::String::New( "floatApprox" ), args[ 0 ] );
+ it->Set( scope->getV8Str( "floatApprox" ), args[ 0 ] );
}
else {
v8::String::Utf8Value data( args[ 0 ] );
@@ -745,21 +888,21 @@ namespace mongo {
}
unsigned long long val = n;
if ( (long long)val == (long long)(double)(long long)(val) ) {
- it->Set( v8::String::New( "floatApprox" ), v8::Number::New( (double)(long long)( val ) ) );
+ it->Set( scope->getV8Str( "floatApprox" ), v8::Number::New( (double)(long long)( val ) ) );
}
else {
- it->Set( v8::String::New( "floatApprox" ), v8::Number::New( (double)(long long)( val ) ) );
- it->Set( v8::String::New( "top" ), v8::Integer::New( val >> 32 ) );
- it->Set( v8::String::New( "bottom" ), v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) ) );
+ it->Set( scope->getV8Str( "floatApprox" ), v8::Number::New( (double)(long long)( val ) ) );
+ it->Set( scope->getV8Str( "top" ), v8::Integer::New( val >> 32 ) );
+ it->Set( scope->getV8Str( "bottom" ), v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) ) );
}
}
}
else {
- it->Set( v8::String::New( "floatApprox" ) , args[0] );
- it->Set( v8::String::New( "top" ) , args[1] );
- it->Set( v8::String::New( "bottom" ) , args[2] );
+ it->Set( scope->getV8Str( "floatApprox" ) , args[0] );
+ it->Set( scope->getV8Str( "top" ) , args[1] );
+ it->Set( scope->getV8Str( "bottom" ) , args[2] );
}
- it->SetHiddenValue( v8::String::New( "__NumberLong" ), v8::Number::New( 1 ) );
+ it->SetHiddenValue( scope->V8STR_NUMBERLONG, v8::Number::New( 1 ) );
return it;
}
@@ -773,29 +916,17 @@ namespace mongo {
(unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() );
}
- v8::Handle<v8::Value> numberLongValueOf( const v8::Arguments& args ) {
-
- if (args.Length() != 0) {
- return v8::ThrowException( v8::String::New( "toNumber needs 0 arguments" ) );
- }
-
+ v8::Handle<v8::Value> numberLongValueOf( V8Scope* scope, const v8::Arguments& args ) {
v8::Handle<v8::Object> it = args.This();
-
long long val = numberLongVal( it );
-
return v8::Number::New( double( val ) );
}
- v8::Handle<v8::Value> numberLongToNumber( const v8::Arguments& args ) {
- return numberLongValueOf( args );
+ v8::Handle<v8::Value> numberLongToNumber( V8Scope* scope, const v8::Arguments& args ) {
+ return numberLongValueOf( scope, args );
}
- v8::Handle<v8::Value> numberLongToString( const v8::Arguments& args ) {
-
- if (args.Length() != 0) {
- return v8::ThrowException( v8::String::New( "toString needs 0 arguments" ) );
- }
-
+ v8::Handle<v8::Value> numberLongToString( V8Scope* scope, const v8::Arguments& args ) {
v8::Handle<v8::Object> it = args.This();
stringstream ss;
@@ -811,18 +942,62 @@ namespace mongo {
return v8::String::New( ret.c_str() );
}
- v8::Handle<v8::Value> bsonsize( const v8::Arguments& args ) {
+ v8::Handle<v8::Value> numberIntInit( V8Scope* scope, const v8::Arguments& args ) {
+
+ if (args.Length() != 0 && args.Length() != 1) {
+ return v8::ThrowException( v8::String::New( "NumberInt needs 0, 1 argument" ) );
+ }
+
+ v8::Handle<v8::Object> it = args.This();
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
+ v8::Function* f = scope->getNamedCons( "NumberInt" );
+ it = f->NewInstance();
+ }
+
+ if ( args.Length() == 0 ) {
+ it->SetHiddenValue( scope->V8STR_NUMBERINT, v8::Number::New( 0 ) );
+ }
+ else if ( args.Length() == 1 ) {
+ it->SetHiddenValue( scope->V8STR_NUMBERINT, args[0]->ToInt32() );
+ }
+
+ return it;
+ }
+
+ v8::Handle<v8::Value> numberIntValueOf( V8Scope* scope, const v8::Arguments& args ) {
+ v8::Handle<v8::Object> it = args.This();
+ int val = it->GetHiddenValue( scope->V8STR_NUMBERINT )->Int32Value();
+ return v8::Number::New( double( val ) );
+ }
+
+ v8::Handle<v8::Value> numberIntToNumber( V8Scope* scope, const v8::Arguments& args ) {
+ return numberIntValueOf( scope, args );
+ }
+
+ v8::Handle<v8::Value> numberIntToString( V8Scope* scope, const v8::Arguments& args ) {
+ v8::Handle<v8::Object> it = args.This();
+
+ stringstream ss;
+ int val = it->GetHiddenValue( scope->V8STR_NUMBERINT )->Int32Value();
+ ss << "NumberInt(" << val << ")";
+
+ string ret = ss.str();
+ return v8::String::New( ret.c_str() );
+ }
+
+ v8::Handle<v8::Value> bsonsize( V8Scope* scope, const v8::Arguments& args ) {
if ( args.Length() != 1 )
- return v8::ThrowException( v8::String::New( "bonsisze needs 1 argument" ) );
+ return v8::ThrowException( v8::String::New( "bsonsize needs 1 argument" ) );
if ( args[0]->IsNull() )
return v8::Number::New(0);
if ( ! args[ 0 ]->IsObject() )
- return v8::ThrowException( v8::String::New( "argument to bonsisze has to be an object" ) );
+ return v8::ThrowException( v8::String::New( "argument to bsonsize has to be an object" ) );
- return v8::Number::New( v8ToMongo( args[ 0 ]->ToObject() ).objsize() );
+ return v8::Number::New( scope->v8ToMongo( args[ 0 ]->ToObject() ).objsize() );
}
// to be called with v8 mutex
diff --git a/scripting/v8_db.h b/scripting/v8_db.h
index 7dbca92..08d15d0 100644
--- a/scripting/v8_db.h
+++ b/scripting/v8_db.h
@@ -22,129 +22,75 @@
#include <cstdio>
#include <cstdlib>
-#include "engine.h"
+#include "engine_v8.h"
#include "../client/dbclient.h"
namespace mongo {
// These functions may depend on the caller creating a handle scope and context scope.
- v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( bool local );
- void installDBTypes( v8::Handle<v8::ObjectTemplate>& global );
- void installDBTypes( v8::Handle<v8::Object>& global );
+ v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( V8Scope * scope, bool local );
+// void installDBTypes( V8Scope * scope, v8::Handle<v8::ObjectTemplate>& global );
+ void installDBTypes( V8Scope * scope, v8::Handle<v8::Object>& global );
// the actual globals
mongo::DBClientBase * getConnection( const v8::Arguments& args );
// Mongo members
- v8::Handle<v8::Value> mongoConsLocal(const v8::Arguments& args);
- v8::Handle<v8::Value> mongoConsExternal(const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoConsExternal(V8Scope* scope, const v8::Arguments& args);
- v8::Handle<v8::Value> mongoFind(const v8::Arguments& args);
- v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args);
- v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args);
- v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments& args);
- v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args);
- v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args);
- v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args);
- v8::Handle<v8::Value> internalCursorObjsLeftInBatch(const v8::Arguments& args);
+ v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, const v8::Arguments& args);
// DB members
- v8::Handle<v8::Value> dbInit(const v8::Arguments& args);
- v8::Handle<v8::Value> collectionInit( const v8::Arguments& args );
- v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args );
+ v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> objectIdInit( V8Scope* scope, const v8::Arguments& args );
- v8::Handle<v8::Value> dbRefInit( const v8::Arguments& args );
- v8::Handle<v8::Value> dbPointerInit( const v8::Arguments& args );
- v8::Handle<v8::Value> dbTimestampInit( const v8::Arguments& args );
+ v8::Handle<v8::Value> dbRefInit( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> dbPointerInit( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> dbTimestampInit( V8Scope* scope, const v8::Arguments& args );
- v8::Handle<v8::Value> binDataInit( const v8::Arguments& args );
- v8::Handle<v8::Value> binDataToString( const v8::Arguments& args );
+ v8::Handle<v8::Value> binDataInit( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> binDataToString( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> binDataToBase64( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> binDataToHex( V8Scope* scope, const v8::Arguments& args );
- v8::Handle<v8::Value> numberLongInit( const v8::Arguments& args );
- v8::Handle<v8::Value> numberLongToNumber(const v8::Arguments& args);
- v8::Handle<v8::Value> numberLongValueOf(const v8::Arguments& args);
- v8::Handle<v8::Value> numberLongToString(const v8::Arguments& args);
+ v8::Handle<v8::Value> uuidInit( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> md5Init( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> hexDataInit( V8Scope* scope, const v8::Arguments& args );
- v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args );
+ v8::Handle<v8::Value> numberLongInit( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> numberLongToNumber(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> numberLongToString(V8Scope* scope, const v8::Arguments& args);
+
+ v8::Handle<v8::Value> numberIntInit( V8Scope* scope, const v8::Arguments& args );
+ v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Arguments& args);
+ v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Arguments& args);
+
+ v8::Handle<v8::Value> dbQueryInit( V8Scope* scope, const v8::Arguments& args );
v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::AccessorInfo& info );
v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info);
- v8::Handle<v8::Value> bsonsize( const v8::Arguments& args );
+ v8::Handle<v8::Value> bsonsize( V8Scope* scope, const v8::Arguments& args );
// call with v8 mutex:
void enableV8Interrupt();
void disableV8Interrupt();
- // The implementation below assumes that SERVER-1816 has been fixed - in
- // particular, interrupted() must return true if an interrupt was ever
- // sent; currently that is not the case if a new killop overwrites the data
- // for an old one
- template < v8::Handle< v8::Value > ( *f ) ( const v8::Arguments& ) >
- v8::Handle< v8::Value > v8Callback( const v8::Arguments &args ) {
- disableV8Interrupt(); // we don't want to have to audit all v8 calls for termination exceptions, so we don't allow these exceptions during the callback
- if ( globalScriptEngine->interrupted() ) {
- v8::V8::TerminateExecution(); // experimentally it seems that TerminateExecution() will override the return value
- return v8::Undefined();
- }
- v8::Handle< v8::Value > ret;
- string exception;
- try {
- ret = f( args );
- }
- catch( const std::exception &e ) {
- exception = e.what();
- }
- catch( ... ) {
- exception = "unknown exception";
- }
- enableV8Interrupt();
- if ( globalScriptEngine->interrupted() ) {
- v8::V8::TerminateExecution();
- return v8::Undefined();
- }
- if ( !exception.empty() ) {
- // technically, ThrowException is supposed to be the last v8 call before returning
- ret = v8::ThrowException( v8::String::New( exception.c_str() ) );
- }
- return ret;
- }
-
- template < v8::Handle< v8::Value > ( *f ) ( const v8::Arguments& ) >
- v8::Local< v8::FunctionTemplate > newV8Function() {
- return v8::FunctionTemplate::New( v8Callback< f > );
- }
-
- // Preemption is going to be allowed for the v8 mutex, and some of our v8
- // usage is not preemption safe. So we are using an additional mutex that
- // will not be preempted. The V8Lock should be used in place of v8::Locker
- // except in certain special cases involving interrupts.
- namespace v8Locks {
- // the implementations are quite simple - objects must be destroyed in
- // reverse of the order created, and should not be shared between threads
- struct RecursiveLock {
- RecursiveLock();
- ~RecursiveLock();
- bool _unlock;
- };
- struct RecursiveUnlock {
- RecursiveUnlock();
- ~RecursiveUnlock();
- bool _lock;
- };
- } // namespace v8Locks
- class V8Lock {
- v8Locks::RecursiveLock _noPreemptionLock;
- v8::Locker _preemptionLock;
- };
- struct V8Unlock {
- v8::Unlocker _preemptionUnlock;
- v8Locks::RecursiveUnlock _noPreemptionUnlock;
- };
}
diff --git a/scripting/v8_utils.cpp b/scripting/v8_utils.cpp
index 171ced5..0f575cf 100644
--- a/scripting/v8_utils.cpp
+++ b/scripting/v8_utils.cpp
@@ -15,14 +15,20 @@
* limitations under the License.
*/
+#if defined(_WIN32)
+/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with
+ our usage of boost */
+#include "boost/cstdint.hpp"
+using namespace boost;
+#define V8STDINT_H_
+#endif
+
#include "v8_utils.h"
#include "v8_db.h"
#include <iostream>
#include <map>
#include <sstream>
#include <vector>
-#include <sys/socket.h>
-#include <netinet/in.h>
#include <boost/smart_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
@@ -33,23 +39,6 @@ using namespace v8;
namespace mongo {
- Handle<v8::Value> Print(const Arguments& args) {
- bool first = true;
- for (int i = 0; i < args.Length(); i++) {
- HandleScope handle_scope;
- if (first) {
- first = false;
- }
- else {
- printf(" ");
- }
- v8::String::Utf8Value str(args[i]);
- printf("%s", *str);
- }
- printf("\n");
- return v8::Undefined();
- }
-
std::string toSTLString( const Handle<v8::Value> & o ) {
v8::String::Utf8Value str(o);
const char * foo = *str;
@@ -136,12 +125,6 @@ namespace mongo {
return s;
}
-
- Handle<v8::Value> Version(const Arguments& args) {
- HandleScope handle_scope;
- return handle_scope.Close( v8::String::New(v8::V8::GetVersion()) );
- }
-
void ReportException(v8::TryCatch* try_catch) {
cout << try_catch << endl;
}
@@ -233,7 +216,7 @@ namespace mongo {
Persistent< Value > returnData_;
};
- Handle< Value > ThreadInit( const Arguments &args ) {
+ Handle< Value > ThreadInit( V8Scope* scope, const Arguments &args ) {
Handle<v8::Object> it = args.This();
// NOTE I believe the passed JSThreadConfig will never be freed. If this
// policy is changed, JSThread may no longer be able to store JSThreadConfig
@@ -242,7 +225,7 @@ namespace mongo {
return v8::Undefined();
}
- Handle< Value > ScopedThreadInit( const Arguments &args ) {
+ Handle< Value > ScopedThreadInit( V8Scope* scope, const Arguments &args ) {
Handle<v8::Object> it = args.This();
// NOTE I believe the passed JSThreadConfig will never be freed. If this
// policy is changed, JSThread may no longer be able to store JSThreadConfig
@@ -251,65 +234,58 @@ namespace mongo {
return v8::Undefined();
}
- JSThreadConfig *thisConfig( const Arguments &args ) {
+ JSThreadConfig *thisConfig( V8Scope* scope, const Arguments &args ) {
Local< External > c = External::Cast( *(args.This()->GetHiddenValue( v8::String::New( "_JSThreadConfig" ) ) ) );
JSThreadConfig *config = (JSThreadConfig *)( c->Value() );
return config;
}
- Handle< Value > ThreadStart( const Arguments &args ) {
- thisConfig( args )->start();
+ Handle< Value > ThreadStart( V8Scope* scope, const Arguments &args ) {
+ thisConfig( scope, args )->start();
return v8::Undefined();
}
- Handle< Value > ThreadJoin( const Arguments &args ) {
- thisConfig( args )->join();
+ Handle< Value > ThreadJoin( V8Scope* scope, const Arguments &args ) {
+ thisConfig( scope, args )->join();
return v8::Undefined();
}
- Handle< Value > ThreadReturnData( const Arguments &args ) {
+ Handle< Value > ThreadReturnData( V8Scope* scope, const Arguments &args ) {
HandleScope handle_scope;
- return handle_scope.Close( thisConfig( args )->returnData() );
+ return handle_scope.Close( thisConfig( scope, args )->returnData() );
}
- Handle< Value > ThreadInject( const Arguments &args ) {
+ Handle< Value > ThreadInject( V8Scope* scope, const Arguments &args ) {
jsassert( args.Length() == 1 , "threadInject takes exactly 1 argument" );
jsassert( args[0]->IsObject() , "threadInject needs to be passed a prototype" );
Local<v8::Object> o = args[0]->ToObject();
- o->Set( v8::String::New( "init" ) , newV8Function< ThreadInit >()->GetFunction() );
- o->Set( v8::String::New( "start" ) , newV8Function< ThreadStart >()->GetFunction() );
- o->Set( v8::String::New( "join" ) , newV8Function< ThreadJoin >()->GetFunction() );
- o->Set( v8::String::New( "returnData" ) , newV8Function< ThreadReturnData >()->GetFunction() );
+ scope->injectV8Function("init", ThreadInit, o);
+ scope->injectV8Function("start", ThreadStart, o);
+ scope->injectV8Function("join", ThreadJoin, o);
+ scope->injectV8Function("returnData", ThreadReturnData, o);
return v8::Undefined();
}
- Handle< Value > ScopedThreadInject( const Arguments &args ) {
+ Handle< Value > ScopedThreadInject( V8Scope* scope, const Arguments &args ) {
jsassert( args.Length() == 1 , "threadInject takes exactly 1 argument" );
jsassert( args[0]->IsObject() , "threadInject needs to be passed a prototype" );
Local<v8::Object> o = args[0]->ToObject();
- o->Set( v8::String::New( "init" ) , newV8Function< ScopedThreadInit >()->GetFunction() );
+ scope->injectV8Function("init", ScopedThreadInit, o);
// inheritance takes care of other member functions
return v8::Undefined();
}
- void installFork( v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context ) {
+ void installFork( V8Scope* scope, v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context ) {
if ( baseContext_.IsEmpty() ) // if this is the shell, first call will be with shell context, otherwise don't expect to use fork() anyway
baseContext_ = context;
- global->Set( v8::String::New( "_threadInject" ), newV8Function< ThreadInject >()->GetFunction() );
- global->Set( v8::String::New( "_scopedThreadInject" ), newV8Function< ScopedThreadInject >()->GetFunction() );
+ scope->injectV8Function("_threadInject", ThreadInject, global);
+ scope->injectV8Function("_scopedThreadInject", ScopedThreadInject, global);
}
- Handle<v8::Value> GCV8(const Arguments& args) {
- V8Lock l;
- while( !V8::IdleNotification() );
- return v8::Undefined();
- }
-
-
}
diff --git a/scripting/v8_utils.h b/scripting/v8_utils.h
index 40662d2..ca5d317 100644
--- a/scripting/v8_utils.h
+++ b/scripting/v8_utils.h
@@ -27,10 +27,6 @@
namespace mongo {
- v8::Handle<v8::Value> Print(const v8::Arguments& args);
- v8::Handle<v8::Value> Version(const v8::Arguments& args);
- v8::Handle<v8::Value> GCV8(const v8::Arguments& args);
-
void ReportException(v8::TryCatch* handler);
#define jsassert(x,msg) assert(x)
@@ -42,6 +38,6 @@ namespace mongo {
std::string toSTLString( const v8::TryCatch * try_catch );
class V8Scope;
- void installFork( v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context );
+ void installFork( V8Scope* scope, v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context );
}
diff --git a/scripting/v8_wrapper.cpp b/scripting/v8_wrapper.cpp
index ff67e8c..7c28a39 100644
--- a/scripting/v8_wrapper.cpp
+++ b/scripting/v8_wrapper.cpp
@@ -15,9 +15,18 @@
* limitations under the License.
*/
+#if defined(_WIN32)
+/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with
+ our usage of boost */
+#include "boost/cstdint.hpp"
+using namespace boost;
+#define V8STDINT_H_
+#endif
+
#include "v8_wrapper.h"
#include "v8_utils.h"
#include "v8_db.h"
+#include "engine_v8.h"
#include <iostream>
@@ -26,540 +35,14 @@ using namespace v8;
namespace mongo {
-#define CONN_STRING (v8::String::New( "_conn" ))
-
#define DDD(x)
- Handle<Value> NamedReadOnlySet( Local<v8::String> property, Local<Value> value, const AccessorInfo& info ) {
- cout << "cannot write to read-only object" << endl;
- return value;
- }
-
- Handle<Boolean> NamedReadOnlyDelete( Local<v8::String> property, const AccessorInfo& info ) {
- cout << "cannot delete from read-only object" << endl;
- return Boolean::New( false );
- }
-
- Handle<Value> IndexedReadOnlySet( uint32_t index, Local<Value> value, const AccessorInfo& info ) {
- cout << "cannot write to read-only array" << endl;
- return value;
- }
-
- Handle<Boolean> IndexedReadOnlyDelete( uint32_t index, const AccessorInfo& info ) {
- cout << "cannot delete from read-only array" << endl;
- return Boolean::New( false );
- }
-
- Local< v8::Value > newFunction( const char *code ) {
- stringstream codeSS;
- codeSS << "____MontoToV8_newFunction_temp = " << code;
- string codeStr = codeSS.str();
- Local< Script > compiled = Script::New( v8::String::New( codeStr.c_str() ) );
- Local< Value > ret = compiled->Run();
- return ret;
- }
-
- Local< v8::Value > newId( const OID &id ) {
- v8::Function * idCons = getObjectIdCons();
- v8::Handle<v8::Value> argv[1];
- argv[0] = v8::String::New( id.str().c_str() );
- return idCons->NewInstance( 1 , argv );
- }
-
- Local<v8::Object> mongoToV8( const BSONObj& m , bool array, bool readOnly ) {
-
- Local<v8::Object> o;
-
- // handle DBRef. needs to come first. isn't it? (metagoto)
- static string ref = "$ref";
- if ( ref == m.firstElement().fieldName() ) {
- const BSONElement& id = m["$id"];
- if (!id.eoo()) { // there's no check on $id exitence in sm implementation. risky ?
- v8::Function* dbRef = getNamedCons( "DBRef" );
- o = dbRef->NewInstance();
- }
- }
-
- Local< v8::ObjectTemplate > readOnlyObjects;
- // Hoping template construction is fast...
- Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New();
- internalFieldObjects->SetInternalFieldCount( 1 );
-
- if ( !o.IsEmpty() ) {
- readOnly = false;
- }
- else if ( array ) {
- // NOTE Looks like it's impossible to add interceptors to v8 arrays.
- readOnly = false;
- o = v8::Array::New();
- }
- else if ( !readOnly ) {
- o = v8::Object::New();
- }
- else {
- // NOTE Our readOnly implemention relies on undocumented ObjectTemplate
- // functionality that may be fragile, but it still seems like the best option
- // for now -- fwiw, the v8 docs are pretty sparse. I've determined experimentally
- // that when property handlers are set for an object template, they will attach
- // to objects previously created by that template. To get this to work, though,
- // it is necessary to initialize the template's property handlers before
- // creating objects from the template (as I have in the following few lines
- // of code).
- // NOTE In my first attempt, I configured the permanent property handlers before
- // constructiong the object and replaced the Set() calls below with ForceSet().
- // However, it turns out that ForceSet() only bypasses handlers for named
- // properties and not for indexed properties.
- readOnlyObjects = v8::ObjectTemplate::New();
- // NOTE This internal field will store type info for special db types. For
- // regular objects the field is unnecessary - for simplicity I'm creating just
- // one readOnlyObjects template for objects where the field is & isn't necessary,
- // assuming that the overhead of an internal field is slight.
- readOnlyObjects->SetInternalFieldCount( 1 );
- readOnlyObjects->SetNamedPropertyHandler( 0 );
- readOnlyObjects->SetIndexedPropertyHandler( 0 );
- o = readOnlyObjects->NewInstance();
- }
-
- mongo::BSONObj sub;
-
- for ( BSONObjIterator i(m); i.more(); ) {
- const BSONElement& f = i.next();
-
- Local<Value> v;
-
- switch ( f.type() ) {
-
- case mongo::Code:
- o->Set( v8::String::New( f.fieldName() ), newFunction( f.valuestr() ) );
- break;
-
- case CodeWScope:
- if ( f.codeWScopeObject().isEmpty() )
- log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
- o->Set( v8::String::New( f.fieldName() ), newFunction( f.codeWScopeCode() ) );
- break;
-
- case mongo::String:
- o->Set( v8::String::New( f.fieldName() ) , v8::String::New( f.valuestr() ) );
- break;
-
- case mongo::jstOID: {
- v8::Function * idCons = getObjectIdCons();
- v8::Handle<v8::Value> argv[1];
- argv[0] = v8::String::New( f.__oid().str().c_str() );
- o->Set( v8::String::New( f.fieldName() ) ,
- idCons->NewInstance( 1 , argv ) );
- break;
- }
-
- case mongo::NumberDouble:
- case mongo::NumberInt:
- o->Set( v8::String::New( f.fieldName() ) , v8::Number::New( f.number() ) );
- break;
-
- case mongo::Array:
- case mongo::Object:
- sub = f.embeddedObject();
- o->Set( v8::String::New( f.fieldName() ) , mongoToV8( sub , f.type() == mongo::Array, readOnly ) );
- break;
-
- case mongo::Date:
- o->Set( v8::String::New( f.fieldName() ) , v8::Date::New( f.date() ) );
- break;
-
- case mongo::Bool:
- o->Set( v8::String::New( f.fieldName() ) , v8::Boolean::New( f.boolean() ) );
- break;
-
- case mongo::jstNULL:
- case mongo::Undefined: // duplicate sm behavior
- o->Set( v8::String::New( f.fieldName() ) , v8::Null() );
- break;
-
- case mongo::RegEx: {
- v8::Function * regex = getNamedCons( "RegExp" );
-
- v8::Handle<v8::Value> argv[2];
- argv[0] = v8::String::New( f.regex() );
- argv[1] = v8::String::New( f.regexFlags() );
-
- o->Set( v8::String::New( f.fieldName() ) , regex->NewInstance( 2 , argv ) );
- break;
- }
-
- case mongo::BinData: {
- Local<v8::Object> b = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
-
- int len;
- const char *data = f.binData( len );
-
- v8::Function* binData = getNamedCons( "BinData" );
- v8::Handle<v8::Value> argv[3];
- argv[0] = v8::Number::New( len );
- argv[1] = v8::Number::New( f.binDataType() );
- argv[2] = v8::String::New( data, len );
- o->Set( v8::String::New( f.fieldName() ), binData->NewInstance(3, argv) );
- break;
- }
-
- case mongo::Timestamp: {
- Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
-
- sub->Set( v8::String::New( "t" ) , v8::Number::New( f.timestampTime() ) );
- sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) );
- sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
-
- o->Set( v8::String::New( f.fieldName() ) , sub );
- break;
- }
-
- case mongo::NumberLong: {
- Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
- unsigned long long val = f.numberLong();
- v8::Function* numberLong = getNamedCons( "NumberLong" );
- if ( (long long)val == (long long)(double)(long long)(val) ) {
- v8::Handle<v8::Value> argv[1];
- argv[0] = v8::Number::New( (double)(long long)( val ) );
- o->Set( v8::String::New( f.fieldName() ), numberLong->NewInstance( 1, argv ) );
- }
- else {
- v8::Handle<v8::Value> argv[3];
- argv[0] = v8::Number::New( (double)(long long)(val) );
- argv[1] = v8::Integer::New( val >> 32 );
- argv[2] = v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) );
- o->Set( v8::String::New( f.fieldName() ), numberLong->NewInstance(3, argv) );
- }
- break;
- }
-
- case mongo::MinKey: {
- Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
- sub->Set( v8::String::New( "$MinKey" ), v8::Boolean::New( true ) );
- sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
- o->Set( v8::String::New( f.fieldName() ) , sub );
- break;
- }
-
- case mongo::MaxKey: {
- Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
- sub->Set( v8::String::New( "$MaxKey" ), v8::Boolean::New( true ) );
- sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
- o->Set( v8::String::New( f.fieldName() ) , sub );
- break;
- }
-
- case mongo::DBRef: {
- v8::Function* dbPointer = getNamedCons( "DBPointer" );
- v8::Handle<v8::Value> argv[2];
- argv[0] = v8::String::New( f.dbrefNS() );
- argv[1] = newId( f.dbrefOID() );
- o->Set( v8::String::New( f.fieldName() ), dbPointer->NewInstance(2, argv) );
- break;
- }
-
- default:
- cout << "can't handle type: ";
- cout << f.type() << " ";
- cout << f.toString();
- cout << endl;
- break;
- }
-
- }
-
- if ( readOnly ) {
- readOnlyObjects->SetNamedPropertyHandler( 0, NamedReadOnlySet, 0, NamedReadOnlyDelete );
- readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete );
- }
-
- return o;
- }
-
- Handle<v8::Value> mongoToV8Element( const BSONElement &f ) {
- Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New();
- internalFieldObjects->SetInternalFieldCount( 1 );
-
- switch ( f.type() ) {
-
- case mongo::Code:
- return newFunction( f.valuestr() );
-
- case CodeWScope:
- if ( f.codeWScopeObject().isEmpty() )
- log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
- return newFunction( f.codeWScopeCode() );
-
- case mongo::String:
- return v8::String::New( f.valuestr() );
-
- case mongo::jstOID:
- return newId( f.__oid() );
-
- case mongo::NumberDouble:
- case mongo::NumberInt:
- return v8::Number::New( f.number() );
-
- case mongo::Array:
- case mongo::Object:
- return mongoToV8( f.embeddedObject() , f.type() == mongo::Array );
-
- case mongo::Date:
- return v8::Date::New( f.date() );
-
- case mongo::Bool:
- return v8::Boolean::New( f.boolean() );
-
- case mongo::EOO:
- case mongo::jstNULL:
- case mongo::Undefined: // duplicate sm behavior
- return v8::Null();
-
- case mongo::RegEx: {
- v8::Function * regex = getNamedCons( "RegExp" );
-
- v8::Handle<v8::Value> argv[2];
- argv[0] = v8::String::New( f.regex() );
- argv[1] = v8::String::New( f.regexFlags() );
-
- return regex->NewInstance( 2 , argv );
- break;
- }
-
- case mongo::BinData: {
- int len;
- const char *data = f.binData( len );
-
- v8::Function* binData = getNamedCons( "BinData" );
- v8::Handle<v8::Value> argv[3];
- argv[0] = v8::Number::New( len );
- argv[1] = v8::Number::New( f.binDataType() );
- argv[2] = v8::String::New( data, len );
- return binData->NewInstance( 3, argv );
- };
-
- case mongo::Timestamp: {
- Local<v8::Object> sub = internalFieldObjects->NewInstance();
-
- sub->Set( v8::String::New( "t" ) , v8::Number::New( f.timestampTime() ) );
- sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) );
- sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
-
- return sub;
- }
-
- case mongo::NumberLong: {
- Local<v8::Object> sub = internalFieldObjects->NewInstance();
- unsigned long long val = f.numberLong();
- v8::Function* numberLong = getNamedCons( "NumberLong" );
- if ( (long long)val == (long long)(double)(long long)(val) ) {
- v8::Handle<v8::Value> argv[1];
- argv[0] = v8::Number::New( (double)(long long)( val ) );
- return numberLong->NewInstance( 1, argv );
- }
- else {
- v8::Handle<v8::Value> argv[3];
- argv[0] = v8::Number::New( (double)(long long)( val ) );
- argv[1] = v8::Integer::New( val >> 32 );
- argv[2] = v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) );
- return numberLong->NewInstance( 3, argv );
- }
- }
-
- case mongo::MinKey: {
- Local<v8::Object> sub = internalFieldObjects->NewInstance();
- sub->Set( v8::String::New( "$MinKey" ), v8::Boolean::New( true ) );
- sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
- return sub;
- }
-
- case mongo::MaxKey: {
- Local<v8::Object> sub = internalFieldObjects->NewInstance();
- sub->Set( v8::String::New( "$MaxKey" ), v8::Boolean::New( true ) );
- sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
- return sub;
- }
-
- case mongo::DBRef: {
- v8::Function* dbPointer = getNamedCons( "DBPointer" );
- v8::Handle<v8::Value> argv[2];
- argv[0] = v8::String::New( f.dbrefNS() );
- argv[1] = newId( f.dbrefOID() );
- return dbPointer->NewInstance(2, argv);
- }
-
- default:
- cout << "can't handle type: ";
- cout << f.type() << " ";
- cout << f.toString();
- cout << endl;
- break;
- }
-
- return v8::Undefined();
- }
-
- void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth ) {
-
- if ( value->IsString() ) {
- b.append( sname , toSTLString( value ).c_str() );
- return;
- }
-
- if ( value->IsFunction() ) {
- b.appendCode( sname , toSTLString( value ) );
- return;
- }
-
- if ( value->IsNumber() ) {
- if ( value->IsInt32() )
- b.append( sname, int( value->ToInt32()->Value() ) );
- else
- b.append( sname , value->ToNumber()->Value() );
- return;
- }
-
- if ( value->IsArray() ) {
- BSONObj sub = v8ToMongo( value->ToObject() , depth );
- b.appendArray( sname , sub );
- return;
- }
-
- if ( value->IsDate() ) {
- b.appendDate( sname , Date_t( (unsigned long long)(v8::Date::Cast( *value )->NumberValue())) );
- return;
- }
-
- if ( value->IsExternal() )
- return;
-
- if ( value->IsObject() ) {
- // The user could potentially modify the fields of these special objects,
- // wreaking havoc when we attempt to reinterpret them. Not doing any validation
- // for now...
- Local< v8::Object > obj = value->ToObject();
- if ( obj->InternalFieldCount() && obj->GetInternalField( 0 )->IsNumber() ) {
- switch( obj->GetInternalField( 0 )->ToInt32()->Value() ) { // NOTE Uint32's Value() gave me a linking error, so going with this instead
- case Timestamp:
- b.appendTimestamp( sname,
- Date_t( (unsigned long long)(obj->Get( v8::String::New( "t" ) )->ToNumber()->Value() )),
- obj->Get( v8::String::New( "i" ) )->ToInt32()->Value() );
- return;
- case MinKey:
- b.appendMinKey( sname );
- return;
- case MaxKey:
- b.appendMaxKey( sname );
- return;
- default:
- assert( "invalid internal field" == 0 );
- }
- }
- string s = toSTLString( value );
- if ( s.size() && s[0] == '/' ) {
- s = s.substr( 1 );
- string r = s.substr( 0 , s.rfind( "/" ) );
- string o = s.substr( s.rfind( "/" ) + 1 );
- b.appendRegex( sname , r , o );
- }
- else if ( value->ToObject()->GetPrototype()->IsObject() &&
- value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ) {
- OID oid;
- oid.init( toSTLString( value ) );
- b.appendOID( sname , &oid );
- }
- else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__NumberLong" ) ).IsEmpty() ) {
- // TODO might be nice to potentially speed this up with an indexed internal
- // field, but I don't yet know how to use an ObjectTemplate with a
- // constructor.
- v8::Handle< v8::Object > it = value->ToObject();
- long long val;
- if ( !it->Has( v8::String::New( "top" ) ) ) {
- val = (long long)( it->Get( v8::String::New( "floatApprox" ) )->NumberValue() );
- }
- else {
- val = (long long)
- ( (unsigned long long)( it->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) +
- (unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() );
- }
-
- b.append( sname, val );
- }
- else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__DBPointer" ) ).IsEmpty() ) {
- OID oid;
- oid.init( toSTLString( value->ToObject()->Get( v8::String::New( "id" ) ) ) );
- string ns = toSTLString( value->ToObject()->Get( v8::String::New( "ns" ) ) );
- b.appendDBRef( sname, ns, oid );
- }
- else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__BinData" ) ).IsEmpty() ) {
- int len = obj->Get( v8::String::New( "len" ) )->ToInt32()->Value();
- v8::String::Utf8Value data( obj->Get( v8::String::New( "data" ) ) );
- const char *dataArray = *data;
- assert( data.length() == len );
- b.appendBinData( sname,
- len,
- mongo::BinDataType( obj->Get( v8::String::New( "type" ) )->ToInt32()->Value() ),
- dataArray );
- }
- else {
- BSONObj sub = v8ToMongo( value->ToObject() , depth );
- b.append( sname , sub );
- }
- return;
- }
-
- if ( value->IsBoolean() ) {
- b.appendBool( sname , value->ToBoolean()->Value() );
- return;
- }
-
- else if ( value->IsUndefined() ) {
- b.appendUndefined( sname );
- return;
- }
-
- else if ( value->IsNull() ) {
- b.appendNull( sname );
- return;
- }
-
- cout << "don't know how to convert to mongo field [" << name << "]\t" << value << endl;
- }
-
- BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth ) {
- BSONObjBuilder b;
-
- if ( depth == 0 ) {
- v8::Handle<v8::String> idName = v8::String::New( "_id" );
- if ( o->HasRealNamedProperty( idName ) ) {
- v8ToMongoElement( b , idName , "_id" , o->Get( idName ) );
- }
- }
-
- Local<v8::Array> names = o->GetPropertyNames();
- for ( unsigned int i=0; i<names->Length(); i++ ) {
- v8::Local<v8::String> name = names->Get(v8::Integer::New(i) )->ToString();
-
- if ( o->GetPrototype()->IsObject() &&
- o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) )
- continue;
-
- v8::Local<v8::Value> value = o->Get( name );
-
- const string sname = toSTLString( name );
- if ( depth == 0 && sname == "_id" )
- continue;
-
- v8ToMongoElement( b , name , sname , value , depth + 1 );
- }
- return b.obj();
- }
-
// --- object wrapper ---
class WrapperHolder {
public:
- WrapperHolder( const BSONObj * o , bool readOnly , bool iDelete )
- : _o(o), _readOnly( readOnly ), _iDelete( iDelete ) {
+ WrapperHolder( V8Scope* scope, const BSONObj * o , bool readOnly , bool iDelete )
+ : _scope(scope), _o(o), _readOnly( readOnly ), _iDelete( iDelete ) {
}
~WrapperHolder() {
@@ -572,22 +55,21 @@ namespace mongo {
v8::Handle<v8::Value> get( v8::Local<v8::String> name ) {
const string& s = toSTLString( name );
const BSONElement& e = _o->getField( s );
- return mongoToV8Element(e);
+ return _scope->mongoToV8Element(e);
}
+ V8Scope* _scope;
const BSONObj * _o;
bool _readOnly;
bool _iDelete;
};
- WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ) {
- return new WrapperHolder( o , readOnly , iDelete );
+ WrapperHolder * createWrapperHolder( V8Scope* scope, const BSONObj * o , bool readOnly , bool iDelete ) {
+ return new WrapperHolder( scope, o , readOnly , iDelete );
}
-#define WRAPPER_STRING (v8::String::New( "_wrapper" ) )
-
WrapperHolder * getWrapper( v8::Handle<v8::Object> o ) {
- Handle<v8::Value> t = o->GetRealNamedProperty( WRAPPER_STRING );
+ Handle<v8::Value> t = o->GetRealNamedProperty( v8::String::New( "_wrapper" ) );
assert( t->IsExternal() );
Local<External> c = External::Cast( *t );
WrapperHolder * w = (WrapperHolder*)(c->Value());
@@ -596,11 +78,11 @@ namespace mongo {
}
- Handle<Value> wrapperCons(const Arguments& args) {
+ Handle<Value> wrapperCons(V8Scope* scope, const Arguments& args) {
if ( ! ( args.Length() == 1 && args[0]->IsExternal() ) )
return v8::ThrowException( v8::String::New( "wrapperCons needs 1 External arg" ) );
- args.This()->Set( WRAPPER_STRING , args[0] );
+ args.This()->Set( v8::String::New( "_wrapper" ) , args[0] );
return v8::Undefined();
}
@@ -609,20 +91,9 @@ namespace mongo {
return getWrapper( info.This() )->get( name );
}
- v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate() {
- v8::Local<v8::FunctionTemplate> t = newV8Function< wrapperCons >();
+ v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate(V8Scope* scope) {
+ v8::Handle<v8::FunctionTemplate> t = scope->createV8Function(wrapperCons);
t->InstanceTemplate()->SetNamedPropertyHandler( wrapperGetHandler );
return t;
}
-
- // --- random utils ----
-
- v8::Function * getNamedCons( const char * name ) {
- return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( v8::String::New( name ) ) ) );
- }
-
- v8::Function * getObjectIdCons() {
- return getNamedCons( "ObjectId" );
- }
-
}
diff --git a/scripting/v8_wrapper.h b/scripting/v8_wrapper.h
index e0b79e3..22f14e6 100644
--- a/scripting/v8_wrapper.h
+++ b/scripting/v8_wrapper.h
@@ -22,22 +22,13 @@
#include <cstdio>
#include <cstdlib>
#include "../db/jsobj.h"
+#include "engine_v8.h"
namespace mongo {
- v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false );
- mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0 );
-
- void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name ,
- const string sname , v8::Handle<v8::Value> value , int depth = 0 );
- v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f );
-
- v8::Function * getNamedCons( const char * name );
- v8::Function * getObjectIdCons();
-
- v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate();
+ v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate(V8Scope* scope);
class WrapperHolder;
- WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete );
+ WrapperHolder * createWrapperHolder( V8Scope* scope, const BSONObj * o , bool readOnly , bool iDelete );
}