summaryrefslogtreecommitdiff
path: root/scripting
diff options
context:
space:
mode:
authorEliot Horowitz <eliot@10gen.com>2009-10-10 01:30:00 -0400
committerEliot Horowitz <eliot@10gen.com>2009-10-10 01:30:00 -0400
commit96d0418d96ccc5c5ff13330551b85410a30bd155 (patch)
treedb93b622a39be4a37fcad3e373a96a477c3dde83 /scripting
parent8a6dfdc9e967dca275720391dfa297087d4337c0 (diff)
downloadmongo-96d0418d96ccc5c5ff13330551b85410a30bd155.tar.gz
checkpoint for playing with v8
Diffstat (limited to 'scripting')
-rw-r--r--scripting/engine_v8.cpp49
-rw-r--r--scripting/engine_v8.h76
-rw-r--r--scripting/v8_utils.cpp136
-rw-r--r--scripting/v8_utils.h34
-rw-r--r--scripting/v8_wrapper.cpp692
-rw-r--r--scripting/v8_wrapper.h55
6 files changed, 1012 insertions, 30 deletions
diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp
index d7c14cb21fd..261fe8a9dfb 100644
--- a/scripting/engine_v8.cpp
+++ b/scripting/engine_v8.cpp
@@ -1,15 +1,36 @@
#include "engine_v8.h"
-#include "../shell/MongoJS.h"
+#include "v8_wrapper.h"
+#include "v8_utils.h"
namespace mongo {
+ V8ScriptEngine::V8ScriptEngine()
+ : _handleScope() , _globalTemplate( ObjectTemplate::New() ) {
+
+ }
+
+ V8ScriptEngine::~V8ScriptEngine(){
+ }
+
void ScriptEngine::setup(){
if ( !globalScriptEngine ){
globalScriptEngine = new V8ScriptEngine();
}
}
-
+
+ V8Scope::V8Scope( V8ScriptEngine * engine )
+ : _handleScope(),
+ _context( Context::New( 0 , engine->_globalTemplate ) ) ,
+ _scope( _context ) ,
+ _global( _context->Global() ){
+
+ }
+
+ V8Scope::~V8Scope(){
+
+ }
+
Handle< Value > V8Scope::nativeCallback( const Arguments &args ) {
Local< External > f = External::Cast( *args.Callee()->Get( v8::String::New( "_native_function" ) ) );
NativeFunction function = ( NativeFunction )( f->Value() );
@@ -30,4 +51,28 @@ namespace mongo {
return mongoToV8Element( ret.firstElement() );
}
+ void V8Scope::setNumber( const char * field , double val ){
+ _global->Set( v8::String::New( field ) , v8::Number::New( val ) );
+ }
+
+ void V8Scope::setString( const char * field , const char * val ){
+ _global->Set( v8::String::New( field ) , v8::String::New( val ) );
+ }
+
+ void V8Scope::setBoolean( const char * field , bool val ){
+ _global->Set( v8::String::New( field ) , v8::Boolean::New( val ) );
+ }
+
+ double V8Scope::getNumber( const char *field ){
+ return _global->Get( v8::String::New( field ) )->ToNumber()->Value();
+ }
+
+ string V8Scope::getString( const char *field ){
+ return toSTLString( _global->Get( v8::String::New( field ) ) );
+ }
+
+ bool V8Scope::getBoolean( const char *field ){
+ return _global->Get( v8::String::New( field ) )->ToBoolean()->Value();
+ }
+
} // namespace mongo
diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h
index db1827e967b..61b026b4719 100644
--- a/scripting/engine_v8.h
+++ b/scripting/engine_v8.h
@@ -6,54 +6,74 @@
using namespace v8;
namespace mongo {
+
+ class V8ScriptEngine;
class V8Scope : public Scope {
public:
- virtual void reset() {}
- virtual void init( BSONObj * data ) {}
-
- virtual void localConnect( const char * dbName ) {}
- virtual double getNumber( const char *field ) { assert( false ); return 0; }
- virtual string getString( const char *field ) { assert( false ); return ""; }
- virtual bool getBoolean( const char *field ) { assert( false ); return false; }
- virtual BSONObj getObject( const char *field ) { assert( false ); return BSONObj(); }
+ V8Scope( V8ScriptEngine * engine );
+ ~V8Scope();
- virtual int type( const char *field ) { assert( false ); return 0; }
+ virtual void reset(){}
+ virtual void init( BSONObj * data ){ assert(0); }
+
+ virtual void localConnect( const char * dbName ){ assert(0); }
+ virtual void externalSetup(){ assert(0); };
- virtual void setNumber( const char *field , double val ) { assert(0); }
- virtual void setString( const char *field , const char * val ) { assert(0); }
- virtual void setObject( const char *field , const BSONObj& obj , bool readOnly) { assert(0); }
- virtual void setBoolean( const char *field , bool val ) { assert(0); }
- virtual void setThis( const BSONObj * obj ) { assert(0); }
+ virtual double getNumber( const char *field );
+ virtual string getString( const char *field );
+ virtual bool getBoolean( const char *field );
+ virtual BSONObj getObject( const char *field ){ assert( false ); return BSONObj(); }
- virtual ScriptingFunction createFunction( const char * code ) { assert( false ); return 0; }
- virtual int invoke( ScriptingFunction func , const BSONObj& args ) { assert( false ); return 0; }
- virtual string getError() { assert( false ); return ""; }
+ virtual int type( const char *field ){ assert( false ); return 0; }
+
+ virtual void setNumber( const char *field , double val );
+ virtual void setString( const char *field , const char * val );
+ virtual void setBoolean( const char *field , bool val );
+ virtual void setElement( const char *field , const BSONElement& e ){ assert( 0 );}
+ virtual void setObject( const char *field , const BSONObj& obj , bool readOnly){ assert(0); }
+ virtual void setThis( const BSONObj * obj ){ assert(0); }
- virtual void injectNative( const char *field, NativeFunction func ) {
+ virtual ScriptingFunction _createFunction( const char * code ){ assert( false ); return 0; }
+ virtual int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = false ){ assert(0); return 0;}
+ virtual bool exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ){ assert(0); return 0; }
+ virtual string getError(){ assert( false ); return ""; }
+
+ virtual void injectNative( const char *field, NativeFunction func ){
Handle< FunctionTemplate > f( v8::FunctionTemplate::New( nativeCallback ) );
f->Set( v8::String::New( "_native_function" ), External::New( (void*)func ) );
- global_->Set( v8::String::New( field ), f );
+ _global->Set( v8::String::New( field ), f->GetFunction() );
}
- void setGlobal( const Handle< v8::ObjectTemplate > &global ) {
- global_ = global;
- }
-
+ void gc(){ assert(0); }
+
private:
+
static Handle< Value > nativeCallback( const Arguments &args );
- Handle< v8::ObjectTemplate > global_;
+
+ HandleScope _handleScope;
+ Handle<Context> _context;
+ Context::Scope _scope;
+ Handle<v8::Object> _global;
};
class V8ScriptEngine : public ScriptEngine {
public:
- V8ScriptEngine() {}
- virtual ~V8ScriptEngine() {}
+ V8ScriptEngine();
+ virtual ~V8ScriptEngine();
- virtual Scope * createScope() { return new V8Scope(); }
+ virtual Scope * createScope(){ return new V8Scope( this ); }
- virtual void runTest() {}
+ virtual void runTest(){}
+
+ bool utf8Ok() const { return true; }
+
+ private:
+ HandleScope _handleScope;
+ Handle<ObjectTemplate> _globalTemplate;
+
+ friend class V8Scope;
};
diff --git a/scripting/v8_utils.cpp b/scripting/v8_utils.cpp
new file mode 100644
index 00000000000..0777b3c3220
--- /dev/null
+++ b/scripting/v8_utils.cpp
@@ -0,0 +1,136 @@
+// ShellUtils.cpp
+
+#include "v8_utils.h"
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <vector>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+using namespace std;
+using namespace v8;
+
+namespace mongo {
+
+ Handle<v8::Value> Print(const Arguments& args) {
+ bool first = true;
+ for (int i = 0; i < args.Length(); i++) {
+ HandleScope handle_scope;
+ if (first) {
+ first = false;
+ } else {
+ printf(" ");
+ }
+ v8::String::Utf8Value str(args[i]);
+ printf("%s", *str);
+ }
+ printf("\n");
+ return v8::Undefined();
+ }
+
+ std::string toSTLString( const Handle<v8::Value> & o ){
+ v8::String::Utf8Value str(o);
+ const char * foo = *str;
+ std::string s(foo);
+ return s;
+ }
+
+ std::ostream& operator<<( std::ostream &s, const Handle<v8::Value> & o ){
+ v8::String::Utf8Value str(o);
+ s << *str;
+ return s;
+ }
+
+ std::ostream& operator<<( std::ostream &s, const v8::TryCatch * try_catch ){
+ HandleScope handle_scope;
+ v8::String::Utf8Value exception(try_catch->Exception());
+ Handle<v8::Message> message = try_catch->Message();
+
+ if (message.IsEmpty()) {
+ s << *exception << endl;
+ }
+ else {
+
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ int linenum = message->GetLineNumber();
+ cout << *filename << ":" << linenum << " " << *exception << endl;
+
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ cout << *sourceline << endl;
+
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++)
+ cout << " ";
+
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++)
+ cout << "^";
+
+ cout << endl;
+ }
+
+ if ( try_catch->next_ )
+ s << try_catch->next_;
+
+ return s;
+ }
+
+
+ Handle<v8::Value> Version(const Arguments& args) {
+ return v8::String::New(v8::V8::GetVersion());
+ }
+
+ bool ExecuteString(Handle<v8::String> source, Handle<v8::Value> name,
+ bool print_result, bool report_exceptions ){
+
+ HandleScope handle_scope;
+ v8::TryCatch try_catch;
+
+ Handle<v8::Script> script = v8::Script::Compile(source, name);
+ if (script.IsEmpty()) {
+ if (report_exceptions)
+ ReportException(&try_catch);
+ return false;
+ }
+
+ Handle<v8::Value> result = script->Run();
+ if ( result.IsEmpty() ){
+ if (report_exceptions)
+ ReportException(&try_catch);
+ return false;
+ }
+
+ if ( print_result ){
+
+ Local<Context> current = Context::GetCurrent();
+ Local<Object> global = current->Global();
+
+ Local<Value> shellPrint = global->Get( String::New( "shellPrint" ) );
+
+ if ( shellPrint->IsFunction() ){
+ v8::Function * f = (v8::Function*)(*shellPrint);
+ Handle<v8::Value> argv[1];
+ argv[0] = result;
+ f->Call( global , 1 , argv );
+ }
+ else if ( ! result->IsUndefined() ){
+ cout << result << endl;
+ }
+ }
+
+ return true;
+ }
+
+ void ReportException(v8::TryCatch* try_catch) {
+ cout << try_catch << endl;
+ }
+
+ extern v8::Handle< v8::Context > baseContext_;
+
+ void installShellUtils( Handle<v8::ObjectTemplate>& global ){
+ global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
+ global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version));
+ }
+
+}
diff --git a/scripting/v8_utils.h b/scripting/v8_utils.h
new file mode 100644
index 00000000000..bcbf54f6f2b
--- /dev/null
+++ b/scripting/v8_utils.h
@@ -0,0 +1,34 @@
+// v8_utils.h
+
+#pragma once
+
+#include <v8.h>
+
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <assert.h>
+#include <iostream>
+
+namespace mongo {
+
+ // Executes a string within the current v8 context.
+ bool ExecuteString(v8::Handle<v8::String> source,
+ v8::Handle<v8::Value> name,
+ bool print_result,
+ bool report_exceptions);
+
+ v8::Handle<v8::Value> Print(const v8::Arguments& args);
+ void ReportException(v8::TryCatch* handler);
+
+
+ void installShellUtils( v8::Handle<v8::ObjectTemplate>& global );
+
+#define jsassert(x,msg) assert(x)
+
+ std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o );
+ std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatch> * try_catch );
+ std::string toSTLString( const v8::Handle<v8::Value> & o );
+
+}
+
diff --git a/scripting/v8_wrapper.cpp b/scripting/v8_wrapper.cpp
new file mode 100644
index 00000000000..e98d3316932
--- /dev/null
+++ b/scripting/v8_wrapper.cpp
@@ -0,0 +1,692 @@
+// v8_wrapper.cpp
+
+#include "v8_wrapper.h"
+#include "v8_utils.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace v8;
+
+namespace mongo {
+
+#define CONN_STRING (v8::String::New( "_conn" ))
+
+#define DDD(x)
+ //#define DDD(x) ( cout << x << endl );
+
+ void installMongoGlobals( Handle<ObjectTemplate>& global ){
+ global->Set(v8::String::New("mongoInject"), FunctionTemplate::New(mongoInject));
+
+ v8::Local<v8::FunctionTemplate> mongo = FunctionTemplate::New( mongoInit );
+ global->Set(v8::String::New("Mongo") , mongo );
+
+ v8::Local<v8::FunctionTemplate> db = FunctionTemplate::New( dbInit );
+ global->Set(v8::String::New("DB") , db );
+ db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
+
+ v8::Local<v8::FunctionTemplate> dbCollection = FunctionTemplate::New( collectionInit );
+ global->Set(v8::String::New("DBCollection") , dbCollection );
+ dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
+
+ v8::Local<v8::FunctionTemplate> dbQuery = FunctionTemplate::New( dbQueryInit );
+ global->Set(v8::String::New("DBQuery") , dbQuery );
+ dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess );
+
+ v8::Local<v8::FunctionTemplate> objectId = FunctionTemplate::New( objectIdInit );
+ global->Set(v8::String::New("ObjectId") , objectId );
+ }
+
+ Handle<Value> mongoInject(const Arguments& args){
+ jsassert( args.Length() == 1 , "mongoInject takes exactly 1 argument" );
+ jsassert( args[0]->IsObject() , "mongoInject needs to be passed a prototype" );
+
+ Local<v8::Object> o = args[0]->ToObject();
+
+ o->Set( v8::String::New( "init" ) , FunctionTemplate::New( mongoInit )->GetFunction() );
+ o->Set( v8::String::New( "find" ) , FunctionTemplate::New( mongoFind )->GetFunction() );
+ o->Set( v8::String::New( "insert" ) , FunctionTemplate::New( mongoInsert )->GetFunction() );
+ o->Set( v8::String::New( "remove" ) , FunctionTemplate::New( mongoRemove )->GetFunction() );
+ o->Set( v8::String::New( "update" ) , FunctionTemplate::New( mongoUpdate )->GetFunction() );
+
+ Local<FunctionTemplate> t = FunctionTemplate::New( internalCursorCons );
+ t->PrototypeTemplate()->Set( v8::String::New("next") , FunctionTemplate::New( internalCursorNext ) );
+ t->PrototypeTemplate()->Set( v8::String::New("hasNext") , FunctionTemplate::New( internalCursorHasNext ) );
+ o->Set( v8::String::New( "internalCursor" ) , t->GetFunction() );
+
+ return v8::Undefined();
+ }
+
+ Handle<Value> mongoInit(const Arguments& args){
+
+ char host[255];
+
+ if ( args.Length() > 0 && args[0]->IsString() ){
+ assert( args[0]->ToString()->Utf8Length() < 250 );
+ args[0]->ToString()->WriteAscii( host );
+ }
+ else {
+ strcpy( host , "127.0.0.1" );
+ }
+
+ DBClientConnection * conn = new DBClientConnection( true );
+
+ string errmsg;
+ if ( ! conn->connect( host , errmsg ) ){
+ return v8::ThrowException( v8::String::New( "couldn't connect" ) );
+ }
+
+ // NOTE I don't believe the conn object will ever be freed.
+ args.This()->Set( CONN_STRING , External::New( conn ) );
+ args.This()->Set( v8::String::New( "slaveOk" ) , Boolean::New( false ) );
+
+ return v8::Undefined();
+ }
+
+
+ // ---
+
+ Local<v8::Object> mongoToV8( const BSONObj& m , bool array ){
+ Local<v8::Object> o;
+ if ( array )
+ o = v8::Array::New();
+ else
+ o = v8::Object::New();
+
+ mongo::BSONObj sub;
+
+ for ( BSONObjIterator i(m); i.more(); ) {
+ BSONElement f = i.next();
+ if ( f.eoo() )
+ break;
+
+ Local<Value> v;
+
+ switch ( f.type() ){
+
+ case mongo::Code:
+ cout << "warning, code saved in database just turned into string right now" << endl;
+ 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 ) );
+ 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 ) );
+ break;
+
+ 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;
+
+ case mongo::jstNULL:
+ 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 = v8::Object::New();
+
+ int len;
+ f.binData( len );
+
+ b->Set( v8::String::New( "subtype" ) , v8::Number::New( f.binDataType() ) );
+ b->Set( v8::String::New( "length" ) , v8::Number::New( len ) );
+
+ o->Set( v8::String::New( f.fieldName() ) , b );
+ break;
+ };
+
+ case mongo::Timestamp: {
+ Local<v8::Object> sub = v8::Object::New();
+
+ sub->Set( v8::String::New( "time" ) , v8::Date::New( f.timestampTime() ) );
+ sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) );
+
+ o->Set( v8::String::New( f.fieldName() ) , sub );
+ break;
+ }
+
+ case mongo::MinKey:
+ // TODO: make a special type
+ o->Set( v8::String::New( f.fieldName() ) , v8::String::New( "MinKey" ) );
+ break;
+
+ case mongo::MaxKey:
+ // TODO: make a special type
+ o->Set( v8::String::New( f.fieldName() ) , v8::String::New( "MaxKey" ) );
+ break;
+
+ default:
+ cout << "can't handle type: ";
+ cout << f.type() << " ";
+ cout << f.toString();
+ cout << endl;
+ break;
+ }
+
+ }
+
+ return o;
+ }
+
+ Handle<v8::Value> mongoToV8Element( const BSONElement &f ) {
+ assert( !f.eoo() );
+ switch ( f.type() ){
+
+ case mongo::Code:
+ cout << "warning, code saved in database just turned into string right now" << endl;
+ case mongo::String:
+ return v8::String::New( f.valuestr() );
+
+ case mongo::jstOID: {
+ v8::Function * idCons = getObjectIdCons();
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::String::New( f.__oid().str().c_str() );
+ return idCons->NewInstance( 1 , argv );
+ }
+
+ 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::jstNULL:
+ 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: {
+ Local<v8::Object> b = v8::Object::New();
+
+ int len;
+ f.binData( len );
+
+ b->Set( v8::String::New( "subtype" ) , v8::Number::New( f.binDataType() ) );
+ b->Set( v8::String::New( "length" ) , v8::Number::New( len ) );
+
+ return b;
+ };
+
+ case mongo::Timestamp: {
+ Local<v8::Object> sub = v8::Object::New();
+
+ sub->Set( v8::String::New( "time" ) , v8::Date::New( f.timestampTime() ) );
+ sub->Set( v8::String::New( "i" ) , v8::Number::New( f.timestampInc() ) );
+
+ return sub;
+ }
+
+ case mongo::MinKey:
+ // TODO: make a special type
+ return v8::String::New( "MinKey" );
+
+ case mongo::MaxKey:
+ // TODO: make a special type
+ return v8::String::New( "MaxKey" );
+
+ case mongo::Undefined:
+ return v8::Undefined();
+
+ default:
+ cout << "can't handle type: ";
+ 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 ){
+
+ if ( value->IsString() ){
+ if ( sname == "$where" )
+ b.appendCode( sname.c_str() , toSTLString( value ).c_str() );
+ else
+ b.append( sname.c_str() , toSTLString( value ).c_str() );
+ return;
+ }
+
+ if ( value->IsFunction() ){
+ b.appendCode( sname.c_str() , toSTLString( value ).c_str() );
+ return;
+ }
+
+ if ( value->IsNumber() ){
+ b.append( sname.c_str() , value->ToNumber()->Value() );
+ return;
+ }
+
+ if ( value->IsArray() ){
+ BSONObj sub = v8ToMongo( value->ToObject() );
+ b.appendArray( sname.c_str() , sub );
+ return;
+ }
+
+ if ( value->IsDate() ){
+ b.appendDate( sname.c_str() , (unsigned long long )(v8::Date::Cast( *value )->NumberValue()) );
+ return;
+ }
+
+ if ( value->IsObject() ){
+ string s = toSTLString( value );
+ if ( s.size() && s[0] == '/' ){
+ s = s.substr( 1 );
+ string r = s.substr( 0 , s.find( "/" ) );
+ string o = s.substr( s.find( "/" ) + 1 );
+ b.appendRegex( sname.c_str() , r.c_str() , o.c_str() );
+ }
+ else if ( value->ToObject()->GetPrototype()->IsObject() &&
+ value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ){
+ OID oid;
+ oid.init( toSTLString( value ) );
+ b.appendOID( sname.c_str() , &oid );
+ }
+ else {
+ BSONObj sub = v8ToMongo( value->ToObject() );
+ b.append( sname.c_str() , sub );
+ }
+ return;
+ }
+
+ if ( value->IsBoolean() ){
+ b.appendBool( sname.c_str() , value->ToBoolean()->Value() );
+ return;
+ }
+
+ else if ( value->IsUndefined() ){
+ return;
+ }
+
+ else if ( value->IsNull() ){
+ b.appendNull( sname.c_str() );
+ return;
+ }
+
+ cout << "don't know how to covert to mongo field [" << name << "]\t" << value << endl;
+ }
+
+ BSONObj v8ToMongo( v8::Handle<v8::Object> o ){
+ BSONObjBuilder b;
+
+ v8::Handle<v8::String> idName = v8::String::New( "_id" );
+ 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++ ){
+ v8::Local<v8::String> name = names->Get(v8::Integer::New(i) )->ToString();
+
+ if ( o->GetPrototype()->IsObject() &&
+ o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) )
+ continue;
+
+ v8::Local<v8::Value> value = o->Get( name );
+
+ const string sname = toSTLString( name );
+ if ( sname == "_id" )
+ continue;
+
+ v8ToMongoElement( b , name , sname , value );
+ }
+ return b.obj();
+ }
+
+#ifdef _WIN32
+#define GETNS char * ns = new char[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns );
+#else
+#define GETNS char ns[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns );
+#endif
+
+ DBClientConnection * getConnection( const Arguments& args ){
+ Local<External> c = External::Cast( *(args.This()->Get( CONN_STRING )) );
+ DBClientConnection * conn = (DBClientConnection*)(c->Value());
+ assert( conn );
+ return conn;
+ }
+
+ // ---- real methods
+
+ /**
+ 0 - namespace
+ 1 - query
+ 2 - fields
+ 3 - limit
+ 4 - skip
+ */
+ Handle<Value> mongoFind(const Arguments& args){
+ jsassert( args.Length() == 5 , "find needs 5 args" );
+ jsassert( args[1]->IsObject() , "needs to be an object" );
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ BSONObj q = v8ToMongo( args[1]->ToObject() );
+ DDD( "query:" << q );
+
+ BSONObj fields;
+ bool haveFields = args[2]->IsObject() && args[2]->ToObject()->GetPropertyNames()->Length() > 0;
+ if ( haveFields )
+ fields = v8ToMongo( args[2]->ToObject() );
+
+ Local<v8::Object> mongo = args.This();
+ Local<v8::Value> slaveOkVal = mongo->Get( v8::String::New( "slaveOk" ) );
+ jsassert( slaveOkVal->IsBoolean(), "slaveOk member invalid" );
+ bool slaveOk = slaveOkVal->BooleanValue();
+
+ try {
+ auto_ptr<mongo::DBClientCursor> cursor;
+ int nToReturn = (int)(args[3]->ToNumber()->Value());
+ int nToSkip = (int)(args[4]->ToNumber()->Value());
+ {
+ v8::Unlocker u;
+ cursor = conn->query( ns, q , nToReturn , nToSkip , haveFields ? &fields : 0, slaveOk ? Option_SlaveOk : 0 );
+ }
+
+ v8::Function * cons = (v8::Function*)( *( mongo->Get( v8::String::New( "internalCursor" ) ) ) );
+ Local<v8::Object> c = cons->NewInstance();
+
+ // NOTE I don't believe the cursor object will ever be freed.
+ c->Set( v8::String::New( "cursor" ) , External::New( cursor.release() ) );
+ return c;
+ }
+ catch ( ... ){
+ return v8::ThrowException( v8::String::New( "socket error on query" ) );
+ }
+ }
+
+ v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args){
+ jsassert( args.Length() == 2 , "insert needs 2 args" );
+ jsassert( args[1]->IsObject() , "have to insert an object" );
+
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ v8::Handle<v8::Object> in = args[1]->ToObject();
+
+ if ( ! in->Has( v8::String::New( "_id" ) ) ){
+ v8::Handle<v8::Value> argv[1];
+ in->Set( v8::String::New( "_id" ) , getObjectIdCons()->NewInstance( 0 , argv ) );
+ }
+
+ BSONObj o = v8ToMongo( in );
+
+ DDD( "want to save : " << o.jsonString() );
+ try {
+ conn->insert( ns , o );
+ }
+ catch ( ... ){
+ return v8::ThrowException( v8::String::New( "socket error on insert" ) );
+ }
+
+ return args[1];
+ }
+
+ v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args){
+ jsassert( args.Length() == 2 , "remove needs 2 args" );
+ jsassert( args[1]->IsObject() , "have to remove an object template" );
+
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ v8::Handle<v8::Object> in = args[1]->ToObject();
+ BSONObj o = v8ToMongo( in );
+
+ DDD( "want to remove : " << o.jsonString() );
+ try {
+ conn->remove( ns , o );
+ }
+ catch ( ... ){
+ return v8::ThrowException( v8::String::New( "socket error on remove" ) );
+ }
+
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args){
+ jsassert( args.Length() >= 3 , "update needs at least 3 args" );
+ jsassert( args[1]->IsObject() , "1st param to update has to be an object" );
+ jsassert( args[2]->IsObject() , "2nd param to update has to be an object" );
+
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ v8::Handle<v8::Object> q = args[1]->ToObject();
+ v8::Handle<v8::Object> o = args[2]->ToObject();
+
+ bool upsert = args.Length() > 3 && args[3]->IsBoolean() && args[3]->ToBoolean()->Value();
+
+ try {
+ conn->update( ns , v8ToMongo( q ) , v8ToMongo( o ) , upsert );
+ }
+ catch ( ... ){
+ return v8::ThrowException( v8::String::New( "socket error on remove" ) );
+ }
+
+ return v8::Undefined();
+ }
+
+
+
+
+ // --- cursor ---
+
+ mongo::DBClientCursor * getCursor( const Arguments& args ){
+ Local<External> c = External::Cast( *(args.This()->Get( v8::String::New( "cursor" ) ) ) );
+ mongo::DBClientCursor * cursor = (mongo::DBClientCursor*)(c->Value());
+ return cursor;
+ }
+
+ v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args){
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args){
+ mongo::DBClientCursor * cursor = getCursor( args );
+ if ( ! cursor )
+ return v8::Undefined();
+ BSONObj o;
+ {
+ v8::Unlocker u;
+ o = cursor->next();
+ }
+ return mongoToV8( o );
+ }
+
+ v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args){
+ mongo::DBClientCursor * cursor = getCursor( args );
+ if ( ! cursor )
+ return Boolean::New( false );
+ bool more;
+ {
+ v8::Unlocker u;
+ more = cursor->more();
+ }
+ return Boolean::New( more );
+ }
+
+
+ // --- DB ----
+
+ v8::Handle<v8::Value> dbInit(const v8::Arguments& args){
+ assert( args.Length() == 2 );
+
+ args.This()->Set( v8::String::New( "_mongo" ) , args[0] );
+ args.This()->Set( v8::String::New( "_name" ) , args[1] );
+
+ for ( int i=0; i<args.Length(); i++ )
+ assert( ! args[i]->IsUndefined() );
+
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::Value> collectionInit( const v8::Arguments& args ){
+ assert( args.Length() == 4 );
+
+ args.This()->Set( v8::String::New( "_mongo" ) , args[0] );
+ args.This()->Set( v8::String::New( "_db" ) , args[1] );
+ args.This()->Set( v8::String::New( "_shortName" ) , args[2] );
+ args.This()->Set( v8::String::New( "_fullName" ) , args[3] );
+
+ for ( int i=0; i<args.Length(); i++ )
+ assert( ! args[i]->IsUndefined() );
+
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args ){
+
+ v8::Handle<v8::Object> t = args.This();
+
+ assert( args.Length() >= 4 );
+
+ t->Set( v8::String::New( "_mongo" ) , args[0] );
+ t->Set( v8::String::New( "_db" ) , args[1] );
+ t->Set( v8::String::New( "_collection" ) , args[2] );
+ t->Set( v8::String::New( "_ns" ) , args[3] );
+
+ if ( args.Length() > 4 && args[4]->IsObject() )
+ t->Set( v8::String::New( "_query" ) , args[4] );
+ else
+ t->Set( v8::String::New( "_query" ) , v8::Object::New() );
+
+ if ( args.Length() > 5 && args[5]->IsObject() )
+ t->Set( v8::String::New( "_fields" ) , args[5] );
+ else
+ t->Set( v8::String::New( "_fields" ) , v8::Null() );
+
+
+ if ( args.Length() > 6 && args[6]->IsNumber() )
+ t->Set( v8::String::New( "_limit" ) , args[6] );
+ else
+ t->Set( v8::String::New( "_limit" ) , Number::New( 0 ) );
+
+ if ( args.Length() > 7 && args[7]->IsNumber() )
+ t->Set( v8::String::New( "_skip" ) , args[7] );
+ else
+ t->Set( v8::String::New( "_skip" ) , Number::New( 0 ) );
+
+ t->Set( v8::String::New( "_cursor" ) , v8::Null() );
+ t->Set( v8::String::New( "_numReturned" ) , v8::Number::New(0) );
+ t->Set( v8::String::New( "_special" ) , Boolean::New(false) );
+
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info) {
+ DDD( "collectionFallback [" << name << "]" );
+
+ v8::Handle<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get( name );
+ if ( ! real->IsUndefined() )
+ return real;
+
+ string sname = toSTLString( name );
+ if ( sname[0] == '_' ){
+ if ( ! ( info.This()->HasRealNamedProperty( name ) ) )
+ return v8::Undefined();
+ return info.This()->GetRealNamedPropertyInPrototypeChain( name );
+ }
+
+ v8::Handle<v8::Value> getCollection = info.This()->GetPrototype()->ToObject()->Get( v8::String::New( "getCollection" ) );
+ assert( getCollection->IsFunction() );
+
+ v8::Function * f = (v8::Function*)(*getCollection);
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = name;
+
+ return f->Call( info.This() , 1 , argv );
+ }
+
+ v8::Handle<v8::Value> dbQueryIndexAccess( unsigned int index , const v8::AccessorInfo& info ){
+ v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get( v8::String::New( "arrayAccess" ) );
+ assert( arrayAccess->IsFunction() );
+
+ v8::Function * f = (v8::Function*)(*arrayAccess);
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::Number::New( index );
+
+ return f->Call( info.This() , 1 , argv );
+ }
+
+ v8::Function * getNamedCons( const char * name ){
+ return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( v8::String::New( name ) ) ) );
+ }
+
+ v8::Function * getObjectIdCons(){
+ return getNamedCons( "ObjectId" );
+ }
+
+ v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ){
+ v8::Handle<v8::Object> it = args.This();
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+ v8::Function * f = getObjectIdCons();
+ it = f->NewInstance();
+ }
+
+ OID oid;
+
+ if ( args.Length() == 0 ){
+ oid.init();
+ }
+ else {
+ string s = toSTLString( args[0] );
+ oid.init( s );
+ }
+
+ it->Set( v8::String::New( "str" ) , v8::String::New( oid.str().c_str() ) );
+
+ return it;
+ }
+
+
+}
diff --git a/scripting/v8_wrapper.h b/scripting/v8_wrapper.h
new file mode 100644
index 00000000000..2f41822ae41
--- /dev/null
+++ b/scripting/v8_wrapper.h
@@ -0,0 +1,55 @@
+// v8_wrapper.h
+
+#pragma once
+
+#include <v8.h>
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+
+#include "../client/dbclient.h"
+
+namespace mongo {
+ void installMongoGlobals( v8::Handle<v8::ObjectTemplate>& global );
+
+ // the actual globals
+ v8::Handle<v8::Value> mongoInject(const v8::Arguments& args);
+
+ // utils
+ v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool array = 0 );
+ mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o );
+
+ void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value );
+ v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f );
+
+ mongo::DBClientConnection * getConnection( const v8::Arguments& args );
+
+
+
+ // Mongo members
+ v8::Handle<v8::Value> mongoInit(const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoFind(const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args);
+ v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args);
+
+
+ v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args);
+ v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args);
+ v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args);
+
+ // DB members
+
+ v8::Handle<v8::Value> dbInit(const v8::Arguments& args);
+ v8::Handle<v8::Value> collectionInit( const v8::Arguments& args );
+ v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args );
+
+ v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args );
+ v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::AccessorInfo& info );
+
+ v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info);
+
+ v8::Function * getNamedCons( const char * name );
+ v8::Function * getObjectIdCons();
+
+}