summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/sm_db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting/sm_db.cpp')
-rw-r--r--src/mongo/scripting/sm_db.cpp1716
1 files changed, 1716 insertions, 0 deletions
diff --git a/src/mongo/scripting/sm_db.cpp b/src/mongo/scripting/sm_db.cpp
new file mode 100644
index 00000000000..e7b547b8df8
--- /dev/null
+++ b/src/mongo/scripting/sm_db.cpp
@@ -0,0 +1,1716 @@
+// sm_db.cpp
+
+/* Copyright 2009 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// hacked in right now from engine_spidermonkey.cpp
+
+#include "mongo/client/dbclientcursor.h"
+#include "mongo/db/namespacestring.h"
+#include "mongo/util/base64.h"
+#include "mongo/util/text.h"
+
+#if( BOOST_VERSION >= 104200 )
+#define HAVE_UUID 1
+#endif
+
+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 ) {
+ static set<string> names;
+ if ( names.size() == 0 ) {
+ names.insert( "tojson" );
+ names.insert( "toJson" );
+ names.insert( "toString" );
+ }
+
+ if ( name.length() == 0 )
+ return false;
+
+ if ( name[0] == '_' )
+ return true;
+
+ return names.count( name ) > 0;
+ }
+
+
+ // ------ cursor ------
+
+ class CursorHolder {
+ public:
+ CursorHolder( auto_ptr< DBClientCursor > &cursor, const shared_ptr< DBClientWithCommands > &connection ) :
+ connection_( connection ),
+ cursor_( cursor ) {
+ verify( cursor_.get() );
+ }
+ DBClientCursor *get() const { return cursor_.get(); }
+ private:
+ 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 ) {
+ try {
+ uassert( 10236 , "no args to internal_cursor_constructor" , argc == 0 );
+ verify( JS_SetPrivate( cx , obj , 0 ) ); // just for safety
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16288 );
+ }
+ return JS_TRUE;
+ }
+
+ void internal_cursor_finalize( JSContext * cx , JSObject * obj ) {
+ try {
+ CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj );
+ if ( holder ) {
+ delete holder;
+ verify( JS_SetPrivate( cx , obj , 0 ) );
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16289 );
+ }
+ }
+
+ JSBool internal_cursor_hasNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ DBClientCursor *cursor = getCursor( cx, obj );
+ *rval = cursor->more() ? JSVAL_TRUE : JSVAL_FALSE;
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16290 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool internal_cursor_objsLeftInBatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ DBClientCursor *cursor = getCursor( cx, obj );
+ Convertor c(cx);
+ *rval = c.toval((double) cursor->objsLeftInBatch() );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16291 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool internal_cursor_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ DBClientCursor *cursor = getCursor( cx, obj );
+ if ( ! cursor->more() ) {
+ JS_ReportError( cx , "cursor at the end" );
+ return JS_FALSE;
+ }
+
+ BSONObj n = cursor->next();
+ Convertor c(cx);
+ *rval = c.toval( &n );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16292 );
+ }
+ return JS_TRUE;
+ }
+
+ 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 }
+ };
+
+ JSClass internal_cursor_class = {
+ "InternalCursor", // class name
+ JSCLASS_HAS_PRIVATE, // flags
+ JS_PropertyStub, // addProperty
+ JS_PropertyStub, // delProperty
+ JS_PropertyStub, // getProperty
+ JS_PropertyStub, // setProperty
+ JS_EnumerateStub, // enumerate
+ JS_ResolveStub, // resolve
+ JS_ConvertStub, // convert
+ internal_cursor_finalize, // finalize
+ JSCLASS_NO_OPTIONAL_MEMBERS // optional members
+ };
+
+
+ // ------ mongo stuff ------
+
+ JSBool mongo_local_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ try {
+ shared_ptr< DBClientWithCommands > client( createDirectClient() );
+ verify( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( client ) ) ) );
+
+ Convertor c( cx );
+ jsval host = c.toval( "EMBEDDED" );
+ verify( JS_SetProperty( cx , obj , "host" , &host ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16293 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ try {
+ smuassert( cx , "0 or 1 args to Mongo" , argc <= 1 );
+
+ string host( "127.0.0.1" );
+ Convertor c( cx );
+ if ( argc > 0 )
+ host = c.toString( argv[0] );
+
+ string errmsg;
+ ConnectionString cs = ConnectionString::parse( host , errmsg );
+ if ( ! cs.isValid() ) {
+ JS_ReportError( cx , errmsg.c_str() );
+ return JS_FALSE;
+ }
+
+ shared_ptr< DBClientWithCommands > conn( cs.connect( errmsg ) );
+ if ( ! conn ) {
+ JS_ReportError( cx , errmsg.c_str() );
+ return JS_FALSE;
+ }
+
+ try {
+ ScriptEngine::runConnectCallback( *conn );
+ }
+ catch ( const AssertionException& e ){
+ // Can happen if connection goes down while we're starting up here
+ // Catch so that we don't get a hard-to-trace segfault from SM
+ JS_ReportError( cx, ((string)( str::stream() << "Error during mongo startup." << causedBy( e ) )).c_str() );
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16294 );
+ }
+
+ verify( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( conn ) ) ) );
+ jsval host_val = c.toval( host.c_str() );
+ verify( JS_SetProperty( cx , obj , "host" , &host_val ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16295 );
+ }
+ return JS_TRUE;
+ }
+
+ DBClientWithCommands *getConnection( JSContext *cx, JSObject *obj ) {
+ shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
+ uassert( 10239 , "no connection!" , connHolder && connHolder->get() );
+ return connHolder->get();
+ }
+
+ void mongo_finalize( JSContext * cx , JSObject * obj ) {
+ try {
+ shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
+ if ( connHolder ) {
+ delete connHolder;
+ verify( JS_SetPrivate( cx , obj , 0 ) );
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16296 );
+ }
+ }
+
+ JSClass mongo_class = {
+ "Mongo", // class name
+ JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, // flags
+ JS_PropertyStub, // addProperty
+ JS_PropertyStub, // delProperty
+ JS_PropertyStub, // getProperty
+ JS_PropertyStub, // setProperty
+ JS_EnumerateStub, // enumerate
+ JS_ResolveStub, // resolve
+ JS_ConvertStub, // convert
+ mongo_finalize, // finalize
+ JSCLASS_NO_OPTIONAL_MEMBERS // optional members
+ };
+
+ JSBool mongo_auth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ smuassert( cx , "mongo_auth needs 3 args" , argc == 3 );
+ 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 db = c.toString( argv[0] );
+ string username = c.toString( argv[1] );
+ string password = c.toString( argv[2] );
+ string errmsg = "";
+
+ if ( ! conn->auth( db, username, password, errmsg ) ) {
+ JS_ReportError( cx, errmsg.c_str() );
+ return JS_FALSE;
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const SocketException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.toString().c_str() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16297 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool mongo_logout(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ smuassert(context, "mongo_logout needs 1 arg" , argc == 1);
+ shared_ptr<DBClientWithCommands>* connHolder =
+ reinterpret_cast<shared_ptr<DBClientWithCommands>*>(
+ JS_GetPrivate(context , obj));
+ smuassert(context, "no connection!", connHolder && connHolder->get());
+ DBClientWithCommands *conn = connHolder->get();
+
+ Convertor convertor(context);
+ string db = convertor.toString(argv[0]);
+
+ BSONObj ret;
+ conn->logout(db, ret);
+ *rval = convertor.toval(&ret);
+ }
+ catch (const AssertionException& e) {
+ if (!JS_IsExceptionPending(context)) {
+ JS_ReportError(context, e.what());
+ }
+ return JS_FALSE;
+ }
+ catch (const SocketException& e) {
+ if (!JS_IsExceptionPending(context)) {
+ JS_ReportError(context, e.toString().c_str());
+ }
+ return JS_FALSE;
+ }
+ catch (const std::exception& e) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed(16396);
+ }
+
+ return JS_TRUE;
+ }
+
+ JSBool mongo_find(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ 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] );
+ int batchSize = (int) c.toNumber( argv[5] );
+ int options = (int)c.toNumber( argv[6] );
+
+ auto_ptr<DBClientCursor> cursor = conn->query( ns , q , nToReturn , nToSkip , f.nFields() ? &f : 0 , options , batchSize );
+ if ( ! cursor.get() ) {
+ log() << "query failed : " << ns << " " << q << " to: " << conn->toString() << endl;
+ JS_ReportError( cx , "error doing query: failed" );
+ return JS_FALSE;
+ }
+ JSObject * mycursor = JS_NewObject( cx , &internal_cursor_class , 0 , 0 );
+ CHECKNEWOBJECT( mycursor, cx, "internal_cursor_class" );
+ verify( JS_SetPrivate( cx , mycursor , new CursorHolder( cursor, *connHolder ) ) );
+ *rval = OBJECT_TO_JSVAL( mycursor );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const SocketException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.toString().c_str() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16298 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool mongo_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ smuassert( cx , "mongo_update needs at least 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" ) ) {
+ JS_ReportError( cx , "js db in read only mode - mongo_update" );
+ return JS_FALSE;
+ }
+
+ DBClientWithCommands * conn = getConnection( cx, obj );
+ uassert( 10245 , "no connection!" , conn );
+
+ string ns = c.toString( argv[0] );
+
+ bool upsert = argc > 3 && c.toBoolean( argv[3] );
+ bool multi = argc > 4 && c.toBoolean( argv[4] );
+
+ conn->update( ns , c.toObject( argv[1] ) , c.toObject( argv[2] ) , upsert , multi );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const SocketException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.toString().c_str() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16299 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool mongo_insert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ 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" ) ) {
+ 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] );
+
+ JSObject * insertObj = JSVAL_TO_OBJECT( argv[1] );
+
+ if( JS_IsArrayObject( cx, insertObj ) ){
+ vector<BSONObj> bos;
+
+ jsuint len;
+ JSBool gotLen = JS_GetArrayLength( cx, insertObj, &len );
+ smuassert( cx, "could not get length of array", gotLen );
+
+ for( jsuint i = 0; i < len; i++ ){
+
+ jsval el;
+ JSBool inserted = JS_GetElement( cx, insertObj, i, &el);
+ smuassert( cx, "could not find element in array object", inserted );
+
+ bos.push_back( c.toObject( el ) );
+ }
+
+ conn->insert( ns, bos );
+ }
+ else {
+ BSONObj o = c.toObject( argv[1] );
+ // TODO: add _id
+
+ conn->insert( ns , o );
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const SocketException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.toString().c_str() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16300 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool mongo_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ 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" ) ) {
+ 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] );
+
+ conn->remove( ns , o , justOne );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const SocketException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.toString().c_str() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16301 );
+ }
+ return JS_TRUE;
+ }
+
+ JSFunctionSpec mongo_functions[] = {
+ { "auth" , mongo_auth , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "logout", mongo_logout, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 },
+ { "find" , mongo_find , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "update" , mongo_update , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "insert" , mongo_insert , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "remove" , mongo_remove , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { 0 }
+ };
+
+ // ------------- db_collection -------------
+
+ JSBool db_collection_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ try {
+ smuassert( cx , "db_collection_constructor wrong args" , argc == 4 );
+ Convertor c(cx);
+ if ( haveLocalShardingInfo( c.toString( argv[3] ) ) ) {
+ JS_ReportError( cx , "can't use sharded collection from db.eval" );
+ return JS_FALSE;
+ }
+
+ verify( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
+ verify( JS_SetProperty( cx , obj , "_db" , &(argv[1]) ) );
+ verify( JS_SetProperty( cx , obj , "_shortName" , &(argv[2]) ) );
+ verify( JS_SetProperty( cx , obj , "_fullName" , &(argv[3]) ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16302 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
+ try {
+ 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;
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16303 );
+ }
+ return JS_TRUE;
+ }
+
+ JSClass db_collection_class = {
+ "DBCollection", // class name
+ JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, // flags
+ JS_PropertyStub, // addProperty
+ JS_PropertyStub, // delProperty
+ JS_PropertyStub, // getProperty
+ JS_PropertyStub, // setProperty
+ JS_EnumerateStub, // enumerate
+ (JSResolveOp)db_collection_resolve, // resolve
+ JS_ConvertStub, // convert
+ JS_FinalizeStub, // finalize
+ JSCLASS_NO_OPTIONAL_MEMBERS // optional members
+ };
+
+
+ JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName ) {
+ Convertor c(cx);
+
+ verify( c.hasProperty( db , "_mongo" ) );
+ verify( 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 ) {
+ try {
+ smuassert( cx, "wrong number of arguments to DB" , argc == 2 );
+
+ Convertor convertor( cx );
+ string dbName = convertor.toString( argv[1] );
+ string msg = str::stream() << "[" << dbName << "] is not a valid database name";
+ smuassert( cx, msg.c_str(), NamespaceString::validDBName( dbName ) );
+
+ verify( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
+ verify( JS_SetProperty( cx , obj , "_name" , &(argv[1]) ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16304 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool db_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
+ try {
+ if ( flags & JSRESOLVE_ASSIGNING )
+ return JS_TRUE;
+
+ Convertor c( cx );
+
+ if ( obj == c.getGlobalPrototype( "DB" ) )
+ return JS_TRUE;
+
+ string collname = c.toString( id );
+
+ if ( isSpecialName( collname ) )
+ return JS_TRUE;
+
+ JSObject * proto = JS_GetPrototype( cx , obj );
+ if ( proto && c.hasProperty( proto , collname.c_str() ) )
+ return JS_TRUE;
+
+ JSObject * coll = doCreateCollection( cx , obj , collname );
+ if ( ! coll )
+ return JS_FALSE;
+ c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
+
+ *objp = obj;
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16305 );
+ }
+ return JS_TRUE;
+ }
+
+ JSClass db_class = {
+ "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 ) {
+ try {
+ Convertor c( cx );
+
+ OID oid;
+ if ( argc == 0 ) {
+ oid.init();
+ }
+ else {
+ smuassert( cx , "object_id_constructor can't take more than 1 param" , argc == 1 );
+ string s = c.toString( argv[0] );
+
+ Scope::validateObjectIdString( s );
+ oid.init( s );
+ }
+
+ 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() );
+ verify( JS_SetProperty( cx , obj , "str" , &v ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16306 );
+ }
+ return JS_TRUE;
+ }
+
+ JSClass object_id_class = {
+ "ObjectId" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ // dbpointer
+
+ JSBool dbpointer_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ try {
+ if ( argc != 2 ) {
+ JS_ReportError( cx, "DBPointer takes 2 arguments -- DBPointer(namespace,objectId)" );
+ return JS_FALSE;
+ }
+ if ( ! JSVAL_IS_OID( argv[1] ) ) {
+ JS_ReportError( cx , "2nd argument to DBPointer must be objectId" );
+ return JS_FALSE;
+ }
+ Convertor c( cx );
+ if ( ! JS_InstanceOf( cx , obj , &dbpointer_class , 0 ) ) {
+ obj = JS_NewObject( cx , &dbpointer_class , 0 , 0 );
+ CHECKNEWOBJECT( obj, cx, "dbpointer_constructor" );
+ *rval = OBJECT_TO_JSVAL( obj );
+ }
+ verify( JS_SetProperty( cx , obj , "ns" , &(argv[0]) ) );
+ verify( JS_SetProperty( cx , obj , "id" , &(argv[1]) ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16307 );
+ }
+ return JS_TRUE;
+ }
+
+ JSClass dbpointer_class = {
+ "DBPointer" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ JSFunctionSpec dbpointer_functions[] = {
+ { 0 }
+ };
+
+
+ JSBool dbref_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ try {
+ if ( argc != 2 ) {
+ JS_ReportError( cx , "DBRef takes 2 arguments -- DBRef($ref,$id)" );
+ verify( JS_SetPrivate( cx , obj , (void*)(new BSONHolder( BSONObj().getOwned() ) ) ) );
+ return JS_FALSE;
+ }
+ Convertor c( cx );
+ if ( ! JS_InstanceOf( cx , obj , &dbref_class , 0 ) ) {
+ obj = JS_NewObject( cx , &dbref_class , 0 , 0 );
+ CHECKNEWOBJECT( obj, cx, "dbref_constructor" );
+ *rval = OBJECT_TO_JSVAL( obj );
+ }
+ JSObject * o = JS_NewObject( cx , NULL , NULL, NULL );
+ CHECKNEWOBJECT( o, cx, "dbref_constructor" );
+ verify( JS_SetProperty( cx, o , "$ref" , &argv[ 0 ] ) );
+ verify( JS_SetProperty( cx, o , "$id" , &argv[ 1 ] ) );
+ BSONObj bo = c.toObject( o );
+ verify( JS_SetPrivate( cx , obj , (void*)(new BSONHolder( bo.getOwned() ) ) ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16308 );
+ }
+ return JS_TRUE;
+ }
+
+ JSClass dbref_class = bson_class; // name will be fixed later
+
+ // UUID **************************
+
+#if 0
+ 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);
+ }
+
+zzz
+
+ verify( 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 );
+ verify( 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;
+ verify( 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 }
+ };
+
+#endif
+
+ // BinData **************************
+
+ JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ try {
+ if ( argc != 2 ) {
+ JS_ReportError( cx , "BinData takes 2 arguments -- BinData(subtype,data)" );
+ return JS_FALSE;
+ }
+ Convertor c( cx );
+ int subtype = static_cast<int>( c.toNumber( argv[ 0 ] ) );
+ if ( subtype < 0 || subtype > 255 ) {
+ JS_ReportError( cx, "BinData subtype must be between 0 and 255" );
+ return JS_FALSE;
+ }
+ string encoded( c.toString( argv[ 1 ] ) );
+ string decoded;
+ try {
+ decoded = base64::decode( encoded );
+ }
+ catch(...) {
+ JS_ReportError( cx, "BinData could not decode base64 parameter" );
+ return JS_FALSE;
+ }
+ if ( ! JS_InstanceOf( cx , obj , &bindata_class , 0 ) ) {
+ obj = JS_NewObject( cx , &bindata_class , 0 , 0 );
+ CHECKNEWOBJECT( obj, cx, "bindata_constructor" );
+ *rval = OBJECT_TO_JSVAL( obj );
+ }
+ verify( JS_SetPrivate( cx, obj, new BinDataHolder( decoded.data(), decoded.length() ) ) );
+ c.setProperty( obj, "len", c.toval( (double)decoded.length() ) );
+ c.setProperty( obj, "type", c.toval( (double)subtype ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16309 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool bindata_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ Convertor c(cx);
+ int type = static_cast<int>( c.getNumber( obj, "type" ) );
+ int len = static_cast<int>(c.getNumber( obj, "len" ) );
+ void *holder = JS_GetPrivate( cx, obj );
+ verify( holder );
+ const char *data = ( ( BinDataHolder* )( holder ) )->c_;
+ stringstream ss;
+ ss << "BinData(" << type << ",\"";
+ base64::encode( ss, data, len );
+ ss << "\")";
+ string ret = ss.str();
+ *rval = c.toval( ret.c_str() );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16310 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool bindataBase64(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ Convertor c(cx);
+ int len = static_cast<int>( c.getNumber( obj, "len" ) );
+ void *holder = JS_GetPrivate( cx, obj );
+ verify( holder );
+ const char *data = ( ( BinDataHolder* )( holder ) )->c_;
+ stringstream ss;
+ base64::encode( ss, data, len );
+ string ret = ss.str();
+ *rval = c.toval( ret.c_str() );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16311 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool bindataAsHex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ Convertor c(cx);
+ int len = static_cast<int>( c.getNumber( obj, "len" ) );
+ void *holder = JS_GetPrivate( cx, obj );
+ verify( holder );
+ const char *data = ( ( BinDataHolder* )( holder ) )->c_;
+ stringstream ss;
+ ss.setf( ios_base::hex, ios_base::basefield );
+ ss.fill( '0' );
+ ss.setf( ios_base::right, ios_base::adjustfield );
+ for( int i = 0; i < len; i++ ) {
+ unsigned v = static_cast<unsigned char>( data[i] );
+ ss << setw(2) << v;
+ }
+ string ret = ss.str();
+ *rval = c.toval( ret.c_str() );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16312 );
+ }
+ return JS_TRUE;
+ }
+
+ void bindata_finalize( JSContext * cx , JSObject * obj ) {
+ try {
+ void *holder = JS_GetPrivate( cx, obj );
+ if ( holder ) {
+ delete reinterpret_cast<BinDataHolder*>( holder );
+ verify( JS_SetPrivate( cx , obj , 0 ) );
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16313 );
+ }
+ }
+
+ JSClass bindata_class = {
+ "BinData" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, bindata_finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ 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 } ,
+ { 0 }
+ };
+
+ // Map
+
+ 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 ) {
+ try {
+ if ( argc > 0 ) {
+ JS_ReportError( cx , "Map takes no arguments" );
+ return JS_FALSE;
+ }
+
+ JSObject * arrayObj = JS_NewObject( cx , 0 , 0 , 0 );
+ CHECKNEWOBJECT( arrayObj, cx, "map_constructor" );
+
+ jsval a = OBJECT_TO_JSVAL( arrayObj );
+ JS_SetProperty( cx , obj , "_data" , &a );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16314 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool map_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp ) {
+ try {
+ Convertor c(cx);
+ string str( c.toString( idval ) );
+ if ( ! specialMapString( str ) ) {
+ log() << "illegal prop access: " << str << endl;
+ JS_ReportError( cx , "can't use array access with Map" );
+ return JS_FALSE;
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16315 );
+ }
+ return JS_TRUE;
+ }
+
+ 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 }
+ };
+
+
+ // -----
+
+ JSClass timestamp_class = {
+ "Timestamp" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ 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 ) {
+ try {
+ smuassert( cx,
+ "Timestamp takes 0 or 2 arguments -- Timestamp() or Timestamp(t,i)",
+ 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 ] );
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16316 );
+ }
+ 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 ) {
+ try {
+ 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 ] ) ) {
+ 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 );
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16317 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool numberlong_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ Convertor c(cx);
+ *rval = c.toval( static_cast<double>( c.toNumberLongUnsafe( obj ) ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16318 );
+ }
+ return JS_TRUE;
+ }
+
+ 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) {
+ try {
+ Convertor c(cx);
+ stringstream ss;
+ long long val = c.toNumberLongUnsafe( obj );
+ const long long limit = 2LL << 30;
+
+ if ( val <= -limit || limit <= val )
+ ss << "NumberLong(\"" << val << "\")";
+ else
+ ss << "NumberLong(" << val << ")";
+
+ string ret = ss.str();
+ *rval = c.toval( ret.c_str() );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16319 );
+ }
+ return JS_TRUE;
+ }
+
+ 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 }
+ };
+
+ JSClass numberint_class = {
+ "NumberInt" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ JSBool numberint_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
+ try {
+ smuassert( cx , "NumberInt needs 0 or 1 args" , argc == 0 || argc == 1 );
+
+ if ( ! JS_InstanceOf( cx , obj , &numberint_class , 0 ) ) {
+ obj = JS_NewObject( cx , &numberint_class , 0 , 0 );
+ CHECKNEWOBJECT( obj, cx, "numberint_constructor" );
+ *rval = OBJECT_TO_JSVAL( obj );
+ }
+
+ Convertor c( cx );
+ if ( argc == 0 ) {
+ c.setProperty( obj, "floatApprox", c.toval( 0.0 ) );
+ }
+ else if ( JSVAL_IS_NUMBER( argv[ 0 ] ) ) {
+ c.setProperty( obj, "floatApprox", argv[ 0 ] );
+ }
+ else {
+ string num = c.toString( argv[ 0 ] );
+ //PRINT(num);
+ const char *numStr = num.c_str();
+ int n;
+ try {
+ n = static_cast<int>( parseLL( numStr ) );
+ //PRINT(n);
+ }
+ catch ( const AssertionException & ) {
+ smuassert( cx , "could not convert string to integer" , false );
+ }
+ c.makeIntObj( n, obj );
+ }
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16320 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool numberint_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ Convertor c(cx);
+ *rval = c.toval( static_cast<double>( c.toNumberInt( obj ) ) );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16321 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool numberint_tonumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ return numberint_valueof( cx, obj, argc, argv, rval );
+ }
+
+ JSBool numberint_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ try {
+ Convertor c(cx);
+ int val = c.toNumberInt( obj );
+ string ret = str::stream() << "NumberInt(" << val << ")";
+ *rval = c.toval( ret.c_str() );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16322 );
+ }
+ return JS_TRUE;
+ }
+
+ JSFunctionSpec numberint_functions[] = {
+ { "valueOf" , numberint_valueof , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "toNumber" , numberint_tonumber , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { "toString" , numberint_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
+ { 0 }
+ };
+
+ JSClass minkey_class = {
+ "MinKey" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ JSClass maxkey_class = {
+ "MaxKey" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ 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 ) {
+ try {
+ 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] );
+ c.setProperty( obj , "_collection" , argv[2] );
+ c.setProperty( obj , "_ns" , argv[3] );
+
+ if ( argc > 4 && JSVAL_IS_OBJECT( argv[4] ) )
+ c.setProperty( obj , "_query" , argv[4] );
+ else {
+ JSObject * temp = JS_NewObject( cx , 0 , 0 , 0 );
+ 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
+ c.setProperty( obj , "_limit" , JSVAL_ZERO );
+
+ if ( argc > 7 && JSVAL_IS_NUMBER( argv[7] ) )
+ c.setProperty( obj , "_skip" , argv[7] );
+ else
+ c.setProperty( obj , "_skip" , JSVAL_ZERO );
+
+ if ( argc > 8 && JSVAL_IS_NUMBER( argv[8] ) )
+ c.setProperty( obj , "_batchSize" , argv[8] );
+ else
+ c.setProperty( obj , "_batchSize" , JSVAL_ZERO );
+
+ if ( argc > 9 && JSVAL_IS_NUMBER( argv[9] ) )
+ c.setProperty( obj , "_options" , argv[9] );
+ 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 );
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16323 );
+ }
+ return JS_TRUE;
+ }
+
+ JSBool dbquery_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
+ try {
+ if ( flags & JSRESOLVE_ASSIGNING )
+ return JS_TRUE;
+
+ if ( ! JSVAL_IS_NUMBER( id ) )
+ return JS_TRUE;
+
+ jsval val = JSVAL_VOID;
+ verify( JS_CallFunctionName( cx , obj , "arrayAccess" , 1 , &id , &val ) );
+ Convertor c(cx);
+ c.setProperty( obj , c.toString( id ).c_str() , val );
+ *objp = obj;
+ }
+ catch ( const AssertionException& e ) {
+ if ( ! JS_IsExceptionPending( cx ) ) {
+ JS_ReportError( cx, e.what() );
+ }
+ return JS_FALSE;
+ }
+ catch ( const std::exception& e ) {
+ log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
+ fassertFailed( 16324 );
+ }
+ return JS_TRUE;
+ }
+
+ JSClass dbquery_class = {
+ "DBQuery" , JSCLASS_NEW_RESOLVE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ 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 ) {
+
+ verify( JS_InitClass( cx , global , 0 , &mongo_class , local ? mongo_local_constructor : mongo_external_constructor , 0 , 0 , mongo_functions , 0 , 0 ) );
+
+ verify( JS_InitClass( cx , global , 0 , &object_id_class , object_id_constructor , 0 , 0 , 0 , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &db_class , db_constructor , 2 , 0 , 0 , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &db_collection_class , db_collection_constructor , 4 , 0 , 0 , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &internal_cursor_class , internal_cursor_constructor , 0 , 0 , internal_cursor_functions , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &dbquery_class , dbquery_constructor , 0 , 0 , 0 , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &dbpointer_class , dbpointer_constructor , 0 , 0 , dbpointer_functions , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) );
+// verify( JS_InitClass( cx , global , 0 , &uuid_class , uuid_constructor , 0 , 0 , uuid_functions , 0 , 0 ) );
+
+ verify( JS_InitClass( cx , global , 0 , &timestamp_class , timestamp_constructor , 0 , 0 , 0 , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &numberlong_class , numberlong_constructor , 0 , 0 , numberlong_functions , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &numberint_class , numberint_constructor , 0 , 0 , numberint_functions , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
+
+ verify( JS_InitClass( cx , global , 0 , &map_class , map_constructor , 0 , 0 , map_functions , 0 , 0 ) );
+
+ verify( JS_InitClass( cx , global , 0 , &bson_ro_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
+ verify( JS_InitClass( cx , global , 0 , &bson_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
+
+ static const char *dbrefName = "DBRef";
+ dbref_class.name = dbrefName;
+ verify( JS_InitClass( cx , global , 0 , &dbref_class , dbref_constructor , 2 , 0 , bson_functions , 0 , 0 ) );
+
+ 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 ) ) {
+ OID oid;
+ oid.init( c->getString( o , "str" ) );
+ b.append( name , oid );
+ return true;
+ }
+
+ if ( JS_InstanceOf( c->_context , o , &minkey_class , 0 ) ) {
+ b.appendMinKey( name );
+ return true;
+ }
+
+ if ( JS_InstanceOf( c->_context , o , &maxkey_class , 0 ) ) {
+ b.appendMaxKey( name );
+ return true;
+ }
+
+ 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 ) ) {
+ b.append( name , c->toNumberLongUnsafe( o ) );
+ return true;
+ }
+
+ if ( JS_InstanceOf( c->_context , o , &numberint_class , 0 ) ) {
+ b.append( name , c->toNumberInt( o ) );
+ return true;
+ }
+
+ if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ) {
+ b.appendDBRef( name , c->getString( o , "ns" ) , c->toOID( c->getProperty( o , "id" ) ) );
+ return true;
+ }
+
+ 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" ) ) ) ,
+ 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 ) {
+ 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 , Date_t(d) );
+ return true;
+ }
+#else
+ if ( JS_InstanceOf( c->_context , o, &js_DateClass , 0 ) ) {
+ jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
+ long long d2 = (long long)d;
+ b.appendDate( name , Date_t((unsigned long long)d2) );
+ return true;
+ }
+#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 ) ) {
+ b.append( name , c->toString( val ) );
+ return true;
+ }
+
+#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 ) ) {
+ c->appendRegex( b , name , c->toString( val ) );
+ return true;
+ }
+#endif
+
+ return false;
+ }
+
+ 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
+ }
+
+}