summaryrefslogtreecommitdiff
path: root/scripting
diff options
context:
space:
mode:
authorAaron <aaron@10gen.com>2009-12-29 17:29:24 -0800
committerAaron <aaron@10gen.com>2009-12-29 17:29:24 -0800
commitaece98652ee7284357a7e729ae2ee21aee259fe5 (patch)
tree147730f97fb66712efdbb2eebcec0a86dc701ce4 /scripting
parent24169676c46d8fd8d996d81f628f2d76c60425d9 (diff)
downloadmongo-aece98652ee7284357a7e729ae2ee21aee259fe5.tar.gz
SERVER-470 allow shell threads to be run in fresh scopes
Diffstat (limited to 'scripting')
-rw-r--r--scripting/engine_v8.cpp20
-rw-r--r--scripting/engine_v8.h1
-rw-r--r--scripting/v8_utils.cpp59
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() );
}
}