2009-10-10 07:30:00 +02:00
|
|
|
// v8_wrapper.cpp
|
|
|
|
|
2009-10-27 20:58:27 +01:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
#include "v8_wrapper.h"
|
|
|
|
#include "v8_utils.h"
|
2010-09-08 06:32:14 +02:00
|
|
|
#include "v8_db.h"
|
2009-10-10 07:30:00 +02:00
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace v8;
|
|
|
|
|
|
|
|
namespace mongo {
|
|
|
|
|
|
|
|
#define CONN_STRING (v8::String::New( "_conn" ))
|
|
|
|
|
|
|
|
#define DDD(x)
|
|
|
|
|
2009-12-16 00:52:41 +01:00
|
|
|
Handle<Value> NamedReadOnlySet( Local<v8::String> property, Local<Value> value, const AccessorInfo& info ) {
|
|
|
|
cout << "cannot write to read-only object" << endl;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<Boolean> NamedReadOnlyDelete( Local<v8::String> property, const AccessorInfo& info ) {
|
|
|
|
cout << "cannot delete from read-only object" << endl;
|
|
|
|
return Boolean::New( false );
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 00:52:41 +01:00
|
|
|
Handle<Value> IndexedReadOnlySet( uint32_t index, Local<Value> value, const AccessorInfo& info ) {
|
|
|
|
cout << "cannot write to read-only array" << endl;
|
|
|
|
return value;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 00:52:41 +01:00
|
|
|
Handle<Boolean> IndexedReadOnlyDelete( uint32_t index, const AccessorInfo& info ) {
|
|
|
|
cout << "cannot delete from read-only array" << endl;
|
|
|
|
return Boolean::New( false );
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 19:36:17 +01:00
|
|
|
Local< v8::Value > newFunction( const char *code ) {
|
|
|
|
stringstream codeSS;
|
|
|
|
codeSS << "____MontoToV8_newFunction_temp = " << code;
|
|
|
|
string codeStr = codeSS.str();
|
|
|
|
Local< Script > compiled = Script::New( v8::String::New( codeStr.c_str() ) );
|
|
|
|
Local< Value > ret = compiled->Run();
|
|
|
|
return ret;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 20:14:48 +01:00
|
|
|
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() );
|
2011-01-04 06:40:41 +01:00
|
|
|
return idCons->NewInstance( 1 , argv );
|
2009-12-16 20:14:48 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
Local<v8::Object> mongoToV8( const BSONObj& m , bool array, bool readOnly ) {
|
2009-11-24 03:36:10 +01:00
|
|
|
|
2010-02-17 21:42:24 +01:00
|
|
|
Local<v8::Object> o;
|
|
|
|
|
2009-11-24 03:36:10 +01:00
|
|
|
// handle DBRef. needs to come first. isn't it? (metagoto)
|
|
|
|
static string ref = "$ref";
|
|
|
|
if ( ref == m.firstElement().fieldName() ) {
|
|
|
|
const BSONElement& id = m["$id"];
|
|
|
|
if (!id.eoo()) { // there's no check on $id exitence in sm implementation. risky ?
|
|
|
|
v8::Function* dbRef = getNamedCons( "DBRef" );
|
2010-02-17 21:42:24 +01:00
|
|
|
o = dbRef->NewInstance();
|
2009-11-24 03:36:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-16 00:52:41 +01:00
|
|
|
Local< v8::ObjectTemplate > readOnlyObjects;
|
2009-12-16 02:40:53 +01:00
|
|
|
// Hoping template construction is fast...
|
2009-12-16 01:54:39 +01:00
|
|
|
Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New();
|
|
|
|
internalFieldObjects->SetInternalFieldCount( 1 );
|
|
|
|
|
2010-02-17 21:42:24 +01:00
|
|
|
if ( !o.IsEmpty() ) {
|
2010-02-17 22:42:16 +01:00
|
|
|
readOnly = false;
|
2011-01-04 06:40:41 +01:00
|
|
|
}
|
|
|
|
else if ( array ) {
|
2010-02-17 21:42:24 +01:00
|
|
|
// NOTE Looks like it's impossible to add interceptors to v8 arrays.
|
2010-02-17 22:42:16 +01:00
|
|
|
readOnly = false;
|
2009-12-16 23:49:22 +01:00
|
|
|
o = v8::Array::New();
|
2011-01-04 06:40:41 +01:00
|
|
|
}
|
|
|
|
else if ( !readOnly ) {
|
2009-12-16 23:49:22 +01:00
|
|
|
o = v8::Object::New();
|
2011-01-04 06:40:41 +01:00
|
|
|
}
|
|
|
|
else {
|
2009-12-16 00:52:41 +01:00
|
|
|
// 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
|
|
|
|
// that when property handlers are set for an object template, they will attach
|
|
|
|
// to objects previously created by that template. To get this to work, though,
|
|
|
|
// it is necessary to initialize the template's property handlers before
|
|
|
|
// creating objects from the template (as I have in the following few lines
|
|
|
|
// of code).
|
|
|
|
// NOTE In my first attempt, I configured the permanent property handlers before
|
|
|
|
// constructiong the object and replaced the Set() calls below with ForceSet().
|
|
|
|
// However, it turns out that ForceSet() only bypasses handlers for named
|
|
|
|
// properties and not for indexed properties.
|
|
|
|
readOnlyObjects = v8::ObjectTemplate::New();
|
2009-12-16 18:31:56 +01:00
|
|
|
// NOTE This internal field will store type info for special db types. For
|
|
|
|
// regular objects the field is unnecessary - for simplicity I'm creating just
|
|
|
|
// one readOnlyObjects template for objects where the field is & isn't necessary,
|
|
|
|
// assuming that the overhead of an internal field is slight.
|
2009-12-16 01:54:39 +01:00
|
|
|
readOnlyObjects->SetInternalFieldCount( 1 );
|
2009-12-16 00:52:41 +01:00
|
|
|
readOnlyObjects->SetNamedPropertyHandler( 0 );
|
|
|
|
readOnlyObjects->SetIndexedPropertyHandler( 0 );
|
|
|
|
o = readOnlyObjects->NewInstance();
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
mongo::BSONObj sub;
|
2009-11-24 03:36:10 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
for ( BSONObjIterator i(m); i.more(); ) {
|
2009-10-15 17:27:02 +02:00
|
|
|
const BSONElement& f = i.next();
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
Local<Value> v;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
switch ( f.type() ) {
|
2009-10-10 07:30:00 +02:00
|
|
|
|
|
|
|
case mongo::Code:
|
2009-12-16 19:36:17 +01:00
|
|
|
o->Set( v8::String::New( f.fieldName() ), newFunction( f.valuestr() ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CodeWScope:
|
|
|
|
if ( f.codeWScopeObject().isEmpty() )
|
|
|
|
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
|
|
|
|
o->Set( v8::String::New( f.fieldName() ), newFunction( f.codeWScopeCode() ) );
|
|
|
|
break;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
case mongo::String:
|
2009-10-10 07:30:00 +02:00
|
|
|
o->Set( v8::String::New( f.fieldName() ) , v8::String::New( f.valuestr() ) );
|
|
|
|
break;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::jstOID: {
|
|
|
|
v8::Function * idCons = getObjectIdCons();
|
|
|
|
v8::Handle<v8::Value> argv[1];
|
|
|
|
argv[0] = v8::String::New( f.__oid().str().c_str() );
|
2011-01-04 06:40:41 +01:00
|
|
|
o->Set( v8::String::New( f.fieldName() ) ,
|
|
|
|
idCons->NewInstance( 1 , argv ) );
|
2009-10-10 07:30:00 +02:00
|
|
|
break;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::NumberDouble:
|
|
|
|
case mongo::NumberInt:
|
|
|
|
o->Set( v8::String::New( f.fieldName() ) , v8::Number::New( f.number() ) );
|
|
|
|
break;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Array:
|
|
|
|
case mongo::Object:
|
|
|
|
sub = f.embeddedObject();
|
2009-12-16 00:52:41 +01:00
|
|
|
o->Set( v8::String::New( f.fieldName() ) , mongoToV8( sub , f.type() == mongo::Array, readOnly ) );
|
2009-10-10 07:30:00 +02:00
|
|
|
break;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Date:
|
|
|
|
o->Set( v8::String::New( f.fieldName() ) , v8::Date::New( f.date() ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case mongo::Bool:
|
|
|
|
o->Set( v8::String::New( f.fieldName() ) , v8::Boolean::New( f.boolean() ) );
|
|
|
|
break;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::jstNULL:
|
2010-02-02 00:53:34 +01:00
|
|
|
case mongo::Undefined: // duplicate sm behavior
|
2009-10-10 07:30:00 +02:00
|
|
|
o->Set( v8::String::New( f.fieldName() ) , v8::Null() );
|
|
|
|
break;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::RegEx: {
|
|
|
|
v8::Function * regex = getNamedCons( "RegExp" );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
v8::Handle<v8::Value> argv[2];
|
|
|
|
argv[0] = v8::String::New( f.regex() );
|
|
|
|
argv[1] = v8::String::New( f.regexFlags() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
o->Set( v8::String::New( f.fieldName() ) , regex->NewInstance( 2 , argv ) );
|
|
|
|
break;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::BinData: {
|
2009-12-16 20:42:46 +01:00
|
|
|
Local<v8::Object> b = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
|
2009-10-10 07:30:00 +02:00
|
|
|
|
|
|
|
int len;
|
2009-12-16 20:42:46 +01:00
|
|
|
const char *data = f.binData( len );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-21 18:55:20 +01:00
|
|
|
v8::Function* binData = getNamedCons( "BinData" );
|
|
|
|
v8::Handle<v8::Value> argv[3];
|
|
|
|
argv[0] = v8::Number::New( len );
|
|
|
|
argv[1] = v8::Number::New( f.binDataType() );
|
|
|
|
argv[2] = v8::String::New( data, len );
|
|
|
|
o->Set( v8::String::New( f.fieldName() ), binData->NewInstance(3, argv) );
|
2009-10-10 07:30:00 +02:00
|
|
|
break;
|
2009-12-16 20:42:46 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Timestamp: {
|
2009-12-16 01:54:39 +01:00
|
|
|
Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-03-09 04:57:06 +01:00
|
|
|
sub->Set( v8::String::New( "t" ) , v8::Number::New( f.timestampTime() ) );
|
2009-10-10 07:30:00 +02:00
|
|
|
sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) );
|
2009-12-16 01:54:39 +01:00
|
|
|
sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
o->Set( v8::String::New( f.fieldName() ) , sub );
|
|
|
|
break;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-01-21 01:21:19 +01:00
|
|
|
case mongo::NumberLong: {
|
|
|
|
Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
|
|
|
|
unsigned long long val = f.numberLong();
|
|
|
|
v8::Function* numberLong = getNamedCons( "NumberLong" );
|
2010-03-01 19:36:40 +01:00
|
|
|
if ( (long long)val == (long long)(double)(long long)(val) ) {
|
2011-01-04 06:40:41 +01:00
|
|
|
v8::Handle<v8::Value> argv[1];
|
2010-03-01 19:36:40 +01:00
|
|
|
argv[0] = v8::Number::New( (double)(long long)( val ) );
|
|
|
|
o->Set( v8::String::New( f.fieldName() ), numberLong->NewInstance( 1, argv ) );
|
2011-01-04 06:40:41 +01:00
|
|
|
}
|
|
|
|
else {
|
2010-03-01 19:36:40 +01:00
|
|
|
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) );
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
break;
|
2010-01-21 01:21:19 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 01:54:39 +01:00
|
|
|
case mongo::MinKey: {
|
|
|
|
Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
|
|
|
|
sub->Set( v8::String::New( "$MinKey" ), v8::Boolean::New( true ) );
|
|
|
|
sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
|
|
|
|
o->Set( v8::String::New( f.fieldName() ) , sub );
|
2009-10-10 07:30:00 +02:00
|
|
|
break;
|
2009-12-16 01:54:39 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 01:54:39 +01:00
|
|
|
case mongo::MaxKey: {
|
|
|
|
Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance();
|
|
|
|
sub->Set( v8::String::New( "$MaxKey" ), v8::Boolean::New( true ) );
|
|
|
|
sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
|
|
|
|
o->Set( v8::String::New( f.fieldName() ) , sub );
|
2009-10-10 07:30:00 +02:00
|
|
|
break;
|
2009-12-16 01:54:39 +01:00
|
|
|
}
|
2009-12-16 20:14:48 +01:00
|
|
|
|
|
|
|
case mongo::DBRef: {
|
2009-12-17 01:50:36 +01:00
|
|
|
v8::Function* dbPointer = getNamedCons( "DBPointer" );
|
2009-12-16 20:14:48 +01:00
|
|
|
v8::Handle<v8::Value> argv[2];
|
|
|
|
argv[0] = v8::String::New( f.dbrefNS() );
|
|
|
|
argv[1] = newId( f.dbrefOID() );
|
2009-12-17 01:50:36 +01:00
|
|
|
o->Set( v8::String::New( f.fieldName() ), dbPointer->NewInstance(2, argv) );
|
|
|
|
break;
|
2009-12-16 20:14:48 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
default:
|
|
|
|
cout << "can't handle type: ";
|
|
|
|
cout << f.type() << " ";
|
|
|
|
cout << f.toString();
|
|
|
|
cout << endl;
|
|
|
|
break;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
}
|
|
|
|
|
2010-02-17 22:42:16 +01:00
|
|
|
if ( readOnly ) {
|
2009-12-16 00:52:41 +01:00
|
|
|
readOnlyObjects->SetNamedPropertyHandler( 0, NamedReadOnlySet, 0, NamedReadOnlyDelete );
|
2011-01-04 06:40:41 +01:00
|
|
|
readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete );
|
2009-12-16 00:52:41 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<v8::Value> mongoToV8Element( const BSONElement &f ) {
|
2009-12-16 01:54:39 +01:00
|
|
|
Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New();
|
|
|
|
internalFieldObjects->SetInternalFieldCount( 1 );
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
switch ( f.type() ) {
|
2009-10-15 17:27:02 +02:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Code:
|
2009-12-16 19:36:17 +01:00
|
|
|
return newFunction( f.valuestr() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 19:36:17 +01:00
|
|
|
case CodeWScope:
|
|
|
|
if ( f.codeWScopeObject().isEmpty() )
|
|
|
|
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
|
|
|
|
return newFunction( f.codeWScopeCode() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
case mongo::String:
|
2009-10-10 07:30:00 +02:00
|
|
|
return v8::String::New( f.valuestr() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 20:14:48 +01:00
|
|
|
case mongo::jstOID:
|
|
|
|
return newId( f.__oid() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::NumberDouble:
|
|
|
|
case mongo::NumberInt:
|
|
|
|
return v8::Number::New( f.number() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Array:
|
|
|
|
case mongo::Object:
|
|
|
|
return mongoToV8( f.embeddedObject() , f.type() == mongo::Array );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Date:
|
|
|
|
return v8::Date::New( f.date() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Bool:
|
|
|
|
return v8::Boolean::New( f.boolean() );
|
2009-10-15 17:27:02 +02:00
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
case mongo::EOO:
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::jstNULL:
|
2010-02-02 00:53:34 +01:00
|
|
|
case mongo::Undefined: // duplicate sm behavior
|
2009-10-10 07:30:00 +02:00
|
|
|
return v8::Null();
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::RegEx: {
|
|
|
|
v8::Function * regex = getNamedCons( "RegExp" );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
v8::Handle<v8::Value> argv[2];
|
|
|
|
argv[0] = v8::String::New( f.regex() );
|
|
|
|
argv[1] = v8::String::New( f.regexFlags() );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
return regex->NewInstance( 2 , argv );
|
|
|
|
break;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::BinData: {
|
|
|
|
int len;
|
2009-12-16 20:42:46 +01:00
|
|
|
const char *data = f.binData( len );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-21 18:55:20 +01:00
|
|
|
v8::Function* binData = getNamedCons( "BinData" );
|
|
|
|
v8::Handle<v8::Value> argv[3];
|
|
|
|
argv[0] = v8::Number::New( len );
|
|
|
|
argv[1] = v8::Number::New( f.binDataType() );
|
|
|
|
argv[2] = v8::String::New( data, len );
|
|
|
|
return binData->NewInstance( 3, argv );
|
2009-10-10 07:30:00 +02:00
|
|
|
};
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
case mongo::Timestamp: {
|
2009-12-16 01:54:39 +01:00
|
|
|
Local<v8::Object> sub = internalFieldObjects->NewInstance();
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-03-09 04:57:06 +01:00
|
|
|
sub->Set( v8::String::New( "t" ) , v8::Number::New( f.timestampTime() ) );
|
2009-10-10 07:30:00 +02:00
|
|
|
sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) );
|
2009-12-16 01:54:39 +01:00
|
|
|
sub->SetInternalField( 0, v8::Uint32::New( f.type() ) );
|
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
return sub;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-01-21 01:21:19 +01:00
|
|
|
case mongo::NumberLong: {
|
|
|
|
Local<v8::Object> sub = internalFieldObjects->NewInstance();
|
|
|
|
unsigned long long val = f.numberLong();
|
|
|
|
v8::Function* numberLong = getNamedCons( "NumberLong" );
|
2010-03-01 19:36:40 +01:00
|
|
|
if ( (long long)val == (long long)(double)(long long)(val) ) {
|
2011-01-04 06:40:41 +01:00
|
|
|
v8::Handle<v8::Value> argv[1];
|
2010-03-01 19:36:40 +01:00
|
|
|
argv[0] = v8::Number::New( (double)(long long)( val ) );
|
|
|
|
return numberLong->NewInstance( 1, argv );
|
2011-01-04 06:40:41 +01:00
|
|
|
}
|
|
|
|
else {
|
2010-03-01 19:36:40 +01:00
|
|
|
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) );
|
|
|
|
return numberLong->NewInstance( 3, argv );
|
|
|
|
}
|
2010-01-21 01:21:19 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 01:54:39 +01:00
|
|
|
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;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 01:54:39 +01:00
|
|
|
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;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-12-16 20:14:48 +01:00
|
|
|
case mongo::DBRef: {
|
2009-12-17 01:50:36 +01:00
|
|
|
v8::Function* dbPointer = getNamedCons( "DBPointer" );
|
2009-12-16 20:14:48 +01:00
|
|
|
v8::Handle<v8::Value> argv[2];
|
|
|
|
argv[0] = v8::String::New( f.dbrefNS() );
|
|
|
|
argv[1] = newId( f.dbrefOID() );
|
2009-12-17 01:50:36 +01:00
|
|
|
return dbPointer->NewInstance(2, argv);
|
2009-12-16 20:14:48 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
default:
|
|
|
|
cout << "can't handle type: ";
|
2011-01-04 06:40:41 +01:00
|
|
|
cout << f.type() << " ";
|
|
|
|
cout << f.toString();
|
|
|
|
cout << endl;
|
2009-10-10 07:30:00 +02:00
|
|
|
break;
|
2011-01-04 06:40:41 +01:00
|
|
|
}
|
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
return v8::Undefined();
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth ) {
|
|
|
|
|
|
|
|
if ( value->IsString() ) {
|
2010-07-20 18:42:07 +02:00
|
|
|
b.append( sname , toSTLString( value ).c_str() );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( value->IsFunction() ) {
|
2010-08-19 00:12:31 +02:00
|
|
|
b.appendCode( sname , toSTLString( value ) );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( value->IsNumber() ) {
|
2009-12-16 02:40:53 +01:00
|
|
|
if ( value->IsInt32() )
|
2010-07-20 18:42:07 +02:00
|
|
|
b.append( sname, int( value->ToInt32()->Value() ) );
|
2009-12-16 02:40:53 +01:00
|
|
|
else
|
2010-07-20 18:42:07 +02:00
|
|
|
b.append( sname , value->ToNumber()->Value() );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( value->IsArray() ) {
|
2010-03-13 19:41:31 +01:00
|
|
|
BSONObj sub = v8ToMongo( value->ToObject() , depth );
|
2010-07-20 18:42:07 +02:00
|
|
|
b.appendArray( sname , sub );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( value->IsDate() ) {
|
2010-08-01 09:47:53 +02:00
|
|
|
b.appendDate( sname , Date_t( (unsigned long long)(v8::Date::Cast( *value )->NumberValue())) );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2009-12-17 02:47:32 +01:00
|
|
|
|
|
|
|
if ( value->IsExternal() )
|
|
|
|
return;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( value->IsObject() ) {
|
2009-12-16 20:42:46 +01:00
|
|
|
// 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...
|
2009-12-16 01:54:39 +01:00
|
|
|
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
|
2011-01-04 06:40:41 +01:00
|
|
|
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 );
|
2009-12-16 01:54:39 +01:00
|
|
|
}
|
|
|
|
}
|
2009-10-10 07:30:00 +02:00
|
|
|
string s = toSTLString( value );
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( s.size() && s[0] == '/' ) {
|
2009-10-10 07:30:00 +02:00
|
|
|
s = s.substr( 1 );
|
2010-02-02 00:59:01 +01:00
|
|
|
string r = s.substr( 0 , s.rfind( "/" ) );
|
|
|
|
string o = s.substr( s.rfind( "/" ) + 1 );
|
2010-08-19 00:12:31 +02:00
|
|
|
b.appendRegex( sname , r , o );
|
2009-10-10 07:30:00 +02:00
|
|
|
}
|
|
|
|
else if ( value->ToObject()->GetPrototype()->IsObject() &&
|
2011-01-04 06:40:41 +01:00
|
|
|
value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ) {
|
2009-10-10 07:30:00 +02:00
|
|
|
OID oid;
|
|
|
|
oid.init( toSTLString( value ) );
|
2010-07-20 18:42:07 +02:00
|
|
|
b.appendOID( sname , &oid );
|
2009-12-17 01:57:12 +01:00
|
|
|
}
|
2010-01-21 01:21:19 +01:00
|
|
|
else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__NumberLong" ) ).IsEmpty() ) {
|
2009-12-17 01:57:12 +01:00
|
|
|
// TODO might be nice to potentially speed this up with an indexed internal
|
|
|
|
// field, but I don't yet know how to use an ObjectTemplate with a
|
|
|
|
// constructor.
|
2010-03-01 19:36:40 +01:00
|
|
|
v8::Handle< v8::Object > it = value->ToObject();
|
|
|
|
long long val;
|
|
|
|
if ( !it->Has( v8::String::New( "top" ) ) ) {
|
|
|
|
val = (long long)( it->Get( v8::String::New( "floatApprox" ) )->NumberValue() );
|
2011-01-04 06:40:41 +01:00
|
|
|
}
|
|
|
|
else {
|
2010-03-01 19:36:40 +01:00
|
|
|
val = (long long)
|
2011-01-04 06:40:41 +01:00
|
|
|
( (unsigned long long)( it->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) +
|
|
|
|
(unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() );
|
2010-03-01 19:36:40 +01:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-07-20 18:42:07 +02:00
|
|
|
b.append( sname, val );
|
2010-01-21 01:21:19 +01:00
|
|
|
}
|
|
|
|
else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__DBPointer" ) ).IsEmpty() ) {
|
2009-12-17 01:50:36 +01:00
|
|
|
OID oid;
|
|
|
|
oid.init( toSTLString( value->ToObject()->Get( v8::String::New( "id" ) ) ) );
|
|
|
|
string ns = toSTLString( value->ToObject()->Get( v8::String::New( "ns" ) ) );
|
2011-01-04 06:40:41 +01:00
|
|
|
b.appendDBRef( sname, ns, oid );
|
2009-12-21 18:55:20 +01:00
|
|
|
}
|
|
|
|
else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__BinData" ) ).IsEmpty() ) {
|
|
|
|
int len = obj->Get( v8::String::New( "len" ) )->ToInt32()->Value();
|
|
|
|
v8::String::Utf8Value data( obj->Get( v8::String::New( "data" ) ) );
|
|
|
|
const char *dataArray = *data;
|
|
|
|
assert( data.length() == len );
|
2010-07-20 18:42:07 +02:00
|
|
|
b.appendBinData( sname,
|
2011-01-04 06:40:41 +01:00
|
|
|
len,
|
|
|
|
mongo::BinDataType( obj->Get( v8::String::New( "type" ) )->ToInt32()->Value() ),
|
|
|
|
dataArray );
|
|
|
|
}
|
|
|
|
else {
|
2010-03-13 19:41:31 +01:00
|
|
|
BSONObj sub = v8ToMongo( value->ToObject() , depth );
|
2010-07-20 18:42:07 +02:00
|
|
|
b.append( sname , sub );
|
2009-10-10 07:30:00 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( value->IsBoolean() ) {
|
2010-07-20 18:42:07 +02:00
|
|
|
b.appendBool( sname , value->ToBoolean()->Value() );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
else if ( value->IsUndefined() ) {
|
2010-07-20 18:42:07 +02:00
|
|
|
b.appendUndefined( sname );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
else if ( value->IsNull() ) {
|
2010-07-20 18:42:07 +02:00
|
|
|
b.appendNull( sname );
|
2009-10-10 07:30:00 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-11-23 01:33:43 +01:00
|
|
|
cout << "don't know how to convert to mongo field [" << name << "]\t" << value << endl;
|
2009-10-10 07:30:00 +02:00
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth ) {
|
2009-10-10 07:30:00 +02:00
|
|
|
BSONObjBuilder b;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( depth == 0 ) {
|
2010-03-13 19:41:31 +01:00
|
|
|
v8::Handle<v8::String> idName = v8::String::New( "_id" );
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( o->HasRealNamedProperty( idName ) ) {
|
2010-03-13 19:41:31 +01:00
|
|
|
v8ToMongoElement( b , idName , "_id" , o->Get( idName ) );
|
|
|
|
}
|
2009-10-10 07:30:00 +02:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
Local<v8::Array> names = o->GetPropertyNames();
|
2011-01-04 06:40:41 +01:00
|
|
|
for ( unsigned int i=0; i<names->Length(); i++ ) {
|
2009-10-10 07:30:00 +02:00
|
|
|
v8::Local<v8::String> name = names->Get(v8::Integer::New(i) )->ToString();
|
|
|
|
|
|
|
|
if ( o->GetPrototype()->IsObject() &&
|
2011-01-04 06:40:41 +01:00
|
|
|
o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) )
|
2009-10-10 07:30:00 +02:00
|
|
|
continue;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
v8::Local<v8::Value> value = o->Get( name );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
const string sname = toSTLString( name );
|
2010-03-13 19:41:31 +01:00
|
|
|
if ( depth == 0 && sname == "_id" )
|
2009-10-10 07:30:00 +02:00
|
|
|
continue;
|
|
|
|
|
2010-03-13 19:41:31 +01:00
|
|
|
v8ToMongoElement( b , name , sname , value , depth + 1 );
|
2009-10-10 07:30:00 +02:00
|
|
|
}
|
|
|
|
return b.obj();
|
|
|
|
}
|
|
|
|
|
2009-10-21 17:40:43 +02:00
|
|
|
// --- object wrapper ---
|
|
|
|
|
|
|
|
class WrapperHolder {
|
|
|
|
public:
|
|
|
|
WrapperHolder( const BSONObj * o , bool readOnly , bool iDelete )
|
|
|
|
: _o(o), _readOnly( readOnly ), _iDelete( iDelete ) {
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
~WrapperHolder() {
|
|
|
|
if ( _o && _iDelete ) {
|
2009-10-21 17:40:43 +02:00
|
|
|
delete _o;
|
|
|
|
}
|
|
|
|
_o = 0;
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
v8::Handle<v8::Value> get( v8::Local<v8::String> name ) {
|
2009-10-21 17:40:43 +02:00
|
|
|
const string& s = toSTLString( name );
|
|
|
|
const BSONElement& e = _o->getField( s );
|
|
|
|
return mongoToV8Element(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
const BSONObj * _o;
|
|
|
|
bool _readOnly;
|
|
|
|
bool _iDelete;
|
|
|
|
};
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ) {
|
2009-10-21 17:40:43 +02:00
|
|
|
return new WrapperHolder( o , readOnly , iDelete );
|
|
|
|
}
|
|
|
|
|
|
|
|
#define WRAPPER_STRING (v8::String::New( "_wrapper" ) )
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
WrapperHolder * getWrapper( v8::Handle<v8::Object> o ) {
|
2009-10-21 17:40:43 +02:00
|
|
|
Handle<v8::Value> t = o->GetRealNamedProperty( WRAPPER_STRING );
|
|
|
|
assert( t->IsExternal() );
|
|
|
|
Local<External> c = External::Cast( *t );
|
|
|
|
WrapperHolder * w = (WrapperHolder*)(c->Value());
|
|
|
|
assert( w );
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
Handle<Value> wrapperCons(const Arguments& args) {
|
2009-10-21 17:40:43 +02:00
|
|
|
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] );
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2009-10-21 17:40:43 +02:00
|
|
|
return v8::Undefined();
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
v8::Handle<v8::Value> wrapperGetHandler( v8::Local<v8::String> name, const v8::AccessorInfo &info) {
|
2009-10-21 17:40:43 +02:00
|
|
|
return getWrapper( info.This() )->get( name );
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate() {
|
2010-09-08 06:32:14 +02:00
|
|
|
v8::Local<v8::FunctionTemplate> t = newV8Function< wrapperCons >();
|
2009-10-21 17:40:43 +02:00
|
|
|
t->InstanceTemplate()->SetNamedPropertyHandler( wrapperGetHandler );
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --- random utils ----
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
v8::Function * getNamedCons( const char * name ) {
|
2009-10-10 07:30:00 +02:00
|
|
|
return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( v8::String::New( name ) ) ) );
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
v8::Function * getObjectIdCons() {
|
2009-10-10 07:30:00 +02:00
|
|
|
return getNamedCons( "ObjectId" );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|