summaryrefslogtreecommitdiff
path: root/scripting
diff options
context:
space:
mode:
authorAntonin Kral <a.kral@bobek.cz>2010-08-11 12:38:57 +0200
committerAntonin Kral <a.kral@bobek.cz>2010-08-11 12:38:57 +0200
commit7645618fd3914cb8a20561625913c20d49504a49 (patch)
tree8370f846f58f6d71165b7a0e2eda04648584ec76 /scripting
parent68c73c3c7608b4c87f07440dc3232801720b1168 (diff)
downloadmongodb-7645618fd3914cb8a20561625913c20d49504a49.tar.gz
Imported Upstream version 1.6.0
Diffstat (limited to 'scripting')
-rw-r--r--scripting/engine.cpp24
-rw-r--r--scripting/engine.h4
-rw-r--r--scripting/engine_java.cpp2
-rw-r--r--scripting/engine_java.h3
-rw-r--r--scripting/engine_spidermonkey.cpp237
-rw-r--r--scripting/engine_spidermonkey.h2
-rw-r--r--scripting/engine_v8.cpp10
-rw-r--r--scripting/sm_db.cpp254
-rw-r--r--scripting/utils.cpp3
-rw-r--r--scripting/v8_db.cpp148
-rw-r--r--scripting/v8_db.h1
-rw-r--r--scripting/v8_utils.cpp7
-rw-r--r--scripting/v8_utils.h1
-rw-r--r--scripting/v8_wrapper.cpp40
14 files changed, 524 insertions, 212 deletions
diff --git a/scripting/engine.cpp b/scripting/engine.cpp
index cc245b6..9e20a3a 100644
--- a/scripting/engine.cpp
+++ b/scripting/engine.cpp
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-#include "stdafx.h"
+#include "pch.h"
#include "engine.h"
#include "../util/file.h"
#include "../client/dbclient.h"
@@ -73,6 +73,9 @@ namespace mongo {
// TODO: make signed
builder.appendDate( fieldName , Date_t((unsigned long long)getNumber( scopeName )) );
break;
+ case Code:
+ builder.appendCode( fieldName , getString( scopeName ).c_str() );
+ break;
default:
stringstream temp;
temp << "can't append type from:";
@@ -93,7 +96,7 @@ namespace mongo {
path p( filename );
if ( ! exists( p ) ){
- cout << "file [" << filename << "] doesn't exist" << endl;
+ log() << "file [" << filename << "] doesn't exist" << endl;
if ( assertOnError )
assert( 0 );
return false;
@@ -113,7 +116,7 @@ namespace mongo {
}
if (empty){
- cout << "directory [" << filename << "] doesn't have any *.js files" << endl;
+ log() << "directory [" << filename << "] doesn't have any *.js files" << endl;
if ( assertOnError )
assert( 0 );
return false;
@@ -167,6 +170,7 @@ namespace mongo {
static DBClientBase * db = createDirectClient();
auto_ptr<DBClientCursor> c = db->query( coll , Query() );
+ assert( c.get() );
set<string> thisTime;
@@ -228,7 +232,7 @@ namespace mongo {
class ScopeCache {
public:
- ScopeCache(){
+ ScopeCache() : _mutex("ScopeCache") {
_magic = 17;
}
@@ -421,5 +425,15 @@ namespace mongo {
void ( *ScriptEngine::_connectCallback )( DBClientWithCommands & ) = 0;
ScriptEngine * globalScriptEngine;
+
+ bool hasJSReturn( const string& code ){
+ size_t x = code.find( "return" );
+ if ( x == string::npos )
+ return false;
+
+ return
+ ( x == 0 || ! isalpha( code[x-1] ) ) &&
+ ! isalpha( code[x+6] );
+ }
}
- \ No newline at end of file
+
diff --git a/scripting/engine.h b/scripting/engine.h
index 9907d31..e097401 100644
--- a/scripting/engine.h
+++ b/scripting/engine.h
@@ -17,7 +17,7 @@
#pragma once
-#include "../stdafx.h"
+#include "../pch.h"
#include "../db/jsobj.h"
extern const char * jsconcatcode; // TODO: change name to mongoJSCode
@@ -162,5 +162,7 @@ namespace mongo {
static void ( *_connectCallback )( DBClientWithCommands & );
};
+ bool hasJSReturn( const string& s );
+
extern ScriptEngine * globalScriptEngine;
}
diff --git a/scripting/engine_java.cpp b/scripting/engine_java.cpp
index 0ed6f1d..dacf532 100644
--- a/scripting/engine_java.cpp
+++ b/scripting/engine_java.cpp
@@ -16,7 +16,7 @@
*/
-#include "stdafx.h"
+#include "pch.h"
#include "engine_java.h"
#include <iostream>
#include <map>
diff --git a/scripting/engine_java.h b/scripting/engine_java.h
index ae11cc1..5c6bc3b 100644
--- a/scripting/engine_java.h
+++ b/scripting/engine_java.h
@@ -19,10 +19,9 @@
#pragma once
-#include "../stdafx.h"
+#include "../pch.h"
#include <jni.h>
-#include <boost/thread/tss.hpp>
#include <errno.h>
#include <sys/types.h>
diff --git a/scripting/engine_spidermonkey.cpp b/scripting/engine_spidermonkey.cpp
index 6609925..22102ba 100644
--- a/scripting/engine_spidermonkey.cpp
+++ b/scripting/engine_spidermonkey.cpp
@@ -15,15 +15,14 @@
* limitations under the License.
*/
-#include "stdafx.h"
+#include "pch.h"
#include "engine_spidermonkey.h"
-
#include "../client/dbclient.h"
#ifndef _WIN32
#include <boost/date_time/posix_time/posix_time.hpp>
#undef assert
-#define assert xassert
+#define assert MONGO_assert
#endif
#define smuassert( cx , msg , val ) \
@@ -38,6 +37,12 @@
}
namespace mongo {
+
+ class InvalidUTF8Exception : public UserException {
+ public:
+ InvalidUTF8Exception() : UserException( 9006 , "invalid utf8" ){
+ }
+ };
string trim( string s ){
while ( s.size() && isspace( s[0] ) )
@@ -128,6 +133,54 @@ namespace mongo {
return new BSONFieldIterator( this );
}
+ class TraverseStack {
+ public:
+ TraverseStack(){
+ _o = 0;
+ _parent = 0;
+ }
+
+ TraverseStack( JSObject * o , const TraverseStack * parent ){
+ _o = o;
+ _parent = parent;
+ }
+
+ TraverseStack dive( JSObject * o ) const {
+ if ( o ){
+ uassert( 13076 , (string)"recursive toObject" , ! has( o ) );
+ }
+ return TraverseStack( o , this );
+ }
+
+ int depth() const {
+ int d = 0;
+ const TraverseStack * s = _parent;
+ while ( s ){
+ s = s->_parent;
+ d++;
+ }
+ return d;
+ }
+
+ bool isTop() const {
+ return _parent == 0;
+ }
+
+ bool has( JSObject * o ) const {
+ if ( ! o )
+ return false;
+ const TraverseStack * s = this;
+ while ( s ){
+ if ( s->_o == o )
+ return true;
+ s = s->_parent;
+ }
+ return false;
+ }
+
+ JSObject * _o;
+ const TraverseStack * _parent;
+ };
class Convertor : boost::noncopyable {
public:
@@ -171,7 +224,7 @@ namespace mongo {
( (boost::uint64_t)(boost::uint32_t)getNumber( o , "top" ) << 32 ) +
( boost::uint32_t)( getNumber( o , "bottom" ) );
} else {
- val = (boost::uint64_t) getNumber( o, "floatApprox" );
+ val = (boost::uint64_t)(boost::int64_t) getNumber( o, "floatApprox" );
}
return val;
}
@@ -198,7 +251,7 @@ namespace mongo {
return oid;
}
- BSONObj toObject( JSObject * o , int depth = 0){
+ BSONObj toObject( JSObject * o , const TraverseStack& stack=TraverseStack() ){
if ( ! o )
return BSONObj();
@@ -222,10 +275,10 @@ namespace mongo {
if ( ! appendSpecialDBObject( this , b , "value" , OBJECT_TO_JSVAL( o ) , o ) ){
- if ( depth == 0 ){
+ if ( stack.isTop() ){
jsval theid = getProperty( o , "_id" );
if ( ! JSVAL_IS_VOID( theid ) ){
- append( b , "_id" , theid , EOO , depth + 1 );
+ append( b , "_id" , theid , EOO , stack.dive( o ) );
}
}
@@ -237,10 +290,10 @@ namespace mongo {
jsval nameval;
assert( JS_IdToValue( _context ,id , &nameval ) );
string name = toString( nameval );
- if ( depth == 0 && name == "_id" )
+ if ( stack.isTop() && name == "_id" )
continue;
- append( b , name , getProperty( o , name.c_str() ) , orig[name].type() , depth + 1 );
+ append( b , name , getProperty( o , name.c_str() ) , orig[name].type() , stack.dive( o ) );
}
JS_DestroyIdArray( _context , properties );
@@ -271,39 +324,39 @@ namespace mongo {
assert( s[0] == '/' );
s = s.substr(1);
string::size_type end = s.rfind( '/' );
- b.appendRegex( name.c_str() , s.substr( 0 , end ).c_str() , s.substr( end + 1 ).c_str() );
+ b.appendRegex( name , s.substr( 0 , end ).c_str() , s.substr( end + 1 ).c_str() );
}
- void append( BSONObjBuilder& b , string name , jsval val , BSONType oldType = EOO , int depth=0 ){
+ 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 ) ){
- case JSTYPE_VOID: b.appendUndefined( name.c_str() ); break;
- case JSTYPE_NULL: b.appendNull( name.c_str() ); break;
+ case JSTYPE_VOID: b.appendUndefined( name ); break;
+ case JSTYPE_NULL: b.appendNull( name ); break;
case JSTYPE_NUMBER: {
double d = toNumber( val );
if ( oldType == NumberInt && ((int)d) == d )
- b.append( name.c_str() , (int)d );
+ b.append( name , (int)d );
else
- b.append( name.c_str() , d );
+ b.append( name , d );
break;
}
- case JSTYPE_STRING: b.append( name.c_str() , toString( val ) ); break;
- case JSTYPE_BOOLEAN: b.appendBool( name.c_str() , toBoolean( val ) ); break;
+ case JSTYPE_STRING: b.append( name , toString( val ) ); break;
+ case JSTYPE_BOOLEAN: b.appendBool( name , toBoolean( val ) ); break;
case JSTYPE_OBJECT: {
JSObject * o = JSVAL_TO_OBJECT( val );
if ( ! o || o == JSVAL_NULL ){
- b.appendNull( name.c_str() );
+ b.appendNull( name );
}
else if ( ! appendSpecialDBObject( this , b , name , val , o ) ){
- BSONObj sub = toObject( o , depth );
+ BSONObj sub = toObject( o , stack );
if ( JS_IsArrayObject( _context , o ) ){
- b.appendArray( name.c_str() , sub );
+ b.appendArray( name , sub );
}
else {
- b.append( name.c_str() , sub );
+ b.append( name , sub );
}
}
break;
@@ -315,7 +368,7 @@ namespace mongo {
appendRegex( b , name , s );
}
else {
- b.appendCode( name.c_str() , getFunctionCode( val ).c_str() );
+ b.appendCode( name , getFunctionCode( val ).c_str() );
}
break;
}
@@ -334,7 +387,7 @@ namespace mongo {
}
bool isSimpleStatement( const string& code ){
- if ( code.find( "return" ) != string::npos )
+ if ( hasJSReturn( code ) )
return false;
if ( code.find( ";" ) != string::npos &&
@@ -416,7 +469,7 @@ namespace mongo {
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 ){
- cout << "compile failed for: " << raw << endl;
+ log() << "compile failed for: " << raw << endl;
return 0;
}
gcName = "cf normal";
@@ -449,11 +502,11 @@ namespace mongo {
free( dst );
if ( ! res ){
- cout << "decode failed. probably invalid utf-8 string [" << c << "]" << endl;
+ tlog() << "decode failed. probably invalid utf-8 string [" << c << "]" << endl;
jsval v;
if ( JS_GetPendingException( _context , &v ) )
- cout << "\t why: " << toString( v ) << endl;
- throw UserException( 9006 , "invalid utf8" );
+ tlog() << "\t why: " << toString( v ) << endl;
+ throw InvalidUTF8Exception();
}
assert( s );
@@ -479,6 +532,24 @@ namespace mongo {
return OBJECT_TO_JSVAL( o );
}
+ void makeLongObj( long long n, JSObject * o ) {
+ boost::uint64_t val = (boost::uint64_t)n;
+ CHECKNEWOBJECT(o,_context,"NumberLong1");
+ setProperty( o , "floatApprox" , toval( (double)(boost::int64_t)( val ) ) );
+ if ( (boost::int64_t)val != (boost::int64_t)(double)(boost::int64_t)( val ) ) {
+ // using 2 doubles here instead of a single double because certain double
+ // bit patterns represent undefined values and sm might trash them
+ setProperty( o , "top" , toval( (double)(boost::uint32_t)( val >> 32 ) ) );
+ 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() ){
@@ -549,7 +620,9 @@ namespace mongo {
}
case Code:{
JSFunction * func = compileFunction( e.valuestr() );
- return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
+ if ( func )
+ return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
+ return JSVAL_NULL;
}
case CodeWScope:{
JSFunction * func = compileFunction( e.codeWScopeCode() );
@@ -578,17 +651,7 @@ namespace mongo {
return OBJECT_TO_JSVAL( o );
}
case NumberLong: {
- boost::uint64_t val = (boost::uint64_t)e.numberLong();
- JSObject * o = JS_NewObject( _context , &numberlong_class , 0 , 0 );
- CHECKNEWOBJECT(o,_context,"NumberLong1");
- setProperty( o , "floatApprox" , toval( (double)(boost::int64_t)( val ) ) );
- if ( (boost::int64_t)val != (boost::int64_t)(double)(boost::int64_t)( val ) ) {
- // using 2 doubles here instead of a single double because certain double
- // bit patterns represent undefined values and sm might trash them
- setProperty( o , "top" , toval( (double)(boost::uint32_t)( val >> 32 ) ) );
- setProperty( o , "bottom" , toval( (double)(boost::uint32_t)( val & 0x00000000ffffffff ) ) );
- }
- return OBJECT_TO_JSVAL( o );
+ return toval( e.numberLong() );
}
case DBRef: {
JSObject * o = JS_NewObject( _context , &dbpointer_class , 0 , 0 );
@@ -609,13 +672,13 @@ namespace mongo {
const char * data = e.binData( len );
assert( JS_SetPrivate( _context , o , new BinDataHolder( data ) ) );
- setProperty( o , "len" , toval( len ) );
- setProperty( o , "type" , toval( (int)e.binDataType() ) );
+ setProperty( o , "len" , toval( (double)len ) );
+ setProperty( o , "type" , toval( (double)e.binDataType() ) );
return OBJECT_TO_JSVAL( o );
}
}
- cout << "toval: unknown type: " << e.type() << endl;
+ log() << "toval: unknown type: " << (int) e.type() << endl;
uassert( 10218 , "not done: toval" , 0 );
return 0;
}
@@ -824,13 +887,15 @@ namespace mongo {
// --- global helpers ---
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++ ){
if ( i > 0 )
- cout << " ";
- cout << c.toString( argv[i] );
+ ss << " ";
+ ss << c.toString( argv[i] );
}
- cout << endl;
+ ss << "\n";
+ Logstream::logLockless( ss.str() );
return JS_TRUE;
}
@@ -894,14 +959,25 @@ namespace mongo {
return JS_FALSE;
}
- BSONHolder * o = GETHOLDER( cx , JSVAL_TO_OBJECT( argv[ 0 ] ) );
+ JSObject * o = JSVAL_TO_OBJECT( argv[0] );
+
+ Convertor c(cx);
double size = 0;
- if ( o ){
- size = o->_obj.objsize();
+
+ if ( JS_InstanceOf( cx , o , &bson_ro_class , 0 ) ||
+ JS_InstanceOf( cx , o , &bson_class , 0 ) ){
+ BSONHolder * h = GETHOLDER( cx , o );
+ if ( h ){
+ size = h->_obj.objsize();
+ }
}
- Convertor c(cx);
+ else {
+ BSONObj temp = c.toObject( o );
+ size = temp.objsize();
+ }
+
*rval = c.toval( size );
- return JS_TRUE;
+ return JS_TRUE;
}
JSFunctionSpec objectHelpers[] = {
@@ -934,7 +1010,15 @@ namespace mongo {
return JS_TRUE;
}
- jsval val = c.toval( e );
+ jsval val;
+ try {
+ val = c.toval( e );
+ }
+ catch ( InvalidUTF8Exception& ) {
+ JS_LeaveLocalRootScope( cx );
+ JS_ReportError( cx , "invalid utf8" );
+ return JS_FALSE;
+ }
assert( ! holder->_inResolve );
holder->_inResolve = true;
@@ -1115,20 +1199,22 @@ namespace mongo {
}
void localConnect( const char * dbName ){
- smlock;
- uassert( 10225 , "already setup for external db" , ! _externalSetup );
- if ( _localConnect ){
- uassert( 10226 , "connected to different db" , _localDBName == dbName );
- return;
+ {
+ smlock;
+ uassert( 10225 , "already setup for external db" , ! _externalSetup );
+ 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;
}
-
- initMongoJS( this , _context , _global , true );
-
- exec( "_mongo = new Mongo();" );
- exec( ((string)"db = _mongo.getDB( \"" + dbName + "\" ); ").c_str() );
-
- _localConnect = true;
- _localDBName = dbName;
loadStored();
}
@@ -1309,6 +1395,15 @@ namespace mongo {
JSBool worked = JS_EvaluateScript( _context , _global , code.c_str() , strlen( code.c_str() ) , name.c_str() , 0 , &ret );
uninstallCheckTimeout( timeoutMs );
+ if ( ! worked && _error.size() == 0 ){
+ jsval v;
+ if ( JS_GetPendingException( _context , &v ) ){
+ _error = _convertor->toString( v );
+ if ( reportError )
+ cout << _error << endl;
+ }
+ }
+
if ( assertOnError )
uassert( 10228 , name + " exec failed" , worked );
@@ -1387,7 +1482,6 @@ namespace mongo {
code << field << "_" << " = { x : " << field << "_ }; ";
code << field << " = function(){ return nativeHelper.apply( " << field << "_ , arguments ); }";
exec( code.str().c_str() );
-
}
virtual void gc(){
@@ -1424,15 +1518,20 @@ namespace mongo {
};
+ /* used to make the logging not overly chatty in the mongo shell. */
+ extern bool isShell;
+
void errorReporter( JSContext *cx, const char *message, JSErrorReport *report ){
stringstream ss;
- ss << "JS Error: " << message;
+ if( !isShell )
+ ss << "JS Error: ";
+ ss << message;
if ( report && report->filename ){
ss << " " << report->filename << ":" << report->lineno;
}
- log() << ss.str() << endl;
+ tlog() << ss.str() << endl;
if ( currentScope.get() ){
currentScope->gotError( ss.str() );
@@ -1446,10 +1545,10 @@ namespace mongo {
for ( uintN i=0; i<argc; i++ ){
string filename = c.toString( argv[i] );
- cout << "should load [" << filename << "]" << endl;
+ //cout << "load [" << filename << "]" << endl;
if ( ! s->execFile( filename , false , true , false ) ){
- JS_ReportError( cx , ((string)"error loading file: " + filename ).c_str() );
+ JS_ReportError( cx , ((string)"error loading js file: " + filename ).c_str() );
return JS_FALSE;
}
}
diff --git a/scripting/engine_spidermonkey.h b/scripting/engine_spidermonkey.h
index 4e420de..4617b5d 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
+#define JS_PUBLIC_API(t) t __cdecl
#define JS_PUBLIC_DATA(t) t
#endif
diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp
index 35f2eb8..08826b1 100644
--- a/scripting/engine_v8.cpp
+++ b/scripting/engine_v8.cpp
@@ -57,9 +57,12 @@ namespace mongo {
_global->Set(v8::String::New("load"),
v8::FunctionTemplate::New(loadCallback, v8::External::New(this))->GetFunction() );
-
- _wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate()->GetFunction() );
+ _wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate()->GetFunction() );
+
+ _global->Set(v8::String::New("gc"), v8::FunctionTemplate::New(GCV8)->GetFunction() );
+
+
installDBTypes( _global );
}
@@ -232,7 +235,7 @@ namespace mongo {
string code = raw;
if ( code.find( "function" ) == string::npos ){
if ( code.find( "\n" ) == string::npos &&
- code.find( "return" ) == string::npos &&
+ ! hasJSReturn( code ) &&
( code.find( ";" ) == string::npos || code.find( ";" ) == code.size() - 1 ) ){
code = "return " + code;
}
@@ -383,6 +386,7 @@ namespace mongo {
}
void V8Scope::gc() {
+ cout << "in gc" << endl;
Locker l;
while( V8::IdleNotification() );
}
diff --git a/scripting/sm_db.cpp b/scripting/sm_db.cpp
index 1c15170..855a50d 100644
--- a/scripting/sm_db.cpp
+++ b/scripting/sm_db.cpp
@@ -19,6 +19,15 @@
#include "../client/syncclusterconnection.h"
#include "../util/base64.h"
+#include "../util/text.h"
+#include "../util/hex.h"
+
+#if( BOOST_VERSION >= 104200 )
+//#include <boost/uuid/uuid.hpp>
+#define HAVE_UUID 1
+#else
+;
+#endif
namespace mongo {
@@ -90,6 +99,13 @@ namespace mongo {
return JS_TRUE;
}
+ 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){
DBClientCursor *cursor = getCursor( cx, obj );
if ( ! cursor->more() ){
@@ -105,6 +121,7 @@ namespace mongo {
JSFunctionSpec internal_cursor_functions[] = {
{ "hasNext" , internal_cursor_hasNext , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "objsLeftInBatch" , internal_cursor_objsLeftInBatch , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "next" , internal_cursor_next , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
@@ -145,37 +162,22 @@ namespace mongo {
if ( argc > 0 )
host = c.toString( argv[0] );
- int numCommas = DBClientBase::countCommas( host );
-
- shared_ptr< DBClientWithCommands > conn;
-
string errmsg;
- if ( numCommas == 0 ){
- DBClientConnection * c = new DBClientConnection( true );
- conn.reset( c );
- if ( ! c->connect( host , errmsg ) ){
- JS_ReportError( cx , ((string)"couldn't connect: " + errmsg).c_str() );
- return JS_FALSE;
- }
- ScriptEngine::runConnectCallback( *c );
- }
- else if ( numCommas == 1 ){ // paired
- DBClientPaired * c = new DBClientPaired();
- conn.reset( c );
- if ( ! c->connect( host ) ){
- JS_ReportError( cx , "couldn't connect to pair" );
- return JS_FALSE;
- }
- }
- else if ( numCommas == 2 ){
- conn.reset( new SyncClusterConnection( host ) );
+
+ ConnectionString cs = ConnectionString::parse( host , errmsg );
+ if ( ! cs.isValid() ){
+ JS_ReportError( cx , errmsg.c_str() );
+ return JS_FALSE;
}
- else {
- JS_ReportError( cx , "1 (paired) or 2(quorum) commas are allowed" );
+
+ shared_ptr< DBClientWithCommands > conn( cs.connect( errmsg ) );
+ 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 ) ) ) );
jsval host_val = c.toval( host.c_str() );
assert( JS_SetProperty( cx , obj , "host" , &host_val ) );
@@ -342,7 +344,6 @@ namespace mongo {
{ 0 }
};
-
// ------------- db_collection -------------
JSBool db_collection_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
@@ -516,7 +517,7 @@ namespace mongo {
JSBool object_id_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
Convertor c(cx);
- return *rval = c.getProperty( obj , "str" );
+ return (JSBool) (*rval = c.getProperty( obj , "str" ));
}
JSFunctionSpec object_id_functions[] = {
@@ -524,7 +525,6 @@ namespace mongo {
{ 0 }
};
-
// dbpointer
JSBool dbpointer_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
@@ -580,8 +580,78 @@ namespace mongo {
JSClass dbref_class = bson_class; // name will be fixed later
- // BinData
+ // UUID **************************
+
+ JSBool uuid_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ Convertor c( cx );
+
+ if( argc == 0 ) {
+#if defined(HAVE_UUID)
+ //uuids::uuid
+#else
+#endif
+ JS_ReportError( cx , "UUID needs 1 argument -- UUID(hexstr)" );
+ 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;
+ }
+
+ 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 ) );
+ c.setProperty( obj, "type", c.toval( (double)3 ) );
+
+ return JS_TRUE;
+ }
+ else {
+ JS_ReportError( cx , "UUID needs 1 argument -- UUID(hexstr)" );
+ return JS_FALSE;
+ }
+ }
+
+ JSBool uuid_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ Convertor c(cx);
+ void *holder = JS_GetPrivate( cx, obj );
+ assert( holder );
+ const char *data = ( ( BinDataHolder* )( holder ) )->c_;
+ stringstream ss;
+ ss << "UUID(\"" << toHex(data, 16);
+ ss << "\")";
+ string ret = ss.str();
+ return *rval = c.toval( ret.c_str() );
+ }
+
+ void uuid_finalize( JSContext * cx , JSObject * obj ){
+ Convertor c(cx);
+ void *holder = JS_GetPrivate( cx, obj );
+ 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,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, uuid_finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+ JSFunctionSpec uuid_functions[] = {
+ { "toString" , uuid_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { 0 }
+ };
+
+ // BinData **************************
JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
@@ -589,17 +659,28 @@ namespace mongo {
if ( argc == 2 ){
int type = (int)c.toNumber( argv[ 0 ] );
+ if( type < 0 || type > 255 ) {
+ JS_ReportError( cx , "invalid BinData subtype -- range is 0..255 see bsonspec.org" );
+ return JS_FALSE;
+ }
string encoded = c.toString( argv[ 1 ] );
- string decoded = base64::decode( encoded );
+ string decoded;
+ try {
+ decoded = base64::decode( encoded );
+ }
+ catch(...) {
+ JS_ReportError(cx, "BinData could not decode base64 parameter");
+ return JS_FALSE;
+ }
assert( JS_SetPrivate( cx, obj, new BinDataHolder( decoded.data(), decoded.length() ) ) );
- c.setProperty( obj, "len", c.toval( decoded.length() ) );
- c.setProperty( obj, "type", c.toval( type ) );
+ c.setProperty( obj, "len", c.toval( (double)decoded.length() ) );
+ c.setProperty( obj, "type", c.toval( (double)type ) );
return JS_TRUE;
}
else {
- JS_ReportError( cx , "BinData needs 2 arguments" );
+ JS_ReportError( cx , "BinData needs 2 arguments -- BinData(subtype,data)" );
return JS_FALSE;
}
}
@@ -612,13 +693,53 @@ namespace mongo {
assert( holder );
const char *data = ( ( BinDataHolder* )( holder ) )->c_;
stringstream ss;
- ss << "BinData( type: " << type << ", base64: \"";
+ ss << "BinData(" << type << ",\"";
base64::encode( ss, (const char *)data, len );
- ss << "\" )";
+ ss << "\")";
+ string ret = ss.str();
+ return *rval = c.toval( ret.c_str() );
+ }
+
+ 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 );
+ assert( holder );
+ const char *data = ( ( BinDataHolder* )( holder ) )->c_;
+ stringstream ss;
+ base64::encode( ss, (const char *)data, len );
+ string ret = ss.str();
+ return *rval = c.toval( ret.c_str() );
+ }
+
+ 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 );
+ assert( holder );
+ const char *data = ( ( BinDataHolder* )( holder ) )->c_;
+ stringstream ss;
+ ss << hex;
+ for( int i = 0; i < len; i++ ) {
+ unsigned v = (unsigned char) data[i];
+ ss << v;
+ }
string ret = ss.str();
return *rval = c.toval( ret.c_str() );
}
+ JSBool bindataLength(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ Convertor c(cx);
+ int len = (int)c.getNumber( obj, "len" );
+ return *rval = c.toval((double) len);
+ }
+
+ JSBool bindataSubtype(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ Convertor c(cx);
+ int t = (int)c.getNumber( obj, "type" );
+ return *rval = c.toval((double) t);
+ }
+
void bindata_finalize( JSContext * cx , JSObject * obj ){
Convertor c(cx);
void *holder = JS_GetPrivate( cx, obj );
@@ -637,6 +758,10 @@ namespace mongo {
JSFunctionSpec bindata_functions[] = {
{ "toString" , bindata_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "hex", bindataAsHex, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "base64", bindataBase64, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "length", bindataLength, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "subtype", bindataSubtype, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
@@ -699,6 +824,31 @@ namespace mongo {
JSCLASS_NO_OPTIONAL_MEMBERS
};
+ 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 );
+
+ Convertor c( cx );
+ if ( argc == 0 ) {
+ c.setProperty( obj, "floatApprox", c.toval( 0.0 ) );
+ } else if ( JSVAL_IS_NUMBER( argv[ 0 ] ) ) {
+ c.setProperty( obj, "floatApprox", argv[ 0 ] );
+ } else {
+ string num = c.toString( argv[ 0 ] );
+ //PRINT(num);
+ const char *numStr = num.c_str();
+ long long n;
+ try {
+ n = parseLL( numStr );
+ //PRINT(n);
+ } 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){
Convertor c(cx);
return *rval = c.toval( double( c.toNumberLongUnsafe( obj ) ) );
@@ -711,7 +861,12 @@ namespace mongo {
JSBool numberlong_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
Convertor c(cx);
stringstream ss;
- ss << c.toNumberLongUnsafe( obj );
+ if ( c.hasProperty( obj, "top" ) ) {
+ long long val = c.toNumberLongUnsafe( obj );
+ ss << "NumberLong( \"" << val << "\" )";
+ } else {
+ ss << "NumberLong( " << c.getNumber( obj, "floatApprox" ) << " )";
+ }
string ret = ss.str();
return *rval = c.toval( ret.c_str() );
}
@@ -819,9 +974,10 @@ namespace mongo {
assert( JS_InitClass( cx , global , 0 , &dbquery_class , dbquery_constructor , 0 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &dbpointer_class , dbpointer_constructor , 0 , 0 , dbpointer_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) );
+ assert( JS_InitClass( cx , global , 0 , &uuid_class , uuid_constructor , 0 , 0 , uuid_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &timestamp_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
- assert( JS_InitClass( cx , global , 0 , &numberlong_class , 0 , 0 , 0 , numberlong_functions , 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 ) );
@@ -842,39 +998,39 @@ namespace mongo {
if ( JS_InstanceOf( c->_context , o , &object_id_class , 0 ) ){
OID oid;
oid.init( c->getString( o , "str" ) );
- b.append( name.c_str() , oid );
+ b.append( name , oid );
return true;
}
if ( JS_InstanceOf( c->_context , o , &minkey_class , 0 ) ){
- b.appendMinKey( name.c_str() );
+ b.appendMinKey( name );
return true;
}
if ( JS_InstanceOf( c->_context , o , &maxkey_class , 0 ) ){
- b.appendMaxKey( name.c_str() );
+ b.appendMaxKey( name );
return true;
}
if ( JS_InstanceOf( c->_context , o , &timestamp_class , 0 ) ){
- b.appendTimestamp( name.c_str() , (unsigned long long)c->getNumber( o , "t" ) , (unsigned int )c->getNumber( o , "i" ) );
+ 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 ) ){
- b.append( name.c_str() , c->toNumberLongUnsafe( o ) );
+ b.append( name , c->toNumberLongUnsafe( o ) );
return true;
}
if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ){
- b.appendDBRef( name.c_str() , c->getString( o , "ns" ).c_str() , c->toOID( c->getProperty( o , "id" ) ) );
+ b.appendDBRef( name , c->getString( o , "ns" ).c_str() , c->toOID( c->getProperty( o , "id" ) ) );
return true;
}
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.c_str() ,
+ b.appendBinData( name ,
(int)(c->getNumber( o , "len" )) , (BinDataType)((char)(c->getNumber( o , "type" ) ) ) ,
data
);
@@ -886,21 +1042,21 @@ namespace mongo {
{
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
if ( d ){
- b.appendDate( name.c_str() , Date_t(d) );
+ b.appendDate( name , Date_t(d) );
return true;
}
}
#elif defined( XULRUNNER )
if ( JS_InstanceOf( c->_context , o, globalSMEngine->_dateClass , 0 ) ){
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
- b.appendDate( name.c_str() , Date_t(d) );
+ b.appendDate( name , Date_t(d) );
return true;
}
#else
if ( JS_InstanceOf( c->_context , o, &js_DateClass , 0 ) ){
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
//TODO: make signed
- b.appendDate( name.c_str() , Date_t((unsigned long long)d) );
+ b.appendDate( name , Date_t((unsigned long long)d) );
return true;
}
#endif
@@ -909,7 +1065,7 @@ namespace mongo {
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 ) ){
- b.append( name.c_str() , c->toString( val ) );
+ b.append( name , c->toString( val ) );
return true;
}
diff --git a/scripting/utils.cpp b/scripting/utils.cpp
index 21089ac..ee01bb2 100644
--- a/scripting/utils.cpp
+++ b/scripting/utils.cpp
@@ -16,9 +16,10 @@
*/
-#include "stdafx.h"
+#include "pch.h"
#include "engine.h"
#include "../util/md5.hpp"
+#include "../util/version.h"
namespace mongo {
diff --git a/scripting/v8_db.cpp b/scripting/v8_db.cpp
index 4d14a03..5752fde 100644
--- a/scripting/v8_db.cpp
+++ b/scripting/v8_db.cpp
@@ -20,6 +20,7 @@
#include "v8_db.h"
#include "engine.h"
#include "util/base64.h"
+#include "util/text.h"
#include "../client/syncclusterconnection.h"
#include <iostream>
@@ -28,12 +29,11 @@ using namespace v8;
namespace mongo {
-#define CONN_STRING (v8::String::New( "_conn" ))
-
#define DDD(x)
v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( bool local ){
v8::Local<v8::FunctionTemplate> mongo = FunctionTemplate::New( local ? mongoConsLocal : mongoConsExternal );
+ mongo->InstanceTemplate()->SetInternalFieldCount( 1 );
v8::Local<v8::Template> proto = mongo->PrototypeTemplate();
@@ -43,9 +43,13 @@ namespace mongo {
proto->Set( v8::String::New( "update" ) , FunctionTemplate::New( mongoUpdate ) );
Local<FunctionTemplate> ic = FunctionTemplate::New( 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 ) );
proto->Set( v8::String::New( "internalCursor" ) , ic );
+
+
return mongo;
}
@@ -131,9 +135,10 @@ namespace mongo {
global->Get( v8::String::New( "Object" ) )->ToObject()->Set( v8::String::New("bsonsize") , FunctionTemplate::New( bsonsize )->GetFunction() );
}
- void destroyConnection( Persistent<Value> object, void* parameter){
- // TODO
- cout << "warning: destroyConnection not implemented" << endl;
+ void destroyConnection( Persistent<Value> self, void* parameter){
+ delete static_cast<DBClientBase*>(parameter);
+ self.Dispose();
+ self.Clear();
}
Handle<Value> mongoConsExternal(const Arguments& args){
@@ -148,47 +153,22 @@ namespace mongo {
strcpy( host , "127.0.0.1" );
}
- DBClientWithCommands * conn = 0;
- int commas = 0;
- for ( int i=0; i<255; i++ ){
- if ( host[i] == ',' )
- commas++;
- else if ( host[i] == 0 )
- break;
- }
+ string errmsg;
+ ConnectionString cs = ConnectionString::parse( host , errmsg );
+ if ( ! cs.isValid() )
+ return v8::ThrowException( v8::String::New( errmsg.c_str() ) );
- if ( commas == 0 ){
- DBClientConnection * c = new DBClientConnection( true );
- string errmsg;
- if ( ! c->connect( host , errmsg ) ){
- delete c;
- string x = "couldn't connect: ";
- x += errmsg;
- return v8::ThrowException( v8::String::New( x.c_str() ) );
- }
- conn = c;
- }
- else if ( commas == 1 ){
- DBClientPaired * c = new DBClientPaired();
- if ( ! c->connect( host ) ){
- delete c;
- return v8::ThrowException( v8::String::New( "couldn't connect to pair" ) );
- }
- conn = c;
- }
- else if ( commas == 2 ){
- conn = new SyncClusterConnection( host );
- }
- else {
- return v8::ThrowException( v8::String::New( "too many commas" ) );
- }
-
- Persistent<v8::Object> self = Persistent<v8::Object>::New( args.This() );
+
+ DBClientWithCommands * 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 );
- // NOTE I don't believe the conn object will ever be freed.
- args.This()->Set( CONN_STRING , External::New( 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 ) );
@@ -206,7 +186,7 @@ namespace mongo {
self.MakeWeak( conn , destroyConnection );
// NOTE I don't believe the conn object will ever be freed.
- args.This()->Set( CONN_STRING , External::New( 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( "EMBEDDED" ) );
@@ -223,7 +203,7 @@ namespace mongo {
#endif
DBClientBase * getConnection( const Arguments& args ){
- Local<External> c = External::Cast( *(args.This()->Get( CONN_STRING )) );
+ Local<External> c = External::Cast( *(args.This()->GetInternalField( 0 )) );
DBClientBase * conn = (DBClientBase*)(c->Value());
assert( conn );
return conn;
@@ -231,6 +211,12 @@ namespace mongo {
// ---- real methods
+ void destroyCursor( Persistent<Value> self, void* parameter){
+ delete static_cast<mongo::DBClientCursor*>(parameter);
+ self.Dispose();
+ self.Clear();
+ }
+
/**
0 - namespace
1 - query
@@ -239,6 +225,8 @@ namespace mongo {
4 - skip
*/
Handle<Value> mongoFind(const Arguments& args){
+ HandleScope handle_scope;
+
jsassert( args.Length() == 6 , "find needs 6 args" );
jsassert( args[1]->IsObject() , "needs to be an object" );
DBClientBase * conn = getConnection( args );
@@ -268,11 +256,12 @@ namespace mongo {
}
v8::Function * cons = (v8::Function*)( *( mongo->Get( v8::String::New( "internalCursor" ) ) ) );
assert( cons );
- Local<v8::Object> c = cons->NewInstance();
-
- // NOTE I don't believe the cursor object will ever be freed.
- c->Set( v8::String::New( "cursor" ) , External::New( cursor.release() ) );
- return c;
+
+ 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" ) );
@@ -362,7 +351,8 @@ namespace mongo {
// --- cursor ---
mongo::DBClientCursor * getCursor( const Arguments& args ){
- Local<External> c = External::Cast( *(args.This()->Get( v8::String::New( "cursor" ) ) ) );
+ Local<External> c = External::Cast( *(args.This()->GetInternalField( 0 ) ) );
+
mongo::DBClientCursor * cursor = (mongo::DBClientCursor*)(c->Value());
return cursor;
}
@@ -395,6 +385,18 @@ namespace mongo {
return Boolean::New( ret );
}
+ 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;
+ ret = cursor->objsLeftInBatch();
+ }
+ return v8::Number::New( (double) ret );
+ }
+
// --- DB ----
@@ -623,17 +625,17 @@ namespace mongo {
v8::String::Utf8Value data( it->Get( v8::String::New( "data" ) ) );
stringstream ss;
- ss << "BinData( type: " << type << ", base64: \"";
+ ss << "BinData(" << type << ",\"";
base64::encode( ss, *data, len );
- ss << "\" )";
+ ss << "\")";
string ret = ss.str();
return v8::String::New( ret.c_str() );
}
v8::Handle<v8::Value> numberLongInit( const v8::Arguments& args ) {
- if (args.Length() != 1 && args.Length() != 3) {
- return v8::ThrowException( v8::String::New( "NumberLong needs 1 or 3 arguments" ) );
+ 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();
@@ -642,9 +644,33 @@ namespace mongo {
v8::Function* f = getNamedCons( "NumberLong" );
it = f->NewInstance();
}
-
- it->Set( v8::String::New( "floatApprox" ) , args[0] );
- if ( args.Length() == 3 ) {
+
+ if ( args.Length() == 0 ) {
+ it->Set( v8::String::New( "floatApprox" ), v8::Number::New( 0 ) );
+ } else if ( args.Length() == 1 ) {
+ if ( args[ 0 ]->IsNumber() ) {
+ 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 & ) {
+ 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 {
+ 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 {
+ it->Set( v8::String::New( "floatApprox" ) , args[0] );
it->Set( v8::String::New( "top" ) , args[1] );
it->Set( v8::String::New( "bottom" ) , args[2] );
}
@@ -687,10 +713,12 @@ namespace mongo {
v8::Handle<v8::Object> it = args.This();
- long long val = numberLongVal( it );
-
stringstream ss;
- ss << val;
+ if ( !it->Has( v8::String::New( "top" ) ) ) {
+ ss << "NumberLong( " << it->Get( v8::String::New( "floatApprox" ) )->NumberValue() << " )";
+ } else {
+ ss << "NumberLong( \"" << numberLongVal( it ) << "\" )";
+ }
string ret = ss.str();
return v8::String::New( ret.c_str() );
}
diff --git a/scripting/v8_db.h b/scripting/v8_db.h
index 92e2ae2..4bebb32 100644
--- a/scripting/v8_db.h
+++ b/scripting/v8_db.h
@@ -49,6 +49,7 @@ namespace mongo {
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
diff --git a/scripting/v8_utils.cpp b/scripting/v8_utils.cpp
index 5e56245..5a07a80 100644
--- a/scripting/v8_utils.cpp
+++ b/scripting/v8_utils.cpp
@@ -302,4 +302,11 @@ namespace mongo {
global->Set( v8::String::New( "_scopedThreadInject" ), FunctionTemplate::New( ScopedThreadInject )->GetFunction() );
}
+ Handle<v8::Value> GCV8(const Arguments& args) {
+ Locker l;
+ while( V8::IdleNotification() );
+ return v8::Undefined();
+ }
+
+
}
diff --git a/scripting/v8_utils.h b/scripting/v8_utils.h
index 8218455..bc4b524 100644
--- a/scripting/v8_utils.h
+++ b/scripting/v8_utils.h
@@ -29,6 +29,7 @@ namespace mongo {
v8::Handle<v8::Value> Print(const v8::Arguments& args);
v8::Handle<v8::Value> Version(const v8::Arguments& args);
+ v8::Handle<v8::Value> GCV8(const v8::Arguments& args);
void ReportException(v8::TryCatch* handler);
diff --git a/scripting/v8_wrapper.cpp b/scripting/v8_wrapper.cpp
index c4e6b7d..0e71c9a 100644
--- a/scripting/v8_wrapper.cpp
+++ b/scripting/v8_wrapper.cpp
@@ -395,31 +395,31 @@ namespace mongo {
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.c_str() , toSTLString( value ).c_str() );
+ b.append( sname , toSTLString( value ).c_str() );
return;
}
if ( value->IsFunction() ){
- b.appendCode( sname.c_str() , toSTLString( value ).c_str() );
+ b.appendCode( sname , toSTLString( value ).c_str() );
return;
}
if ( value->IsNumber() ){
if ( value->IsInt32() )
- b.append( sname.c_str(), int( value->ToInt32()->Value() ) );
+ b.append( sname, int( value->ToInt32()->Value() ) );
else
- b.append( sname.c_str() , value->ToNumber()->Value() );
+ b.append( sname , value->ToNumber()->Value() );
return;
}
if ( value->IsArray() ){
BSONObj sub = v8ToMongo( value->ToObject() , depth );
- b.appendArray( sname.c_str() , sub );
+ b.appendArray( sname , sub );
return;
}
if ( value->IsDate() ){
- b.appendDate( sname.c_str() , Date_t(v8::Date::Cast( *value )->NumberValue()) );
+ b.appendDate( sname , Date_t( (unsigned long long)(v8::Date::Cast( *value )->NumberValue())) );
return;
}
@@ -434,15 +434,15 @@ namespace mongo {
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.c_str(),
- Date_t( obj->Get( v8::String::New( "t" ) )->ToNumber()->Value() ),
- obj->Get( v8::String::New( "i" ) )->ToInt32()->Value() );
+ 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.c_str() );
+ b.appendMinKey( sname );
return;
case MaxKey:
- b.appendMaxKey( sname.c_str() );
+ b.appendMaxKey( sname );
return;
default:
assert( "invalid internal field" == 0 );
@@ -453,13 +453,13 @@ namespace mongo {
s = s.substr( 1 );
string r = s.substr( 0 , s.rfind( "/" ) );
string o = s.substr( s.rfind( "/" ) + 1 );
- b.appendRegex( sname.c_str() , r.c_str() , o.c_str() );
+ b.appendRegex( sname , r.c_str() , o.c_str() );
}
else if ( value->ToObject()->GetPrototype()->IsObject() &&
value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ){
OID oid;
oid.init( toSTLString( value ) );
- b.appendOID( sname.c_str() , &oid );
+ b.appendOID( sname , &oid );
}
else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__NumberLong" ) ).IsEmpty() ) {
// TODO might be nice to potentially speed this up with an indexed internal
@@ -475,42 +475,42 @@ namespace mongo {
(unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() );
}
- b.append( sname.c_str(), val );
+ 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.c_str(), ns.c_str(), oid );
+ b.appendDBRef( sname, ns.c_str(), oid );
}
else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__BinData" ) ).IsEmpty() ) {
int len = obj->Get( v8::String::New( "len" ) )->ToInt32()->Value();
v8::String::Utf8Value data( obj->Get( v8::String::New( "data" ) ) );
const char *dataArray = *data;
assert( data.length() == len );
- b.appendBinData( sname.c_str(),
+ b.appendBinData( sname,
len,
mongo::BinDataType( obj->Get( v8::String::New( "type" ) )->ToInt32()->Value() ),
dataArray );
} else {
BSONObj sub = v8ToMongo( value->ToObject() , depth );
- b.append( sname.c_str() , sub );
+ b.append( sname , sub );
}
return;
}
if ( value->IsBoolean() ){
- b.appendBool( sname.c_str() , value->ToBoolean()->Value() );
+ b.appendBool( sname , value->ToBoolean()->Value() );
return;
}
else if ( value->IsUndefined() ){
- b.appendUndefined( sname.c_str() );
+ b.appendUndefined( sname );
return;
}
else if ( value->IsNull() ){
- b.appendNull( sname.c_str() );
+ b.appendNull( sname );
return;
}