diff options
author | agirbal <antoine@10gen.com> | 2011-04-27 11:57:10 -0700 |
---|---|---|
committer | agirbal <antoine@10gen.com> | 2011-05-02 16:08:41 -0700 |
commit | 1725803c209ddf6ca52190447ddb1bdbc7371719 (patch) | |
tree | 4af2d44f3eeb3340ba0f5d5c902ce098b5389338 | |
parent | 52c03d3954fe7bc988a04b05e28882a266f5be0d (diff) | |
download | mongo-1725803c209ddf6ca52190447ddb1bdbc7371719.tar.gz |
SERVER-2579: all v8 object creation is now lazy.
Correct allocation and freeing of underlying C++ object.
Correct ordering of object properties even when new ones are added within JS.
Supports deletion of properties with lazzy wrapper.
Also removed scope->setThis in favor of passing it in the invoke() command.
-rw-r--r-- | db/commands/group.cpp | 7 | ||||
-rw-r--r-- | db/commands/mr.cpp | 7 | ||||
-rw-r--r-- | db/dbeval.cpp | 2 | ||||
-rw-r--r-- | db/matcher.cpp | 4 | ||||
-rw-r--r-- | dbtests/jstests.cpp | 12 | ||||
-rw-r--r-- | scripting/engine.cpp | 14 | ||||
-rw-r--r-- | scripting/engine.h | 14 | ||||
-rw-r--r-- | scripting/engine_spidermonkey.cpp | 12 | ||||
-rw-r--r-- | scripting/engine_v8.cpp | 187 | ||||
-rw-r--r-- | scripting/engine_v8.h | 10 | ||||
-rw-r--r-- | scripting/v8_db.cpp | 2 | ||||
-rw-r--r-- | shell/dbshell.cpp | 3 |
12 files changed, 156 insertions, 118 deletions
diff --git a/db/commands/group.cpp b/db/commands/group.cpp index 422c35588b7..be6213da889 100644 --- a/db/commands/group.cpp +++ b/db/commands/group.cpp @@ -36,7 +36,8 @@ namespace mongo { if ( func ) { BSONObjBuilder b( obj.objsize() + 32 ); b.append( "0" , obj ); - int res = s->invoke( func , b.obj() ); + const BSONObj& key = b.obj(); + int res = s->invoke( func , &key, 0 ); uassert( 10041 , (string)"invoke failed in $keyf: " + s->getError() , res == 0 ); int type = s->type("return"); uassert( 10042 , "return of $key has to be an object" , type == Object ); @@ -110,7 +111,7 @@ namespace mongo { s->setObject( "obj" , obj , true ); s->setNumber( "n" , n - 1 ); - if ( s->invoke( f , BSONObj() , 0 , true ) ) { + if ( s->invoke( f , 0, 0 , 0 , true ) ) { throw UserException( 9010 , (string)"reduce invoke failed: " + s->getError() ); } } @@ -125,7 +126,7 @@ namespace mongo { " $arr[i] = ret; " " } " "}" ); - s->invoke( g , BSONObj() , 0 , true ); + s->invoke( g , 0, 0 , 0 , true ); } result.appendArray( "retval" , s->getObject( "$arr" ) ); diff --git a/db/commands/mr.cpp b/db/commands/mr.cpp index 75d65553edc..f70922a6eef 100644 --- a/db/commands/mr.cpp +++ b/db/commands/mr.cpp @@ -66,8 +66,7 @@ namespace mongo { void JSMapper::map( const BSONObj& o ) { Scope * s = _func.scope(); assert( s ); - s->setThis( &o ); - if ( s->invoke( _func.func() , _params , 0 , true ) ) + if ( s->invoke( _func.func() , &_params, &o , 0 , true ) ) throw UserException( 9014, str::stream() << "map invoke failed: " + s->getError() ); } @@ -79,7 +78,7 @@ namespace mongo { Scope * s = _func.scope(); Scope::NoDBAccess no = s->disableDBAccess( "can't access db inside finalize" ); - s->invokeSafe( _func.func() , o ); + s->invokeSafe( _func.func() , &o, 0 ); // don't want to use o.objsize() to size b // since there are many cases where the point of finalize @@ -183,7 +182,7 @@ namespace mongo { Scope * s = _func.scope(); - s->invokeSafe( _func.func() , args ); + s->invokeSafe( _func.func() , &args, 0 ); if ( s->type( "return" ) == Array ) { uasserted( 10075 , "reduce -> multiple not supported yet"); diff --git a/db/dbeval.cpp b/db/dbeval.cpp index 31d52609b6e..5a0671296e6 100644 --- a/db/dbeval.cpp +++ b/db/dbeval.cpp @@ -86,7 +86,7 @@ namespace mongo { int res; { Timer t; - res = s->invoke(f,args, cmdLine.quota ? 10 * 60 * 1000 : 0 ); + res = s->invoke(f, &args, 0, cmdLine.quota ? 10 * 60 * 1000 : 0 ); int m = t.millis(); if ( m > cmdLine.slowMS ) { out() << "dbeval slow, time: " << dec << m << "ms " << dbName << endl; diff --git a/db/matcher.cpp b/db/matcher.cpp index 4a96f5c38ca..c210984d55d 100644 --- a/db/matcher.cpp +++ b/db/matcher.cpp @@ -892,12 +892,10 @@ namespace mongo { if ( where->jsScope ) { where->scope->init( where->jsScope ); } - where->scope->setThis( const_cast< BSONObj * >( &jsobj ) ); where->scope->setObject( "obj", const_cast< BSONObj & >( jsobj ) ); where->scope->setBoolean( "fullObject" , true ); // this is a hack b/c fullObject used to be relevant - int err = where->scope->invoke( where->func , BSONObj() , 1000 * 60 , false ); - where->scope->setThis( 0 ); + int err = where->scope->invoke( where->func , 0, &jsobj , 1000 * 60 , false ); if ( err == -3 ) { // INVOKE_ERROR stringstream ss; ss << "error on invocation of $where function:\n" diff --git a/dbtests/jstests.cpp b/dbtests/jstests.cpp index c33b2005b38..3611a2ce998 100644 --- a/dbtests/jstests.cpp +++ b/dbtests/jstests.cpp @@ -143,8 +143,7 @@ namespace JSTests { s->invoke( "return blah.y;" , BSONObj() ); ASSERT_EQUALS( "eliot" , s->getString( "return" ) ); - s->setThis( & o ); - s->invoke( "return this.z;" , BSONObj() ); + s->invoke( "return this.z;" , 0, &o ); ASSERT_EQUALS( "sara" , s->getString( "return" ) ); s->invoke( "return this.z == 'sara';" , BSONObj() ); @@ -715,9 +714,8 @@ namespace JSTests { } //cout << "ELIOT: " << b.jsonString() << endl; - s->setThis( &b ); // its ok if this is handled by js, just can't create a c++ exception - s->invoke( "x=this.x.length;" , BSONObj() ); + s->invoke( "x=this.x.length;" , 0, &b ); } }; @@ -903,12 +901,11 @@ namespace JSTests { s.reset( globalScriptEngine->newScope() ); ScriptingFunction f = s->createFunction( "return this.x + 6;" ); - s->setThis( &start ); Timer t; double n = 0; for ( ; n < 100000; n++ ) { - s->invoke( f , empty ); + s->invoke( f , &empty, &start ); ASSERT_EQUALS( 11 , s->getNumber( "return" ) ); } //cout << "speed1: " << ( n / t.millis() ) << " ops/ms" << endl; @@ -934,10 +931,9 @@ namespace JSTests { BSONObjBuilder b; s->append( b , "z" , "x" ); temp = b.obj(); - s->setThis( &temp ); } - s->invokeSafe( "foo = this.z();" , BSONObj() ); + s->invokeSafe( "foo = this.z();" , 0, &temp ); ASSERT_EQUALS( 17 , s->getNumber( "foo" ) ); } }; diff --git a/scripting/engine.cpp b/scripting/engine.cpp index f995f7a4079..3f6c84cc143 100644 --- a/scripting/engine.cpp +++ b/scripting/engine.cpp @@ -85,10 +85,10 @@ namespace mongo { } - int Scope::invoke( const char* code , const BSONObj& args, int timeoutMs ) { + int Scope::invoke( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs ) { ScriptingFunction func = createFunction( code ); uassert( 10207 , "compile failed" , func ); - return invoke( func , args, timeoutMs ); + return invoke( func , args, recv, timeoutMs ); } bool Scope::execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs ) { @@ -394,9 +394,9 @@ namespace mongo { void setBoolean( const char *field , bool val ) { _real->setBoolean( field , val ); } - void setThis( const BSONObj * obj ) { - _real->setThis( obj ); - } +// void setThis( const BSONObj * obj ) { +// _real->setThis( obj ); +// } ScriptingFunction createFunction( const char * code ) { return _real->createFunction( code ); @@ -413,8 +413,8 @@ namespace mongo { /** * @return 0 on success */ - int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs , bool ignoreReturn ) { - return _real->invoke( func , args , timeoutMs , ignoreReturn ); + int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs , bool ignoreReturn ) { + return _real->invoke( func , args , recv, timeoutMs , ignoreReturn ); } string getError() { diff --git a/scripting/engine.h b/scripting/engine.h index e2aeb423e00..bec8abc76df 100644 --- a/scripting/engine.h +++ b/scripting/engine.h @@ -76,7 +76,7 @@ namespace mongo { virtual void setString( const char *field , const char * val ) = 0; virtual void setObject( const char *field , const BSONObj& obj , bool readOnly=true ) = 0; virtual void setBoolean( const char *field , bool val ) = 0; - virtual void setThis( const BSONObj * obj ) = 0; +// virtual void setThis( const BSONObj * obj ) = 0; virtual ScriptingFunction createFunction( const char * code ); @@ -84,18 +84,18 @@ namespace mongo { /** * @return 0 on success */ - virtual int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = false ) = 0; - void invokeSafe( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 ) { - int res = invoke( func , args , timeoutMs ); + virtual int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false ) = 0; + void invokeSafe( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 ) { + int res = invoke( func , args , recv, timeoutMs ); if ( res == 0 ) return; throw UserException( 9004 , (string)"invoke failed: " + getError() ); } virtual string getError() = 0; - int invoke( const char* code , const BSONObj& args, int timeoutMs = 0 ); - void invokeSafe( const char* code , const BSONObj& args, int timeoutMs = 0 ) { - if ( invoke( code , args , timeoutMs ) == 0 ) + int invoke( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 ); + void invokeSafe( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 ) { + if ( invoke( code , args , recv, timeoutMs ) == 0 ) return; throw UserException( 9005 , (string)"invoke failed: " + getError() ); } diff --git a/scripting/engine_spidermonkey.cpp b/scripting/engine_spidermonkey.cpp index b455145568e..0dfee9a568a 100644 --- a/scripting/engine_spidermonkey.cpp +++ b/scripting/engine_spidermonkey.cpp @@ -1463,13 +1463,13 @@ namespace mongo { return worked; } - int invoke( JSFunction * func , const BSONObj& args, int timeoutMs , bool ignoreReturn ) { + int invoke( JSFunction * func , const BSONObj* args, const BSONObj* recv, int timeoutMs , bool ignoreReturn ) { smlock; precall(); assert( JS_EnterLocalRootScope( _context ) ); - int nargs = args.nFields(); + int nargs = args ? args->nFields() : 0; scoped_array<jsval> smargsPtr( new jsval[nargs] ); if ( nargs ) { BSONObjIterator it( args ); @@ -1478,18 +1478,20 @@ namespace mongo { } } - if ( args.isEmpty() ) { + if ( !args ) { _convertor->setProperty( _global , "args" , JSVAL_NULL ); } else { - setObject( "args" , args , true ); // this is for backwards compatability + setObject( "args" , *args , true ); // this is for backwards compatability } JS_LeaveLocalRootScope( _context ); installInterrupt( timeoutMs ); jsval rval; + setThis(recv); JSBool ret = JS_CallFunction( _context , _this ? _this : _global , func , nargs , smargsPtr.get() , &rval ); + setThis(0); uninstallInterrupt( timeoutMs ); if ( !ret ) { @@ -1503,7 +1505,7 @@ namespace mongo { return 0; } - int invoke( ScriptingFunction funcAddr , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = 0 ) { + int invoke( ScriptingFunction funcAddr , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = 0 ) { return invoke( (JSFunction*)funcAddr , args , timeoutMs , ignoreReturn ); } diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp index 7f3f5cee5e5..d3f40265e1d 100644 --- a/scripting/engine_v8.cpp +++ b/scripting/engine_v8.cpp @@ -39,9 +39,26 @@ namespace mongo { return static_cast<BSONObj*>(ptr); } + static void weakRefCallback(v8::Persistent<v8::Value> p, void* scope) { + // should we lock here? no idea, and no doc from v8 of course + HandleScope handle_scope; + if (!p.IsNearDeath()) + return; + BSONObj* obj = unwrapBSONObj(v8::Persistent<v8::Object>::Cast(p)); + delete obj; + p.Dispose(); + cout << "deleted obj" << endl; + } + static Handle<v8::Value> namedGet(Local<v8::String> name, const v8::AccessorInfo &info) { - if (info.This()->HasRealNamedProperty(name)) { - return info.This()->GetRealNamedProperty(name); + // all properties should be set, otherwise means builtin or deleted + if (!(info.This()->HasRealNamedProperty(name))) + return v8::Handle<v8::Value>(); + + Handle<v8::Value> val = info.This()->GetRealNamedProperty(name); + if (!val->IsUndefined()) { + // value already cached + return val; } string key = toSTLString(name); @@ -51,8 +68,8 @@ namespace mongo { BSONElement elmt = obj->getField(key.c_str()); Local< External > scp = External::Cast( *info.Data() ); V8Scope* scope = (V8Scope*)(scp->Value()); - Handle<Value> val = scope->mongoToV8Element(elmt, true); - info.This()->ForceSet(name, val, DontEnum); + val = scope->mongoToV8Element(elmt); + info.This()->ForceSet(name, val); return val; } @@ -60,21 +77,21 @@ namespace mongo { // return Handle<Value>(); // } - static Handle<v8::Array> namedEnumerator(const AccessorInfo &info) { - BSONObj *obj = unwrapBSONObj(info.Holder()); - Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields())); - int i = 0; - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - // note here that if keys are parseable number, v8 will access them using index - for ( BSONObjIterator it(*obj); it.more(); ++i) { - const BSONElement& f = it.next(); -// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName()))); - Handle<v8::String> name = scope->getV8Str(f.fieldName()); - arr->Set(i, name); - } - return arr; - } +// static Handle<v8::Array> namedEnumerator(const AccessorInfo &info) { +// BSONObj *obj = unwrapBSONObj(info.Holder()); +// Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields())); +// int i = 0; +// Local< External > scp = External::Cast( *info.Data() ); +// V8Scope* scope = (V8Scope*)(scp->Value()); +// // note here that if keys are parseable number, v8 will access them using index +// for ( BSONObjIterator it(*obj); it.more(); ++i) { +// const BSONElement& f = it.next(); +//// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName()))); +// Handle<v8::String> name = scope->getV8Str(f.fieldName()); +// arr->Set(i, name); +// } +// return arr; +// } // v8::Handle<v8::Integer> namedQuery(Local<v8::String> property, const AccessorInfo& info) { // string key = ToString(property); @@ -82,21 +99,29 @@ namespace mongo { // } static Handle<v8::Value> indexedGet(uint32_t index, const v8::AccessorInfo &info) { + // all properties should be set, otherwise means builtin or deleted + if (!(info.This()->HasRealIndexedProperty(index))) + return v8::Handle<v8::Value>(); + StringBuilder ss; ss << index; string key = ss.str(); Local< External > scp = External::Cast( *info.Data() ); V8Scope* scope = (V8Scope*)(scp->Value()); - Handle<v8::String> name = scope->getV8Str(key); - // v8 API really confusing here, must check existence on index, but then fetch with name - if (info.This()->HasRealIndexedProperty(index)) - return info.This()->GetRealNamedProperty(name); + // cannot get v8 to properly cache the indexed val in the js object +// Handle<v8::String> name = scope->getV8Str(key); +// // v8 API really confusing here, must check existence on index, but then fetch with name +// if (info.This()->HasRealIndexedProperty(index)) { +// Handle<v8::Value> val = info.This()->GetRealNamedProperty(name); +// if (!val.IsEmpty() && !val->IsNull()) +// return val; +// } BSONObj *obj = unwrapBSONObj(info.Holder()); if (!obj || !obj->hasElement(key.c_str())) return Handle<Value>(); BSONElement elmt = obj->getField(key); - Handle<Value> val = scope->mongoToV8Element(elmt, true); - info.This()->ForceSet(name, val); + Handle<Value> val = scope->mongoToV8Element(elmt); +// info.This()->ForceSet(name, val); return val; } @@ -104,19 +129,19 @@ namespace mongo { // return Handle<Value>(); // } - static Handle<v8::Array> indexedEnumerator(const AccessorInfo &info) { - BSONObj *obj = unwrapBSONObj(info.Holder()); - Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields())); - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - int i = 0; - for ( BSONObjIterator it(*obj); it.more(); ++i) { - const BSONElement& f = it.next(); -// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName()))); - arr->Set(i, scope->getV8Str(f.fieldName())); - } - return arr; - } +// static Handle<v8::Array> indexedEnumerator(const AccessorInfo &info) { +// BSONObj *obj = unwrapBSONObj(info.Holder()); +// Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields())); +// Local< External > scp = External::Cast( *info.Data() ); +// V8Scope* scope = (V8Scope*)(scp->Value()); +// int i = 0; +// for ( BSONObjIterator it(*obj); it.more(); ++i) { +// const BSONElement& f = it.next(); +//// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName()))); +// arr->Set(i, scope->getV8Str(f.fieldName())); +// } +// return arr; +// } // --- engine --- @@ -160,12 +185,12 @@ namespace mongo { _context = Context::New(); Context::Scope context_scope( _context ); _global = Persistent< v8::Object >::New( _context->Global() ); - _this = Persistent< v8::Object >::New( v8::Object::New() ); + _emptyObj = Persistent< v8::Object >::New( v8::Object::New() ); // initialize lazy object template lzObjectTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); lzObjectTemplate->SetInternalFieldCount( 1 ); - lzObjectTemplate->SetNamedPropertyHandler(namedGet, 0, 0, 0, namedEnumerator, v8::External::New(this)); + lzObjectTemplate->SetNamedPropertyHandler(namedGet, 0, 0, 0, 0, v8::External::New(this)); lzObjectTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0, v8::External::New(this)); // initialize lazy array template @@ -174,7 +199,7 @@ namespace mongo { // this it creates issues when calling certain methods that check array type lzArrayTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); lzArrayTemplate->SetInternalFieldCount( 1 ); - lzArrayTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, indexedEnumerator, v8::External::New(this)); + lzArrayTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0, v8::External::New(this)); V8STR_CONN = getV8Str( "_conn" ); V8STR_ID = getV8Str( "_id" ); @@ -208,7 +233,7 @@ namespace mongo { V8Lock l; Context::Scope context_scope( _context ); _wrapper.Dispose(); - _this.Dispose(); + _emptyObj.Dispose(); for( unsigned i = 0; i < _funcs.size(); ++i ) _funcs[ i ].Dispose(); _funcs.clear(); @@ -480,18 +505,18 @@ namespace mongo { return num; } - void V8Scope::setThis( const BSONObj * obj ) { - V8_SIMPLE_HEADER - if ( ! obj ) { - _this = Persistent< v8::Object >::New( v8::Object::New() ); - return; - } - - //_this = mongoToV8( *obj ); - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::External::New( createWrapperHolder( this, obj , true , false ) ); - _this = Persistent< v8::Object >::New( _wrapper->NewInstance( 1, argv ) ); - } +// void V8Scope::setThis( const BSONObj * obj ) { +// V8_SIMPLE_HEADER +// if ( ! obj ) { +// _this = Persistent< v8::Object >::New( v8::Object::New() ); +// return; +// } +// +// //_this = mongoToV8( *obj ); +// v8::Handle<v8::Value> argv[1]; +// argv[0] = v8::External::New( createWrapperHolder( this, obj , true , false ) ); +// _this = Persistent< v8::Object >::New( _wrapper->NewInstance( 1, argv ) ); +// } void V8Scope::rename( const char * from , const char * to ) { V8_SIMPLE_HEADER; @@ -501,21 +526,21 @@ namespace mongo { _global->Set( f , v8::Undefined() ); } - int V8Scope::invoke( ScriptingFunction func , const BSONObj& argsObject, int timeoutMs , bool ignoreReturn ) { + int V8Scope::invoke( ScriptingFunction func , const BSONObj* argsObject, const BSONObj* recv, int timeoutMs , bool ignoreReturn ) { V8_SIMPLE_HEADER Handle<Value> funcValue = _funcs[func-1]; TryCatch try_catch; - int nargs = argsObject.nFields(); + int nargs = argsObject ? argsObject->nFields() : 0; scoped_array< Handle<Value> > args; if ( nargs ) { args.reset( new Handle<Value>[nargs] ); - BSONObjIterator it( argsObject ); + BSONObjIterator it( *argsObject ); for ( int i=0; i<nargs; i++ ) { BSONElement next = it.next(); args[i] = mongoToV8Element( next ); } - setObject( "args", argsObject, true ); // for backwards compatibility + setObject( "args", *argsObject, true ); // for backwards compatibility } else { _global->Set( V8STR_ARGS, v8::Undefined() ); @@ -527,8 +552,14 @@ namespace mongo { log() << _error << endl; return 1; } + Handle<v8::Object> v8recv; + if (recv != 0) + v8recv = mongoToLZV8(*recv, false); + else + v8recv = _emptyObj; + enableV8Interrupt(); // because of v8 locker we can check interrupted, then enable - Local<Value> result = ((v8::Function*)(*funcValue))->Call( _this , nargs , args.get() ); + Local<Value> result = ((v8::Function*)(*funcValue))->Call( v8recv , nargs , nargs ? args.get() : 0 ); disableV8Interrupt(); if ( result.IsEmpty() ) { @@ -837,9 +868,12 @@ namespace mongo { break; case mongo::Array: + sub = f.embeddedObject(); + o->Set( name , mongoToV8( sub , true, readOnly ) ); + break; case mongo::Object: sub = f.embeddedObject(); - o->Set( name , mongoToV8( sub , f.type() == mongo::Array, readOnly ) ); + o->Set( name , mongoToLZV8( sub , false, readOnly ) ); break; case mongo::Date: @@ -958,7 +992,7 @@ namespace mongo { /** * converts a BSONObj to a Lazy V8 object */ - Local<v8::Object> V8Scope::mongoToLZV8( const BSONObj& m , bool array, bool readOnly ) { + Handle<v8::Object> V8Scope::mongoToLZV8( const BSONObj& m , bool array, bool readOnly ) { Local<v8::Object> o; if (array) { @@ -979,12 +1013,21 @@ namespace mongo { } } - BSONObj* p = new BSONObj(m); - o->SetInternalField(0, v8::External::New(p)); - return o; + o->SetInternalField(0, v8::External::New((new BSONObj( m.getOwned() )))); + // need to set all keys with dummy values, so that order of keys is correct during enumeration + // otherwise v8 will list any newly set property in JS before the ones of underlying BSON obj. + for (BSONObjIterator it(m); it.more();) { + const BSONElement& f = it.next(); + o->ForceSet(getV8Str(f.fieldName()), v8::Undefined()); + } + + Persistent<v8::Object> p = Persistent<v8::Object>::New(o); + p.MakeWeak(this, weakRefCallback); + cout << "created obj" << endl; + return p; } - Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f, bool lazy ) { + Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f ) { Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New(); internalFieldObjects->SetInternalFieldCount( 1 ); @@ -1013,11 +1056,9 @@ namespace mongo { // - the lazy array is not a true v8 array and requires some v8 src change for all methods to work // - it made several tests about 1.5x slower // - most times when an array is accessed, all its values will be used - return mongoToV8( f.embeddedObject() , f.type() == mongo::Array ); + return mongoToV8( f.embeddedObject() , true ); case mongo::Object: - if (lazy) - return mongoToLZV8( f.embeddedObject() , f.type() == mongo::Array); - return mongoToV8( f.embeddedObject() , f.type() == mongo::Array ); + return mongoToLZV8( f.embeddedObject() , false); case mongo::Date: return v8::Date::New( f.date() ); @@ -1252,11 +1293,11 @@ namespace mongo { Local<v8::Array> names = o->GetPropertyNames(); for ( unsigned int i=0; i<names->Length(); i++ ) { - v8::Local<v8::String> name = names->Get(v8::Integer::New(i) )->ToString(); + v8::Local<v8::String> name = names->Get( i )->ToString(); - if ( o->GetPrototype()->IsObject() && - o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) ) - continue; +// if ( o->GetPrototype()->IsObject() && +// o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) ) +// continue; v8::Local<v8::Value> value = o->Get( name ); diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h index bc4fec222b8..f8acc9d1346 100644 --- a/scripting/engine_v8.h +++ b/scripting/engine_v8.h @@ -85,13 +85,13 @@ namespace mongo { virtual void setBoolean( const char *field , bool val ); virtual void setElement( const char *field , const BSONElement& e ); virtual void setObject( const char *field , const BSONObj& obj , bool readOnly); - virtual void setThis( const BSONObj * obj ); +// virtual void setThis( const BSONObj * obj ); virtual void rename( const char * from , const char * to ); virtual ScriptingFunction _createFunction( const char * code ); Local< v8::Function > __createFunction( const char * code ); - virtual int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = false ); + virtual int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false ); virtual bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ); virtual string getError() { return _error; } @@ -107,12 +107,12 @@ namespace mongo { Handle< Context > context() const { return _context; } v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false ); - v8::Local<v8::Object> mongoToLZV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false ); + v8::Handle<v8::Object> mongoToLZV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false ); mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0 ); void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth = 0 ); - v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool lazy = true ); + v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f ); v8::Function * getNamedCons( const char * name ); v8::Function * getObjectIdCons(); @@ -156,7 +156,7 @@ namespace mongo { string _error; vector< Persistent<Value> > _funcs; - v8::Persistent<v8::Object> _this; + v8::Persistent<v8::Object> _emptyObj; v8::Persistent<v8::Function> _wrapper; diff --git a/scripting/v8_db.cpp b/scripting/v8_db.cpp index 6c4fdec505d..bb80957c8e6 100644 --- a/scripting/v8_db.cpp +++ b/scripting/v8_db.cpp @@ -403,7 +403,7 @@ namespace mongo { V8Unlock u; o = cursor->next(); } - return scope->mongoToV8( o ); + return scope->mongoToLZV8( o ); } v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args) { diff --git a/shell/dbshell.cpp b/shell/dbshell.cpp index 9868c427ecb..5a3cf375b4d 100644 --- a/shell/dbshell.cpp +++ b/shell/dbshell.cpp @@ -66,7 +66,8 @@ void generateCompletions( const string& prefix , vector<string>& all ) { if ( prefix.find( '"' ) != string::npos ) return; - shellMainScope->invokeSafe("function(x) {shellAutocomplete(x)}", BSON("0" << prefix), 1000); + BSONObj args = BSON("0" << prefix); + shellMainScope->invokeSafe("function(x) {shellAutocomplete(x)}", &args, 0, 1000); BSONObjBuilder b; shellMainScope->append( b , "" , "__autocomplete__" ); BSONObj res = b.obj(); |