summaryrefslogtreecommitdiff
path: root/scripting
diff options
context:
space:
mode:
Diffstat (limited to 'scripting')
-rw-r--r--scripting/bench.cpp173
-rw-r--r--scripting/engine.cpp253
-rw-r--r--scripting/engine.h134
-rw-r--r--scripting/engine_java.cpp77
-rw-r--r--scripting/engine_java.h16
-rw-r--r--scripting/engine_none.cpp2
-rw-r--r--scripting/engine_spidermonkey.cpp624
-rw-r--r--scripting/engine_spidermonkey.h16
-rw-r--r--scripting/engine_v8.cpp323
-rw-r--r--scripting/engine_v8.h55
-rw-r--r--scripting/sm_db.cpp628
-rw-r--r--scripting/utils.cpp23
-rw-r--r--scripting/v8_db.cpp498
-rw-r--r--scripting/v8_db.h92
-rw-r--r--scripting/v8_utils.cpp141
-rw-r--r--scripting/v8_utils.h4
-rw-r--r--scripting/v8_wrapper.cpp282
-rw-r--r--scripting/v8_wrapper.h4
18 files changed, 2007 insertions, 1338 deletions
diff --git a/scripting/bench.cpp b/scripting/bench.cpp
new file mode 100644
index 0000000..2723985
--- /dev/null
+++ b/scripting/bench.cpp
@@ -0,0 +1,173 @@
+/** @file bench.cpp */
+
+/*
+ * Copyright (C) 2010 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pch.h"
+#include "engine.h"
+#include "../util/md5.hpp"
+#include "../util/version.h"
+#include "../client/dbclient.h"
+#include "../client/connpool.h"
+// ---------------------------------
+// ---- benchmarking system --------
+// ---------------------------------
+
+
+namespace mongo {
+
+
+ /**
+ * benchQuery( "foo" , { _id : 1 } )
+ */
+ BSONObj benchQuery( const BSONObj& args ) {
+ return BSONObj();
+ }
+
+ struct BenchRunConfig {
+ BenchRunConfig() {
+ host = "localhost";
+ db = "test";
+
+ parallel = 1;
+ seconds = 1;
+
+ active = true;
+ threadsReady = 0;
+ error = false;
+ }
+
+ string host;
+ string db;
+
+ unsigned parallel;
+ int seconds;
+
+ BSONObj ops;
+
+ bool active; // true at starts, gets set to false when should stop
+ AtomicUInt threadsReady;
+
+ bool error;
+ };
+
+ static void benchThread( BenchRunConfig * config ) {
+ ScopedDbConnection conn( config->host );
+ config->threadsReady++;
+
+ while ( config->active ) {
+ BSONObjIterator i( config->ops );
+ while ( i.more() ) {
+ BSONElement e = i.next();
+ string ns = e["ns"].String();
+ string op = e["op"].String();
+
+ if ( op == "findOne" ) {
+ conn->findOne( ns , e["query"].Obj() );
+ }
+ else {
+ log() << "don't understand op: " << op << endl;
+ config->error = true;
+ return;
+ }
+
+ }
+ }
+
+ conn.done();
+ }
+
+ /**
+ * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 }
+ */
+ BSONObj benchRun( const BSONObj& argsFake ) {
+ assert( argsFake.firstElement().isABSONObj() );
+ BSONObj args = argsFake.firstElement().Obj();
+
+ // setup
+
+ BenchRunConfig config;
+
+ if ( args["host"].type() == String )
+ config.host = args["host"].String();
+ if ( args["db"].type() == String )
+ config.db = args["db"].String();
+
+ if ( args["parallel"].isNumber() )
+ config.parallel = args["parallel"].numberInt();
+ if ( args["seconds"].isNumber() )
+ config.seconds = args["seconds"].numberInt();
+
+
+ config.ops = args["ops"].Obj();
+
+ // execute
+
+ ScopedDbConnection conn( config.host );
+
+ // start threads
+ vector<boost::thread*> all;
+ for ( unsigned i=0; i<config.parallel; i++ )
+ all.push_back( new boost::thread( boost::bind( benchThread , &config ) ) );
+
+ // give them time to init
+ while ( config.threadsReady < config.parallel )
+ sleepmillis( 1 );
+
+ BSONObj before;
+ conn->simpleCommand( "admin" , &before , "serverStatus" );
+
+ sleepsecs( config.seconds );
+
+ BSONObj after;
+ conn->simpleCommand( "admin" , &after , "serverStatus" );
+
+ conn.done();
+
+ config.active = false;
+
+ for ( unsigned i=0; i<all.size(); i++ )
+ all[i]->join();
+
+ if ( config.error )
+ return BSON( "err" << 1 );
+
+ // compute actual ops/sec
+
+ before = before["opcounters"].Obj();
+ after = after["opcounters"].Obj();
+
+ BSONObjBuilder buf;
+ buf.append( "note" , "values per second" );
+
+ {
+ BSONObjIterator i( after );
+ while ( i.more() ) {
+ BSONElement e = i.next();
+ double x = e.number();
+ x = x - before[e.fieldName()].number();
+ buf.append( e.fieldName() , x / config.seconds );
+ }
+ }
+ BSONObj zoo = buf.obj();
+ return BSON( "" << zoo );
+ }
+
+ void installBenchmarkSystem( Scope& scope ) {
+ scope.injectNative( "benchRun" , benchRun );
+ }
+
+}
diff --git a/scripting/engine.cpp b/scripting/engine.cpp
index da108c6..60e56ae 100644
--- a/scripting/engine.cpp
+++ b/scripting/engine.cpp
@@ -23,27 +23,27 @@
namespace mongo {
long long Scope::_lastVersion = 1;
-
+
int Scope::_numScopes = 0;
- Scope::Scope() : _localDBName("") , _loadedVersion(0){
+ Scope::Scope() : _localDBName("") , _loadedVersion(0) {
_numScopes++;
}
- Scope::~Scope(){
+ Scope::~Scope() {
_numScopes--;
}
ScriptEngine::ScriptEngine() : _scopeInitCallback() {
}
- ScriptEngine::~ScriptEngine(){
+ ScriptEngine::~ScriptEngine() {
}
- void Scope::append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ){
+ void Scope::append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ) {
int t = type( scopeName );
-
- switch ( t ){
+
+ switch ( t ) {
case Object:
builder.append( fieldName , getObject( scopeName ) );
break;
@@ -74,7 +74,7 @@ namespace mongo {
builder.appendDate( fieldName , Date_t((unsigned long long)getNumber( scopeName )) );
break;
case Code:
- builder.appendCode( fieldName , getString( scopeName ).c_str() );
+ builder.appendCode( fieldName , getString( scopeName ) );
break;
default:
stringstream temp;
@@ -82,20 +82,20 @@ namespace mongo {
temp << t;
uassert( 10206 , temp.str() , 0 );
}
-
+
}
- int Scope::invoke( const char* code , const BSONObj& args, int timeoutMs ){
+ int Scope::invoke( const char* code , const BSONObj& args, int timeoutMs ) {
ScriptingFunction func = createFunction( code );
uassert( 10207 , "compile failed" , func );
return invoke( func , args, timeoutMs );
}
-
- bool Scope::execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs ){
-
+
+ bool Scope::execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs ) {
+
path p( filename );
- if ( ! exists( p ) ){
+ if ( ! exists( p ) ) {
log() << "file [" << filename << "] doesn't exist" << endl;
if ( assertOnError )
assert( 0 );
@@ -103,10 +103,10 @@ namespace mongo {
}
// iterate directories and recurse using all *.js files in the directory
- if ( is_directory( p ) ){
+ if ( is_directory( p ) ) {
directory_iterator end;
bool empty = true;
- for (directory_iterator it (p); it != end; it++){
+ for (directory_iterator it (p); it != end; it++) {
empty = false;
path sub (*it);
if (!endsWith(sub.string().c_str(), ".js"))
@@ -115,7 +115,7 @@ namespace mongo {
return false;
}
- if (empty){
+ if (empty) {
log() << "directory [" << filename << "] doesn't have any *.js files" << endl;
if ( assertOnError )
assert( 0 );
@@ -124,83 +124,97 @@ namespace mongo {
return true;
}
-
+
File f;
f.open( filename.c_str() , true );
- fileofs L = f.len();
- assert( L <= 0x7ffffffe );
- char * data = (char*)malloc( (size_t) L+1 );
+ unsigned L;
+ {
+ fileofs fo = f.len();
+ assert( fo <= 0x7ffffffe );
+ L = (unsigned) fo;
+ }
+ boost::scoped_array<char> data (new char[L+1]);
data[L] = 0;
- f.read( 0 , data , (size_t) L );
-
- return exec( data , filename , printResult , reportError , assertOnError, timeoutMs );
+ f.read( 0 , data.get() , L );
+
+ int offset = 0;
+ if (data[0] == '#' && data[1] == '!') {
+ const char* newline = strchr(data.get(), '\n');
+ if (! newline)
+ return true; // file of just shebang treated same as empty file
+ offset = newline - data.get();
+ }
+
+ StringData code (data.get() + offset, L - offset);
+
+ return exec( code , filename , printResult , reportError , assertOnError, timeoutMs );
}
- void Scope::storedFuncMod(){
+ void Scope::storedFuncMod() {
_lastVersion++;
}
-
+
void Scope::validateObjectIdString( const string &str ) {
massert( 10448 , "invalid object id: length", str.size() == 24 );
- for ( string::size_type i=0; i<str.size(); i++ ){
+ for ( string::size_type i=0; i<str.size(); i++ ) {
char c = str[i];
if ( ( c >= '0' && c <= '9' ) ||
- ( c >= 'a' && c <= 'f' ) ||
- ( c >= 'A' && c <= 'F' ) ){
+ ( c >= 'a' && c <= 'f' ) ||
+ ( c >= 'A' && c <= 'F' ) ) {
continue;
}
massert( 10430 , "invalid object id: not hex", false );
- }
+ }
}
- void Scope::loadStored( bool ignoreNotConnected ){
- if ( _localDBName.size() == 0 ){
+ void Scope::loadStored( bool ignoreNotConnected ) {
+ if ( _localDBName.size() == 0 ) {
if ( ignoreNotConnected )
return;
uassert( 10208 , "need to have locallyConnected already" , _localDBName.size() );
}
if ( _loadedVersion == _lastVersion )
return;
-
+
_loadedVersion = _lastVersion;
string coll = _localDBName + ".system.js";
-
+
static DBClientBase * db = createDirectClient();
- auto_ptr<DBClientCursor> c = db->query( coll , Query() );
+ auto_ptr<DBClientCursor> c = db->query( coll , Query(), 0, 0, NULL, QueryOption_SlaveOk, 0 );
assert( c.get() );
-
+
set<string> thisTime;
-
- while ( c->more() ){
+
+ while ( c->more() ) {
BSONObj o = c->next();
BSONElement n = o["_id"];
BSONElement v = o["value"];
-
+
uassert( 10209 , "name has to be a string" , n.type() == String );
uassert( 10210 , "value has to be set" , v.type() != EOO );
-
+
setElement( n.valuestr() , v );
thisTime.insert( n.valuestr() );
_storedNames.insert( n.valuestr() );
-
+
}
// --- remove things from scope that were removed
list<string> toremove;
- for ( set<string>::iterator i=_storedNames.begin(); i!=_storedNames.end(); i++ ){
+ for ( set<string>::iterator i=_storedNames.begin(); i!=_storedNames.end(); i++ ) {
string n = *i;
if ( thisTime.count( n ) == 0 )
toremove.push_back( n );
}
-
- for ( list<string>::iterator i=toremove.begin(); i!=toremove.end(); i++ ){
+
+ for ( list<string>::iterator i=toremove.begin(); i!=toremove.end(); i++ ) {
string n = *i;
_storedNames.erase( n );
execSetup( (string)"delete " + n , "clean up scope" );
@@ -208,11 +222,11 @@ namespace mongo {
}
- ScriptingFunction Scope::createFunction( const char * code ){
- if ( code[0] == '/' && code [1] == '*' ){
+ ScriptingFunction Scope::createFunction( const char * code ) {
+ if ( code[0] == '/' && code [1] == '*' ) {
code += 2;
- while ( code[0] && code[1] ){
- if ( code[0] == '*' && code[1] == '/' ){
+ while ( code[0] && code[1] ) {
+ if ( code[0] == '*' && code[1] == '/' ) {
code += 2;
break;
}
@@ -226,7 +240,7 @@ namespace mongo {
_cachedFunctions[code] = f;
return f;
}
-
+
typedef map< string , list<Scope*> > PoolToScopes;
class ScopeCache {
@@ -235,21 +249,21 @@ namespace mongo {
ScopeCache() : _mutex("ScopeCache") {
_magic = 17;
}
-
- ~ScopeCache(){
+
+ ~ScopeCache() {
assert( _magic == 17 );
_magic = 1;
if ( inShutdown() )
return;
-
+
clear();
}
- void done( const string& pool , Scope * s ){
+ void done( const string& pool , Scope * s ) {
scoped_lock lk( _mutex );
list<Scope*> & l = _pools[pool];
- if ( l.size() > 10 ){
+ if ( l.size() > 10 ) {
delete s;
}
else {
@@ -257,31 +271,31 @@ namespace mongo {
s->reset();
}
}
-
- Scope * get( const string& pool ){
+
+ Scope * get( const string& pool ) {
scoped_lock lk( _mutex );
list<Scope*> & l = _pools[pool];
if ( l.size() == 0 )
return 0;
-
+
Scope * s = l.back();
l.pop_back();
s->reset();
return s;
}
-
- void clear(){
+
+ void clear() {
set<Scope*> seen;
-
- for ( PoolToScopes::iterator i=_pools.begin() ; i != _pools.end(); i++ ){
- for ( list<Scope*>::iterator j=i->second.begin(); j != i->second.end(); j++ ){
+
+ for ( PoolToScopes::iterator i=_pools.begin() ; i != _pools.end(); i++ ) {
+ for ( list<Scope*>::iterator j=i->second.begin(); j != i->second.end(); j++ ) {
Scope * s = *j;
assert( ! seen.count( s ) );
delete s;
seen.insert( s );
}
}
-
+
_pools.clear();
}
@@ -295,12 +309,12 @@ namespace mongo {
class PooledScope : public Scope {
public:
- PooledScope( const string pool , Scope * real ) : _pool( pool ) , _real( real ){
+ PooledScope( const string pool , Scope * real ) : _pool( pool ) , _real( real ) {
_real->loadStored( true );
};
- virtual ~PooledScope(){
+ virtual ~PooledScope() {
ScopeCache * sc = scopeCache.get();
- if ( sc ){
+ if ( sc ) {
sc->done( _pool , _real );
_real = 0;
}
@@ -312,88 +326,92 @@ namespace mongo {
_real = 0;
}
}
-
- void reset(){
+
+ void reset() {
_real->reset();
}
- void init( BSONObj * data ){
+ void init( const BSONObj * data ) {
_real->init( data );
}
-
- void localConnect( const char * dbName ){
+
+ void localConnect( const char * dbName ) {
_real->localConnect( dbName );
}
- void externalSetup(){
+ void externalSetup() {
_real->externalSetup();
}
-
- double getNumber( const char *field ){
+
+ double getNumber( const char *field ) {
return _real->getNumber( field );
}
- string getString( const char *field ){
+ string getString( const char *field ) {
return _real->getString( field );
}
- bool getBoolean( const char *field ){
+ bool getBoolean( const char *field ) {
return _real->getBoolean( field );
}
- BSONObj getObject( const char *field ){
+ BSONObj getObject( const char *field ) {
return _real->getObject( field );
}
- int type( const char *field ){
+ int type( const char *field ) {
return _real->type( field );
}
- void setElement( const char *field , const BSONElement& val ){
+ void setElement( const char *field , const BSONElement& val ) {
_real->setElement( field , val );
}
- void setNumber( const char *field , double val ){
+ void setNumber( const char *field , double val ) {
_real->setNumber( field , val );
}
- void setString( const char *field , const char * val ){
+ void setString( const char *field , const char * val ) {
_real->setString( field , val );
}
- void setObject( const char *field , const BSONObj& obj , bool readOnly=true ){
+ void setObject( const char *field , const BSONObj& obj , bool readOnly=true ) {
_real->setObject( field , obj , readOnly );
}
- void setBoolean( const char *field , bool val ){
+ void setBoolean( const char *field , bool val ) {
_real->setBoolean( field , val );
}
- void setThis( const BSONObj * obj ){
+ void setThis( const BSONObj * obj ) {
_real->setThis( obj );
}
-
- ScriptingFunction createFunction( const char * code ){
+
+ ScriptingFunction createFunction( const char * code ) {
return _real->createFunction( code );
}
- ScriptingFunction _createFunction( const char * code ){
+ ScriptingFunction _createFunction( const char * code ) {
return _real->createFunction( code );
}
+ void rename( const char * from , const char * to ) {
+ _real->rename( from , to );
+ }
+
/**
* @return 0 on success
*/
- int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs , bool ignoreReturn ){
+ int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs , bool ignoreReturn ) {
return _real->invoke( func , args , timeoutMs , ignoreReturn );
}
- string getError(){
+ string getError() {
return _real->getError();
}
-
- bool exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ){
+
+ bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) {
return _real->exec( code , name , printResult , reportError , assertOnError , timeoutMs );
}
- bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ){
+ bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) {
return _real->execFile( filename , printResult , reportError , assertOnError , timeoutMs );
}
-
- void injectNative( const char *field, NativeFunction func ){
+
+ void injectNative( const char *field, NativeFunction func ) {
_real->injectNative( field , func );
}
-
- void gc(){
+
+ void gc() {
_real->gc();
}
@@ -402,40 +420,57 @@ namespace mongo {
Scope * _real;
};
- auto_ptr<Scope> ScriptEngine::getPooledScope( const string& pool ){
- if ( ! scopeCache.get() ){
+ auto_ptr<Scope> ScriptEngine::getPooledScope( const string& pool ) {
+ if ( ! scopeCache.get() ) {
scopeCache.reset( new ScopeCache() );
}
Scope * s = scopeCache->get( pool );
- if ( ! s ){
+ if ( ! s ) {
s = newScope();
}
-
+
auto_ptr<Scope> p;
p.reset( new PooledScope( pool , s ) );
return p;
}
-
- void ScriptEngine::threadDone(){
+
+ void ScriptEngine::threadDone() {
ScopeCache * sc = scopeCache.get();
- if ( sc ){
+ if ( sc ) {
sc->clear();
}
}
-
+
void ( *ScriptEngine::_connectCallback )( DBClientWithCommands & ) = 0;
-
- ScriptEngine * globalScriptEngine;
+ const char * ( *ScriptEngine::_checkInterruptCallback )() = 0;
+ unsigned ( *ScriptEngine::_getInterruptSpecCallback )() = 0;
+
+ ScriptEngine * globalScriptEngine = 0;
- bool hasJSReturn( const string& code ){
+ bool hasJSReturn( const string& code ) {
size_t x = code.find( "return" );
if ( x == string::npos )
return false;
- return
+ return
( x == 0 || ! isalpha( code[x-1] ) ) &&
! isalpha( code[x+6] );
}
+
+ const char * jsSkipWhiteSpace( const char * raw ) {
+ while ( raw[0] ) {
+ while (isspace(*raw)) {
+ raw++;
+ }
+
+ if ( raw[0] != '/' || raw[1] != '/' )
+ break;
+
+ while ( raw[0] && raw[0] != '\n' )
+ raw++;
+ }
+ return raw;
+ }
}
-
+
diff --git a/scripting/engine.h b/scripting/engine.h
index e097401..62afd77 100644
--- a/scripting/engine.h
+++ b/scripting/engine.h
@@ -20,10 +20,23 @@
#include "../pch.h"
#include "../db/jsobj.h"
-extern const char * jsconcatcode; // TODO: change name to mongoJSCode
-
namespace mongo {
+ struct JSFile {
+ const char* name;
+ 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 );
@@ -31,20 +44,35 @@ namespace mongo {
public:
Scope();
virtual ~Scope();
-
+
virtual void reset() = 0;
- virtual void init( BSONObj * data ) = 0;
- void init( const char * data ){
+ virtual void init( const BSONObj * data ) = 0;
+ void init( const char * data ) {
BSONObj o( data , 0 );
init( &o );
}
-
+
virtual void localConnect( const char * dbName ) = 0;
virtual void externalSetup() = 0;
-
+
+ class NoDBAccess {
+ Scope * _s;
+ public:
+ NoDBAccess( Scope * s ) {
+ _s = s;
+ }
+ ~NoDBAccess() {
+ _s->rename( "____db____" , "db" );
+ }
+ };
+ NoDBAccess disableDBAccess( const char * why ) {
+ rename( "db" , "____db____" );
+ return NoDBAccess( this );
+ }
+
virtual double getNumber( const char *field ) = 0;
- virtual int getNumberInt( const char *field ){ return (int)getNumber( field ); }
- virtual long long getNumberLongLong( const char *field ){ return (long long)getNumber( field ); }
+ virtual int getNumberInt( const char *field ) { return (int)getNumber( field ); }
+ virtual long long getNumberLongLong( const char *field ) { return (long long)getNumber( field ); }
virtual string getString( const char *field ) = 0;
virtual bool getBoolean( const char *field ) = 0;
virtual BSONObj getObject( const char *field ) = 0;
@@ -59,52 +87,68 @@ namespace mongo {
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 ScriptingFunction createFunction( const char * code );
-
+
+ virtual void rename( const char * from , const char * to ) = 0;
/**
* @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 ){
+ void invokeSafe( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 ) {
int res = invoke( func , args , timeoutMs );
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 ){
+ void invokeSafe( const char* code , const BSONObj& args, int timeoutMs = 0 ) {
if ( invoke( code , args , timeoutMs ) == 0 )
return;
throw UserException( 9005 , (string)"invoke failed: " + getError() );
}
- virtual bool exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0;
- virtual void execSetup( const string& code , const string& name = "setup" ){
+ virtual bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0;
+ virtual void execSetup( const StringData& code , const string& name = "setup" ) {
exec( code , name , false , true , true , 0 );
}
+
+ void execSetup( const JSFile& file) {
+ 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);
+ }
+
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 gc() = 0;
void loadStored( bool ignoreNotConnected = false );
-
+
/**
if any changes are made to .system.js, call this
right now its just global - slightly inefficient, but a lot simpler
*/
static void storedFuncMod();
-
- static int getNumScopes(){
+
+ static int getNumScopes() {
return _numScopes;
}
-
+
static void validateObjectIdString( const string &str );
-
+
protected:
virtual ScriptingFunction _createFunction( const char * code ) = 0;
@@ -117,16 +161,16 @@ namespace mongo {
static int _numScopes;
};
-
+
void installGlobalUtils( Scope& scope );
class DBClientWithCommands;
-
+
class ScriptEngine : boost::noncopyable {
public:
ScriptEngine();
virtual ~ScriptEngine();
-
+
virtual Scope * newScope() {
Scope *s = createScope();
if ( s && _scopeInitCallback )
@@ -134,35 +178,63 @@ namespace mongo {
installGlobalUtils( *s );
return s;
}
-
+
virtual void runTest() = 0;
-
+
virtual bool utf8Ok() const = 0;
static void setup();
auto_ptr<Scope> getPooledScope( const string& pool );
void threadDone();
-
+
struct Unlocker { virtual ~Unlocker() {} };
virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< Unlocker >( new Unlocker ); }
-
+
void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInitCallback = func; }
static void setConnectCallback( void ( *func )( DBClientWithCommands& ) ) { _connectCallback = func; }
static void runConnectCallback( DBClientWithCommands &c ) {
if ( _connectCallback )
_connectCallback( c );
}
-
+
+ // engine implementation may either respond to interrupt events or
+ // poll for interrupts
+
+ // the interrupt functions must not wait indefinitely on a lock
+ virtual void interrupt( unsigned opSpec ) {}
+ virtual void interruptAll() {}
+
+ static void setGetInterruptSpecCallback( unsigned ( *func )() ) { _getInterruptSpecCallback = func; }
+ static bool haveGetInterruptSpecCallback() { return _getInterruptSpecCallback; }
+ static unsigned getInterruptSpec() {
+ massert( 13474, "no _getInterruptSpecCallback", _getInterruptSpecCallback );
+ return _getInterruptSpecCallback();
+ }
+
+ static void setCheckInterruptCallback( const char * ( *func )() ) { _checkInterruptCallback = func; }
+ static bool haveCheckInterruptCallback() { return _checkInterruptCallback; }
+ static const char * checkInterrupt() {
+ return _checkInterruptCallback ? _checkInterruptCallback() : "";
+ }
+ static bool interrupted() {
+ const char *r = checkInterrupt();
+ return r && r[ 0 ];
+ }
+
protected:
virtual Scope * createScope() = 0;
-
+
private:
void ( *_scopeInitCallback )( Scope & );
static void ( *_connectCallback )( DBClientWithCommands & );
+ static const char * ( *_checkInterruptCallback )();
+ static unsigned ( *_getInterruptSpecCallback )();
};
bool hasJSReturn( const string& s );
+ const char * jsSkipWhiteSpace( const char * raw );
+
extern ScriptEngine * globalScriptEngine;
}
diff --git a/scripting/engine_java.cpp b/scripting/engine_java.cpp
index dacf532..fc8945f 100644
--- a/scripting/engine_java.cpp
+++ b/scripting/engine_java.cpp
@@ -55,19 +55,19 @@ namespace mongo {
no tss cleanup on windows for boost lib?
we don't care for now esp on windows only
- the boost source says:
-
- This function's sole purpose is to cause a link error in cases where
- automatic tss cleanup is not implemented by Boost.Threads as a
- reminder that user code is responsible for calling the necessary
- functions at the appropriate times (and for implementing an a
- tss_cleanup_implemented() function to eliminate the linker's
- missing symbol error).
-
- If Boost.Threads later implements automatic tss cleanup in cases
- where it currently doesn't (which is the plan), the duplicate
- symbol error will warn the user that their custom solution is no
- longer needed and can be removed.
+ the boost source says:
+
+ This function's sole purpose is to cause a link error in cases where
+ automatic tss cleanup is not implemented by Boost.Threads as a
+ reminder that user code is responsible for calling the necessary
+ functions at the appropriate times (and for implementing an a
+ tss_cleanup_implemented() function to eliminate the linker's
+ missing symbol error).
+
+ If Boost.Threads later implements automatic tss cleanup in cases
+ where it currently doesn't (which is the plan), the duplicate
+ symbol error will warn the user that their custom solution is no
+ longer needed and can be removed.
*/
extern "C" void tss_cleanup_implemented(void) {
//out() << "tss_cleanup_implemented called" << endl;
@@ -185,10 +185,10 @@ namespace mongo {
if ( res ) {
log() << "using classpath: " << q << endl;
log()
- << " res : " << (unsigned) res << " "
- << "_jvm : " << _jvm << " "
- << "_env : " << _mainEnv << " "
- << endl;
+ << " res : " << (unsigned) res << " "
+ << "_jvm : " << _jvm << " "
+ << "_env : " << _mainEnv << " "
+ << endl;
problem() << "Couldn't create JVM res:" << (int) res << " terminating" << endl;
log() << "(try --nojni if you do not require that functionality)" << endl;
exit(22);
@@ -397,12 +397,11 @@ namespace mongo {
return retStr;
}
- BSONObj JavaJSImpl::scopeGetObject( jlong id , const char * field )
- {
+ BSONObj JavaJSImpl::scopeGetObject( jlong id , const char * field ) {
jstring s1 = _getEnv()->NewStringUTF( field );
int guess = _getEnv()->CallStaticIntMethod( _dbhook , _scopeGuessObjectSize , id , _getEnv()->NewStringUTF( field ) );
_getEnv()->DeleteLocalRef( s1 );
-
+
if ( guess == 0 )
return BSONObj();
@@ -471,12 +470,12 @@ namespace mongo {
return env;
}
- Scope * JavaJSImpl::createScope(){
+ Scope * JavaJSImpl::createScope() {
return new JavaScope();
}
- void ScriptEngine::setup(){
- if ( ! JavaJS ){
+ void ScriptEngine::setup() {
+ if ( ! JavaJS ) {
JavaJS = new JavaJSImpl();
globalScriptEngine = JavaJS;
}
@@ -564,40 +563,40 @@ namespace mongo {
if ( ! possible.size() ) {
possible.push_back( "./" );
possible.push_back( "../" );
-
+
log(2) << "dbExecCommand: " << dbExecCommand << endl;
-
+
string dbDir = dbExecCommand;
#ifdef WIN32
- if ( dbDir.find( "\\" ) != string::npos ){
+ if ( dbDir.find( "\\" ) != string::npos ) {
dbDir = dbDir.substr( 0 , dbDir.find_last_of( "\\" ) );
}
else {
dbDir = ".";
}
#else
- if ( dbDir.find( "/" ) != string::npos ){
+ if ( dbDir.find( "/" ) != string::npos ) {
dbDir = dbDir.substr( 0 , dbDir.find_last_of( "/" ) );
}
else {
bool found = false;
-
- if ( getenv( "PATH" ) ){
+
+ if ( getenv( "PATH" ) ) {
string s = getenv( "PATH" );
s += ":";
pcrecpp::StringPiece input( s );
string dir;
pcrecpp::RE re("(.*?):");
- while ( re.Consume( &input, &dir ) ){
+ while ( re.Consume( &input, &dir ) ) {
string test = dir + "/" + dbExecCommand;
- if ( boost::filesystem::exists( test ) ){
- while ( boost::filesystem::symbolic_link_exists( test ) ){
+ if ( boost::filesystem::exists( test ) ) {
+ while ( boost::filesystem::symbolic_link_exists( test ) ) {
char tmp[2048];
int len = readlink( test.c_str() , tmp , 2048 );
tmp[len] = 0;
log(5) << " symlink " << test << " -->> " << tmp << endl;
test = tmp;
-
+
dir = test.substr( 0 , test.rfind( "/" ) );
}
dbDir = dir;
@@ -606,12 +605,12 @@ namespace mongo {
}
}
}
-
+
if ( ! found )
dbDir = ".";
}
#endif
-
+
log(2) << "dbDir [" << dbDir << "]" << endl;
possible.push_back( ( dbDir + "/../lib/mongo/" ));
possible.push_back( ( dbDir + "/../lib64/mongo/" ));
@@ -624,7 +623,7 @@ namespace mongo {
for ( list<string>::iterator i = possible.begin() ; i != possible.end(); i++ ) {
const string temp = *i;
const string jarDir = ((string)temp) + "jars/";
-
+
log(5) << "possible jarDir [" << jarDir << "]" << endl;
path p(jarDir );
@@ -641,7 +640,7 @@ namespace mongo {
};
-
+
// ---
JNIEXPORT void JNICALL java_native_say(JNIEnv * env , jclass, jobject outBuffer ) {
@@ -692,7 +691,7 @@ namespace mongo {
jlong func1 = JavaJS.functionCreate( "foo = 5.6; bar = \"eliot\"; abc = { foo : 517 }; " );
- jassert( ! JavaJS.invoke( scope , func1 ) );
+ jassert( ! JavaJS.invoke( scope , func1 ) );
if ( debug ) out() << "func3 start" << endl;
@@ -757,7 +756,7 @@ namespace mongo {
assert( 12 == JavaJS.scopeGetNumber( scope , "return" ) );
}
-
+
#endif
} // namespace mongo
diff --git a/scripting/engine_java.h b/scripting/engine_java.h
index 5c6bc3b..b8245ba 100644
--- a/scripting/engine_java.h
+++ b/scripting/engine_java.h
@@ -163,10 +163,10 @@ namespace mongo {
JavaJS->scopeInit( s , o );
}
- void localConnect( const char * dbName ){
+ void localConnect( const char * dbName ) {
setString("$client", dbName );
}
-
+
double getNumber(const char *field) {
return JavaJS->scopeGetNumber(s,field);
}
@@ -183,7 +183,7 @@ namespace mongo {
return JavaJS->scopeGetType(s,field);
}
- void setThis( const BSONObj * obj ){
+ void setThis( const BSONObj * obj ) {
JavaJS->scopeSetThis( s , obj );
}
@@ -200,17 +200,17 @@ namespace mongo {
void setBoolean(const char *field, bool val ) {
JavaJS->scopeSetBoolean(s,field,val);
}
-
- ScriptingFunction createFunction( const char * code ){
+
+ ScriptingFunction createFunction( const char * code ) {
return JavaJS->functionCreate( code );
}
- int invoke( ScriptingFunction function , const BSONObj& args ){
+ int invoke( ScriptingFunction function , const BSONObj& args ) {
setObject( "args" , args , true );
return JavaJS->invoke(s,function);
}
-
- string getError(){
+
+ string getError() {
return getString( "error" );
}
diff --git a/scripting/engine_none.cpp b/scripting/engine_none.cpp
index 2320d0e..d13dbec 100644
--- a/scripting/engine_none.cpp
+++ b/scripting/engine_none.cpp
@@ -18,7 +18,7 @@
#include "engine.h"
namespace mongo {
- void ScriptEngine::setup(){
+ void ScriptEngine::setup() {
// noop
}
}
diff --git a/scripting/engine_spidermonkey.cpp b/scripting/engine_spidermonkey.cpp
index c8f2eca..73ebfaa 100644
--- a/scripting/engine_spidermonkey.cpp
+++ b/scripting/engine_spidermonkey.cpp
@@ -26,31 +26,34 @@
#endif
#define smuassert( cx , msg , val ) \
- if ( ! ( val ) ){ \
- JS_ReportError( cx , msg ); \
- return JS_FALSE; \
- }
+ if ( ! ( val ) ){ \
+ JS_ReportError( cx , msg ); \
+ return JS_FALSE; \
+ }
#define CHECKNEWOBJECT(xx,ctx,w) \
if ( ! xx ){ \
massert(13072,(string)"JS_NewObject failed: " + w ,xx); \
}
+#define CHECKJSALLOC( newthing ) \
+ massert( 13615 , "JS allocation failed, either memory leak or using too much memory" , newthing )
+
namespace mongo {
-
+
class InvalidUTF8Exception : public UserException {
public:
- InvalidUTF8Exception() : UserException( 9006 , "invalid utf8" ){
+ InvalidUTF8Exception() : UserException( 9006 , "invalid utf8" ) {
}
};
- string trim( string s ){
+ string trim( string s ) {
while ( s.size() && isspace( s[0] ) )
s = s.substr( 1 );
-
+
while ( s.size() && isspace( s[s.size()-1] ) )
s = s.substr( 0 , s.size() - 1 );
-
+
return s;
}
@@ -65,18 +68,18 @@ namespace mongo {
class BSONHolder {
public:
- BSONHolder( BSONObj obj ){
+ BSONHolder( BSONObj obj ) {
_obj = obj.getOwned();
_inResolve = false;
_modified = false;
_magic = 17;
}
-
- ~BSONHolder(){
+
+ ~BSONHolder() {
_magic = 18;
}
- void check(){
+ void check() {
uassert( 10212 , "holder magic value is wrong" , _magic == 17 && _obj.isValid() );
}
@@ -89,24 +92,24 @@ namespace mongo {
set<string> _removed;
bool _modified;
};
-
+
class BSONFieldIterator {
public:
- BSONFieldIterator( BSONHolder * holder ){
+ BSONFieldIterator( BSONHolder * holder ) {
set<string> added;
BSONObjIterator it( holder->_obj );
- while ( it.more() ){
+ while ( it.more() ) {
BSONElement e = it.next();
if ( holder->_removed.count( e.fieldName() ) )
continue;
_names.push_back( e.fieldName() );
added.insert( e.fieldName() );
}
-
- for ( list<string>::iterator i = holder->_extra.begin(); i != holder->_extra.end(); i++ ){
+
+ for ( list<string>::iterator i = holder->_extra.begin(); i != holder->_extra.end(); i++ ) {
if ( ! added.count( *i ) )
_names.push_back( *i );
}
@@ -114,11 +117,11 @@ namespace mongo {
_it = _names.begin();
}
- bool more(){
+ bool more() {
return _it != _names.end();
}
- string next(){
+ string next() {
string s = *_it;
_it++;
return s;
@@ -129,24 +132,24 @@ namespace mongo {
list<string>::iterator _it;
};
- BSONFieldIterator * BSONHolder::it(){
+ BSONFieldIterator * BSONHolder::it() {
return new BSONFieldIterator( this );
}
class TraverseStack {
public:
- TraverseStack(){
+ TraverseStack() {
_o = 0;
_parent = 0;
}
- TraverseStack( JSObject * o , const TraverseStack * parent ){
+ TraverseStack( JSObject * o , const TraverseStack * parent ) {
_o = o;
_parent = parent;
}
TraverseStack dive( JSObject * o ) const {
- if ( o ){
+ if ( o ) {
uassert( 13076 , (string)"recursive toObject" , ! has( o ) );
}
return TraverseStack( o , this );
@@ -155,7 +158,7 @@ namespace mongo {
int depth() const {
int d = 0;
const TraverseStack * s = _parent;
- while ( s ){
+ while ( s ) {
s = s->_parent;
d++;
}
@@ -165,12 +168,12 @@ namespace mongo {
bool isTop() const {
return _parent == 0;
}
-
+
bool has( JSObject * o ) const {
if ( ! o )
return false;
const TraverseStack * s = this;
- while ( s ){
+ while ( s ) {
if ( s->_o == o )
return true;
s = s->_parent;
@@ -184,11 +187,11 @@ namespace mongo {
class Convertor : boost::noncopyable {
public:
- Convertor( JSContext * cx ){
+ Convertor( JSContext * cx ) {
_context = cx;
}
- string toString( JSString * so ){
+ string toString( JSString * so ) {
jschar * s = JS_GetStringChars( so );
size_t srclen = JS_GetStringLength( so );
if( srclen == 0 )
@@ -202,7 +205,16 @@ namespace mongo {
// units, but experiments suggest 8bit units expected. We allocate
// enough memory that either will work.
- assert( JS_EncodeCharacters( _context , s , srclen , dst , &len) );
+ if ( !JS_EncodeCharacters( _context , s , srclen , dst , &len) ) {
+ StringBuilder temp;
+ temp << "Not proper UTF-16: ";
+ for ( size_t i=0; i<srclen; i++ ) {
+ if ( i > 0 )
+ temp << ",";
+ temp << s[i];
+ }
+ uasserted( 13498 , temp.str() );
+ }
string ss( dst , len );
free( dst );
@@ -212,7 +224,7 @@ namespace mongo {
return ss;
}
- string toString( jsval v ){
+ string toString( jsval v ) {
return toString( JS_ValueToString( _context , v ) );
}
@@ -221,27 +233,28 @@ namespace mongo {
boost::uint64_t val;
if ( hasProperty( o, "top" ) ) {
val =
- ( (boost::uint64_t)(boost::uint32_t)getNumber( o , "top" ) << 32 ) +
- ( boost::uint32_t)( getNumber( o , "bottom" ) );
- } else {
+ ( (boost::uint64_t)(boost::uint32_t)getNumber( o , "top" ) << 32 ) +
+ ( boost::uint32_t)( getNumber( o , "bottom" ) );
+ }
+ else {
val = (boost::uint64_t)(boost::int64_t) getNumber( o, "floatApprox" );
}
return val;
}
-
- double toNumber( jsval v ){
+
+ double toNumber( jsval v ) {
double d;
uassert( 10214 , "not a number" , JS_ValueToNumber( _context , v , &d ) );
return d;
}
- bool toBoolean( jsval v ){
+ bool toBoolean( jsval v ) {
JSBool b;
assert( JS_ValueToBoolean( _context, v , &b ) );
return b;
}
- OID toOID( jsval v ){
+ OID toOID( jsval v ) {
JSContext * cx = _context;
assert( JSVAL_IS_OID( v ) );
@@ -251,21 +264,21 @@ namespace mongo {
return oid;
}
- BSONObj toObject( JSObject * o , const TraverseStack& stack=TraverseStack() ){
+ BSONObj toObject( JSObject * o , const TraverseStack& stack=TraverseStack() ) {
if ( ! o )
return BSONObj();
- if ( JS_InstanceOf( _context , o , &bson_ro_class , 0 ) ){
+ if ( JS_InstanceOf( _context , o , &bson_ro_class , 0 ) ) {
BSONHolder * holder = GETHOLDER( _context , o );
assert( holder );
return holder->_obj.getOwned();
}
BSONObj orig;
- if ( JS_InstanceOf( _context , o , &bson_class , 0 ) ){
+ if ( JS_InstanceOf( _context , o , &bson_class , 0 ) ) {
BSONHolder * holder = GETHOLDER(_context,o);
assert( holder );
- if ( ! holder->_modified ){
+ if ( ! holder->_modified ) {
return holder->_obj;
}
orig = holder->_obj;
@@ -273,26 +286,26 @@ namespace mongo {
BSONObjBuilder b;
- if ( ! appendSpecialDBObject( this , b , "value" , OBJECT_TO_JSVAL( o ) , o ) ){
+ if ( ! appendSpecialDBObject( this , b , "value" , OBJECT_TO_JSVAL( o ) , o ) ) {
- if ( stack.isTop() ){
+ if ( stack.isTop() ) {
jsval theid = getProperty( o , "_id" );
- if ( ! JSVAL_IS_VOID( theid ) ){
+ if ( ! JSVAL_IS_VOID( theid ) ) {
append( b , "_id" , theid , EOO , stack.dive( o ) );
}
}
-
+
JSIdArray * properties = JS_Enumerate( _context , o );
assert( properties );
-
- for ( jsint i=0; i<properties->length; i++ ){
+
+ for ( jsint i=0; i<properties->length; i++ ) {
jsid id = properties->vector[i];
jsval nameval;
assert( JS_IdToValue( _context ,id , &nameval ) );
string name = toString( nameval );
if ( stack.isTop() && name == "_id" )
continue;
-
+
append( b , name , getProperty( o , name.c_str() ) , orig[name].type() , stack.dive( o ) );
}
@@ -302,34 +315,34 @@ namespace mongo {
return b.obj();
}
- BSONObj toObject( jsval v ){
+ BSONObj toObject( jsval v ) {
if ( JSVAL_IS_NULL( v ) ||
- JSVAL_IS_VOID( v ) )
+ JSVAL_IS_VOID( v ) )
return BSONObj();
uassert( 10215 , "not an object" , JSVAL_IS_OBJECT( v ) );
return toObject( JSVAL_TO_OBJECT( v ) );
}
- string getFunctionCode( JSFunction * func ){
+ string getFunctionCode( JSFunction * func ) {
return toString( JS_DecompileFunction( _context , func , 0 ) );
}
- string getFunctionCode( jsval v ){
+ string getFunctionCode( jsval v ) {
uassert( 10216 , "not a function" , JS_TypeOfValue( _context , v ) == JSTYPE_FUNCTION );
return getFunctionCode( JS_ValueToFunction( _context , v ) );
}
-
- void appendRegex( BSONObjBuilder& b , const string& name , string s ){
+
+ void appendRegex( BSONObjBuilder& b , const string& name , string s ) {
assert( s[0] == '/' );
s = s.substr(1);
string::size_type end = s.rfind( '/' );
- b.appendRegex( name , s.substr( 0 , end ).c_str() , s.substr( end + 1 ).c_str() );
+ b.appendRegex( name , s.substr( 0 , end ) , s.substr( end + 1 ) );
}
- void append( BSONObjBuilder& b , string name , jsval val , BSONType oldType = EOO , const TraverseStack& stack=TraverseStack() ){
+ void append( BSONObjBuilder& b , string name , jsval val , BSONType oldType = EOO , const TraverseStack& stack=TraverseStack() ) {
//cout << "name: " << name << "\t" << typeString( val ) << " oldType: " << oldType << endl;
- switch ( JS_TypeOfValue( _context , val ) ){
+ switch ( JS_TypeOfValue( _context , val ) ) {
case JSTYPE_VOID: b.appendUndefined( name ); break;
case JSTYPE_NULL: b.appendNull( name ); break;
@@ -347,12 +360,12 @@ namespace mongo {
case JSTYPE_OBJECT: {
JSObject * o = JSVAL_TO_OBJECT( val );
- if ( ! o || o == JSVAL_NULL ){
+ if ( ! o || o == JSVAL_NULL ) {
b.appendNull( name );
}
- else if ( ! appendSpecialDBObject( this , b , name , val , o ) ){
+ else if ( ! appendSpecialDBObject( this , b , name , val , o ) ) {
BSONObj sub = toObject( o , stack );
- if ( JS_IsArrayObject( _context , o ) ){
+ if ( JS_IsArrayObject( _context , o ) ) {
b.appendArray( name , sub );
}
else {
@@ -364,11 +377,11 @@ namespace mongo {
case JSTYPE_FUNCTION: {
string s = toString(val);
- if ( s[0] == '/' ){
+ if ( s[0] == '/' ) {
appendRegex( b , name , s );
}
else {
- b.appendCode( name , getFunctionCode( val ).c_str() );
+ b.appendCode( name , getFunctionCode( val ) );
}
break;
}
@@ -379,25 +392,28 @@ namespace mongo {
// ---------- to spider monkey ---------
- bool hasFunctionIdentifier( const string& code ){
+ bool hasFunctionIdentifier( const string& code ) {
if ( code.size() < 9 || code.find( "function" ) != 0 )
return false;
return code[8] == ' ' || code[8] == '(';
}
- bool isSimpleStatement( const string& code ){
+ bool isSimpleStatement( const string& code ) {
if ( hasJSReturn( code ) )
return false;
- if ( code.find( ";" ) != string::npos &&
- code.find( ";" ) != code.rfind( ";" ) )
+ if ( code.find( ';' ) != string::npos &&
+ code.find( ';' ) != code.rfind( ';' ) )
+ return false;
+
+ if ( code.find( '\n') != string::npos )
return false;
if ( code.find( "for(" ) != string::npos ||
- code.find( "for (" ) != string::npos ||
- code.find( "while (" ) != string::npos ||
- code.find( "while(" ) != string::npos )
+ code.find( "for (" ) != string::npos ||
+ code.find( "while (" ) != string::npos ||
+ code.find( "while(" ) != string::npos )
return false;
return true;
@@ -405,20 +421,20 @@ namespace mongo {
void addRoot( JSFunction * f , const char * name );
- JSFunction * compileFunction( const char * code, JSObject * assoc = 0 ){
+ JSFunction * compileFunction( const char * code, JSObject * assoc = 0 ) {
const char * gcName = "unknown";
JSFunction * f = _compileFunction( code , assoc , gcName );
//addRoot( f , gcName );
return f;
}
- JSFunction * _compileFunction( const char * raw , JSObject * assoc , const char *& gcName ){
+ JSFunction * _compileFunction( const char * raw , JSObject * assoc , const char *& gcName ) {
if ( ! assoc )
assoc = JS_GetGlobalObject( _context );
- while (isspace(*raw)) {
- raw++;
- }
+ raw = jsSkipWhiteSpace( raw );
+
+ //cout << "RAW\n---\n" << raw << "\n---" << endl;
stringstream fname;
fname << "cf_";
@@ -426,34 +442,34 @@ namespace mongo {
fname << "_" << fnum++ << "_";
- if ( ! hasFunctionIdentifier( raw ) ){
+ if ( ! hasFunctionIdentifier( raw ) ) {
string s = raw;
- if ( isSimpleStatement( s ) ){
+ if ( isSimpleStatement( s ) ) {
s = "return " + s;
}
gcName = "cf anon";
fname << "anon";
- return JS_CompileFunction( _context , assoc , fname.str().c_str() , 0 , 0 , s.c_str() , strlen( s.c_str() ) , "nofile_a" , 0 );
+ return JS_CompileFunction( _context , assoc , fname.str().c_str() , 0 , 0 , s.c_str() , s.size() , "nofile_a" , 0 );
}
string code = raw;
-
+
size_t start = code.find( '(' );
assert( start != string::npos );
-
+
fname << "_f_" << trim( code.substr( 9 , start - 9 ) );
code = code.substr( start + 1 );
size_t end = code.find( ')' );
assert( end != string::npos );
-
+
string paramString = trim( code.substr( 0 , end ) );
code = code.substr( end + 1 );
-
+
vector<string> params;
- while ( paramString.size() ){
+ while ( paramString.size() ) {
size_t c = paramString.find( ',' );
- if ( c == string::npos ){
+ if ( c == string::npos ) {
params.push_back( paramString );
break;
}
@@ -461,14 +477,14 @@ namespace mongo {
paramString = trim( paramString.substr( c + 1 ) );
paramString = trim( paramString );
}
-
+
boost::scoped_array<const char *> paramArray (new const char*[params.size()]);
for ( size_t i=0; i<params.size(); i++ )
paramArray[i] = params[i].c_str();
-
- JSFunction * func = JS_CompileFunction( _context , assoc , fname.str().c_str() , params.size() , paramArray.get() , code.c_str() , strlen( code.c_str() ) , "nofile_b" , 0 );
- if ( ! func ){
+ JSFunction * func = JS_CompileFunction( _context , assoc , fname.str().c_str() , params.size() , paramArray.get() , code.c_str() , code.size() , "nofile_b" , 0 );
+
+ if ( ! func ) {
log() << "compile failed for: " << raw << endl;
return 0;
}
@@ -477,31 +493,31 @@ namespace mongo {
}
- jsval toval( double d ){
+ jsval toval( double d ) {
jsval val;
assert( JS_NewNumberValue( _context, d , &val ) );
return val;
}
- jsval toval( const char * c ){
+ jsval toval( const char * c ) {
JSString * s = JS_NewStringCopyZ( _context , c );
if ( s )
return STRING_TO_JSVAL( s );
-
+
// possibly unicode, try manual
-
+
size_t len = strlen( c );
size_t dstlen = len * 4;
jschar * dst = (jschar*)malloc( dstlen );
-
+
JSBool res = JS_DecodeBytes( _context , c , len , dst, &dstlen );
- if ( res ){
+ if ( res ) {
s = JS_NewUCStringCopyN( _context , dst , dstlen );
}
free( dst );
- if ( ! res ){
+ if ( ! res ) {
tlog() << "decode failed. probably invalid utf-8 string [" << c << "]" << endl;
jsval v;
if ( JS_GetPendingException( _context , &v ) )
@@ -509,13 +525,13 @@ namespace mongo {
throw InvalidUTF8Exception();
}
- assert( s );
+ CHECKJSALLOC( s );
return STRING_TO_JSVAL( s );
}
- JSObject * toJSObject( const BSONObj * obj , bool readOnly=false ){
+ JSObject * toJSObject( const BSONObj * obj , bool readOnly=false ) {
static string ref = "$ref";
- if ( ref == obj->firstElement().fieldName() ){
+ if ( ref == obj->firstElement().fieldName() ) {
JSObject * o = JS_NewObject( _context , &dbref_class , NULL, NULL);
CHECKNEWOBJECT(o,_context,"toJSObject1");
assert( JS_SetPrivate( _context , o , (void*)(new BSONHolder( obj->getOwned() ) ) ) );
@@ -527,7 +543,7 @@ namespace mongo {
return o;
}
- jsval toval( const BSONObj* obj , bool readOnly=false ){
+ jsval toval( const BSONObj* obj , bool readOnly=false ) {
JSObject * o = toJSObject( obj , readOnly );
return OBJECT_TO_JSVAL( o );
}
@@ -535,7 +551,7 @@ 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 ) ) );
+ setProperty( o , "floatApprox" , toval( (double)(boost::int64_t)( val ) ) );
if ( (boost::int64_t)val != (boost::int64_t)(double)(boost::int64_t)( val ) ) {
// using 2 doubles here instead of a single double because certain double
// bit patterns represent undefined values and sm might trash them
@@ -543,16 +559,16 @@ namespace mongo {
setProperty( o , "bottom" , toval( (double)(boost::uint32_t)( val & 0x00000000ffffffff ) ) );
}
}
-
+
jsval toval( long long n ) {
JSObject * o = JS_NewObject( _context , &numberlong_class , 0 , 0 );
makeLongObj( n, o );
return OBJECT_TO_JSVAL( o );
}
-
- jsval toval( const BSONElement& e ){
- switch( e.type() ){
+ jsval toval( const BSONElement& e ) {
+
+ switch( e.type() ) {
case EOO:
case jstNULL:
case Undefined:
@@ -565,50 +581,50 @@ namespace mongo {
return toval( e.valuestr() );
case Bool:
return e.boolean() ? JSVAL_TRUE : JSVAL_FALSE;
- case Object:{
+ case Object: {
BSONObj embed = e.embeddedObject().getOwned();
return toval( &embed );
}
- case Array:{
+ case Array: {
BSONObj embed = e.embeddedObject().getOwned();
- if ( embed.isEmpty() ){
+ if ( embed.isEmpty() ) {
return OBJECT_TO_JSVAL( JS_NewArrayObject( _context , 0 , 0 ) );
}
-
- int n = embed.nFields();
-
- JSObject * array = JS_NewArrayObject( _context , n , 0 );
- assert( array );
+
+ JSObject * array = JS_NewArrayObject( _context , 1 , 0 );
+ CHECKJSALLOC( array );
jsval myarray = OBJECT_TO_JSVAL( array );
- for ( int i=0; i<n; i++ ){
- jsval v = toval( embed[i] );
- assert( JS_SetElement( _context , array , i , &v ) );
+ BSONObjIterator i( embed );
+ while ( i.more() ){
+ const BSONElement& e = i.next();
+ jsval v = toval( e );
+ assert( JS_SetElement( _context , array , atoi(e.fieldName()) , &v ) );
}
return myarray;
}
- case jstOID:{
+ case jstOID: {
OID oid = e.__oid();
JSObject * o = JS_NewObject( _context , &object_id_class , 0 , 0 );
CHECKNEWOBJECT(o,_context,"jstOID");
setProperty( o , "str" , toval( oid.str().c_str() ) );
return OBJECT_TO_JSVAL( o );
}
- case RegEx:{
+ case RegEx: {
const char * flags = e.regexFlags();
uintN flagNumber = 0;
- while ( *flags ){
- switch ( *flags ){
+ while ( *flags ) {
+ switch ( *flags ) {
case 'g': flagNumber |= JSREG_GLOB; break;
case 'i': flagNumber |= JSREG_FOLD; break;
case 'm': flagNumber |= JSREG_MULTILINE; break;
//case 'y': flagNumber |= JSREG_STICKY; break;
-
- default:
+
+ default:
log() << "warning: unknown regex flag:" << *flags << endl;
}
flags++;
@@ -618,17 +634,17 @@ namespace mongo {
assert( r );
return OBJECT_TO_JSVAL( r );
}
- case Code:{
+ case Code: {
JSFunction * func = compileFunction( e.valuestr() );
if ( func )
return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
return JSVAL_NULL;
}
- case CodeWScope:{
+ case CodeWScope: {
JSFunction * func = compileFunction( e.codeWScopeCode() );
BSONObj extraScope = e.codeWScopeObject();
- if ( ! extraScope.isEmpty() ){
+ if ( ! extraScope.isEmpty() ) {
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
}
@@ -665,7 +681,7 @@ namespace mongo {
setProperty( o , "id" , OBJECT_TO_JSVAL( oid ) );
return OBJECT_TO_JSVAL( o );
}
- case BinData:{
+ case BinData: {
JSObject * o = JS_NewObject( _context , &bindata_class , 0 , 0 );
CHECKNEWOBJECT(o,_context,"Bindata_BinData1");
int len;
@@ -686,55 +702,55 @@ namespace mongo {
// ------- object helpers ------
- JSObject * getJSObject( JSObject * o , const char * name ){
+ JSObject * getJSObject( JSObject * o , const char * name ) {
jsval v;
assert( JS_GetProperty( _context , o , name , &v ) );
return JSVAL_TO_OBJECT( v );
}
- JSObject * getGlobalObject( const char * name ){
+ JSObject * getGlobalObject( const char * name ) {
return getJSObject( JS_GetGlobalObject( _context ) , name );
}
- JSObject * getGlobalPrototype( const char * name ){
+ JSObject * getGlobalPrototype( const char * name ) {
return getJSObject( getGlobalObject( name ) , "prototype" );
}
- bool hasProperty( JSObject * o , const char * name ){
+ bool hasProperty( JSObject * o , const char * name ) {
JSBool res;
assert( JS_HasProperty( _context , o , name , & res ) );
return res;
}
- jsval getProperty( JSObject * o , const char * field ){
+ jsval getProperty( JSObject * o , const char * field ) {
uassert( 10219 , "object passed to getPropery is null" , o );
jsval v;
assert( JS_GetProperty( _context , o , field , &v ) );
return v;
}
- void setProperty( JSObject * o , const char * field , jsval v ){
+ void setProperty( JSObject * o , const char * field , jsval v ) {
assert( JS_SetProperty( _context , o , field , &v ) );
}
- string typeString( jsval v ){
+ string typeString( jsval v ) {
JSType t = JS_TypeOfValue( _context , v );
return JS_GetTypeName( _context , t );
}
- bool getBoolean( JSObject * o , const char * field ){
+ bool getBoolean( JSObject * o , const char * field ) {
return toBoolean( getProperty( o , field ) );
}
- double getNumber( JSObject * o , const char * field ){
+ double getNumber( JSObject * o , const char * field ) {
return toNumber( getProperty( o , field ) );
}
- string getString( JSObject * o , const char * field ){
+ string getString( JSObject * o , const char * field ) {
return toString( getProperty( o , field ) );
}
- JSClass * getClass( JSObject * o , const char * field ){
+ JSClass * getClass( JSObject * o , const char * field ) {
jsval v;
assert( JS_GetProperty( _context , o , field , &v ) );
if ( ! JSVAL_IS_OBJECT( v ) )
@@ -748,25 +764,25 @@ namespace mongo {
};
- void bson_finalize( JSContext * cx , JSObject * obj ){
+ void bson_finalize( JSContext * cx , JSObject * obj ) {
BSONHolder * o = GETHOLDER( cx , obj );
- if ( o ){
+ if ( o ) {
delete o;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
}
- JSBool bson_enumerate( JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp ){
+ JSBool bson_enumerate( JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp ) {
BSONHolder * o = GETHOLDER( cx , obj );
-
- if ( enum_op == JSENUMERATE_INIT ){
- if ( o ){
+
+ if ( enum_op == JSENUMERATE_INIT ) {
+ if ( o ) {
BSONFieldIterator * it = o->it();
*statep = PRIVATE_TO_JSVAL( it );
}
else {
- *statep = 0;
+ *statep = 0;
}
if ( idp )
*idp = JSVAL_ZERO;
@@ -774,13 +790,13 @@ namespace mongo {
}
BSONFieldIterator * it = (BSONFieldIterator*)JSVAL_TO_PRIVATE( *statep );
- if ( ! it ){
+ if ( ! it ) {
*statep = 0;
return JS_TRUE;
}
- if ( enum_op == JSENUMERATE_NEXT ){
- if ( it->more() ){
+ if ( enum_op == JSENUMERATE_NEXT ) {
+ if ( it->more() ) {
string name = it->next();
Convertor c(cx);
assert( JS_ValueToId( cx , c.toval( name.c_str() ) , idp ) );
@@ -792,7 +808,7 @@ namespace mongo {
return JS_TRUE;
}
- if ( enum_op == JSENUMERATE_DESTROY ){
+ if ( enum_op == JSENUMERATE_DESTROY ) {
if ( it )
delete it;
return JS_TRUE;
@@ -802,9 +818,9 @@ namespace mongo {
return JS_FALSE;
}
- JSBool noaccess( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
+ JSBool noaccess( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
BSONHolder * holder = GETHOLDER( cx , obj );
- if ( ! holder ){
+ if ( ! holder ) {
// in init code still
return JS_TRUE;
}
@@ -821,7 +837,7 @@ namespace mongo {
JSCLASS_NO_OPTIONAL_MEMBERS
};
- JSBool bson_cons( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool bson_cons( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
cerr << "bson_cons : shouldn't be here!" << endl;
JS_ReportError( cx , "can't construct bson object" );
return JS_FALSE;
@@ -830,26 +846,26 @@ namespace mongo {
JSFunctionSpec bson_functions[] = {
{ 0 }
};
-
- JSBool bson_add_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
+
+ JSBool bson_add_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
BSONHolder * holder = GETHOLDER( cx , obj );
- if ( ! holder ){
+ if ( ! holder ) {
// static init
return JS_TRUE;
}
- if ( ! holder->_inResolve ){
+ if ( ! holder->_inResolve ) {
Convertor c(cx);
string name = c.toString( idval );
- if ( holder->_obj[name].eoo() ){
+ if ( holder->_obj[name].eoo() ) {
holder->_extra.push_back( name );
}
holder->_modified = true;
}
return JS_TRUE;
}
-
- JSBool mark_modified( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
+
+ JSBool mark_modified( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
Convertor c(cx);
BSONHolder * holder = GETHOLDER( cx , obj );
if ( !holder ) // needed when we're messing with DBRef.prototype
@@ -860,8 +876,8 @@ namespace mongo {
holder->_removed.erase( c.toString( idval ) );
return JS_TRUE;
}
-
- JSBool mark_modified_remove( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
+
+ JSBool mark_modified_remove( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
Convertor c(cx);
BSONHolder * holder = GETHOLDER( cx , obj );
if ( holder->_inResolve )
@@ -887,10 +903,10 @@ namespace mongo {
// --- global helpers ---
- JSBool native_print( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ){
+ JSBool native_print( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) {
stringstream ss;
Convertor c( cx );
- for ( uintN i=0; i<argc; i++ ){
+ for ( uintN i=0; i<argc; i++ ) {
if ( i > 0 )
ss << " ";
ss << c.toString( argv[i] );
@@ -900,32 +916,32 @@ namespace mongo {
return JS_TRUE;
}
- JSBool native_helper( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
+ JSBool native_helper( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ) {
Convertor c(cx);
-
+
NativeFunction func = (NativeFunction)((long long)c.getNumber( obj , "x" ) );
assert( func );
-
+
BSONObj a;
- if ( argc > 0 ){
+ if ( argc > 0 ) {
BSONObjBuilder args;
- for ( uintN i=0; i<argc; i++ ){
+ for ( uintN i=0; i<argc; i++ ) {
c.append( args , args.numStr( i ) , argv[i] );
}
-
+
a = args.obj();
}
-
+
BSONObj out;
try {
out = func( a );
}
- catch ( std::exception& e ){
+ catch ( std::exception& e ) {
JS_ReportError( cx , e.what() );
return JS_FALSE;
}
-
- if ( out.isEmpty() ){
+
+ if ( out.isEmpty() ) {
*rval = JSVAL_VOID;
}
else {
@@ -937,7 +953,7 @@ namespace mongo {
JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval );
- JSBool native_gc( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
+ JSBool native_gc( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ) {
JS_GC( cx );
return JS_TRUE;
}
@@ -953,22 +969,28 @@ namespace mongo {
// ----END global helpers ----
// Object helpers
-
- JSBool bson_get_size(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+
+ JSBool bson_get_size(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
if ( argc != 1 || !JSVAL_IS_OBJECT( argv[ 0 ] ) ) {
JS_ReportError( cx , "bsonsize requires one valid object" );
return JS_FALSE;
}
-
- JSObject * o = JSVAL_TO_OBJECT( argv[0] );
Convertor c(cx);
+
+ if ( argv[0] == JSVAL_VOID || argv[0] == JSVAL_NULL ) {
+ *rval = c.toval( 0.0 );
+ return JS_TRUE;
+ }
+
+ JSObject * o = JSVAL_TO_OBJECT( argv[0] );
+
double size = 0;
if ( JS_InstanceOf( cx , o , &bson_ro_class , 0 ) ||
- JS_InstanceOf( cx , o , &bson_class , 0 ) ){
+ JS_InstanceOf( cx , o , &bson_class , 0 ) ) {
BSONHolder * h = GETHOLDER( cx , o );
- if ( h ){
+ if ( h ) {
size = h->_obj.objsize();
}
}
@@ -976,36 +998,36 @@ namespace mongo {
BSONObj temp = c.toObject( o );
size = temp.objsize();
}
-
+
*rval = c.toval( size );
- return JS_TRUE;
+ return JS_TRUE;
}
-
+
JSFunctionSpec objectHelpers[] = {
- { "bsonsize" , &bson_get_size , 1 , 0 , 0 } ,
- { 0 , 0 , 0 , 0 , 0 }
+ { "bsonsize" , &bson_get_size , 1 , 0 , 0 } ,
+ { 0 , 0 , 0 , 0 , 0 }
};
-
+
// end Object helpers
- JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
+ JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
assert( JS_EnterLocalRootScope( cx ) );
Convertor c( cx );
BSONHolder * holder = GETHOLDER( cx , obj );
- if ( ! holder ){
+ if ( ! holder ) {
// static init
*objp = 0;
JS_LeaveLocalRootScope( cx );
return JS_TRUE;
}
holder->check();
-
+
string s = c.toString( id );
BSONElement e = holder->_obj[ s.c_str() ];
-
- if ( e.type() == EOO || holder->_removed.count( s ) ){
+
+ if ( e.type() == EOO || holder->_removed.count( s ) ) {
*objp = 0;
JS_LeaveLocalRootScope( cx );
return JS_TRUE;
@@ -1025,12 +1047,12 @@ namespace mongo {
holder->_inResolve = true;
assert( JS_SetProperty( cx , obj , s.c_str() , &val ) );
holder->_inResolve = false;
-
- if ( val != JSVAL_NULL && val != JSVAL_VOID && JSVAL_IS_OBJECT( val ) ){
+
+ if ( val != JSVAL_NULL && val != JSVAL_VOID && JSVAL_IS_OBJECT( val ) ) {
// TODO: this is a hack to get around sub objects being modified
JSObject * oo = JSVAL_TO_OBJECT( val );
- if ( JS_InstanceOf( cx , oo , &bson_class , 0 ) ||
- JS_IsArrayObject( cx , oo ) ){
+ if ( JS_InstanceOf( cx , oo , &bson_class , 0 ) ||
+ JS_IsArrayObject( cx , oo ) ) {
holder->_modified = true;
}
}
@@ -1046,15 +1068,15 @@ namespace mongo {
class SMEngine : public ScriptEngine {
public:
- SMEngine(){
+ SMEngine() {
#ifdef SM18
JS_SetCStringsAreUTF8();
#endif
_runtime = JS_NewRuntime(8L * 1024L * 1024L);
uassert( 10221 , "JS_NewRuntime failed" , _runtime );
-
- if ( ! utf8Ok() ){
+
+ if ( ! utf8Ok() ) {
log() << "*** warning: spider monkey build without utf8 support. consider rebuilding with utf8 support" << endl;
}
@@ -1063,7 +1085,7 @@ namespace mongo {
uassert( 10222 , "assert not being executed" , x == 1 );
}
- ~SMEngine(){
+ ~SMEngine() {
JS_DestroyRuntime( _runtime );
JS_ShutDown();
}
@@ -1088,7 +1110,7 @@ namespace mongo {
SMEngine * globalSMEngine;
- void ScriptEngine::setup(){
+ void ScriptEngine::setup() {
globalSMEngine = new SMEngine();
globalScriptEngine = globalSMEngine;
}
@@ -1097,11 +1119,11 @@ namespace mongo {
// ------ scope ------
- JSBool no_gc(JSContext *cx, JSGCStatus status){
+ JSBool no_gc(JSContext *cx, JSGCStatus status) {
return JS_FALSE;
}
- JSBool yes_gc(JSContext *cx, JSGCStatus status){
+ JSBool yes_gc(JSContext *cx, JSGCStatus status) {
return JS_TRUE;
}
@@ -1125,64 +1147,65 @@ namespace mongo {
JS_SetOptions( _context , JS_GetOptions( _context ) | JSOPTION_VAROBJFIX );
JS_DefineFunctions( _context , _global , globalHelpers );
-
+
JS_DefineFunctions( _context , _convertor->getGlobalObject( "Object" ), objectHelpers );
//JS_SetGCCallback( _context , no_gc ); // this is useful for seeing if something is a gc problem
_postCreateHacks();
}
-
- ~SMScope(){
+
+ ~SMScope() {
smlock;
uassert( 10223 , "deleted SMScope twice?" , _convertor );
- for ( list<void*>::iterator i=_roots.begin(); i != _roots.end(); i++ ){
+ for ( list<void*>::iterator i=_roots.begin(); i != _roots.end(); i++ ) {
JS_RemoveRoot( _context , *i );
}
_roots.clear();
-
- if ( _this ){
+
+ if ( _this ) {
JS_RemoveRoot( _context , &_this );
_this = 0;
}
- if ( _convertor ){
+ if ( _convertor ) {
delete _convertor;
_convertor = 0;
}
-
- if ( _context ){
+
+ if ( _context ) {
+ // This is expected to reclaim _global as well.
JS_DestroyContext( _context );
_context = 0;
}
}
-
- void reset(){
+
+ void reset() {
smlock;
assert( _convertor );
return;
- if ( _this ){
+ if ( _this ) {
JS_RemoveRoot( _context , &_this );
_this = 0;
}
currentScope.reset( this );
_error = "";
}
-
- void addRoot( void * root , const char * name ){
+
+ void addRoot( void * root , const char * name ) {
JS_AddNamedRoot( _context , root , name );
_roots.push_back( root );
}
- void init( BSONObj * data ){
+ void init( const BSONObj * data ) {
smlock;
if ( ! data )
return;
BSONObjIterator i( *data );
- while ( i.more() ){
+ while ( i.more() ) {
BSONElement e = i.next();
_convertor->setProperty( _global , e.fieldName() , _convertor->toval( e ) );
_initFieldNames.insert( e.fieldName() );
@@ -1190,7 +1213,7 @@ namespace mongo {
}
- void externalSetup(){
+ void externalSetup() {
smlock;
uassert( 10224 , "already local connected" , ! _localConnect );
if ( _externalSetup )
@@ -1199,20 +1222,20 @@ namespace mongo {
_externalSetup = true;
}
- void localConnect( const char * dbName ){
+ void localConnect( const char * dbName ) {
{
smlock;
uassert( 10225 , "already setup for external db" , ! _externalSetup );
- if ( _localConnect ){
+ if ( _localConnect ) {
uassert( 10226 , "connected to different db" , _localDBName == dbName );
return;
}
-
+
initMongoJS( this , _context , _global , true );
-
+
exec( "_mongo = new Mongo();" );
exec( ((string)"db = _mongo.getDB( \"" + dbName + "\" ); ").c_str() );
-
+
_localConnect = true;
_localDBName = dbName;
}
@@ -1220,14 +1243,14 @@ namespace mongo {
}
// ----- getters ------
- double getNumber( const char *field ){
+ double getNumber( const char *field ) {
smlock;
jsval val;
assert( JS_GetProperty( _context , _global , field , &val ) );
return _convertor->toNumber( val );
}
- string getString( const char *field ){
+ string getString( const char *field ) {
smlock;
jsval val;
assert( JS_GetProperty( _context , _global , field , &val ) );
@@ -1235,27 +1258,27 @@ namespace mongo {
return _convertor->toString( s );
}
- bool getBoolean( const char *field ){
+ bool getBoolean( const char *field ) {
smlock;
return _convertor->getBoolean( _global , field );
}
- BSONObj getObject( const char *field ){
+ BSONObj getObject( const char *field ) {
smlock;
return _convertor->toObject( _convertor->getProperty( _global , field ) );
}
- JSObject * getJSObject( const char * field ){
+ JSObject * getJSObject( const char * field ) {
smlock;
return _convertor->getJSObject( _global , field );
}
- int type( const char *field ){
+ int type( const char *field ) {
smlock;
jsval val;
assert( JS_GetProperty( _context , _global , field , &val ) );
- switch ( JS_TypeOfValue( _context , val ) ){
+ switch ( JS_TypeOfValue( _context , val ) ) {
case JSTYPE_VOID: return Undefined;
case JSTYPE_NULL: return jstNULL;
case JSTYPE_OBJECT: {
@@ -1280,52 +1303,61 @@ namespace mongo {
// ----- setters ------
- void setElement( const char *field , const BSONElement& val ){
+ void setElement( const char *field , const BSONElement& val ) {
smlock;
jsval v = _convertor->toval( val );
assert( JS_SetProperty( _context , _global , field , &v ) );
}
- void setNumber( const char *field , double val ){
+ void setNumber( const char *field , double val ) {
smlock;
jsval v = _convertor->toval( val );
assert( JS_SetProperty( _context , _global , field , &v ) );
}
- void setString( const char *field , const char * val ){
+ void setString( const char *field , const char * val ) {
smlock;
jsval v = _convertor->toval( val );
assert( JS_SetProperty( _context , _global , field , &v ) );
}
- void setObject( const char *field , const BSONObj& obj , bool readOnly ){
+ void setObject( const char *field , const BSONObj& obj , bool readOnly ) {
smlock;
jsval v = _convertor->toval( &obj , readOnly );
JS_SetProperty( _context , _global , field , &v );
}
- void setBoolean( const char *field , bool val ){
+ void setBoolean( const char *field , bool val ) {
smlock;
jsval v = BOOLEAN_TO_JSVAL( val );
assert( JS_SetProperty( _context , _global , field , &v ) );
}
- void setThis( const BSONObj * obj ){
+ void setThis( const BSONObj * obj ) {
smlock;
- if ( _this ){
+ if ( _this ) {
JS_RemoveRoot( _context , &_this );
_this = 0;
}
-
- if ( obj ){
+
+ if ( obj ) {
_this = _convertor->toJSObject( obj );
JS_AddNamedRoot( _context , &_this , "scope this" );
}
}
+ void rename( const char * from , const char * to ) {
+ smlock;
+ jsval v;
+ assert( JS_GetProperty( _context , _global , from , &v ) );
+ assert( JS_SetProperty( _context , _global , to , &v ) );
+ v = JSVAL_VOID;
+ assert( JS_SetProperty( _context , _global , from , &v ) );
+ }
+
// ---- functions -----
- ScriptingFunction _createFunction( const char * code ){
+ ScriptingFunction _createFunction( const char * code ) {
smlock;
precall();
return (ScriptingFunction)_convertor->compileFunction( code );
@@ -1337,40 +1369,49 @@ namespace mongo {
int count;
};
- static JSBool _checkTimeout( JSContext *cx ){
+ // should not generate exceptions, as those can be caught in
+ // javascript code; returning false without an exception exits
+ // immediately
+ static JSBool _interrupt( JSContext *cx ) {
TimeoutSpec &spec = *(TimeoutSpec *)( JS_GetContextPrivate( cx ) );
if ( ++spec.count % 1000 != 0 )
return JS_TRUE;
+ const char * interrupt = ScriptEngine::checkInterrupt();
+ if ( interrupt && interrupt[ 0 ] ) {
+ return JS_FALSE;
+ }
+ if ( spec.timeout.ticks() == 0 ) {
+ return JS_TRUE;
+ }
boost::posix_time::time_duration elapsed = ( boost::posix_time::microsec_clock::local_time() - spec.start );
if ( elapsed < spec.timeout ) {
return JS_TRUE;
}
- JS_ReportError( cx, "Timeout exceeded" );
return JS_FALSE;
}
- static JSBool checkTimeout( JSContext *cx, JSScript *script ){
- return _checkTimeout( cx );
- }
+ static JSBool interrupt( JSContext *cx, JSScript *script ) {
+ return _interrupt( cx );
+ }
- void installCheckTimeout( int timeoutMs ) {
- if ( timeoutMs > 0 ) {
+ void installInterrupt( int timeoutMs ) {
+ if ( timeoutMs != 0 || ScriptEngine::haveCheckInterruptCallback() ) {
TimeoutSpec *spec = new TimeoutSpec;
spec->timeout = boost::posix_time::millisec( timeoutMs );
spec->start = boost::posix_time::microsec_clock::local_time();
spec->count = 0;
JS_SetContextPrivate( _context, (void*)spec );
#if defined(SM181) && !defined(XULRUNNER190)
- JS_SetOperationCallback( _context, _checkTimeout );
+ JS_SetOperationCallback( _context, _interrupt );
#else
- JS_SetBranchCallback( _context, checkTimeout );
+ JS_SetBranchCallback( _context, interrupt );
#endif
}
}
- void uninstallCheckTimeout( int timeoutMs ) {
- if ( timeoutMs > 0 ) {
+ void uninstallInterrupt( int timeoutMs ) {
+ if ( timeoutMs != 0 || ScriptEngine::haveCheckInterruptCallback() ) {
#if defined(SM181) && !defined(XULRUNNER190)
JS_SetOperationCallback( _context , 0 );
#else
@@ -1381,34 +1422,33 @@ namespace mongo {
}
}
- void precall(){
+ void precall() {
_error = "";
currentScope.reset( this );
}
- bool exec( const string& code , const string& name = "(anon)" , bool printResult = false , bool reportError = true , bool assertOnError = true, int timeoutMs = 0 ){
+ bool exec( const StringData& code , const string& name = "(anon)" , bool printResult = false , bool reportError = true , bool assertOnError = true, int timeoutMs = 0 ) {
smlock;
precall();
jsval ret = JSVAL_VOID;
- installCheckTimeout( timeoutMs );
- JSBool worked = JS_EvaluateScript( _context , _global , code.c_str() , strlen( code.c_str() ) , name.c_str() , 0 , &ret );
- uninstallCheckTimeout( timeoutMs );
+ installInterrupt( timeoutMs );
+ JSBool worked = JS_EvaluateScript( _context , _global , code.data() , code.size() , name.c_str() , 1 , &ret );
+ uninstallInterrupt( timeoutMs );
- if ( ! worked && _error.size() == 0 ){
+ if ( ! worked && _error.size() == 0 ) {
jsval v;
- if ( JS_GetPendingException( _context , &v ) ){
+ if ( JS_GetPendingException( _context , &v ) ) {
_error = _convertor->toString( v );
if ( reportError )
cout << _error << endl;
}
}
- if ( assertOnError )
- uassert( 10228 , name + " exec failed" , worked );
+ uassert( 10228 , str::stream() << name + " exec failed: " << _error , worked || ! assertOnError );
- if ( reportError && ! _error.empty() ){
+ if ( reportError && ! _error.empty() ) {
// cout << "exec error: " << _error << endl;
// already printed in reportError, so... TODO
}
@@ -1421,23 +1461,23 @@ namespace mongo {
return worked;
}
-
- int invoke( JSFunction * func , const BSONObj& args, int timeoutMs , bool ignoreReturn ){
+
+ int invoke( JSFunction * func , const BSONObj& args, int timeoutMs , bool ignoreReturn ) {
smlock;
precall();
assert( JS_EnterLocalRootScope( _context ) );
-
+
int nargs = args.nFields();
scoped_array<jsval> smargsPtr( new jsval[nargs] );
- if ( nargs ){
+ if ( nargs ) {
BSONObjIterator it( args );
- for ( int i=0; i<nargs; i++ ){
+ for ( int i=0; i<nargs; i++ ) {
smargsPtr[i] = _convertor->toval( it.next() );
}
}
- if ( args.isEmpty() ){
+ if ( args.isEmpty() ) {
_convertor->setProperty( _global , "args" , JSVAL_NULL );
}
else {
@@ -1446,35 +1486,35 @@ namespace mongo {
JS_LeaveLocalRootScope( _context );
- installCheckTimeout( timeoutMs );
+ installInterrupt( timeoutMs );
jsval rval;
JSBool ret = JS_CallFunction( _context , _this ? _this : _global , func , nargs , smargsPtr.get() , &rval );
- uninstallCheckTimeout( timeoutMs );
+ uninstallInterrupt( timeoutMs );
if ( !ret ) {
return -3;
}
-
- if ( ! ignoreReturn ){
+
+ if ( ! ignoreReturn ) {
assert( JS_SetProperty( _context , _global , "return" , &rval ) );
}
return 0;
}
- int invoke( ScriptingFunction funcAddr , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = 0 ){
+ int invoke( ScriptingFunction funcAddr , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = 0 ) {
return invoke( (JSFunction*)funcAddr , args , timeoutMs , ignoreReturn );
}
- void gotError( string s ){
+ void gotError( string s ) {
_error = s;
}
- string getError(){
+ string getError() {
return _error;
}
- void injectNative( const char *field, NativeFunction func ){
+ void injectNative( const char *field, NativeFunction func ) {
smlock;
string name = field;
_convertor->setProperty( _global , (name + "_").c_str() , _convertor->toval( (double)(long long)func ) );
@@ -1482,19 +1522,19 @@ namespace mongo {
stringstream code;
code << field << "_" << " = { x : " << field << "_ }; ";
code << field << " = function(){ return nativeHelper.apply( " << field << "_ , arguments ); }";
- exec( code.str().c_str() );
+ exec( code.str() );
}
- virtual void gc(){
+ virtual void gc() {
smlock;
JS_GC( _context );
}
JSContext *SavedContext() const { return _context; }
-
+
private:
- void _postCreateHacks(){
+ void _postCreateHacks() {
#ifdef XULRUNNER
exec( "__x__ = new Date(1);" );
globalSMEngine->_dateClass = _convertor->getClass( _global , "__x__" );
@@ -1502,7 +1542,7 @@ namespace mongo {
globalSMEngine->_regexClass = _convertor->getClass( _global , "__x__" );
#endif
}
-
+
JSContext * _context;
Convertor * _convertor;
@@ -1514,41 +1554,41 @@ namespace mongo {
bool _externalSetup;
bool _localConnect;
-
+
set<string> _initFieldNames;
-
+
};
/* used to make the logging not overly chatty in the mongo shell. */
extern bool isShell;
- void errorReporter( JSContext *cx, const char *message, JSErrorReport *report ){
+ void errorReporter( JSContext *cx, const char *message, JSErrorReport *report ) {
stringstream ss;
- if( !isShell )
+ if( !isShell )
ss << "JS Error: ";
ss << message;
- if ( report && report->filename ){
+ if ( report && report->filename ) {
ss << " " << report->filename << ":" << report->lineno;
}
tlog() << ss.str() << endl;
- if ( currentScope.get() ){
+ if ( currentScope.get() ) {
currentScope->gotError( ss.str() );
}
}
- JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
+ JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ) {
Convertor c(cx);
Scope * s = currentScope.get();
- for ( uintN i=0; i<argc; i++ ){
+ for ( uintN i=0; i<argc; i++ ) {
string filename = c.toString( argv[i] );
//cout << "load [" << filename << "]" << endl;
- if ( ! s->execFile( filename , false , true , false ) ){
+ if ( ! s->execFile( filename , false , true , false ) ) {
JS_ReportError( cx , ((string)"error loading js file: " + filename ).c_str() );
return JS_FALSE;
}
@@ -1559,7 +1599,7 @@ namespace mongo {
- void SMEngine::runTest(){
+ void SMEngine::runTest() {
SMScope s;
s.localConnect( "foo" );
@@ -1589,17 +1629,17 @@ namespace mongo {
}
- Scope * SMEngine::createScope(){
+ Scope * SMEngine::createScope() {
return new SMScope();
}
- void Convertor::addRoot( JSFunction * f , const char * name ){
+ void Convertor::addRoot( JSFunction * f , const char * name ) {
if ( ! f )
return;
SMScope * scope = currentScope.get();
uassert( 10229 , "need a scope" , scope );
-
+
JSObject * o = JS_GetFunctionObject( f );
assert( o );
scope->addRoot( &o , name );
diff --git a/scripting/engine_spidermonkey.h b/scripting/engine_spidermonkey.h
index 4617b5d..3ee7495 100644
--- a/scripting/engine_spidermonkey.h
+++ b/scripting/engine_spidermonkey.h
@@ -37,7 +37,7 @@
#include "jstypes.h"
#undef JS_PUBLIC_API
#undef JS_PUBLIC_DATA
-#define JS_PUBLIC_API(t) t __cdecl
+#define JS_PUBLIC_API(t) t __cdecl
#define JS_PUBLIC_DATA(t) t
#endif
@@ -64,7 +64,7 @@
#define JSCLASS_GLOBAL_FLAGS 0
-JSBool JS_CStringsAreUTF8(){
+JSBool JS_CStringsAreUTF8() {
return false;
}
@@ -85,7 +85,7 @@ namespace mongo {
class SMScope;
class Convertor;
-
+
extern JSClass bson_class;
extern JSClass bson_ro_class;
@@ -99,10 +99,10 @@ namespace mongo {
extern JSClass maxkey_class;
// internal things
- void dontDeleteScope( SMScope * s ){}
+ void dontDeleteScope( SMScope * s ) {}
void errorReporter( JSContext *cx, const char *message, JSErrorReport *report );
extern boost::thread_specific_ptr<SMScope> currentScope;
-
+
// bson
JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp );
@@ -112,14 +112,14 @@ namespace mongo {
bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , jsval val , JSObject * o );
#define JSVAL_IS_OID(v) ( JSVAL_IS_OBJECT( v ) && JS_InstanceOf( cx , JSVAL_TO_OBJECT( v ) , &object_id_class , 0 ) )
-
+
bool isDate( JSContext * cx , JSObject * o );
// JS private data must be 2byte aligned, so we use a holder to refer to an unaligned pointer.
struct BinDataHolder {
BinDataHolder( const char *c, int copyLen = -1 ) :
- c_( const_cast< char * >( c ) ),
- iFree_( copyLen != -1 ) {
+ c_( const_cast< char * >( c ) ),
+ iFree_( copyLen != -1 ) {
if ( copyLen != -1 ) {
c_ = (char*)malloc( copyLen );
memcpy( c_, c, copyLen );
diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp
index 08826b1..cd186b4 100644
--- a/scripting/engine_v8.cpp
+++ b/scripting/engine_v8.cpp
@@ -1,4 +1,4 @@
-//engine_v8.cpp
+//engine_v8.cpp
/* Copyright 2009 10gen Inc.
*
@@ -21,54 +21,74 @@
#include "v8_utils.h"
#include "v8_db.h"
-#define V8_SIMPLE_HEADER Locker l; HandleScope handle_scope; Context::Scope context_scope( _context );
+#define V8_SIMPLE_HEADER V8Lock l; HandleScope handle_scope; Context::Scope context_scope( _context );
namespace mongo {
+ // guarded by v8 mutex
+ map< unsigned, int > __interruptSpecToThreadId;
+
// --- engine ---
V8ScriptEngine::V8ScriptEngine() {}
-
- V8ScriptEngine::~V8ScriptEngine(){
+
+ V8ScriptEngine::~V8ScriptEngine() {
}
- void ScriptEngine::setup(){
- if ( !globalScriptEngine ){
+ void ScriptEngine::setup() {
+ if ( !globalScriptEngine ) {
globalScriptEngine = new V8ScriptEngine();
}
}
+ void V8ScriptEngine::interrupt( unsigned opSpec ) {
+ v8::Locker l;
+ if ( __interruptSpecToThreadId.count( opSpec ) ) {
+ V8::TerminateExecution( __interruptSpecToThreadId[ opSpec ] );
+ }
+ }
+ void V8ScriptEngine::interruptAll() {
+ v8::Locker l;
+ vector< int > toKill; // v8 mutex could potentially be yielded during the termination call
+ for( map< unsigned, int >::const_iterator i = __interruptSpecToThreadId.begin(); i != __interruptSpecToThreadId.end(); ++i ) {
+ toKill.push_back( i->second );
+ }
+ for( vector< int >::const_iterator i = toKill.begin(); i != toKill.end(); ++i ) {
+ V8::TerminateExecution( *i );
+ }
+ }
+
// --- scope ---
-
- V8Scope::V8Scope( V8ScriptEngine * engine )
- : _engine( engine ) ,
- _connectState( NOT ){
- Locker l;
- HandleScope handleScope;
+ V8Scope::V8Scope( V8ScriptEngine * engine )
+ : _engine( engine ) ,
+ _connectState( NOT ) {
+
+ V8Lock l;
+ HandleScope handleScope;
_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"), v8::FunctionTemplate::New(Print)->GetFunction() );
- _global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version)->GetFunction() );
+ _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(loadCallback, v8::External::New(this))->GetFunction() );
-
+ v8::FunctionTemplate::New( v8Callback< loadCallback >, v8::External::New(this))->GetFunction() );
+
_wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate()->GetFunction() );
-
- _global->Set(v8::String::New("gc"), v8::FunctionTemplate::New(GCV8)->GetFunction() );
+
+ _global->Set(v8::String::New("gc"), newV8Function< GCV8 >()->GetFunction() );
installDBTypes( _global );
}
- V8Scope::~V8Scope(){
- Locker l;
- Context::Scope context_scope( _context );
+ V8Scope::~V8Scope() {
+ V8Lock l;
+ Context::Scope context_scope( _context );
_wrapper.Dispose();
_this.Dispose();
for( unsigned i = 0; i < _funcs.size(); ++i )
@@ -79,7 +99,7 @@ namespace mongo {
}
Handle< Value > V8Scope::nativeCallback( const Arguments &args ) {
- Locker l;
+ V8Lock l;
HandleScope handle_scope;
Local< External > f = External::Cast( *args.Callee()->Get( v8::String::New( "_native_function" ) ) );
NativeFunction function = (NativeFunction)(f->Value());
@@ -93,16 +113,18 @@ namespace mongo {
BSONObj ret;
try {
ret = function( nativeArgs );
- } catch( const std::exception &e ) {
+ }
+ catch( const std::exception &e ) {
return v8::ThrowException(v8::String::New(e.what()));
- } catch( ... ) {
- return v8::ThrowException(v8::String::New("unknown exception"));
+ }
+ catch( ... ) {
+ return v8::ThrowException(v8::String::New("unknown exception"));
}
return handle_scope.Close( mongoToV8Element( ret.firstElement() ) );
}
Handle< Value > V8Scope::loadCallback( const Arguments &args ) {
- Locker l;
+ V8Lock l;
HandleScope handle_scope;
Handle<External> field = Handle<External>::Cast(args.Data());
void* ptr = field->Value();
@@ -120,46 +142,46 @@ namespace mongo {
// ---- global stuff ----
- void V8Scope::init( BSONObj * data ){
- Locker l;
+ void V8Scope::init( const BSONObj * data ) {
+ V8Lock l;
if ( ! data )
return;
-
+
BSONObjIterator i( *data );
- while ( i.more() ){
+ while ( i.more() ) {
BSONElement e = i.next();
setElement( e.fieldName() , e );
}
}
-
- void V8Scope::setNumber( const char * field , double val ){
+
+ void V8Scope::setNumber( const char * field , double val ) {
V8_SIMPLE_HEADER
_global->Set( v8::String::New( field ) , v8::Number::New( val ) );
}
- void V8Scope::setString( const char * field , const char * val ){
+ void V8Scope::setString( const char * field , const char * val ) {
V8_SIMPLE_HEADER
_global->Set( v8::String::New( field ) , v8::String::New( val ) );
}
- void V8Scope::setBoolean( const char * field , bool val ){
+ void V8Scope::setBoolean( const char * field , bool val ) {
V8_SIMPLE_HEADER
_global->Set( v8::String::New( field ) , v8::Boolean::New( val ) );
}
- void V8Scope::setElement( const char *field , const BSONElement& e ){
+ void V8Scope::setElement( const char *field , const BSONElement& e ) {
V8_SIMPLE_HEADER
_global->Set( v8::String::New( field ) , mongoToV8Element( e ) );
}
- void V8Scope::setObject( const char *field , const BSONObj& obj , bool readOnly){
+ 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) );
}
- int V8Scope::type( const char *field ){
+ int V8Scope::type( const char *field ) {
V8_SIMPLE_HEADER
Handle<Value> v = get( field );
if ( v->IsNull() )
@@ -178,7 +200,7 @@ namespace mongo {
return NumberInt;
if ( v->IsNumber() )
return NumberDouble;
- if ( v->IsExternal() ){
+ if ( v->IsExternal() ) {
uassert( 10230 , "can't handle external yet" , 0 );
return -1;
}
@@ -190,36 +212,36 @@ namespace mongo {
throw UserException( 12509, (string)"don't know what this is: " + field );
}
- v8::Handle<v8::Value> V8Scope::get( const char * field ){
+ v8::Handle<v8::Value> V8Scope::get( const char * field ) {
return _global->Get( v8::String::New( field ) );
}
- double V8Scope::getNumber( const char *field ){
+ double V8Scope::getNumber( const char *field ) {
V8_SIMPLE_HEADER
return get( field )->ToNumber()->Value();
}
- int V8Scope::getNumberInt( const char *field ){
+ int V8Scope::getNumberInt( const char *field ) {
V8_SIMPLE_HEADER
return get( field )->ToInt32()->Value();
}
- long long V8Scope::getNumberLongLong( const char *field ){
+ long long V8Scope::getNumberLongLong( const char *field ) {
V8_SIMPLE_HEADER
return get( field )->ToInteger()->Value();
}
- string V8Scope::getString( const char *field ){
+ string V8Scope::getString( const char *field ) {
V8_SIMPLE_HEADER
return toSTLString( get( field ) );
}
- bool V8Scope::getBoolean( const char *field ){
+ bool V8Scope::getBoolean( const char *field ) {
V8_SIMPLE_HEADER
return get( field )->ToBoolean()->Value();
}
-
- BSONObj V8Scope::getObject( const char * field ){
+
+ BSONObj V8Scope::getObject( const char * field ) {
V8_SIMPLE_HEADER
Handle<Value> v = get( field );
if ( v->IsNull() || v->IsUndefined() )
@@ -227,21 +249,28 @@ namespace mongo {
uassert( 10231 , "not an object" , v->IsObject() );
return v8ToMongo( v->ToObject() );
}
-
+
// --- functions -----
- Local< v8::Function > V8Scope::__createFunction( const char * raw ){
- for(; isspace( *raw ); ++raw ); // skip whitespace
+ bool hasFunctionIdentifier( const string& code ) {
+ if ( code.size() < 9 || code.find( "function" ) != 0 )
+ return false;
+
+ return code[8] == ' ' || code[8] == '(';
+ }
+
+ Local< v8::Function > V8Scope::__createFunction( const char * raw ) {
+ raw = jsSkipWhiteSpace( raw );
string code = raw;
- if ( code.find( "function" ) == string::npos ){
- if ( code.find( "\n" ) == string::npos &&
- ! hasJSReturn( code ) &&
- ( code.find( ";" ) == string::npos || code.find( ";" ) == code.size() - 1 ) ){
+ if ( !hasFunctionIdentifier( code ) ) {
+ if ( code.find( "\n" ) == string::npos &&
+ ! hasJSReturn( code ) &&
+ ( code.find( ";" ) == string::npos || code.find( ";" ) == code.size() - 1 ) ) {
code = "return " + code;
}
code = "function(){ " + code + "}";
}
-
+
int num = _funcs.size() + 1;
string fn;
@@ -250,29 +279,30 @@ namespace mongo {
ss << "_funcs" << num;
fn = ss.str();
}
-
+
code = fn + " = " + code;
TryCatch try_catch;
- Handle<Script> script = v8::Script::Compile( v8::String::New( code.c_str() ) ,
- v8::String::New( fn.c_str() ) );
- if ( script.IsEmpty() ){
+ // this might be time consuming, consider allowing an interrupt
+ Handle<Script> script = v8::Script::Compile( v8::String::New( code.c_str() ) ,
+ v8::String::New( fn.c_str() ) );
+ if ( script.IsEmpty() ) {
_error = (string)"compile error: " + toSTLString( &try_catch );
log() << _error << endl;
return Local< v8::Function >();
}
-
+
Local<Value> result = script->Run();
- if ( result.IsEmpty() ){
+ if ( result.IsEmpty() ) {
_error = (string)"compile error: " + toSTLString( &try_catch );
log() << _error << endl;
return Local< v8::Function >();
- }
-
+ }
+
return v8::Function::Cast( *_global->Get( v8::String::New( fn.c_str() ) ) );
}
-
- ScriptingFunction V8Scope::_createFunction( const char * raw ){
+
+ ScriptingFunction V8Scope::_createFunction( const char * raw ) {
V8_SIMPLE_HEADER
Local< Value > ret = __createFunction( raw );
if ( ret.IsEmpty() )
@@ -284,9 +314,9 @@ namespace mongo {
return num;
}
- void V8Scope::setThis( const BSONObj * obj ){
+ void V8Scope::setThis( const BSONObj * obj ) {
V8_SIMPLE_HEADER
- if ( ! obj ){
+ if ( ! obj ) {
_this = Persistent< v8::Object >::New( v8::Object::New() );
return;
}
@@ -296,57 +326,80 @@ namespace mongo {
argv[0] = v8::External::New( createWrapperHolder( obj , true , false ) );
_this = Persistent< v8::Object >::New( _wrapper->NewInstance( 1, argv ) );
}
-
- int V8Scope::invoke( ScriptingFunction func , const BSONObj& argsObject, int timeoutMs , bool ignoreReturn ){
+
+ 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 );
+ _global->Set( t , _global->Get( f ) );
+ _global->Set( f , v8::Undefined() );
+ }
+
+ int V8Scope::invoke( ScriptingFunction func , const BSONObj& argsObject, int timeoutMs , bool ignoreReturn ) {
V8_SIMPLE_HEADER
Handle<Value> funcValue = _funcs[func-1];
-
- TryCatch try_catch;
+
+ TryCatch try_catch;
int nargs = argsObject.nFields();
scoped_array< Handle<Value> > args;
- if ( nargs ){
+ if ( nargs ) {
args.reset( new Handle<Value>[nargs] );
BSONObjIterator it( argsObject );
- for ( int i=0; i<nargs; i++ ){
+ for ( int i=0; i<nargs; i++ ) {
BSONElement next = it.next();
args[i] = mongoToV8Element( next );
}
setObject( "args", argsObject, true ); // for backwards compatibility
- } else {
+ }
+ else {
_global->Set( v8::String::New( "args" ), v8::Undefined() );
}
+ if ( globalScriptEngine->interrupted() ) {
+ stringstream ss;
+ ss << "error in invoke: " << globalScriptEngine->checkInterrupt();
+ _error = ss.str();
+ log() << _error << endl;
+ return 1;
+ }
+ enableV8Interrupt(); // because of v8 locker we can check interrupted, then enable
Local<Value> result = ((v8::Function*)(*funcValue))->Call( _this , nargs , args.get() );
-
- if ( result.IsEmpty() ){
+ disableV8Interrupt();
+
+ if ( result.IsEmpty() ) {
stringstream ss;
- ss << "error in invoke: " << toSTLString( &try_catch );
+ if ( try_catch.HasCaught() && !try_catch.CanContinue() ) {
+ ss << "error in invoke: " << globalScriptEngine->checkInterrupt();
+ }
+ else {
+ ss << "error in invoke: " << toSTLString( &try_catch );
+ }
_error = ss.str();
log() << _error << endl;
return 1;
}
- if ( ! ignoreReturn ){
+ if ( ! ignoreReturn ) {
_global->Set( v8::String::New( "return" ) , result );
}
return 0;
}
- bool V8Scope::exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ){
- if ( timeoutMs ){
+ bool V8Scope::exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ) {
+ if ( timeoutMs ) {
static bool t = 1;
- if ( t ){
- log() << "timeoutMs not support for v8 yet" << endl;
+ if ( t ) {
+ log() << "timeoutMs not support for v8 yet code: " << code << endl;
t = 0;
}
}
-
+
V8_SIMPLE_HEADER
-
+
TryCatch try_catch;
-
- Handle<Script> script = v8::Script::Compile( v8::String::New( code.c_str() ) ,
- v8::String::New( name.c_str() ) );
+
+ Handle<Script> script = v8::Script::Compile( v8::String::New( code.data() ) ,
+ v8::String::New( name.c_str() ) );
if (script.IsEmpty()) {
stringstream ss;
ss << "compile error: " << toSTLString( &try_catch );
@@ -356,65 +409,87 @@ namespace mongo {
if ( assertOnError )
uassert( 10233 , _error , 0 );
return false;
- }
-
+ }
+
+ if ( globalScriptEngine->interrupted() ) {
+ _error = (string)"exec error: " + globalScriptEngine->checkInterrupt();
+ if ( reportError ) {
+ log() << _error << endl;
+ }
+ if ( assertOnError ) {
+ uassert( 13475 , _error , 0 );
+ }
+ return false;
+ }
+ enableV8Interrupt(); // because of v8 locker we can check interrupted, then enable
Handle<v8::Value> result = script->Run();
- if ( result.IsEmpty() ){
- _error = (string)"exec error: " + toSTLString( &try_catch );
+ disableV8Interrupt();
+ if ( result.IsEmpty() ) {
+ if ( try_catch.HasCaught() && !try_catch.CanContinue() ) {
+ _error = (string)"exec error: " + globalScriptEngine->checkInterrupt();
+ }
+ else {
+ _error = (string)"exec error: " + toSTLString( &try_catch );
+ }
if ( reportError )
log() << _error << endl;
if ( assertOnError )
uassert( 10234 , _error , 0 );
return false;
- }
-
+ }
+
_global->Set( v8::String::New( "__lastres__" ) , result );
- if ( printResult && ! result->IsUndefined() ){
+ if ( printResult && ! result->IsUndefined() ) {
cout << toSTLString( result ) << endl;
}
-
+
return true;
}
-
- void V8Scope::injectNative( const char *field, NativeFunction func ){
+
+ void V8Scope::injectNative( const char *field, NativeFunction func ) {
V8_SIMPLE_HEADER
-
- Handle< FunctionTemplate > f( v8::FunctionTemplate::New( nativeCallback ) );
+
+ Handle< FunctionTemplate > f( newV8Function< nativeCallback >() );
f->Set( v8::String::New( "_native_function" ), External::New( (void*)func ) );
_global->Set( v8::String::New( field ), f->GetFunction() );
- }
-
+ }
+
void V8Scope::gc() {
cout << "in gc" << endl;
- Locker l;
- while( V8::IdleNotification() );
+ V8Lock l;
+ while( !V8::IdleNotification() );
}
// ----- db access -----
- void V8Scope::localConnect( const char * dbName ){
- V8_SIMPLE_HEADER
+ void V8Scope::localConnect( const char * dbName ) {
+ {
+ V8_SIMPLE_HEADER
+
+ if ( _connectState == EXTERNAL )
+ throw UserException( 12510, "externalSetup already called, can't call externalSetup" );
+ if ( _connectState == LOCAL ) {
+ if ( _localDBName == dbName )
+ return;
+ throw UserException( 12511, "localConnect called with a different name previously" );
+ }
- if ( _connectState == EXTERNAL )
- throw UserException( 12510, "externalSetup already called, can't call externalSetup" );
- if ( _connectState == LOCAL ){
- if ( _localDBName == dbName )
- return;
- throw UserException( 12511, "localConnect called with a different name previously" );
- }
+ // needed for killop / interrupt support
+ v8::Locker::StartPreemption( 50 );
- //_global->Set( v8::String::New( "Mongo" ) , _engine->_externalTemplate->GetFunction() );
- _global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( true )->GetFunction() );
- exec( jsconcatcode , "localConnect 1" , false , true , true , 0 );
- exec( "_mongo = new Mongo();" , "local connect 2" , false , true , true , 0 );
- exec( (string)"db = _mongo.getDB(\"" + dbName + "\");" , "local connect 3" , false , true , true , 0 );
- _connectState = LOCAL;
- _localDBName = dbName;
+ //_global->Set( v8::String::New( "Mongo" ) , _engine->_externalTemplate->GetFunction() );
+ _global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( 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 );
+ _connectState = LOCAL;
+ _localDBName = dbName;
+ }
loadStored();
}
-
- void V8Scope::externalSetup(){
+
+ void V8Scope::externalSetup() {
V8_SIMPLE_HEADER
if ( _connectState == EXTERNAL )
return;
@@ -423,18 +498,18 @@ namespace mongo {
installFork( _global, _context );
_global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( false )->GetFunction() );
- exec( jsconcatcode , "shell setup" , false , true , true , 0 );
+ execCoreFiles();
_connectState = EXTERNAL;
}
// ----- internal -----
- void V8Scope::reset(){
+ void V8Scope::reset() {
_startCall();
}
- void V8Scope::_startCall(){
+ void V8Scope::_startCall() {
_error = "";
}
-
+
} // namespace mongo
diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h
index 9d86d92..c770955 100644
--- a/scripting/engine_v8.h
+++ b/scripting/engine_v8.h
@@ -19,6 +19,7 @@
#include <vector>
#include "engine.h"
+#include "v8_db.h"
#include <v8.h>
using namespace v8;
@@ -26,19 +27,19 @@ using namespace v8;
namespace mongo {
class V8ScriptEngine;
-
+
class V8Scope : public Scope {
public:
-
+
V8Scope( V8ScriptEngine * engine );
~V8Scope();
-
+
virtual void reset();
- virtual void init( BSONObj * data );
+ virtual void init( const BSONObj * data );
virtual void localConnect( const char * dbName );
virtual void externalSetup();
-
+
v8::Handle<v8::Value> get( const char * field ); // caller must create context and handle scopes
virtual double getNumber( const char *field );
virtual int getNumberInt( const char *field );
@@ -46,7 +47,7 @@ namespace mongo {
virtual string getString( const char *field );
virtual bool getBoolean( const char *field );
virtual BSONObj getObject( const char *field );
-
+
virtual int type( const char *field );
virtual void setNumber( const char *field , double val );
@@ -55,22 +56,24 @@ namespace mongo {
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 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 bool exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs );
- virtual string getError(){ return _error; }
-
+ 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 );
void gc();
Handle< Context > context() const { return _context; }
-
+
private:
void _startCall();
-
+
static Handle< Value > nativeCallback( const Arguments &args );
static Handle< Value > loadCallback( const Arguments &args );
@@ -89,28 +92,32 @@ namespace mongo {
enum ConnectState { NOT , LOCAL , EXTERNAL };
ConnectState _connectState;
};
-
+
class V8ScriptEngine : public ScriptEngine {
public:
V8ScriptEngine();
virtual ~V8ScriptEngine();
-
- virtual Scope * createScope(){ return new V8Scope( this ); }
-
- virtual void runTest(){}
+
+ virtual Scope * createScope() { return new V8Scope( this ); }
+
+ virtual void runTest() {}
bool utf8Ok() const { return true; }
- class V8Unlocker : public Unlocker {
- v8::Unlocker u_;
+ class V8UnlockForClient : public Unlocker {
+ V8Unlock u_;
};
-
- virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< Unlocker >( new V8Unlocker ); }
-
+
+ virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< Unlocker >( new V8UnlockForClient ); }
+
+ virtual void interrupt( unsigned opSpec );
+ virtual void interruptAll();
+
private:
friend class V8Scope;
};
-
-
+
+
extern ScriptEngine * globalScriptEngine;
+ extern map< unsigned, int > __interruptSpecToThreadId;
}
diff --git a/scripting/sm_db.cpp b/scripting/sm_db.cpp
index 8ba612b..4c9d541 100644
--- a/scripting/sm_db.cpp
+++ b/scripting/sm_db.cpp
@@ -34,15 +34,15 @@ namespace mongo {
bool haveLocalShardingInfo( const string& ns );
// ------------ some defs needed ---------------
-
+
JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName );
-
+
// ------------ utils ------------------
-
- bool isSpecialName( const string& name ){
+
+ bool isSpecialName( const string& name ) {
static set<string> names;
- if ( names.size() == 0 ){
+ if ( names.size() == 0 ) {
names.insert( "tojson" );
names.insert( "toJson" );
names.insert( "toString" );
@@ -50,10 +50,10 @@ namespace mongo {
if ( name.length() == 0 )
return false;
-
+
if ( name[0] == '_' )
return true;
-
+
return names.count( name ) > 0;
}
@@ -63,8 +63,8 @@ namespace mongo {
class CursorHolder {
public:
CursorHolder( auto_ptr< DBClientCursor > &cursor, const shared_ptr< DBClientWithCommands > &connection ) :
- connection_( connection ),
- cursor_( cursor ) {
+ connection_( connection ),
+ cursor_( cursor ) {
assert( cursor_.get() );
}
DBClientCursor *get() const { return cursor_.get(); }
@@ -72,60 +72,60 @@ namespace mongo {
shared_ptr< DBClientWithCommands > connection_;
auto_ptr< DBClientCursor > cursor_;
};
-
+
DBClientCursor *getCursor( JSContext *cx, JSObject *obj ) {
CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj );
uassert( 10235 , "no cursor!" , holder );
return holder->get();
}
-
- JSBool internal_cursor_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+
+ JSBool internal_cursor_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
uassert( 10236 , "no args to internal_cursor_constructor" , argc == 0 );
assert( JS_SetPrivate( cx , obj , 0 ) ); // just for safety
return JS_TRUE;
}
- void internal_cursor_finalize( JSContext * cx , JSObject * obj ){
+ void internal_cursor_finalize( JSContext * cx , JSObject * obj ) {
CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj );
- if ( holder ){
+ if ( holder ) {
delete holder;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
}
- JSBool internal_cursor_hasNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool internal_cursor_hasNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
DBClientCursor *cursor = getCursor( cx, obj );
try {
*rval = cursor->more() ? JSVAL_TRUE : JSVAL_FALSE;
}
- catch ( std::exception& e ){
+ catch ( std::exception& e ) {
JS_ReportError( cx , e.what() );
return JS_FALSE;
}
return JS_TRUE;
}
- JSBool internal_cursor_objsLeftInBatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool internal_cursor_objsLeftInBatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
DBClientCursor *cursor = getCursor( cx, obj );
Convertor c(cx);
*rval = c.toval((double) cursor->objsLeftInBatch() );
return JS_TRUE;
}
- JSBool internal_cursor_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool internal_cursor_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
DBClientCursor *cursor = getCursor( cx, obj );
- BSONObj n;
-
+ BSONObj n;
+
try {
- if ( ! cursor->more() ){
+ if ( ! cursor->more() ) {
JS_ReportError( cx , "cursor at the end" );
return JS_FALSE;
}
n = cursor->next();
}
- catch ( std::exception& e ){
+ catch ( std::exception& e ) {
JS_ReportError( cx , e.what() );
return JS_FALSE;
}
@@ -149,15 +149,15 @@ namespace mongo {
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
+
// ------ mongo stuff ------
- JSBool mongo_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool mongo_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
uassert( 10237 , "mongo_constructor not implemented yet" , 0 );
throw -1;
}
-
- JSBool mongo_local_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+
+ JSBool mongo_local_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
shared_ptr< DBClientWithCommands > client( createDirectClient() );
@@ -169,29 +169,29 @@ namespace mongo {
return JS_TRUE;
}
- JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
-
+
smuassert( cx , "0 or 1 args to Mongo" , argc <= 1 );
-
+
string host = "127.0.0.1";
if ( argc > 0 )
host = c.toString( argv[0] );
-
+
string errmsg;
ConnectionString cs = ConnectionString::parse( host , errmsg );
- if ( ! cs.isValid() ){
+ if ( ! cs.isValid() ) {
JS_ReportError( cx , errmsg.c_str() );
return JS_FALSE;
}
shared_ptr< DBClientWithCommands > conn( cs.connect( errmsg ) );
- if ( ! conn ){
+ if ( ! conn ) {
JS_ReportError( cx , errmsg.c_str() );
return JS_FALSE;
}
-
+
ScriptEngine::runConnectCallback( *conn );
assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( conn ) ) ) );
@@ -206,10 +206,10 @@ namespace mongo {
uassert( 10239 , "no connection!" , connHolder && connHolder->get() );
return connHolder->get();
}
-
- void mongo_finalize( JSContext * cx , JSObject * obj ){
+
+ void mongo_finalize( JSContext * cx , JSObject * obj ) {
shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
- if ( connHolder ){
+ if ( connHolder ) {
delete connHolder;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
@@ -220,30 +220,31 @@ namespace mongo {
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, mongo_finalize,
JSCLASS_NO_OPTIONAL_MEMBERS
- };
+ };
- JSBool mongo_find(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
- smuassert( cx , "mongo_find needs 6 args" , argc == 6 );
+ JSBool mongo_find(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ smuassert( cx , "mongo_find needs 7 args" , argc == 7 );
shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
smuassert( cx , "no connection!" , connHolder && connHolder->get() );
DBClientWithCommands *conn = connHolder->get();
-
+
Convertor c( cx );
string ns = c.toString( argv[0] );
-
+
BSONObj q = c.toObject( argv[1] );
BSONObj f = c.toObject( argv[2] );
-
+
int nToReturn = (int) c.toNumber( argv[3] );
int nToSkip = (int) c.toNumber( argv[4] );
bool slaveOk = c.getBoolean( obj , "slaveOk" );
int batchSize = (int) c.toNumber( argv[5] );
+ int options = (int)c.toNumber( argv[6] );
try {
- auto_ptr<DBClientCursor> cursor = conn->query( ns , q , nToReturn , nToSkip , f.nFields() ? &f : 0 , slaveOk ? QueryOption_SlaveOk : 0 , batchSize );
- if ( ! cursor.get() ){
+ auto_ptr<DBClientCursor> cursor = conn->query( ns , q , nToReturn , nToSkip , f.nFields() ? &f : 0 , options | ( slaveOk ? QueryOption_SlaveOk : 0 ) , batchSize );
+ if ( ! cursor.get() ) {
log() << "query failed : " << ns << " " << q << " to: " << conn->toString() << endl;
JS_ReportError( cx , "error doing query: failed" );
return JS_FALSE;
@@ -254,19 +255,19 @@ namespace mongo {
*rval = OBJECT_TO_JSVAL( mycursor );
return JS_TRUE;
}
- catch ( ... ){
+ catch ( ... ) {
JS_ReportError( cx , "error doing query: unknown" );
return JS_FALSE;
}
}
- JSBool mongo_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool mongo_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
smuassert( cx , "mongo_find needs at elast 3 args" , argc >= 3 );
smuassert( cx , "2nd param to update has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
smuassert( cx , "3rd param to update has to be an object" , JSVAL_IS_OBJECT( argv[2] ) );
Convertor c( cx );
- if ( c.getBoolean( obj , "readOnly" ) ){
+ if ( c.getBoolean( obj , "readOnly" ) ) {
JS_ReportError( cx , "js db in read only mode - mongo_update" );
return JS_FALSE;
}
@@ -283,76 +284,80 @@ namespace mongo {
conn->update( ns , c.toObject( argv[1] ) , c.toObject( argv[2] ) , upsert , multi );
return JS_TRUE;
}
- catch ( ... ){
+ catch ( ... ) {
JS_ReportError( cx , "error doing update" );
return JS_FALSE;
}
}
- JSBool mongo_insert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool mongo_insert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
smuassert( cx , "mongo_insert needs 2 args" , argc == 2 );
smuassert( cx , "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
Convertor c( cx );
- if ( c.getBoolean( obj , "readOnly" ) ){
+ if ( c.getBoolean( obj , "readOnly" ) ) {
JS_ReportError( cx , "js db in read only mode - mongo_insert" );
return JS_FALSE;
}
-
+
DBClientWithCommands * conn = getConnection( cx, obj );
uassert( 10248 , "no connection!" , conn );
-
-
+
string ns = c.toString( argv[0] );
- BSONObj o = c.toObject( argv[1] );
- // TODO: add _id
-
try {
+ BSONObj o = c.toObject( argv[1] );
+ // TODO: add _id
+
conn->insert( ns , o );
return JS_TRUE;
}
- catch ( std::exception& e ){
+ catch ( std::exception& e ) {
stringstream ss;
ss << "error doing insert:" << e.what();
string s = ss.str();
JS_ReportError( cx , s.c_str() );
return JS_FALSE;
}
- catch ( ... ){
+ catch ( ... ) {
JS_ReportError( cx , "error doing insert" );
return JS_FALSE;
}
}
- JSBool mongo_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool mongo_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
smuassert( cx , "mongo_remove needs 2 or 3 arguments" , argc == 2 || argc == 3 );
smuassert( cx , "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
Convertor c( cx );
- if ( c.getBoolean( obj , "readOnly" ) ){
+ if ( c.getBoolean( obj , "readOnly" ) ) {
JS_ReportError( cx , "js db in read only mode - mongo_remove" );
return JS_FALSE;
}
DBClientWithCommands * conn = getConnection( cx, obj );
uassert( 10251 , "no connection!" , conn );
-
+
string ns = c.toString( argv[0] );
BSONObj o = c.toObject( argv[1] );
bool justOne = false;
if ( argc > 2 )
justOne = c.toBoolean( argv[2] );
-
+
try {
conn->remove( ns , o , justOne );
return JS_TRUE;
}
- catch ( ... ){
- JS_ReportError( cx , "error doing remove" );
+ catch ( std::exception& e ) {
+ JS_ReportError( cx , e.what() );
return JS_FALSE;
}
+ catch ( ... ) {
+ JS_ReportError( cx , "error doing remove" );
+ return JS_FALSE;
+ }
+
}
JSFunctionSpec mongo_functions[] = {
@@ -363,93 +368,93 @@ namespace mongo {
{ 0 }
};
- // ------------- db_collection -------------
-
- JSBool db_collection_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
- smuassert( cx , "db_collection_constructor wrong args" , argc == 4 );
- assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
- assert( JS_SetProperty( cx , obj , "_db" , &(argv[1]) ) );
- assert( JS_SetProperty( cx , obj , "_shortName" , &(argv[2]) ) );
- assert( JS_SetProperty( cx , obj , "_fullName" , &(argv[3]) ) );
-
- Convertor c(cx);
- if ( haveLocalShardingInfo( c.toString( argv[3] ) ) ){
- JS_ReportError( cx , "can't use sharded collection from db.eval" );
- return JS_FALSE;
- }
-
- return JS_TRUE;
- }
-
- JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
- if ( flags & JSRESOLVE_ASSIGNING )
- return JS_TRUE;
-
- Convertor c( cx );
- string collname = c.toString( id );
-
- if ( isSpecialName( collname ) )
- return JS_TRUE;
-
- if ( obj == c.getGlobalPrototype( "DBCollection" ) )
- return JS_TRUE;
-
- JSObject * proto = JS_GetPrototype( cx , obj );
- if ( c.hasProperty( obj , collname.c_str() ) || ( proto && c.hasProperty( proto , collname.c_str() ) ) )
- return JS_TRUE;
-
- string name = c.toString( c.getProperty( obj , "_shortName" ) );
- name += ".";
- name += collname;
-
- jsval db = c.getProperty( obj , "_db" );
- if ( ! JSVAL_IS_OBJECT( db ) )
- return JS_TRUE;
-
- JSObject * coll = doCreateCollection( cx , JSVAL_TO_OBJECT( db ) , name );
- if ( ! coll )
- return JS_FALSE;
- c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
- *objp = obj;
- return JS_TRUE;
- }
+ // ------------- db_collection -------------
+
+ JSBool db_collection_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ smuassert( cx , "db_collection_constructor wrong args" , argc == 4 );
+ assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
+ assert( JS_SetProperty( cx , obj , "_db" , &(argv[1]) ) );
+ assert( JS_SetProperty( cx , obj , "_shortName" , &(argv[2]) ) );
+ assert( JS_SetProperty( cx , obj , "_fullName" , &(argv[3]) ) );
+
+ Convertor c(cx);
+ if ( haveLocalShardingInfo( c.toString( argv[3] ) ) ) {
+ JS_ReportError( cx , "can't use sharded collection from db.eval" );
+ return JS_FALSE;
+ }
+
+ return JS_TRUE;
+ }
+
+ JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
+ if ( flags & JSRESOLVE_ASSIGNING )
+ return JS_TRUE;
+
+ Convertor c( cx );
+ string collname = c.toString( id );
+
+ if ( isSpecialName( collname ) )
+ return JS_TRUE;
+
+ if ( obj == c.getGlobalPrototype( "DBCollection" ) )
+ return JS_TRUE;
+
+ JSObject * proto = JS_GetPrototype( cx , obj );
+ if ( c.hasProperty( obj , collname.c_str() ) || ( proto && c.hasProperty( proto , collname.c_str() ) ) )
+ return JS_TRUE;
+
+ string name = c.toString( c.getProperty( obj , "_shortName" ) );
+ name += ".";
+ name += collname;
+
+ jsval db = c.getProperty( obj , "_db" );
+ if ( ! JSVAL_IS_OBJECT( db ) )
+ return JS_TRUE;
+
+ JSObject * coll = doCreateCollection( cx , JSVAL_TO_OBJECT( db ) , name );
+ if ( ! coll )
+ return JS_FALSE;
+ c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
+ *objp = obj;
+ return JS_TRUE;
+ }
JSClass db_collection_class = {
- "DBCollection" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub, (JSResolveOp)(&db_collection_resolve) , JS_ConvertStub, JS_FinalizeStub,
- JSCLASS_NO_OPTIONAL_MEMBERS
- };
-
-
- JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName ){
- Convertor c(cx);
-
- assert( c.hasProperty( db , "_mongo" ) );
- assert( c.hasProperty( db , "_name" ) );
-
- JSObject * coll = JS_NewObject( cx , &db_collection_class , 0 , 0 );
- CHECKNEWOBJECT( coll, cx, "doCreateCollection" );
- c.setProperty( coll , "_mongo" , c.getProperty( db , "_mongo" ) );
- c.setProperty( coll , "_db" , OBJECT_TO_JSVAL( db ) );
- c.setProperty( coll , "_shortName" , c.toval( shortName.c_str() ) );
-
- string name = c.toString( c.getProperty( db , "_name" ) );
- name += "." + shortName;
- c.setProperty( coll , "_fullName" , c.toval( name.c_str() ) );
-
- if ( haveLocalShardingInfo( name ) ){
- JS_ReportError( cx , "can't use sharded collection from db.eval" );
- return 0;
- }
-
- return coll;
+ "DBCollection" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, (JSResolveOp)(&db_collection_resolve) , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+
+ JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName ) {
+ Convertor c(cx);
+
+ assert( c.hasProperty( db , "_mongo" ) );
+ assert( c.hasProperty( db , "_name" ) );
+
+ JSObject * coll = JS_NewObject( cx , &db_collection_class , 0 , 0 );
+ CHECKNEWOBJECT( coll, cx, "doCreateCollection" );
+ c.setProperty( coll , "_mongo" , c.getProperty( db , "_mongo" ) );
+ c.setProperty( coll , "_db" , OBJECT_TO_JSVAL( db ) );
+ c.setProperty( coll , "_shortName" , c.toval( shortName.c_str() ) );
+
+ string name = c.toString( c.getProperty( db , "_name" ) );
+ name += "." + shortName;
+ c.setProperty( coll , "_fullName" , c.toval( name.c_str() ) );
+
+ if ( haveLocalShardingInfo( name ) ) {
+ JS_ReportError( cx , "can't use sharded collection from db.eval" );
+ return 0;
+ }
+
+ return coll;
}
-
+
// -------------- DB ---------------
-
-
- JSBool db_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+
+
+ JSBool db_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
smuassert( cx, "wrong number of arguments to DB" , argc == 2 );
assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
assert( JS_SetProperty( cx , obj , "_name" , &(argv[1]) ) );
@@ -457,7 +462,7 @@ namespace mongo {
return JS_TRUE;
}
- JSBool db_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
+ JSBool db_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
if ( flags & JSRESOLVE_ASSIGNING )
return JS_TRUE;
@@ -467,9 +472,9 @@ namespace mongo {
return JS_TRUE;
string collname = c.toString( id );
-
+
if ( isSpecialName( collname ) )
- return JS_TRUE;
+ return JS_TRUE;
JSObject * proto = JS_GetPrototype( cx , obj );
if ( proto && c.hasProperty( proto , collname.c_str() ) )
@@ -479,26 +484,26 @@ namespace mongo {
if ( ! coll )
return JS_FALSE;
c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
-
+
*objp = obj;
return JS_TRUE;
}
JSClass db_class = {
- "DB" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
+ "DB" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, (JSResolveOp)(&db_resolve) , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
+
// -------------- object id -------------
- JSBool object_id_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool object_id_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
OID oid;
- if ( argc == 0 ){
+ if ( argc == 0 ) {
oid.init();
}
else {
@@ -507,26 +512,27 @@ namespace mongo {
try {
Scope::validateObjectIdString( s );
- } catch ( const MsgAssertionException &m ) {
+ }
+ catch ( const MsgAssertionException &m ) {
static string error = m.toString();
JS_ReportError( cx, error.c_str() );
return JS_FALSE;
}
oid.init( s );
}
-
- if ( ! JS_InstanceOf( cx , obj , &object_id_class , 0 ) ){
+
+ if ( ! JS_InstanceOf( cx , obj , &object_id_class , 0 ) ) {
obj = JS_NewObject( cx , &object_id_class , 0 , 0 );
CHECKNEWOBJECT( obj, cx, "object_id_constructor" );
*rval = OBJECT_TO_JSVAL( obj );
}
-
+
jsval v = c.toval( oid.str().c_str() );
assert( JS_SetProperty( cx , obj , "str" , &v ) );
return JS_TRUE;
}
-
+
JSClass object_id_class = {
"ObjectId" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
@@ -534,7 +540,7 @@ namespace mongo {
JSCLASS_NO_OPTIONAL_MEMBERS
};
- JSBool object_id_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool object_id_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
Convertor c(cx);
return (JSBool) (*rval = c.getProperty( obj , "str" ));
}
@@ -546,26 +552,26 @@ namespace mongo {
// dbpointer
- JSBool dbpointer_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool dbpointer_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
-
- if ( argc == 2 ){
- if ( ! JSVAL_IS_OID( argv[1] ) ){
+ if ( argc == 2 ) {
+
+ if ( ! JSVAL_IS_OID( argv[1] ) ) {
JS_ReportError( cx , "2nd arg to DBPointer needs to be oid" );
- return JS_FALSE;
+ return JS_FALSE;
}
-
+
assert( JS_SetProperty( cx , obj , "ns" , &(argv[0]) ) );
assert( JS_SetProperty( cx , obj , "id" , &(argv[1]) ) );
return JS_TRUE;
}
else {
JS_ReportError( cx , "DBPointer needs 2 arguments" );
- return JS_FALSE;
+ return JS_FALSE;
}
}
-
+
JSClass dbpointer_class = {
"DBPointer" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
@@ -578,10 +584,10 @@ namespace mongo {
};
- JSBool dbref_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool dbref_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
- if ( argc == 2 ){
+ if ( argc == 2 ) {
JSObject * o = JS_NewObject( cx , NULL , NULL, NULL );
CHECKNEWOBJECT( o, cx, "dbref_constructor" );
assert( JS_SetProperty( cx, o , "$ref" , &argv[ 0 ] ) );
@@ -593,37 +599,37 @@ namespace mongo {
else {
JS_ReportError( cx , "DBRef needs 2 arguments" );
assert( JS_SetPrivate( cx , obj , (void*)(new BSONHolder( BSONObj().getOwned() ) ) ) );
- return JS_FALSE;
+ return JS_FALSE;
}
}
-
+
JSClass dbref_class = bson_class; // name will be fixed later
// UUID **************************
- JSBool uuid_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool uuid_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
-
- if( argc == 0 ) {
+
+ if( argc == 0 ) {
#if defined(HAVE_UUID)
//uuids::uuid
#else
#endif
JS_ReportError( cx , "UUID needs 1 argument -- UUID(hexstr)" );
- return JS_FALSE;
+ return JS_FALSE;
}
else if ( argc == 1 ) {
string encoded = c.toString( argv[ 0 ] );
- if( encoded.size() != 32 ) {
- JS_ReportError( cx, "expect 32 char hex string to UUID()" );
- return JS_FALSE;
- }
+ if( encoded.size() != 32 ) {
+ JS_ReportError( cx, "expect 32 char hex string to UUID()" );
+ return JS_FALSE;
+ }
- char buf[16];
- for( int i = 0; i < 16; i++ ) {
- buf[i] = fromHex(encoded.c_str() + i * 2);
- }
+ char buf[16];
+ for( int i = 0; i < 16; i++ ) {
+ buf[i] = fromHex(encoded.c_str() + i * 2);
+ }
assert( JS_SetPrivate( cx, obj, new BinDataHolder( buf, 16 ) ) );
c.setProperty( obj, "len", c.toval( (double)16 ) );
@@ -633,11 +639,11 @@ namespace mongo {
}
else {
JS_ReportError( cx , "UUID needs 1 argument -- UUID(hexstr)" );
- return JS_FALSE;
+ return JS_FALSE;
}
}
-
- JSBool uuid_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+
+ JSBool uuid_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
Convertor c(cx);
void *holder = JS_GetPrivate( cx, obj );
assert( holder );
@@ -649,15 +655,15 @@ namespace mongo {
return *rval = c.toval( ret.c_str() );
}
- void uuid_finalize( JSContext * cx , JSObject * obj ){
+ void uuid_finalize( JSContext * cx , JSObject * obj ) {
Convertor c(cx);
void *holder = JS_GetPrivate( cx, obj );
- if ( holder ){
+ if ( holder ) {
delete ( BinDataHolder* )holder;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
- }
-
+ }
+
JSClass uuid_class = {
"UUID" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
@@ -669,25 +675,25 @@ namespace mongo {
{ "toString" , uuid_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
-
+
// BinData **************************
- JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
Convertor c( cx );
-
- if ( argc == 2 ){
+
+ if ( argc == 2 ) {
int type = (int)c.toNumber( argv[ 0 ] );
- if( type < 0 || type > 255 ) {
+ if( type < 0 || type > 255 ) {
JS_ReportError( cx , "invalid BinData subtype -- range is 0..255 see bsonspec.org" );
- return JS_FALSE;
+ return JS_FALSE;
}
string encoded = c.toString( argv[ 1 ] );
string decoded;
try {
decoded = base64::decode( encoded );
}
- catch(...) {
+ catch(...) {
JS_ReportError(cx, "BinData could not decode base64 parameter");
return JS_FALSE;
}
@@ -700,11 +706,11 @@ namespace mongo {
}
else {
JS_ReportError( cx , "BinData needs 2 arguments -- BinData(subtype,data)" );
- return JS_FALSE;
+ return JS_FALSE;
}
}
-
- JSBool bindata_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+
+ JSBool bindata_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
Convertor c(cx);
int type = (int)c.getNumber( obj , "type" );
int len = (int)c.getNumber( obj, "len" );
@@ -719,7 +725,7 @@ namespace mongo {
return *rval = c.toval( ret.c_str() );
}
- JSBool bindataBase64(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool bindataBase64(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
Convertor c(cx);
int len = (int)c.getNumber( obj, "len" );
void *holder = JS_GetPrivate( cx, obj );
@@ -731,7 +737,7 @@ namespace mongo {
return *rval = c.toval( ret.c_str() );
}
- JSBool bindataAsHex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool bindataAsHex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
Convertor c(cx);
int len = (int)c.getNumber( obj, "len" );
void *holder = JS_GetPrivate( cx, obj );
@@ -747,27 +753,27 @@ namespace mongo {
return *rval = c.toval( ret.c_str() );
}
- JSBool bindataLength(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ 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){
+ 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 ){
+ void bindata_finalize( JSContext * cx , JSObject * obj ) {
Convertor c(cx);
void *holder = JS_GetPrivate( cx, obj );
- if ( holder ){
+ if ( holder ) {
delete ( BinDataHolder* )holder;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
- }
-
+ }
+
JSClass bindata_class = {
"BinData" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
@@ -783,15 +789,15 @@ namespace mongo {
{ "subtype", bindataSubtype, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
-
+
// Map
- bool specialMapString( const string& s ){
+ bool specialMapString( const string& s ) {
return s == "put" || s == "get" || s == "_get" || s == "values" || s == "_data" || s == "constructor" ;
}
- JSBool map_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
- if ( argc > 0 ){
+ JSBool map_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ if ( argc > 0 ) {
JS_ReportError( cx , "Map takes no arguments" );
return JS_FALSE;
}
@@ -804,28 +810,28 @@ namespace mongo {
return JS_TRUE;
}
-
- JSBool map_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp ){
+
+ JSBool map_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp ) {
Convertor c(cx);
if ( specialMapString( c.toString( idval ) ) )
return JS_TRUE;
-
+
log() << "illegal prop access: " << c.toString( idval ) << endl;
JS_ReportError( cx , "can't use array access with Map" );
return JS_FALSE;
}
-
+
JSClass map_class = {
"Map" , JSCLASS_HAS_PRIVATE ,
map_prop, JS_PropertyStub, map_prop, map_prop,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
+
JSFunctionSpec map_functions[] = {
{ 0 }
};
-
+
// -----
@@ -835,23 +841,54 @@ namespace mongo {
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
+
+ JSBool timestamp_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ smuassert( cx , "Timestamp needs 0 or 2 args" , argc == 0 || argc == 2 );
+
+ if ( ! JS_InstanceOf( cx , obj , &timestamp_class , 0 ) ) {
+ obj = JS_NewObject( cx , &timestamp_class , 0 , 0 );
+ CHECKNEWOBJECT( obj, cx, "timestamp_constructor" );
+ *rval = OBJECT_TO_JSVAL( obj );
+ }
+
+ Convertor c( cx );
+ if ( argc == 0 ) {
+ c.setProperty( obj, "t", c.toval( 0.0 ) );
+ c.setProperty( obj, "i", c.toval( 0.0 ) );
+ }
+ else {
+ c.setProperty( obj, "t", argv[ 0 ] );
+ c.setProperty( obj, "i", argv[ 1 ] );
+ }
+
+ return JS_TRUE;
+ }
+
+
JSClass numberlong_class = {
"NumberLong" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
- JSBool numberlong_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+
+ JSBool numberlong_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
smuassert( cx , "NumberLong needs 0 or 1 args" , argc == 0 || argc == 1 );
-
+
+ if ( ! JS_InstanceOf( cx , obj , &numberlong_class , 0 ) ) {
+ obj = JS_NewObject( cx , &numberlong_class , 0 , 0 );
+ CHECKNEWOBJECT( obj, cx, "numberlong_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 ] ) ) {
+ }
+ else if ( JSVAL_IS_NUMBER( argv[ 0 ] ) ) {
c.setProperty( obj, "floatApprox", argv[ 0 ] );
- } else {
+ }
+ else {
string num = c.toString( argv[ 0 ] );
//PRINT(num);
const char *numStr = num.c_str();
@@ -859,25 +896,26 @@ namespace mongo {
try {
n = parseLL( numStr );
//PRINT(n);
- } catch ( const AssertionException & ) {
- smuassert( cx , "could not convert string to long long" , false );
+ }
+ catch ( const AssertionException & ) {
+ smuassert( cx , "could not convert string to long long" , false );
}
c.makeLongObj( n, obj );
}
-
+
return JS_TRUE;
}
-
- JSBool numberlong_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+
+ JSBool numberlong_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
Convertor c(cx);
- return *rval = c.toval( double( c.toNumberLongUnsafe( obj ) ) );
+ return *rval = c.toval( double( c.toNumberLongUnsafe( obj ) ) );
}
-
- JSBool numberlong_tonumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+
+ JSBool numberlong_tonumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
return numberlong_valueof( cx, obj, argc, argv, rval );
}
- JSBool numberlong_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSBool numberlong_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
Convertor c(cx);
stringstream ss;
long long val = c.toNumberLongUnsafe( obj );
@@ -893,12 +931,12 @@ namespace mongo {
}
JSFunctionSpec numberlong_functions[] = {
- { "valueOf" , numberlong_valueof , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
- { "toNumber" , numberlong_tonumber , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
- { "toString" , numberlong_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
- { 0 }
- };
-
+ { "valueOf" , numberlong_valueof , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "toNumber" , numberlong_tonumber , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "toString" , numberlong_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { 0 }
+ };
+
JSClass minkey_class = {
"MinKey" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
@@ -912,12 +950,12 @@ namespace mongo {
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
+
// dbquery
- JSBool dbquery_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JSBool dbquery_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
smuassert( cx , "DDQuery needs at least 4 args" , argc >= 4 );
-
+
Convertor c(cx);
c.setProperty( obj , "_mongo" , argv[0] );
c.setProperty( obj , "_db" , argv[1] );
@@ -931,28 +969,34 @@ namespace mongo {
CHECKNEWOBJECT( temp, cx, "dbquery_constructor" );
c.setProperty( obj , "_query" , OBJECT_TO_JSVAL( temp ) );
}
-
+
if ( argc > 5 && JSVAL_IS_OBJECT( argv[5] ) )
c.setProperty( obj , "_fields" , argv[5] );
else
c.setProperty( obj , "_fields" , JSVAL_NULL );
-
-
+
+
if ( argc > 6 && JSVAL_IS_NUMBER( argv[6] ) )
c.setProperty( obj , "_limit" , argv[6] );
- else
+ else
c.setProperty( obj , "_limit" , JSVAL_ZERO );
-
+
if ( argc > 7 && JSVAL_IS_NUMBER( argv[7] ) )
c.setProperty( obj , "_skip" , argv[7] );
- else
+ else
c.setProperty( obj , "_skip" , JSVAL_ZERO );
if ( argc > 8 && JSVAL_IS_NUMBER( argv[8] ) )
c.setProperty( obj , "_batchSize" , argv[8] );
- else
+ else
c.setProperty( obj , "_batchSize" , JSVAL_ZERO );
-
+
+ if ( argc > 9 && JSVAL_IS_NUMBER( argv[9] ) )
+ c.setProperty( obj , "_options" , argv[8] );
+ else
+ c.setProperty( obj , "_options" , JSVAL_ZERO );
+
+
c.setProperty( obj , "_cursor" , JSVAL_NULL );
c.setProperty( obj , "_numReturned" , JSVAL_ZERO );
c.setProperty( obj , "_special" , JSVAL_FALSE );
@@ -960,7 +1004,7 @@ namespace mongo {
return JS_TRUE;
}
- JSBool dbquery_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
+ JSBool dbquery_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
if ( flags & JSRESOLVE_ASSIGNING )
return JS_TRUE;
@@ -981,13 +1025,13 @@ namespace mongo {
JS_EnumerateStub, (JSResolveOp)(&dbquery_resolve) , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
+
// ---- other stuff ----
-
- void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ){
+
+ void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ) {
assert( JS_InitClass( cx , global , 0 , &mongo_class , local ? mongo_local_constructor : mongo_external_constructor , 0 , 0 , mongo_functions , 0 , 0 ) );
-
+
assert( JS_InitClass( cx , global , 0 , &object_id_class , object_id_constructor , 0 , 0 , object_id_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &db_class , db_constructor , 2 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &db_collection_class , db_collection_constructor , 4 , 0 , 0 , 0 , 0 ) );
@@ -997,84 +1041,84 @@ namespace mongo {
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 , &timestamp_class , 0 , 0 , 0 , 0 , 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 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &map_class , map_constructor , 0 , 0 , map_functions , 0 , 0 ) );
-
+
assert( JS_InitClass( cx , global , 0 , &bson_ro_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &bson_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
-
+
static const char *dbrefName = "DBRef";
dbref_class.name = dbrefName;
assert( JS_InitClass( cx , global , 0 , &dbref_class , dbref_constructor , 2 , 0 , bson_functions , 0 , 0 ) );
-
- scope->exec( jsconcatcode );
+
+ scope->execCoreFiles();
}
- bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , jsval val , JSObject * o ){
-
- if ( JS_InstanceOf( c->_context , o , &object_id_class , 0 ) ){
+ bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , jsval val , JSObject * o ) {
+
+ if ( JS_InstanceOf( c->_context , o , &object_id_class , 0 ) ) {
OID oid;
oid.init( c->getString( o , "str" ) );
b.append( name , oid );
return true;
}
- if ( JS_InstanceOf( c->_context , o , &minkey_class , 0 ) ){
+ if ( JS_InstanceOf( c->_context , o , &minkey_class , 0 ) ) {
b.appendMinKey( name );
return true;
}
- if ( JS_InstanceOf( c->_context , o , &maxkey_class , 0 ) ){
+ if ( JS_InstanceOf( c->_context , o , &maxkey_class , 0 ) ) {
b.appendMaxKey( name );
return true;
}
-
- if ( JS_InstanceOf( c->_context , o , &timestamp_class , 0 ) ){
+
+ if ( JS_InstanceOf( c->_context , o , &timestamp_class , 0 ) ) {
b.appendTimestamp( name , (unsigned long long)c->getNumber( o , "t" ) , (unsigned int )c->getNumber( o , "i" ) );
return true;
}
- if ( JS_InstanceOf( c->_context , o , &numberlong_class , 0 ) ){
+ if ( JS_InstanceOf( c->_context , o , &numberlong_class , 0 ) ) {
b.append( name , c->toNumberLongUnsafe( o ) );
return true;
}
-
- if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ){
- b.appendDBRef( name , c->getString( o , "ns" ).c_str() , c->toOID( c->getProperty( o , "id" ) ) );
+
+ if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ) {
+ b.appendDBRef( name , c->getString( o , "ns" ) , c->toOID( c->getProperty( o , "id" ) ) );
return true;
}
-
- if ( JS_InstanceOf( c->_context , o , &bindata_class , 0 ) ){
+
+ if ( JS_InstanceOf( c->_context , o , &bindata_class , 0 ) ) {
void *holder = JS_GetPrivate( c->_context , o );
const char *data = ( ( BinDataHolder * )( holder ) )->c_;
- b.appendBinData( name ,
- (int)(c->getNumber( o , "len" )) , (BinDataType)((char)(c->getNumber( o , "type" ) ) ) ,
+ b.appendBinData( name ,
+ (int)(c->getNumber( o , "len" )) , (BinDataType)((char)(c->getNumber( o , "type" ) ) ) ,
data
- );
+ );
return true;
}
-
+
#if defined( SM16 ) || defined( MOZJS )
#warning dates do not work in your version of spider monkey
{
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
- if ( d ){
+ if ( d ) {
b.appendDate( name , Date_t(d) );
return true;
}
}
#elif defined( XULRUNNER )
- if ( JS_InstanceOf( c->_context , o, globalSMEngine->_dateClass , 0 ) ){
+ if ( JS_InstanceOf( c->_context , o, globalSMEngine->_dateClass , 0 ) ) {
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
b.appendDate( name , Date_t(d) );
return true;
}
#else
- if ( JS_InstanceOf( c->_context , o, &js_DateClass , 0 ) ){
+ 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) );
@@ -1082,35 +1126,35 @@ namespace mongo {
}
#endif
-
+
if ( JS_InstanceOf( c->_context , o , &dbquery_class , 0 ) ||
- JS_InstanceOf( c->_context , o , &mongo_class , 0 ) ||
- JS_InstanceOf( c->_context , o , &db_collection_class , 0 ) ){
+ JS_InstanceOf( c->_context , o , &mongo_class , 0 ) ||
+ JS_InstanceOf( c->_context , o , &db_collection_class , 0 ) ) {
b.append( name , c->toString( val ) );
return true;
}
-#if defined( XULRUNNER )
- if ( JS_InstanceOf( c->_context , o , globalSMEngine->_regexClass , 0 ) ){
+#if defined( XULRUNNER )
+ if ( JS_InstanceOf( c->_context , o , globalSMEngine->_regexClass , 0 ) ) {
c->appendRegex( b , name , c->toString( val ) );
return true;
}
-#elif defined( SM18 )
- if ( JS_InstanceOf( c->_context , o , &js_RegExpClass , 0 ) ){
+#elif defined( SM18 )
+ if ( JS_InstanceOf( c->_context , o , &js_RegExpClass , 0 ) ) {
c->appendRegex( b , name , c->toString( val ) );
return true;
}
#endif
-
+
return false;
}
- bool isDate( JSContext * cx , JSObject * o ){
+ bool isDate( JSContext * cx , JSObject * o ) {
#if defined( SM16 ) || defined( MOZJS ) || defined( XULRUNNER )
return js_DateGetMsecSinceEpoch( cx , o ) != 0;
#else
return JS_InstanceOf( cx , o, &js_DateClass, 0 );
#endif
}
-
+
}
diff --git a/scripting/utils.cpp b/scripting/utils.cpp
index ee01bb2..97eea10 100644
--- a/scripting/utils.cpp
+++ b/scripting/utils.cpp
@@ -23,31 +23,40 @@
namespace mongo {
- BSONObj jsmd5( const BSONObj &a ){
+ void installBenchmarkSystem( Scope& scope );
+
+ BSONObj jsmd5( const BSONObj &a ) {
uassert( 10261 , "js md5 needs a string" , a.firstElement().type() == String );
const char * s = a.firstElement().valuestrsafe();
-
+
md5digest d;
md5_state_t st;
md5_init(&st);
md5_append( &st , (const md5_byte_t*)s , strlen( s ) );
md5_finish(&st, d);
-
+
return BSON( "" << digestToString( d ) );
}
-
- BSONObj JSVersion( const BSONObj& args ){
+
+ BSONObj JSVersion( const BSONObj& args ) {
cout << "version: " << versionString << endl;
if ( strstr( versionString , "+" ) )
printGitVersion();
return BSONObj();
}
- void installGlobalUtils( Scope& scope ){
+
+ // ---------------------------------
+ // ---- installer --------
+ // ---------------------------------
+
+ void installGlobalUtils( Scope& scope ) {
scope.injectNative( "hex_md5" , jsmd5 );
scope.injectNative( "version" , JSVersion );
+
+ installBenchmarkSystem( scope );
}
}
-
+
diff --git a/scripting/v8_db.cpp b/scripting/v8_db.cpp
index e178875..4d12454 100644
--- a/scripting/v8_db.cpp
+++ b/scripting/v8_db.cpp
@@ -18,10 +18,11 @@
#include "v8_wrapper.h"
#include "v8_utils.h"
#include "v8_db.h"
-#include "engine.h"
+#include "engine_v8.h"
#include "util/base64.h"
#include "util/text.h"
#include "../client/syncclusterconnection.h"
+#include "../s/d_logic.h"
#include <iostream>
using namespace std;
@@ -31,99 +32,118 @@ namespace mongo {
#define DDD(x)
- v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( bool local ){
- v8::Local<v8::FunctionTemplate> mongo = FunctionTemplate::New( local ? mongoConsLocal : mongoConsExternal );
+ v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( bool local ) {
+ v8::Local<v8::FunctionTemplate> mongo;
+ if ( local ) {
+ mongo = newV8Function< mongoConsLocal >();
+ }
+ else {
+ mongo = newV8Function< mongoConsExternal >();
+ }
mongo->InstanceTemplate()->SetInternalFieldCount( 1 );
-
+
v8::Local<v8::Template> proto = mongo->PrototypeTemplate();
- proto->Set( v8::String::New( "find" ) , FunctionTemplate::New( mongoFind ) );
- proto->Set( v8::String::New( "insert" ) , FunctionTemplate::New( mongoInsert ) );
- proto->Set( v8::String::New( "remove" ) , FunctionTemplate::New( mongoRemove ) );
- proto->Set( v8::String::New( "update" ) , FunctionTemplate::New( mongoUpdate ) );
+ 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 = FunctionTemplate::New( internalCursorCons );
+ Local<FunctionTemplate> ic = newV8Function< internalCursorCons >();
ic->InstanceTemplate()->SetInternalFieldCount( 1 );
- ic->PrototypeTemplate()->Set( v8::String::New("next") , FunctionTemplate::New( internalCursorNext ) );
- ic->PrototypeTemplate()->Set( v8::String::New("hasNext") , FunctionTemplate::New( internalCursorHasNext ) );
- ic->PrototypeTemplate()->Set( v8::String::New("objsLeftInBatch") , FunctionTemplate::New( internalCursorObjsLeftInBatch ) );
+ 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 );
-
+
return mongo;
}
v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate() {
- v8::Local<v8::FunctionTemplate> numberLong = FunctionTemplate::New( numberLongInit );
+ v8::Local<v8::FunctionTemplate> numberLong = newV8Function< numberLongInit >();
v8::Local<v8::Template> proto = numberLong->PrototypeTemplate();
-
- proto->Set( v8::String::New( "valueOf" ) , FunctionTemplate::New( numberLongValueOf ) );
- proto->Set( v8::String::New( "toNumber" ) , FunctionTemplate::New( numberLongToNumber ) );
- proto->Set( v8::String::New( "toString" ) , FunctionTemplate::New( numberLongToString ) );
-
+
+ proto->Set( v8::String::New( "valueOf" ) , newV8Function< numberLongValueOf >() );
+ proto->Set( v8::String::New( "toNumber" ) , newV8Function< numberLongToNumber >() );
+ proto->Set( v8::String::New( "toString" ) , newV8Function< numberLongToString >() );
+
return numberLong;
}
v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate() {
- v8::Local<v8::FunctionTemplate> binData = FunctionTemplate::New( binDataInit );
+ v8::Local<v8::FunctionTemplate> binData = newV8Function< binDataInit >();
v8::Local<v8::Template> proto = binData->PrototypeTemplate();
-
- proto->Set( v8::String::New( "toString" ) , FunctionTemplate::New( binDataToString ) );
-
+
+ proto->Set( v8::String::New( "toString" ) , newV8Function< binDataToString >() );
+
return binData;
- }
-
- void installDBTypes( Handle<ObjectTemplate>& global ){
- v8::Local<v8::FunctionTemplate> db = FunctionTemplate::New( dbInit );
+ }
+
+ 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;
+ }
+
+
+ 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 = FunctionTemplate::New( collectionInit );
+
+ v8::Local<v8::FunctionTemplate> dbCollection = newV8Function< collectionInit >();
dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
global->Set(v8::String::New("DBCollection") , dbCollection );
- v8::Local<v8::FunctionTemplate> dbQuery = FunctionTemplate::New( dbQueryInit );
+ v8::Local<v8::FunctionTemplate> dbQuery = newV8Function< dbQueryInit >();
dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess );
global->Set(v8::String::New("DBQuery") , dbQuery );
- global->Set( v8::String::New("ObjectId") , FunctionTemplate::New( objectIdInit ) );
+ global->Set( v8::String::New("ObjectId") , newV8Function< objectIdInit >() );
- global->Set( v8::String::New("DBRef") , FunctionTemplate::New( dbRefInit ) );
+ global->Set( v8::String::New("DBRef") , newV8Function< dbRefInit >() );
- global->Set( v8::String::New("DBPointer") , FunctionTemplate::New( dbPointerInit ) );
+ global->Set( v8::String::New("DBPointer") , newV8Function< dbPointerInit >() );
global->Set( v8::String::New("BinData") , getBinDataFunctionTemplate() );
global->Set( v8::String::New("NumberLong") , getNumberLongFunctionTemplate() );
+ global->Set( v8::String::New("Timestamp") , getTimestampFunctionTemplate() );
}
- void installDBTypes( Handle<v8::Object>& global ){
- v8::Local<v8::FunctionTemplate> db = FunctionTemplate::New( dbInit );
+ void installDBTypes( Handle<v8::Object>& global ) {
+ v8::Local<v8::FunctionTemplate> db = newV8Function< dbInit >();
db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
global->Set(v8::String::New("DB") , db->GetFunction() );
-
- v8::Local<v8::FunctionTemplate> dbCollection = FunctionTemplate::New( collectionInit );
+
+ v8::Local<v8::FunctionTemplate> dbCollection = newV8Function< collectionInit >();
dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
global->Set(v8::String::New("DBCollection") , dbCollection->GetFunction() );
- v8::Local<v8::FunctionTemplate> dbQuery = FunctionTemplate::New( dbQueryInit );
+ v8::Local<v8::FunctionTemplate> dbQuery = newV8Function< dbQueryInit >();
dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess );
global->Set(v8::String::New("DBQuery") , dbQuery->GetFunction() );
- global->Set( v8::String::New("ObjectId") , FunctionTemplate::New( objectIdInit )->GetFunction() );
+ global->Set( v8::String::New("ObjectId") , newV8Function< objectIdInit >()->GetFunction() );
- global->Set( v8::String::New("DBRef") , FunctionTemplate::New( dbRefInit )->GetFunction() );
-
- global->Set( v8::String::New("DBPointer") , FunctionTemplate::New( dbPointerInit )->GetFunction() );
+ global->Set( v8::String::New("DBRef") , newV8Function< dbRefInit >()->GetFunction() );
+
+ global->Set( v8::String::New("DBPointer") , newV8Function< dbPointerInit >()->GetFunction() );
global->Set( v8::String::New("BinData") , getBinDataFunctionTemplate()->GetFunction() );
global->Set( v8::String::New("NumberLong") , getNumberLongFunctionTemplate()->GetFunction() );
+ global->Set( v8::String::New("Timestamp") , getTimestampFunctionTemplate()->GetFunction() );
+
BSONObjBuilder b;
b.appendMaxKey( "" );
b.appendMinKey( "" );
@@ -131,21 +151,21 @@ namespace mongo {
BSONObjIterator i( o );
global->Set( v8::String::New("MaxKey"), mongoToV8Element( i.next() ) );
global->Set( v8::String::New("MinKey"), mongoToV8Element( i.next() ) );
-
- global->Get( v8::String::New( "Object" ) )->ToObject()->Set( v8::String::New("bsonsize") , FunctionTemplate::New( bsonsize )->GetFunction() );
+
+ global->Get( v8::String::New( "Object" ) )->ToObject()->Set( v8::String::New("bsonsize") , newV8Function< bsonsize >()->GetFunction() );
}
- void destroyConnection( Persistent<Value> self, void* parameter){
+ void destroyConnection( Persistent<Value> self, void* parameter) {
delete static_cast<DBClientBase*>(parameter);
self.Dispose();
self.Clear();
}
- Handle<Value> mongoConsExternal(const Arguments& args){
+ Handle<Value> mongoConsExternal(const Arguments& args) {
char host[255];
-
- if ( args.Length() > 0 && args[0]->IsString() ){
+
+ if ( args.Length() > 0 && args[0]->IsString() ) {
assert( args[0]->ToString()->Utf8Length() < 250 );
args[0]->ToString()->WriteAscii( host );
}
@@ -157,30 +177,41 @@ namespace mongo {
ConnectionString cs = ConnectionString::parse( host , errmsg );
if ( ! cs.isValid() )
return v8::ThrowException( v8::String::New( errmsg.c_str() ) );
-
-
- DBClientWithCommands * conn = cs.connect( errmsg );
+
+
+ DBClientWithCommands * conn;
+ {
+ V8Unlock ul;
+ conn = cs.connect( errmsg );
+ }
if ( ! conn )
return v8::ThrowException( v8::String::New( errmsg.c_str() ) );
-
+
Persistent<v8::Object> self = Persistent<v8::Object>::New( args.Holder() );
self.MakeWeak( conn , destroyConnection );
- ScriptEngine::runConnectCallback( *conn );
+ {
+ V8Unlock ul;
+ ScriptEngine::runConnectCallback( *conn );
+ }
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 ) );
-
+
return v8::Undefined();
}
- Handle<Value> mongoConsLocal(const Arguments& args){
-
+ Handle<Value> mongoConsLocal(const Arguments& args) {
+
if ( args.Length() > 0 )
return v8::ThrowException( v8::String::New( "local Mongo constructor takes no args" ) );
- DBClientBase * conn = createDirectClient();
+ DBClientBase * conn;
+ {
+ V8Unlock ul;
+ conn = createDirectClient();
+ }
Persistent<v8::Object> self = Persistent<v8::Object>::New( args.This() );
self.MakeWeak( conn , destroyConnection );
@@ -189,7 +220,7 @@ 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( "EMBEDDED" ) );
-
+
return v8::Undefined();
}
@@ -197,12 +228,12 @@ namespace mongo {
// ---
#ifdef _WIN32
-#define GETNS char * ns = new char[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns );
+#define GETNS char * ns = new char[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns );
#else
-#define GETNS char ns[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns );
+#define GETNS char ns[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns );
#endif
- DBClientBase * getConnection( const Arguments& args ){
+ DBClientBase * getConnection( const Arguments& args ) {
Local<External> c = External::Cast( *(args.This()->GetInternalField( 0 )) );
DBClientBase * conn = (DBClientBase*)(c->Value());
assert( conn );
@@ -211,7 +242,7 @@ namespace mongo {
// ---- real methods
- void destroyCursor( Persistent<Value> self, void* parameter){
+ void destroyCursor( Persistent<Value> self, void* parameter) {
delete static_cast<mongo::DBClientCursor*>(parameter);
self.Dispose();
self.Clear();
@@ -224,60 +255,64 @@ namespace mongo {
3 - limit
4 - skip
*/
- Handle<Value> mongoFind(const Arguments& args){
+ Handle<Value> mongoFind(const Arguments& args) {
HandleScope handle_scope;
- jsassert( args.Length() == 6 , "find needs 6 args" );
+ jsassert( args.Length() == 7 , "find needs 7 args" );
jsassert( args[1]->IsObject() , "needs to be an object" );
DBClientBase * conn = getConnection( args );
GETNS;
BSONObj q = 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() );
-
+
Local<v8::Object> mongo = args.This();
Local<v8::Value> slaveOkVal = mongo->Get( v8::String::New( "slaveOk" ) );
jsassert( slaveOkVal->IsBoolean(), "slaveOk member invalid" );
bool slaveOk = slaveOkVal->BooleanValue();
-
+
try {
auto_ptr<mongo::DBClientCursor> cursor;
int nToReturn = (int)(args[3]->ToNumber()->Value());
int nToSkip = (int)(args[4]->ToNumber()->Value());
int batchSize = (int)(args[5]->ToNumber()->Value());
+ int options = (int)(args[6]->ToNumber()->Value());
{
- v8::Unlocker u;
- cursor = conn->query( ns, q , nToReturn , nToSkip , haveFields ? &fields : 0, slaveOk ? QueryOption_SlaveOk : 0 , batchSize );
+ V8Unlock u;
+ cursor = conn->query( ns, q , nToReturn , nToSkip , haveFields ? &fields : 0, options | ( slaveOk ? QueryOption_SlaveOk : 0 ) , batchSize );
}
v8::Function * cons = (v8::Function*)( *( mongo->Get( v8::String::New( "internalCursor" ) ) ) );
assert( cons );
-
+
Persistent<v8::Object> c = Persistent<v8::Object>::New( cons->NewInstance() );
c.MakeWeak( cursor.get() , destroyCursor );
-
+
c->SetInternalField( 0 , External::New( cursor.release() ) );
return handle_scope.Close(c);
}
- catch ( ... ){
- return v8::ThrowException( v8::String::New( "socket error on query" ) );
+ catch ( ... ) {
+ return v8::ThrowException( v8::String::New( "socket error on query" ) );
}
}
- v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args){
+ v8::Handle<v8::Value> mongoInsert(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() )
+ 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();
-
- if ( ! in->Has( v8::String::New( "_id" ) ) ){
+
+ if ( ! in->Has( v8::String::New( "_id" ) ) ) {
v8::Handle<v8::Value> argv[1];
in->Set( v8::String::New( "_id" ) , getObjectIdCons()->NewInstance( 0 , argv ) );
}
@@ -286,64 +321,70 @@ namespace mongo {
DDD( "want to save : " << o.jsonString() );
try {
- v8::Unlocker u;
+ V8Unlock u;
conn->insert( ns , o );
}
- catch ( ... ){
+ catch ( ... ) {
return v8::ThrowException( v8::String::New( "socket error on insert" ) );
}
-
+
return v8::Undefined();
}
- v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args){
+ v8::Handle<v8::Value> mongoRemove(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() )
+ 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 );
-
+
bool justOne = false;
- if ( args.Length() > 2 ){
+ if ( args.Length() > 2 ) {
justOne = args[2]->BooleanValue();
}
DDD( "want to remove : " << o.jsonString() );
try {
- v8::Unlocker u;
+ V8Unlock u;
conn->remove( ns , o , justOne );
}
- catch ( ... ){
+ catch ( ... ) {
return v8::ThrowException( v8::String::New( "socket error on remove" ) );
}
return v8::Undefined();
}
- v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args){
+ v8::Handle<v8::Value> mongoUpdate(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() )
+ return v8::ThrowException( v8::String::New( "js db in read only mode" ) );
DBClientBase * conn = getConnection( args );
GETNS;
-
+
v8::Handle<v8::Object> q = args[1]->ToObject();
v8::Handle<v8::Object> o = args[2]->ToObject();
-
+
bool upsert = args.Length() > 3 && args[3]->IsBoolean() && args[3]->ToBoolean()->Value();
- bool multi = args.Length() > 4 && args[4]->IsBoolean() && args[4]->ToBoolean()->Value();
-
+ bool multi = args.Length() > 4 && args[4]->IsBoolean() && args[4]->ToBoolean()->Value();
+
try {
BSONObj q1 = v8ToMongo( q );
BSONObj o1 = v8ToMongo( o );
- v8::Unlocker u;
+ V8Unlock u;
conn->update( ns , q1 , o1 , upsert, multi );
}
- catch ( ... ){
+ catch ( ... ) {
return v8::ThrowException( v8::String::New( "socket error on remove" ) );
}
@@ -355,48 +396,48 @@ namespace mongo {
// --- cursor ---
- mongo::DBClientCursor * getCursor( const Arguments& args ){
+ mongo::DBClientCursor * getCursor( const Arguments& args ) {
Local<External> c = External::Cast( *(args.This()->GetInternalField( 0 ) ) );
mongo::DBClientCursor * cursor = (mongo::DBClientCursor*)(c->Value());
return cursor;
}
- v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args){
+ v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args) {
return v8::Undefined();
}
- v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args){
+ v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args) {
mongo::DBClientCursor * cursor = getCursor( args );
if ( ! cursor )
return v8::Undefined();
BSONObj o;
{
- v8::Unlocker u;
+ V8Unlock u;
o = cursor->next();
}
return mongoToV8( o );
}
- v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args){
+ v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args) {
mongo::DBClientCursor * cursor = getCursor( args );
if ( ! cursor )
return Boolean::New( false );
bool ret;
{
- v8::Unlocker u;
+ V8Unlock u;
ret = cursor->more();
}
return Boolean::New( ret );
}
- v8::Handle<v8::Value> internalCursorObjsLeftInBatch(const v8::Arguments& args){
+ v8::Handle<v8::Value> internalCursorObjsLeftInBatch(const v8::Arguments& args) {
mongo::DBClientCursor * cursor = getCursor( args );
if ( ! cursor )
return v8::Number::New( (double) 0 );
int ret;
{
- v8::Unlocker u;
+ V8Unlock u;
ret = cursor->objsLeftInBatch();
}
return v8::Number::New( (double) ret );
@@ -405,7 +446,7 @@ namespace mongo {
// --- DB ----
- v8::Handle<v8::Value> dbInit(const v8::Arguments& args){
+ v8::Handle<v8::Value> dbInit(const v8::Arguments& args) {
assert( args.Length() == 2 );
args.This()->Set( v8::String::New( "_mongo" ) , args[0] );
@@ -417,26 +458,29 @@ namespace mongo {
return v8::Undefined();
}
- v8::Handle<v8::Value> collectionInit( const v8::Arguments& args ){
+ v8::Handle<v8::Value> collectionInit( 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] );
-
+
+ if ( haveLocalShardingInfo( toSTLString( args[3] ) ) )
+ return v8::ThrowException( v8::String::New( "can't use sharded collection from db.eval" ) );
+
for ( int i=0; i<args.Length(); i++ )
assert( ! args[i]->IsUndefined() );
return v8::Undefined();
}
- v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args ){
-
+ v8::Handle<v8::Value> dbQueryInit( 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] );
@@ -444,46 +488,52 @@ namespace mongo {
if ( args.Length() > 4 && args[4]->IsObject() )
t->Set( v8::String::New( "_query" ) , args[4] );
- else
+ else
t->Set( v8::String::New( "_query" ) , v8::Object::New() );
-
+
if ( args.Length() > 5 && args[5]->IsObject() )
t->Set( v8::String::New( "_fields" ) , args[5] );
else
t->Set( v8::String::New( "_fields" ) , v8::Null() );
-
+
if ( args.Length() > 6 && args[6]->IsNumber() )
t->Set( v8::String::New( "_limit" ) , args[6] );
- else
+ else
t->Set( v8::String::New( "_limit" ) , Number::New( 0 ) );
if ( args.Length() > 7 && args[7]->IsNumber() )
t->Set( v8::String::New( "_skip" ) , args[7] );
- else
+ else
t->Set( v8::String::New( "_skip" ) , Number::New( 0 ) );
if ( args.Length() > 8 && args[8]->IsNumber() )
- t->Set( v8::String::New( "_batchSize" ) , args[7] );
- else
+ t->Set( v8::String::New( "_batchSize" ) , args[8] );
+ else
t->Set( v8::String::New( "_batchSize" ) , Number::New( 0 ) );
-
+
+ if ( args.Length() > 9 && args[9]->IsNumber() )
+ t->Set( v8::String::New( "_options" ) , args[9] );
+ else
+ t->Set( v8::String::New( "_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) );
-
+
return v8::Undefined();
}
v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info) {
DDD( "collectionFallback [" << name << "]" );
-
+
v8::Handle<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get( name );
if ( ! real->IsUndefined() )
return real;
-
+
string sname = toSTLString( name );
- if ( sname[0] == '_' ){
+ if ( sname[0] == '_' ) {
if ( ! ( info.This()->HasRealNamedProperty( name ) ) )
return v8::Undefined();
return info.This()->GetRealNamedPropertyInPrototypeChain( name );
@@ -499,7 +549,7 @@ namespace mongo {
return f->Call( info.This() , 1 , argv );
}
- v8::Handle<v8::Value> dbQueryIndexAccess( unsigned int index , const v8::AccessorInfo& info ){
+ v8::Handle<v8::Value> dbQueryIndexAccess( unsigned int index , const v8::AccessorInfo& info ) {
v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get( v8::String::New( "arrayAccess" ) );
assert( arrayAccess->IsFunction() );
@@ -507,35 +557,36 @@ namespace mongo {
v8::Handle<v8::Value> argv[1];
argv[0] = v8::Number::New( index );
- return f->Call( info.This() , 1 , argv );
+ return f->Call( info.This() , 1 , argv );
}
- v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ){
+ v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ) {
v8::Handle<v8::Object> it = args.This();
-
- if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
v8::Function * f = getObjectIdCons();
it = f->NewInstance();
}
-
+
OID oid;
-
- if ( args.Length() == 0 ){
+
+ if ( args.Length() == 0 ) {
oid.init();
}
else {
string s = toSTLString( args[0] );
try {
Scope::validateObjectIdString( s );
- } catch ( const MsgAssertionException &m ) {
+ }
+ catch ( const MsgAssertionException &m ) {
string error = m.toString();
return v8::ThrowException( v8::String::New( error.c_str() ) );
- }
+ }
oid.init( s );
- }
+ }
it->Set( v8::String::New( "str" ) , v8::String::New( oid.str().c_str() ) );
-
+
return it;
}
@@ -547,7 +598,7 @@ namespace mongo {
v8::Handle<v8::Object> it = args.This();
- if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
v8::Function* f = getNamedCons( "DBRef" );
it = f->NewInstance();
}
@@ -561,74 +612,98 @@ namespace mongo {
}
v8::Handle<v8::Value> dbPointerInit( const v8::Arguments& args ) {
-
+
if (args.Length() != 2) {
return v8::ThrowException( v8::String::New( "DBPointer needs 2 arguments" ) );
}
-
+
v8::Handle<v8::Object> it = args.This();
-
- if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
v8::Function* f = 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 ) );
-
+
+ return it;
+ }
+
+ v8::Handle<v8::Value> dbTimestampInit( 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 ) );
+ }
+ else if ( args.Length() == 2 ) {
+ it->Set( v8::String::New( "t" ) , args[0] );
+ it->Set( v8::String::New( "i" ) , args[1] );
+ }
+ else {
+ return v8::ThrowException( v8::String::New( "Timestamp needs 0 or 2 arguments" ) );
+ }
+
+ it->SetInternalField( 0, v8::Uint32::New( Timestamp ) );
+
return it;
}
+
v8::Handle<v8::Value> binDataInit( const v8::Arguments& args ) {
v8::Handle<v8::Object> it = args.This();
-
+
// 3 args: len, type, data
if (args.Length() == 3) {
-
- if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
v8::Function* f = 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
- } else if ( args.Length() == 2 ) {
-
- if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+ // 2 args: type, base64 string
+ }
+ else if ( args.Length() == 2 ) {
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
v8::Function* f = 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 ) );
-
- } else {
+ it->SetHiddenValue( v8::String::New( "__BinData" ), v8::Number::New( 1 ) );
+
+ }
+ else {
return v8::ThrowException( v8::String::New( "BinData needs 3 arguments" ) );
}
return it;
}
-
+
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::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" ) ) );
-
+
stringstream ss;
ss << "BinData(" << type << ",\"";
base64::encode( ss, *data, len );
@@ -638,49 +713,54 @@ namespace mongo {
}
v8::Handle<v8::Value> numberLongInit( 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" ) );
}
-
+
v8::Handle<v8::Object> it = args.This();
-
- if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) {
v8::Function* f = getNamedCons( "NumberLong" );
it = f->NewInstance();
}
if ( args.Length() == 0 ) {
it->Set( v8::String::New( "floatApprox" ), v8::Number::New( 0 ) );
- } else if ( args.Length() == 1 ) {
+ }
+ else if ( args.Length() == 1 ) {
if ( args[ 0 ]->IsNumber() ) {
- it->Set( v8::String::New( "floatApprox" ), args[ 0 ] );
- } else {
+ it->Set( v8::String::New( "floatApprox" ), args[ 0 ] );
+ }
+ else {
v8::String::Utf8Value data( args[ 0 ] );
string num = *data;
const char *numStr = num.c_str();
long long n;
try {
n = parseLL( numStr );
- } catch ( const AssertionException & ) {
+ }
+ catch ( const AssertionException & ) {
return v8::ThrowException( v8::String::New( "could not convert string to long long" ) );
}
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 ) ) );
- } else {
+ }
+ 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) ) );
- }
+ }
}
- } else {
+ }
+ 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->SetHiddenValue( v8::String::New( "__NumberLong" ), v8::Number::New( 1 ) );
-
+
return it;
}
@@ -688,21 +768,21 @@ namespace mongo {
if ( !it->Has( v8::String::New( "top" ) ) )
return (long long)( it->Get( v8::String::New( "floatApprox" ) )->NumberValue() );
return
- (long long)
- ( (unsigned long long)( it->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) +
- (unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() );
+ (long long)
+ ( (unsigned long long)( it->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) +
+ (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::Object> it = args.This();
-
+
long long val = numberLongVal( it );
-
+
return v8::Number::New( double( val ) );
}
@@ -711,13 +791,13 @@ namespace mongo {
}
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::Object> it = args.This();
-
+
stringstream ss;
long long val = numberLongVal( it );
const long long limit = 2LL << 30;
@@ -730,13 +810,65 @@ namespace mongo {
string ret = ss.str();
return v8::String::New( ret.c_str() );
}
-
+
v8::Handle<v8::Value> bsonsize( const v8::Arguments& args ) {
-
- if (args.Length() != 1 || !args[ 0 ]->IsObject()) {
- return v8::ThrowException( v8::String::New( "bonsisze needs 1 object" ) );
- }
+
+ if ( args.Length() != 1 )
+ return v8::ThrowException( v8::String::New( "bonsisze 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::Number::New( v8ToMongo( args[ 0 ]->ToObject() ).objsize() );
}
+
+ // to be called with v8 mutex
+ void enableV8Interrupt() {
+ if ( globalScriptEngine->haveGetInterruptSpecCallback() ) {
+ __interruptSpecToThreadId[ globalScriptEngine->getInterruptSpec() ] = v8::V8::GetCurrentThreadId();
+ }
+ }
+
+ // to be called with v8 mutex
+ void disableV8Interrupt() {
+ if ( globalScriptEngine->haveGetInterruptSpecCallback() ) {
+ __interruptSpecToThreadId.erase( globalScriptEngine->getInterruptSpec() );
+ }
+ }
+
+ namespace v8Locks {
+ boost::mutex& __v8Mutex = *( new boost::mutex );
+ ThreadLocalValue< bool > __locked;
+
+ RecursiveLock::RecursiveLock() : _unlock() {
+ if ( !__locked.get() ) {
+ __v8Mutex.lock();
+ __locked.set( true );
+ _unlock = true;
+ }
+ }
+ RecursiveLock::~RecursiveLock() {
+ if ( _unlock ) {
+ __v8Mutex.unlock();
+ __locked.set( false );
+ }
+ }
+
+ RecursiveUnlock::RecursiveUnlock() : _lock() {
+ if ( __locked.get() ) {
+ __v8Mutex.unlock();
+ __locked.set( false );
+ _lock = true;
+ }
+ }
+ RecursiveUnlock::~RecursiveUnlock() {
+ if ( _lock ) {
+ __v8Mutex.lock();
+ __locked.set( true );
+ }
+ }
+ } // namespace v8Locks
}
diff --git a/scripting/v8_db.h b/scripting/v8_db.h
index 4bebb32..7dbca92 100644
--- a/scripting/v8_db.h
+++ b/scripting/v8_db.h
@@ -22,43 +22,45 @@
#include <cstdio>
#include <cstdlib>
+#include "engine.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 );
-
+
// 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> 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> 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);
-
+
// 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> 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> binDataInit( const v8::Arguments& args );
v8::Handle<v8::Value> binDataToString( const v8::Arguments& args );
@@ -67,12 +69,82 @@ namespace mongo {
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> dbQueryInit( 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 );
+ // 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 5a07a80..171ced5 100644
--- a/scripting/v8_utils.cpp
+++ b/scripting/v8_utils.cpp
@@ -16,6 +16,7 @@
*/
#include "v8_utils.h"
+#include "v8_db.h"
#include <iostream>
#include <map>
#include <sstream>
@@ -38,7 +39,8 @@ namespace mongo {
HandleScope handle_scope;
if (first) {
first = false;
- } else {
+ }
+ else {
printf(" ");
}
v8::String::Utf8Value str(args[i]);
@@ -48,66 +50,66 @@ namespace mongo {
return v8::Undefined();
}
- std::string toSTLString( const Handle<v8::Value> & o ){
- v8::String::Utf8Value str(o);
+ std::string toSTLString( const Handle<v8::Value> & o ) {
+ v8::String::Utf8Value str(o);
const char * foo = *str;
std::string s(foo);
return s;
}
- std::string toSTLString( const v8::TryCatch * try_catch ){
-
+ std::string toSTLString( const v8::TryCatch * try_catch ) {
+
stringstream ss;
-
+
//while ( try_catch ){ // disabled for v8 bleeding edge
-
- v8::String::Utf8Value exception(try_catch->Exception());
- Handle<v8::Message> message = try_catch->Message();
-
- if (message.IsEmpty()) {
- ss << *exception << endl;
- }
- else {
-
- v8::String::Utf8Value filename(message->GetScriptResourceName());
- int linenum = message->GetLineNumber();
- ss << *filename << ":" << linenum << " " << *exception << endl;
-
- v8::String::Utf8Value sourceline(message->GetSourceLine());
- ss << *sourceline << endl;
-
- int start = message->GetStartColumn();
- for (int i = 0; i < start; i++)
- ss << " ";
-
- int end = message->GetEndColumn();
- for (int i = start; i < end; i++)
- ss << "^";
-
- ss << endl;
- }
-
- //try_catch = try_catch->next_;
+
+ v8::String::Utf8Value exception(try_catch->Exception());
+ Handle<v8::Message> message = try_catch->Message();
+
+ if (message.IsEmpty()) {
+ ss << *exception << endl;
+ }
+ else {
+
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ int linenum = message->GetLineNumber();
+ ss << *filename << ":" << linenum << " " << *exception << endl;
+
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ ss << *sourceline << endl;
+
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++)
+ ss << " ";
+
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++)
+ ss << "^";
+
+ ss << endl;
+ }
+
+ //try_catch = try_catch->next_;
//}
-
+
return ss.str();
}
- std::ostream& operator<<( std::ostream &s, const Handle<v8::Value> & o ){
- v8::String::Utf8Value str(o);
+ std::ostream& operator<<( std::ostream &s, const Handle<v8::Value> & o ) {
+ v8::String::Utf8Value str(o);
s << *str;
return s;
}
- std::ostream& operator<<( std::ostream &s, const v8::TryCatch * try_catch ){
+ std::ostream& operator<<( std::ostream &s, const v8::TryCatch * try_catch ) {
HandleScope handle_scope;
v8::String::Utf8Value exception(try_catch->Exception());
Handle<v8::Message> message = try_catch->Message();
-
+
if (message.IsEmpty()) {
s << *exception << endl;
- }
+ }
else {
v8::String::Utf8Value filename(message->GetScriptResourceName());
@@ -126,7 +128,7 @@ namespace mongo {
cout << "^";
cout << endl;
- }
+ }
//if ( try_catch->next_ ) // disabled for v8 bleeding edge
// s << try_catch->next_;
@@ -143,9 +145,9 @@ namespace mongo {
void ReportException(v8::TryCatch* try_catch) {
cout << try_catch << endl;
}
-
+
Handle< Context > baseContext_;
-
+
class JSThreadConfig {
public:
JSThreadConfig( const Arguments &args, bool newScope = false ) : started_(), done_(), newScope_( newScope ) {
@@ -170,7 +172,7 @@ namespace mongo {
}
void join() {
jsassert( started_ && !done_, "Thread not running" );
- Unlocker u;
+ V8Unlock u;
thread_->join();
done_ = true;
}
@@ -184,7 +186,7 @@ namespace mongo {
public:
JSThread( JSThreadConfig &config ) : config_( config ) {}
void operator()() {
- Locker l;
+ V8Lock l;
HandleScope handle_scope;
Handle< Context > context;
Handle< v8::Function > fun;
@@ -198,7 +200,8 @@ namespace mongo {
string fCode = toSTLString( config_.f_->ToString() );
Context::Scope context_scope( context );
fun = scope->__createFunction( fCode.c_str() );
- } else {
+ }
+ else {
context = baseContext_;
Context::Scope context_scope( context );
fun = config_.f_;
@@ -220,7 +223,7 @@ namespace mongo {
private:
JSThreadConfig &config_;
};
-
+
bool started_;
bool done_;
bool newScope_;
@@ -229,7 +232,7 @@ namespace mongo {
auto_ptr< boost::thread > thread_;
Persistent< Value > returnData_;
};
-
+
Handle< Value > ThreadInit( const Arguments &args ) {
Handle<v8::Object> it = args.This();
// NOTE I believe the passed JSThreadConfig will never be freed. If this
@@ -238,7 +241,7 @@ namespace mongo {
it->SetHiddenValue( v8::String::New( "_JSThreadConfig" ), External::New( new JSThreadConfig( args ) ) );
return v8::Undefined();
}
-
+
Handle< Value > ScopedThreadInit( const Arguments &args ) {
Handle<v8::Object> it = args.This();
// NOTE I believe the passed JSThreadConfig will never be freed. If this
@@ -253,17 +256,17 @@ namespace mongo {
JSThreadConfig *config = (JSThreadConfig *)( c->Value() );
return config;
}
-
+
Handle< Value > ThreadStart( const Arguments &args ) {
thisConfig( args )->start();
return v8::Undefined();
}
-
+
Handle< Value > ThreadJoin( const Arguments &args ) {
thisConfig( args )->join();
return v8::Undefined();
}
-
+
Handle< Value > ThreadReturnData( const Arguments &args ) {
HandleScope handle_scope;
return handle_scope.Close( thisConfig( args )->returnData() );
@@ -272,39 +275,39 @@ namespace mongo {
Handle< Value > ThreadInject( 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" ) , FunctionTemplate::New( ThreadInit )->GetFunction() );
- o->Set( v8::String::New( "start" ) , FunctionTemplate::New( ThreadStart )->GetFunction() );
- o->Set( v8::String::New( "join" ) , FunctionTemplate::New( ThreadJoin )->GetFunction() );
- o->Set( v8::String::New( "returnData" ) , FunctionTemplate::New( ThreadReturnData )->GetFunction() );
-
- return v8::Undefined();
+
+ 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() );
+
+ return v8::Undefined();
}
Handle< Value > ScopedThreadInject( 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" ) , FunctionTemplate::New( ScopedThreadInit )->GetFunction() );
+
+ o->Set( v8::String::New( "init" ) , newV8Function< ScopedThreadInit >()->GetFunction() );
// inheritance takes care of other member functions
-
+
return v8::Undefined();
}
-
+
void installFork( 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" ), FunctionTemplate::New( ThreadInject )->GetFunction() );
- global->Set( v8::String::New( "_scopedThreadInject" ), FunctionTemplate::New( ScopedThreadInject )->GetFunction() );
+ global->Set( v8::String::New( "_threadInject" ), newV8Function< ThreadInject >()->GetFunction() );
+ global->Set( v8::String::New( "_scopedThreadInject" ), newV8Function< ScopedThreadInject >()->GetFunction() );
}
Handle<v8::Value> GCV8(const Arguments& args) {
- Locker l;
- while( V8::IdleNotification() );
+ V8Lock l;
+ while( !V8::IdleNotification() );
return v8::Undefined();
}
diff --git a/scripting/v8_utils.h b/scripting/v8_utils.h
index bc4b524..40662d2 100644
--- a/scripting/v8_utils.h
+++ b/scripting/v8_utils.h
@@ -32,9 +32,9 @@ namespace mongo {
v8::Handle<v8::Value> GCV8(const v8::Arguments& args);
void ReportException(v8::TryCatch* handler);
-
+
#define jsassert(x,msg) assert(x)
-
+
std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o );
std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatch> * try_catch );
diff --git a/scripting/v8_wrapper.cpp b/scripting/v8_wrapper.cpp
index 0e71c9a..ff67e8c 100644
--- a/scripting/v8_wrapper.cpp
+++ b/scripting/v8_wrapper.cpp
@@ -17,6 +17,7 @@
#include "v8_wrapper.h"
#include "v8_utils.h"
+#include "v8_db.h"
#include <iostream>
@@ -38,17 +39,17 @@ namespace mongo {
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;
@@ -57,15 +58,15 @@ namespace mongo {
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 );
+ return idCons->NewInstance( 1 , argv );
}
-
- Local<v8::Object> mongoToV8( const BSONObj& m , bool array, bool readOnly ){
+
+ Local<v8::Object> mongoToV8( const BSONObj& m , bool array, bool readOnly ) {
Local<v8::Object> o;
@@ -86,13 +87,16 @@ namespace mongo {
if ( !o.IsEmpty() ) {
readOnly = false;
- } else if ( array ) {
+ }
+ else if ( array ) {
// NOTE Looks like it's impossible to add interceptors to v8 arrays.
readOnly = false;
o = v8::Array::New();
- } else if ( !readOnly ) {
+ }
+ else if ( !readOnly ) {
o = v8::Object::New();
- } else {
+ }
+ 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
@@ -115,15 +119,15 @@ namespace mongo {
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() ){
+
+ switch ( f.type() ) {
case mongo::Code:
o->Set( v8::String::New( f.fieldName() ), newFunction( f.valuestr() ) );
@@ -134,31 +138,31 @@ namespace mongo {
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
o->Set( v8::String::New( f.fieldName() ), newFunction( f.codeWScopeCode() ) );
break;
-
- case mongo::String:
+
+ 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 ) );
+ 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;
@@ -166,29 +170,29 @@ namespace mongo {
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 );
@@ -197,36 +201,37 @@ namespace mongo {
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];
+ 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 {
+ }
+ 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;
+ break;
}
-
+
case mongo::MinKey: {
Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
sub->Set( v8::String::New( "$MinKey" ), v8::Boolean::New( true ) );
@@ -234,7 +239,7 @@ namespace mongo {
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 ) );
@@ -251,7 +256,7 @@ namespace mongo {
o->Set( v8::String::New( f.fieldName() ), dbPointer->NewInstance(2, argv) );
break;
}
-
+
default:
cout << "can't handle type: ";
cout << f.type() << " ";
@@ -259,14 +264,14 @@ namespace mongo {
cout << endl;
break;
}
-
+
}
if ( readOnly ) {
readOnlyObjects->SetNamedPropertyHandler( 0, NamedReadOnlySet, 0, NamedReadOnlyDelete );
- readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete );
+ readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete );
}
-
+
return o;
}
@@ -274,56 +279,56 @@ namespace mongo {
Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New();
internalFieldObjects->SetInternalFieldCount( 1 );
- switch ( f.type() ){
+ 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:
+
+ 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::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 );
@@ -331,26 +336,27 @@ namespace mongo {
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];
+ v8::Handle<v8::Value> argv[1];
argv[0] = v8::Number::New( (double)(long long)( val ) );
return numberLong->NewInstance( 1, argv );
- } else {
+ }
+ else {
v8::Handle<v8::Value> argv[3];
argv[0] = v8::Number::New( (double)(long long)( val ) );
argv[1] = v8::Integer::New( val >> 32 );
@@ -358,21 +364,21 @@ namespace mongo {
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];
@@ -380,83 +386,83 @@ namespace mongo {
argv[1] = newId( f.dbrefOID() );
return dbPointer->NewInstance(2, argv);
}
-
+
default:
cout << "can't handle type: ";
- cout << f.type() << " ";
- cout << f.toString();
- cout << endl;
+ 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() ){
+ 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 ).c_str() );
+
+ if ( value->IsFunction() ) {
+ b.appendCode( sname , toSTLString( value ) );
return;
}
-
- if ( value->IsNumber() ){
+
+ if ( value->IsNumber() ) {
if ( value->IsInt32() )
b.append( sname, int( value->ToInt32()->Value() ) );
else
b.append( sname , value->ToNumber()->Value() );
return;
}
-
- if ( value->IsArray() ){
+
+ if ( value->IsArray() ) {
BSONObj sub = v8ToMongo( value->ToObject() , depth );
b.appendArray( sname , sub );
return;
}
-
- if ( value->IsDate() ){
+
+ if ( value->IsDate() ) {
b.appendDate( sname , Date_t( (unsigned long long)(v8::Date::Cast( *value )->NumberValue())) );
return;
}
if ( value->IsExternal() )
return;
-
- if ( value->IsObject() ){
+
+ 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 );
+ 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] == '/' ){
+ 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.c_str() , o.c_str() );
+ b.appendRegex( sname , r , o );
}
else if ( value->ToObject()->GetPrototype()->IsObject() &&
- value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ){
+ value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ) {
OID oid;
oid.init( toSTLString( value ) );
b.appendOID( sname , &oid );
@@ -469,19 +475,20 @@ namespace mongo {
long long val;
if ( !it->Has( v8::String::New( "top" ) ) ) {
val = (long long)( it->Get( v8::String::New( "floatApprox" ) )->NumberValue() );
- } else {
+ }
+ 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() );
+ ( (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.c_str(), oid );
+ 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();
@@ -489,27 +496,28 @@ namespace mongo {
const char *dataArray = *data;
assert( data.length() == len );
b.appendBinData( sname,
- len,
- mongo::BinDataType( obj->Get( v8::String::New( "type" ) )->ToInt32()->Value() ),
- dataArray );
- } else {
+ 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() ){
+
+ if ( value->IsBoolean() ) {
b.appendBool( sname , value->ToBoolean()->Value() );
return;
}
-
- else if ( value->IsUndefined() ){
+
+ else if ( value->IsUndefined() ) {
b.appendUndefined( sname );
return;
}
-
- else if ( value->IsNull() ){
+
+ else if ( value->IsNull() ) {
b.appendNull( sname );
return;
}
@@ -517,26 +525,26 @@ namespace mongo {
cout << "don't know how to convert to mongo field [" << name << "]\t" << value << endl;
}
- BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth ){
+ BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth ) {
BSONObjBuilder b;
-
- if ( depth == 0 ){
+
+ if ( depth == 0 ) {
v8::Handle<v8::String> idName = v8::String::New( "_id" );
- if ( o->HasRealNamedProperty( idName ) ){
+ 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++ ){
+ 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 ) )
+ 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;
@@ -553,15 +561,15 @@ namespace mongo {
WrapperHolder( const BSONObj * o , bool readOnly , bool iDelete )
: _o(o), _readOnly( readOnly ), _iDelete( iDelete ) {
}
-
- ~WrapperHolder(){
- if ( _o && _iDelete ){
+
+ ~WrapperHolder() {
+ if ( _o && _iDelete ) {
delete _o;
}
_o = 0;
}
- v8::Handle<v8::Value> get( v8::Local<v8::String> name ){
+ v8::Handle<v8::Value> get( v8::Local<v8::String> name ) {
const string& s = toSTLString( name );
const BSONElement& e = _o->getField( s );
return mongoToV8Element(e);
@@ -572,13 +580,13 @@ namespace mongo {
bool _iDelete;
};
- WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ){
+ WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ) {
return new WrapperHolder( o , readOnly , iDelete );
}
#define WRAPPER_STRING (v8::String::New( "_wrapper" ) )
- WrapperHolder * getWrapper( v8::Handle<v8::Object> o ){
+ WrapperHolder * getWrapper( v8::Handle<v8::Object> o ) {
Handle<v8::Value> t = o->GetRealNamedProperty( WRAPPER_STRING );
assert( t->IsExternal() );
Local<External> c = External::Cast( *t );
@@ -588,32 +596,32 @@ namespace mongo {
}
- Handle<Value> wrapperCons(const Arguments& args){
+ Handle<Value> wrapperCons(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] );
-
+
return v8::Undefined();
}
- v8::Handle<v8::Value> wrapperGetHandler( v8::Local<v8::String> name, const v8::AccessorInfo &info){
+ v8::Handle<v8::Value> wrapperGetHandler( v8::Local<v8::String> name, const v8::AccessorInfo &info) {
return getWrapper( info.This() )->get( name );
}
- v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate(){
- v8::Local<v8::FunctionTemplate> t = FunctionTemplate::New( wrapperCons );
+ v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate() {
+ v8::Local<v8::FunctionTemplate> t = newV8Function< wrapperCons >();
t->InstanceTemplate()->SetNamedPropertyHandler( wrapperGetHandler );
return t;
}
// --- random utils ----
- v8::Function * getNamedCons( const char * name ){
+ v8::Function * getNamedCons( const char * name ) {
return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( v8::String::New( name ) ) ) );
}
- v8::Function * getObjectIdCons(){
+ v8::Function * getObjectIdCons() {
return getNamedCons( "ObjectId" );
}
diff --git a/scripting/v8_wrapper.h b/scripting/v8_wrapper.h
index 838aaf4..e0b79e3 100644
--- a/scripting/v8_wrapper.h
+++ b/scripting/v8_wrapper.h
@@ -28,10 +28,10 @@ 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 ,
+ 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();