0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 09:06:21 +01:00
mongodb/scripting/engine_v8.cpp

430 lines
14 KiB
C++
Raw Normal View History

//engine_v8.cpp
/* 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-05-04 17:49:18 +02:00
#include "engine_v8.h"
2009-10-10 07:30:00 +02:00
#include "v8_wrapper.h"
#include "v8_utils.h"
#include "v8_db.h"
#define V8_SIMPLE_HEADER Locker l; HandleScope handle_scope; Context::Scope context_scope( _context );
2009-05-04 17:49:18 +02:00
namespace mongo {
2009-10-11 04:24:08 +02:00
// --- engine ---
V8ScriptEngine::V8ScriptEngine() {}
2009-10-10 07:30:00 +02:00
V8ScriptEngine::~V8ScriptEngine(){
}
void ScriptEngine::setup(){
if ( !globalScriptEngine ){
globalScriptEngine = new V8ScriptEngine();
}
}
2009-10-10 07:30:00 +02:00
2009-10-11 04:24:08 +02:00
// --- scope ---
2009-10-10 07:30:00 +02:00
V8Scope::V8Scope( V8ScriptEngine * engine )
: _engine( engine ) ,
2009-10-13 16:12:44 +02:00
_connectState( NOT ){
Locker l;
HandleScope handleScope;
_context = Context::New();
Context::Scope context_scope( _context );
_global = Persistent< v8::Object >::New( _context->Global() );
_this = Persistent< v8::Object >::New( v8::Object::New() );
_global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)->GetFunction() );
_global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version)->GetFunction() );
_global->Set(v8::String::New("load"),
v8::FunctionTemplate::New(loadCallback, v8::External::New(this))->GetFunction() );
2009-11-23 01:33:43 +01:00
_wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate()->GetFunction() );
installDBTypes( _global );
installFork( _global, _context );
2009-10-10 07:30:00 +02:00
}
V8Scope::~V8Scope(){
Locker l;
Context::Scope context_scope( _context );
_wrapper.Dispose();
_this.Dispose();
for( unsigned i = 0; i < _funcs.size(); ++i )
_funcs[ i ].Dispose();
_funcs.clear();
_global.Dispose();
_context.Dispose();
2009-10-10 07:30:00 +02:00
}
Handle< Value > V8Scope::nativeCallback( const Arguments &args ) {
Locker l;
HandleScope handle_scope;
Local< External > f = External::Cast( *args.Callee()->Get( v8::String::New( "_native_function" ) ) );
2009-10-15 17:27:02 +02:00
NativeFunction function = (NativeFunction)(f->Value());
BSONObjBuilder b;
for( int i = 0; i < args.Length(); ++i ) {
stringstream ss;
ss << i;
v8ToMongoElement( b, v8::String::New( "foo" ), ss.str(), args[ i ] );
}
2009-10-15 17:27:02 +02:00
BSONObj nativeArgs = b.obj();
BSONObj ret;
try {
2009-10-15 17:27:02 +02:00
ret = function( nativeArgs );
} catch( const std::exception &e ) {
return v8::ThrowException(v8::String::New(e.what()));
} catch( ... ) {
return v8::ThrowException(v8::String::New("unknown exception"));
}
return handle_scope.Close( mongoToV8Element( ret.firstElement() ) );
2009-05-04 17:49:18 +02:00
}
2009-11-23 01:33:43 +01:00
Handle< Value > V8Scope::loadCallback( const Arguments &args ) {
Locker l;
HandleScope handle_scope;
2009-11-23 01:33:43 +01:00
Handle<External> field = Handle<External>::Cast(args.Data());
void* ptr = field->Value();
V8Scope* self = static_cast<V8Scope*>(ptr);
Context::Scope context_scope(self->_context);
for (int i = 0; i < args.Length(); ++i) {
std::string filename(toSTLString(args[i]));
if (!self->execFile(filename, false , true , false)) {
return v8::ThrowException(v8::String::New((std::string("error loading file: ") + filename).c_str()));
}
}
return v8::True();
}
2009-10-13 16:12:44 +02:00
// ---- global stuff ----
void V8Scope::init( BSONObj * data ){
Locker l;
2009-10-13 16:12:44 +02:00
if ( ! data )
return;
BSONObjIterator i( *data );
while ( i.more() ){
BSONElement e = i.next();
setElement( e.fieldName() , e );
}
}
2009-10-10 07:30:00 +02:00
void V8Scope::setNumber( const char * field , double val ){
V8_SIMPLE_HEADER
2009-10-10 07:30:00 +02:00
_global->Set( v8::String::New( field ) , v8::Number::New( val ) );
}
void V8Scope::setString( const char * field , const char * val ){
V8_SIMPLE_HEADER
2009-10-10 07:30:00 +02:00
_global->Set( v8::String::New( field ) , v8::String::New( val ) );
}
void V8Scope::setBoolean( const char * field , bool val ){
V8_SIMPLE_HEADER
2009-10-10 07:30:00 +02:00
_global->Set( v8::String::New( field ) , v8::Boolean::New( val ) );
}
2009-10-12 17:22:30 +02:00
void V8Scope::setElement( const char *field , const BSONElement& e ){
V8_SIMPLE_HEADER
2009-10-12 17:22:30 +02:00
_global->Set( v8::String::New( field ) , mongoToV8Element( e ) );
}
void V8Scope::setObject( const char *field , const BSONObj& obj , bool readOnly){
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( v8::String::New( field ) , mongoToV8( obj, false, readOnly) );
2009-10-12 17:22:30 +02:00
}
2009-10-13 16:12:44 +02:00
int V8Scope::type( const char *field ){
V8_SIMPLE_HEADER
2009-10-13 16:12:44 +02:00
Handle<Value> v = get( field );
if ( v->IsNull() )
return jstNULL;
if ( v->IsUndefined() )
return Undefined;
if ( v->IsString() )
return String;
if ( v->IsFunction() )
return Code;
if ( v->IsArray() )
return Array;
if ( v->IsBoolean() )
return Bool;
if ( v->IsInt32() )
return NumberInt;
if ( v->IsNumber() )
return NumberDouble;
if ( v->IsExternal() ){
uassert( "can't handle external yet" , 0 );
return -1;
}
if ( v->IsDate() )
return Date;
if ( v->IsObject() )
return Object;
2009-10-13 16:12:44 +02:00
throw UserException( (string)"don't know what this is: " + field );
2009-10-12 17:22:30 +02:00
}
2009-10-12 17:30:43 +02:00
v8::Handle<v8::Value> V8Scope::get( const char * field ){
return _global->Get( v8::String::New( field ) );
}
2009-10-10 07:30:00 +02:00
double V8Scope::getNumber( const char *field ){
V8_SIMPLE_HEADER
2009-10-12 17:30:43 +02:00
return get( field )->ToNumber()->Value();
2009-10-10 07:30:00 +02:00
}
int V8Scope::getNumberInt( const char *field ){
V8_SIMPLE_HEADER
2009-10-27 21:23:07 +01:00
return get( field )->ToInt32()->Value();
}
long long V8Scope::getNumberLongLong( const char *field ){
V8_SIMPLE_HEADER
2009-10-27 21:23:07 +01:00
return get( field )->ToInteger()->Value();
}
2009-10-10 07:30:00 +02:00
string V8Scope::getString( const char *field ){
V8_SIMPLE_HEADER
2009-10-12 17:30:43 +02:00
return toSTLString( get( field ) );
2009-10-10 07:30:00 +02:00
}
bool V8Scope::getBoolean( const char *field ){
V8_SIMPLE_HEADER
2009-10-12 17:30:43 +02:00
return get( field )->ToBoolean()->Value();
}
2009-10-13 16:12:44 +02:00
2009-10-12 17:30:43 +02:00
BSONObj V8Scope::getObject( const char * field ){
V8_SIMPLE_HEADER
2009-10-12 17:30:43 +02:00
Handle<Value> v = get( field );
if ( v->IsNull() || v->IsUndefined() )
return BSONObj();
uassert( "not an object" , v->IsObject() );
return v8ToMongo( v->ToObject() );
2009-10-10 07:30:00 +02:00
}
2009-10-13 16:12:44 +02:00
// --- functions -----
2009-10-11 04:24:08 +02:00
2009-10-12 00:15:12 +02:00
ScriptingFunction V8Scope::_createFunction( const char * raw ){
V8_SIMPLE_HEADER
for(; isspace( *raw ); ++raw ); // skip whitespace
2009-10-12 00:15:12 +02:00
string code = raw;
if ( code.find( "function" ) == string::npos ){
2009-10-12 17:25:49 +02:00
if ( code.find( "\n" ) == string::npos &&
code.find( "return" ) == string::npos &&
( code.find( ";" ) == string::npos || code.find( ";" ) == code.size() - 1 ) ){
code = "return " + code;
}
2009-10-12 00:15:12 +02:00
code = "function(){ " + code + "}";
}
int num = _funcs.size() + 1;
string fn;
{
stringstream ss;
ss << "_funcs" << num;
fn = ss.str();
}
code = fn + " = " + code;
TryCatch try_catch;
Handle<Script> script = v8::Script::Compile( v8::String::New( code.c_str() ) ,
v8::String::New( fn.c_str() ) );
if ( script.IsEmpty() ){
_error = (string)"compile error: " + toSTLString( &try_catch );
log() << _error << endl;
return 0;
}
Local<Value> result = script->Run();
if ( result.IsEmpty() ){
_error = (string)"compile error: " + toSTLString( &try_catch );
log() << _error << endl;
return 0;
}
Persistent<Value> f = Persistent< Value >::New( _global->Get( v8::String::New( fn.c_str() ) ) );
2009-10-12 00:15:12 +02:00
uassert( "not a func" , f->IsFunction() );
_funcs.push_back( f );
return num;
}
2009-10-13 16:12:44 +02:00
void V8Scope::setThis( const BSONObj * obj ){
V8_SIMPLE_HEADER
if ( ! obj ){
_this = Persistent< v8::Object >::New( v8::Object::New() );
return;
}
//_this = mongoToV8( *obj );
v8::Handle<v8::Value> argv[1];
argv[0] = v8::External::New( createWrapperHolder( obj , true , false ) );
_this = Persistent< v8::Object >::New( _wrapper->NewInstance( 1, argv ) );
2009-10-13 16:12:44 +02:00
}
2009-10-12 00:15:12 +02:00
int V8Scope::invoke( ScriptingFunction func , const BSONObj& argsObject, int timeoutMs , bool ignoreReturn ){
V8_SIMPLE_HEADER
2009-10-12 00:15:12 +02:00
Handle<Value> funcValue = _funcs[func-1];
TryCatch try_catch;
int nargs = argsObject.nFields();
2009-12-22 00:36:00 +01:00
scoped_array< Handle<Value> > args;
2009-10-12 00:15:12 +02:00
if ( nargs ){
args.reset( new Handle<Value>[nargs] );
BSONObjIterator it( argsObject );
for ( int i=0; i<nargs; i++ ){
BSONElement next = it.next();
2009-12-22 00:36:00 +01:00
args[i] = mongoToV8Element( next );
2009-10-12 00:15:12 +02:00
}
setObject( "args", argsObject, true ); // for backwards compatibility
} else {
_global->Set( v8::String::New( "args" ), v8::Undefined() );
2009-10-12 00:15:12 +02:00
}
Local<Value> result = ((v8::Function*)(*funcValue))->Call( _this , nargs , args.get() );
if ( result.IsEmpty() ){
stringstream ss;
ss << "error in invoke: " << toSTLString( &try_catch );
_error = ss.str();
log() << _error << endl;
return 1;
}
if ( ! ignoreReturn ){
_global->Set( v8::String::New( "return" ) , result );
}
return 0;
}
2009-10-11 04:24:08 +02:00
bool V8Scope::exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ){
if ( timeoutMs ){
static bool t = 1;
if ( t ){
log() << "timeoutMs not support for v8 yet" << endl;
t = 0;
}
}
V8_SIMPLE_HEADER
2009-10-11 04:24:08 +02:00
TryCatch try_catch;
Handle<Script> script = v8::Script::Compile( v8::String::New( code.c_str() ) ,
v8::String::New( name.c_str() ) );
if (script.IsEmpty()) {
stringstream ss;
2009-10-12 00:15:12 +02:00
ss << "compile error: " << toSTLString( &try_catch );
2009-10-11 04:24:08 +02:00
_error = ss.str();
if (reportError)
2009-10-12 00:15:12 +02:00
log() << _error << endl;
2009-10-11 04:24:08 +02:00
if ( assertOnError )
uassert( _error , 0 );
return false;
}
Handle<v8::Value> result = script->Run();
if ( result.IsEmpty() ){
_error = (string)"exec error: " + toSTLString( &try_catch );
2009-10-11 04:24:08 +02:00
if ( reportError )
2009-10-12 00:15:12 +02:00
log() << _error << endl;
2009-10-11 04:24:08 +02:00
if ( assertOnError )
uassert( _error , 0 );
return false;
}
_global->Set( v8::String::New( "__lastres__" ) , result );
if ( printResult && ! result->IsUndefined() ){
2009-10-11 04:24:08 +02:00
cout << toSTLString( result ) << endl;
}
return true;
}
2009-12-22 20:39:23 +01:00
void V8Scope::injectNative( const char *field, NativeFunction func ){
V8_SIMPLE_HEADER
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->GetFunction() );
}
2009-12-22 20:39:23 +01:00
void V8Scope::gc() {
Locker l;
2009-12-22 20:39:23 +01:00
while( V8::IdleNotification() );
}
2009-10-11 04:24:08 +02:00
2009-10-13 16:12:44 +02:00
// ----- db access -----
void V8Scope::localConnect( const char * dbName ){
V8_SIMPLE_HEADER
2009-10-13 16:12:44 +02:00
if ( _connectState == EXTERNAL )
throw UserException( "externalSetup already called, can't call externalSetup" );
if ( _connectState == LOCAL ){
if ( _localDBName == dbName )
return;
throw UserException( "localConnect called with a different name previously" );
}
//_global->Set( v8::String::New( "Mongo" ) , _engine->_externalTemplate->GetFunction() );
_global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( true )->GetFunction() );
exec( jsconcatcode , "localConnect 1" , false , true , true , 0 );
exec( "_mongo = new Mongo();" , "local connect 2" , false , true , true , 0 );
exec( (string)"db = _mongo.getDB(\"" + dbName + "\");" , "local connect 3" , false , true , true , 0 );
2009-10-13 16:12:44 +02:00
_connectState = LOCAL;
2009-10-15 17:27:02 +02:00
_localDBName = dbName;
2009-12-22 18:52:11 +01:00
loadStored();
2009-10-13 16:12:44 +02:00
}
void V8Scope::externalSetup(){
V8_SIMPLE_HEADER
2009-10-13 16:12:44 +02:00
if ( _connectState == EXTERNAL )
return;
if ( _connectState == LOCAL )
throw UserException( "localConnect already called, can't call externalSetup" );
_global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( false )->GetFunction() );
exec( jsconcatcode , "shell setup" , false , true , true , 0 );
2009-10-13 16:12:44 +02:00
_connectState = EXTERNAL;
}
// ----- internal -----
2009-10-13 17:11:55 +02:00
void V8Scope::reset(){
_startCall();
2009-10-13 17:11:55 +02:00
}
2009-10-11 04:24:08 +02:00
void V8Scope::_startCall(){
_error = "";
}
2009-10-10 07:30:00 +02:00
2009-05-05 00:45:32 +02:00
} // namespace mongo