diff options
author | agirbal <antoine@10gen.com> | 2011-05-10 15:23:45 -0700 |
---|---|---|
committer | agirbal <antoine@10gen.com> | 2011-05-10 15:30:02 -0700 |
commit | 13e71279c964fc3f84e682901f37152935557f70 (patch) | |
tree | 4882c553e0d1769f82dd6260b0bcc54fe7e6af33 /scripting | |
parent | 8d13203c40150d93b5747adf4430a8b6edd21ac9 (diff) | |
download | mongo-13e71279c964fc3f84e682901f37152935557f70.tar.gz |
SERVER-2579: added readonly object for lazy v8 objects, which makes them much faster to access
SERVER-2976: further implementation of jsMode for M/R. Can be turned on using temp flag jsMode:true
Diffstat (limited to 'scripting')
-rw-r--r-- | scripting/engine_v8.cpp | 135 | ||||
-rw-r--r-- | scripting/engine_v8.h | 63 |
2 files changed, 136 insertions, 62 deletions
diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp index b0286dd3fd3..548401bab69 100644 --- a/scripting/engine_v8.cpp +++ b/scripting/engine_v8.cpp @@ -33,10 +33,10 @@ namespace mongo { */ static BSONObj* unwrapBSONObj(const Handle<v8::Object>& obj) { Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0)); - if (field.IsEmpty() || !field->IsExternal()) - return 0; +// if (field.IsEmpty() || !field->IsExternal()) +// return 0; void* ptr = field->Value(); - return static_cast<BSONObj*>(ptr); + return (BSONObj*)ptr; } static void weakRefCallback(v8::Persistent<v8::Value> p, void* scope) { @@ -62,16 +62,28 @@ namespace mongo { string key = toSTLString(name); BSONObj *obj = unwrapBSONObj(info.Holder()); - if (!obj || !obj->hasElement(key.c_str())) - return Handle<Value>(); BSONElement elmt = obj->getField(key.c_str()); + if (elmt.eoo()) + return Handle<Value>(); Local< External > scp = External::Cast( *info.Data() ); V8Scope* scope = (V8Scope*)(scp->Value()); - val = scope->mongoToV8Element(elmt); + val = scope->mongoToV8Element(elmt, false); info.This()->ForceSet(name, val); return val; } + static Handle<v8::Value> namedGetRO(Local<v8::String> name, const v8::AccessorInfo &info) { + string key = toSTLString(name); + BSONObj *obj = unwrapBSONObj(info.Holder()); + BSONElement elmt = obj->getField(key.c_str()); + if (elmt.eoo()) + return Handle<Value>(); + Local< External > scp = External::Cast( *info.Data() ); + V8Scope* scope = (V8Scope*)(scp->Value()); + Handle<v8::Value> val = scope->mongoToV8Element(elmt, true); + return val; + } + // static Handle<v8::Value> namedSet(Local<v8::String> name, Local<v8::Value> value_obj, const v8::AccessorInfo& info) { // return Handle<Value>(); // } @@ -116,10 +128,33 @@ namespace mongo { // 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); + if (elmt.eoo()) + return Handle<Value>(); + Handle<Value> val = scope->mongoToV8Element(elmt, false); +// info.This()->ForceSet(name, val); + return val; + } + + static Handle<v8::Value> indexedGetRO(uint32_t index, const v8::AccessorInfo &info) { + StringBuilder ss; + ss << index; + string key = ss.str(); + Local< External > scp = External::Cast( *info.Data() ); + V8Scope* scope = (V8Scope*)(scp->Value()); + // cannot get v8 to properly cache the indexed val in the js object +// Handle<v8::String> name = scope->getV8Str(key); +// // v8 API really confusing here, must check existence on index, but then fetch with name +// if (info.This()->HasRealIndexedProperty(index)) { +// Handle<v8::Value> val = info.This()->GetRealNamedProperty(name); +// if (!val.IsEmpty() && !val->IsNull()) +// return val; +// } + BSONObj *obj = unwrapBSONObj(info.Holder()); + BSONElement elmt = obj->getField(key); + if (elmt.eoo()) + return Handle<Value>(); + Handle<Value> val = scope->mongoToV8Element(elmt, true); // info.This()->ForceSet(name, val); return val; } @@ -192,6 +227,12 @@ namespace mongo { lzObjectTemplate->SetNamedPropertyHandler(namedGet, 0, 0, 0, 0, v8::External::New(this)); lzObjectTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0, v8::External::New(this)); + roObjectTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); + roObjectTemplate->SetInternalFieldCount( 1 ); + roObjectTemplate->SetNamedPropertyHandler(0); + roObjectTemplate->SetNamedPropertyHandler(namedGetRO, 0, 0, 0, 0, v8::External::New(this)); + roObjectTemplate->SetIndexedPropertyHandler(indexedGetRO, 0, 0, 0, 0, v8::External::New(this)); + // initialize lazy array template // unfortunately it is not possible to create true v8 array from a template // this means we use an object template and copy methods over @@ -200,6 +241,9 @@ namespace mongo { lzArrayTemplate->SetInternalFieldCount( 1 ); lzArrayTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0, v8::External::New(this)); + internalFieldObjects = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); + internalFieldObjects->SetInternalFieldCount( 1 ); + V8STR_CONN = getV8Str( "_conn" ); V8STR_ID = getV8Str( "_id" ); V8STR_LENGTH = getV8Str( "length" ); @@ -247,6 +291,8 @@ namespace mongo { } lzObjectTemplate.Dispose(); lzArrayTemplate.Dispose(); + roObjectTemplate.Dispose(); + internalFieldObjects.Dispose(); } /** @@ -370,7 +416,7 @@ namespace mongo { V8_SIMPLE_HEADER // Set() accepts a ReadOnly parameter, but this just prevents the field itself // from being overwritten and doesn't protect the object stored in 'field'. - _global->Set( getV8Str( field ) , mongoToLZV8( obj, false, readOnly) ); + _global->Set( getV8Str( field ) , mongoToV8( obj, false, readOnly) ); } int V8Scope::type( const char *field ) { @@ -544,7 +590,7 @@ namespace mongo { BSONObjIterator it( *argsObject ); for ( int i=0; i<nargs; i++ ) { BSONElement next = it.next(); - args[i] = mongoToV8Element( next ); + args[i] = mongoToV8Element( next, true ); } setObject( "args", *argsObject, true ); // for backwards compatibility } @@ -560,7 +606,7 @@ namespace mongo { } Handle<v8::Object> v8recv; if (recv != 0) - v8recv = mongoToLZV8(*recv, false); + v8recv = mongoToLZV8(*recv, false, true); else v8recv = _emptyObj; @@ -797,9 +843,6 @@ namespace mongo { } Local< v8::ObjectTemplate > readOnlyObjects; - // Hoping template construction is fast... - Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New(); - internalFieldObjects->SetInternalFieldCount( 1 ); if ( !o.IsEmpty() ) { readOnly = false; @@ -1002,42 +1045,47 @@ namespace mongo { Handle<v8::Object> V8Scope::mongoToLZV8( const BSONObj& m , bool array, bool readOnly ) { Local<v8::Object> o; - if (array) { - o = lzArrayTemplate->NewInstance(); - o->SetPrototype(v8::Array::New(1)->GetPrototype()); - o->Set(V8STR_LENGTH, v8::Integer::New(m.nFields()), DontEnum); -// o->Set(ARRAY_STRING, v8::Boolean::New(true), DontEnum); + if (readOnly) { + o = roObjectTemplate->NewInstance(); } else { - o = lzObjectTemplate->NewInstance(); - - static string ref = "$ref"; - if ( ref == m.firstElement().fieldName() ) { - const BSONElement& id = m["$id"]; - if (!id.eoo()) { - v8::Function* dbRef = getNamedCons( "DBRef" ); - o->SetPrototype(dbRef->NewInstance()->GetPrototype()); - } + if (array) { + o = lzArrayTemplate->NewInstance(); + o->SetPrototype(v8::Array::New(1)->GetPrototype()); + o->Set(V8STR_LENGTH, v8::Integer::New(m.nFields()), DontEnum); + // o->Set(ARRAY_STRING, v8::Boolean::New(true), DontEnum); + } else { + o = lzObjectTemplate->NewInstance(); + + static string ref = "$ref"; + if ( ref == m.firstElement().fieldName() ) { + const BSONElement& id = m["$id"]; + if (!id.eoo()) { + v8::Function* dbRef = getNamedCons( "DBRef" ); + o->SetPrototype(dbRef->NewInstance()->GetPrototype()); + } + } + } + + // need to set all keys with dummy values, so that order of keys is correct during enumeration + // otherwise v8 will list any newly set property in JS before the ones of underlying BSON obj. + for (BSONObjIterator it(m); it.more();) { + const BSONElement& f = it.next(); + o->ForceSet(getV8Str(f.fieldName()), v8::Undefined()); } } BSONObj* own = new BSONObj(m.getOwned()); // BSONObj* own = new BSONObj(m); o->SetInternalField(0, v8::External::New(own)); - // 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); return p; } - Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f ) { - Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New(); - internalFieldObjects->SetInternalFieldCount( 1 ); + Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f, bool readOnly ) { +// Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New(); +// internalFieldObjects->SetInternalFieldCount( 1 ); switch ( f.type() ) { @@ -1050,7 +1098,9 @@ namespace mongo { return newFunction( f.codeWScopeCode() ); case mongo::String: +// return v8::String::NewExternal( new ExternalString( f.valuestr() )); return v8::String::New( f.valuestr() ); +// return getV8Str( f.valuestr() ); case mongo::jstOID: return newId( f.__oid() ); @@ -1064,9 +1114,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() , true ); + return mongoToV8( f.embeddedObject() , true, readOnly ); case mongo::Object: - return mongoToLZV8( f.embeddedObject() , false); + return mongoToLZV8( f.embeddedObject() , false, readOnly); case mongo::Date: return v8::Date::New( f.date() ); @@ -1173,6 +1223,9 @@ namespace mongo { void V8Scope::v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth ) { if ( value->IsString() ) { +// Handle<v8::String> str = Handle<v8::String>::Cast(value); +// ExternalString* es = (ExternalString*) (str->GetExternalAsciiStringResource()); +// b.append( sname , es->data() ); b.append( sname , toSTLString( value ).c_str() ); return; } @@ -1366,7 +1419,7 @@ namespace mongo { /** * Gets a V8 strings from the scope's cache, creating one if needed */ - v8::Persistent<v8::String> V8Scope::getV8Str(string str) { + v8::Handle<v8::String> V8Scope::getV8Str(string str) { Persistent<v8::String> ptr = _strCache[str]; if (ptr.IsEmpty()) { ptr = Persistent<v8::String>::New(v8::String::New(str.c_str())); diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h index 4b17e0fc796..eb3b16c248e 100644 --- a/scripting/engine_v8.h +++ b/scripting/engine_v8.h @@ -113,33 +113,35 @@ namespace mongo { void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth = 0 ); - v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f ); + v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool readOnly = false ); virtual void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ); v8::Function * getNamedCons( const char * name ); v8::Function * getObjectIdCons(); Local< v8::Value > newId( const OID &id ); - v8::Persistent<v8::String> getV8Str(string str); - - Persistent<v8::String> V8STR_CONN; - Persistent<v8::String> V8STR_ID; - Persistent<v8::String> V8STR_LENGTH; - Persistent<v8::String> V8STR_ISOBJECTID; - Persistent<v8::String> V8STR_NATIVE_FUNC; - Persistent<v8::String> V8STR_NATIVE_DATA; - Persistent<v8::String> V8STR_V8_FUNC; - Persistent<v8::String> V8STR_RETURN; - Persistent<v8::String> V8STR_ARGS; - Persistent<v8::String> V8STR_T; - Persistent<v8::String> V8STR_I; - Persistent<v8::String> V8STR_EMPTY; - Persistent<v8::String> V8STR_MINKEY; - Persistent<v8::String> V8STR_MAXKEY; - Persistent<v8::String> V8STR_NUMBERLONG; - Persistent<v8::String> V8STR_DBPTR; - Persistent<v8::String> V8STR_BINDATA; - Persistent<v8::String> V8STR_WRAPPER; + v8::Handle<v8::String> getV8Str(string str); +// inline v8::Handle<v8::String> getV8Str(string str) { return v8::String::New(str.c_str()); } + inline v8::Handle<v8::String> getLocalV8Str(string str) { return v8::String::New(str.c_str()); } + + Handle<v8::String> V8STR_CONN; + Handle<v8::String> V8STR_ID; + Handle<v8::String> V8STR_LENGTH; + Handle<v8::String> V8STR_ISOBJECTID; + Handle<v8::String> V8STR_NATIVE_FUNC; + Handle<v8::String> V8STR_NATIVE_DATA; + Handle<v8::String> V8STR_V8_FUNC; + Handle<v8::String> V8STR_RETURN; + Handle<v8::String> V8STR_ARGS; + Handle<v8::String> V8STR_T; + Handle<v8::String> V8STR_I; + Handle<v8::String> V8STR_EMPTY; + Handle<v8::String> V8STR_MINKEY; + Handle<v8::String> V8STR_MAXKEY; + Handle<v8::String> V8STR_NUMBERLONG; + Handle<v8::String> V8STR_DBPTR; + Handle<v8::String> V8STR_BINDATA; + Handle<v8::String> V8STR_WRAPPER; private: void _startCall(); @@ -169,7 +171,9 @@ namespace mongo { std::map <string, v8::Persistent <v8::String> > _strCache; Persistent<v8::ObjectTemplate> lzObjectTemplate; + Persistent<v8::ObjectTemplate> roObjectTemplate; Persistent<v8::ObjectTemplate> lzArrayTemplate; + Persistent<v8::ObjectTemplate> internalFieldObjects; }; class V8ScriptEngine : public ScriptEngine { @@ -196,6 +200,23 @@ namespace mongo { friend class V8Scope; }; + class ExternalString : public v8::String::ExternalAsciiStringResource { + public: + ExternalString(std::string str) : _data(str) { + } + + ~ExternalString() { + } + + const char* data () const { return _data.c_str(); } + size_t length () const { return _data.length(); } + private: +// string _str; +// const char* _data; + std::string _data; +// size_t _len; + }; + extern ScriptEngine * globalScriptEngine; extern map< unsigned, int > __interruptSpecToThreadId; |