2009-10-27 20:58:27 +01:00
|
|
|
// v8_utils.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-10-10 07:30:00 +02:00
|
|
|
|
|
|
|
#include "v8_utils.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include <map>
|
|
|
|
#include <sstream>
|
|
|
|
#include <vector>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
2009-12-22 23:32:45 +01:00
|
|
|
#include <boost/smart_ptr.hpp>
|
|
|
|
#include <boost/thread/thread.hpp>
|
|
|
|
#include <boost/thread/xtime.hpp>
|
2009-12-30 02:29:24 +01:00
|
|
|
#include "engine_v8.h"
|
2009-10-10 07:30:00 +02:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace v8;
|
|
|
|
|
|
|
|
namespace mongo {
|
|
|
|
|
|
|
|
Handle<v8::Value> Print(const Arguments& args) {
|
|
|
|
bool first = true;
|
|
|
|
for (int i = 0; i < args.Length(); i++) {
|
|
|
|
HandleScope handle_scope;
|
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
printf(" ");
|
|
|
|
}
|
|
|
|
v8::String::Utf8Value str(args[i]);
|
|
|
|
printf("%s", *str);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
return v8::Undefined();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string toSTLString( const Handle<v8::Value> & o ){
|
|
|
|
v8::String::Utf8Value str(o);
|
|
|
|
const char * foo = *str;
|
|
|
|
std::string s(foo);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2009-10-12 00:15:12 +02:00
|
|
|
std::string toSTLString( const v8::TryCatch * try_catch ){
|
|
|
|
|
|
|
|
stringstream ss;
|
|
|
|
|
2009-11-24 03:36:10 +01:00
|
|
|
//while ( try_catch ){ // disabled for v8 bleeding edge
|
2009-10-12 00:15:12 +02:00
|
|
|
|
|
|
|
v8::String::Utf8Value exception(try_catch->Exception());
|
|
|
|
Handle<v8::Message> message = try_catch->Message();
|
|
|
|
|
|
|
|
if (message.IsEmpty()) {
|
|
|
|
ss << *exception << endl;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
v8::String::Utf8Value filename(message->GetScriptResourceName());
|
|
|
|
int linenum = message->GetLineNumber();
|
|
|
|
ss << *filename << ":" << linenum << " " << *exception << endl;
|
|
|
|
|
|
|
|
v8::String::Utf8Value sourceline(message->GetSourceLine());
|
|
|
|
ss << *sourceline << endl;
|
|
|
|
|
|
|
|
int start = message->GetStartColumn();
|
|
|
|
for (int i = 0; i < start; i++)
|
|
|
|
ss << " ";
|
|
|
|
|
|
|
|
int end = message->GetEndColumn();
|
|
|
|
for (int i = start; i < end; i++)
|
|
|
|
ss << "^";
|
|
|
|
|
|
|
|
ss << endl;
|
|
|
|
}
|
|
|
|
|
2009-11-14 03:41:02 +01:00
|
|
|
//try_catch = try_catch->next_;
|
|
|
|
//}
|
2009-10-12 00:15:12 +02:00
|
|
|
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-10 07:30:00 +02:00
|
|
|
std::ostream& operator<<( std::ostream &s, const Handle<v8::Value> & o ){
|
|
|
|
v8::String::Utf8Value str(o);
|
|
|
|
s << *str;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream& operator<<( std::ostream &s, const v8::TryCatch * try_catch ){
|
|
|
|
HandleScope handle_scope;
|
|
|
|
v8::String::Utf8Value exception(try_catch->Exception());
|
|
|
|
Handle<v8::Message> message = try_catch->Message();
|
|
|
|
|
|
|
|
if (message.IsEmpty()) {
|
|
|
|
s << *exception << endl;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
v8::String::Utf8Value filename(message->GetScriptResourceName());
|
|
|
|
int linenum = message->GetLineNumber();
|
|
|
|
cout << *filename << ":" << linenum << " " << *exception << endl;
|
|
|
|
|
|
|
|
v8::String::Utf8Value sourceline(message->GetSourceLine());
|
|
|
|
cout << *sourceline << endl;
|
|
|
|
|
|
|
|
int start = message->GetStartColumn();
|
|
|
|
for (int i = 0; i < start; i++)
|
|
|
|
cout << " ";
|
|
|
|
|
|
|
|
int end = message->GetEndColumn();
|
|
|
|
for (int i = start; i < end; i++)
|
|
|
|
cout << "^";
|
|
|
|
|
|
|
|
cout << endl;
|
|
|
|
}
|
|
|
|
|
2009-11-24 03:36:10 +01:00
|
|
|
//if ( try_catch->next_ ) // disabled for v8 bleeding edge
|
2009-11-14 03:41:02 +01:00
|
|
|
// s << try_catch->next_;
|
2009-10-10 07:30:00 +02:00
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Handle<v8::Value> Version(const Arguments& args) {
|
2009-12-29 20:04:00 +01:00
|
|
|
HandleScope handle_scope;
|
|
|
|
return handle_scope.Close( v8::String::New(v8::V8::GetVersion()) );
|
2009-10-10 07:30:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ReportException(v8::TryCatch* try_catch) {
|
|
|
|
cout << try_catch << endl;
|
|
|
|
}
|
2009-12-22 23:32:45 +01:00
|
|
|
|
2009-12-23 02:56:57 +01:00
|
|
|
Handle< Context > baseContext_;
|
2009-12-22 23:32:45 +01:00
|
|
|
|
|
|
|
class JSThreadConfig {
|
|
|
|
public:
|
2009-12-30 02:29:24 +01:00
|
|
|
JSThreadConfig( const Arguments &args, bool newScope = false ) : started_(), done_(), newScope_( newScope ) {
|
2009-12-22 23:32:45 +01:00
|
|
|
jsassert( args.Length() > 0, "need at least one argument" );
|
|
|
|
jsassert( args[ 0 ]->IsFunction(), "first argument must be a function" );
|
2009-12-23 02:56:57 +01:00
|
|
|
Local< v8::Function > f = v8::Function::Cast( *args[ 0 ] );
|
|
|
|
f_ = Persistent< v8::Function >::New( f );
|
2009-12-22 23:32:45 +01:00
|
|
|
for( int i = 1; i < args.Length(); ++i )
|
|
|
|
args_.push_back( Persistent< Value >::New( args[ i ] ) );
|
|
|
|
}
|
|
|
|
~JSThreadConfig() {
|
|
|
|
f_.Dispose();
|
|
|
|
for( vector< Persistent< Value > >::iterator i = args_.begin(); i != args_.end(); ++i )
|
|
|
|
i->Dispose();
|
|
|
|
returnData_.Dispose();
|
|
|
|
}
|
|
|
|
void start() {
|
|
|
|
jsassert( !started_, "Thread already started" );
|
|
|
|
JSThread jt( *this );
|
|
|
|
thread_.reset( new boost::thread( jt ) );
|
|
|
|
started_ = true;
|
|
|
|
}
|
|
|
|
void join() {
|
|
|
|
jsassert( started_ && !done_, "Thread not running" );
|
|
|
|
Unlocker u;
|
|
|
|
thread_->join();
|
|
|
|
done_ = true;
|
|
|
|
}
|
|
|
|
Local< Value > returnData() {
|
|
|
|
if ( !done_ )
|
|
|
|
join();
|
|
|
|
return Local< Value >::New( returnData_ );
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
class JSThread {
|
|
|
|
public:
|
|
|
|
JSThread( JSThreadConfig &config ) : config_( config ) {}
|
|
|
|
void operator()() {
|
|
|
|
Locker l;
|
|
|
|
HandleScope handle_scope;
|
2009-12-30 02:29:24 +01:00
|
|
|
Handle< Context > context;
|
|
|
|
Handle< v8::Function > fun;
|
|
|
|
auto_ptr< V8Scope > scope;
|
|
|
|
if ( config_.newScope_ ) {
|
2009-12-30 03:27:49 +01:00
|
|
|
scope.reset( dynamic_cast< V8Scope * >( globalScriptEngine->newScope() ) );
|
2009-12-30 02:29:24 +01:00
|
|
|
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 );
|
2009-12-30 04:26:54 +01:00
|
|
|
boost::scoped_array< Local< Value > > argv( new Local< Value >[ config_.args_.size() ] );
|
2009-12-22 23:32:45 +01:00
|
|
|
for( unsigned int i = 0; i < config_.args_.size(); ++i )
|
2009-12-30 04:26:54 +01:00
|
|
|
argv[ i ] = Local< Value >::New( config_.args_[ i ] );
|
2010-01-13 02:33:03 +01:00
|
|
|
TryCatch try_catch;
|
|
|
|
Handle< Value > ret = fun->Call( context->Global(), config_.args_.size(), argv.get() );
|
|
|
|
if ( ret.IsEmpty() ) {
|
|
|
|
string e = toSTLString( &try_catch );
|
|
|
|
log() << "js thread raised exception: " << e << endl;
|
|
|
|
// v8 probably does something sane if ret is empty, but not going to assume that for now
|
|
|
|
ret = v8::Undefined();
|
|
|
|
}
|
2009-12-22 23:32:45 +01:00
|
|
|
config_.returnData_ = Persistent< Value >::New( ret );
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
JSThreadConfig &config_;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool started_;
|
|
|
|
bool done_;
|
2009-12-30 02:29:24 +01:00
|
|
|
bool newScope_;
|
2009-12-23 02:56:57 +01:00
|
|
|
Persistent< v8::Function > f_;
|
2009-12-22 23:32:45 +01:00
|
|
|
vector< Persistent< Value > > args_;
|
|
|
|
auto_ptr< boost::thread > thread_;
|
|
|
|
Persistent< Value > returnData_;
|
|
|
|
};
|
|
|
|
|
|
|
|
Handle< Value > ThreadInit( const Arguments &args ) {
|
2009-12-23 02:56:57 +01:00
|
|
|
Handle<v8::Object> it = args.This();
|
2009-12-22 23:32:45 +01:00
|
|
|
// 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.
|
2010-01-13 03:00:29 +01:00
|
|
|
it->SetHiddenValue( v8::String::New( "_JSThreadConfig" ), External::New( new JSThreadConfig( args ) ) );
|
2009-12-23 02:56:57 +01:00
|
|
|
return v8::Undefined();
|
2009-12-22 23:32:45 +01:00
|
|
|
}
|
|
|
|
|
2009-12-30 02:29:24 +01:00
|
|
|
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.
|
2010-01-13 03:00:29 +01:00
|
|
|
it->SetHiddenValue( v8::String::New( "_JSThreadConfig" ), External::New( new JSThreadConfig( args, true ) ) );
|
2009-12-30 02:29:24 +01:00
|
|
|
return v8::Undefined();
|
|
|
|
}
|
|
|
|
|
2009-12-22 23:32:45 +01:00
|
|
|
JSThreadConfig *thisConfig( const Arguments &args ) {
|
2010-01-13 03:00:29 +01:00
|
|
|
Local< External > c = External::Cast( *(args.This()->GetHiddenValue( v8::String::New( "_JSThreadConfig" ) ) ) );
|
2009-12-22 23:32:45 +01:00
|
|
|
JSThreadConfig *config = (JSThreadConfig *)( c->Value() );
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle< Value > ThreadStart( const Arguments &args ) {
|
|
|
|
thisConfig( args )->start();
|
2009-12-23 02:56:57 +01:00
|
|
|
return v8::Undefined();
|
2009-12-22 23:32:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Handle< Value > ThreadJoin( const Arguments &args ) {
|
|
|
|
thisConfig( args )->join();
|
2009-12-23 02:56:57 +01:00
|
|
|
return v8::Undefined();
|
2009-12-22 23:32:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Handle< Value > ThreadReturnData( const Arguments &args ) {
|
2009-12-29 20:04:00 +01:00
|
|
|
HandleScope handle_scope;
|
|
|
|
return handle_scope.Close( thisConfig( args )->returnData() );
|
2009-12-22 23:32:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Handle< Value > ThreadInject( 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();
|
|
|
|
|
2009-12-23 02:56:57 +01:00
|
|
|
o->Set( v8::String::New( "init" ) , FunctionTemplate::New( ThreadInit )->GetFunction() );
|
|
|
|
o->Set( v8::String::New( "start" ) , FunctionTemplate::New( ThreadStart )->GetFunction() );
|
|
|
|
o->Set( v8::String::New( "join" ) , FunctionTemplate::New( ThreadJoin )->GetFunction() );
|
|
|
|
o->Set( v8::String::New( "returnData" ) , FunctionTemplate::New( ThreadReturnData )->GetFunction() );
|
2009-12-22 23:32:45 +01:00
|
|
|
|
|
|
|
return v8::Undefined();
|
|
|
|
}
|
|
|
|
|
2009-12-30 02:29:24 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2009-12-23 02:56:57 +01:00
|
|
|
void installFork( v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context ) {
|
2009-12-30 02:29:24 +01:00
|
|
|
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;
|
2009-12-22 23:32:45 +01:00
|
|
|
global->Set( v8::String::New( "_threadInject" ), FunctionTemplate::New( ThreadInject )->GetFunction() );
|
2009-12-30 02:29:24 +01:00
|
|
|
global->Set( v8::String::New( "_scopedThreadInject" ), FunctionTemplate::New( ScopedThreadInject )->GetFunction() );
|
2009-12-22 23:32:45 +01:00
|
|
|
}
|
2009-10-10 07:30:00 +02:00
|
|
|
|
|
|
|
}
|