0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/scripting/engine_spidermonkey.cpp

1240 lines
39 KiB
C++
Raw Normal View History

2009-04-23 23:40:43 +02:00
// engine_spidermonkey.cpp
2009-05-12 21:49:40 +02:00
#include "stdafx.h"
#include "engine_spidermonkey.h"
2009-04-23 23:40:43 +02:00
2009-05-02 03:25:26 +02:00
#include "../client/dbclient.h"
2009-07-27 20:02:32 +02:00
#ifndef _WIN32
2009-07-24 17:10:35 +02:00
#include <boost/date_time/posix_time/posix_time.hpp>
2009-07-27 20:02:32 +02:00
#endif
2009-05-02 03:25:26 +02:00
2009-04-23 23:40:43 +02:00
namespace mongo {
boost::thread_specific_ptr<SMScope> currentScope( dontDeleteScope );
2009-08-05 19:39:01 +02:00
boost::mutex smmutex;
2009-08-05 19:30:55 +02:00
#define smlock boostlock ___lk( smmutex );
#define GETHOLDER(x,o) ((BSONHolder*)JS_GetPrivate( x , o ))
2009-05-10 15:24:54 +02:00
class BSONFieldIterator;
2009-06-19 21:46:53 +02:00
class BSONHolder {
public:
2009-06-19 21:46:53 +02:00
BSONHolder( BSONObj obj ){
2009-05-20 22:43:43 +02:00
_obj = obj.getOwned();
_inResolve = false;
_modified = false;
2009-05-20 22:43:43 +02:00
_magic = 17;
}
~BSONHolder(){
_magic = 18;
}
2009-06-19 21:46:53 +02:00
2009-05-20 22:43:43 +02:00
void check(){
uassert( "holder magic value is wrong" , _magic == 17 && _obj.isValid() );
2009-05-20 22:43:43 +02:00
}
2009-05-10 15:24:54 +02:00
BSONFieldIterator * it();
BSONObj _obj;
bool _inResolve;
2009-05-20 22:43:43 +02:00
char _magic;
2009-05-10 15:24:54 +02:00
list<string> _extra;
set<string> _removed;
bool _modified;
2009-05-10 15:24:54 +02:00
};
2009-05-10 15:24:54 +02:00
class BSONFieldIterator {
public:
2009-06-19 21:46:53 +02:00
2009-05-10 15:24:54 +02:00
BSONFieldIterator( BSONHolder * holder ){
2009-07-23 22:47:52 +02:00
set<string> added;
2009-05-10 15:24:54 +02:00
BSONObjIterator it( holder->_obj );
while ( it.more() ){
2009-05-10 15:24:54 +02:00
BSONElement e = it.next();
if ( holder->_removed.count( e.fieldName() ) )
continue;
2009-05-10 15:24:54 +02:00
_names.push_back( e.fieldName() );
2009-07-23 22:47:52 +02:00
added.insert( e.fieldName() );
2009-05-10 15:24:54 +02:00
}
2009-07-23 22:47:52 +02:00
for ( list<string>::iterator i = holder->_extra.begin(); i != holder->_extra.end(); i++ ){
if ( ! added.count( *i ) )
_names.push_back( *i );
}
2009-06-19 21:46:53 +02:00
2009-05-10 15:24:54 +02:00
_it = _names.begin();
}
2009-06-19 21:46:53 +02:00
2009-05-10 15:24:54 +02:00
bool more(){
return _it != _names.end();
}
string next(){
string s = *_it;
_it++;
return s;
}
2009-06-19 21:46:53 +02:00
2009-05-10 15:24:54 +02:00
private:
list<string> _names;
list<string>::iterator _it;
};
2009-05-10 15:24:54 +02:00
BSONFieldIterator * BSONHolder::it(){
return new BSONFieldIterator( this );
}
2009-05-27 20:31:23 +02:00
2009-05-08 17:02:12 +02:00
class Convertor : boost::noncopyable {
2009-04-29 16:16:39 +02:00
public:
Convertor( JSContext * cx ){
_context = cx;
}
2009-06-19 21:46:53 +02:00
2009-04-29 16:16:39 +02:00
string toString( JSString * so ){
jschar * s = JS_GetStringChars( so );
size_t srclen = JS_GetStringLength( so );
2009-05-13 17:42:48 +02:00
if( srclen == 0 )
return "";
2009-06-19 21:46:53 +02:00
size_t len = srclen * 6; // we only need *3, but see note on len below
2009-04-29 16:16:39 +02:00
char * dst = (char*)malloc( len );
2009-06-19 21:46:53 +02:00
len /= 2;
// doc re weird JS_EncodeCharacters api claims len expected in 16bit
// units, but experiments suggest 8bit units expected. We allocate
// enough memory that either will work.
assert( JS_EncodeCharacters( _context , s , srclen , dst , &len) );
2009-04-29 16:16:39 +02:00
string ss( dst , len );
free( dst );
2009-05-27 20:31:23 +02:00
if ( !JS_CStringsAreUTF8() )
for( string::const_iterator i = ss.begin(); i != ss.end(); ++i )
uassert( "non ascii character detected", (unsigned char)(*i) <= 127 );
2009-04-29 16:16:39 +02:00
return ss;
}
string toString( jsval v ){
2009-06-19 21:46:53 +02:00
return toString( JS_ValueToString( _context , v ) );
2009-04-29 16:16:39 +02:00
}
double toNumber( jsval v ){
double d;
uassert( "not a number" , JS_ValueToNumber( _context , v , &d ) );
return d;
}
bool toBoolean( jsval v ){
JSBool b;
assert( JS_ValueToBoolean( _context, v , &b ) );
return b;
}
OID toOID( jsval v ){
JSContext * cx = _context;
assert( JSVAL_IS_OID( v ) );
JSObject * o = JSVAL_TO_OBJECT( v );
OID oid;
oid.init( getString( o , "str" ) );
return oid;
}
2009-06-19 21:46:53 +02:00
2009-05-06 17:53:39 +02:00
BSONObj toObject( JSObject * o ){
if ( ! o )
return BSONObj();
2009-06-19 21:46:53 +02:00
if ( JS_InstanceOf( _context , o , &bson_ro_class , 0 ) ){
return GETHOLDER( _context , o )->_obj.getOwned();
}
BSONObj orig;
if ( JS_InstanceOf( _context , o , &bson_class , 0 ) ){
BSONHolder * holder = GETHOLDER(_context,o);
2009-07-23 23:37:52 +02:00
if ( ! holder->_modified ){
return holder->_obj;
2009-07-23 23:37:52 +02:00
}
orig = holder->_obj;
}
2009-06-19 21:46:53 +02:00
2009-05-06 17:53:39 +02:00
BSONObjBuilder b;
2009-06-19 21:46:53 +02:00
2009-07-24 14:26:35 +02:00
if ( ! appendSpecialDBObject( this , b , "value" , OBJECT_TO_JSVAL( o ) , o ) ){
jsval theid = getProperty( o , "_id" );
if ( ! JSVAL_IS_VOID( theid ) ){
append( b , "_id" , theid );
}
JSIdArray * properties = JS_Enumerate( _context , o );
assert( properties );
for ( jsint i=0; i<properties->length; i++ ){
jsid id = properties->vector[i];
jsval nameval;
assert( JS_IdToValue( _context ,id , &nameval ) );
string name = toString( nameval );
if ( name == "_id" )
continue;
append( b , name , getProperty( o , name.c_str() ) , orig[name].type() );
}
JS_DestroyIdArray( _context , properties );
2009-05-14 22:35:43 +02:00
}
2009-06-19 21:46:53 +02:00
2009-05-06 17:53:39 +02:00
return b.obj();
}
2009-06-19 21:46:53 +02:00
2009-05-06 17:53:39 +02:00
BSONObj toObject( jsval v ){
2009-06-19 21:46:53 +02:00
if ( JSVAL_IS_NULL( v ) ||
2009-05-06 17:53:39 +02:00
JSVAL_IS_VOID( v ) )
return BSONObj();
2009-06-19 21:46:53 +02:00
2009-05-06 17:53:39 +02:00
uassert( "not an object" , JSVAL_IS_OBJECT( v ) );
return toObject( JSVAL_TO_OBJECT( v ) );
}
2009-06-19 21:46:53 +02:00
2009-05-07 16:52:57 +02:00
string getFunctionCode( JSFunction * func ){
return toString( JS_DecompileFunction( _context , func , 0 ) );
}
string getFunctionCode( jsval v ){
uassert( "not a function" , JS_TypeOfValue( _context , v ) == JSTYPE_FUNCTION );
return getFunctionCode( JS_ValueToFunction( _context , v ) );
}
2009-07-24 14:26:35 +02:00
void appendRegex( BSONObjBuilder& b , const string& name , string s ){
assert( s[0] == '/' );
s = s.substr(1);
string::size_type end = s.rfind( '/' );
b.appendRegex( name.c_str() , s.substr( 0 , end ).c_str() , s.substr( end + 1 ).c_str() );
}
2009-05-07 16:52:57 +02:00
void append( BSONObjBuilder& b , string name , jsval val , BSONType oldType = EOO ){
//cout << "name: " << name << "\t" << typeString( val ) << " oldType: " << oldType << endl;
2009-05-06 17:53:39 +02:00
switch ( JS_TypeOfValue( _context , val ) ){
2009-06-19 21:46:53 +02:00
2009-05-06 17:53:39 +02:00
case JSTYPE_VOID: b.appendUndefined( name.c_str() ); break;
case JSTYPE_NULL: b.appendNull( name.c_str() ); break;
2009-06-19 21:46:53 +02:00
case JSTYPE_NUMBER: {
double d = toNumber( val );
if ( oldType == NumberInt && ((int)d) == d )
b.append( name.c_str() , (int)d );
else
b.append( name.c_str() , d );
break;
}
2009-05-06 17:53:39 +02:00
case JSTYPE_STRING: b.append( name.c_str() , toString( val ) ); break;
case JSTYPE_BOOLEAN: b.appendBool( name.c_str() , toBoolean( val ) ); break;
2009-05-08 17:02:12 +02:00
case JSTYPE_OBJECT: {
JSObject * o = JSVAL_TO_OBJECT( val );
2009-05-14 22:23:19 +02:00
if ( ! o || o == JSVAL_NULL ){
b.appendNull( name.c_str() );
}
2009-07-24 14:26:35 +02:00
else if ( ! appendSpecialDBObject( this , b , name , val , o ) ){
2009-05-08 22:57:41 +02:00
BSONObj sub = toObject( o );
if ( JS_IsArrayObject( _context , o ) ){
b.appendArray( name.c_str() , sub );
}
else {
b.append( name.c_str() , sub );
}
}
2009-05-08 17:02:12 +02:00
break;
}
2009-05-14 22:23:19 +02:00
2009-05-14 22:11:01 +02:00
case JSTYPE_FUNCTION: {
string s = toString(val);
if ( s[0] == '/' ){
2009-07-24 14:26:35 +02:00
appendRegex( b , name , s );
2009-05-14 22:11:01 +02:00
}
else {
2009-06-19 21:46:53 +02:00
b.appendCode( name.c_str() , getFunctionCode( val ).c_str() );
2009-05-14 22:11:01 +02:00
}
break;
}
2009-06-19 21:46:53 +02:00
2009-05-07 16:52:57 +02:00
default: uassert( (string)"can't append field. name:" + name + " type: " + typeString( val ) , 0 );
2009-05-06 17:53:39 +02:00
}
}
2009-06-19 21:46:53 +02:00
// ---------- to spider monkey ---------
2009-05-07 16:52:57 +02:00
bool hasFunctionIdentifier( const string& code ){
if ( code.size() < 9 || code.find( "function" ) != 0 )
return false;
2009-06-19 21:46:53 +02:00
2009-05-07 16:52:57 +02:00
return code[8] == ' ' || code[8] == '(';
}
2009-05-21 16:05:26 +02:00
bool isSimpleStatement( const string& code ){
if ( code.find( "return" ) != string::npos )
return false;
if ( code.find( ";" ) != string::npos &&
code.find( ";" ) != code.rfind( ";" ) )
return false;
if ( code.find( "for(" ) != string::npos ||
code.find( "for (" ) != string::npos ||
code.find( "while (" ) != string::npos ||
code.find( "while(" ) != string::npos )
return false;
return true;
}
void addRoot( JSFunction * f );
2009-06-01 18:53:24 +02:00
JSFunction * compileFunction( const char * code, JSObject * assoc = 0 ){
JSFunction * f = _compileFunction( code , assoc );
addRoot( f );
return f;
}
2009-06-19 21:46:53 +02:00
JSFunction * _compileFunction( const char * code, JSObject * assoc ){
while (isspace(*code)) {
code++;
}
2009-05-07 16:52:57 +02:00
if ( ! hasFunctionIdentifier( code ) ){
string s = code;
2009-05-21 16:05:26 +02:00
if ( isSimpleStatement( s ) ){
2009-05-07 16:52:57 +02:00
s = "return " + s;
2009-05-21 16:05:26 +02:00
}
2009-06-01 18:53:24 +02:00
return JS_CompileFunction( _context , assoc , "anonymous" , 0 , 0 , s.c_str() , strlen( s.c_str() ) , "nofile_a" , 0 );
2009-05-07 16:52:57 +02:00
}
2009-06-19 21:46:53 +02:00
2009-05-07 16:52:57 +02:00
// TODO: there must be a way in spider monkey to do this - this is a total hack
string s = "return ";
s += code;
s += ";";
2009-06-01 18:53:24 +02:00
JSFunction * func = JS_CompileFunction( _context , assoc , "anonymous" , 0 , 0 , s.c_str() , strlen( s.c_str() ) , "nofile_b" , 0 );
2009-05-20 21:47:48 +02:00
if ( ! func ){
cerr << "compile for hack failed" << endl;
return 0;
}
2009-06-19 21:46:53 +02:00
2009-05-07 16:52:57 +02:00
jsval ret;
2009-05-20 21:47:48 +02:00
if ( ! JS_CallFunction( _context , 0 , func , 0 , 0 , &ret ) ){
cerr << "call function for hack failed" << endl;
return 0;
}
addRoot( func );
2009-05-20 21:47:48 +02:00
uassert( "return for compile hack failed" , JS_TypeOfValue( _context , ret ) == JSTYPE_FUNCTION );
2009-05-07 16:52:57 +02:00
return JS_ValueToFunction( _context , ret );
}
2009-06-19 21:46:53 +02:00
2009-04-29 16:16:39 +02:00
jsval toval( double d ){
jsval val;
assert( JS_NewNumberValue( _context, d , &val ) );
return val;
}
2009-04-29 16:29:56 +02:00
jsval toval( const char * c ){
2009-05-27 20:31:23 +02:00
JSString * s = JS_NewStringCopyZ( _context , c );
2009-05-25 23:58:02 +02:00
assert( s );
2009-04-29 16:29:56 +02:00
return STRING_TO_JSVAL( s );
}
2009-04-29 17:12:35 +02:00
2009-05-10 04:07:36 +02:00
JSObject * toJSObject( const BSONObj * obj , bool readOnly=false ){
JSObject * o = JS_NewObject( _context , readOnly ? &bson_ro_class : &bson_class , NULL, NULL);
2009-05-25 23:58:02 +02:00
assert( o );
assert( JS_SetPrivate( _context , o , (void*)(new BSONHolder( obj->getOwned() ) ) ) );
2009-04-29 17:12:35 +02:00
return o;
}
2009-06-19 21:46:53 +02:00
2009-05-10 04:07:36 +02:00
jsval toval( const BSONObj* obj , bool readOnly=false ){
JSObject * o = toJSObject( obj , readOnly );
2009-04-29 17:12:35 +02:00
return OBJECT_TO_JSVAL( o );
}
2009-05-27 20:31:23 +02:00
2009-04-30 16:40:33 +02:00
jsval toval( const BSONElement& e ){
2009-06-19 21:46:53 +02:00
2009-04-30 16:40:33 +02:00
switch( e.type() ){
case EOO:
case jstNULL:
case Undefined:
2009-04-30 16:40:33 +02:00
return JSVAL_NULL;
case NumberDouble:
case NumberInt:
2009-08-04 18:07:09 +02:00
case NumberLong:
2009-04-30 16:40:33 +02:00
return toval( e.number() );
2009-06-05 15:54:35 +02:00
case Symbol: // TODO: should we make a special class for this
2009-04-30 16:40:33 +02:00
case String:
return toval( e.valuestr() );
case Bool:
return e.boolean() ? JSVAL_TRUE : JSVAL_FALSE;
case Object:{
2009-05-25 23:58:02 +02:00
BSONObj embed = e.embeddedObject().getOwned();
2009-05-10 04:07:36 +02:00
return toval( &embed );
}
2009-05-07 04:26:32 +02:00
case Array:{
2009-06-19 21:46:53 +02:00
2009-05-25 23:58:02 +02:00
BSONObj embed = e.embeddedObject().getOwned();
2009-06-19 21:46:53 +02:00
2009-05-25 23:58:02 +02:00
if ( embed.isEmpty() ){
return OBJECT_TO_JSVAL( JS_NewArrayObject( _context , 0 , 0 ) );
}
2009-05-07 04:26:32 +02:00
int n = embed.nFields();
2009-05-25 23:58:02 +02:00
assert( n > 0 );
2009-05-07 04:26:32 +02:00
JSObject * array = JS_NewArrayObject( _context , embed.nFields() , 0 );
assert( array );
2009-06-19 21:46:53 +02:00
jsval myarray = OBJECT_TO_JSVAL( array );
2009-06-19 21:46:53 +02:00
2009-05-07 04:26:32 +02:00
for ( int i=0; i<n; i++ ){
2009-05-20 22:54:11 +02:00
jsval v = toval( embed[i] );
2009-05-07 04:26:32 +02:00
assert( JS_SetElement( _context , array , i , &v ) );
}
2009-06-19 21:46:53 +02:00
return myarray;
2009-05-07 04:26:32 +02:00
}
2009-05-06 23:33:00 +02:00
case jstOID:{
OID oid = e.__oid();
JSObject * o = JS_NewObject( _context , &object_id_class , 0 , 0 );
setProperty( o , "str" , toval( oid.str().c_str() ) );
return OBJECT_TO_JSVAL( o );
}
case RegEx:{
const char * flags = e.regexFlags();
uintN flagNumber = 0;
while ( *flags ){
switch ( *flags ){
case 'g': flagNumber |= JSREG_GLOB; break;
case 'i': flagNumber |= JSREG_FOLD; break;
case 'm': flagNumber |= JSREG_MULTILINE; break;
//case 'y': flagNumber |= JSREG_STICKY; break;
default: uassert( "unknown regex flag" , 0 );
}
flags++;
}
2009-06-19 21:46:53 +02:00
2009-05-27 20:31:23 +02:00
JSObject * r = JS_NewRegExpObject( _context , (char*)e.regex() , strlen( e.regex() ) , flagNumber );
2009-05-06 23:33:00 +02:00
assert( r );
return OBJECT_TO_JSVAL( r );
}
case Code:{
2009-05-07 16:52:57 +02:00
JSFunction * func = compileFunction( e.valuestr() );
return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
}
2009-06-01 18:53:24 +02:00
case CodeWScope:{
JSFunction * func = compileFunction( e.codeWScopeCode() );
2009-06-19 21:46:53 +02:00
2009-06-01 18:53:24 +02:00
BSONObj extraScope = e.codeWScopeObject();
if ( ! extraScope.isEmpty() ){
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
}
return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
}
2009-06-19 21:46:53 +02:00
case Date:
2009-05-14 15:59:50 +02:00
return OBJECT_TO_JSVAL( js_NewDateObjectMsec( _context , (jsdouble) e.date() ) );
2009-06-19 21:46:53 +02:00
case MinKey:
return OBJECT_TO_JSVAL( JS_NewObject( _context , &minkey_class , 0 , 0 ) );
case MaxKey:
return OBJECT_TO_JSVAL( JS_NewObject( _context , &maxkey_class , 0 , 0 ) );
case Timestamp: {
JSObject * o = JS_NewObject( _context , &timestamp_class , 0 , 0 );
2009-05-15 19:07:14 +02:00
setProperty( o , "t" , toval( (double)(e.timestampTime()) ) );
setProperty( o , "i" , toval( (double)(e.timestampInc()) ) );
return OBJECT_TO_JSVAL( o );
}
case DBRef: {
JSObject * o = JS_NewObject( _context , &dbref_class , 0 , 0 );
setProperty( o , "ns" , toval( e.dbrefNS() ) );
JSObject * oid = JS_NewObject( _context , &object_id_class , 0 , 0 );
setProperty( oid , "str" , toval( e.dbrefOID().str().c_str() ) );
2009-06-19 21:46:53 +02:00
setProperty( o , "id" , OBJECT_TO_JSVAL( oid ) );
return OBJECT_TO_JSVAL( o );
}
2009-06-05 15:54:35 +02:00
case BinData:{
JSObject * o = JS_NewObject( _context , &bindata_class , 0 , 0 );
int len;
void * data = (void*)e.binData( len );
assert( JS_SetPrivate( _context , o , data ) );
2009-06-19 21:46:53 +02:00
2009-06-05 15:54:35 +02:00
setProperty( o , "len" , toval( len ) );
setProperty( o , "type" , toval( (int)e.binDataType() ) );
return OBJECT_TO_JSVAL( o );
}
2009-04-30 16:40:33 +02:00
}
2009-06-19 21:46:53 +02:00
uassert( "not done: toval" , 0 );
2009-04-30 16:40:33 +02:00
return 0;
}
2009-06-19 21:46:53 +02:00
// ------- object helpers ------
2009-04-29 16:16:39 +02:00
JSObject * getJSObject( JSObject * o , const char * name ){
jsval v;
2009-05-27 20:31:23 +02:00
assert( JS_GetProperty( _context , o , name , &v ) );
return JSVAL_TO_OBJECT( v );
}
2009-06-19 21:46:53 +02:00
JSObject * getGlobalObject( const char * name ){
return getJSObject( JS_GetGlobalObject( _context ) , name );
}
JSObject * getGlobalPrototype( const char * name ){
return getJSObject( getGlobalObject( name ) , "prototype" );
}
2009-06-19 21:46:53 +02:00
bool hasProperty( JSObject * o , const char * name ){
JSBool res;
assert( JS_HasProperty( _context , o , name , & res ) );
return res;
}
jsval getProperty( JSObject * o , const char * field ){
uassert( "object passed to getPropery is null" , o );
jsval v;
assert( JS_GetProperty( _context , o , field , &v ) );
return v;
}
void setProperty( JSObject * o , const char * field , jsval v ){
assert( JS_SetProperty( _context , o , field , &v ) );
}
2009-06-19 21:46:53 +02:00
string typeString( jsval v ){
JSType t = JS_TypeOfValue( _context , v );
return JS_GetTypeName( _context , t );
}
2009-06-19 21:46:53 +02:00
bool getBoolean( JSObject * o , const char * field ){
return toBoolean( getProperty( o , field ) );
}
double getNumber( JSObject * o , const char * field ){
return toNumber( getProperty( o , field ) );
}
2009-06-19 21:46:53 +02:00
2009-05-08 17:02:12 +02:00
string getString( JSObject * o , const char * field ){
return toString( getProperty( o , field ) );
}
JSContext * _context;
};
void bson_finalize( JSContext * cx , JSObject * obj ){
BSONHolder * o = GETHOLDER( cx , obj );
if ( o ){
delete o;
2009-05-25 23:58:02 +02:00
assert( JS_SetPrivate( cx , obj , 0 ) );
}
}
JSBool bson_enumerate( JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp ){
if ( enum_op == JSENUMERATE_INIT ){
2009-05-10 15:24:54 +02:00
BSONFieldIterator * it = GETHOLDER( cx , obj )->it();
*statep = PRIVATE_TO_JSVAL( it );
if ( idp )
*idp = JSVAL_ZERO;
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
2009-05-10 15:24:54 +02:00
BSONFieldIterator * it = (BSONFieldIterator*)JSVAL_TO_PRIVATE( *statep );
2009-06-19 21:46:53 +02:00
if ( enum_op == JSENUMERATE_NEXT ){
if ( it->more() ){
2009-05-10 15:24:54 +02:00
string name = it->next();
Convertor c(cx);
assert( JS_ValueToId( cx , c.toval( name.c_str() ) , idp ) );
}
else {
delete it;
*statep = 0;
}
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
if ( enum_op == JSENUMERATE_DESTROY ){
2009-06-19 21:46:53 +02:00
if ( it )
delete it;
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
uassert( "don't know what to do with this op" , 0 );
return JS_FALSE;
}
2009-06-19 21:46:53 +02:00
2009-05-10 04:07:36 +02:00
JSBool noaccess( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
BSONHolder * holder = GETHOLDER( cx , obj );
if ( holder->_inResolve )
2009-05-10 04:07:36 +02:00
return JS_TRUE;
JS_ReportError( cx , "doing write op on read only operation" );
return JS_FALSE;
}
2009-06-19 21:46:53 +02:00
JSClass bson_ro_class = {
2009-06-19 21:46:53 +02:00
"bson_ro_object" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE ,
2009-05-10 04:07:36 +02:00
noaccess, noaccess, JS_PropertyStub, noaccess,
(JSEnumerateOp)bson_enumerate, (JSResolveOp)(&resolveBSONField) , JS_ConvertStub, bson_finalize ,
JSCLASS_NO_OPTIONAL_MEMBERS
};
2009-05-10 15:24:54 +02:00
JSBool bson_add_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
BSONHolder * holder = GETHOLDER( cx , obj );
if ( ! holder->_inResolve ){
Convertor c(cx);
string name = c.toString( idval );
2009-07-23 22:47:52 +02:00
if ( holder->_obj[name].eoo() ){
holder->_extra.push_back( name );
2009-07-23 22:47:52 +02:00
}
holder->_modified = true;
2009-05-10 15:24:54 +02:00
}
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
JSBool mark_modified( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
Convertor c(cx);
BSONHolder * holder = GETHOLDER( cx , obj );
if ( holder->_inResolve )
return JS_TRUE;
holder->_modified = true;
holder->_removed.erase( c.toString( idval ) );
return JS_TRUE;
}
JSBool mark_modified_remove( JSContext *cx, JSObject *obj, jsval idval, jsval *vp){
Convertor c(cx);
BSONHolder * holder = GETHOLDER( cx , obj );
if ( holder->_inResolve )
return JS_TRUE;
holder->_modified = true;
holder->_removed.insert( c.toString( idval ) );
return JS_TRUE;
}
2009-05-10 04:07:36 +02:00
JSClass bson_class = {
2009-06-19 21:46:53 +02:00
"bson_object" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE ,
bson_add_prop, mark_modified_remove, JS_PropertyStub, mark_modified,
(JSEnumerateOp)bson_enumerate, (JSResolveOp)(&resolveBSONField) , JS_ConvertStub, bson_finalize ,
JSCLASS_NO_OPTIONAL_MEMBERS
};
2009-04-23 23:40:43 +02:00
static JSClass global_class = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
2009-06-19 21:46:53 +02:00
};
2009-05-02 03:25:26 +02:00
// --- global helpers ---
JSBool native_print( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
for ( uintN i=0; i<argc; i++ ){
if ( i > 0 )
cout << " ";
cout << c.toString( argv[i] );
}
cout << endl;
return JS_TRUE;
}
JSBool native_helper( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
Convertor c(cx);
uassert( "native_helper needs at least 1 arg" , argc >= 1 );
NativeFunction func = (NativeFunction)JSVAL_TO_PRIVATE( argv[0] );
BSONObjBuilder args;
for ( uintN i=1; i<argc; i++ ){
c.append( args , args.numStr( i ) , argv[i] );
}
BSONObj out = func( args.obj() );
2009-06-19 21:46:53 +02:00
if ( out.isEmpty() ){
*rval = JSVAL_VOID;
}
else {
*rval = c.toval( out.firstElement() );
}
return JS_TRUE;
}
JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval );
JSBool native_gc( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
JS_GC( cx );
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
JSFunctionSpec globalHelpers[] = {
{ "print" , &native_print , 0 , 0 , 0 } ,
{ "nativeHelper" , &native_helper , 1 , 0 , 0 } ,
{ "load" , &native_load , 1 , 0 , 0 } ,
{ "gc" , &native_gc , 1 , 0 , 0 } ,
{ 0 , 0 , 0 , 0 , 0 }
2009-05-02 03:25:26 +02:00
};
// ----END global helpers ----
2009-06-19 21:46:53 +02:00
2009-04-29 16:16:39 +02:00
JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
assert( JS_EnterLocalRootScope( cx ) );
2009-04-29 16:16:39 +02:00
Convertor c( cx );
2009-06-19 21:46:53 +02:00
BSONHolder * holder = GETHOLDER( cx , obj );
2009-05-20 22:43:43 +02:00
holder->check();
2009-04-29 16:16:39 +02:00
string s = c.toString( id );
2009-06-19 21:46:53 +02:00
BSONElement e = holder->_obj[ s.c_str() ];
2009-04-30 16:40:33 +02:00
if ( e.type() == EOO || holder->_removed.count( s ) ){
2009-04-29 16:16:39 +02:00
*objp = 0;
JS_LeaveLocalRootScope( cx );
2009-04-29 16:16:39 +02:00
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
2009-04-30 16:40:33 +02:00
jsval val = c.toval( e );
2009-04-29 16:29:56 +02:00
assert( ! holder->_inResolve );
holder->_inResolve = true;
2009-04-29 16:29:56 +02:00
assert( JS_SetProperty( cx , obj , s.c_str() , &val ) );
holder->_inResolve = false;
2009-07-23 23:37:52 +02:00
2009-07-24 00:58:32 +02:00
if ( val != JSVAL_NULL && val != JSVAL_VOID && JSVAL_IS_OBJECT( val ) ){
2009-07-23 23:37:52 +02:00
// TODO: this is a hack to get around sub objects being modified
2009-07-24 00:58:32 +02:00
JSObject * oo = JSVAL_TO_OBJECT( val );
if ( JS_InstanceOf( cx , oo , &bson_class , 0 ) ||
JS_IsArrayObject( cx , oo ) ){
holder->_modified = true;
}
2009-07-23 23:37:52 +02:00
}
2009-05-10 04:07:36 +02:00
2009-04-29 16:16:39 +02:00
*objp = obj;
JS_LeaveLocalRootScope( cx );
2009-04-29 16:16:39 +02:00
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
class SMScope;
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
class SMEngine : public ScriptEngine {
public:
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
SMEngine(){
2009-07-24 14:26:35 +02:00
#ifdef SM18
JS_SetCStringsAreUTF8();
#endif
2009-04-23 23:40:43 +02:00
_runtime = JS_NewRuntime(8L * 1024L * 1024L);
uassert( "JS_NewRuntime failed" , _runtime );
2009-07-24 14:26:35 +02:00
if ( ! utf8Ok() ){
cerr << "*** warning: spider monkey build without utf8 support. consider rebuilding with utf8 support" << endl;
}
2009-04-23 23:40:43 +02:00
}
~SMEngine(){
JS_DestroyRuntime( _runtime );
JS_ShutDown();
}
2009-04-24 05:52:47 +02:00
Scope * createScope();
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
void runTest();
2009-06-19 21:46:53 +02:00
2009-05-27 21:20:09 +02:00
virtual bool utf8Ok() const { return JS_CStringsAreUTF8(); }
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
private:
JSRuntime * _runtime;
friend class SMScope;
};
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
SMEngine * globalSMEngine;
void ScriptEngine::setup(){
globalSMEngine = new SMEngine();
globalScriptEngine = globalSMEngine;
}
2009-05-02 03:25:26 +02:00
// ------ special helpers -------
2009-06-19 21:46:53 +02:00
JSBool object_keyset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
2009-06-19 21:46:53 +02:00
JSIdArray * properties = JS_Enumerate( cx , obj );
assert( properties );
JSObject * array = JS_NewArrayObject( cx , properties->length , 0 );
assert( array );
2009-06-19 21:46:53 +02:00
for ( jsint i=0; i<properties->length; i++ ){
jsid id = properties->vector[i];
jsval idval;
assert( JS_IdToValue( cx , id , &idval ) );
assert( JS_SetElement( cx , array , i , &idval ) );
}
2009-06-19 21:46:53 +02:00
2009-06-07 04:47:53 +02:00
JS_DestroyIdArray( cx , properties );
*rval = OBJECT_TO_JSVAL( array );
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
2009-05-02 03:25:26 +02:00
// ------ scope ------
JSBool no_gc(JSContext *cx, JSGCStatus status){
return JS_FALSE;
}
2009-06-19 21:46:53 +02:00
JSBool yes_gc(JSContext *cx, JSGCStatus status){
return JS_TRUE;
}
2009-04-24 05:52:47 +02:00
class SMScope : public Scope {
2009-04-23 23:40:43 +02:00
public:
SMScope(){
2009-08-05 19:30:55 +02:00
smlock;
2009-04-23 23:40:43 +02:00
_context = JS_NewContext( globalSMEngine->_runtime , 8192 );
2009-04-29 16:16:39 +02:00
_convertor = new Convertor( _context );
2009-04-23 23:40:43 +02:00
massert( "JS_NewContext failed" , _context );
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
JS_SetOptions( _context , JSOPTION_VAROBJFIX);
//JS_SetVersion( _context , JSVERSION_LATEST); TODO
JS_SetErrorReporter( _context , errorReporter );
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
_global = JS_NewObject( _context , &global_class, NULL, NULL);
massert( "JS_NewObject failed for global" , _global );
2009-06-04 18:22:26 +02:00
JS_SetGlobalObject( _context , _global );
2009-04-24 05:52:47 +02:00
massert( "js init failed" , JS_InitStandardClasses( _context , _global ) );
2009-06-19 21:46:53 +02:00
JS_SetOptions( _context , JS_GetOptions( _context ) | JSOPTION_VAROBJFIX );
2009-05-02 03:25:26 +02:00
JS_DefineFunctions( _context , _global , globalHelpers );
2009-06-19 21:46:53 +02:00
// install my special helpers
2009-06-19 21:46:53 +02:00
assert( JS_DefineFunction( _context , _convertor->getGlobalPrototype( "Object" ) ,
"keySet" , object_keyset , 0 , JSPROP_READONLY ) );
2009-04-29 17:12:35 +02:00
_this = 0;
_externalSetup = false;
_localConnect = false;
//JS_SetGCCallback( _context , no_gc ); // this is useful for seeing if something is a gc problem
2009-04-23 23:40:43 +02:00
}
2009-04-23 23:40:43 +02:00
~SMScope(){
2009-08-05 19:30:55 +02:00
smlock;
uassert( "deleted SMScope twice?" , _convertor );
for ( list<void*>::iterator i=_roots.begin(); i != _roots.end(); i++ ){
JS_RemoveRoot( _context , *i );
}
_roots.clear();
if ( _this ){
2009-06-09 21:17:50 +02:00
JS_RemoveRoot( _context , &_this );
_this = 0;
}
if ( _convertor ){
delete _convertor;
_convertor = 0;
}
if ( _context ){
JS_DestroyContext( _context );
_context = 0;
}
2009-06-19 21:46:53 +02:00
}
2009-04-24 05:52:47 +02:00
void reset(){
2009-08-05 19:34:54 +02:00
smlock;
assert( _convertor );
return;
if ( _this ){
JS_RemoveRoot( _context , &_this );
_this = 0;
}
currentScope.reset( this );
_error = "";
2009-04-24 05:52:47 +02:00
}
void addRoot( void * root , const char * name ){
JS_AddNamedRoot( _context , root , name );
_roots.push_back( root );
}
2009-06-19 21:46:53 +02:00
2009-04-24 05:52:47 +02:00
void init( BSONObj * data ){
2009-05-08 22:43:59 +02:00
if ( ! data )
return;
2009-06-19 21:46:53 +02:00
2009-05-08 22:43:59 +02:00
BSONObjIterator i( *data );
while ( i.more() ){
2009-05-08 22:43:59 +02:00
BSONElement e = i.next();
_convertor->setProperty( _global , e.fieldName() , _convertor->toval( e ) );
_initFieldNames.insert( e.fieldName() );
2009-05-08 22:43:59 +02:00
}
2009-04-24 05:52:47 +02:00
}
2009-04-23 23:40:43 +02:00
2009-05-15 22:32:31 +02:00
void externalSetup(){
uassert( "already local connected" , ! _localConnect );
if ( _externalSetup )
return;
2009-05-15 22:32:31 +02:00
initMongoJS( this , _context , _global , false );
_externalSetup = true;
}
void localConnect( const char * dbName ){
uassert( "already setup for external db" , ! _externalSetup );
if ( _localConnect ){
uassert( "connected to different db" , _dbName == dbName );
return;
}
2009-05-15 22:32:31 +02:00
initMongoJS( this , _context , _global , true );
exec( "_mongo = new Mongo();" );
exec( ((string)"db = _mongo.getDB( \"" + dbName + "\" ); ").c_str() );
_localConnect = true;
_dbName = dbName;
}
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
// ----- getters ------
double getNumber( const char *field ){
jsval val;
assert( JS_GetProperty( _context , _global , field , &val ) );
return _convertor->toNumber( val );
2009-04-23 23:40:43 +02:00
}
2009-06-19 21:46:53 +02:00
2009-04-24 05:52:47 +02:00
string getString( const char *field ){
jsval val;
assert( JS_GetProperty( _context , _global , field , &val ) );
JSString * s = JS_ValueToString( _context , val );
2009-04-29 16:16:39 +02:00
return _convertor->toString( s );
2009-04-24 05:52:47 +02:00
}
bool getBoolean( const char *field ){
return _convertor->getBoolean( _global , field );
2009-04-24 05:52:47 +02:00
}
2009-06-19 21:46:53 +02:00
2009-04-24 05:52:47 +02:00
BSONObj getObject( const char *field ){
2009-05-06 17:53:39 +02:00
return _convertor->toObject( _convertor->getProperty( _global , field ) );
2009-04-24 05:52:47 +02:00
}
JSObject * getJSObject( const char * field ){
return _convertor->getJSObject( _global , field );
}
2009-04-24 05:52:47 +02:00
int type( const char *field ){
2009-04-30 16:40:33 +02:00
jsval val;
assert( JS_GetProperty( _context , _global , field , &val ) );
2009-06-19 21:46:53 +02:00
2009-04-30 16:40:33 +02:00
switch ( JS_TypeOfValue( _context , val ) ){
case JSTYPE_VOID: return Undefined;
case JSTYPE_NULL: return jstNULL;
2009-06-19 21:46:53 +02:00
case JSTYPE_OBJECT: {
if ( val == JSVAL_NULL )
return jstNULL;
2009-06-08 16:54:08 +02:00
JSObject * o = JSVAL_TO_OBJECT( val );
if ( JS_IsArrayObject( _context , o ) )
return Array;
if ( isDate( _context , o ) )
return Date;
2009-06-08 16:54:08 +02:00
return Object;
}
2009-04-30 16:40:33 +02:00
case JSTYPE_FUNCTION: return Code;
case JSTYPE_STRING: return String;
case JSTYPE_NUMBER: return NumberDouble;
case JSTYPE_BOOLEAN: return Bool;
default:
uassert( "unknown type" , 0 );
}
return 0;
2009-04-24 05:52:47 +02:00
}
// ----- setters ------
2009-06-19 21:46:53 +02:00
2009-04-24 05:52:47 +02:00
void setNumber( const char *field , double val ){
2009-04-29 16:16:39 +02:00
jsval v = _convertor->toval( val );
2009-04-24 05:52:47 +02:00
assert( JS_SetProperty( _context , _global , field , &v ) );
}
void setString( const char *field , const char * val ){
2009-04-29 16:29:56 +02:00
jsval v = _convertor->toval( val );
2009-04-24 05:52:47 +02:00
assert( JS_SetProperty( _context , _global , field , &v ) );
}
2009-05-10 04:07:36 +02:00
void setObject( const char *field , const BSONObj& obj , bool readOnly ){
jsval v = _convertor->toval( &obj , readOnly );
2009-04-29 16:16:39 +02:00
JS_SetProperty( _context , _global , field , &v );
2009-04-24 05:52:47 +02:00
}
void setBoolean( const char *field , bool val ){
jsval v = BOOLEAN_TO_JSVAL( val );
2009-06-19 21:46:53 +02:00
assert( JS_SetProperty( _context , _global , field , &v ) );
2009-04-24 05:52:47 +02:00
}
2009-06-19 21:46:53 +02:00
2009-04-24 05:52:47 +02:00
void setThis( const BSONObj * obj ){
2009-06-09 21:17:50 +02:00
if ( _this )
JS_RemoveRoot( _context , &_this );
_this = _convertor->toJSObject( obj );
2009-06-19 21:46:53 +02:00
2009-06-09 21:17:50 +02:00
JS_AddNamedRoot( _context , &_this , "scope this" );
2009-04-24 05:52:47 +02:00
}
2009-04-23 23:40:43 +02:00
// ---- functions -----
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
ScriptingFunction createFunction( const char * code ){
2009-05-20 21:47:48 +02:00
precall();
2009-05-07 16:52:57 +02:00
return (ScriptingFunction)_convertor->compileFunction( code );
2009-04-23 23:40:43 +02:00
}
2009-05-02 03:25:26 +02:00
struct TimeoutSpec {
boost::posix_time::ptime start;
boost::posix_time::time_duration timeout;
int count;
};
2009-06-19 21:46:53 +02:00
static JSBool checkTimeout( JSContext *cx, JSScript *script ) {
TimeoutSpec &spec = *(TimeoutSpec *)( JS_GetContextPrivate( cx ) );
if ( ++spec.count % 1000 != 0 )
return JS_TRUE;
boost::posix_time::time_duration elapsed = ( boost::posix_time::microsec_clock::local_time() - spec.start );
if ( elapsed < spec.timeout ) {
return JS_TRUE;
}
JS_ReportError( cx, "Timeout exceeded" );
return JS_FALSE;
}
void installCheckTimeout( int timeoutMs ) {
if ( timeoutMs > 0 ) {
TimeoutSpec *spec = new TimeoutSpec;
spec->timeout = boost::posix_time::millisec( timeoutMs );
spec->start = boost::posix_time::microsec_clock::local_time();
spec->count = 0;
JS_SetContextPrivate( _context, (void*)spec );
JS_SetBranchCallback( _context, checkTimeout );
2009-06-19 21:46:53 +02:00
}
}
void uninstallCheckTimeout( int timeoutMs ) {
if ( timeoutMs > 0 ) {
JS_SetBranchCallback( _context, 0 );
delete (TimeoutSpec *)JS_GetContextPrivate( _context );
JS_SetContextPrivate( _context, 0 );
}
}
2009-06-19 21:46:53 +02:00
2009-05-02 03:25:26 +02:00
void precall(){
_error = "";
currentScope.reset( this );
}
2009-06-19 21:46:53 +02:00
bool exec( const string& code , const string& name = "(anon)" , bool printResult = false , bool reportError = true , bool assertOnError = true, int timeoutMs = 0 ){
2009-05-02 03:25:26 +02:00
precall();
2009-06-19 21:46:53 +02:00
jsval ret = JSVAL_VOID;
2009-06-19 21:46:53 +02:00
installCheckTimeout( timeoutMs );
JSBool worked = JS_EvaluateScript( _context , _global , code.c_str() , strlen( code.c_str() ) , name.c_str() , 0 , &ret );
uninstallCheckTimeout( timeoutMs );
2009-06-19 21:46:53 +02:00
if ( assertOnError )
uassert( name + " exec failed" , worked );
2009-06-19 21:46:53 +02:00
if ( reportError && ! _error.empty() ){
// cout << "exec error: " << _error << endl;
// already printed in reportError, so... TODO
}
2009-06-19 21:46:53 +02:00
2009-06-12 02:27:07 +02:00
if ( worked )
_convertor->setProperty( _global , "__lastres__" , ret );
2009-06-19 21:46:53 +02:00
if ( worked && printResult && ! JSVAL_IS_VOID( ret ) )
cout << _convertor->toString( ret ) << endl;
return worked;
2009-05-02 03:25:26 +02:00
}
int invoke( JSFunction * func , const BSONObj& args, int timeoutMs ){
2009-08-05 19:34:54 +02:00
smlock;
2009-05-02 03:25:26 +02:00
precall();
2009-04-23 23:40:43 +02:00
jsval rval;
2009-06-19 21:46:53 +02:00
2009-04-30 16:40:33 +02:00
int nargs = args.nFields();
2009-05-12 21:49:40 +02:00
auto_ptr<jsval> smargsPtr( new jsval[nargs] );
jsval* smargs = smargsPtr.get();
2009-04-30 16:40:33 +02:00
BSONObjIterator it( args );
for ( int i=0; i<nargs; i++ )
smargs[i] = _convertor->toval( it.next() );
2009-06-19 21:46:53 +02:00
2009-05-10 04:07:36 +02:00
setObject( "args" , args , true ); // this is for backwards compatability
installCheckTimeout( timeoutMs );
JSBool ret = JS_CallFunction( _context , _this , func , nargs , smargs , &rval );
uninstallCheckTimeout( timeoutMs );
2009-06-19 21:46:53 +02:00
if ( !ret ) {
return -3;
2009-05-02 03:25:26 +02:00
}
2009-04-24 22:41:40 +02:00
assert( JS_SetProperty( _context , _global , "return" , &rval ) );
2009-04-23 23:40:43 +02:00
return 0;
}
int invoke( ScriptingFunction funcAddr , const BSONObj& args, int timeoutMs = 0 ){
return invoke( (JSFunction*)funcAddr , args , timeoutMs );
2009-04-23 23:40:43 +02:00
}
2009-05-02 03:25:26 +02:00
void gotError( string s ){
_error = s;
}
2009-06-19 21:46:53 +02:00
2009-05-02 03:25:26 +02:00
string getError(){
return _error;
}
void injectNative( const char *field, NativeFunction func ){
string name = field;
_convertor->setProperty( _global , (name + "_").c_str() , PRIVATE_TO_JSVAL( func ) );
2009-06-19 21:46:53 +02:00
stringstream code;
code << field << " = function(){ var a = [ " << field << "_ ]; for ( var i=0; i<arguments.length; i++ ){ a.push( arguments[i] ); } return nativeHelper.apply( null , a ); }";
exec( code.str().c_str() );
2009-06-19 21:46:53 +02:00
}
virtual void gc(){
JS_GC( _context );
}
2009-05-14 20:40:08 +02:00
JSContext *context() const { return _context; }
2009-06-19 21:46:53 +02:00
2009-04-23 23:40:43 +02:00
private:
JSContext * _context;
2009-04-29 16:16:39 +02:00
Convertor * _convertor;
2009-04-29 17:12:35 +02:00
2009-04-23 23:40:43 +02:00
JSObject * _global;
2009-04-29 17:12:35 +02:00
JSObject * _this;
2009-05-02 03:25:26 +02:00
string _error;
list<void*> _roots;
bool _externalSetup;
bool _localConnect;
string _dbName;
set<string> _initFieldNames;
2009-04-23 23:40:43 +02:00
};
2009-05-02 03:25:26 +02:00
void errorReporter( JSContext *cx, const char *message, JSErrorReport *report ){
stringstream ss;
ss << "JS Error: " << message;
2009-06-19 21:46:53 +02:00
2009-05-02 03:25:26 +02:00
if ( report ){
ss << " " << report->filename << ":" << report->lineno;
}
2009-06-19 21:46:53 +02:00
2009-05-02 03:25:26 +02:00
log() << ss.str() << endl;
2009-05-20 21:47:48 +02:00
if ( currentScope.get() ){
currentScope->gotError( ss.str() );
}
2009-05-02 03:25:26 +02:00
}
JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
Convertor c(cx);
Scope * s = currentScope.get();
for ( uintN i=0; i<argc; i++ ){
string filename = c.toString( argv[i] );
cout << "should load [" << filename << "]" << endl;
2009-06-19 21:46:53 +02:00
if ( ! s->execFile( filename , false , true , false ) ){
JS_ReportError( cx , ((string)"error loading file: " + filename ).c_str() );
return JS_FALSE;
}
}
2009-06-19 21:46:53 +02:00
return JS_TRUE;
}
2009-06-19 21:46:53 +02:00
2009-05-02 03:25:26 +02:00
2009-04-23 23:40:43 +02:00
void SMEngine::runTest(){
SMScope s;
2009-06-19 21:46:53 +02:00
s.localConnect( "foo" );
s.exec( "assert( db.getMongo() )" );
s.exec( "assert( db.bar , 'collection getting does not work' ); " );
s.exec( "assert.eq( db._name , 'foo' );" );
s.exec( "assert( _mongo == db.getMongo() ); " );
s.exec( "assert( _mongo == db._mongo ); " );
s.exec( "assert( typeof DB.bar == 'undefined' ); " );
s.exec( "assert( typeof DB.prototype.bar == 'undefined' , 'resolution is happening on prototype, not object' ); " );
s.exec( "assert( db.bar ); " );
s.exec( "assert( typeof db.addUser == 'function' )" );
s.exec( "assert( db.addUser == DB.prototype.addUser )" );
s.exec( "assert.eq( 'foo.bar' , db.bar._fullName ); " );
s.exec( "db.bar.verify();" );
s.exec( "db.bar.silly.verify();" );
s.exec( "assert.eq( 'foo.bar.silly' , db.bar.silly._fullName )" );
2009-05-05 17:02:47 +02:00
s.exec( "assert.eq( 'function' , typeof _mongo.find , 'mongo.find is not a function' )" );
2009-04-23 23:40:43 +02:00
}
2009-04-24 05:52:47 +02:00
Scope * SMEngine::createScope(){
return new SMScope();
}
void Convertor::addRoot( JSFunction * f ){
if ( ! f )
return;
2009-06-19 21:46:53 +02:00
SMScope * scope = currentScope.get();
uassert( "need a scope" , scope );
2009-06-19 21:46:53 +02:00
scope->addRoot( f , "cf" );
2009-06-19 21:46:53 +02:00
}
2009-04-23 23:40:43 +02:00
}
#include "sm_db.cpp"