summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/engine_spidermonkey.cpp
diff options
context:
space:
mode:
authorApollon Oikonomopoulos <apoikos@debian.org>2016-01-14 00:10:06 +0200
committerApollon Oikonomopoulos <apollon@skroutz.gr>2016-01-14 00:10:06 +0200
commit374e1947abcd3e127a2a613aff73ecffdb9199ea (patch)
treed83973c3c9802450acd5b5e86fe0d4e8e60a3a1b /src/mongo/scripting/engine_spidermonkey.cpp
parent65585c90b12d6523bea75a2aebaae2a2fdf9e641 (diff)
Imported Upstream version 2.6.11upstream/2.6.11
Diffstat (limited to 'src/mongo/scripting/engine_spidermonkey.cpp')
-rw-r--r--src/mongo/scripting/engine_spidermonkey.cpp1872
1 files changed, 0 insertions, 1872 deletions
diff --git a/src/mongo/scripting/engine_spidermonkey.cpp b/src/mongo/scripting/engine_spidermonkey.cpp
deleted file mode 100644
index e39734305a2..00000000000
--- a/src/mongo/scripting/engine_spidermonkey.cpp
+++ /dev/null
@@ -1,1872 +0,0 @@
-// engine_spidermonkey.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mongo/pch.h"
-
-#include "mongo/scripting/engine_spidermonkey.h"
-
-#include <boost/smart_ptr/scoped_array.hpp>
-#include <boost/thread/recursive_mutex.hpp>
-#ifndef _WIN32
-#include <boost/date_time/posix_time/posix_time.hpp>
-#endif
-
-#include <third_party/js-1.7/jsdate.h>
-
-#include "mongo/scripting/engine_spidermonkey_internal.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-
- /* used to make the logging not overly chatty in the mongo shell. */
- extern bool isShell;
-
-namespace spidermonkey {
-
- string trim( string s ) {
- while ( s.size() && isspace( s[0] ) )
- s = s.substr( 1 );
-
- while ( s.size() && isspace( s[s.size()-1] ) )
- s = s.substr( 0 , s.size() - 1 );
-
- return s;
- }
-
- boost::thread_specific_ptr<SMScope> currentScope( dontDeleteScope );
- boost::recursive_mutex &smmutex = *( new boost::recursive_mutex );
-
- class BSONFieldIterator {
- public:
-
- BSONFieldIterator( BSONHolder * holder ) {
-
- set<string> added;
-
- BSONObjIterator it( holder->_obj );
- while ( it.more() ) {
- BSONElement e = it.next();
- if ( holder->_removed.count( e.fieldName() ) )
- continue;
- _names.push_back( e.fieldName() );
- added.insert( e.fieldName() );
- }
-
- for ( list<string>::iterator i = holder->_extra.begin(); i != holder->_extra.end(); i++ ) {
- if ( ! added.count( *i ) )
- _names.push_back( *i );
- }
-
- _it = _names.begin();
- }
-
- bool more() {
- return _it != _names.end();
- }
-
- string next() {
- string s = *_it;
- _it++;
- return s;
- }
-
- private:
- list<string> _names;
- list<string>::iterator _it;
- };
-
- BSONFieldIterator * BSONHolder::it() {
- return new BSONFieldIterator( this );
- }
-
- Convertor::Convertor( JSContext * cx ) {
- _context = cx;
- }
-
- string Convertor::toString( JSString* jsString ) {
- size_t srclen = JS_GetStringLength( jsString );
- if( srclen == 0 )
- return "";
-
- size_t len = (srclen * 6) + 1;
- boost::scoped_array<char> utf8Chars( new char[len] );
- jschar* utf16Chars = JS_GetStringChars( jsString );
- if ( !JS_EncodeCharacters( _context, utf16Chars, srclen, utf8Chars.get(), &len ) ) {
- uasserted( 16268, "error converting UTF-16 string to UTF-8" );
- }
- return string( utf8Chars.get(), len );
- }
-
- string Convertor::toString( jsval v ) {
- return toString( JS_ValueToString( _context , v ) );
- }
-
- long long Convertor::toNumberLongUnsafe( JSObject *o ) {
- boost::uint64_t val;
- if ( hasProperty( o, "top" ) ) {
- val =
- ( (boost::uint64_t)(boost::uint32_t)getNumber( o , "top" ) << 32 ) +
- ( boost::uint32_t)( getNumber( o , "bottom" ) );
- }
- else {
- val = (boost::uint64_t)(boost::int64_t) getNumber( o, "floatApprox" );
- }
- return val;
- }
-
- int Convertor::toNumberInt( JSObject *o ) {
- return (boost::uint32_t)(boost::int32_t) getNumber( o, "floatApprox" );
- }
-
- double Convertor::toNumber( jsval v ) {
- double d;
- uassert( 10214 , "not a number" , JS_ValueToNumber( _context , v , &d ) );
- return d;
- }
-
- bool Convertor::toBoolean( jsval v ) {
- JSBool b;
- verify( JS_ValueToBoolean( _context, v , &b ) );
- return b;
- }
-
- OID Convertor::toOID( jsval v ) {
- JSContext * cx = _context;
- verify( JSVAL_IS_OID( v ) );
-
- JSObject * o = JSVAL_TO_OBJECT( v );
- OID oid;
- oid.init( getString( o , "str" ) );
- return oid;
- }
-
- BSONObj Convertor::toObject( JSObject * o , const TraverseStack& stack ) {
- if ( ! o )
- return BSONObj();
-
- if ( JS_InstanceOf( _context , o , &bson_ro_class , 0 ) ) {
- BSONHolder * holder = GETHOLDER( _context , o );
- verify( holder );
- return holder->_obj.getOwned();
- }
-
- BSONObj orig;
- if ( JS_InstanceOf( _context , o , &bson_class , 0 ) ) {
- BSONHolder * holder = GETHOLDER(_context,o);
- verify( holder );
- if ( ! holder->_modified ) {
- return holder->_obj;
- }
- orig = holder->_obj;
- }
-
- BSONObjBuilder b;
-
- if ( ! appendSpecialDBObject( this , b , "value" , OBJECT_TO_JSVAL( o ) , o ) ) {
-
- if ( stack.isTop() ) {
- jsval theid = getProperty( o , "_id" );
- if ( ! JSVAL_IS_VOID( theid ) ) {
- append( b , "_id" , theid , EOO , stack.dive( o ) );
- }
- }
-
- JSIdArray * properties = JS_Enumerate( _context , o );
- verify( properties );
-
- try {
- for ( jsint i=0; i<properties->length; i++ ) {
- jsid id = properties->vector[i];
- jsval nameval;
- verify( JS_IdToValue( _context, id, &nameval ) );
- string name = toString( nameval );
- if ( stack.isTop() && name == "_id" )
- continue;
-
- append( b,
- name,
- getProperty( o, name.c_str() ),
- orig[name].type(),
- stack.dive( o ) );
- }
- }
- catch ( const AssertionException& ) {
- JS_DestroyIdArray( _context , properties );
- throw;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16269 );
- }
- JS_DestroyIdArray( _context , properties );
- }
-
- return b.obj();
- }
-
- BSONObj Convertor::toObject( jsval v ) {
- if ( JSVAL_IS_NULL( v ) || JSVAL_IS_VOID( v ) )
- return BSONObj();
-
- uassert( 10215, "not an object", JSVAL_IS_OBJECT( v ) );
- return toObject( JSVAL_TO_OBJECT( v ) );
- }
-
- string Convertor::getFunctionCode( JSFunction * func ) {
- return toString( JS_DecompileFunction( _context , func , 0 ) );
- }
-
- string Convertor::getFunctionCode( jsval v ) {
- uassert( 10216, "not a function", JS_TypeOfValue( _context, v ) == JSTYPE_FUNCTION );
- return getFunctionCode( JS_ValueToFunction( _context , v ) );
- }
-
- void Convertor::appendRegex( BSONObjBuilder& b , const string& name , string s ) {
- verify( s[0] == '/' );
- s = s.substr(1);
- string::size_type end = s.rfind( '/' );
- b.appendRegex( name , s.substr( 0 , end ) , s.substr( end + 1 ) );
- }
-
- void Convertor::append( BSONObjBuilder& b,
- const std::string& name,
- jsval val,
- BSONType oldType,
- const TraverseStack& stack ) {
- //cout << "name: " << name << "\t" << typeString( val ) << " oldType: " << oldType << endl;
- switch ( JS_TypeOfValue( _context , val ) ) {
-
- case JSTYPE_VOID: b.appendUndefined( name ); break;
- case JSTYPE_NULL: b.appendNull( name ); break;
-
- case JSTYPE_NUMBER: {
- double d = toNumber( val );
- if ( oldType == NumberInt && ((int)d) == d )
- b.append( name , (int)d );
- else
- b.append( name , d );
- break;
- }
- case JSTYPE_STRING: b.append( name , toString( val ) ); break;
- case JSTYPE_BOOLEAN: b.appendBool( name , toBoolean( val ) ); break;
-
- case JSTYPE_OBJECT: {
- JSObject * o = JSVAL_TO_OBJECT( val );
- if ( ! o || o == JSVAL_NULL ) {
- b.appendNull( name );
- }
- else if ( ! appendSpecialDBObject( this , b , name , val , o ) ) {
- BSONObj sub = toObject( o , stack );
- if ( JS_IsArrayObject( _context , o ) ) {
- b.appendArray( name , sub );
- }
- else {
- b.append( name , sub );
- }
- }
- break;
- }
-
- case JSTYPE_FUNCTION: {
- string s = toString(val);
- if ( s[0] == '/' ) {
- appendRegex( b , name , s );
- }
- else {
- b.appendCode( name , getFunctionCode( val ) );
- }
- break;
- }
-
- default: uassert( 10217 , (string)"can't append field. name:" + name + " type: " + typeString( val ) , 0 );
- }
- }
-
- bool Convertor::hasFunctionIdentifier( const string& code ) {
- if ( code.size() < 9 || code.find( "function" ) != 0 )
- return false;
-
- return code[8] == ' ' || code[8] == '(';
- }
-
- bool Convertor::isSimpleStatement( const string& code ) {
- if ( hasJSReturn( code ) )
- return false;
-
- if ( code.find( ';' ) != string::npos &&
- code.find( ';' ) != code.rfind( ';' ) )
- return false;
-
- if ( code.find( '\n') != string::npos )
- 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;
- }
-
- JSFunction * Convertor::compileFunction( const char * code, JSObject * assoc ) {
- const char * gcName = "unknown";
- JSFunction * f = _compileFunction( code , assoc , gcName );
- return f;
- }
-
- JSFunction * Convertor::_compileFunction(
- const char * raw , JSObject * assoc , const char *& gcName ) {
-
- if ( ! assoc )
- assoc = JS_GetGlobalObject( _context );
-
- raw = jsSkipWhiteSpace( raw );
-
- //cout << "RAW\n---\n" << raw << "\n---" << endl;
-
- static int fnum = 1;
- stringstream fname;
- fname << "__cf__" << fnum++ << "__";
-
- if ( ! hasFunctionIdentifier( raw ) ) {
- string s = raw;
- if ( isSimpleStatement( s ) ) {
- s = "return " + s;
- }
- gcName = "cf anon";
- fname << "anon";
- return JS_CompileFunction( _context , assoc , fname.str().c_str() , 0 , 0 , s.c_str() , s.size() , "nofile_a" , 0 );
- }
-
- string code = raw;
-
- size_t start = code.find( '(' );
- verify( start != string::npos );
-
- string fbase;
- if ( start > 9 ) {
- fbase = trim( code.substr( 9 , start - 9 ) );
- }
- if ( fbase.length() == 0 ) {
- fbase = "anonymous_function";
- }
- fname << "f__" << fbase;
-
- code = code.substr( start + 1 );
- size_t end = code.find( ')' );
- verify( end != string::npos );
-
- string paramString = trim( code.substr( 0 , end ) );
- code = code.substr( end + 1 );
-
- vector<string> params;
- while ( paramString.size() ) {
- size_t c = paramString.find( ',' );
- if ( c == string::npos ) {
- params.push_back( paramString );
- break;
- }
- params.push_back( trim( paramString.substr( 0 , c ) ) );
- paramString = trim( paramString.substr( c + 1 ) );
- paramString = trim( paramString );
- }
-
- boost::scoped_array<const char *> paramArray (new const char*[params.size()]);
- for ( size_t i=0; i<params.size(); i++ )
- paramArray[i] = params[i].c_str();
-
- // avoid munging previously munged name (kludge; switching to v8 fixes underlying issue)
- if ( fbase.find("__cf__") != 0 && fbase.find("__f__") == string::npos ) {
- fbase = fname.str();
- }
-
- JSFunction * func = JS_CompileFunction( _context , assoc , fbase.c_str() , params.size() , paramArray.get() , code.c_str() , code.size() , "nofile_b" , 0 );
-
- if ( ! func ) {
- log() << "compile failed for: " << raw << endl;
- return 0;
- }
- gcName = "cf normal";
- return func;
- }
-
- jsval Convertor::toval( double d ) {
- jsval val;
- verify( JS_NewNumberValue( _context, d , &val ) );
- return val;
- }
-
- jsval Convertor::toval( const char * c ) {
- JSString * s = JS_NewStringCopyZ( _context , c );
- if ( s )
- return STRING_TO_JSVAL( s );
-
- // possibly unicode, try manual
-
- size_t len = strlen( c );
- size_t dstlen = len * 4;
- jschar * dst = (jschar*)malloc( dstlen );
-
- JSBool res = JS_DecodeBytes( _context , c , len , dst, &dstlen );
- if ( res ) {
- s = JS_NewUCStringCopyN( _context , dst , dstlen );
- }
-
- free( dst );
-
- if ( ! res ) {
- tlog() << "decode failed. probably invalid utf-8 string [" << c << "]" << endl;
- jsval v;
- if ( JS_GetPendingException( _context , &v ) )
- tlog() << "\t why: " << toString( v ) << endl;
- uassert( 16270, "conversion from string to JavaScript value failed", false );
- }
-
- CHECKJSALLOC( s );
- return STRING_TO_JSVAL( s );
- }
-
- JSObject * Convertor::toJSObject( const BSONObj * obj , bool readOnly ) {
- static string ref = "$ref";
- if ( ref == obj->firstElementFieldName() ) {
- JSObject * o = JS_NewObject( _context , &dbref_class , NULL, NULL);
- CHECKNEWOBJECT(o,_context,"toJSObject1");
- verify( JS_SetPrivate( _context , o , (void*)(new BSONHolder( obj->getOwned() ) ) ) );
- return o;
- }
- JSObject * o = JS_NewObject( _context , readOnly ? &bson_ro_class : &bson_class , NULL, NULL);
- CHECKNEWOBJECT(o,_context,"toJSObject2");
- verify( JS_SetPrivate( _context , o , (void*)(new BSONHolder( obj->getOwned() ) ) ) );
- return o;
- }
-
- jsval Convertor::toval( const BSONObj* obj , bool readOnly ) {
- JSObject * o = toJSObject( obj , readOnly );
- return OBJECT_TO_JSVAL( o );
- }
-
- void Convertor::makeLongObj( long long n, JSObject * o ) {
- boost::uint64_t val = (boost::uint64_t)n;
- CHECKNEWOBJECT(o,_context,"NumberLong1");
- double floatApprox = (double)(boost::int64_t)val;
- setProperty( o , "floatApprox" , toval( floatApprox ) );
- if ( (boost::int64_t)val != (boost::int64_t)floatApprox ) {
- // using 2 doubles here instead of a single double because certain double
- // bit patterns represent undefined values and sm might trash them
- setProperty( o , "top" , toval( (double)(boost::uint32_t)( val >> 32 ) ) );
- setProperty( o , "bottom" , toval( (double)(boost::uint32_t)( val & 0x00000000ffffffff ) ) );
- }
- }
-
- jsval Convertor::toval( long long n ) {
- JSObject * o = JS_NewObject( _context , &numberlong_class , 0 , 0 );
- makeLongObj( n, o );
- return OBJECT_TO_JSVAL( o );
- }
-
- void Convertor::makeIntObj( int n, JSObject * o ) {
- boost::uint32_t val = (boost::uint32_t)n;
- CHECKNEWOBJECT(o,_context,"NumberInt1");
- double floatApprox = (double)(boost::int32_t)val;
- setProperty( o , "floatApprox" , toval( floatApprox ) );
- }
-
- jsval Convertor::toval( int n ) {
- JSObject * o = JS_NewObject( _context , &numberint_class , 0 , 0 );
- makeIntObj( n, o );
- return OBJECT_TO_JSVAL( o );
- }
-
- jsval Convertor::toval( const BSONElement& e ) {
-
- switch( e.type() ) {
- case EOO:
- case jstNULL:
- case Undefined:
- return JSVAL_NULL;
- case NumberDouble:
- case NumberInt:
- return toval( e.number() );
-// case NumberInt:
-// return toval( e.numberInt() );
- case Symbol: // TODO: should we make a special class for this
- case String:
- return toval( e.valuestr() );
- case Bool:
- return e.boolean() ? JSVAL_TRUE : JSVAL_FALSE;
- case Object: {
- BSONObj embed = e.embeddedObject().getOwned();
- return toval( &embed );
- }
- case Array: {
-
- BSONObj embed = e.embeddedObject().getOwned();
-
- if ( embed.isEmpty() ) {
- return OBJECT_TO_JSVAL( JS_NewArrayObject( _context , 0 , 0 ) );
- }
-
- JSObject * array = JS_NewArrayObject( _context , 1 , 0 );
- CHECKJSALLOC( array );
-
- jsval myarray = OBJECT_TO_JSVAL( array );
-
- BSONObjIterator i( embed );
- while ( i.more() ){
- const BSONElement& e = i.next();
- jsval v = toval( e );
- verify( JS_SetElement( _context , array , atoi(e.fieldName()) , &v ) );
- }
-
- return myarray;
- }
- case jstOID: {
- OID oid = e.__oid();
- JSObject * o = JS_NewObject( _context , &object_id_class , 0 , 0 );
- CHECKNEWOBJECT(o,_context,"jstOID");
- 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:
- log() << "warning: unknown regex flag:" << *flags << endl;
- }
- flags++;
- }
-
- JSObject * r = JS_NewRegExpObject( _context , (char*)e.regex() , strlen( e.regex() ) , flagNumber );
- verify( r );
- return OBJECT_TO_JSVAL( r );
- }
- case Code: {
- JSFunction * func = compileFunction( e.valuestr() );
- if ( func )
- return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
- return JSVAL_NULL;
- }
- case CodeWScope: {
- JSFunction * func = compileFunction( e.codeWScopeCode() );
- if ( !func )
- return JSVAL_NULL;
-
- BSONObj extraScope = e.codeWScopeObject();
- if ( ! extraScope.isEmpty() ) {
- log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
- }
-
- return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
- }
- case Date:
- return OBJECT_TO_JSVAL( js_NewDateObjectMsec( _context , (jsdouble) ((long long)e.date().millis) ) );
-
- 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 );
- CHECKNEWOBJECT(o,_context,"Timestamp1");
- setProperty( o , "t" , toval( (double)(e.timestampTime() / 1000) ) );
- setProperty( o , "i" , toval( (double)(e.timestampInc()) ) );
- return OBJECT_TO_JSVAL( o );
- }
- case NumberLong: {
- return toval( e.numberLong() );
- }
- case DBRef: {
- JSObject * o = JS_NewObject( _context , &dbpointer_class , 0 , 0 );
- CHECKNEWOBJECT(o,_context,"DBRef1");
- setProperty( o , "ns" , toval( e.dbrefNS() ) );
-
- JSObject * oid = JS_NewObject( _context , &object_id_class , 0 , 0 );
- CHECKNEWOBJECT(oid,_context,"DBRef2");
- setProperty( oid , "str" , toval( e.dbrefOID().str().c_str() ) );
-
- setProperty( o , "id" , OBJECT_TO_JSVAL( oid ) );
- return OBJECT_TO_JSVAL( o );
- }
- case BinData: {
- JSObject * o = JS_NewObject( _context , &bindata_class , 0 , 0 );
- CHECKNEWOBJECT(o,_context,"Bindata_BinData1");
- int len;
- const char * data = e.binData( len );
- verify( data );
- verify( JS_SetPrivate( _context , o , new BinDataHolder( data , len ) ) );
-
- setProperty( o , "len" , toval( (double)len ) );
- setProperty( o , "type" , toval( (double)e.binDataType() ) );
- return OBJECT_TO_JSVAL( o );
- }
- }
-
- log() << "toval: unknown type: " << (int) e.type() << endl;
- uassert( 10218 , "not done: toval" , 0 );
- return 0;
- }
-
- JSObject * Convertor::getJSObject( JSObject * o , const char * name ) {
- jsval v;
- verify( JS_GetProperty( _context , o , name , &v ) );
- return JSVAL_TO_OBJECT( v );
- }
-
- JSObject * Convertor::getGlobalObject( const char * name ) {
- return getJSObject( JS_GetGlobalObject( _context ) , name );
- }
-
- JSObject * Convertor::getGlobalPrototype( const char * name ) {
- return getJSObject( getGlobalObject( name ) , "prototype" );
- }
-
- bool Convertor::hasProperty( JSObject * o , const char * name ) {
- JSBool res;
- verify( JS_HasProperty( _context , o , name , & res ) );
- return res;
- }
-
- jsval Convertor::getProperty( JSObject * o , const char * field ) {
- uassert( 10219 , "object passed to getPropery is null" , o );
- jsval v;
- verify( JS_GetProperty( _context , o , field , &v ) );
- return v;
- }
-
- void Convertor::setProperty( JSObject * o , const char * field , jsval v ) {
- verify( JS_SetProperty( _context , o , field , &v ) );
- }
-
- string Convertor::typeString( jsval v ) {
- JSType t = JS_TypeOfValue( _context , v );
- return JS_GetTypeName( _context , t );
- }
-
- bool Convertor::getBoolean( JSObject * o , const char * field ) {
- return toBoolean( getProperty( o , field ) );
- }
-
- double Convertor::getNumber( JSObject * o , const char * field ) {
- return toNumber( getProperty( o , field ) );
- }
-
- string Convertor::getString( JSObject * o , const char * field ) {
- return toString( getProperty( o , field ) );
- }
-
- JSClass * Convertor::getClass( JSObject * o , const char * field ) {
- jsval v;
- verify( JS_GetProperty( _context , o , field , &v ) );
- if ( ! JSVAL_IS_OBJECT( v ) )
- return 0;
- return JS_GET_CLASS( _context , JSVAL_TO_OBJECT( v ) );
- }
-
- void bson_finalize( JSContext * cx , JSObject * obj ) {
- try {
- BSONHolder * o = GETHOLDER( cx , obj );
- if ( o ) {
- delete o;
- verify( JS_SetPrivate( cx , obj , 0 ) );
- }
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) JS_ReportError( cx, e.what() );
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16271 );
- }
- }
-
- JSBool bson_enumerate( JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp ) {
- try {
- BSONHolder * o = GETHOLDER( cx , obj );
-
- if ( enum_op == JSENUMERATE_INIT ) {
- if ( o ) {
- BSONFieldIterator * it = o->it();
- *statep = PRIVATE_TO_JSVAL( it );
- }
- else {
- *statep = 0;
- }
- if ( idp )
- *idp = JSVAL_ZERO;
- return JS_TRUE;
- }
-
- BSONFieldIterator * it = (BSONFieldIterator*)JSVAL_TO_PRIVATE( *statep );
- if ( ! it ) {
- *statep = 0;
- return JS_TRUE;
- }
-
- if ( enum_op == JSENUMERATE_NEXT ) {
- if ( it->more() ) {
- string name = it->next();
- Convertor c(cx);
- verify( JS_ValueToId( cx , c.toval( name.c_str() ) , idp ) );
- }
- else {
- delete it;
- *statep = 0;
- }
- return JS_TRUE;
- }
-
- if ( enum_op == JSENUMERATE_DESTROY ) {
- if ( it )
- delete it;
- return JS_TRUE;
- }
-
- uassert( 10220 , "don't know what to do with this op" , 0 );
- return JS_FALSE;
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16272 );
- }
- }
-
- JSBool noaccess( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
- try {
- BSONHolder * holder = GETHOLDER( cx , obj );
- if ( ! holder ) {
- // in init code still
- return JS_TRUE;
- }
- if ( holder->_inResolve )
- return JS_TRUE;
- JS_ReportError( cx , "doing write op on read only operation" );
- return JS_FALSE;
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16273 );
- }
- }
-
- JSClass bson_ro_class = {
- "bson_ro_object", // class name
- JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE, // flags
- noaccess, // addProperty
- noaccess, // delProperty
- JS_PropertyStub, // getProperty
- noaccess, // setProperty
- (JSEnumerateOp)bson_enumerate, // enumerate
- (JSResolveOp)resolveBSONField, // resolve
- JS_ConvertStub, // convert
- bson_finalize, // finalize
- JSCLASS_NO_OPTIONAL_MEMBERS // optional members
- };
-
- JSBool bson_add_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
- try {
- BSONHolder * holder = GETHOLDER( cx , obj );
- if ( ! holder ) {
- // static init
- return JS_TRUE;
- }
- if ( ! holder->_inResolve ) {
- Convertor c(cx);
- string name( c.toString( idval ) );
- if ( holder->_obj[name].eoo() ) {
- holder->_extra.push_back( name );
- }
- holder->_modified = true;
- }
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16274 );
- }
- return JS_TRUE;
- }
-
-
- JSBool mark_modified( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
- try {
- Convertor c(cx);
- BSONHolder * holder = GETHOLDER( cx , obj );
- if ( !holder ) // needed when we're messing with DBRef.prototype
- return JS_TRUE;
- if ( holder->_inResolve )
- return JS_TRUE;
- holder->_modified = true;
- holder->_removed.erase( c.toString( idval ) );
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16275 );
- }
- return JS_TRUE;
- }
-
- JSBool mark_modified_remove( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) {
- try {
- Convertor c(cx);
- BSONHolder * holder = GETHOLDER( cx , obj );
- if ( holder->_inResolve )
- return JS_TRUE;
- holder->_modified = true;
- holder->_removed.insert( c.toString( idval ) );
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16276 );
- }
- return JS_TRUE;
- }
-
- JSClass bson_class = {
- "bson_object", // class name
- JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE, // flags
- bson_add_prop, // addProperty
- mark_modified_remove, // delProperty
- JS_PropertyStub, // getProperty
- mark_modified, // setProperty
- (JSEnumerateOp)bson_enumerate, // enumerate
- (JSResolveOp)resolveBSONField, // resolve
- JS_ConvertStub, // convert
- bson_finalize, // finalize
- JSCLASS_NO_OPTIONAL_MEMBERS // optional members
- };
-
- static JSClass global_class = {
- "global", // class name
- JSCLASS_GLOBAL_FLAGS, // flags
- JS_PropertyStub, // addProperty
- JS_PropertyStub, // delProperty
- JS_PropertyStub, // getProperty
- JS_PropertyStub, // setProperty
- JS_EnumerateStub, // enumerate
- JS_ResolveStub, // resolve
- JS_ConvertStub, // convert
- JS_FinalizeStub, // finalize
- JSCLASS_NO_OPTIONAL_MEMBERS // optional members
- };
-
- // --- global helpers ---
-
- static void hexToBinData( JSContext* cx,
- Convertor* c,
- jsval* rval,
- int subtype,
- const string& s ) {
- JSObject * o = JS_NewObject( cx , &bindata_class , 0 , 0 );
- CHECKNEWOBJECT(o,_context,"Bindata_BinData1");
- size_t s_size = s.size();
- int len = s_size / 2;
- boost::scoped_array<char> data( new char[len] );
- char* p = data.get();
- const char *src = s.c_str();
- for( size_t i = 0; i+1 < s_size; i += 2 ) {
- *p++ = fromHex( src + i );
- }
- verify( JS_SetPrivate( cx, o, new BinDataHolder( data.get(), len ) ) );
- c->setProperty( o, "len", c->toval( static_cast<double>(len) ) );
- c->setProperty( o, "type", c->toval( static_cast<double>(subtype) ) );
- *rval = OBJECT_TO_JSVAL( o );
- }
-
- static bool testHexString( JSContext* cx, const string& hexString ) {
- size_t len = hexString.length();
- for ( size_t i = 0; i < len; ++i ) {
- char ch = hexString[i];
- if ( (ch>='0' && ch<='9') || (ch>='a' && ch<='f') || (ch>='A' && ch<='F') ) {
- continue;
- }
- JS_ReportError( cx, "invalid hexstring character '%c'", ch );
- return false;
- }
- return true;
- }
-
- JSBool _HexData( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) {
- try {
- if ( argc != 2 ) {
- JS_ReportError( cx , "HexData needs 2 arguments -- HexData(subtype,hexstring)" );
- return JS_FALSE;
- }
- Convertor c( cx );
- int subtype = static_cast<int>( c.toNumber( argv[ 0 ] ) );
- if ( subtype == 2 ) {
- JS_ReportError( cx , "BinData subtype 2 is deprecated" );
- return JS_FALSE;
- }
- else if ( subtype < 0 || subtype > 255 ) {
- JS_ReportError( cx, "subtype must be between 0 and 255" );
- return JS_FALSE;
- }
- string s( c.toString( argv[1] ) );
- if ( ! testHexString( cx, s ) ) {
- return JS_FALSE;
- }
- size_t len = s.length();
- if ( 0 != ( len % 2 ) ) {
- JS_ReportError( cx, "hexstring must be even length" );
- return JS_FALSE;
- }
- hexToBinData(cx, &c, rval, subtype, s);
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16277 );
- }
- return JS_TRUE;
- }
-
- JSBool _UUID( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) {
- try {
- if ( argc != 1 ) {
- JS_ReportError( cx , "UUID needs argument -- UUID(hexstring)" );
- return JS_FALSE;
- }
- Convertor c( cx );
- string s( c.toString( argv[0] ) );
- if ( ! testHexString( cx, s ) ) {
- return JS_FALSE;
- }
- size_t len = s.length();
- if ( len != 32 ) {
- JS_ReportError( cx, "UUID hexstring length is %d, must be 32", len );
- return JS_FALSE;
- }
- hexToBinData(cx, &c, rval, 3, s);
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16278 );
- }
- return JS_TRUE;
- }
-
- JSBool _MD5( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) {
- try {
- if ( argc != 1 ) {
- JS_ReportError( cx , "MD5 needs argument -- MD5(hexstring)" );
- return JS_FALSE;
- }
- Convertor c( cx );
- string s( c.toString( argv[0] ) );
- if ( ! testHexString( cx, s ) ) {
- return JS_FALSE;
- }
- size_t len = s.length();
- if ( len != 32 ) {
- JS_ReportError( cx, "MD5 hexstring length is %d, must be 32", len );
- return JS_FALSE;
- }
- hexToBinData(cx, &c, rval, 5, s);
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16279 );
- }
- return JS_TRUE;
- }
-
- JSBool native_print( JSContext * cx, JSObject * obj, uintN argc, jsval *argv, jsval *rval ) {
- stringstream ss;
- bool someWritten = false;
- try {
- Convertor c( cx );
- for ( uintN i=0; i<argc; i++ ) {
- if ( i > 0 )
- ss << " ";
- ss << c.toString( argv[i] );
- someWritten = true;
- }
- ss << "\n";
- Logstream::logLockless( ss.str() );
- }
- catch ( const AssertionException& ) {
- if ( someWritten ) {
- ss << "\n";
- Logstream::logLockless( ss.str() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16280 );
- }
- return JS_TRUE;
- }
-
- JSBool native_helper( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ) {
- try {
- Convertor c(cx);
-
- // get function pointer from JS caller's argument property 'x'
- massert(16740, "nativeHelper argument requires object with 'x' property",
- c.hasProperty(obj, "x"));
- FunctionMap::iterator funcIter = currentScope->_functionMap.find(c.getNumber(obj, "x"));
- massert(16742, "JavaScript function not in map",
- funcIter != currentScope->_functionMap.end());
- NativeFunction func = funcIter->second;
- verify(func);
-
- // get data pointer from JS caller's argument property 'y'
- void* data = NULL;
- if (c.hasProperty(obj, "y")) {
- ArgumentMap::iterator argIter =
- currentScope->_argumentMap.find(c.getNumber(obj, "y"));
- massert(16741, "nativeHelper 'y' parameter must be in the argumentMap",
- argIter != currentScope->_argumentMap.end());
- data = argIter->second;
- }
-
- BSONObj a;
- if ( argc > 0 ) {
- BSONObjBuilder args;
- for ( uintN i = 0; i < argc; ++i ) {
- c.append( args , args.numStr( i ) , argv[i] );
- }
- a = args.obj();
- }
-
- BSONObj out;
- try {
- out = func( a, data );
- }
- catch ( std::exception& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
-
- if ( out.isEmpty() ) {
- *rval = JSVAL_VOID;
- }
- else {
- *rval = c.toval( out.firstElement() );
- }
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16281 );
- }
- 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;
- }
-
- 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 } ,
- { "UUID", &_UUID, 0, 0, 0 } ,
- { "MD5", &_MD5, 0, 0, 0 } ,
- { "HexData", &_HexData, 0, 0, 0 } ,
- { 0 , 0 , 0 , 0 , 0 }
- };
-
- // ----END global helpers ----
-
- // Object helpers
-
- JSBool bson_get_size(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- try {
- if ( argc != 1 || !JSVAL_IS_OBJECT( argv[ 0 ] ) ) {
- JS_ReportError( cx , "bsonsize requires one valid object" );
- return JS_FALSE;
- }
- Convertor c(cx);
- if ( argv[0] == JSVAL_VOID || argv[0] == JSVAL_NULL ) {
- *rval = c.toval( 0.0 );
- return JS_TRUE;
- }
-
- JSObject * o = JSVAL_TO_OBJECT( argv[0] );
- double size = 0.0;
-
- if ( JS_InstanceOf( cx , o , &bson_ro_class , 0 ) ||
- JS_InstanceOf( cx , o , &bson_class , 0 ) ) {
- BSONHolder * h = GETHOLDER( cx , o );
- if ( h ) {
- size = h->_obj.objsize();
- }
- }
- else {
- BSONObj temp = c.toObject( o );
- size = temp.objsize();
- }
- *rval = c.toval( size );
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16282 );
- }
- return JS_TRUE;
- }
-
- JSFunctionSpec objectHelpers[] = {
- { "bsonsize" , &bson_get_size , 1 , 0 , 0 } ,
- { 0 , 0 , 0 , 0 , 0 }
- };
-
- // end Object helpers
-
- JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
- try {
- verify( JS_EnterLocalRootScope( cx ) );
- }
- catch ( const AssertionException& e ) {
- JS_ReportError( cx, e.what() );
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16283 );
- }
-
- try {
- BSONHolder * holder = GETHOLDER( cx , obj );
- if ( ! holder ) {
- // static init
- *objp = 0;
- JS_LeaveLocalRootScope( cx );
- return JS_TRUE;
- }
- holder->check();
-
- Convertor c( cx );
- string s( c.toString( id ) );
- BSONElement e = holder->_obj[ s.c_str() ];
- if ( e.type() == EOO || holder->_removed.count( s ) ) {
- *objp = 0;
- JS_LeaveLocalRootScope( cx );
- return JS_TRUE;
- }
-
- jsval val = c.toval( e );
-
- verify( ! holder->_inResolve );
- holder->_inResolve = true;
- verify( JS_SetProperty( cx , obj , s.c_str() , &val ) );
- holder->_inResolve = false;
-
- if ( val != JSVAL_NULL && val != JSVAL_VOID && JSVAL_IS_OBJECT( val ) ) {
- // TODO: this is a hack to get around sub objects being modified
- // basically right now whenever a sub object is read we mark whole obj as possibly modified
- JSObject * oo = JSVAL_TO_OBJECT( val );
- if ( JS_InstanceOf( cx , oo , &bson_class , 0 ) ||
- JS_IsArrayObject( cx , oo ) ) {
- holder->_modified = true;
- }
- }
-
- *objp = obj;
- JS_LeaveLocalRootScope( cx );
- }
- catch ( const AssertionException& e ) {
- JS_LeaveLocalRootScope( cx );
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16284 );
- }
- return JS_TRUE;
- }
-
-
- class SMScope;
-
- class SMEngine : public ScriptEngine {
- public:
-
- SMEngine() {
-#ifdef SM18
- JS_SetCStringsAreUTF8();
-#endif
-
- _runtime = JS_NewRuntime(64L * 1024L * 1024L);
- uassert( 10221 , "JS_NewRuntime failed" , _runtime );
-
- if ( ! utf8Ok() ) {
- log() << "*** warning: spider monkey build without utf8 support. consider rebuilding with utf8 support" << endl;
- }
- }
-
- ~SMEngine() {
- JS_DestroyRuntime( _runtime );
- JS_ShutDown();
- }
-
- Scope * createScope();
-
- virtual Scope* newScope() {
- Scope* s = createScope();
- if (!s) return NULL;
- if (_scopeInitCallback)
- _scopeInitCallback(*s);
- installGlobalUtils(*s);
- return s;
- }
-
- void runTest();
-
- virtual bool utf8Ok() const { return JS_CStringsAreUTF8(); }
-
-#ifdef XULRUNNER
- JSClass * _dateClass;
- JSClass * _regexClass;
-#endif
-
-
- private:
- JSRuntime * _runtime;
- friend class SMScope;
- };
-
- SMEngine * globalSMEngine;
-
-
- // ------ scope ------
-
-
- JSBool no_gc(JSContext *cx, JSGCStatus status) {
- return JS_FALSE;
- }
-
- JSBool yes_gc(JSContext *cx, JSGCStatus status) {
- return JS_TRUE;
- }
-
- SMScope::SMScope() :
- _this( 0 ),
- _reportError( true ),
- _externalSetup( false ),
- _localConnect( false ) {
- smlock;
- _context = JS_NewContext( globalSMEngine->_runtime , 8192 );
- _convertor = new Convertor( _context );
- massert( 10431 , "JS_NewContext failed" , _context );
-
- JS_SetOptions( _context , JSOPTION_VAROBJFIX);
- //JS_SetVersion( _context , JSVERSION_LATEST); TODO
- JS_SetErrorReporter( _context , errorReporter );
-
- _global = JS_NewObject( _context , &global_class, NULL, NULL);
- massert( 10432 , "JS_NewObject failed for global" , _global );
- JS_SetGlobalObject( _context , _global );
- massert( 10433 , "js init failed" , JS_InitStandardClasses( _context , _global ) );
-
- JS_SetOptions( _context , JS_GetOptions( _context ) | JSOPTION_VAROBJFIX );
-
- JS_DefineFunctions( _context , _global , globalHelpers );
-
- JS_DefineFunctions( _context , _convertor->getGlobalObject( "Object" ), objectHelpers );
-
- //JS_SetGCCallback( _context , no_gc ); // this is useful for seeing if something is a gc problem
-
- _postCreateHacks();
- }
-
- SMScope::~SMScope() {
- smlock;
- uassert( 10223 , "deleted SMScope twice?" , _convertor );
-
- if ( _this ) {
- JS_RemoveRoot( _context , &_this );
- _this = 0;
- }
-
- if ( _convertor ) {
- delete _convertor;
- _convertor = 0;
- }
-
- if ( _context ) {
- // This is expected to reclaim _global as well.
- JS_DestroyContext( _context );
- _context = 0;
- }
-
- }
-
- void SMScope::reset() {
- smlock;
- verify( _convertor );
- }
-
- void SMScope::init( const BSONObj * data ) {
- smlock;
- if ( ! data )
- return;
-
- BSONObjIterator i( *data );
- while ( i.more() ) {
- BSONElement e = i.next();
- _convertor->setProperty( _global , e.fieldName() , _convertor->toval( e ) );
- _initFieldNames.insert( e.fieldName() );
- }
-
- }
-
- bool SMScope::hasOutOfMemoryException() {
- string err = getError();
- return err.find("out of memory") != string::npos;
- }
-
- void SMScope::externalSetup() {
- smlock;
- uassert( 10224 , "already local connected" , ! _localConnect );
- if ( _externalSetup )
- return;
- initMongoJS( this , _context , _global , false );
- _externalSetup = true;
- }
-
- void SMScope::localConnect( const char * dbName ) {
- {
- smlock;
- uassert( 10225 , "already setup for external db" , ! _externalSetup );
- if ( _localConnect ) {
- uassert( 10226 , "connected to different db" , _localDBName == dbName );
- return;
- }
-
- initMongoJS( this , _context , _global , true );
-
- exec( "_mongo = new Mongo();" );
- exec( ((string)"db = _mongo.getDB( \"" + dbName + "\" ); ").c_str() );
-
- _localConnect = true;
- _localDBName = dbName;
- }
- loadStored();
- }
-
- double SMScope::getNumber( const char *field ) {
- smlock;
- jsval val;
- verify( JS_GetProperty( _context , _global , field , &val ) );
- return _convertor->toNumber( val );
- }
-
- string SMScope::getString( const char *field ) {
- smlock;
- jsval val;
- verify( JS_GetProperty( _context , _global , field , &val ) );
- JSString * s = JS_ValueToString( _context , val );
- return _convertor->toString( s );
- }
-
- bool SMScope::getBoolean( const char *field ) {
- smlock;
- return _convertor->getBoolean( _global , field );
- }
-
- BSONObj SMScope::getObject( const char *field ) {
- smlock;
- return _convertor->toObject( _convertor->getProperty( _global , field ) );
- }
-
- JSObject * SMScope::getJSObject( const char * field ) {
- smlock;
- return _convertor->getJSObject( _global , field );
- }
-
- bool SMScope::isKillPending() const { return globalSMEngine->interrupted(); }
-
- int SMScope::type( const char *field ) {
- smlock;
- jsval val;
- verify( JS_GetProperty( _context , _global , field , &val ) );
-
- switch ( JS_TypeOfValue( _context , val ) ) {
- case JSTYPE_VOID: return Undefined;
- case JSTYPE_NULL: return jstNULL;
- case JSTYPE_OBJECT: {
- if ( val == JSVAL_NULL )
- return jstNULL;
- JSObject * o = JSVAL_TO_OBJECT( val );
- if ( JS_IsArrayObject( _context , o ) )
- return Array;
- if ( isDate( _context , o ) )
- return Date;
- return Object;
- }
- case JSTYPE_FUNCTION: return Code;
- case JSTYPE_STRING: return String;
- case JSTYPE_NUMBER: return NumberDouble;
- case JSTYPE_BOOLEAN: return Bool;
- default:
- uassert( 10227 , "unknown type" , 0 );
- }
- return 0;
- }
-
- void SMScope::setElement( const char *field , const BSONElement& val ) {
- smlock;
- jsval v = _convertor->toval( val );
- verify( JS_SetProperty( _context , _global , field , &v ) );
- }
-
- void SMScope::setNumber( const char *field , double val ) {
- smlock;
- jsval v = _convertor->toval( val );
- verify( JS_SetProperty( _context , _global , field , &v ) );
- }
-
- void SMScope::setString( const char *field , const char * val ) {
- smlock;
- jsval v = _convertor->toval( val );
- verify( JS_SetProperty( _context , _global , field , &v ) );
- }
-
- void SMScope::setObject( const char *field , const BSONObj& obj , bool readOnly ) {
- smlock;
- jsval v = _convertor->toval( &obj , readOnly );
- JS_SetProperty( _context , _global , field , &v );
- }
-
- void SMScope::setBoolean( const char *field , bool val ) {
- smlock;
- jsval v = BOOLEAN_TO_JSVAL( val );
- verify( JS_SetProperty( _context , _global , field , &v ) );
- }
-
- void SMScope::setThis( const BSONObj * obj ) {
- smlock;
- if ( _this ) {
- JS_RemoveRoot( _context , &_this );
- _this = 0;
- }
-
- if ( obj ) {
- _this = _convertor->toJSObject( obj );
- JS_AddNamedRoot( _context , &_this , "scope this" );
- }
- }
-
- void SMScope::setFunction( const char *field , const char * code ) {
- smlock;
- jsval v = OBJECT_TO_JSVAL(JS_GetFunctionObject(_convertor->compileFunction(code)));
- JS_SetProperty( _context , _global , field , &v );
- }
-
- void SMScope::rename( const char * from , const char * to ) {
- smlock;
- jsval v;
- verify( JS_GetProperty( _context , _global , from , &v ) );
- verify( JS_SetProperty( _context , _global , to , &v ) );
- v = JSVAL_VOID;
- verify( JS_SetProperty( _context , _global , from , &v ) );
- }
-
- ScriptingFunction SMScope::_createFunction(const char* code, ScriptingFunction functionNumber) {
- smlock;
- precall();
- return (ScriptingFunction)_convertor->compileFunction( code );
- }
-
- // should not generate exceptions, as those can be caught in
- // javascript code; returning false without an exception exits
- // immediately
- JSBool SMScope::_interrupt( JSContext *cx ) {
- TimeoutSpec &spec = *(TimeoutSpec *)( JS_GetContextPrivate( cx ) );
- if ( ++spec.count % 1000 != 0 )
- return JS_TRUE;
- const char * interrupt = ScriptEngine::checkInterrupt();
- if ( interrupt && interrupt[ 0 ] ) {
- return JS_FALSE;
- }
- if ( spec.timeout.ticks() == 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;
- }
- return JS_FALSE;
-
- }
-
- void SMScope::installInterrupt( int timeoutMs ) {
- if ( timeoutMs != 0 || ScriptEngine::haveCheckInterruptCallback() ) {
- 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 );
-#if defined(SM181) && !defined(XULRUNNER190)
- JS_SetOperationCallback( _context, _interrupt );
-#else
- JS_SetBranchCallback( _context, interrupt );
-#endif
- }
- }
-
- void SMScope::uninstallInterrupt( int timeoutMs ) {
- if ( timeoutMs != 0 || ScriptEngine::haveCheckInterruptCallback() ) {
-#if defined(SM181) && !defined(XULRUNNER190)
- JS_SetOperationCallback( _context , 0 );
-#else
- JS_SetBranchCallback( _context, 0 );
-#endif
- delete (TimeoutSpec *)JS_GetContextPrivate( _context );
- JS_SetContextPrivate( _context, 0 );
- }
- }
-
- void SMScope::precall() {
- _error = "";
- currentScope.reset( this );
- }
-
- bool SMScope::exec( const StringData& code,
- const string& name,
- bool printResult,
- bool reportError,
- bool assertOnError,
- int timeoutMs) {
- smlock;
- precall();
-
- jsval ret = JSVAL_VOID;
-
- installInterrupt( timeoutMs );
- _reportError = reportError;
- JSBool worked = JS_EvaluateScript( _context,
- _global,
- code.rawData(),
- code.size(),
- name.c_str(),
- 1,
- &ret );
- _reportError = true;
- uninstallInterrupt( timeoutMs );
-
- if ( ! worked && _error.size() == 0 ) {
- jsval v;
- if ( JS_GetPendingException( _context , &v ) ) {
- _error = _convertor->toString( v );
- if ( reportError )
- cout << _error << endl;
- }
- }
-
- uassert( 10228, mongoutils::str::stream() << name + " exec failed: " << _error,
- worked || ! assertOnError );
-
- if ( worked )
- _convertor->setProperty( _global , "__lastres__" , ret );
-
- if ( worked && printResult && ! JSVAL_IS_VOID( ret ) )
- cout << _convertor->toString( ret ) << endl;
-
- return worked;
- }
-
- int SMScope::invoke( JSFunction* func,
- const BSONObj* args,
- const BSONObj* recv,
- int timeoutMs,
- bool ignoreReturn,
- bool readOnlyArgs,
- bool readOnlyRecv ) {
- smlock;
- precall();
-
- try {
- verify( JS_EnterLocalRootScope( _context ) );
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( _context ) ) {
- JS_ReportError( _context, e.what() );
- }
- return 0;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16285 );
- }
-
- int nargs = args ? args->nFields() : 0;
- scoped_array<jsval> smargsPtr( new jsval[nargs] );
- try {
- if ( nargs ) {
- BSONObjIterator it( *args );
- for ( int i=0; i<nargs; i++ ) {
- smargsPtr[i] = _convertor->toval( it.next() );
- }
- }
-
- if ( !args ) {
- _convertor->setProperty( _global , "args" , JSVAL_NULL );
- }
- else {
- setObject( "args" , *args , true ); // this is for backwards compatibility
- }
- }
- catch ( const AssertionException& e ) {
- JS_LeaveLocalRootScope( _context );
- if ( ! JS_IsExceptionPending( _context ) ) {
- JS_ReportError( _context, e.what() );
- }
- return 0;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16286 );
- }
- JS_LeaveLocalRootScope( _context );
-
- installInterrupt( timeoutMs );
- jsval rval;
- setThis(recv);
- JSBool ret = JS_CallFunction( _context,
- _this ? _this : _global,
- func,
- nargs,
- smargsPtr.get(),
- &rval );
- setThis(0);
- uninstallInterrupt( timeoutMs );
-
- if ( !ret ) {
- return -3;
- }
-
- if ( ! ignoreReturn ) {
- verify( JS_SetProperty( _context , _global , "__returnValue" , &rval ) );
- }
-
- return 0;
- }
-
- void SMScope::injectNative( const char *field, NativeFunction func, void* data ) {
- smlock;
- string name = field;
- jsval v;
- uint32_t funcId = _functionMap.size();
- _functionMap.insert(make_pair(funcId, func));
- v = _convertor->toval(static_cast<double>(funcId));
- _convertor->setProperty( _global, (name + "_").c_str(), v );
-
- stringstream code;
- if (data) {
- uint32_t argsId = _argumentMap.size();
- _argumentMap.insert(make_pair(argsId, data));
- v = _convertor->toval(static_cast<double>(argsId));
- _convertor->setProperty( _global, (name + "_data_").c_str(), v );
- code << field << "_" << " = { x : " << field << "_ , y: " << field << "_data_ }; ";
- } else {
- code << field << "_" << " = { x : " << field << "_ }; ";
- }
- code << field << " = function(){ return nativeHelper.apply( " <<
- field << "_ , arguments ); }";
- exec( code.str() );
- }
-
- void SMScope::gc() {
- smlock;
- JS_GC( _context );
- }
-
- void SMScope::_postCreateHacks() {
-#ifdef XULRUNNER
- exec( "__x__ = new Date(1);" );
- globalSMEngine->_dateClass = _convertor->getClass( _global , "__x__" );
- exec( "__x__ = /abc/i" );
- globalSMEngine->_regexClass = _convertor->getClass( _global , "__x__" );
-#endif
- }
-
- void errorReporter( JSContext *cx, const char *message, JSErrorReport *report ) {
- stringstream ss;
- if( !isShell )
- ss << "JS Error: ";
- ss << message;
-
- if ( report && report->filename ) {
- ss << " " << report->filename << ":" << report->lineno;
- }
-
- if ( !currentScope.get() || currentScope->isReportingErrors() ) {
- tlog() << ss.str() << endl;
- }
-
- if ( currentScope.get() ) {
- currentScope->gotError( ss.str() );
- }
- }
-
- JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ) {
- try {
- Convertor c(cx);
-
- Scope * s = currentScope.get();
-
- for ( uintN i=0; i<argc; i++ ) {
- string filename( c.toString( argv[i] ) );
- //cout << "load [" << filename << "]" << endl;
-
- if ( ! s->execFile( filename, false, true, false ) ) {
- JS_ReportError( cx, (string("error loading js file: ") + filename).c_str() );
- return JS_FALSE;
- }
- }
- }
- catch ( const AssertionException& e ) {
- if ( ! JS_IsExceptionPending( cx ) ) {
- JS_ReportError( cx, e.what() );
- }
- return JS_FALSE;
- }
- catch ( const std::exception& e ) {
- log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;
- fassertFailed( 16287 );
- }
- return JS_TRUE;
- }
-
-
-
- void SMEngine::runTest() {
- SMScope s;
-
- 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 )" );
- s.exec( "assert.eq( 'function' , typeof _mongo.find , 'mongo.find is not a function' )" );
-
- verify( (string)"abc" == trim( "abc" ) );
- verify( (string)"abc" == trim( " abc" ) );
- verify( (string)"abc" == trim( "abc " ) );
- verify( (string)"abc" == trim( " abc " ) );
-
- }
-
- Scope * SMEngine::createScope() {
- return new SMScope();
- }
-
-} // namespace spidermonkey
-
- void ScriptEngine::setup() {
- spidermonkey::globalSMEngine = new spidermonkey::SMEngine();
- globalScriptEngine = spidermonkey::globalSMEngine;
- }
-
- std::string ScriptEngine::getInterpreterVersionString() {
- return "SpiderMonkey 1.7";
- }
-
-} // namespace mongo
-