diff options
author | Aaron <aaron@10gen.com> | 2009-12-29 17:29:24 -0800 |
---|---|---|
committer | Aaron <aaron@10gen.com> | 2009-12-29 17:29:24 -0800 |
commit | aece98652ee7284357a7e729ae2ee21aee259fe5 (patch) | |
tree | 147730f97fb66712efdbb2eebcec0a86dc701ce4 /scripting | |
parent | 24169676c46d8fd8d996d81f628f2d76c60425d9 (diff) | |
download | mongo-aece98652ee7284357a7e729ae2ee21aee259fe5.tar.gz |
SERVER-470 allow shell threads to be run in fresh scopes
Diffstat (limited to 'scripting')
-rw-r--r-- | scripting/engine_v8.cpp | 20 | ||||
-rw-r--r-- | scripting/engine_v8.h | 1 | ||||
-rw-r--r-- | scripting/v8_utils.cpp | 59 |
3 files changed, 70 insertions, 10 deletions
diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp index 9956a36b578..1806158bb5e 100644 --- a/scripting/engine_v8.cpp +++ b/scripting/engine_v8.cpp @@ -228,8 +228,7 @@ namespace mongo { // --- functions ----- - ScriptingFunction V8Scope::_createFunction( const char * raw ){ - V8_SIMPLE_HEADER + Local< v8::Function > V8Scope::__createFunction( const char * raw ){ for(; isspace( *raw ); ++raw ); // skip whitespace string code = raw; if ( code.find( "function" ) == string::npos ){ @@ -258,18 +257,27 @@ namespace mongo { if ( script.IsEmpty() ){ _error = (string)"compile error: " + toSTLString( &try_catch ); log() << _error << endl; - return 0; + return Local< v8::Function >(); } Local<Value> result = script->Run(); if ( result.IsEmpty() ){ _error = (string)"compile error: " + toSTLString( &try_catch ); log() << _error << endl; - return 0; + return Local< v8::Function >(); } - - Persistent<Value> f = Persistent< Value >::New( _global->Get( v8::String::New( fn.c_str() ) ) ); + + 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; } diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h index 8f8ad64e520..9d86d923297 100644 --- a/scripting/engine_v8.h +++ b/scripting/engine_v8.h @@ -57,6 +57,7 @@ namespace mongo { virtual void setThis( const BSONObj * obj ); virtual ScriptingFunction _createFunction( const char * code ); + Local< v8::Function > __createFunction( const char * code ); virtual int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 , bool ignoreReturn = false ); virtual bool exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ); virtual string getError(){ return _error; } diff --git a/scripting/v8_utils.cpp b/scripting/v8_utils.cpp index 9da9bbe1403..b3f2277f8ed 100644 --- a/scripting/v8_utils.cpp +++ b/scripting/v8_utils.cpp @@ -25,10 +25,15 @@ #include <boost/smart_ptr.hpp> #include <boost/thread/thread.hpp> #include <boost/thread/xtime.hpp> +#include "engine_v8.h" +#include "v8_wrapper.h" +#include <shell/utils.h> using namespace std; using namespace v8; +extern const char * jsconcatcode_server; + namespace mongo { Handle<v8::Value> Print(const Arguments& args) { @@ -147,7 +152,7 @@ namespace mongo { class JSThreadConfig { public: - JSThreadConfig( const Arguments &args ) : started_(), done_() { + JSThreadConfig( const Arguments &args, bool newScope = false ) : started_(), done_(), newScope_( newScope ) { jsassert( args.Length() > 0, "need at least one argument" ); jsassert( args[ 0 ]->IsFunction(), "first argument must be a function" ); Local< v8::Function > f = v8::Function::Cast( *args[ 0 ] ); @@ -184,12 +189,34 @@ namespace mongo { JSThread( JSThreadConfig &config ) : config_( config ) {} void operator()() { Locker l; - Context::Scope context_scope( baseContext_ ); HandleScope handle_scope; + Handle< Context > context; + Handle< v8::Function > fun; + auto_ptr< V8Scope > scope; + if ( config_.newScope_ ) { + scope.reset( dynamic_cast< V8Scope * >( globalScriptEngine->createScope() ) ); + // these lines duplicated from dbshell, extract to common location. + scope->externalSetup(); + mongo::shellUtils::installShellUtils( *scope ); + scope->execSetup( jsconcatcode_server , "setupServerCode" ); + context = scope->context(); + // A v8::Function tracks the context in which it was created, so we have to + // create a new function in the new context. + Context::Scope baseScope( baseContext_ ); + string fCode = toSTLString( config_.f_->ToString() ); + Context::Scope context_scope( context ); + fun = scope->__createFunction( fCode.c_str() ); + } else { + context = baseContext_; + Context::Scope context_scope( context ); + fun = config_.f_; + } + Context::Scope context_scope( context ); + // TODO maybe we can get away with local handles here boost::scoped_array< Persistent< Value > > argv( new Persistent< Value >[ config_.args_.size() ] ); for( unsigned int i = 0; i < config_.args_.size(); ++i ) argv[ i ] = Persistent< Value >::New( config_.args_[ i ] ); - Local< Value > ret = config_.f_->Call( Context::GetCurrent()->Global(), config_.args_.size(), argv.get() ); + Local< Value > ret = fun->Call( context->Global(), config_.args_.size(), argv.get() ); for( unsigned int i = 0; i < config_.args_.size(); ++i ) argv[ i ].Dispose(); config_.returnData_ = Persistent< Value >::New( ret ); @@ -200,6 +227,7 @@ namespace mongo { bool started_; bool done_; + bool newScope_; Persistent< v8::Function > f_; vector< Persistent< Value > > args_; auto_ptr< boost::thread > thread_; @@ -215,6 +243,15 @@ namespace mongo { return v8::Undefined(); } + Handle< Value > ScopedThreadInit( const Arguments &args ) { + Handle<v8::Object> it = args.This(); + // NOTE I believe the passed JSThreadConfig will never be freed. If this + // policy is changed, JSThread may no longer be able to store JSThreadConfig + // by reference. + it->Set( v8::String::New( "_JSThreadConfig" ), External::New( new JSThreadConfig( args, true ) ) ); + return v8::Undefined(); + } + JSThreadConfig *thisConfig( const Arguments &args ) { Local< External > c = External::Cast( *(args.This()->Get( v8::String::New( "_JSThreadConfig" ) ) ) ); JSThreadConfig *config = (JSThreadConfig *)( c->Value() ); @@ -250,9 +287,23 @@ namespace mongo { return v8::Undefined(); } + Handle< Value > ScopedThreadInject( const Arguments &args ) { + jsassert( args.Length() == 1 , "threadInject takes exactly 1 argument" ); + jsassert( args[0]->IsObject() , "threadInject needs to be passed a prototype" ); + + Local<v8::Object> o = args[0]->ToObject(); + + o->Set( v8::String::New( "init" ) , FunctionTemplate::New( ScopedThreadInit )->GetFunction() ); + // inheritance takes care of other member functions + + return v8::Undefined(); + } + void installFork( v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context ) { - baseContext_ = context; + if ( baseContext_.IsEmpty() ) // if this is the shell, first call will be with shell context, otherwise don't expect to use fork() anyway + baseContext_ = context; global->Set( v8::String::New( "_threadInject" ), FunctionTemplate::New( ThreadInject )->GetFunction() ); + global->Set( v8::String::New( "_scopedThreadInject" ), FunctionTemplate::New( ScopedThreadInject )->GetFunction() ); } } |