diff options
Diffstat (limited to 'scripting/v8_wrapper.cpp')
-rw-r--r-- | scripting/v8_wrapper.cpp | 282 |
1 files changed, 145 insertions, 137 deletions
diff --git a/scripting/v8_wrapper.cpp b/scripting/v8_wrapper.cpp index 0e71c9a..ff67e8c 100644 --- a/scripting/v8_wrapper.cpp +++ b/scripting/v8_wrapper.cpp @@ -17,6 +17,7 @@ #include "v8_wrapper.h" #include "v8_utils.h" +#include "v8_db.h" #include <iostream> @@ -38,17 +39,17 @@ namespace mongo { cout << "cannot delete from read-only object" << endl; return Boolean::New( false ); } - + Handle<Value> IndexedReadOnlySet( uint32_t index, Local<Value> value, const AccessorInfo& info ) { cout << "cannot write to read-only array" << endl; return value; } - + Handle<Boolean> IndexedReadOnlyDelete( uint32_t index, const AccessorInfo& info ) { cout << "cannot delete from read-only array" << endl; return Boolean::New( false ); } - + Local< v8::Value > newFunction( const char *code ) { stringstream codeSS; codeSS << "____MontoToV8_newFunction_temp = " << code; @@ -57,15 +58,15 @@ namespace mongo { Local< Value > ret = compiled->Run(); return ret; } - + Local< v8::Value > newId( const OID &id ) { v8::Function * idCons = getObjectIdCons(); v8::Handle<v8::Value> argv[1]; argv[0] = v8::String::New( id.str().c_str() ); - return idCons->NewInstance( 1 , argv ); + return idCons->NewInstance( 1 , argv ); } - - Local<v8::Object> mongoToV8( const BSONObj& m , bool array, bool readOnly ){ + + Local<v8::Object> mongoToV8( const BSONObj& m , bool array, bool readOnly ) { Local<v8::Object> o; @@ -86,13 +87,16 @@ namespace mongo { if ( !o.IsEmpty() ) { readOnly = false; - } else if ( array ) { + } + else if ( array ) { // NOTE Looks like it's impossible to add interceptors to v8 arrays. readOnly = false; o = v8::Array::New(); - } else if ( !readOnly ) { + } + else if ( !readOnly ) { o = v8::Object::New(); - } else { + } + else { // NOTE Our readOnly implemention relies on undocumented ObjectTemplate // functionality that may be fragile, but it still seems like the best option // for now -- fwiw, the v8 docs are pretty sparse. I've determined experimentally @@ -115,15 +119,15 @@ namespace mongo { readOnlyObjects->SetIndexedPropertyHandler( 0 ); o = readOnlyObjects->NewInstance(); } - + mongo::BSONObj sub; for ( BSONObjIterator i(m); i.more(); ) { const BSONElement& f = i.next(); - + Local<Value> v; - - switch ( f.type() ){ + + switch ( f.type() ) { case mongo::Code: o->Set( v8::String::New( f.fieldName() ), newFunction( f.valuestr() ) ); @@ -134,31 +138,31 @@ namespace mongo { log() << "warning: CodeWScope doesn't transfer to db.eval" << endl; o->Set( v8::String::New( f.fieldName() ), newFunction( f.codeWScopeCode() ) ); break; - - case mongo::String: + + case mongo::String: o->Set( v8::String::New( f.fieldName() ) , v8::String::New( f.valuestr() ) ); break; - + case mongo::jstOID: { v8::Function * idCons = getObjectIdCons(); v8::Handle<v8::Value> argv[1]; argv[0] = v8::String::New( f.__oid().str().c_str() ); - o->Set( v8::String::New( f.fieldName() ) , - idCons->NewInstance( 1 , argv ) ); + o->Set( v8::String::New( f.fieldName() ) , + idCons->NewInstance( 1 , argv ) ); break; } - + case mongo::NumberDouble: case mongo::NumberInt: o->Set( v8::String::New( f.fieldName() ) , v8::Number::New( f.number() ) ); break; - + case mongo::Array: case mongo::Object: sub = f.embeddedObject(); o->Set( v8::String::New( f.fieldName() ) , mongoToV8( sub , f.type() == mongo::Array, readOnly ) ); break; - + case mongo::Date: o->Set( v8::String::New( f.fieldName() ) , v8::Date::New( f.date() ) ); break; @@ -166,29 +170,29 @@ namespace mongo { case mongo::Bool: o->Set( v8::String::New( f.fieldName() ) , v8::Boolean::New( f.boolean() ) ); break; - + case mongo::jstNULL: case mongo::Undefined: // duplicate sm behavior o->Set( v8::String::New( f.fieldName() ) , v8::Null() ); break; - + case mongo::RegEx: { v8::Function * regex = getNamedCons( "RegExp" ); - + v8::Handle<v8::Value> argv[2]; argv[0] = v8::String::New( f.regex() ); argv[1] = v8::String::New( f.regexFlags() ); - + o->Set( v8::String::New( f.fieldName() ) , regex->NewInstance( 2 , argv ) ); break; } - + case mongo::BinData: { Local<v8::Object> b = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); int len; const char *data = f.binData( len ); - + v8::Function* binData = getNamedCons( "BinData" ); v8::Handle<v8::Value> argv[3]; argv[0] = v8::Number::New( len ); @@ -197,36 +201,37 @@ namespace mongo { o->Set( v8::String::New( f.fieldName() ), binData->NewInstance(3, argv) ); break; } - + case mongo::Timestamp: { Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); - + sub->Set( v8::String::New( "t" ) , v8::Number::New( f.timestampTime() ) ); sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) ); sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); - + o->Set( v8::String::New( f.fieldName() ) , sub ); break; } - + case mongo::NumberLong: { Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); unsigned long long val = f.numberLong(); v8::Function* numberLong = getNamedCons( "NumberLong" ); if ( (long long)val == (long long)(double)(long long)(val) ) { - v8::Handle<v8::Value> argv[1]; + v8::Handle<v8::Value> argv[1]; argv[0] = v8::Number::New( (double)(long long)( val ) ); o->Set( v8::String::New( f.fieldName() ), numberLong->NewInstance( 1, argv ) ); - } else { + } + else { v8::Handle<v8::Value> argv[3]; argv[0] = v8::Number::New( (double)(long long)(val) ); argv[1] = v8::Integer::New( val >> 32 ); argv[2] = v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) ); o->Set( v8::String::New( f.fieldName() ), numberLong->NewInstance(3, argv) ); } - break; + break; } - + case mongo::MinKey: { Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); sub->Set( v8::String::New( "$MinKey" ), v8::Boolean::New( true ) ); @@ -234,7 +239,7 @@ namespace mongo { o->Set( v8::String::New( f.fieldName() ) , sub ); break; } - + case mongo::MaxKey: { Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); sub->Set( v8::String::New( "$MaxKey" ), v8::Boolean::New( true ) ); @@ -251,7 +256,7 @@ namespace mongo { o->Set( v8::String::New( f.fieldName() ), dbPointer->NewInstance(2, argv) ); break; } - + default: cout << "can't handle type: "; cout << f.type() << " "; @@ -259,14 +264,14 @@ namespace mongo { cout << endl; break; } - + } if ( readOnly ) { readOnlyObjects->SetNamedPropertyHandler( 0, NamedReadOnlySet, 0, NamedReadOnlyDelete ); - readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete ); + readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete ); } - + return o; } @@ -274,56 +279,56 @@ namespace mongo { Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New(); internalFieldObjects->SetInternalFieldCount( 1 ); - switch ( f.type() ){ + switch ( f.type() ) { case mongo::Code: return newFunction( f.valuestr() ); - + case CodeWScope: if ( f.codeWScopeObject().isEmpty() ) log() << "warning: CodeWScope doesn't transfer to db.eval" << endl; return newFunction( f.codeWScopeCode() ); - - case mongo::String: + + case mongo::String: return v8::String::New( f.valuestr() ); - + case mongo::jstOID: return newId( f.__oid() ); - + case mongo::NumberDouble: case mongo::NumberInt: return v8::Number::New( f.number() ); - + case mongo::Array: case mongo::Object: return mongoToV8( f.embeddedObject() , f.type() == mongo::Array ); - + case mongo::Date: return v8::Date::New( f.date() ); - + case mongo::Bool: return v8::Boolean::New( f.boolean() ); - case mongo::EOO: + case mongo::EOO: case mongo::jstNULL: case mongo::Undefined: // duplicate sm behavior return v8::Null(); - + case mongo::RegEx: { v8::Function * regex = getNamedCons( "RegExp" ); - + v8::Handle<v8::Value> argv[2]; argv[0] = v8::String::New( f.regex() ); argv[1] = v8::String::New( f.regexFlags() ); - + return regex->NewInstance( 2 , argv ); break; } - + case mongo::BinData: { int len; const char *data = f.binData( len ); - + v8::Function* binData = getNamedCons( "BinData" ); v8::Handle<v8::Value> argv[3]; argv[0] = v8::Number::New( len ); @@ -331,26 +336,27 @@ namespace mongo { argv[2] = v8::String::New( data, len ); return binData->NewInstance( 3, argv ); }; - + case mongo::Timestamp: { Local<v8::Object> sub = internalFieldObjects->NewInstance(); - + sub->Set( v8::String::New( "t" ) , v8::Number::New( f.timestampTime() ) ); sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) ); sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); return sub; } - + case mongo::NumberLong: { Local<v8::Object> sub = internalFieldObjects->NewInstance(); unsigned long long val = f.numberLong(); v8::Function* numberLong = getNamedCons( "NumberLong" ); if ( (long long)val == (long long)(double)(long long)(val) ) { - v8::Handle<v8::Value> argv[1]; + v8::Handle<v8::Value> argv[1]; argv[0] = v8::Number::New( (double)(long long)( val ) ); return numberLong->NewInstance( 1, argv ); - } else { + } + else { v8::Handle<v8::Value> argv[3]; argv[0] = v8::Number::New( (double)(long long)( val ) ); argv[1] = v8::Integer::New( val >> 32 ); @@ -358,21 +364,21 @@ namespace mongo { return numberLong->NewInstance( 3, argv ); } } - + case mongo::MinKey: { Local<v8::Object> sub = internalFieldObjects->NewInstance(); sub->Set( v8::String::New( "$MinKey" ), v8::Boolean::New( true ) ); sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); return sub; } - + case mongo::MaxKey: { Local<v8::Object> sub = internalFieldObjects->NewInstance(); sub->Set( v8::String::New( "$MaxKey" ), v8::Boolean::New( true ) ); sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); return sub; } - + case mongo::DBRef: { v8::Function* dbPointer = getNamedCons( "DBPointer" ); v8::Handle<v8::Value> argv[2]; @@ -380,83 +386,83 @@ namespace mongo { argv[1] = newId( f.dbrefOID() ); return dbPointer->NewInstance(2, argv); } - + default: cout << "can't handle type: "; - cout << f.type() << " "; - cout << f.toString(); - cout << endl; + cout << f.type() << " "; + cout << f.toString(); + cout << endl; break; - } - + } + return v8::Undefined(); } - void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth ){ - - if ( value->IsString() ){ + void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth ) { + + if ( value->IsString() ) { b.append( sname , toSTLString( value ).c_str() ); return; } - - if ( value->IsFunction() ){ - b.appendCode( sname , toSTLString( value ).c_str() ); + + if ( value->IsFunction() ) { + b.appendCode( sname , toSTLString( value ) ); return; } - - if ( value->IsNumber() ){ + + if ( value->IsNumber() ) { if ( value->IsInt32() ) b.append( sname, int( value->ToInt32()->Value() ) ); else b.append( sname , value->ToNumber()->Value() ); return; } - - if ( value->IsArray() ){ + + if ( value->IsArray() ) { BSONObj sub = v8ToMongo( value->ToObject() , depth ); b.appendArray( sname , sub ); return; } - - if ( value->IsDate() ){ + + if ( value->IsDate() ) { b.appendDate( sname , Date_t( (unsigned long long)(v8::Date::Cast( *value )->NumberValue())) ); return; } if ( value->IsExternal() ) return; - - if ( value->IsObject() ){ + + if ( value->IsObject() ) { // The user could potentially modify the fields of these special objects, // wreaking havoc when we attempt to reinterpret them. Not doing any validation // for now... Local< v8::Object > obj = value->ToObject(); if ( obj->InternalFieldCount() && obj->GetInternalField( 0 )->IsNumber() ) { switch( obj->GetInternalField( 0 )->ToInt32()->Value() ) { // NOTE Uint32's Value() gave me a linking error, so going with this instead - case Timestamp: - b.appendTimestamp( sname, - Date_t( (unsigned long long)(obj->Get( v8::String::New( "t" ) )->ToNumber()->Value() )), - obj->Get( v8::String::New( "i" ) )->ToInt32()->Value() ); - return; - case MinKey: - b.appendMinKey( sname ); - return; - case MaxKey: - b.appendMaxKey( sname ); - return; - default: - assert( "invalid internal field" == 0 ); + case Timestamp: + b.appendTimestamp( sname, + Date_t( (unsigned long long)(obj->Get( v8::String::New( "t" ) )->ToNumber()->Value() )), + obj->Get( v8::String::New( "i" ) )->ToInt32()->Value() ); + return; + case MinKey: + b.appendMinKey( sname ); + return; + case MaxKey: + b.appendMaxKey( sname ); + return; + default: + assert( "invalid internal field" == 0 ); } } string s = toSTLString( value ); - if ( s.size() && s[0] == '/' ){ + if ( s.size() && s[0] == '/' ) { s = s.substr( 1 ); string r = s.substr( 0 , s.rfind( "/" ) ); string o = s.substr( s.rfind( "/" ) + 1 ); - b.appendRegex( sname , r.c_str() , o.c_str() ); + b.appendRegex( sname , r , o ); } else if ( value->ToObject()->GetPrototype()->IsObject() && - value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ){ + value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ) { OID oid; oid.init( toSTLString( value ) ); b.appendOID( sname , &oid ); @@ -469,19 +475,20 @@ namespace mongo { long long val; if ( !it->Has( v8::String::New( "top" ) ) ) { val = (long long)( it->Get( v8::String::New( "floatApprox" ) )->NumberValue() ); - } else { + } + else { val = (long long) - ( (unsigned long long)( it->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) + - (unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() ); + ( (unsigned long long)( it->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) + + (unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() ); } - + b.append( sname, val ); } else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__DBPointer" ) ).IsEmpty() ) { OID oid; oid.init( toSTLString( value->ToObject()->Get( v8::String::New( "id" ) ) ) ); string ns = toSTLString( value->ToObject()->Get( v8::String::New( "ns" ) ) ); - b.appendDBRef( sname, ns.c_str(), oid ); + b.appendDBRef( sname, ns, oid ); } else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__BinData" ) ).IsEmpty() ) { int len = obj->Get( v8::String::New( "len" ) )->ToInt32()->Value(); @@ -489,27 +496,28 @@ namespace mongo { const char *dataArray = *data; assert( data.length() == len ); b.appendBinData( sname, - len, - mongo::BinDataType( obj->Get( v8::String::New( "type" ) )->ToInt32()->Value() ), - dataArray ); - } else { + len, + mongo::BinDataType( obj->Get( v8::String::New( "type" ) )->ToInt32()->Value() ), + dataArray ); + } + else { BSONObj sub = v8ToMongo( value->ToObject() , depth ); b.append( sname , sub ); } return; } - - if ( value->IsBoolean() ){ + + if ( value->IsBoolean() ) { b.appendBool( sname , value->ToBoolean()->Value() ); return; } - - else if ( value->IsUndefined() ){ + + else if ( value->IsUndefined() ) { b.appendUndefined( sname ); return; } - - else if ( value->IsNull() ){ + + else if ( value->IsNull() ) { b.appendNull( sname ); return; } @@ -517,26 +525,26 @@ namespace mongo { cout << "don't know how to convert to mongo field [" << name << "]\t" << value << endl; } - BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth ){ + BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth ) { BSONObjBuilder b; - - if ( depth == 0 ){ + + if ( depth == 0 ) { v8::Handle<v8::String> idName = v8::String::New( "_id" ); - if ( o->HasRealNamedProperty( idName ) ){ + if ( o->HasRealNamedProperty( idName ) ) { v8ToMongoElement( b , idName , "_id" , o->Get( idName ) ); } } - + Local<v8::Array> names = o->GetPropertyNames(); - for ( unsigned int i=0; i<names->Length(); i++ ){ + for ( unsigned int i=0; i<names->Length(); i++ ) { v8::Local<v8::String> name = names->Get(v8::Integer::New(i) )->ToString(); if ( o->GetPrototype()->IsObject() && - o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) ) + o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) ) continue; - + v8::Local<v8::Value> value = o->Get( name ); - + const string sname = toSTLString( name ); if ( depth == 0 && sname == "_id" ) continue; @@ -553,15 +561,15 @@ namespace mongo { WrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ) : _o(o), _readOnly( readOnly ), _iDelete( iDelete ) { } - - ~WrapperHolder(){ - if ( _o && _iDelete ){ + + ~WrapperHolder() { + if ( _o && _iDelete ) { delete _o; } _o = 0; } - v8::Handle<v8::Value> get( v8::Local<v8::String> name ){ + v8::Handle<v8::Value> get( v8::Local<v8::String> name ) { const string& s = toSTLString( name ); const BSONElement& e = _o->getField( s ); return mongoToV8Element(e); @@ -572,13 +580,13 @@ namespace mongo { bool _iDelete; }; - WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ){ + WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ) { return new WrapperHolder( o , readOnly , iDelete ); } #define WRAPPER_STRING (v8::String::New( "_wrapper" ) ) - WrapperHolder * getWrapper( v8::Handle<v8::Object> o ){ + WrapperHolder * getWrapper( v8::Handle<v8::Object> o ) { Handle<v8::Value> t = o->GetRealNamedProperty( WRAPPER_STRING ); assert( t->IsExternal() ); Local<External> c = External::Cast( *t ); @@ -588,32 +596,32 @@ namespace mongo { } - Handle<Value> wrapperCons(const Arguments& args){ + Handle<Value> wrapperCons(const Arguments& args) { if ( ! ( args.Length() == 1 && args[0]->IsExternal() ) ) return v8::ThrowException( v8::String::New( "wrapperCons needs 1 External arg" ) ); args.This()->Set( WRAPPER_STRING , args[0] ); - + return v8::Undefined(); } - v8::Handle<v8::Value> wrapperGetHandler( v8::Local<v8::String> name, const v8::AccessorInfo &info){ + v8::Handle<v8::Value> wrapperGetHandler( v8::Local<v8::String> name, const v8::AccessorInfo &info) { return getWrapper( info.This() )->get( name ); } - v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate(){ - v8::Local<v8::FunctionTemplate> t = FunctionTemplate::New( wrapperCons ); + v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate() { + v8::Local<v8::FunctionTemplate> t = newV8Function< wrapperCons >(); t->InstanceTemplate()->SetNamedPropertyHandler( wrapperGetHandler ); return t; } // --- random utils ---- - v8::Function * getNamedCons( const char * name ){ + v8::Function * getNamedCons( const char * name ) { return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( v8::String::New( name ) ) ) ); } - v8::Function * getObjectIdCons(){ + v8::Function * getObjectIdCons() { return getNamedCons( "ObjectId" ); } |