diff options
author | agirbal <antoine@10gen.com> | 2011-04-25 10:58:43 -0700 |
---|---|---|
committer | agirbal <antoine@10gen.com> | 2011-04-25 11:02:52 -0700 |
commit | c75b2cbae1b0d6315324cd94a11d4ea460676047 (patch) | |
tree | 5826cc75fac3db6e2932c0d28bd3764fd99a9ae8 /scripting | |
parent | 32afd00ca76f7358a9e52fb15b0c5a00bfc17cb0 (diff) | |
download | mongo-c75b2cbae1b0d6315324cd94a11d4ea460676047.tar.gz |
SERVER-2579: added framework for lazy v8
Diffstat (limited to 'scripting')
-rw-r--r-- | scripting/engine_v8.cpp | 147 | ||||
-rw-r--r-- | scripting/engine_v8.h | 6 |
2 files changed, 148 insertions, 5 deletions
diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp index 50c7f3e8fb6..ae1007221ef 100644 --- a/scripting/engine_v8.cpp +++ b/scripting/engine_v8.cpp @@ -28,6 +28,96 @@ namespace mongo { // guarded by v8 mutex map< unsigned, int > __interruptSpecToThreadId; + /** + * Unwraps a BSONObj from the JS wrapper + */ + static BSONObj* unwrapBSONObj(const Handle<v8::Object>& obj) { + Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0)); + if (field.IsEmpty() || !field->IsExternal()) + return 0; + void* ptr = field->Value(); + return static_cast<BSONObj*>(ptr); + } + + static Handle<v8::Value> namedGet(Local<v8::String> name, const v8::AccessorInfo &info) { + if (info.This()->HasRealNamedProperty(name)) { + return info.This()->GetRealNamedProperty(name); + } + + 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()); + 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); + return val; + } + +// static Handle<v8::Value> namedSet(Local<v8::String> name, Local<v8::Value> value_obj, const v8::AccessorInfo& info) { +// 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; + } + +// v8::Handle<v8::Integer> namedQuery(Local<v8::String> property, const AccessorInfo& info) { +// string key = ToString(property); +// return v8::Integer::New(None); +// } + + static Handle<v8::Value> indexedGet(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()); + 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); + 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); + return val; + } + +// static Handle<v8::Value> indexedSet(uint32_t index, Local<v8::Value> value_obj, const v8::AccessorInfo& info) { +// 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; + } + // --- engine --- V8ScriptEngine::V8ScriptEngine() { @@ -72,6 +162,20 @@ namespace mongo { _global = Persistent< v8::Object >::New( _context->Global() ); _this = 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->SetIndexedPropertyHandler(indexedGet, 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 + // 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)); + V8STR_CONN = getV8Str( "_conn" ); V8STR_ID = getV8Str( "_id" ); V8STR_LENGTH = getV8Str( "length" ); @@ -849,7 +953,36 @@ namespace mongo { return o; } - Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f ) { + /** + * converts a BSONObj to a Lazy V8 object + */ + Local<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); + } 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()); + } + } + } + + BSONObj* p = new BSONObj(m); + o->SetInternalField(0, v8::External::New(p)); + return o; + } + + Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f, bool lazy ) { Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New(); internalFieldObjects->SetInternalFieldCount( 1 ); @@ -874,7 +1007,14 @@ namespace mongo { return v8::Number::New( f.number() ); case mongo::Array: + // for arrays it's better to use non lazy object because: + // - 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 ); case mongo::Object: + if (lazy) + return mongoToLZV8( f.embeddedObject() , f.type() == mongo::Array); return mongoToV8( f.embeddedObject() , f.type() == mongo::Array ); case mongo::Date: @@ -1103,9 +1243,8 @@ namespace mongo { BSONObjBuilder b; if ( depth == 0 ) { - v8::Handle<v8::String> idName = V8STR_ID; - if ( o->HasRealNamedProperty( idName ) ) { - v8ToMongoElement( b , idName , "_id" , o->Get( idName ) ); + if ( o->HasRealNamedProperty( V8STR_ID ) ) { + v8ToMongoElement( b , V8STR_ID , "_id" , o->Get( V8STR_ID ) ); } } diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h index e242374f427..bc4fec222b8 100644 --- a/scripting/engine_v8.h +++ b/scripting/engine_v8.h @@ -107,11 +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 ); 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 ); + v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool lazy = true ); v8::Function * getNamedCons( const char * name ); v8::Function * getObjectIdCons(); @@ -163,6 +164,9 @@ namespace mongo { ConnectState _connectState; std::map <string, v8::Persistent <v8::String> > _strCache; + + Persistent<v8::ObjectTemplate> lzObjectTemplate; + Persistent<v8::ObjectTemplate> lzArrayTemplate; }; class V8ScriptEngine : public ScriptEngine { |