Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

438 lines (361 sloc) 14.782 kb
//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.
*/
#include "engine_v8.h"
#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 );
namespace mongo {
// --- engine ---
V8ScriptEngine::V8ScriptEngine() {}
V8ScriptEngine::~V8ScriptEngine(){
}
void ScriptEngine::setup(){
if ( !globalScriptEngine ){
globalScriptEngine = new V8ScriptEngine();
}
}
// --- scope ---
V8Scope::V8Scope( V8ScriptEngine * engine )
: _engine( engine ) ,
_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() );
_wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate()->GetFunction() );
installDBTypes( _global );
installFork( _global, _context );
}
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();
}
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" ) ) );
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 ] );
}
BSONObj nativeArgs = b.obj();
BSONObj ret;
try {
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() ) );
}
Handle< Value > V8Scope::loadCallback( const Arguments &args ) {
Locker l;
HandleScope handle_scope;
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();
}
// ---- global stuff ----
void V8Scope::init( BSONObj * data ){
Locker l;
if ( ! data )
return;
BSONObjIterator i( *data );
while ( i.more() ){
BSONElement e = i.next();
setElement( e.fieldName() , e );
}
}
void V8Scope::setNumber( const char * field , double val ){
V8_SIMPLE_HEADER
_global->Set( v8::String::New( field ) , v8::Number::New( val ) );
}
void V8Scope::setString( const char * field , const char * val ){
V8_SIMPLE_HEADER
_global->Set( v8::String::New( field ) , v8::String::New( val ) );
}
void V8Scope::setBoolean( const char * field , bool val ){
V8_SIMPLE_HEADER
_global->Set( v8::String::New( field ) , v8::Boolean::New( val ) );
}
void V8Scope::setElement( const char *field , const BSONElement& e ){
V8_SIMPLE_HEADER
_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) );
}
int V8Scope::type( const char *field ){
V8_SIMPLE_HEADER
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( 10230 , "can't handle external yet" , 0 );
return -1;
}
if ( v->IsDate() )
return Date;
if ( v->IsObject() )
return Object;
throw UserException( 12509, (string)"don't know what this is: " + field );
}
v8::Handle<v8::Value> V8Scope::get( const char * field ){
return _global->Get( v8::String::New( field ) );
}
double V8Scope::getNumber( const char *field ){
V8_SIMPLE_HEADER
return get( field )->ToNumber()->Value();
}
int V8Scope::getNumberInt( const char *field ){
V8_SIMPLE_HEADER
return get( field )->ToInt32()->Value();
}
long long V8Scope::getNumberLongLong( const char *field ){
V8_SIMPLE_HEADER
return get( field )->ToInteger()->Value();
}
string V8Scope::getString( const char *field ){
V8_SIMPLE_HEADER
return toSTLString( get( field ) );
}
bool V8Scope::getBoolean( const char *field ){
V8_SIMPLE_HEADER
return get( field )->ToBoolean()->Value();
}
BSONObj V8Scope::getObject( const char * field ){
V8_SIMPLE_HEADER
Handle<Value> v = get( field );
if ( v->IsNull() || v->IsUndefined() )
return BSONObj();
uassert( 10231 , "not an object" , v->IsObject() );
return v8ToMongo( v->ToObject() );
}
// --- functions -----
Local< v8::Function > V8Scope::__createFunction( const char * raw ){
for(; isspace( *raw ); ++raw ); // skip whitespace
string code = raw;
if ( code.find( "function" ) == string::npos ){
if ( code.find( "\n" ) == string::npos &&
code.find( "return" ) == string::npos &&
( code.find( ";" ) == string::npos || code.find( ";" ) == code.size() - 1 ) ){
code = "return " + code;
}
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 Local< v8::Function >();
}
Local<Value> result = script->Run();
if ( result.IsEmpty() ){
_error = (string)"compile error: " + toSTLString( &try_catch );
log() << _error << endl;
return Local< v8::Function >();
}
return v8::Function::Cast( *_global->Get( v8::String::New( fn.c_str() ) ) );
}
ScriptingFunction V8Scope::_createFunction( const char * raw ){
V8_SIMPLE_HEADER
Local< Value > ret = __createFunction( raw );
if ( ret.IsEmpty() )
return 0;
Persistent<Value> f = Persistent< Value >::New( ret );
uassert( 10232, "not a func" , f->IsFunction() );
int num = _funcs.size() + 1;
_funcs.push_back( f );
return num;
}
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 ) );
}
int V8Scope::invoke( ScriptingFunction func , const BSONObj& argsObject, int timeoutMs , bool ignoreReturn ){
V8_SIMPLE_HEADER
Handle<Value> funcValue = _funcs[func-1];
TryCatch try_catch;
int nargs = argsObject.nFields();
scoped_array< Handle<Value> > args;
if ( nargs ){
args.reset( new Handle<Value>[nargs] );
BSONObjIterator it( argsObject );
for ( int i=0; i<nargs; i++ ){
BSONElement next = it.next();
args[i] = mongoToV8Element( next );
}
setObject( "args", argsObject, true ); // for backwards compatibility
} else {
_global->Set( v8::String::New( "args" ), v8::Undefined() );
}
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;
}
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
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;
ss << "compile error: " << toSTLString( &try_catch );
_error = ss.str();
if (reportError)
log() << _error << endl;
if ( assertOnError )
uassert( 10233 , _error , 0 );
return false;
}
Handle<v8::Value> result = script->Run();
if ( result.IsEmpty() ){
_error = (string)"exec error: " + toSTLString( &try_catch );
if ( reportError )
log() << _error << endl;
if ( assertOnError )
uassert( 10234 , _error , 0 );
return false;
}
_global->Set( v8::String::New( "__lastres__" ) , result );
if ( printResult && ! result->IsUndefined() ){
cout << toSTLString( result ) << endl;
}
return true;
}
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() );
}
void V8Scope::gc() {
Locker l;
while( V8::IdleNotification() );
}
// ----- db access -----
void V8Scope::localConnect( const char * dbName ){
V8_SIMPLE_HEADER
if ( _connectState == EXTERNAL )
throw UserException( 12510, "externalSetup already called, can't call externalSetup" );
if ( _connectState == LOCAL ){
if ( _localDBName == dbName )
return;
throw UserException( 12511, "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 );
_connectState = LOCAL;
_localDBName = dbName;
loadStored();
}
void V8Scope::externalSetup(){
V8_SIMPLE_HEADER
if ( _connectState == EXTERNAL )
return;
if ( _connectState == LOCAL )
throw UserException( 12512, "localConnect already called, can't call externalSetup" );
_global->Set( v8::String::New( "Mongo" ) , getMongoFunctionTemplate( false )->GetFunction() );
exec( jsconcatcode , "shell setup" , false , true , true , 0 );
_connectState = EXTERNAL;
}
// ----- internal -----
void V8Scope::reset(){
_startCall();
}
void V8Scope::_startCall(){
_error = "";
}
} // namespace mongo
Jump to Line
Something went wrong with that request. Please try again.