//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 ); } 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 field = Handle::Cast(args.Data()); void* ptr = field->Value(); V8Scope* self = static_cast(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 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 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 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 && ! hasJSReturn( code ) && ( 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