diff options
| author | Antonin Kral <a.kral@bobek.cz> | 2012-08-29 20:54:51 +0200 |
|---|---|---|
| committer | Antonin Kral <a.kral@bobek.cz> | 2012-08-29 20:54:51 +0200 |
| commit | 83957b73f9177f6e38bd5375bd93ca1f6a47188c (patch) | |
| tree | f20b7d6ac9a9c64ff5bb6b5910a24abbb356b1d5 /scripting | |
| parent | 5071d203970edd4c995493d810abe20987e76fe9 (diff) | |
Imported Upstream version 2.2.0upstream/2.2.0
Diffstat (limited to 'scripting')
| -rw-r--r-- | scripting/bench.cpp | 244 | ||||
| -rw-r--r-- | scripting/engine.cpp | 507 | ||||
| -rw-r--r-- | scripting/engine.h | 234 | ||||
| -rw-r--r-- | scripting/engine_java.cpp | 764 | ||||
| -rw-r--r-- | scripting/engine_java.h | 223 | ||||
| -rw-r--r-- | scripting/engine_none.cpp | 24 | ||||
| -rw-r--r-- | scripting/engine_spidermonkey.cpp | 1749 | ||||
| -rw-r--r-- | scripting/engine_spidermonkey.h | 105 | ||||
| -rw-r--r-- | scripting/engine_v8.cpp | 1547 | ||||
| -rw-r--r-- | scripting/engine_v8.h | 231 | ||||
| -rw-r--r-- | scripting/sm_db.cpp | 1240 | ||||
| -rw-r--r-- | scripting/utils.cpp | 77 | ||||
| -rw-r--r-- | scripting/v8_db.cpp | 1049 | ||||
| -rw-r--r-- | scripting/v8_db.h | 96 | ||||
| -rw-r--r-- | scripting/v8_utils.cpp | 291 | ||||
| -rw-r--r-- | scripting/v8_utils.h | 43 | ||||
| -rw-r--r-- | scripting/v8_wrapper.cpp | 99 | ||||
| -rw-r--r-- | scripting/v8_wrapper.h | 34 |
18 files changed, 0 insertions, 8557 deletions
diff --git a/scripting/bench.cpp b/scripting/bench.cpp deleted file mode 100644 index 9ada7d6495c..00000000000 --- a/scripting/bench.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/** @file bench.cpp */ - -/* - * Copyright (C) 2010 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 "pch.h" -#include "engine.h" -#include "../util/md5.hpp" -#include "../util/version.h" -#include "../client/dbclient.h" -#include "../client/connpool.h" -// --------------------------------- -// ---- benchmarking system -------- -// --------------------------------- - - -namespace mongo { - - - struct BenchRunConfig { - BenchRunConfig() { - host = "localhost"; - db = "test"; - - parallel = 1; - seconds = 1; - - active = true; - threadsReady = 0; - error = false; - } - - string host; - string db; - - unsigned parallel; - double seconds; - - BSONObj ops; - - bool active; // true at starts, gets set to false when should stop - AtomicUInt threadsReady; - - bool error; - }; - - static bool _hasSpecial( const BSONObj& obj ) { - BSONObjIterator i( obj ); - while ( i.more() ) { - BSONElement e = i.next(); - if ( e.fieldName()[0] == '#' ) - return true; - - if ( ! e.isABSONObj() ) - continue; - - if ( _hasSpecial( e.Obj() ) ) - return true; - } - return false; - } - - static void _fixField( BSONObjBuilder& b , const BSONElement& e ) { - assert( e.type() == Object ); - - BSONObj sub = e.Obj(); - assert( sub.nFields() == 1 ); - - BSONElement f = sub.firstElement(); - if ( str::equals( "#RAND_INT" , f.fieldName() ) ) { - BSONObjIterator i( f.Obj() ); - int min = i.next().numberInt(); - int max = i.next().numberInt(); - - int x = min + ( rand() % ( max - min ) ); - b.append( e.fieldName() , x ); - } - else { - uasserted( 14811 , str::stream() << "invalid bench dynamic piece: " << f.fieldName() ); - } - - } - - static void fixQuery( BSONObjBuilder& b , const BSONObj& obj ) { - BSONObjIterator i( obj ); - while ( i.more() ) { - BSONElement e = i.next(); - - if ( e.type() != Object ) { - b.append( e ); - continue; - } - - BSONObj sub = e.Obj(); - if ( sub.firstElement().fieldName()[0] != '#' ) { - b.append( e ); - continue; - } - - _fixField( b , e ); - } - } - - - static BSONObj fixQuery( const BSONObj& obj ) { - if ( ! _hasSpecial( obj ) ) - return obj; - - BSONObjBuilder b( obj.objsize() + 128 ); - fixQuery( b , obj ); - return b.obj(); - } - - static void benchThread( BenchRunConfig * config ) { - ScopedDbConnection conn( config->host ); - config->threadsReady++; - - while ( config->active ) { - BSONObjIterator i( config->ops ); - while ( i.more() ) { - BSONElement e = i.next(); - string ns = e["ns"].String(); - string op = e["op"].String(); - - if ( op == "findOne" ) { - conn->findOne( ns , fixQuery( e["query"].Obj() ) ); - } - else if ( op == "remove" ) { - conn->remove( ns , fixQuery( e["query"].Obj() ) ); - } - else if ( op == "update" ) { - conn->update( ns , fixQuery( e["query"].Obj() ) , e["update"].Obj() , e["upsert"].trueValue() ); - } - else { - log() << "don't understand op: " << op << endl; - config->error = true; - return; - } - - } - } - - conn.done(); - } - - /** - * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } - */ - BSONObj benchRun( const BSONObj& argsFake, void* data ) { - assert( argsFake.firstElement().isABSONObj() ); - BSONObj args = argsFake.firstElement().Obj(); - - // setup - - BenchRunConfig config; - - if ( args["host"].type() == String ) - config.host = args["host"].String(); - if ( args["db"].type() == String ) - config.db = args["db"].String(); - - if ( args["parallel"].isNumber() ) - config.parallel = args["parallel"].numberInt(); - if ( args["seconds"].isNumber() ) - config.seconds = args["seconds"].number(); - - - config.ops = args["ops"].Obj(); - - // execute - - ScopedDbConnection conn( config.host ); - - // start threads - vector<boost::thread*> all; - for ( unsigned i=0; i<config.parallel; i++ ) - all.push_back( new boost::thread( boost::bind( benchThread , &config ) ) ); - - // give them time to init - while ( config.threadsReady < config.parallel ) - sleepmillis( 1 ); - - BSONObj before; - conn->simpleCommand( "admin" , &before , "serverStatus" ); - - sleepmillis( (int)(1000.0 * config.seconds) ); - - BSONObj after; - conn->simpleCommand( "admin" , &after , "serverStatus" ); - - conn.done(); - - config.active = false; - - for ( unsigned i=0; i<all.size(); i++ ) - all[i]->join(); - - if ( config.error ) - return BSON( "err" << 1 ); - - // compute actual ops/sec - - before = before["opcounters"].Obj().copy(); - after = after["opcounters"].Obj().copy(); - - bool totals = args["totals"].trueValue(); - - BSONObjBuilder buf; - if ( ! totals ) - buf.append( "note" , "values per second" ); - - { - BSONObjIterator i( after ); - while ( i.more() ) { - BSONElement e = i.next(); - double x = e.number(); - x = x - before[e.fieldName()].number(); - if ( ! totals ) - x = x / config.seconds; - buf.append( e.fieldName() , x ); - } - } - BSONObj zoo = buf.obj(); - return BSON( "" << zoo ); - } - - void installBenchmarkSystem( Scope& scope ) { - scope.injectNative( "benchRun" , benchRun ); - } - -} diff --git a/scripting/engine.cpp b/scripting/engine.cpp deleted file mode 100644 index 7d13cb764fc..00000000000 --- a/scripting/engine.cpp +++ /dev/null @@ -1,507 +0,0 @@ -// engine.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. - */ - -#include "pch.h" -#include "engine.h" -#include "../util/file.h" -#include "../client/dbclient.h" - -namespace mongo { - - long long Scope::_lastVersion = 1; - - int Scope::_numScopes = 0; - - Scope::Scope() : _localDBName("") , _loadedVersion(0), _numTimeUsed(0) { - _numScopes++; - } - - Scope::~Scope() { - _numScopes--; - } - - ScriptEngine::ScriptEngine() : _scopeInitCallback() { - } - - ScriptEngine::~ScriptEngine() { - } - - void Scope::append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ) { - int t = type( scopeName ); - - switch ( t ) { - case Object: - builder.append( fieldName , getObject( scopeName ) ); - break; - case Array: - builder.appendArray( fieldName , getObject( scopeName ) ); - break; - case NumberDouble: - builder.append( fieldName , getNumber( scopeName ) ); - break; - case NumberInt: - builder.append( fieldName , getNumberInt( scopeName ) ); - break; - case NumberLong: - builder.append( fieldName , getNumberLongLong( scopeName ) ); - break; - case String: - builder.append( fieldName , getString( scopeName ).c_str() ); - break; - case Bool: - builder.appendBool( fieldName , getBoolean( scopeName ) ); - break; - case jstNULL: - case Undefined: - builder.appendNull( fieldName ); - break; - case Date: - // TODO: make signed - builder.appendDate( fieldName , Date_t((unsigned long long)getNumber( scopeName )) ); - break; - case Code: - builder.appendCode( fieldName , getString( scopeName ) ); - break; - default: - stringstream temp; - temp << "can't append type from:"; - temp << t; - uassert( 10206 , temp.str() , 0 ); - } - - } - - int Scope::invoke( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs ) { - ScriptingFunction func = createFunction( code ); - uassert( 10207 , "compile failed" , func ); - return invoke( func , args, recv, timeoutMs ); - } - - bool Scope::execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs ) { - - path p( filename ); - - if ( ! exists( p ) ) { - log() << "file [" << filename << "] doesn't exist" << endl; - if ( assertOnError ) - assert( 0 ); - return false; - } - - // iterate directories and recurse using all *.js files in the directory - if ( is_directory( p ) ) { - directory_iterator end; - bool empty = true; - for (directory_iterator it (p); it != end; it++) { - empty = false; - path sub (*it); - if (!endsWith(sub.string().c_str(), ".js")) - continue; - if (!execFile(sub.string().c_str(), printResult, reportError, assertOnError, timeoutMs)) - return false; - } - - if (empty) { - log() << "directory [" << filename << "] doesn't have any *.js files" << endl; - if ( assertOnError ) - assert( 0 ); - return false; - } - - return true; - } - - File f; - f.open( filename.c_str() , true ); - - unsigned L; - { - fileofs fo = f.len(); - assert( fo <= 0x7ffffffe ); - L = (unsigned) fo; - } - boost::scoped_array<char> data (new char[L+1]); - data[L] = 0; - f.read( 0 , data.get() , L ); - - int offset = 0; - if (data[0] == '#' && data[1] == '!') { - const char* newline = strchr(data.get(), '\n'); - if (! newline) - return true; // file of just shebang treated same as empty file - offset = newline - data.get(); - } - - StringData code (data.get() + offset, L - offset); - - return exec( code , filename , printResult , reportError , assertOnError, timeoutMs ); - } - - void Scope::storedFuncMod() { - _lastVersion++; - } - - void Scope::validateObjectIdString( const string &str ) { - massert( 10448 , "invalid object id: length", str.size() == 24 ); - - for ( string::size_type i=0; i<str.size(); i++ ) { - char c = str[i]; - if ( ( c >= '0' && c <= '9' ) || - ( c >= 'a' && c <= 'f' ) || - ( c >= 'A' && c <= 'F' ) ) { - continue; - } - massert( 10430 , "invalid object id: not hex", false ); - } - } - - void Scope::loadStored( bool ignoreNotConnected ) { - if ( _localDBName.size() == 0 ) { - if ( ignoreNotConnected ) - return; - uassert( 10208 , "need to have locallyConnected already" , _localDBName.size() ); - } - if ( _loadedVersion == _lastVersion ) - return; - - _loadedVersion = _lastVersion; - - string coll = _localDBName + ".system.js"; - - static DBClientBase * db = createDirectClient(); - auto_ptr<DBClientCursor> c = db->query( coll , Query(), 0, 0, NULL, QueryOption_SlaveOk, 0 ); - assert( c.get() ); - - set<string> thisTime; - - while ( c->more() ) { - BSONObj o = c->nextSafe(); - - BSONElement n = o["_id"]; - BSONElement v = o["value"]; - - uassert( 10209 , str::stream() << "name has to be a string: " << n , n.type() == String ); - uassert( 10210 , "value has to be set" , v.type() != EOO ); - - setElement( n.valuestr() , v ); - - thisTime.insert( n.valuestr() ); - _storedNames.insert( n.valuestr() ); - - } - - // --- remove things from scope that were removed - - list<string> toremove; - - for ( set<string>::iterator i=_storedNames.begin(); i!=_storedNames.end(); i++ ) { - string n = *i; - if ( thisTime.count( n ) == 0 ) - toremove.push_back( n ); - } - - for ( list<string>::iterator i=toremove.begin(); i!=toremove.end(); i++ ) { - string n = *i; - _storedNames.erase( n ); - execSetup( (string)"delete " + n , "clean up scope" ); - } - - } - - ScriptingFunction Scope::createFunction( const char * code ) { - if ( code[0] == '/' && code [1] == '*' ) { - code += 2; - while ( code[0] && code[1] ) { - if ( code[0] == '*' && code[1] == '/' ) { - code += 2; - break; - } - code++; - } - } - map<string,ScriptingFunction>::iterator i = _cachedFunctions.find( code ); - if ( i != _cachedFunctions.end() ) - return i->second; - ScriptingFunction f = _createFunction( code ); - _cachedFunctions[code] = f; - return f; - } - - namespace JSFiles { - extern const JSFile collection; - extern const JSFile db; - extern const JSFile mongo; - extern const JSFile mr; - extern const JSFile query; - extern const JSFile utils; - extern const JSFile utils_sh; - } - - void Scope::execCoreFiles() { - // keeping same order as in SConstruct - execSetup(JSFiles::utils); - execSetup(JSFiles::utils_sh); - execSetup(JSFiles::db); - execSetup(JSFiles::mongo); - execSetup(JSFiles::mr); - execSetup(JSFiles::query); - execSetup(JSFiles::collection); - } - - typedef map< string , list<Scope*> > PoolToScopes; - - class ScopeCache { - public: - - ScopeCache() : _mutex("ScopeCache") { - _magic = 17; - } - - ~ScopeCache() { - assert( _magic == 17 ); - _magic = 1; - - if ( inShutdown() ) - return; - - clear(); - } - - void done( const string& pool , Scope * s ) { - scoped_lock lk( _mutex ); - list<Scope*> & l = _pools[pool]; - // make we dont keep to many contexts, or use them for too long - if ( l.size() > 10 || s->getTimeUsed() > 100 ) { - delete s; - } - else { - l.push_back( s ); - s->reset(); - } - } - - Scope * get( const string& pool ) { - scoped_lock lk( _mutex ); - list<Scope*> & l = _pools[pool]; - if ( l.size() == 0 ) - return 0; - - Scope * s = l.back(); - l.pop_back(); - s->reset(); - s->incTimeUsed(); - return s; - } - - void clear() { - set<Scope*> seen; - - for ( PoolToScopes::iterator i=_pools.begin() ; i != _pools.end(); i++ ) { - for ( list<Scope*>::iterator j=i->second.begin(); j != i->second.end(); j++ ) { - Scope * s = *j; - assert( ! seen.count( s ) ); - delete s; - seen.insert( s ); - } - } - - _pools.clear(); - } - - private: - PoolToScopes _pools; - mongo::mutex _mutex; - int _magic; - }; - - thread_specific_ptr<ScopeCache> scopeCache; - - class PooledScope : public Scope { - public: - PooledScope( const string pool , Scope * real ) : _pool( pool ) , _real( real ) { - _real->loadStored( true ); - }; - virtual ~PooledScope() { - ScopeCache * sc = scopeCache.get(); - if ( sc ) { - sc->done( _pool , _real ); - _real = 0; - } - else { - // this means that the Scope was killed from a different thread - // for example a cursor got timed out that has a $where clause - log(3) << "warning: scopeCache is empty!" << endl; - delete _real; - _real = 0; - } - } - - void reset() { - _real->reset(); - } - void init( const BSONObj * data ) { - _real->init( data ); - } - - void localConnect( const char * dbName ) { - _real->localConnect( dbName ); - } - void externalSetup() { - _real->externalSetup(); - } - - double getNumber( const char *field ) { - return _real->getNumber( field ); - } - string getString( const char *field ) { - return _real->getString( field ); - } - bool getBoolean( const char *field ) { - return _real->getBoolean( field ); - } - BSONObj getObject( const char *field ) { - return _real->getObject( field ); - } - - int type( const char *field ) { - return _real->type( field ); - } - - void setElement( const char *field , const BSONElement& val ) { - _real->setElement( field , val ); - } - void setNumber( const char *field , double val ) { - _real->setNumber( field , val ); - } - void setString( const char *field , const char * val ) { - _real->setString( field , val ); - } - void setObject( const char *field , const BSONObj& obj , bool readOnly=true ) { - _real->setObject( field , obj , readOnly ); - } - void setBoolean( const char *field , bool val ) { - _real->setBoolean( field , val ); - } -// void setThis( const BSONObj * obj ) { -// _real->setThis( obj ); -// } - - void setFunction( const char *field , const char * code ) { - _real->setFunction(field, code); - } - - ScriptingFunction createFunction( const char * code ) { - return _real->createFunction( code ); - } - - ScriptingFunction _createFunction( const char * code ) { - return _real->createFunction( code ); - } - - void rename( const char * from , const char * to ) { - _real->rename( from , to ); - } - - /** - * @return 0 on success - */ - int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs , bool ignoreReturn, bool readOnlyArgs, bool readOnlyRecv ) { - return _real->invoke( func , args , recv, timeoutMs , ignoreReturn ); - } - - string getError() { - return _real->getError(); - } - - bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) { - return _real->exec( code , name , printResult , reportError , assertOnError , timeoutMs ); - } - bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) { - return _real->execFile( filename , printResult , reportError , assertOnError , timeoutMs ); - } - - void injectNative( const char *field, NativeFunction func, void* data ) { - _real->injectNative( field , func, data ); - } - - void gc() { - _real->gc(); - } - - void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ) { - _real->append(builder, fieldName, scopeName); - } - - private: - string _pool; - Scope * _real; - }; - - auto_ptr<Scope> ScriptEngine::getPooledScope( const string& pool ) { - if ( ! scopeCache.get() ) { - scopeCache.reset( new ScopeCache() ); - } - - Scope * s = scopeCache->get( pool ); - if ( ! s ) { - s = newScope(); - } - - auto_ptr<Scope> p; - p.reset( new PooledScope( pool , s ) ); - return p; - } - - void ScriptEngine::threadDone() { - ScopeCache * sc = scopeCache.get(); - if ( sc ) { - sc->clear(); - } - } - - void ( *ScriptEngine::_connectCallback )( DBClientWithCommands & ) = 0; - const char * ( *ScriptEngine::_checkInterruptCallback )() = 0; - unsigned ( *ScriptEngine::_getInterruptSpecCallback )() = 0; - - ScriptEngine * globalScriptEngine = 0; - - bool hasJSReturn( const string& code ) { - size_t x = code.find( "return" ); - if ( x == string::npos ) - return false; - - return - ( x == 0 || ! isalpha( code[x-1] ) ) && - ! isalpha( code[x+6] ); - } - - const char * jsSkipWhiteSpace( const char * raw ) { - while ( raw[0] ) { - while (isspace(*raw)) { - raw++; - } - - if ( raw[0] != '/' || raw[1] != '/' ) - break; - - while ( raw[0] && raw[0] != '\n' ) - raw++; - } - return raw; - } -} - diff --git a/scripting/engine.h b/scripting/engine.h deleted file mode 100644 index 9dd5f1d322c..00000000000 --- a/scripting/engine.h +++ /dev/null @@ -1,234 +0,0 @@ -// engine.h - -/* 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. - */ - -#pragma once - -#include "../pch.h" -#include "../db/jsobj.h" - -namespace mongo { - - struct JSFile { - const char* name; - const StringData& source; - }; - - typedef unsigned long long ScriptingFunction; - typedef BSONObj (*NativeFunction) ( const BSONObj &args, void* data ); - - class Scope : boost::noncopyable { - public: - Scope(); - virtual ~Scope(); - - virtual void reset() = 0; - virtual void init( const BSONObj * data ) = 0; - void init( const char * data ) { - BSONObj o( data ); - init( &o ); - } - - virtual void localConnect( const char * dbName ) = 0; - virtual void externalSetup() = 0; - - class NoDBAccess { - Scope * _s; - public: - NoDBAccess( Scope * s ) { - _s = s; - } - ~NoDBAccess() { - _s->rename( "____db____" , "db" ); - } - }; - NoDBAccess disableDBAccess( const char * why ) { - rename( "db" , "____db____" ); - return NoDBAccess( this ); - } - - virtual double getNumber( const char *field ) = 0; - virtual int getNumberInt( const char *field ) { return (int)getNumber( field ); } - virtual long long getNumberLongLong( const char *field ) { return (long long)getNumber( field ); } - virtual string getString( const char *field ) = 0; - virtual bool getBoolean( const char *field ) = 0; - virtual BSONObj getObject( const char *field ) = 0; - - virtual int type( const char *field ) = 0; - - virtual void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ); - - virtual void setElement( const char *field , const BSONElement& e ) = 0; - virtual void setNumber( const char *field , double val ) = 0; - virtual void setString( const char *field , const char * val ) = 0; - virtual void setObject( const char *field , const BSONObj& obj , bool readOnly=true ) = 0; - virtual void setBoolean( const char *field , bool val ) = 0; - virtual void setFunction( const char *field , const char * code ) = 0; -// virtual void setThis( const BSONObj * obj ) = 0; - - virtual ScriptingFunction createFunction( const char * code ); - - virtual void rename( const char * from , const char * to ) = 0; - /** - * @return 0 on success - */ - virtual int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool readOnlyArgs = false, bool readOnlyRecv = false ) = 0; - void invokeSafe( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0, bool readOnlyArgs = false, bool readOnlyRecv = false ) { - int res = invoke( func , args , recv, timeoutMs, readOnlyArgs, readOnlyRecv ); - if ( res == 0 ) - return; - throw UserException( 9004 , (string)"invoke failed: " + getError() ); - } - virtual string getError() = 0; - - int invoke( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 ); - void invokeSafe( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 ) { - if ( invoke( code , args , recv, timeoutMs ) == 0 ) - return; - throw UserException( 9005 , (string)"invoke failed: " + getError() ); - } - - virtual bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0; - virtual void execSetup( const StringData& code , const string& name = "setup" ) { - exec( code , name , false , true , true , 0 ); - } - - void execSetup( const JSFile& file) { - execSetup(file.source, file.name); - } - - void execCoreFiles(); - - virtual bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ); - - virtual void injectNative( const char *field, NativeFunction func, void* data = 0 ) = 0; - - virtual void gc() = 0; - - void loadStored( bool ignoreNotConnected = false ); - - /** - if any changes are made to .system.js, call this - right now its just global - slightly inefficient, but a lot simpler - */ - static void storedFuncMod(); - - static int getNumScopes() { - return _numScopes; - } - - static void validateObjectIdString( const string &str ); - - /** increments the number of times a scope was used */ - void incTimeUsed() { ++_numTimeUsed; } - /** gets the number of times a scope was used */ - int getTimeUsed() { return _numTimeUsed; } - - protected: - - virtual ScriptingFunction _createFunction( const char * code ) = 0; - - string _localDBName; - long long _loadedVersion; - set<string> _storedNames; - static long long _lastVersion; - map<string,ScriptingFunction> _cachedFunctions; - int _numTimeUsed; - - static int _numScopes; - }; - - void installGlobalUtils( Scope& scope ); - - class DBClientWithCommands; - - class ScriptEngine : boost::noncopyable { - public: - ScriptEngine(); - virtual ~ScriptEngine(); - - virtual Scope * newScope() { - Scope *s = createScope(); - if ( s && _scopeInitCallback ) - _scopeInitCallback( *s ); - installGlobalUtils( *s ); - return s; - } - - virtual void runTest() = 0; - - virtual bool utf8Ok() const = 0; - - static void setup(); - - /** gets a scope from the pool or a new one if pool is empty - * @param pool An identifier for the pool, usually the db name - * @return the scope */ - auto_ptr<Scope> getPooledScope( const string& pool ); - - /** call this method to release some JS resources when a thread is done */ - void threadDone(); - - struct Unlocker { virtual ~Unlocker() {} }; - virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< Unlocker >( new Unlocker ); } - - void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInitCallback = func; } - static void setConnectCallback( void ( *func )( DBClientWithCommands& ) ) { _connectCallback = func; } - static void runConnectCallback( DBClientWithCommands &c ) { - if ( _connectCallback ) - _connectCallback( c ); - } - - // engine implementation may either respond to interrupt events or - // poll for interrupts - - // the interrupt functions must not wait indefinitely on a lock - virtual void interrupt( unsigned opSpec ) {} - virtual void interruptAll() {} - - static void setGetInterruptSpecCallback( unsigned ( *func )() ) { _getInterruptSpecCallback = func; } - static bool haveGetInterruptSpecCallback() { return _getInterruptSpecCallback; } - static unsigned getInterruptSpec() { - massert( 13474, "no _getInterruptSpecCallback", _getInterruptSpecCallback ); - return _getInterruptSpecCallback(); - } - - static void setCheckInterruptCallback( const char * ( *func )() ) { _checkInterruptCallback = func; } - static bool haveCheckInterruptCallback() { return _checkInterruptCallback; } - static const char * checkInterrupt() { - return _checkInterruptCallback ? _checkInterruptCallback() : ""; - } - static bool interrupted() { - const char *r = checkInterrupt(); - return r && r[ 0 ]; - } - - protected: - virtual Scope * createScope() = 0; - - private: - void ( *_scopeInitCallback )( Scope & ); - static void ( *_connectCallback )( DBClientWithCommands & ); - static const char * ( *_checkInterruptCallback )(); - static unsigned ( *_getInterruptSpecCallback )(); - }; - - bool hasJSReturn( const string& s ); - - const char * jsSkipWhiteSpace( const char * raw ); - - extern ScriptEngine * globalScriptEngine; -} diff --git a/scripting/engine_java.cpp b/scripting/engine_java.cpp deleted file mode 100644 index 57388166e98..00000000000 --- a/scripting/engine_java.cpp +++ /dev/null @@ -1,764 +0,0 @@ -// java.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. - */ - - -#include "pch.h" -#include "engine_java.h" -#include <iostream> -#include <map> -#include <list> - -#include "../db/jsobj.h" -#include "../db/db.h" - -using namespace boost::filesystem; - -namespace mongo { - -//#define JNI_DEBUG 1 - -#ifdef JNI_DEBUG -#undef JNI_DEBUG -#define JNI_DEBUG(x) cout << x << endl -#else -#undef JNI_DEBUG -#define JNI_DEBUG(x) -#endif - -} // namespace mongo - - - -#include "../util/net/message.h" -#include "../db/db.h" - -using namespace std; - -namespace mongo { - -#if defined(_WIN32) - /* [dm] this being undefined without us adding it here means there is - no tss cleanup on windows for boost lib? - we don't care for now esp on windows only - - the boost source says: - - This function's sole purpose is to cause a link error in cases where - automatic tss cleanup is not implemented by Boost.Threads as a - reminder that user code is responsible for calling the necessary - functions at the appropriate times (and for implementing an a - tss_cleanup_implemented() function to eliminate the linker's - missing symbol error). - - If Boost.Threads later implements automatic tss cleanup in cases - where it currently doesn't (which is the plan), the duplicate - symbol error will warn the user that their custom solution is no - longer needed and can be removed. - */ - extern "C" void tss_cleanup_implemented(void) { - //out() << "tss_cleanup_implemented called" << endl; - } -#endif - - JavaJSImpl * JavaJS = 0; - extern string dbExecCommand; - -#if !defined(NOJNI) - - void myJNIClean( JNIEnv * env ) { - JavaJS->detach( env ); - } - -#if defined(_WIN32) - const char SYSTEM_COLON = ';'; -#else - const char SYSTEM_COLON = ':'; -#endif - - - void _addClassPath( const char * ed , stringstream & ss , const char * subdir ) { - path includeDir(ed); - includeDir /= subdir; - directory_iterator end; - try { - directory_iterator i(includeDir); - while ( i != end ) { - path p = *i; - ss << SYSTEM_COLON << p.string(); - i++; - } - } - catch (...) { - problem() << "exception looking for ed class path includeDir: " << includeDir.string() << endl; - sleepsecs(3); - dbexit( EXIT_JAVA ); - } - } - - - JavaJSImpl::JavaJSImpl(const char *appserverPath) { - _jvm = 0; - _mainEnv = 0; - _dbhook = 0; - - stringstream ss; - string edTemp; - - const char * ed = 0; - ss << "-Djava.class.path=."; - - if ( appserverPath ) { - ed = findEd(appserverPath); - assert( ed ); - - ss << SYSTEM_COLON << ed << "/build/"; - - _addClassPath( ed , ss , "include" ); - _addClassPath( ed , ss , "include/jython/" ); - _addClassPath( ed , ss , "include/jython/javalib" ); - } - else { - const string jars = findJars(); - _addClassPath( jars.c_str() , ss , "jars" ); - - edTemp += (string)jars + "/jars/mongojs-js.jar"; - ed = edTemp.c_str(); - } - - - -#if defined(_WIN32) - ss << SYSTEM_COLON << "C:\\Program Files\\Java\\jdk\\lib\\tools.jar"; -#else - ss << SYSTEM_COLON << "/opt/java/lib/tools.jar"; -#endif - - if ( getenv( "CLASSPATH" ) ) - ss << SYSTEM_COLON << getenv( "CLASSPATH" ); - - string s = ss.str(); - char * p = (char *)malloc( s.size() * 4 ); - strcpy( p , s.c_str() ); - char *q = p; -#if defined(_WIN32) - while ( *p ) { - if ( *p == '/' ) *p = '\\'; - p++; - } -#endif - - log(1) << "classpath: " << q << endl; - - JavaVMOption * options = new JavaVMOption[4]; - options[0].optionString = q; - options[1].optionString = (char*)"-Djava.awt.headless=true"; - options[2].optionString = (char*)"-Xmx300m"; - - // Prevent JVM from using async signals internally, since on linux the pre-installed handlers for these - // signals don't seem to be respected by JNI. - options[3].optionString = (char*)"-Xrs"; - // -Xcheck:jni - - _vmArgs = new JavaVMInitArgs(); - _vmArgs->version = JNI_VERSION_1_4; - _vmArgs->options = options; - _vmArgs->nOptions = 4; - _vmArgs->ignoreUnrecognized = JNI_FALSE; - - log(1) << "loading JVM" << endl; - jint res = JNI_CreateJavaVM( &_jvm, (void**)&_mainEnv, _vmArgs ); - - if ( res ) { - log() << "using classpath: " << q << endl; - log() - << " res : " << (unsigned) res << " " - << "_jvm : " << _jvm << " " - << "_env : " << _mainEnv << " " - << endl; - problem() << "Couldn't create JVM res:" << (int) res << " terminating" << endl; - log() << "(try --nojni if you do not require that functionality)" << endl; - exit(22); - } - jassert( res == 0 ); - jassert( _jvm > 0 ); - jassert( _mainEnv > 0 ); - - _envs = new boost::thread_specific_ptr<JNIEnv>( myJNIClean ); - assert( ! _envs->get() ); - _envs->reset( _mainEnv ); - - _dbhook = findClass( "ed/db/JSHook" ); - if ( _dbhook == 0 ) { - log() << "using classpath: " << q << endl; - printException(); - } - jassert( _dbhook ); - - if ( ed ) { - jmethodID init = _mainEnv->GetStaticMethodID( _dbhook , "init" , "(Ljava/lang/String;)V" ); - jassert( init ); - _mainEnv->CallStaticVoidMethod( _dbhook , init , _getEnv()->NewStringUTF( ed ) ); - } - - _dbjni = findClass( "ed/db/DBJni" ); - jassert( _dbjni ); - - _scopeCreate = _mainEnv->GetStaticMethodID( _dbhook , "scopeCreate" , "()J" ); - _scopeInit = _mainEnv->GetStaticMethodID( _dbhook , "scopeInit" , "(JLjava/nio/ByteBuffer;)Z" ); - _scopeSetThis = _mainEnv->GetStaticMethodID( _dbhook , "scopeSetThis" , "(JLjava/nio/ByteBuffer;)Z" ); - _scopeReset = _mainEnv->GetStaticMethodID( _dbhook , "scopeReset" , "(J)Z" ); - _scopeFree = _mainEnv->GetStaticMethodID( _dbhook , "scopeFree" , "(J)V" ); - - _scopeGetNumber = _mainEnv->GetStaticMethodID( _dbhook , "scopeGetNumber" , "(JLjava/lang/String;)D" ); - _scopeGetString = _mainEnv->GetStaticMethodID( _dbhook , "scopeGetString" , "(JLjava/lang/String;)Ljava/lang/String;" ); - _scopeGetBoolean = _mainEnv->GetStaticMethodID( _dbhook , "scopeGetBoolean" , "(JLjava/lang/String;)Z" ); - _scopeGetType = _mainEnv->GetStaticMethodID( _dbhook , "scopeGetType" , "(JLjava/lang/String;)B" ); - _scopeGetObject = _mainEnv->GetStaticMethodID( _dbhook , "scopeGetObject" , "(JLjava/lang/String;Ljava/nio/ByteBuffer;)I" ); - _scopeGuessObjectSize = _mainEnv->GetStaticMethodID( _dbhook , "scopeGuessObjectSize" , "(JLjava/lang/String;)J" ); - - _scopeSetNumber = _mainEnv->GetStaticMethodID( _dbhook , "scopeSetNumber" , "(JLjava/lang/String;D)Z" ); - _scopeSetBoolean = _mainEnv->GetStaticMethodID( _dbhook , "scopeSetBoolean" , "(JLjava/lang/String;Z)Z" ); - _scopeSetString = _mainEnv->GetStaticMethodID( _dbhook , "scopeSetString" , "(JLjava/lang/String;Ljava/lang/String;)Z" ); - _scopeSetObject = _mainEnv->GetStaticMethodID( _dbhook , "scopeSetObject" , "(JLjava/lang/String;Ljava/nio/ByteBuffer;)Z" ); - - _functionCreate = _mainEnv->GetStaticMethodID( _dbhook , "functionCreate" , "(Ljava/lang/String;)J" ); - _invoke = _mainEnv->GetStaticMethodID( _dbhook , "invoke" , "(JJ)I" ); - - jassert( _scopeCreate ); - jassert( _scopeInit ); - jassert( _scopeSetThis ); - jassert( _scopeReset ); - jassert( _scopeFree ); - - jassert( _scopeGetNumber ); - jassert( _scopeGetString ); - jassert( _scopeGetObject ); - jassert( _scopeGetBoolean ); - jassert( _scopeGetType ); - jassert( _scopeGuessObjectSize ); - - jassert( _scopeSetNumber ); - jassert( _scopeSetBoolean ); - jassert( _scopeSetString ); - jassert( _scopeSetObject ); - - jassert( _functionCreate ); - jassert( _invoke ); - - JNINativeMethod * nativeSay = new JNINativeMethod(); - nativeSay->name = (char*)"native_say"; - nativeSay->signature = (char*)"(Ljava/nio/ByteBuffer;)V"; - nativeSay->fnPtr = (void*)java_native_say; - _mainEnv->RegisterNatives( _dbjni , nativeSay , 1 ); - - - JNINativeMethod * nativeCall = new JNINativeMethod(); - nativeCall->name = (char*)"native_call"; - nativeCall->signature = (char*)"(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I"; - nativeCall->fnPtr = (void*)java_native_call; - _mainEnv->RegisterNatives( _dbjni , nativeCall , 1 ); - - } - - JavaJSImpl::~JavaJSImpl() { - if ( _jvm ) { - _jvm->DestroyJavaVM(); - cout << "Destroying JVM" << endl; - } - } - -// scope - - jlong JavaJSImpl::scopeCreate() { - return _getEnv()->CallStaticLongMethod( _dbhook , _scopeCreate ); - } - - jboolean JavaJSImpl::scopeReset( jlong id ) { - return _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeReset ); - } - - void JavaJSImpl::scopeFree( jlong id ) { - _getEnv()->CallStaticVoidMethod( _dbhook , _scopeFree , id ); - } - -// scope setters - - int JavaJSImpl::scopeSetBoolean( jlong id , const char * field , jboolean val ) { - jstring fieldString = _getEnv()->NewStringUTF( field ); - int res = _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeSetNumber , id , fieldString , val ); - _getEnv()->DeleteLocalRef( fieldString ); - return res; - } - - int JavaJSImpl::scopeSetNumber( jlong id , const char * field , double val ) { - jstring fieldString = _getEnv()->NewStringUTF( field ); - int res = _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeSetNumber , id , fieldString , val ); - _getEnv()->DeleteLocalRef( fieldString ); - return res; - } - - int JavaJSImpl::scopeSetString( jlong id , const char * field , const char * val ) { - jstring s1 = _getEnv()->NewStringUTF( field ); - jstring s2 = _getEnv()->NewStringUTF( val ); - int res = _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeSetString , id , s1 , s2 ); - _getEnv()->DeleteLocalRef( s1 ); - _getEnv()->DeleteLocalRef( s2 ); - return res; - } - - int JavaJSImpl::scopeSetObject( jlong id , const char * field , const BSONObj * obj ) { - jobject bb = 0; - if ( obj ) { - bb = _getEnv()->NewDirectByteBuffer( (void*)(obj->objdata()) , (jlong)(obj->objsize()) ); - jassert( bb ); - } - - jstring s1 = _getEnv()->NewStringUTF( field ); - int res = _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeSetObject , id , s1 , bb ); - _getEnv()->DeleteLocalRef( s1 ); - if ( bb ) - _getEnv()->DeleteLocalRef( bb ); - - return res; - } - - int JavaJSImpl::scopeInit( jlong id , const BSONObj * obj ) { - if ( ! obj ) - return 0; - - jobject bb = _getEnv()->NewDirectByteBuffer( (void*)(obj->objdata()) , (jlong)(obj->objsize()) ); - jassert( bb ); - - int res = _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeInit , id , bb ); - _getEnv()->DeleteLocalRef( bb ); - return res; - } - - int JavaJSImpl::scopeSetThis( jlong id , const BSONObj * obj ) { - if ( ! obj ) - return 0; - - jobject bb = _getEnv()->NewDirectByteBuffer( (void*)(obj->objdata()) , (jlong)(obj->objsize()) ); - jassert( bb ); - - int res = _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeSetThis , id , bb ); - _getEnv()->DeleteLocalRef( bb ); - return res; - } - -// scope getters - - char JavaJSImpl::scopeGetType( jlong id , const char * field ) { - jstring s1 = _getEnv()->NewStringUTF( field ); - int res =_getEnv()->CallStaticByteMethod( _dbhook , _scopeGetType , id , s1 ); - _getEnv()->DeleteLocalRef( s1 ); - return res; - } - - double JavaJSImpl::scopeGetNumber( jlong id , const char * field ) { - jstring s1 = _getEnv()->NewStringUTF( field ); - double res = _getEnv()->CallStaticDoubleMethod( _dbhook , _scopeGetNumber , id , s1 ); - _getEnv()->DeleteLocalRef( s1 ); - return res; - } - - jboolean JavaJSImpl::scopeGetBoolean( jlong id , const char * field ) { - jstring s1 = _getEnv()->NewStringUTF( field ); - jboolean res = _getEnv()->CallStaticBooleanMethod( _dbhook , _scopeGetBoolean , id , s1 ); - _getEnv()->DeleteLocalRef( s1 ); - return res; - } - - string JavaJSImpl::scopeGetString( jlong id , const char * field ) { - jstring s1 = _getEnv()->NewStringUTF( field ); - jstring s = (jstring)_getEnv()->CallStaticObjectMethod( _dbhook , _scopeGetString , id , s1 ); - _getEnv()->DeleteLocalRef( s1 ); - - if ( ! s ) - return ""; - - const char * c = _getEnv()->GetStringUTFChars( s , 0 ); - string retStr(c); - _getEnv()->ReleaseStringUTFChars( s , c ); - return retStr; - } - - BSONObj JavaJSImpl::scopeGetObject( jlong id , const char * field ) { - jstring s1 = _getEnv()->NewStringUTF( field ); - int guess = _getEnv()->CallStaticIntMethod( _dbhook , _scopeGuessObjectSize , id , _getEnv()->NewStringUTF( field ) ); - _getEnv()->DeleteLocalRef( s1 ); - - if ( guess == 0 ) - return BSONObj(); - - BSONObj::Holder* holder = (BSONObj::Holder*) malloc(guess + sizeof(unsigned)); - holder->zero() - - jobject bb = _getEnv()->NewDirectByteBuffer( (void*)holder->data , guess ); - jassert( bb ); - - int len = _getEnv()->CallStaticIntMethod( _dbhook , _scopeGetObject , id , _getEnv()->NewStringUTF( field ) , bb ); - _getEnv()->DeleteLocalRef( bb ); - jassert( len > 0 && len < guess ); - - BSONObj obj(holder); - assert( obj.objsize() <= guess ); - return obj; - } - -// other - - jlong JavaJSImpl::functionCreate( const char * code ) { - jstring s = _getEnv()->NewStringUTF( code ); - jassert( s ); - jlong id = _getEnv()->CallStaticLongMethod( _dbhook , _functionCreate , s ); - _getEnv()->DeleteLocalRef( s ); - return id; - } - - int JavaJSImpl::invoke( jlong scope , jlong function ) { - return _getEnv()->CallStaticIntMethod( _dbhook , _invoke , scope , function ); - } - -// --- fun run method - - void JavaJSImpl::run( const char * js ) { - jclass c = findClass( "ed/js/JS" ); - jassert( c ); - - jmethodID m = _getEnv()->GetStaticMethodID( c , "eval" , "(Ljava/lang/String;)Ljava/lang/Object;" ); - jassert( m ); - - jstring s = _getEnv()->NewStringUTF( js ); - log() << _getEnv()->CallStaticObjectMethod( c , m , s ) << endl; - _getEnv()->DeleteLocalRef( s ); - } - - void JavaJSImpl::printException() { - jthrowable exc = _getEnv()->ExceptionOccurred(); - if ( exc ) { - _getEnv()->ExceptionDescribe(); - _getEnv()->ExceptionClear(); - } - - } - - JNIEnv * JavaJSImpl::_getEnv() { - JNIEnv * env = _envs->get(); - if ( env ) - return env; - - int res = _jvm->AttachCurrentThread( (void**)&env , (void*)&_vmArgs ); - if ( res ) { - out() << "ERROR javajs attachcurrentthread fails res:" << res << '\n'; - assert(false); - } - - _envs->reset( env ); - return env; - } - - Scope * JavaJSImpl::createScope() { - return new JavaScope(); - } - - void ScriptEngine::setup() { - if ( ! JavaJS ) { - JavaJS = new JavaJSImpl(); - globalScriptEngine = JavaJS; - } - } - - void jasserted(const char *msg, const char *file, unsigned line) { - log() << "jassert failed " << msg << " " << file << " " << line << endl; - if ( JavaJS ) JavaJS->printException(); - throw AssertionException(); - } - - - const char* findEd(const char *path) { - -#if defined(_WIN32) - - if (!path) { - path = findEd(); - } - - // @TODO check validity - - return path; -#else - - if (!path) { - return findEd(); - } - - log() << "Appserver location specified : " << path << endl; - - if (!path) { - log() << " invalid appserver location : " << path << " : terminating - prepare for bus error" << endl; - return 0; - } - - DIR *testDir = opendir(path); - - if (testDir) { - log(1) << " found directory for appserver : " << path << endl; - closedir(testDir); - return path; - } - else { - log() << " ERROR : not a directory for specified appserver location : " << path << " - prepare for bus error" << endl; - return null; - } -#endif - } - - const char * findEd() { - -#if defined(_WIN32) - log() << "Appserver location will be WIN32 default : c:/l/ed/" << endl; - return "c:/l/ed"; -#else - - static list<const char*> possibleEdDirs; - if ( ! possibleEdDirs.size() ) { - possibleEdDirs.push_back( "../../ed/ed/" ); // this one for dwight dev box - possibleEdDirs.push_back( "../ed/" ); - possibleEdDirs.push_back( "../../ed/" ); - possibleEdDirs.push_back( "../babble/" ); - possibleEdDirs.push_back( "../../babble/" ); - } - - for ( list<const char*>::iterator i = possibleEdDirs.begin() ; i != possibleEdDirs.end(); i++ ) { - const char * temp = *i; - DIR * test = opendir( temp ); - if ( ! test ) - continue; - - closedir( test ); - log(1) << "found directory for appserver : " << temp << endl; - return temp; - } - - return 0; -#endif - }; - - const string findJars() { - - static list<string> possible; - if ( ! possible.size() ) { - possible.push_back( "./" ); - possible.push_back( "../" ); - - log(2) << "dbExecCommand: " << dbExecCommand << endl; - - string dbDir = dbExecCommand; -#ifdef WIN32 - if ( dbDir.find( "\\" ) != string::npos ) { - dbDir = dbDir.substr( 0 , dbDir.find_last_of( "\\" ) ); - } - else { - dbDir = "."; - } -#else - if ( dbDir.find( "/" ) != string::npos ) { - dbDir = dbDir.substr( 0 , dbDir.find_last_of( "/" ) ); - } - else { - bool found = false; - - if ( getenv( "PATH" ) ) { - string s = getenv( "PATH" ); - s += ":"; - pcrecpp::StringPiece input( s ); - string dir; - pcrecpp::RE re("(.*?):"); - while ( re.Consume( &input, &dir ) ) { - string test = dir + "/" + dbExecCommand; - if ( boost::filesystem::exists( test ) ) { - while ( boost::filesystem::symbolic_link_exists( test ) ) { - char tmp[2048]; - int len = readlink( test.c_str() , tmp , 2048 ); - tmp[len] = 0; - log(5) << " symlink " << test << " -->> " << tmp << endl; - test = tmp; - - dir = test.substr( 0 , test.rfind( "/" ) ); - } - dbDir = dir; - found = true; - break; - } - } - } - - if ( ! found ) - dbDir = "."; - } -#endif - - log(2) << "dbDir [" << dbDir << "]" << endl; - possible.push_back( ( dbDir + "/../lib/mongo/" )); - possible.push_back( ( dbDir + "/../lib64/mongo/" )); - possible.push_back( ( dbDir + "/../lib32/mongo/" )); - possible.push_back( ( dbDir + "/" )); - possible.push_back( ( dbDir + "/lib64/mongo/" )); - possible.push_back( ( dbDir + "/lib32/mongo/" )); - } - - for ( list<string>::iterator i = possible.begin() ; i != possible.end(); i++ ) { - const string temp = *i; - const string jarDir = ((string)temp) + "jars/"; - - log(5) << "possible jarDir [" << jarDir << "]" << endl; - - path p(jarDir ); - if ( ! boost::filesystem::exists( p) ) - continue; - - log(1) << "found directory for jars : " << jarDir << endl; - return temp; - } - - problem() << "ERROR : can't find directory for jars - terminating" << endl; - exit(44); - return 0; - - }; - - -// --- - - JNIEXPORT void JNICALL java_native_say(JNIEnv * env , jclass, jobject outBuffer ) { - JNI_DEBUG( "native say called!" ); - - Message out( env->GetDirectBufferAddress( outBuffer ) , false ); - Message in; - - jniCallback( out , in ); - assert( ! out.doIFreeIt() ); - curNs = 0; - } - - JNIEXPORT jint JNICALL java_native_call(JNIEnv * env , jclass, jobject outBuffer , jobject inBuffer ) { - JNI_DEBUG( "native call called!" ); - - Message out( env->GetDirectBufferAddress( outBuffer ) , false ); - Message in; - - jniCallback( out , in ); - curNs = 0; - - JNI_DEBUG( "in.data : " << in.data ); - if ( in.data && in.data->len > 0 ) { - JNI_DEBUG( "copying data of len :" << in.data->len ); - assert( env->GetDirectBufferCapacity( inBuffer ) >= in.data->len ); - memcpy( env->GetDirectBufferAddress( inBuffer ) , in.data , in.data->len ); - - assert( ! out.doIFreeIt() ); - assert( in.doIFreeIt() ); - return in.data->len; - } - - return 0; - } - -// ---- - - void JavaJSImpl::runTest() { - - const int debug = 0; - - JavaJSImpl& JavaJS = *mongo::JavaJS; - - jlong scope = JavaJS.scopeCreate(); - jassert( scope ); - if ( debug ) out() << "got scope" << endl; - - - jlong func1 = JavaJS.functionCreate( "foo = 5.6; bar = \"eliot\"; abc = { foo : 517 }; " ); - jassert( ! JavaJS.invoke( scope , func1 ) ); - - - if ( debug ) out() << "func3 start" << endl; - jlong func3 = JavaJS.functionCreate( "function(){ z = true; } " ); - jassert( func3 ); - jassert( ! JavaJS.invoke( scope , func3 ) ); - jassert( JavaJS.scopeGetBoolean( scope , "z" ) ); - if ( debug ) out() << "func3 done" << endl; - - if ( debug ) out() << "going to get object" << endl; - BSONObj obj = JavaJS.scopeGetObject( scope , "abc" ); - if ( debug ) out() << "done getting object" << endl; - - if ( debug ) { - out() << "obj : " << obj.toString() << endl; - } - - { - time_t start = time(0); - for ( int i=0; i<5000; i++ ) { - JavaJS.scopeSetObject( scope , "obj" , &obj ); - } - time_t end = time(0); - - if ( debug ) - out() << "time : " << (unsigned) ( end - start ) << endl; - } - - if ( debug ) out() << "func4 start" << endl; - JavaJS.scopeSetObject( scope , "obj" , &obj ); - if ( debug ) out() << "\t here 1" << endl; - jlong func4 = JavaJS.functionCreate( "tojson( obj );" ); - if ( debug ) out() << "\t here 2" << endl; - jassert( ! JavaJS.invoke( scope , func4 ) ); - if ( debug ) out() << "func4 end" << endl; - - if ( debug ) out() << "func5 start" << endl; - jassert( JavaJS.scopeSetObject( scope , "c" , &obj ) ); - jlong func5 = JavaJS.functionCreate( "assert.eq( 517 , c.foo );" ); - jassert( func5 ); - jassert( ! JavaJS.invoke( scope , func5 ) ); - if ( debug ) out() << "func5 done" << endl; - - if ( debug ) out() << "func6 start" << endl; - for ( int i=0; i<100; i++ ) { - double val = i + 5; - JavaJS.scopeSetNumber( scope , "zzz" , val ); - jlong func6 = JavaJS.functionCreate( " xxx = zzz; " ); - jassert( ! JavaJS.invoke( scope , func6 ) ); - double n = JavaJS.scopeGetNumber( scope , "xxx" ); - jassert( val == n ); - } - if ( debug ) out() << "func6 done" << endl; - - jlong func7 = JavaJS.functionCreate( "return 11;" ); - jassert( ! JavaJS.invoke( scope , func7 ) ); - assert( 11 == JavaJS.scopeGetNumber( scope , "return" ) ); - - scope = JavaJS.scopeCreate(); - jlong func8 = JavaJS.functionCreate( "function(){ return 12; }" ); - jassert( ! JavaJS.invoke( scope , func8 ) ); - assert( 12 == JavaJS.scopeGetNumber( scope , "return" ) ); - - } - -#endif - -} // namespace mongo diff --git a/scripting/engine_java.h b/scripting/engine_java.h deleted file mode 100644 index b8245ba6f22..00000000000 --- a/scripting/engine_java.h +++ /dev/null @@ -1,223 +0,0 @@ -// engine_java.h - -/* 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. - */ - -/* this file contains code to call into java (into the 10gen sandbox) from inside the database */ - -#pragma once - -#include "../pch.h" - -#include <jni.h> -#include <errno.h> -#include <sys/types.h> - -#if !defined(_WIN32) -#include <dirent.h> -#endif - -#include "../db/jsobj.h" - -#include "engine.h" - -namespace mongo { - - void jasserted(const char *msg, const char *file, unsigned line); -#define jassert(_Expression) if ( ! ( _Expression ) ){ jasserted(#_Expression, __FILE__, __LINE__); } - - const char * findEd(); - const char * findEd(const char *); - const string findJars(); - - class BSONObj; - - class JavaJSImpl : public ScriptEngine { - public: - JavaJSImpl(const char * = 0); - ~JavaJSImpl(); - - jlong scopeCreate(); - int scopeInit( jlong id , const BSONObj * obj ); - int scopeSetThis( jlong id , const BSONObj * obj ); - jboolean scopeReset( jlong id ); - void scopeFree( jlong id ); - - double scopeGetNumber( jlong id , const char * field ); - string scopeGetString( jlong id , const char * field ); - jboolean scopeGetBoolean( jlong id , const char * field ); - BSONObj scopeGetObject( jlong id , const char * field ); - char scopeGetType( jlong id , const char * field ); - - int scopeSetNumber( jlong id , const char * field , double val ); - int scopeSetString( jlong id , const char * field , const char * val ); - int scopeSetObject( jlong id , const char * field , const BSONObj * obj ); - int scopeSetBoolean( jlong id , const char * field , jboolean val ); - - jlong functionCreate( const char * code ); - - /* return values: - public static final int NO_SCOPE = -1; - public static final int NO_FUNCTION = -2; - public static final int INVOKE_ERROR = -3; - public static final int INVOKE_SUCCESS = 0; - */ - int invoke( jlong scope , jlong function ); - - void printException(); - - void run( const char * js ); - - void detach( JNIEnv * env ) { - _jvm->DetachCurrentThread(); - } - - Scope * createScope(); - - void runTest(); - private: - - jobject create( const char * name ) { - jclass c = findClass( name ); - if ( ! c ) - return 0; - - jmethodID cons = _getEnv()->GetMethodID( c , "<init>" , "()V" ); - if ( ! cons ) - return 0; - - return _getEnv()->NewObject( c , cons ); - } - - jclass findClass( const char * name ) { - return _getEnv()->FindClass( name ); - } - - - private: - - JNIEnv * _getEnv(); - - JavaVM * _jvm; - JNIEnv * _mainEnv; - JavaVMInitArgs * _vmArgs; - - boost::thread_specific_ptr<JNIEnv> * _envs; - - jclass _dbhook; - jclass _dbjni; - - jmethodID _scopeCreate; - jmethodID _scopeInit; - jmethodID _scopeSetThis; - jmethodID _scopeReset; - jmethodID _scopeFree; - - jmethodID _scopeGetNumber; - jmethodID _scopeGetString; - jmethodID _scopeGetObject; - jmethodID _scopeGetBoolean; - jmethodID _scopeGuessObjectSize; - jmethodID _scopeGetType; - - jmethodID _scopeSetNumber; - jmethodID _scopeSetString; - jmethodID _scopeSetObject; - jmethodID _scopeSetBoolean; - - jmethodID _functionCreate; - - jmethodID _invoke; - - }; - - extern JavaJSImpl *JavaJS; - -// a javascript "scope" - class JavaScope : public Scope { - public: - JavaScope() { - s = JavaJS->scopeCreate(); - } - virtual ~JavaScope() { - JavaJS->scopeFree(s); - s = 0; - } - void reset() { - JavaJS->scopeReset(s); - } - - void init( BSONObj * o ) { - JavaJS->scopeInit( s , o ); - } - - void localConnect( const char * dbName ) { - setString("$client", dbName ); - } - - double getNumber(const char *field) { - return JavaJS->scopeGetNumber(s,field); - } - string getString(const char *field) { - return JavaJS->scopeGetString(s,field); - } - bool getBoolean(const char *field) { - return JavaJS->scopeGetBoolean(s,field); - } - BSONObj getObject(const char *field ) { - return JavaJS->scopeGetObject(s,field); - } - int type(const char *field ) { - return JavaJS->scopeGetType(s,field); - } - - void setThis( const BSONObj * obj ) { - JavaJS->scopeSetThis( s , obj ); - } - - void setNumber(const char *field, double val ) { - JavaJS->scopeSetNumber(s,field,val); - } - void setString(const char *field, const char * val ) { - JavaJS->scopeSetString(s,field,val); - } - void setObject(const char *field, const BSONObj& obj , bool readOnly ) { - uassert( 10211 , "only readOnly setObject supported in java" , readOnly ); - JavaJS->scopeSetObject(s,field,&obj); - } - void setBoolean(const char *field, bool val ) { - JavaJS->scopeSetBoolean(s,field,val); - } - - ScriptingFunction createFunction( const char * code ) { - return JavaJS->functionCreate( code ); - } - - int invoke( ScriptingFunction function , const BSONObj& args ) { - setObject( "args" , args , true ); - return JavaJS->invoke(s,function); - } - - string getError() { - return getString( "error" ); - } - - jlong s; - }; - - JNIEXPORT void JNICALL java_native_say(JNIEnv *, jclass, jobject outBuffer ); - JNIEXPORT jint JNICALL java_native_call(JNIEnv *, jclass, jobject outBuffer , jobject inBuffer ); - -} // namespace mongo diff --git a/scripting/engine_none.cpp b/scripting/engine_none.cpp deleted file mode 100644 index d13dbecc06e..00000000000 --- a/scripting/engine_none.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// engine_none.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. - */ - -#include "engine.h" - -namespace mongo { - void ScriptEngine::setup() { - // noop - } -} diff --git a/scripting/engine_spidermonkey.cpp b/scripting/engine_spidermonkey.cpp deleted file mode 100644 index 64fe21cd098..00000000000 --- a/scripting/engine_spidermonkey.cpp +++ /dev/null @@ -1,1749 +0,0 @@ -// engine_spidermonkey.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. - */ - -#include "pch.h" -#include "engine_spidermonkey.h" -#include "../client/dbclient.h" - -#ifndef _WIN32 -#include <boost/date_time/posix_time/posix_time.hpp> -#undef assert -#define assert MONGO_assert -#endif - -#define smuassert( cx , msg , val ) \ - if ( ! ( val ) ){ \ - JS_ReportError( cx , msg ); \ - return JS_FALSE; \ - } - -#define CHECKNEWOBJECT(xx,ctx,w) \ - if ( ! xx ){ \ - massert(13072,(string)"JS_NewObject failed: " + w ,xx); \ - } - -#define CHECKJSALLOC( newthing ) \ - massert( 13615 , "JS allocation failed, either memory leak or using too much memory" , newthing ) - -namespace mongo { - - class InvalidUTF8Exception : public UserException { - public: - InvalidUTF8Exception() : UserException( 9006 , "invalid utf8" ) { - } - }; - - 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 ); -#define smlock recursive_scoped_lock ___lk( smmutex ); - -#define GETHOLDER(x,o) ((BSONHolder*)JS_GetPrivate( x , o )) - - class BSONFieldIterator; - - class BSONHolder { - public: - - BSONHolder( BSONObj obj ) { - _obj = obj.getOwned(); - _inResolve = false; - _modified = false; - _magic = 17; - } - - ~BSONHolder() { - _magic = 18; - } - - void check() { - uassert( 10212 , "holder magic value is wrong" , _magic == 17 && _obj.isValid() ); - } - - BSONFieldIterator * it(); - - BSONObj _obj; - bool _inResolve; - char _magic; - list<string> _extra; - set<string> _removed; - bool _modified; - }; - - 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 ); - } - - class TraverseStack { - public: - TraverseStack() { - _o = 0; - _parent = 0; - } - - TraverseStack( JSObject * o , const TraverseStack * parent ) { - _o = o; - _parent = parent; - } - - TraverseStack dive( JSObject * o ) const { - if ( o ) { - uassert( 13076 , (string)"recursive toObject" , ! has( o ) ); - } - return TraverseStack( o , this ); - } - - int depth() const { - int d = 0; - const TraverseStack * s = _parent; - while ( s ) { - s = s->_parent; - d++; - } - return d; - } - - bool isTop() const { - return _parent == 0; - } - - bool has( JSObject * o ) const { - if ( ! o ) - return false; - const TraverseStack * s = this; - while ( s ) { - if ( s->_o == o ) - return true; - s = s->_parent; - } - return false; - } - - JSObject * _o; - const TraverseStack * _parent; - }; - - class Convertor : boost::noncopyable { - public: - Convertor( JSContext * cx ) { - _context = cx; - } - - string toString( JSString * so ) { - jschar * s = JS_GetStringChars( so ); - size_t srclen = JS_GetStringLength( so ); - if( srclen == 0 ) - return ""; - - size_t len = srclen * 6; // we only need *3, but see note on len below - char * dst = (char*)malloc( len ); - - 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. - - if ( !JS_EncodeCharacters( _context , s , srclen , dst , &len) ) { - StringBuilder temp; - temp << "Not proper UTF-16: "; - for ( size_t i=0; i<srclen; i++ ) { - if ( i > 0 ) - temp << ","; - temp << s[i]; - } - uasserted( 13498 , temp.str() ); - } - - string ss( dst , len ); - free( dst ); - if ( !JS_CStringsAreUTF8() ) - for( string::const_iterator i = ss.begin(); i != ss.end(); ++i ) - uassert( 10213 , "non ascii character detected", (unsigned char)(*i) <= 127 ); - return ss; - } - - string toString( jsval v ) { - return toString( JS_ValueToString( _context , v ) ); - } - - // NOTE No validation of passed in object - long long 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 toNumberInt( JSObject *o ) { - return (boost::uint32_t)(boost::int32_t) getNumber( o, "floatApprox" ); - } - - double toNumber( jsval v ) { - double d; - uassert( 10214 , "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; - } - - BSONObj toObject( JSObject * o , const TraverseStack& stack=TraverseStack() ) { - if ( ! o ) - return BSONObj(); - - if ( JS_InstanceOf( _context , o , &bson_ro_class , 0 ) ) { - BSONHolder * holder = GETHOLDER( _context , o ); - assert( holder ); - return holder->_obj.getOwned(); - } - - BSONObj orig; - if ( JS_InstanceOf( _context , o , &bson_class , 0 ) ) { - BSONHolder * holder = GETHOLDER(_context,o); - assert( 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 ); - 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 ( stack.isTop() && name == "_id" ) - continue; - - append( b , name , getProperty( o , name.c_str() ) , orig[name].type() , stack.dive( o ) ); - } - - JS_DestroyIdArray( _context , properties ); - } - - return b.obj(); - } - - BSONObj 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 getFunctionCode( JSFunction * func ) { - return toString( JS_DecompileFunction( _context , func , 0 ) ); - } - - string getFunctionCode( jsval v ) { - uassert( 10216 , "not a function" , JS_TypeOfValue( _context , v ) == JSTYPE_FUNCTION ); - return getFunctionCode( JS_ValueToFunction( _context , v ) ); - } - - 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 , s.substr( 0 , end ) , s.substr( end + 1 ) ); - } - - void append( BSONObjBuilder& b , string name , jsval val , BSONType oldType = EOO , const TraverseStack& stack=TraverseStack() ) { - //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 ); - } - } - - // ---------- to spider monkey --------- - - bool hasFunctionIdentifier( const string& code ) { - if ( code.size() < 9 || code.find( "function" ) != 0 ) - return false; - - return code[8] == ' ' || code[8] == '('; - } - - bool 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; - } - - void addRoot( JSFunction * f , const char * name ); - - JSFunction * compileFunction( const char * code, JSObject * assoc = 0 ) { - const char * gcName = "unknown"; - JSFunction * f = _compileFunction( code , assoc , gcName ); - //addRoot( f , gcName ); - return f; - } - - JSFunction * _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; - - stringstream fname; - fname << "cf_"; - static int fnum = 1; - fname << "_" << 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( '(' ); - assert( start != string::npos ); - - fname << "_f_" << trim( code.substr( 9 , start - 9 ) ); - - code = code.substr( start + 1 ); - size_t end = code.find( ')' ); - assert( 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(); - - JSFunction * func = JS_CompileFunction( _context , assoc , fname.str().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 toval( double d ) { - jsval val; - assert( JS_NewNumberValue( _context, d , &val ) ); - return val; - } - - jsval 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; - throw InvalidUTF8Exception(); - } - - CHECKJSALLOC( s ); - return STRING_TO_JSVAL( s ); - } - - JSObject * toJSObject( const BSONObj * obj , bool readOnly=false ) { - static string ref = "$ref"; - if ( ref == obj->firstElementFieldName() ) { - JSObject * o = JS_NewObject( _context , &dbref_class , NULL, NULL); - CHECKNEWOBJECT(o,_context,"toJSObject1"); - assert( 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"); - assert( JS_SetPrivate( _context , o , (void*)(new BSONHolder( obj->getOwned() ) ) ) ); - return o; - } - - jsval toval( const BSONObj* obj , bool readOnly=false ) { - JSObject * o = toJSObject( obj , readOnly ); - return OBJECT_TO_JSVAL( o ); - } - - void 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 toval( long long n ) { - JSObject * o = JS_NewObject( _context , &numberlong_class , 0 , 0 ); - makeLongObj( n, o ); - return OBJECT_TO_JSVAL( o ); - } - - void 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 toval( int n ) { - JSObject * o = JS_NewObject( _context , &numberint_class , 0 , 0 ); - makeIntObj( n, o ); - return OBJECT_TO_JSVAL( o ); - } - - jsval 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 ); - assert( 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 ); - assert( 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() ); - - 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 , ×tamp_class , 0 , 0 ); - CHECKNEWOBJECT(o,_context,"Timestamp1"); - setProperty( o , "t" , toval( (double)(e.timestampTime()) ) ); - 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 ); - assert( data ); - assert( 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; - } - - // ------- object helpers ------ - - JSObject * getJSObject( JSObject * o , const char * name ) { - jsval v; - assert( JS_GetProperty( _context , o , name , &v ) ); - return JSVAL_TO_OBJECT( v ); - } - - JSObject * getGlobalObject( const char * name ) { - return getJSObject( JS_GetGlobalObject( _context ) , name ); - } - - JSObject * getGlobalPrototype( const char * name ) { - return getJSObject( getGlobalObject( name ) , "prototype" ); - } - - 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( 10219 , "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 ) ); - } - - string typeString( jsval v ) { - JSType t = JS_TypeOfValue( _context , v ); - return JS_GetTypeName( _context , t ); - } - - bool getBoolean( JSObject * o , const char * field ) { - return toBoolean( getProperty( o , field ) ); - } - - double getNumber( JSObject * o , const char * field ) { - return toNumber( getProperty( o , field ) ); - } - - string getString( JSObject * o , const char * field ) { - return toString( getProperty( o , field ) ); - } - - JSClass * getClass( JSObject * o , const char * field ) { - jsval v; - assert( JS_GetProperty( _context , o , field , &v ) ); - if ( ! JSVAL_IS_OBJECT( v ) ) - return 0; - return JS_GET_CLASS( _context , JSVAL_TO_OBJECT( v ) ); - } - - JSContext * _context; - - - }; - - - void bson_finalize( JSContext * cx , JSObject * obj ) { - BSONHolder * o = GETHOLDER( cx , obj ); - if ( o ) { - delete o; - assert( JS_SetPrivate( cx , obj , 0 ) ); - } - } - - JSBool bson_enumerate( JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp ) { - - 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); - assert( 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; - } - - JSBool noaccess( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) { - 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; - } - - JSClass bson_ro_class = { - "bson_ro_object" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE , - noaccess, noaccess, JS_PropertyStub, noaccess, - (JSEnumerateOp)bson_enumerate, (JSResolveOp)(&resolveBSONField) , JS_ConvertStub, bson_finalize , - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSBool bson_cons( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - cerr << "bson_cons : shouldn't be here!" << endl; - JS_ReportError( cx , "can't construct bson object" ); - return JS_FALSE; - } - - JSFunctionSpec bson_functions[] = { - { 0 } - }; - - JSBool bson_add_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) { - 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; - } - return JS_TRUE; - } - - - JSBool mark_modified( JSContext *cx, JSObject *obj, jsval idval, jsval *vp) { - 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 ) ); - 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; - } - - JSClass bson_class = { - "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 - }; - - 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 - }; - - // --- global helpers --- - - JSBool hexToBinData(JSContext * cx, jsval *rval, int subtype, string s) { - JSObject * o = JS_NewObject( cx , &bindata_class , 0 , 0 ); - CHECKNEWOBJECT(o,_context,"Bindata_BinData1"); - int len = s.size() / 2; - char * data = new char[len]; - char *p = data; - const char *src = s.c_str(); - for( size_t i = 0; i+1 < s.size(); i += 2 ) { - *p++ = fromHex(src + i); - } - assert( JS_SetPrivate( cx , o , new BinDataHolder( data , len ) ) ); - Convertor c(cx); - c.setProperty( o, "len", c.toval((double)len) ); - c.setProperty( o, "type", c.toval((double)subtype) ); - *rval = OBJECT_TO_JSVAL( o ); - delete data; - return JS_TRUE; - } - - JSBool _HexData( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - if ( argc != 2 ) { - JS_ReportError( cx , "HexData needs 2 arguments -- HexData(subtype,hexstring)" ); - return JS_FALSE; - } - int type = (int)c.toNumber( argv[ 0 ] ); - if ( type == 2 ) { - JS_ReportError( cx , "BinData subtype 2 is deprecated" ); - return JS_FALSE; - } - string s = c.toString(argv[1]); - return hexToBinData(cx, rval, type, s); - } - - JSBool _UUID( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - if ( argc != 1 ) { - JS_ReportError( cx , "UUID needs argument -- UUID(hexstring)" ); - return JS_FALSE; - } - string s = c.toString(argv[0]); - if( s.size() != 32 ) { - JS_ReportError( cx , "bad UUID hex string len" ); - return JS_FALSE; - } - return hexToBinData(cx, rval, 3, s); - } - - JSBool _MD5( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - if ( argc != 1 ) { - JS_ReportError( cx , "MD5 needs argument -- MD5(hexstring)" ); - return JS_FALSE; - } - string s = c.toString(argv[0]); - if( s.size() != 32 ) { - JS_ReportError( cx , "bad MD5 hex string len" ); - return JS_FALSE; - } - return hexToBinData(cx, rval, 5, s); - } - - JSBool native_print( JSContext * cx , JSObject * obj , uintN argc, jsval *argv, jsval *rval ) { - stringstream ss; - Convertor c( cx ); - for ( uintN i=0; i<argc; i++ ) { - if ( i > 0 ) - ss << " "; - ss << c.toString( argv[i] ); - } - ss << "\n"; - Logstream::logLockless( ss.str() ); - return JS_TRUE; - } - - JSBool native_helper( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ) { - Convertor c(cx); - - NativeFunction func = (NativeFunction)((long long)c.getNumber( obj , "x" ) ); - void* data = (void*)((long long)c.getNumber( obj , "y" ) ); - assert( func ); - - 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 ) { - JS_ReportError( cx , e.what() ); - return JS_FALSE; - } - - 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; - } - - 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) { - 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; - - 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 ); - 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 ) { - assert( JS_EnterLocalRootScope( cx ) ); - Convertor c( cx ); - - BSONHolder * holder = GETHOLDER( cx , obj ); - if ( ! holder ) { - // static init - *objp = 0; - JS_LeaveLocalRootScope( cx ); - return JS_TRUE; - } - holder->check(); - - 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; - try { - val = c.toval( e ); - } - catch ( InvalidUTF8Exception& ) { - JS_LeaveLocalRootScope( cx ); - JS_ReportError( cx , "invalid utf8" ); - return JS_FALSE; - } - - assert( ! holder->_inResolve ); - holder->_inResolve = true; - assert( 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 ); - 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; - } - - int x = 0; - assert( x = 1 ); - uassert( 10222 , "assert not being executed" , x == 1 ); - } - - ~SMEngine() { - JS_DestroyRuntime( _runtime ); - JS_ShutDown(); - } - - Scope * createScope(); - - void runTest(); - - virtual bool utf8Ok() const { return JS_CStringsAreUTF8(); } - -#ifdef XULRUNNER - JSClass * _dateClass; - JSClass * _regexClass; -#endif - - - private: - JSRuntime * _runtime; - friend class SMScope; - }; - - SMEngine * globalSMEngine; - - - void ScriptEngine::setup() { - globalSMEngine = new SMEngine(); - globalScriptEngine = globalSMEngine; - } - - - // ------ scope ------ - - - JSBool no_gc(JSContext *cx, JSGCStatus status) { - return JS_FALSE; - } - - JSBool yes_gc(JSContext *cx, JSGCStatus status) { - return JS_TRUE; - } - - class SMScope : public Scope { - public: - SMScope() : _this( 0 ) , _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() { - smlock; - uassert( 10223 , "deleted SMScope twice?" , _convertor ); - - for ( list<void*>::iterator i=_roots.begin(); i != _roots.end(); i++ ) { - JS_RemoveRoot( _context , *i ); - } - _roots.clear(); - - 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 reset() { - smlock; - assert( _convertor ); - return; - if ( _this ) { - JS_RemoveRoot( _context , &_this ); - _this = 0; - } - currentScope.reset( this ); - _error = ""; - } - - void addRoot( void * root , const char * name ) { - JS_AddNamedRoot( _context , root , name ); - _roots.push_back( root ); - } - - void 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() ); - } - - } - - void externalSetup() { - smlock; - uassert( 10224 , "already local connected" , ! _localConnect ); - if ( _externalSetup ) - return; - initMongoJS( this , _context , _global , false ); - _externalSetup = true; - } - - void 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(); - } - - // ----- getters ------ - double getNumber( const char *field ) { - smlock; - jsval val; - assert( JS_GetProperty( _context , _global , field , &val ) ); - return _convertor->toNumber( val ); - } - - string getString( const char *field ) { - smlock; - jsval val; - assert( JS_GetProperty( _context , _global , field , &val ) ); - JSString * s = JS_ValueToString( _context , val ); - return _convertor->toString( s ); - } - - bool getBoolean( const char *field ) { - smlock; - return _convertor->getBoolean( _global , field ); - } - - BSONObj getObject( const char *field ) { - smlock; - return _convertor->toObject( _convertor->getProperty( _global , field ) ); - } - - JSObject * getJSObject( const char * field ) { - smlock; - return _convertor->getJSObject( _global , field ); - } - - int type( const char *field ) { - smlock; - jsval val; - assert( 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; - } - - // ----- setters ------ - - void setElement( const char *field , const BSONElement& val ) { - smlock; - jsval v = _convertor->toval( val ); - assert( JS_SetProperty( _context , _global , field , &v ) ); - } - - void setNumber( const char *field , double val ) { - smlock; - jsval v = _convertor->toval( val ); - assert( JS_SetProperty( _context , _global , field , &v ) ); - } - - void setString( const char *field , const char * val ) { - smlock; - jsval v = _convertor->toval( val ); - assert( JS_SetProperty( _context , _global , field , &v ) ); - } - - void setObject( const char *field , const BSONObj& obj , bool readOnly ) { - smlock; - jsval v = _convertor->toval( &obj , readOnly ); - JS_SetProperty( _context , _global , field , &v ); - } - - void setBoolean( const char *field , bool val ) { - smlock; - jsval v = BOOLEAN_TO_JSVAL( val ); - assert( JS_SetProperty( _context , _global , field , &v ) ); - } - - void 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 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 rename( const char * from , const char * to ) { - smlock; - jsval v; - assert( JS_GetProperty( _context , _global , from , &v ) ); - assert( JS_SetProperty( _context , _global , to , &v ) ); - v = JSVAL_VOID; - assert( JS_SetProperty( _context , _global , from , &v ) ); - } - - // ---- functions ----- - - ScriptingFunction _createFunction( const char * code ) { - smlock; - precall(); - return (ScriptingFunction)_convertor->compileFunction( code ); - } - - struct TimeoutSpec { - boost::posix_time::ptime start; - boost::posix_time::time_duration timeout; - int count; - }; - - // should not generate exceptions, as those can be caught in - // javascript code; returning false without an exception exits - // immediately - static JSBool _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; - - } - - static JSBool interrupt( JSContext *cx, JSScript *script ) { - return _interrupt( cx ); - } - - void 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 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 precall() { - _error = ""; - currentScope.reset( this ); - } - - bool exec( const StringData& code , const string& name = "(anon)" , bool printResult = false , bool reportError = true , bool assertOnError = true, int timeoutMs = 0 ) { - smlock; - precall(); - - jsval ret = JSVAL_VOID; - - installInterrupt( timeoutMs ); - JSBool worked = JS_EvaluateScript( _context , _global , code.data() , code.size() , name.c_str() , 1 , &ret ); - 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 , str::stream() << name + " exec failed: " << _error , worked || ! assertOnError ); - - if ( reportError && ! _error.empty() ) { - // cout << "exec error: " << _error << endl; - // already printed in reportError, so... TODO - } - - if ( worked ) - _convertor->setProperty( _global , "__lastres__" , ret ); - - if ( worked && printResult && ! JSVAL_IS_VOID( ret ) ) - cout << _convertor->toString( ret ) << endl; - - return worked; - } - - int invoke( JSFunction * func , const BSONObj* args, const BSONObj* recv, int timeoutMs , bool ignoreReturn, bool readOnlyArgs, bool readOnlyRecv ) { - smlock; - precall(); - - assert( JS_EnterLocalRootScope( _context ) ); - - int nargs = args ? args->nFields() : 0; - scoped_array<jsval> smargsPtr( new jsval[nargs] ); - 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 compatability - } - - 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 ) { - assert( JS_SetProperty( _context , _global , "return" , &rval ) ); - } - - return 0; - } - - int invoke( ScriptingFunction funcAddr , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = 0, bool readOnlyArgs = false, bool readOnlyRecv = false ) { - return invoke( (JSFunction*)funcAddr , args , recv, timeoutMs , ignoreReturn, readOnlyArgs, readOnlyRecv); - } - - void gotError( string s ) { - _error = s; - } - - string getError() { - return _error; - } - - void injectNative( const char *field, NativeFunction func, void* data ) { - smlock; - string name = field; - _convertor->setProperty( _global , (name + "_").c_str() , _convertor->toval( (double)(long long)func ) ); - - stringstream code; - if (data) { - _convertor->setProperty( _global , (name + "_data_").c_str() , _convertor->toval( (double)(long long)data ) ); - code << field << "_" << " = { x : " << field << "_ , y: " << field << "_data_ }; "; - } else { - code << field << "_" << " = { x : " << field << "_ }; "; - } - code << field << " = function(){ return nativeHelper.apply( " << field << "_ , arguments ); }"; - exec( code.str() ); - } - - virtual void gc() { - smlock; - JS_GC( _context ); - } - - JSContext *SavedContext() const { return _context; } - - private: - - void _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 - } - - JSContext * _context; - Convertor * _convertor; - - JSObject * _global; - JSObject * _this; - - string _error; - list<void*> _roots; - - bool _externalSetup; - bool _localConnect; - - set<string> _initFieldNames; - - }; - - /* used to make the logging not overly chatty in the mongo shell. */ - extern bool isShell; - - 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; - } - - tlog() << ss.str() << endl; - - if ( currentScope.get() ) { - currentScope->gotError( ss.str() ); - } - } - - 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 << "load [" << filename << "]" << endl; - - if ( ! s->execFile( filename , false , true , false ) ) { - JS_ReportError( cx , ((string)"error loading js file: " + filename ).c_str() ); - return JS_FALSE; - } - } - - 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' )" ); - - assert( (string)"abc" == trim( "abc" ) ); - assert( (string)"abc" == trim( " abc" ) ); - assert( (string)"abc" == trim( "abc " ) ); - assert( (string)"abc" == trim( " abc " ) ); - - } - - Scope * SMEngine::createScope() { - return new SMScope(); - } - - void Convertor::addRoot( JSFunction * f , const char * name ) { - if ( ! f ) - return; - - SMScope * scope = currentScope.get(); - uassert( 10229 , "need a scope" , scope ); - - JSObject * o = JS_GetFunctionObject( f ); - assert( o ); - scope->addRoot( &o , name ); - } - -} - -#include "sm_db.cpp" diff --git a/scripting/engine_spidermonkey.h b/scripting/engine_spidermonkey.h deleted file mode 100644 index 9fd430d853d..00000000000 --- a/scripting/engine_spidermonkey.h +++ /dev/null @@ -1,105 +0,0 @@ -// engine_spidermonkey.h - -/* 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. - */ - -#pragma once - -#include "engine.h" - -// START inc hacking - -#ifdef WIN32 -#include "jstypes.h" -#undef JS_PUBLIC_API -#undef JS_PUBLIC_DATA -#define JS_PUBLIC_API(t) t __cdecl -#define JS_PUBLIC_DATA(t) t -#endif - -#include "jsapi.h" -#include "jsobj.h" -#include "jsdate.h" -#include "jsregexp.h" - -// END inc hacking - -// -- SM 1.6 hacks --- -#ifndef JSCLASS_GLOBAL_FLAGS -#error old version of spider monkey ( probably 1.6 ) you should upgrade to at least 1.7 -#endif -// -- END SM 1.6 hacks --- - -#ifdef JSVAL_IS_TRACEABLE -#define SM18 -#endif - -#ifdef XULRUNNER -#define SM181 -#endif - -namespace mongo { - - class SMScope; - class Convertor; - - extern JSClass bson_class; - extern JSClass bson_ro_class; - - extern JSClass object_id_class; - extern JSClass dbpointer_class; - extern JSClass dbref_class; - extern JSClass bindata_class; - extern JSClass timestamp_class; - extern JSClass numberlong_class; - extern JSClass numberint_class; - extern JSClass minkey_class; - extern JSClass maxkey_class; - - // internal things - void dontDeleteScope( SMScope * s ) {} - void errorReporter( JSContext *cx, const char *message, JSErrorReport *report ); - extern boost::thread_specific_ptr<SMScope> currentScope; - - // bson - JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ); - - - // mongo - void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ); - bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , jsval val , JSObject * o ); - -#define JSVAL_IS_OID(v) ( JSVAL_IS_OBJECT( v ) && JS_InstanceOf( cx , JSVAL_TO_OBJECT( v ) , &object_id_class , 0 ) ) - - bool isDate( JSContext * cx , JSObject * o ); - - // JS private data must be 2byte aligned, so we use a holder to refer to an unaligned pointer. - struct BinDataHolder { - BinDataHolder( const char *c, int copyLen = -1 ) : - c_( const_cast< char * >( c ) ), - iFree_( copyLen != -1 ) { - if ( copyLen != -1 ) { - c_ = (char*)malloc( copyLen ); - memcpy( c_, c, copyLen ); - } - } - ~BinDataHolder() { - if ( iFree_ ) - free( c_ ); - } - char *c_; - bool iFree_; - }; -} diff --git a/scripting/engine_v8.cpp b/scripting/engine_v8.cpp deleted file mode 100644 index 2173391893e..00000000000 --- a/scripting/engine_v8.cpp +++ /dev/null @@ -1,1547 +0,0 @@ -//engine_v8.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. - */ - -#if defined(_WIN32) -/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with - our usage of boost */ -#include "boost/cstdint.hpp" -using namespace boost; -#define V8STDINT_H_ -#endif - -#include "engine_v8.h" - -#include "v8_wrapper.h" -#include "v8_utils.h" -#include "v8_db.h" - -#define V8_SIMPLE_HEADER V8Lock l; HandleScope handle_scope; Context::Scope context_scope( _context ); - -namespace mongo { - - // guarded by v8 mutex - map< unsigned, int > __interruptSpecToThreadId; - - /** - * Unwraps a BSONObj from the JS wrapper - */ - static BSONObj* unwrapBSONObj(const Handle<v8::Object>& obj) { - Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0)); - if (field.IsEmpty() || !field->IsExternal()) - return 0; - void* ptr = field->Value(); - return (BSONObj*)ptr; - } - - static void weakRefBSONCallback(v8::Persistent<v8::Value> p, void* scope) { - // should we lock here? no idea, and no doc from v8 of course - HandleScope handle_scope; - if (!p.IsNearDeath()) - return; - Handle<External> field = Handle<External>::Cast(p->ToObject()->GetInternalField(0)); - BSONObj* data = (BSONObj*) field->Value(); - delete data; - p.Dispose(); - } - - Persistent<v8::Object> V8Scope::wrapBSONObject(Local<v8::Object> obj, BSONObj* data) { - obj->SetInternalField(0, v8::External::New(data)); - Persistent<v8::Object> p = Persistent<v8::Object>::New(obj); - p.MakeWeak(this, weakRefBSONCallback); - return p; - } - - static void weakRefArrayCallback(v8::Persistent<v8::Value> p, void* scope) { - // should we lock here? no idea, and no doc from v8 of course - HandleScope handle_scope; - if (!p.IsNearDeath()) - return; - Handle<External> field = Handle<External>::Cast(p->ToObject()->GetInternalField(0)); - char* data = (char*) field->Value(); - delete [] data; - p.Dispose(); - } - - Persistent<v8::Object> V8Scope::wrapArrayObject(Local<v8::Object> obj, char* data) { - obj->SetInternalField(0, v8::External::New(data)); - Persistent<v8::Object> p = Persistent<v8::Object>::New(obj); - p.MakeWeak(this, weakRefArrayCallback); - return p; - } - - static Handle<v8::Value> namedGet(Local<v8::String> name, const v8::AccessorInfo &info) { - // all properties should be set, otherwise means builtin or deleted - if (!(info.This()->HasRealNamedProperty(name))) - return v8::Handle<v8::Value>(); - - Handle<v8::Value> val = info.This()->GetRealNamedProperty(name); - if (!val->IsUndefined()) { - // value already cached - return val; - } - - string key = toSTLString(name); - BSONObj *obj = unwrapBSONObj(info.Holder()); - BSONElement elmt = obj->getField(key.c_str()); - if (elmt.eoo()) - return Handle<Value>(); - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - val = scope->mongoToV8Element(elmt, false); - info.This()->ForceSet(name, val); - - if (elmt.type() == mongo::Object || elmt.type() == mongo::Array) { - // if accessing a subobject, it may get modified and base obj would not know - // have to set base as modified, which means some optim is lost - info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true)); - } - return val; - } - - static Handle<v8::Value> namedGetRO(Local<v8::String> name, const v8::AccessorInfo &info) { - string key = toSTLString(name); - BSONObj *obj = unwrapBSONObj(info.Holder()); - BSONElement elmt = obj->getField(key.c_str()); - if (elmt.eoo()) - return Handle<Value>(); - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - Handle<v8::Value> val = scope->mongoToV8Element(elmt, true); - return val; - } - - static Handle<v8::Value> namedSet(Local<v8::String> name, Local<v8::Value> value_obj, const v8::AccessorInfo& info) { - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true)); - return Handle<Value>(); - } - - static Handle<v8::Array> namedEnumerator(const AccessorInfo &info) { - BSONObj *obj = unwrapBSONObj(info.Holder()); - Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields())); - int i = 0; - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - // note here that if keys are parseable number, v8 will access them using index - for ( BSONObjIterator it(*obj); it.more(); ++i) { - const BSONElement& f = it.next(); -// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName()))); - Handle<v8::String> name = scope->getV8Str(f.fieldName()); - arr->Set(i, name); - } - return arr; - } - - Handle<Boolean> namedDelete( Local<v8::String> property, const AccessorInfo& info ) { - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true)); - return Handle<Boolean>(); - } - -// v8::Handle<v8::Integer> namedQuery(Local<v8::String> property, const AccessorInfo& info) { -// string key = ToString(property); -// return v8::Integer::New(None); -// } - - static Handle<v8::Value> indexedGet(uint32_t index, const v8::AccessorInfo &info) { - // all properties should be set, otherwise means builtin or deleted - if (!(info.This()->HasRealIndexedProperty(index))) - return v8::Handle<v8::Value>(); - - StringBuilder ss; - ss << index; - string key = ss.str(); - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - // cannot get v8 to properly cache the indexed val in the js object -// Handle<v8::String> name = scope->getV8Str(key); -// // v8 API really confusing here, must check existence on index, but then fetch with name -// if (info.This()->HasRealIndexedProperty(index)) { -// Handle<v8::Value> val = info.This()->GetRealNamedProperty(name); -// if (!val.IsEmpty() && !val->IsNull()) -// return val; -// } - BSONObj *obj = unwrapBSONObj(info.Holder()); - BSONElement elmt = obj->getField(key); - if (elmt.eoo()) - return Handle<Value>(); - Handle<Value> val = scope->mongoToV8Element(elmt, false); -// info.This()->ForceSet(name, val); - - if (elmt.type() == mongo::Object || elmt.type() == mongo::Array) { - // if accessing a subobject, it may get modified and base obj would not know - // have to set base as modified, which means some optim is lost - info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true)); - } - return val; - } - - Handle<Boolean> indexedDelete( uint32_t index, const AccessorInfo& info ) { - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true)); - return Handle<Boolean>(); - } - - static Handle<v8::Value> indexedGetRO(uint32_t index, const v8::AccessorInfo &info) { - StringBuilder ss; - ss << index; - string key = ss.str(); - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - // cannot get v8 to properly cache the indexed val in the js object -// Handle<v8::String> name = scope->getV8Str(key); -// // v8 API really confusing here, must check existence on index, but then fetch with name -// if (info.This()->HasRealIndexedProperty(index)) { -// Handle<v8::Value> val = info.This()->GetRealNamedProperty(name); -// if (!val.IsEmpty() && !val->IsNull()) -// return val; -// } - BSONObj *obj = unwrapBSONObj(info.Holder()); - BSONElement elmt = obj->getField(key); - if (elmt.eoo()) - return Handle<Value>(); - Handle<Value> val = scope->mongoToV8Element(elmt, true); -// info.This()->ForceSet(name, val); - return val; - } - - static Handle<v8::Value> indexedSet(uint32_t index, Local<v8::Value> value_obj, const v8::AccessorInfo& info) { - Local< External > scp = External::Cast( *info.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - info.This()->SetHiddenValue(scope->V8STR_MODIFIED, v8::Boolean::New(true)); - return Handle<Value>(); - } - -// static Handle<v8::Array> indexedEnumerator(const AccessorInfo &info) { -// BSONObj *obj = unwrapBSONObj(info.Holder()); -// Handle<v8::Array> arr = Handle<v8::Array>(v8::Array::New(obj->nFields())); -// Local< External > scp = External::Cast( *info.Data() ); -// V8Scope* scope = (V8Scope*)(scp->Value()); -// int i = 0; -// for ( BSONObjIterator it(*obj); it.more(); ++i) { -// const BSONElement& f = it.next(); -//// arr->Set(i, v8::String::NewExternal(new ExternalString(f.fieldName()))); -// arr->Set(i, scope->getV8Str(f.fieldName())); -// } -// return arr; -// } - - Handle<Value> NamedReadOnlySet( Local<v8::String> property, Local<Value> value, const AccessorInfo& info ) { - string key = toSTLString(property); - cout << "cannot write property " << key << " to read-only object" << endl; - return value; - } - - Handle<Boolean> NamedReadOnlyDelete( Local<v8::String> property, const AccessorInfo& info ) { - string key = toSTLString(property); - cout << "cannot delete property " << key << " from read-only object" << endl; - return Boolean::New( false ); - } - - Handle<Value> IndexedReadOnlySet( uint32_t index, Local<Value> value, const AccessorInfo& info ) { - cout << "cannot write property " << index << " to read-only array" << endl; - return value; - } - - Handle<Boolean> IndexedReadOnlyDelete( uint32_t index, const AccessorInfo& info ) { - cout << "cannot delete property " << index << " from read-only array" << endl; - return Boolean::New( false ); - } - - // --- engine --- - - V8ScriptEngine::V8ScriptEngine() { - } - - V8ScriptEngine::~V8ScriptEngine() { - } - - void ScriptEngine::setup() { - if ( !globalScriptEngine ) { - globalScriptEngine = new V8ScriptEngine(); - } - } - - void V8ScriptEngine::interrupt( unsigned opSpec ) { - v8::Locker l; - if ( __interruptSpecToThreadId.count( opSpec ) ) { - V8::TerminateExecution( __interruptSpecToThreadId[ opSpec ] ); - } - } - void V8ScriptEngine::interruptAll() { - v8::Locker l; - vector< int > toKill; // v8 mutex could potentially be yielded during the termination call - for( map< unsigned, int >::const_iterator i = __interruptSpecToThreadId.begin(); i != __interruptSpecToThreadId.end(); ++i ) { - toKill.push_back( i->second ); - } - for( vector< int >::const_iterator i = toKill.begin(); i != toKill.end(); ++i ) { - V8::TerminateExecution( *i ); - } - } - - // --- scope --- - - V8Scope::V8Scope( V8ScriptEngine * engine ) - : _engine( engine ) , - _connectState( NOT ) { - - V8Lock l; - HandleScope handleScope; - _context = Context::New(); - Context::Scope context_scope( _context ); - _global = Persistent< v8::Object >::New( _context->Global() ); - _emptyObj = Persistent< v8::Object >::New( v8::Object::New() ); - - // initialize lazy object template - lzObjectTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); - lzObjectTemplate->SetInternalFieldCount( 1 ); - lzObjectTemplate->SetNamedPropertyHandler(namedGet, namedSet, 0, namedDelete, 0, v8::External::New(this)); - lzObjectTemplate->SetIndexedPropertyHandler(indexedGet, indexedSet, 0, indexedDelete, 0, v8::External::New(this)); - - roObjectTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); - roObjectTemplate->SetInternalFieldCount( 1 ); - roObjectTemplate->SetNamedPropertyHandler(namedGetRO, NamedReadOnlySet, 0, NamedReadOnlyDelete, namedEnumerator, v8::External::New(this)); - roObjectTemplate->SetIndexedPropertyHandler(indexedGetRO, IndexedReadOnlySet, 0, IndexedReadOnlyDelete, 0, v8::External::New(this)); - - // initialize lazy array template - // unfortunately it is not possible to create true v8 array from a template - // this means we use an object template and copy methods over - // this it creates issues when calling certain methods that check array type - lzArrayTemplate = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); - lzArrayTemplate->SetInternalFieldCount( 1 ); - lzArrayTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0, v8::External::New(this)); - - internalFieldObjects = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); - internalFieldObjects->SetInternalFieldCount( 1 ); - - V8STR_CONN = getV8Str( "_conn" ); - V8STR_ID = getV8Str( "_id" ); - V8STR_LENGTH = getV8Str( "length" ); - V8STR_LEN = getV8Str( "len" ); - V8STR_TYPE = getV8Str( "type" ); - V8STR_ISOBJECTID = getV8Str( "isObjectId" ); - V8STR_RETURN = getV8Str( "return" ); - V8STR_ARGS = getV8Str( "args" ); - V8STR_T = getV8Str( "t" ); - V8STR_I = getV8Str( "i" ); - V8STR_EMPTY = getV8Str( "" ); - V8STR_MINKEY = getV8Str( "$MinKey" ); - V8STR_MAXKEY = getV8Str( "$MaxKey" ); - V8STR_NUMBERLONG = getV8Str( "__NumberLong" ); - V8STR_NUMBERINT = getV8Str( "__NumberInt" ); - V8STR_DBPTR = getV8Str( "__DBPointer" ); - V8STR_BINDATA = getV8Str( "__BinData" ); - V8STR_NATIVE_FUNC = getV8Str( "_native_function" ); - V8STR_NATIVE_DATA = getV8Str( "_native_data" ); - V8STR_V8_FUNC = getV8Str( "_v8_function" ); - V8STR_RO = getV8Str( "_ro" ); - V8STR_MODIFIED = getV8Str( "_mod" ); - - injectV8Function("print", Print); - injectV8Function("version", Version); - injectV8Function("load", load); - - _wrapper = Persistent< v8::Function >::New( getObjectWrapperTemplate(this)->GetFunction() ); - - injectV8Function("gc", GCV8); - - installDBTypes( this, _global ); - } - - V8Scope::~V8Scope() { - V8Lock l; - Context::Scope context_scope( _context ); - _wrapper.Dispose(); - _emptyObj.Dispose(); - for( unsigned i = 0; i < _funcs.size(); ++i ) - _funcs[ i ].Dispose(); - _funcs.clear(); - _global.Dispose(); - _context.Dispose(); - std::map <string, v8::Persistent <v8::String> >::iterator it = _strCache.begin(); - std::map <string, v8::Persistent <v8::String> >::iterator end = _strCache.end(); - while (it != end) { - it->second.Dispose(); - ++it; - } - lzObjectTemplate.Dispose(); - lzArrayTemplate.Dispose(); - roObjectTemplate.Dispose(); - internalFieldObjects.Dispose(); - } - - /** - * JS Callback that will call a c++ function with BSON arguments. - */ - Handle< Value > V8Scope::nativeCallback( V8Scope* scope, const Arguments &args ) { - V8Lock l; - HandleScope handle_scope; - Local< External > f = External::Cast( *args.Callee()->Get( scope->V8STR_NATIVE_FUNC ) ); - NativeFunction function = (NativeFunction)(f->Value()); - Local< External > data = External::Cast( *args.Callee()->Get( scope->V8STR_NATIVE_DATA ) ); - BSONObjBuilder b; - for( int i = 0; i < args.Length(); ++i ) { - stringstream ss; - ss << i; - scope->v8ToMongoElement( b, scope->V8STR_EMPTY, ss.str(), args[ i ] ); - } - BSONObj nativeArgs = b.obj(); - BSONObj ret; - try { - ret = function( nativeArgs, data->Value() ); - } - catch( const std::exception &e ) { - return v8::ThrowException(v8::String::New(e.what())); - } - catch( ... ) { - return v8::ThrowException(v8::String::New("unknown exception")); - } - return handle_scope.Close( scope->mongoToV8Element( ret.firstElement() ) ); - } - - Handle< Value > V8Scope::load( V8Scope* scope, const Arguments &args ) { - Context::Scope context_scope(scope->_context); - for (int i = 0; i < args.Length(); ++i) { - std::string filename(toSTLString(args[i])); - if (!scope->execFile(filename, false , true , false)) { - return v8::ThrowException(v8::String::New((std::string("error loading file: ") + filename).c_str())); - } - } - return v8::True(); - } - - /** - * JS Callback that will call a c++ function with the v8 scope and v8 arguments. - * Handles interrupts, exception handling, etc - * - * The implementation below assumes that SERVER-1816 has been fixed - in - * particular, interrupted() must return true if an interrupt was ever - * sent; currently that is not the case if a new killop overwrites the data - * for an old one - */ - v8::Handle< v8::Value > V8Scope::v8Callback( const v8::Arguments &args ) { - disableV8Interrupt(); // we don't want to have to audit all v8 calls for termination exceptions, so we don't allow these exceptions during the callback - if ( globalScriptEngine->interrupted() ) { - v8::V8::TerminateExecution(); // experimentally it seems that TerminateExecution() will override the return value - return v8::Undefined(); - } - Local< External > f = External::Cast( *args.Callee()->Get( v8::String::New( "_v8_function" ) ) ); - v8Function function = (v8Function)(f->Value()); - Local< External > scp = External::Cast( *args.Data() ); - V8Scope* scope = (V8Scope*)(scp->Value()); - - v8::Handle< v8::Value > ret; - string exception; - try { - ret = function( scope, args ); - } - catch( const std::exception &e ) { - exception = e.what(); - } - catch( ... ) { - exception = "unknown exception"; - } - enableV8Interrupt(); - if ( globalScriptEngine->interrupted() ) { - v8::V8::TerminateExecution(); - return v8::Undefined(); - } - if ( !exception.empty() ) { - // technically, ThrowException is supposed to be the last v8 call before returning - ret = v8::ThrowException( v8::String::New( exception.c_str() ) ); - } - return ret; - } - - // ---- global stuff ---- - - void V8Scope::init( const BSONObj * data ) { - V8Lock l; - if ( ! data ) - return; - - BSONObjIterator i( *data ); - while ( i.more() ) { - BSONElement e = i.next(); - setElement( e.fieldName() , e ); - } - } - - void V8Scope::setNumber( const char * field , double val ) { - V8_SIMPLE_HEADER - _global->Set( getV8Str( field ) , v8::Number::New( val ) ); - } - - void V8Scope::setString( const char * field , const char * val ) { - V8_SIMPLE_HEADER - _global->Set( getV8Str( field ) , v8::String::New( val ) ); - } - - void V8Scope::setBoolean( const char * field , bool val ) { - V8_SIMPLE_HEADER - _global->Set( getV8Str( field ) , v8::Boolean::New( val ) ); - } - - void V8Scope::setElement( const char *field , const BSONElement& e ) { - V8_SIMPLE_HEADER - _global->Set( getV8Str( field ) , mongoToV8Element( e ) ); - } - - void V8Scope::setObject( const char *field , const BSONObj& obj , bool readOnly) { - V8_SIMPLE_HEADER - // Set() accepts a ReadOnly parameter, but this just prevents the field itself - // from being overwritten and doesn't protect the object stored in 'field'. - _global->Set( getV8Str( field ) , mongoToLZV8( obj, false, readOnly) ); - } - - int V8Scope::type( const char *field ) { - V8_SIMPLE_HEADER - Handle<Value> v = get( field ); - if ( v->IsNull() ) - return jstNULL; - if ( v->IsUndefined() ) - return Undefined; - if ( v->IsString() ) - return String; - if ( v->IsFunction() ) - return Code; - if ( v->IsArray() ) - return Array; - if ( v->IsBoolean() ) - return Bool; - // needs to be explicit NumberInt to use integer -// if ( v->IsInt32() ) -// return NumberInt; - if ( v->IsNumber() ) - return NumberDouble; - if ( v->IsExternal() ) { - uassert( 10230 , "can't handle external yet" , 0 ); - return -1; - } - if ( v->IsDate() ) - return Date; - if ( v->IsObject() ) - return Object; - - throw UserException( 12509, (string)"don't know what this is: " + field ); - } - - v8::Handle<v8::Value> V8Scope::get( const char * field ) { - return _global->Get( getV8Str( field ) ); - } - - double V8Scope::getNumber( const char *field ) { - V8_SIMPLE_HEADER - return get( field )->ToNumber()->Value(); - } - - int V8Scope::getNumberInt( const char *field ) { - V8_SIMPLE_HEADER - return get( field )->ToInt32()->Value(); - } - - long long V8Scope::getNumberLongLong( const char *field ) { - V8_SIMPLE_HEADER - return get( field )->ToInteger()->Value(); - } - - string V8Scope::getString( const char *field ) { - V8_SIMPLE_HEADER - return toSTLString( get( field ) ); - } - - bool V8Scope::getBoolean( const char *field ) { - V8_SIMPLE_HEADER - return get( field )->ToBoolean()->Value(); - } - - BSONObj V8Scope::getObject( const char * field ) { - V8_SIMPLE_HEADER - Handle<Value> v = get( field ); - if ( v->IsNull() || v->IsUndefined() ) - return BSONObj(); - uassert( 10231 , "not an object" , v->IsObject() ); - return v8ToMongo( v->ToObject() ); - } - - // --- functions ----- - - bool hasFunctionIdentifier( const string& code ) { - if ( code.size() < 9 || code.find( "function" ) != 0 ) - return false; - - return code[8] == ' ' || code[8] == '('; - } - - Local< v8::Function > V8Scope::__createFunction( const char * raw ) { - raw = jsSkipWhiteSpace( raw ); - string code = raw; - if ( !hasFunctionIdentifier( code ) ) { - if ( code.find( "\n" ) == string::npos && - ! hasJSReturn( code ) && - ( code.find( ";" ) == string::npos || code.find( ";" ) == code.size() - 1 ) ) { - code = "return " + code; - } - code = "function(){ " + code + "}"; - } - - int num = _funcs.size() + 1; - - string fn; - { - stringstream ss; - ss << "_funcs" << num; - fn = ss.str(); - } - - code = fn + " = " + code; - - TryCatch try_catch; - // this might be time consuming, consider allowing an interrupt - Handle<Script> script = v8::Script::Compile( v8::String::New( code.c_str() ) , - v8::String::New( fn.c_str() ) ); - if ( script.IsEmpty() ) { - _error = (string)"compile error: " + toSTLString( &try_catch ); - log() << _error << endl; - return Local< v8::Function >(); - } - - Local<Value> result = script->Run(); - if ( result.IsEmpty() ) { - _error = (string)"compile error: " + toSTLString( &try_catch ); - log() << _error << endl; - return Local< v8::Function >(); - } - - return v8::Function::Cast( *_global->Get( v8::String::New( fn.c_str() ) ) ); - } - - ScriptingFunction V8Scope::_createFunction( const char * raw ) { - V8_SIMPLE_HEADER - Local< Value > ret = __createFunction( raw ); - if ( ret.IsEmpty() ) - return 0; - Persistent<Value> f = Persistent< Value >::New( ret ); - uassert( 10232, "not a func" , f->IsFunction() ); - int num = _funcs.size() + 1; - _funcs.push_back( f ); - return num; - } - - void V8Scope::setFunction( const char *field , const char * code ) { - V8_SIMPLE_HEADER - _global->Set( getV8Str( field ) , __createFunction(code) ); - } - -// void V8Scope::setThis( const BSONObj * obj ) { -// V8_SIMPLE_HEADER -// if ( ! obj ) { -// _this = Persistent< v8::Object >::New( v8::Object::New() ); -// return; -// } -// -// //_this = mongoToV8( *obj ); -// v8::Handle<v8::Value> argv[1]; -// argv[0] = v8::External::New( createWrapperHolder( this, obj , true , false ) ); -// _this = Persistent< v8::Object >::New( _wrapper->NewInstance( 1, argv ) ); -// } - - void V8Scope::rename( const char * from , const char * to ) { - V8_SIMPLE_HEADER; - Handle<v8::String> f = getV8Str( from ); - Handle<v8::String> t = getV8Str( to ); - _global->Set( t , _global->Get( f ) ); - _global->Set( f , v8::Undefined() ); - } - - int V8Scope::invoke( ScriptingFunction func , const BSONObj* argsObject, const BSONObj* recv, int timeoutMs , bool ignoreReturn, bool readOnlyArgs, bool readOnlyRecv ) { - V8_SIMPLE_HEADER - Handle<Value> funcValue = _funcs[func-1]; - - TryCatch try_catch; - int nargs = argsObject ? argsObject->nFields() : 0; - scoped_array< Handle<Value> > args; - if ( nargs ) { - args.reset( new Handle<Value>[nargs] ); - BSONObjIterator it( *argsObject ); - for ( int i=0; i<nargs; i++ ) { - BSONElement next = it.next(); - args[i] = mongoToV8Element( next, readOnlyArgs ); - } - setObject( "args", *argsObject, readOnlyArgs); // for backwards compatibility - } - else { - _global->Set( V8STR_ARGS, v8::Undefined() ); - } - if ( globalScriptEngine->interrupted() ) { - stringstream ss; - ss << "error in invoke: " << globalScriptEngine->checkInterrupt(); - _error = ss.str(); - log() << _error << endl; - return 1; - } - Handle<v8::Object> v8recv; - if (recv != 0) - v8recv = mongoToLZV8(*recv, false, readOnlyRecv); - else - v8recv = _emptyObj; - - enableV8Interrupt(); // because of v8 locker we can check interrupted, then enable - Local<Value> result = ((v8::Function*)(*funcValue))->Call( v8recv , nargs , nargs ? args.get() : 0 ); - disableV8Interrupt(); - - if ( result.IsEmpty() ) { - stringstream ss; - if ( try_catch.HasCaught() && !try_catch.CanContinue() ) { - ss << "error in invoke: " << globalScriptEngine->checkInterrupt(); - } - else { - ss << "error in invoke: " << toSTLString( &try_catch ); - } - _error = ss.str(); - log() << _error << endl; - return 1; - } - - if ( ! ignoreReturn ) { - _global->Set( V8STR_RETURN , result ); - } - - return 0; - } - - bool V8Scope::exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ) { - if ( timeoutMs ) { - static bool t = 1; - if ( t ) { - log() << "timeoutMs not support for v8 yet code: " << code << endl; - t = 0; - } - } - - V8_SIMPLE_HEADER - - TryCatch try_catch; - - Handle<Script> script = v8::Script::Compile( v8::String::New( code.data() ) , - v8::String::New( name.c_str() ) ); - if (script.IsEmpty()) { - stringstream ss; - ss << "compile error: " << toSTLString( &try_catch ); - _error = ss.str(); - if (reportError) - log() << _error << endl; - if ( assertOnError ) - uassert( 10233 , _error , 0 ); - return false; - } - - if ( globalScriptEngine->interrupted() ) { - _error = (string)"exec error: " + globalScriptEngine->checkInterrupt(); - if ( reportError ) { - log() << _error << endl; - } - if ( assertOnError ) { - uassert( 13475 , _error , 0 ); - } - return false; - } - enableV8Interrupt(); // because of v8 locker we can check interrupted, then enable - Handle<v8::Value> result = script->Run(); - disableV8Interrupt(); - if ( result.IsEmpty() ) { - if ( try_catch.HasCaught() && !try_catch.CanContinue() ) { - _error = (string)"exec error: " + globalScriptEngine->checkInterrupt(); - } - else { - _error = (string)"exec error: " + toSTLString( &try_catch ); - } - if ( reportError ) - log() << _error << endl; - if ( assertOnError ) - uassert( 10234 , _error , 0 ); - return false; - } - - _global->Set( getV8Str( "__lastres__" ) , result ); - - if ( printResult && ! result->IsUndefined() ) { - cout << toSTLString( result ) << endl; - } - - return true; - } - - void V8Scope::injectNative( const char *field, NativeFunction func, void* data ) { - injectNative(field, func, _global, data); - } - - void V8Scope::injectNative( const char *field, NativeFunction func, Handle<v8::Object>& obj, void* data ) { - V8_SIMPLE_HEADER - - Handle< FunctionTemplate > ft = createV8Function(nativeCallback); - ft->Set( this->V8STR_NATIVE_FUNC, External::New( (void*)func ) ); - ft->Set( this->V8STR_NATIVE_DATA, External::New( data ) ); - obj->Set( getV8Str( field ), ft->GetFunction() ); - } - - void V8Scope::injectV8Function( const char *field, v8Function func ) { - injectV8Function(field, func, _global); - } - - void V8Scope::injectV8Function( const char *field, v8Function func, Handle<v8::Object>& obj ) { - V8_SIMPLE_HEADER - - Handle< FunctionTemplate > ft = createV8Function(func); - Handle<v8::Function> f = ft->GetFunction(); - obj->Set( getV8Str( field ), f ); - } - - void V8Scope::injectV8Function( const char *field, v8Function func, Handle<v8::Template>& t ) { - V8_SIMPLE_HEADER - - Handle< FunctionTemplate > ft = createV8Function(func); - Handle<v8::Function> f = ft->GetFunction(); - t->Set( getV8Str( field ), f ); - } - - Handle<FunctionTemplate> V8Scope::createV8Function( v8Function func ) { - Handle< FunctionTemplate > ft = v8::FunctionTemplate::New(v8Callback, External::New( this )); - ft->Set( this->V8STR_V8_FUNC, External::New( (void*)func ) ); - return ft; - } - - void V8Scope::gc() { - cout << "in gc" << endl; - V8Lock l; - while( !V8::IdleNotification() ); - } - - // ----- db access ----- - - void V8Scope::localConnect( const char * dbName ) { - { - V8_SIMPLE_HEADER - - if ( _connectState == EXTERNAL ) - throw UserException( 12510, "externalSetup already called, can't call externalSetup" ); - if ( _connectState == LOCAL ) { - if ( _localDBName == dbName ) - return; - throw UserException( 12511, "localConnect called with a different name previously" ); - } - - // needed for killop / interrupt support - v8::Locker::StartPreemption( 50 ); - - //_global->Set( v8::String::New( "Mongo" ) , _engine->_externalTemplate->GetFunction() ); - _global->Set( getV8Str( "Mongo" ) , getMongoFunctionTemplate( this, true )->GetFunction() ); - execCoreFiles(); - exec( "_mongo = new Mongo();" , "local connect 2" , false , true , true , 0 ); - exec( (string)"db = _mongo.getDB(\"" + dbName + "\");" , "local connect 3" , false , true , true , 0 ); - _connectState = LOCAL; - _localDBName = dbName; - } - loadStored(); - } - - void V8Scope::externalSetup() { - V8_SIMPLE_HEADER - if ( _connectState == EXTERNAL ) - return; - if ( _connectState == LOCAL ) - throw UserException( 12512, "localConnect already called, can't call externalSetup" ); - - installFork( this, _global, _context ); - _global->Set( getV8Str( "Mongo" ) , getMongoFunctionTemplate( this, false )->GetFunction() ); - execCoreFiles(); - _connectState = EXTERNAL; - } - - // ----- internal ----- - - void V8Scope::reset() { - _startCall(); - } - - void V8Scope::_startCall() { - _error = ""; - } - - Local< v8::Value > newFunction( const char *code ) { - stringstream codeSS; - codeSS << "____MontoToV8_newFunction_temp = " << code; - string codeStr = codeSS.str(); - Local< Script > compiled = Script::New( v8::String::New( codeStr.c_str() ) ); - Local< Value > ret = compiled->Run(); - return ret; - } - - Local< v8::Value > V8Scope::newId( const OID &id ) { - v8::Function * idCons = this->getObjectIdCons(); - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::String::New( id.str().c_str() ); - return idCons->NewInstance( 1 , argv ); - } - - Local<v8::Object> V8Scope::mongoToV8( const BSONObj& m , bool array, bool readOnly ) { - - Local<v8::Object> o; - - // handle DBRef. needs to come first. isn't it? (metagoto) - static string ref = "$ref"; - if ( ref == m.firstElement().fieldName() ) { - const BSONElement& id = m["$id"]; - if (!id.eoo()) { // there's no check on $id exitence in sm implementation. risky ? - v8::Function* dbRef = getNamedCons( "DBRef" ); - o = dbRef->NewInstance(); - } - } - - Local< v8::ObjectTemplate > readOnlyObjects; - - if ( !o.IsEmpty() ) { - readOnly = false; - } - else if ( array ) { - // NOTE Looks like it's impossible to add interceptors to v8 arrays. - readOnly = false; - o = v8::Array::New(); - } - else if ( !readOnly ) { - o = v8::Object::New(); - } - else { - // NOTE Our readOnly implemention relies on undocumented ObjectTemplate - // functionality that may be fragile, but it still seems like the best option - // for now -- fwiw, the v8 docs are pretty sparse. I've determined experimentally - // that when property handlers are set for an object template, they will attach - // to objects previously created by that template. To get this to work, though, - // it is necessary to initialize the template's property handlers before - // creating objects from the template (as I have in the following few lines - // of code). - // NOTE In my first attempt, I configured the permanent property handlers before - // constructiong the object and replaced the Set() calls below with ForceSet(). - // However, it turns out that ForceSet() only bypasses handlers for named - // properties and not for indexed properties. - readOnlyObjects = v8::ObjectTemplate::New(); - // NOTE This internal field will store type info for special db types. For - // regular objects the field is unnecessary - for simplicity I'm creating just - // one readOnlyObjects template for objects where the field is & isn't necessary, - // assuming that the overhead of an internal field is slight. - readOnlyObjects->SetInternalFieldCount( 1 ); - readOnlyObjects->SetNamedPropertyHandler( 0 ); - readOnlyObjects->SetIndexedPropertyHandler( 0 ); - o = readOnlyObjects->NewInstance(); - } - - mongo::BSONObj sub; - - for ( BSONObjIterator i(m); i.more(); ) { - const BSONElement& f = i.next(); - - Local<Value> v; - Handle<v8::String> name = getV8Str(f.fieldName()); - - switch ( f.type() ) { - - case mongo::Code: - o->Set( name, newFunction( f.valuestr() ) ); - break; - - case CodeWScope: - if ( f.codeWScopeObject().isEmpty() ) - log() << "warning: CodeWScope doesn't transfer to db.eval" << endl; - o->Set( name, newFunction( f.codeWScopeCode() ) ); - break; - - case mongo::String: - o->Set( name , v8::String::New( f.valuestr() ) ); - break; - - case mongo::jstOID: { - v8::Function * idCons = getObjectIdCons(); - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::String::New( f.__oid().str().c_str() ); - o->Set( name , - idCons->NewInstance( 1 , argv ) ); - break; - } - - case mongo::NumberDouble: - case mongo::NumberInt: - o->Set( name , v8::Number::New( f.number() ) ); - break; - -// case mongo::NumberInt: { -// Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); -// int val = f.numberInt(); -// v8::Function* numberInt = getNamedCons( "NumberInt" ); -// v8::Handle<v8::Value> argv[1]; -// argv[0] = v8::Int32::New( val ); -// o->Set( name, numberInt->NewInstance( 1, argv ) ); -// break; -// } - - case mongo::Array: - sub = f.embeddedObject(); - o->Set( name , mongoToV8( sub , true, readOnly ) ); - break; - case mongo::Object: - sub = f.embeddedObject(); - o->Set( name , mongoToLZV8( sub , false, readOnly ) ); - break; - - case mongo::Date: - o->Set( name , v8::Date::New( (double) ((long long)f.date().millis) )); - break; - - case mongo::Bool: - o->Set( name , v8::Boolean::New( f.boolean() ) ); - break; - - case mongo::jstNULL: - case mongo::Undefined: // duplicate sm behavior - o->Set( name , v8::Null() ); - break; - - case mongo::RegEx: { - v8::Function * regex = getNamedCons( "RegExp" ); - - v8::Handle<v8::Value> argv[2]; - argv[0] = v8::String::New( f.regex() ); - argv[1] = v8::String::New( f.regexFlags() ); - - o->Set( name , regex->NewInstance( 2 , argv ) ); - break; - } - - case mongo::BinData: { - /*Local<v8::Object> b = */ readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); - - int len; - const char *data = f.binData( len ); - - v8::Function* binData = getNamedCons( "BinData" ); - v8::Handle<v8::Value> argv[3]; - argv[0] = v8::Number::New( len ); - argv[1] = v8::Number::New( f.binDataType() ); - argv[2] = v8::String::New( data, len ); - o->Set( name, binData->NewInstance(3, argv) ); - break; - } - - case mongo::Timestamp: { - Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); - - sub->Set( V8STR_T , v8::Number::New( f.timestampTime() ) ); - sub->Set( V8STR_I , v8::Number::New( f.timestampInc() ) ); - sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); - - o->Set( name , sub ); - break; - } - - case mongo::NumberLong: { - /* Local<v8::Object> sub = */ readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); - unsigned long long val = f.numberLong(); - v8::Function* numberLong = getNamedCons( "NumberLong" ); - double floatApprox = (double)(long long)val; - if ( (long long)val == (long long)floatApprox ) { - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::Number::New( floatApprox ); - o->Set( name, numberLong->NewInstance( 1, argv ) ); - } - else { - v8::Handle<v8::Value> argv[3]; - argv[0] = v8::Number::New( floatApprox ); - argv[1] = v8::Integer::New( val >> 32 ); - argv[2] = v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) ); - o->Set( name, numberLong->NewInstance(3, argv) ); - } - break; - } - - case mongo::MinKey: { - Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); - sub->Set( V8STR_MINKEY, v8::Boolean::New( true ) ); - sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); - o->Set( name , sub ); - break; - } - - case mongo::MaxKey: { - Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() : internalFieldObjects->NewInstance(); - sub->Set( V8STR_MAXKEY, v8::Boolean::New( true ) ); - sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); - o->Set( name , sub ); - break; - } - - case mongo::DBRef: { - v8::Function* dbPointer = getNamedCons( "DBPointer" ); - v8::Handle<v8::Value> argv[2]; - argv[0] = getV8Str( f.dbrefNS() ); - argv[1] = newId( f.dbrefOID() ); - o->Set( name, dbPointer->NewInstance(2, argv) ); - break; - } - - default: - cout << "can't handle type: "; - cout << f.type() << " "; - cout << f.toString(); - cout << endl; - break; - } - - } - - if ( readOnly ) { - readOnlyObjects->SetNamedPropertyHandler( 0, NamedReadOnlySet, 0, NamedReadOnlyDelete ); - readOnlyObjects->SetIndexedPropertyHandler( 0, IndexedReadOnlySet, 0, IndexedReadOnlyDelete ); - } - - return o; - } - - /** - * converts a BSONObj to a Lazy V8 object - */ - Handle<v8::Object> V8Scope::mongoToLZV8( const BSONObj& m , bool array, bool readOnly ) { - Local<v8::Object> o; - - if (readOnly) { - o = roObjectTemplate->NewInstance(); - o->SetHiddenValue(V8STR_RO, v8::Boolean::New(true)); - } else { - if (array) { - o = lzArrayTemplate->NewInstance(); - o->SetPrototype(v8::Array::New(1)->GetPrototype()); - o->Set(V8STR_LENGTH, v8::Integer::New(m.nFields()), DontEnum); - // o->Set(ARRAY_STRING, v8::Boolean::New(true), DontEnum); - } else { - o = lzObjectTemplate->NewInstance(); - - static string ref = "$ref"; - if ( ref == m.firstElement().fieldName() ) { - const BSONElement& id = m["$id"]; - if (!id.eoo()) { - v8::Function* dbRef = getNamedCons( "DBRef" ); - o->SetPrototype(dbRef->NewInstance()->GetPrototype()); - } - } - } - - // need to set all keys with dummy values, so that order of keys is correct during enumeration - // otherwise v8 will list any newly set property in JS before the ones of underlying BSON obj. - for (BSONObjIterator it(m); it.more();) { - const BSONElement& f = it.next(); - o->ForceSet(getV8Str(f.fieldName()), v8::Undefined()); - } - } - - BSONObj* own = new BSONObj(m.getOwned()); -// BSONObj* own = new BSONObj(m); - Persistent<v8::Object> p = wrapBSONObject(o, own); - return p; - } - - Handle<v8::Value> V8Scope::mongoToV8Element( const BSONElement &f, bool readOnly ) { -// Local< v8::ObjectTemplate > internalFieldObjects = v8::ObjectTemplate::New(); -// internalFieldObjects->SetInternalFieldCount( 1 ); - - switch ( f.type() ) { - - case mongo::Code: - return newFunction( f.valuestr() ); - - case CodeWScope: - if ( f.codeWScopeObject().isEmpty() ) - log() << "warning: CodeWScope doesn't transfer to db.eval" << endl; - return newFunction( f.codeWScopeCode() ); - - case mongo::String: -// return v8::String::NewExternal( new ExternalString( f.valuestr() )); - return v8::String::New( f.valuestr() ); -// return getV8Str( f.valuestr() ); - - case mongo::jstOID: - return newId( f.__oid() ); - - case mongo::NumberDouble: - case mongo::NumberInt: - return v8::Number::New( f.number() ); - - case mongo::Array: - // for arrays it's better to use non lazy object because: - // - the lazy array is not a true v8 array and requires some v8 src change for all methods to work - // - it made several tests about 1.5x slower - // - most times when an array is accessed, all its values will be used - return mongoToV8( f.embeddedObject() , true, readOnly ); - case mongo::Object: - return mongoToLZV8( f.embeddedObject() , false, readOnly); - - case mongo::Date: - return v8::Date::New( (double) ((long long)f.date().millis) ); - - case mongo::Bool: - return v8::Boolean::New( f.boolean() ); - - case mongo::EOO: - case mongo::jstNULL: - case mongo::Undefined: // duplicate sm behavior - return v8::Null(); - - case mongo::RegEx: { - v8::Function * regex = getNamedCons( "RegExp" ); - - v8::Handle<v8::Value> argv[2]; - argv[0] = v8::String::New( f.regex() ); - argv[1] = v8::String::New( f.regexFlags() ); - - return regex->NewInstance( 2 , argv ); - break; - } - - case mongo::BinData: { - int len; - const char *data = f.binData( len ); - - v8::Function* binData = getNamedCons( "BinData" ); - v8::Handle<v8::Value> argv[3]; - argv[0] = v8::Number::New( len ); - argv[1] = v8::Number::New( f.binDataType() ); - argv[2] = v8::String::New( data, len ); - return binData->NewInstance( 3, argv ); - }; - - case mongo::Timestamp: { - Local<v8::Object> sub = internalFieldObjects->NewInstance(); - - sub->Set( V8STR_T , v8::Number::New( f.timestampTime() ) ); - sub->Set( V8STR_I , v8::Number::New( f.timestampInc() ) ); - sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); - - return sub; - } - - case mongo::NumberLong: { - /* Local<v8::Object> sub = */ internalFieldObjects->NewInstance(); - unsigned long long val = f.numberLong(); - v8::Function* numberLong = getNamedCons( "NumberLong" ); - if ( (long long)val == (long long)(double)(long long)(val) ) { - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::Number::New( (double)(long long)( val ) ); - return numberLong->NewInstance( 1, argv ); - } - else { - v8::Handle<v8::Value> argv[3]; - argv[0] = v8::Number::New( (double)(long long)( val ) ); - argv[1] = v8::Integer::New( val >> 32 ); - argv[2] = v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) ); - return numberLong->NewInstance( 3, argv ); - } - } - -// case mongo::NumberInt: { -// Local<v8::Object> sub = internalFieldObjects->NewInstance(); -// int val = f.numberInt(); -// v8::Function* numberInt = getNamedCons( "NumberInt" ); -// v8::Handle<v8::Value> argv[1]; -// argv[0] = v8::Int32::New(val); -// return numberInt->NewInstance( 1, argv ); -// } - - case mongo::MinKey: { - Local<v8::Object> sub = internalFieldObjects->NewInstance(); - sub->Set( V8STR_MINKEY, v8::Boolean::New( true ) ); - sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); - return sub; - } - - case mongo::MaxKey: { - Local<v8::Object> sub = internalFieldObjects->NewInstance(); - sub->Set( V8STR_MAXKEY, v8::Boolean::New( true ) ); - sub->SetInternalField( 0, v8::Uint32::New( f.type() ) ); - return sub; - } - - case mongo::DBRef: { - v8::Function* dbPointer = getNamedCons( "DBPointer" ); - v8::Handle<v8::Value> argv[2]; - argv[0] = getV8Str( f.dbrefNS() ); - argv[1] = newId( f.dbrefOID() ); - return dbPointer->NewInstance(2, argv); - } - - default: - cout << "can't handle type: "; - cout << f.type() << " "; - cout << f.toString(); - cout << endl; - break; - } - - return v8::Undefined(); - } - - void V8Scope::append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ) { - V8_SIMPLE_HEADER - Handle<v8::String> v8name = getV8Str(scopeName); - Handle<Value> value = _global->Get( v8name ); - v8ToMongoElement(builder, v8name, fieldName, value); - } - - void V8Scope::v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value , int depth, BSONObj* originalParent ) { - - if ( value->IsString() ) { -// Handle<v8::String> str = Handle<v8::String>::Cast(value); -// ExternalString* es = (ExternalString*) (str->GetExternalAsciiStringResource()); -// b.append( sname , es->data() ); - b.append( sname , toSTLString( value ).c_str() ); - return; - } - - if ( value->IsFunction() ) { - b.appendCode( sname , toSTLString( value ) ); - return; - } - - if ( value->IsNumber() ) { - double val = value->ToNumber()->Value(); - // if previous type was integer, keep it - int intval = (int)val; - if (val == intval && originalParent) { - BSONElement elmt = originalParent->getField(sname); - if (elmt.type() == mongo::NumberInt) { - b.append( sname , intval ); - return; - } - } - - b.append( sname , val ); - return; - } - - if ( value->IsArray() ) { - BSONObj sub = v8ToMongo( value->ToObject() , depth ); - b.appendArray( sname , sub ); - return; - } - - if ( value->IsDate() ) { - long long dateval = (long long)(v8::Date::Cast( *value )->NumberValue()); - b.appendDate( sname , Date_t( (unsigned long long) dateval ) ); - return; - } - - if ( value->IsExternal() ) - return; - - if ( value->IsObject() ) { - // The user could potentially modify the fields of these special objects, - // wreaking havoc when we attempt to reinterpret them. Not doing any validation - // for now... - Local< v8::Object > obj = value->ToObject(); - if ( obj->InternalFieldCount() && obj->GetInternalField( 0 )->IsNumber() ) { - switch( obj->GetInternalField( 0 )->ToInt32()->Value() ) { // NOTE Uint32's Value() gave me a linking error, so going with this instead - case Timestamp: - b.appendTimestamp( sname, - Date_t( (unsigned long long)(obj->Get( V8STR_T )->ToNumber()->Value() )), - obj->Get( V8STR_I )->ToInt32()->Value() ); - return; - case MinKey: - b.appendMinKey( sname ); - return; - case MaxKey: - b.appendMaxKey( sname ); - return; - default: - assert( "invalid internal field" == 0 ); - } - } - string s = toSTLString( value ); - if ( s.size() && s[0] == '/' ) { - s = s.substr( 1 ); - string r = s.substr( 0 , s.rfind( "/" ) ); - string o = s.substr( s.rfind( "/" ) + 1 ); - b.appendRegex( sname , r , o ); - } - else if ( value->ToObject()->GetPrototype()->IsObject() && - value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( V8STR_ISOBJECTID ) ) { - OID oid; - oid.init( toSTLString( value ) ); - b.appendOID( sname , &oid ); - } - else if ( !value->ToObject()->GetHiddenValue( V8STR_NUMBERLONG ).IsEmpty() ) { - // TODO might be nice to potentially speed this up with an indexed internal - // field, but I don't yet know how to use an ObjectTemplate with a - // constructor. - v8::Handle< v8::Object > it = value->ToObject(); - long long val; - if ( !it->Has( getV8Str( "top" ) ) ) { - val = (long long)( it->Get( getV8Str( "floatApprox" ) )->NumberValue() ); - } - else { - val = (long long) - ( (unsigned long long)( it->Get( getV8Str( "top" ) )->ToInt32()->Value() ) << 32 ) + - (unsigned)( it->Get( getV8Str( "bottom" ) )->ToInt32()->Value() ); - } - - b.append( sname, val ); - } - else if ( !value->ToObject()->GetHiddenValue( V8STR_NUMBERINT ).IsEmpty() ) { - v8::Handle< v8::Object > it = value->ToObject(); - b.append(sname, it->GetHiddenValue(V8STR_NUMBERINT)->Int32Value()); - } - else if ( !value->ToObject()->GetHiddenValue( V8STR_DBPTR ).IsEmpty() ) { - OID oid; - oid.init( toSTLString( value->ToObject()->Get( getV8Str( "id" ) ) ) ); - string ns = toSTLString( value->ToObject()->Get( getV8Str( "ns" ) ) ); - b.appendDBRef( sname, ns, oid ); - } - else if ( !value->ToObject()->GetHiddenValue( V8STR_BINDATA ).IsEmpty() ) { - int len = obj->Get( getV8Str( "len" ) )->ToInt32()->Value(); - Local<External> c = External::Cast( *(obj->GetInternalField( 0 )) ); - const char* dataArray = (char*)(c->Value());; - b.appendBinData( sname, - len, - mongo::BinDataType( obj->Get( getV8Str( "type" ) )->ToInt32()->Value() ), - dataArray ); - } - else { - BSONObj sub = v8ToMongo( value->ToObject() , depth ); - b.append( sname , sub ); - } - return; - } - - if ( value->IsBoolean() ) { - b.appendBool( sname , value->ToBoolean()->Value() ); - return; - } - - else if ( value->IsUndefined() ) { - b.appendUndefined( sname ); - return; - } - - else if ( value->IsNull() ) { - b.appendNull( sname ); - return; - } - - cout << "don't know how to convert to mongo field [" << name << "]\t" << value << endl; - } - - BSONObj V8Scope::v8ToMongo( v8::Handle<v8::Object> o , int depth ) { - BSONObj* originalBSON = 0; - if (o->HasNamedLookupInterceptor()) { - originalBSON = unwrapBSONObj(o); - } - - if ( !o->GetHiddenValue( V8STR_RO ).IsEmpty() || - (o->HasNamedLookupInterceptor() && o->GetHiddenValue( V8STR_MODIFIED ).IsEmpty()) ) { - // object was readonly, use bson as is - if (originalBSON) - return *originalBSON; - } - - BSONObjBuilder b; - - if ( depth == 0 ) { - if ( o->HasRealNamedProperty( V8STR_ID ) ) { - v8ToMongoElement( b , V8STR_ID , "_id" , o->Get( V8STR_ID ), 0, originalBSON ); - } - } - - Local<v8::Array> names = o->GetPropertyNames(); - for ( unsigned int i=0; i<names->Length(); i++ ) { - v8::Local<v8::String> name = names->Get( i )->ToString(); - -// if ( o->GetPrototype()->IsObject() && -// o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) ) -// continue; - - v8::Local<v8::Value> value = o->Get( name ); - - const string sname = toSTLString( name ); - if ( depth == 0 && sname == "_id" ) - continue; - - v8ToMongoElement( b , name , sname , value , depth + 1, originalBSON ); - } - return b.obj(); - } - - // --- random utils ---- - - v8::Function * V8Scope::getNamedCons( const char * name ) { - return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( getV8Str( name ) ) ) ); - } - - v8::Function * V8Scope::getObjectIdCons() { - return getNamedCons( "ObjectId" ); - } - - Handle<v8::Value> V8Scope::Print(V8Scope* scope, 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(); - } - - Handle<v8::Value> V8Scope::Version(V8Scope* scope, const Arguments& args) { - HandleScope handle_scope; - return handle_scope.Close( v8::String::New(v8::V8::GetVersion()) ); - } - - Handle<v8::Value> V8Scope::GCV8(V8Scope* scope, const Arguments& args) { - V8Lock l; - while( !V8::IdleNotification() ); - return v8::Undefined(); - } - - /** - * Gets a V8 strings from the scope's cache, creating one if needed - */ - v8::Handle<v8::String> V8Scope::getV8Str(string str) { - Persistent<v8::String> ptr = _strCache[str]; - if (ptr.IsEmpty()) { - ptr = Persistent<v8::String>::New(v8::String::New(str.c_str())); - _strCache[str] = ptr; -// cout << "Adding str " + str << endl; - } -// cout << "Returning str " + str << endl; - return ptr; - } - -} // namespace mongo diff --git a/scripting/engine_v8.h b/scripting/engine_v8.h deleted file mode 100644 index 3f116c9c2c2..00000000000 --- a/scripting/engine_v8.h +++ /dev/null @@ -1,231 +0,0 @@ -//engine_v8.h - -/* 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. - */ - -#pragma once - -#include <vector> -#include "engine.h" -#include <v8.h> - -using namespace v8; - -namespace mongo { - - class V8ScriptEngine; - class V8Scope; - - typedef Handle< Value > (*v8Function) ( V8Scope* scope, const v8::Arguments& args ); - - // Preemption is going to be allowed for the v8 mutex, and some of our v8 - // usage is not preemption safe. So we are using an additional mutex that - // will not be preempted. The V8Lock should be used in place of v8::Locker - // except in certain special cases involving interrupts. - namespace v8Locks { - // the implementations are quite simple - objects must be destroyed in - // reverse of the order created, and should not be shared between threads - struct RecursiveLock { - RecursiveLock(); - ~RecursiveLock(); - bool _unlock; - }; - struct RecursiveUnlock { - RecursiveUnlock(); - ~RecursiveUnlock(); - bool _lock; - }; - } // namespace v8Locks - class V8Lock { - v8Locks::RecursiveLock _noPreemptionLock; - v8::Locker _preemptionLock; - }; - struct V8Unlock { - v8::Unlocker _preemptionUnlock; - v8Locks::RecursiveUnlock _noPreemptionUnlock; - }; - - class V8Scope : public Scope { - public: - - V8Scope( V8ScriptEngine * engine ); - ~V8Scope(); - - virtual void reset(); - virtual void init( const BSONObj * data ); - - virtual void localConnect( const char * dbName ); - virtual void externalSetup(); - - v8::Handle<v8::Value> get( const char * field ); // caller must create context and handle scopes - virtual double getNumber( const char *field ); - virtual int getNumberInt( const char *field ); - virtual long long getNumberLongLong( const char *field ); - virtual string getString( const char *field ); - virtual bool getBoolean( const char *field ); - virtual BSONObj getObject( const char *field ); - Handle<v8::Object> getGlobalObject() { return _global; }; - - virtual int type( const char *field ); - - virtual void setNumber( const char *field , double val ); - virtual void setString( const char *field , const char * val ); - virtual void setBoolean( const char *field , bool val ); - virtual void setElement( const char *field , const BSONElement& e ); - virtual void setObject( const char *field , const BSONObj& obj , bool readOnly); - virtual void setFunction( const char *field , const char * code ); -// virtual void setThis( const BSONObj * obj ); - - virtual void rename( const char * from , const char * to ); - - virtual ScriptingFunction _createFunction( const char * code ); - Local< v8::Function > __createFunction( const char * code ); - virtual int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool readOnlyArgs = false, bool readOnlyRecv = false ); - virtual bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs ); - virtual string getError() { return _error; } - - virtual void injectNative( const char *field, NativeFunction func, void* data = 0 ); - void injectNative( const char *field, NativeFunction func, Handle<v8::Object>& obj, void* data = 0 ); - void injectV8Function( const char *field, v8Function func ); - void injectV8Function( const char *field, v8Function func, Handle<v8::Object>& obj ); - void injectV8Function( const char *field, v8Function func, Handle<v8::Template>& t ); - Handle<v8::FunctionTemplate> createV8Function( v8Function func ); - - void gc(); - - Handle< Context > context() const { return _context; } - - v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false ); - v8::Handle<v8::Object> mongoToLZV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false ); - mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0 ); - - void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , - const string sname , v8::Handle<v8::Value> value , int depth = 0, BSONObj* originalParent=0 ); - v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool readOnly = false ); - virtual void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ); - - v8::Function * getNamedCons( const char * name ); - v8::Function * getObjectIdCons(); - Local< v8::Value > newId( const OID &id ); - - Persistent<v8::Object> wrapBSONObject(Local<v8::Object> obj, BSONObj* data); - Persistent<v8::Object> wrapArrayObject(Local<v8::Object> obj, char* data); - - v8::Handle<v8::String> getV8Str(string str); -// inline v8::Handle<v8::String> getV8Str(string str) { return v8::String::New(str.c_str()); } - inline v8::Handle<v8::String> getLocalV8Str(string str) { return v8::String::New(str.c_str()); } - - Handle<v8::String> V8STR_CONN; - Handle<v8::String> V8STR_ID; - Handle<v8::String> V8STR_LENGTH; - Handle<v8::String> V8STR_LEN; - Handle<v8::String> V8STR_TYPE; - Handle<v8::String> V8STR_ISOBJECTID; - Handle<v8::String> V8STR_NATIVE_FUNC; - Handle<v8::String> V8STR_NATIVE_DATA; - Handle<v8::String> V8STR_V8_FUNC; - Handle<v8::String> V8STR_RETURN; - Handle<v8::String> V8STR_ARGS; - Handle<v8::String> V8STR_T; - Handle<v8::String> V8STR_I; - Handle<v8::String> V8STR_EMPTY; - Handle<v8::String> V8STR_MINKEY; - Handle<v8::String> V8STR_MAXKEY; - Handle<v8::String> V8STR_NUMBERLONG; - Handle<v8::String> V8STR_NUMBERINT; - Handle<v8::String> V8STR_DBPTR; - Handle<v8::String> V8STR_BINDATA; - Handle<v8::String> V8STR_WRAPPER; - Handle<v8::String> V8STR_RO; - Handle<v8::String> V8STR_MODIFIED; - - private: - void _startCall(); - - static Handle< Value > nativeCallback( V8Scope* scope, const Arguments &args ); - static v8::Handle< v8::Value > v8Callback( const v8::Arguments &args ); - static Handle< Value > load( V8Scope* scope, const Arguments &args ); - static Handle< Value > Print(V8Scope* scope, const v8::Arguments& args); - static Handle< Value > Version(V8Scope* scope, const v8::Arguments& args); - static Handle< Value > GCV8(V8Scope* scope, const v8::Arguments& args); - - - V8ScriptEngine * _engine; - - Persistent<Context> _context; - Persistent<v8::Object> _global; - - string _error; - vector< Persistent<Value> > _funcs; - v8::Persistent<v8::Object> _emptyObj; - - v8::Persistent<v8::Function> _wrapper; - - enum ConnectState { NOT , LOCAL , EXTERNAL }; - ConnectState _connectState; - - std::map <string, v8::Persistent <v8::String> > _strCache; - - Persistent<v8::ObjectTemplate> lzObjectTemplate; - Persistent<v8::ObjectTemplate> roObjectTemplate; - Persistent<v8::ObjectTemplate> lzArrayTemplate; - Persistent<v8::ObjectTemplate> internalFieldObjects; - }; - - class V8ScriptEngine : public ScriptEngine { - public: - V8ScriptEngine(); - virtual ~V8ScriptEngine(); - - virtual Scope * createScope() { return new V8Scope( this ); } - - virtual void runTest() {} - - bool utf8Ok() const { return true; } - - class V8UnlockForClient : public Unlocker { - V8Unlock u_; - }; - - virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< Unlocker >( new V8UnlockForClient ); } - - virtual void interrupt( unsigned opSpec ); - virtual void interruptAll(); - - private: - friend class V8Scope; - }; - - class ExternalString : public v8::String::ExternalAsciiStringResource { - public: - ExternalString(std::string str) : _data(str) { - } - - ~ExternalString() { - } - - const char* data () const { return _data.c_str(); } - size_t length () const { return _data.length(); } - private: -// string _str; -// const char* _data; - std::string _data; -// size_t _len; - }; - - extern ScriptEngine * globalScriptEngine; - extern map< unsigned, int > __interruptSpecToThreadId; - -} diff --git a/scripting/sm_db.cpp b/scripting/sm_db.cpp deleted file mode 100644 index 2a9169ba082..00000000000 --- a/scripting/sm_db.cpp +++ /dev/null @@ -1,1240 +0,0 @@ -// sm_db.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. - */ - -// hacked in right now from engine_spidermonkey.cpp - -#include "../client/syncclusterconnection.h" -#include "../util/base64.h" -#include "../util/text.h" -#include "../util/hex.h" - -#if( BOOST_VERSION >= 104200 ) -//#include <boost/uuid/uuid.hpp> -#define HAVE_UUID 1 -#else -; -#endif - -namespace mongo { - - bool haveLocalShardingInfo( const string& ns ); - - // ------------ some defs needed --------------- - - JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName ); - - // ------------ utils ------------------ - - - bool isSpecialName( const string& name ) { - static set<string> names; - if ( names.size() == 0 ) { - names.insert( "tojson" ); - names.insert( "toJson" ); - names.insert( "toString" ); - } - - if ( name.length() == 0 ) - return false; - - if ( name[0] == '_' ) - return true; - - return names.count( name ) > 0; - } - - - // ------ cursor ------ - - class CursorHolder { - public: - CursorHolder( auto_ptr< DBClientCursor > &cursor, const shared_ptr< DBClientWithCommands > &connection ) : - connection_( connection ), - cursor_( cursor ) { - assert( cursor_.get() ); - } - DBClientCursor *get() const { return cursor_.get(); } - private: - shared_ptr< DBClientWithCommands > connection_; - auto_ptr< DBClientCursor > cursor_; - }; - - DBClientCursor *getCursor( JSContext *cx, JSObject *obj ) { - CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj ); - uassert( 10235 , "no cursor!" , holder ); - return holder->get(); - } - - JSBool internal_cursor_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - uassert( 10236 , "no args to internal_cursor_constructor" , argc == 0 ); - assert( JS_SetPrivate( cx , obj , 0 ) ); // just for safety - return JS_TRUE; - } - - void internal_cursor_finalize( JSContext * cx , JSObject * obj ) { - CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj ); - if ( holder ) { - delete holder; - assert( JS_SetPrivate( cx , obj , 0 ) ); - } - } - - JSBool internal_cursor_hasNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - DBClientCursor *cursor = getCursor( cx, obj ); - try { - *rval = cursor->more() ? JSVAL_TRUE : JSVAL_FALSE; - } - catch ( std::exception& e ) { - JS_ReportError( cx , e.what() ); - return JS_FALSE; - } - return JS_TRUE; - } - - JSBool internal_cursor_objsLeftInBatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - DBClientCursor *cursor = getCursor( cx, obj ); - Convertor c(cx); - *rval = c.toval((double) cursor->objsLeftInBatch() ); - return JS_TRUE; - } - - JSBool internal_cursor_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - DBClientCursor *cursor = getCursor( cx, obj ); - - BSONObj n; - - try { - if ( ! cursor->more() ) { - JS_ReportError( cx , "cursor at the end" ); - return JS_FALSE; - } - - n = cursor->next(); - } - catch ( std::exception& e ) { - JS_ReportError( cx , e.what() ); - return JS_FALSE; - } - - Convertor c(cx); - *rval = c.toval( &n ); - return JS_TRUE; - } - - JSFunctionSpec internal_cursor_functions[] = { - { "hasNext" , internal_cursor_hasNext , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "objsLeftInBatch" , internal_cursor_objsLeftInBatch , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "next" , internal_cursor_next , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { 0 } - }; - - JSClass internal_cursor_class = { - "InternalCursor" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, internal_cursor_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - - // ------ mongo stuff ------ - - JSBool mongo_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - uassert( 10237 , "mongo_constructor not implemented yet" , 0 ); - throw -1; - } - - JSBool mongo_local_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - - shared_ptr< DBClientWithCommands > client( createDirectClient() ); - assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( client ) ) ) ); - - jsval host = c.toval( "EMBEDDED" ); - assert( JS_SetProperty( cx , obj , "host" , &host ) ); - - return JS_TRUE; - } - - JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - - smuassert( cx , "0 or 1 args to Mongo" , argc <= 1 ); - - string host = "127.0.0.1"; - if ( argc > 0 ) - host = c.toString( argv[0] ); - - string errmsg; - - ConnectionString cs = ConnectionString::parse( host , errmsg ); - if ( ! cs.isValid() ) { - JS_ReportError( cx , errmsg.c_str() ); - return JS_FALSE; - } - - shared_ptr< DBClientWithCommands > conn( cs.connect( errmsg ) ); - if ( ! conn ) { - JS_ReportError( cx , errmsg.c_str() ); - return JS_FALSE; - } - - try{ - ScriptEngine::runConnectCallback( *conn ); - } - catch( std::exception& e ){ - // Can happen if connection goes down while we're starting up here - // Catch so that we don't get a hard-to-trace segfault from SM - JS_ReportError( cx, ((string)( str::stream() << "Error during mongo startup." << causedBy( e ) )).c_str() ); - return JS_FALSE; - } - - assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( conn ) ) ) ); - jsval host_val = c.toval( host.c_str() ); - assert( JS_SetProperty( cx , obj , "host" , &host_val ) ); - return JS_TRUE; - - } - - DBClientWithCommands *getConnection( JSContext *cx, JSObject *obj ) { - shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj ); - uassert( 10239 , "no connection!" , connHolder && connHolder->get() ); - return connHolder->get(); - } - - void mongo_finalize( JSContext * cx , JSObject * obj ) { - shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj ); - if ( connHolder ) { - delete connHolder; - assert( JS_SetPrivate( cx , obj , 0 ) ); - } - } - - JSClass mongo_class = { - "Mongo" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, mongo_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSBool mongo_find(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - smuassert( cx , "mongo_find needs 7 args" , argc == 7 ); - shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj ); - smuassert( cx , "no connection!" , connHolder && connHolder->get() ); - DBClientWithCommands *conn = connHolder->get(); - - Convertor c( cx ); - - string ns = c.toString( argv[0] ); - - BSONObj q = c.toObject( argv[1] ); - BSONObj f = c.toObject( argv[2] ); - - int nToReturn = (int) c.toNumber( argv[3] ); - int nToSkip = (int) c.toNumber( argv[4] ); - bool slaveOk = c.getBoolean( obj , "slaveOk" ); - int batchSize = (int) c.toNumber( argv[5] ); - int options = (int)c.toNumber( argv[6] ); - - try { - - auto_ptr<DBClientCursor> cursor = conn->query( ns , q , nToReturn , nToSkip , f.nFields() ? &f : 0 , options | ( slaveOk ? QueryOption_SlaveOk : 0 ) , batchSize ); - if ( ! cursor.get() ) { - log() << "query failed : " << ns << " " << q << " to: " << conn->toString() << endl; - JS_ReportError( cx , "error doing query: failed" ); - return JS_FALSE; - } - JSObject * mycursor = JS_NewObject( cx , &internal_cursor_class , 0 , 0 ); - CHECKNEWOBJECT( mycursor, cx, "internal_cursor_class" ); - assert( JS_SetPrivate( cx , mycursor , new CursorHolder( cursor, *connHolder ) ) ); - *rval = OBJECT_TO_JSVAL( mycursor ); - return JS_TRUE; - } - catch ( ... ) { - JS_ReportError( cx , "error doing query: unknown" ); - return JS_FALSE; - } - } - - JSBool mongo_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - smuassert( cx , "mongo_find needs at elast 3 args" , argc >= 3 ); - smuassert( cx , "2nd param to update has to be an object" , JSVAL_IS_OBJECT( argv[1] ) ); - smuassert( cx , "3rd param to update has to be an object" , JSVAL_IS_OBJECT( argv[2] ) ); - - Convertor c( cx ); - if ( c.getBoolean( obj , "readOnly" ) ) { - JS_ReportError( cx , "js db in read only mode - mongo_update" ); - return JS_FALSE; - } - - DBClientWithCommands * conn = getConnection( cx, obj ); - uassert( 10245 , "no connection!" , conn ); - - string ns = c.toString( argv[0] ); - - bool upsert = argc > 3 && c.toBoolean( argv[3] ); - bool multi = argc > 4 && c.toBoolean( argv[4] ); - - try { - conn->update( ns , c.toObject( argv[1] ) , c.toObject( argv[2] ) , upsert , multi ); - return JS_TRUE; - } - catch ( ... ) { - JS_ReportError( cx , "error doing update" ); - return JS_FALSE; - } - } - - JSBool mongo_insert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - smuassert( cx , "mongo_insert needs 2 args" , argc == 2 ); - smuassert( cx , "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) ); - - Convertor c( cx ); - if ( c.getBoolean( obj , "readOnly" ) ) { - JS_ReportError( cx , "js db in read only mode - mongo_insert" ); - return JS_FALSE; - } - - DBClientWithCommands * conn = getConnection( cx, obj ); - uassert( 10248 , "no connection!" , conn ); - - string ns = c.toString( argv[0] ); - - try { - BSONObj o = c.toObject( argv[1] ); - // TODO: add _id - - conn->insert( ns , o ); - return JS_TRUE; - } - catch ( std::exception& e ) { - stringstream ss; - ss << "error doing insert:" << e.what(); - string s = ss.str(); - JS_ReportError( cx , s.c_str() ); - return JS_FALSE; - } - catch ( ... ) { - JS_ReportError( cx , "error doing insert" ); - return JS_FALSE; - } - } - - JSBool mongo_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - smuassert( cx , "mongo_remove needs 2 or 3 arguments" , argc == 2 || argc == 3 ); - smuassert( cx , "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) ); - - Convertor c( cx ); - if ( c.getBoolean( obj , "readOnly" ) ) { - JS_ReportError( cx , "js db in read only mode - mongo_remove" ); - return JS_FALSE; - } - - DBClientWithCommands * conn = getConnection( cx, obj ); - uassert( 10251 , "no connection!" , conn ); - - string ns = c.toString( argv[0] ); - BSONObj o = c.toObject( argv[1] ); - bool justOne = false; - if ( argc > 2 ) - justOne = c.toBoolean( argv[2] ); - - try { - conn->remove( ns , o , justOne ); - return JS_TRUE; - } - catch ( std::exception& e ) { - JS_ReportError( cx , e.what() ); - return JS_FALSE; - } - - catch ( ... ) { - JS_ReportError( cx , "error doing remove" ); - return JS_FALSE; - } - - } - - JSFunctionSpec mongo_functions[] = { - { "find" , mongo_find , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "update" , mongo_update , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "insert" , mongo_insert , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "remove" , mongo_remove , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { 0 } - }; - - // ------------- db_collection ------------- - - JSBool db_collection_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - smuassert( cx , "db_collection_constructor wrong args" , argc == 4 ); - assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) ); - assert( JS_SetProperty( cx , obj , "_db" , &(argv[1]) ) ); - assert( JS_SetProperty( cx , obj , "_shortName" , &(argv[2]) ) ); - assert( JS_SetProperty( cx , obj , "_fullName" , &(argv[3]) ) ); - - Convertor c(cx); - if ( haveLocalShardingInfo( c.toString( argv[3] ) ) ) { - JS_ReportError( cx , "can't use sharded collection from db.eval" ); - return JS_FALSE; - } - - return JS_TRUE; - } - - JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) { - if ( flags & JSRESOLVE_ASSIGNING ) - return JS_TRUE; - - Convertor c( cx ); - string collname = c.toString( id ); - - if ( isSpecialName( collname ) ) - return JS_TRUE; - - if ( obj == c.getGlobalPrototype( "DBCollection" ) ) - return JS_TRUE; - - JSObject * proto = JS_GetPrototype( cx , obj ); - if ( c.hasProperty( obj , collname.c_str() ) || ( proto && c.hasProperty( proto , collname.c_str() ) ) ) - return JS_TRUE; - - string name = c.toString( c.getProperty( obj , "_shortName" ) ); - name += "."; - name += collname; - - jsval db = c.getProperty( obj , "_db" ); - if ( ! JSVAL_IS_OBJECT( db ) ) - return JS_TRUE; - - JSObject * coll = doCreateCollection( cx , JSVAL_TO_OBJECT( db ) , name ); - if ( ! coll ) - return JS_FALSE; - c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) ); - *objp = obj; - return JS_TRUE; - } - - JSClass db_collection_class = { - "DBCollection" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, (JSResolveOp)(&db_collection_resolve) , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - - JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName ) { - Convertor c(cx); - - assert( c.hasProperty( db , "_mongo" ) ); - assert( c.hasProperty( db , "_name" ) ); - - JSObject * coll = JS_NewObject( cx , &db_collection_class , 0 , 0 ); - CHECKNEWOBJECT( coll, cx, "doCreateCollection" ); - c.setProperty( coll , "_mongo" , c.getProperty( db , "_mongo" ) ); - c.setProperty( coll , "_db" , OBJECT_TO_JSVAL( db ) ); - c.setProperty( coll , "_shortName" , c.toval( shortName.c_str() ) ); - - string name = c.toString( c.getProperty( db , "_name" ) ); - name += "." + shortName; - c.setProperty( coll , "_fullName" , c.toval( name.c_str() ) ); - - if ( haveLocalShardingInfo( name ) ) { - JS_ReportError( cx , "can't use sharded collection from db.eval" ); - return 0; - } - - return coll; - } - - // -------------- DB --------------- - - - JSBool db_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - smuassert( cx, "wrong number of arguments to DB" , argc == 2 ); - assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) ); - assert( JS_SetProperty( cx , obj , "_name" , &(argv[1]) ) ); - - return JS_TRUE; - } - - JSBool db_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) { - if ( flags & JSRESOLVE_ASSIGNING ) - return JS_TRUE; - - Convertor c( cx ); - - if ( obj == c.getGlobalPrototype( "DB" ) ) - return JS_TRUE; - - string collname = c.toString( id ); - - if ( isSpecialName( collname ) ) - return JS_TRUE; - - JSObject * proto = JS_GetPrototype( cx , obj ); - if ( proto && c.hasProperty( proto , collname.c_str() ) ) - return JS_TRUE; - - JSObject * coll = doCreateCollection( cx , obj , collname ); - if ( ! coll ) - return JS_FALSE; - c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) ); - - *objp = obj; - return JS_TRUE; - } - - JSClass db_class = { - "DB" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, (JSResolveOp)(&db_resolve) , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - - // -------------- object id ------------- - - JSBool object_id_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - - OID oid; - if ( argc == 0 ) { - oid.init(); - } - else { - smuassert( cx , "object_id_constructor can't take more than 1 param" , argc == 1 ); - string s = c.toString( argv[0] ); - - try { - Scope::validateObjectIdString( s ); - } - catch ( const MsgAssertionException &m ) { - static string error = m.toString(); - JS_ReportError( cx, error.c_str() ); - return JS_FALSE; - } - oid.init( s ); - } - - if ( ! JS_InstanceOf( cx , obj , &object_id_class , 0 ) ) { - obj = JS_NewObject( cx , &object_id_class , 0 , 0 ); - CHECKNEWOBJECT( obj, cx, "object_id_constructor" ); - *rval = OBJECT_TO_JSVAL( obj ); - } - - jsval v = c.toval( oid.str().c_str() ); - assert( JS_SetProperty( cx , obj , "str" , &v ) ); - - return JS_TRUE; - } - - JSClass object_id_class = { - "ObjectId" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSBool object_id_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - return (JSBool) (*rval = c.getProperty( obj , "str" )); - } - - JSFunctionSpec object_id_functions[] = { - { "toString" , object_id_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { 0 } - }; - - // dbpointer - - JSBool dbpointer_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - - if ( argc == 2 ) { - - if ( ! JSVAL_IS_OID( argv[1] ) ) { - JS_ReportError( cx , "2nd arg to DBPointer needs to be oid" ); - return JS_FALSE; - } - - assert( JS_SetProperty( cx , obj , "ns" , &(argv[0]) ) ); - assert( JS_SetProperty( cx , obj , "id" , &(argv[1]) ) ); - return JS_TRUE; - } - else { - JS_ReportError( cx , "DBPointer needs 2 arguments" ); - return JS_FALSE; - } - } - - JSClass dbpointer_class = { - "DBPointer" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSFunctionSpec dbpointer_functions[] = { - { 0 } - }; - - - JSBool dbref_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - - if ( argc == 2 ) { - JSObject * o = JS_NewObject( cx , NULL , NULL, NULL ); - CHECKNEWOBJECT( o, cx, "dbref_constructor" ); - assert( JS_SetProperty( cx, o , "$ref" , &argv[ 0 ] ) ); - assert( JS_SetProperty( cx, o , "$id" , &argv[ 1 ] ) ); - BSONObj bo = c.toObject( o ); - assert( JS_SetPrivate( cx , obj , (void*)(new BSONHolder( bo.getOwned() ) ) ) ); - return JS_TRUE; - } - else { - JS_ReportError( cx , "DBRef needs 2 arguments" ); - assert( JS_SetPrivate( cx , obj , (void*)(new BSONHolder( BSONObj().getOwned() ) ) ) ); - return JS_FALSE; - } - } - - JSClass dbref_class = bson_class; // name will be fixed later - - // UUID ************************** - -#if 0 - JSBool uuid_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - - if( argc == 0 ) { -#if defined(HAVE_UUID) - //uuids::uuid -#else -#endif - JS_ReportError( cx , "UUID needs 1 argument -- UUID(hexstr)" ); - return JS_FALSE; - } - else if ( argc == 1 ) { - - string encoded = c.toString( argv[ 0 ] ); - if( encoded.size() != 32 ) { - JS_ReportError( cx, "expect 32 char hex string to UUID()" ); - return JS_FALSE; - } - - char buf[16]; - for( int i = 0; i < 16; i++ ) { - buf[i] = fromHex(encoded.c_str() + i * 2); - } - -zzz - - assert( JS_SetPrivate( cx, obj, new BinDataHolder( buf, 16 ) ) ); - c.setProperty( obj, "len", c.toval( (double)16 ) ); - c.setProperty( obj, "type", c.toval( (double)3 ) ); - - return JS_TRUE; - } - else { - JS_ReportError( cx , "UUID needs 1 argument -- UUID(hexstr)" ); - return JS_FALSE; - } - } - - JSBool uuid_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - void *holder = JS_GetPrivate( cx, obj ); - assert( holder ); - const char *data = ( ( BinDataHolder* )( holder ) )->c_; - stringstream ss; - ss << "UUID(\"" << toHex(data, 16); - ss << "\")"; - string ret = ss.str(); - return *rval = c.toval( ret.c_str() ); - } - - void uuid_finalize( JSContext * cx , JSObject * obj ) { - Convertor c(cx); - void *holder = JS_GetPrivate( cx, obj ); - if ( holder ) { - delete ( BinDataHolder* )holder; - assert( JS_SetPrivate( cx , obj , 0 ) ); - } - } - - JSClass uuid_class = { - "UUID" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, uuid_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSFunctionSpec uuid_functions[] = { - { "toString" , uuid_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { 0 } - }; - -#endif - - // BinData ************************** - - JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - Convertor c( cx ); - - if ( argc == 2 ) { - - int type = (int)c.toNumber( argv[ 0 ] ); - if( type < 0 || type > 255 ) { - JS_ReportError( cx , "invalid BinData subtype -- range is 0..255 see bsonspec.org" ); - return JS_FALSE; - } - string encoded = c.toString( argv[ 1 ] ); - string decoded; - try { - decoded = base64::decode( encoded ); - } - catch(...) { - JS_ReportError(cx, "BinData could not decode base64 parameter"); - return JS_FALSE; - } - - assert( JS_SetPrivate( cx, obj, new BinDataHolder( decoded.data(), decoded.length() ) ) ); - c.setProperty( obj, "len", c.toval( (double)decoded.length() ) ); - c.setProperty( obj, "type", c.toval( (double)type ) ); - - return JS_TRUE; - } - else { - JS_ReportError( cx , "BinData needs 2 arguments -- BinData(subtype,data)" ); - return JS_FALSE; - } - } - - JSBool bindata_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - int type = (int)c.getNumber( obj , "type" ); - int len = (int)c.getNumber( obj, "len" ); - void *holder = JS_GetPrivate( cx, obj ); - assert( holder ); - const char *data = ( ( BinDataHolder* )( holder ) )->c_; - stringstream ss; - ss << "BinData(" << type << ",\""; - base64::encode( ss, (const char *)data, len ); - ss << "\")"; - string ret = ss.str(); - return *rval = c.toval( ret.c_str() ); - } - - JSBool bindataBase64(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - int len = (int)c.getNumber( obj, "len" ); - void *holder = JS_GetPrivate( cx, obj ); - assert( holder ); - const char *data = ( ( BinDataHolder* )( holder ) )->c_; - stringstream ss; - base64::encode( ss, (const char *)data, len ); - string ret = ss.str(); - return *rval = c.toval( ret.c_str() ); - } - - JSBool bindataAsHex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - int len = (int)c.getNumber( obj, "len" ); - void *holder = JS_GetPrivate( cx, obj ); - assert( holder ); - const char *data = ( ( BinDataHolder* )( holder ) )->c_; - stringstream ss; - ss.setf (ios_base::hex , ios_base::basefield); - ss.fill ('0'); - ss.setf (ios_base::right , ios_base::adjustfield); - for( int i = 0; i < len; i++ ) { - unsigned v = (unsigned char) data[i]; - ss << setw(2) << v; - } - string ret = ss.str(); - return *rval = c.toval( ret.c_str() ); - } - - void bindata_finalize( JSContext * cx , JSObject * obj ) { - Convertor c(cx); - void *holder = JS_GetPrivate( cx, obj ); - if ( holder ) { - delete ( BinDataHolder* )holder; - assert( JS_SetPrivate( cx , obj , 0 ) ); - } - } - - JSClass bindata_class = { - "BinData" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, bindata_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSFunctionSpec bindata_functions[] = { - { "toString" , bindata_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "hex", bindataAsHex, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "base64", bindataBase64, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { 0 } - }; - - // Map - - bool specialMapString( const string& s ) { - return s == "put" || s == "get" || s == "_get" || s == "values" || s == "_data" || s == "constructor" ; - } - - JSBool map_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - if ( argc > 0 ) { - JS_ReportError( cx , "Map takes no arguments" ); - return JS_FALSE; - } - - JSObject * array = JS_NewObject( cx , 0 , 0 , 0 ); - CHECKNEWOBJECT( array, cx, "map_constructor" ); - - jsval a = OBJECT_TO_JSVAL( array ); - JS_SetProperty( cx , obj , "_data" , &a ); - - return JS_TRUE; - } - - JSBool map_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp ) { - Convertor c(cx); - if ( specialMapString( c.toString( idval ) ) ) - return JS_TRUE; - - log() << "illegal prop access: " << c.toString( idval ) << endl; - JS_ReportError( cx , "can't use array access with Map" ); - return JS_FALSE; - } - - JSClass map_class = { - "Map" , JSCLASS_HAS_PRIVATE , - map_prop, JS_PropertyStub, map_prop, map_prop, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSFunctionSpec map_functions[] = { - { 0 } - }; - - - // ----- - - JSClass timestamp_class = { - "Timestamp" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSBool timestamp_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - smuassert( cx , "Timestamp needs 0 or 2 args" , argc == 0 || argc == 2 ); - - if ( ! JS_InstanceOf( cx , obj , ×tamp_class , 0 ) ) { - obj = JS_NewObject( cx , ×tamp_class , 0 , 0 ); - CHECKNEWOBJECT( obj, cx, "timestamp_constructor" ); - *rval = OBJECT_TO_JSVAL( obj ); - } - - Convertor c( cx ); - if ( argc == 0 ) { - c.setProperty( obj, "t", c.toval( 0.0 ) ); - c.setProperty( obj, "i", c.toval( 0.0 ) ); - } - else { - c.setProperty( obj, "t", argv[ 0 ] ); - c.setProperty( obj, "i", argv[ 1 ] ); - } - - return JS_TRUE; - } - - - JSClass numberlong_class = { - "NumberLong" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSBool numberlong_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - smuassert( cx , "NumberLong needs 0 or 1 args" , argc == 0 || argc == 1 ); - - if ( ! JS_InstanceOf( cx , obj , &numberlong_class , 0 ) ) { - obj = JS_NewObject( cx , &numberlong_class , 0 , 0 ); - CHECKNEWOBJECT( obj, cx, "numberlong_constructor" ); - *rval = OBJECT_TO_JSVAL( obj ); - } - - Convertor c( cx ); - if ( argc == 0 ) { - c.setProperty( obj, "floatApprox", c.toval( 0.0 ) ); - } - else if ( JSVAL_IS_NUMBER( argv[ 0 ] ) ) { - c.setProperty( obj, "floatApprox", argv[ 0 ] ); - } - else { - string num = c.toString( argv[ 0 ] ); - //PRINT(num); - const char *numStr = num.c_str(); - long long n; - try { - n = parseLL( numStr ); - //PRINT(n); - } - catch ( const AssertionException & ) { - smuassert( cx , "could not convert string to long long" , false ); - } - c.makeLongObj( n, obj ); - } - - return JS_TRUE; - } - - JSBool numberlong_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - return *rval = c.toval( double( c.toNumberLongUnsafe( obj ) ) ); - } - - JSBool numberlong_tonumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - return numberlong_valueof( cx, obj, argc, argv, rval ); - } - - JSBool numberlong_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - stringstream ss; - long long val = c.toNumberLongUnsafe( obj ); - const long long limit = 2LL << 30; - - if ( val <= -limit || limit <= val ) - ss << "NumberLong(\"" << val << "\")"; - else - ss << "NumberLong(" << val << ")"; - - string ret = ss.str(); - return *rval = c.toval( ret.c_str() ); - } - - JSFunctionSpec numberlong_functions[] = { - { "valueOf" , numberlong_valueof , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "toNumber" , numberlong_tonumber , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "toString" , numberlong_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { 0 } - }; - - JSClass numberint_class = { - "NumberInt" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSBool numberint_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - smuassert( cx , "NumberInt needs 0 or 1 args" , argc == 0 || argc == 1 ); - - if ( ! JS_InstanceOf( cx , obj , &numberint_class , 0 ) ) { - obj = JS_NewObject( cx , &numberint_class , 0 , 0 ); - CHECKNEWOBJECT( obj, cx, "numberint_constructor" ); - *rval = OBJECT_TO_JSVAL( obj ); - } - - Convertor c( cx ); - if ( argc == 0 ) { - c.setProperty( obj, "floatApprox", c.toval( 0.0 ) ); - } - else if ( JSVAL_IS_NUMBER( argv[ 0 ] ) ) { - c.setProperty( obj, "floatApprox", argv[ 0 ] ); - } - else { - string num = c.toString( argv[ 0 ] ); - //PRINT(num); - const char *numStr = num.c_str(); - int n; - try { - n = (int) parseLL( numStr ); - //PRINT(n); - } - catch ( const AssertionException & ) { - smuassert( cx , "could not convert string to integer" , false ); - } - c.makeIntObj( n, obj ); - } - - return JS_TRUE; - } - - JSBool numberint_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - return *rval = c.toval( double( c.toNumberInt( obj ) ) ); - } - - JSBool numberint_tonumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - return numberint_valueof( cx, obj, argc, argv, rval ); - } - - JSBool numberint_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - int val = c.toNumberInt( obj ); - string ret = str::stream() << "NumberInt(" << val << ")"; - return *rval = c.toval( ret.c_str() ); - } - - JSBool numberint_tojson(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - Convertor c(cx); - int val = c.toNumberInt( obj ); - string ret = str::stream() << val; - return *rval = c.toval( ret.c_str() ); - } - - - JSFunctionSpec numberint_functions[] = { - { "valueOf" , numberint_valueof , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "toNumber" , numberint_tonumber , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "toString" , numberint_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { "tojson" , numberint_tojson , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } , - { 0 } - }; - - JSClass minkey_class = { - "MinKey" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - JSClass maxkey_class = { - "MaxKey" , JSCLASS_HAS_PRIVATE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - // dbquery - - JSBool dbquery_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) { - smuassert( cx , "DDQuery needs at least 4 args" , argc >= 4 ); - - Convertor c(cx); - c.setProperty( obj , "_mongo" , argv[0] ); - c.setProperty( obj , "_db" , argv[1] ); - c.setProperty( obj , "_collection" , argv[2] ); - c.setProperty( obj , "_ns" , argv[3] ); - - if ( argc > 4 && JSVAL_IS_OBJECT( argv[4] ) ) - c.setProperty( obj , "_query" , argv[4] ); - else { - JSObject * temp = JS_NewObject( cx , 0 , 0 , 0 ); - CHECKNEWOBJECT( temp, cx, "dbquery_constructor" ); - c.setProperty( obj , "_query" , OBJECT_TO_JSVAL( temp ) ); - } - - if ( argc > 5 && JSVAL_IS_OBJECT( argv[5] ) ) - c.setProperty( obj , "_fields" , argv[5] ); - else - c.setProperty( obj , "_fields" , JSVAL_NULL ); - - - if ( argc > 6 && JSVAL_IS_NUMBER( argv[6] ) ) - c.setProperty( obj , "_limit" , argv[6] ); - else - c.setProperty( obj , "_limit" , JSVAL_ZERO ); - - if ( argc > 7 && JSVAL_IS_NUMBER( argv[7] ) ) - c.setProperty( obj , "_skip" , argv[7] ); - else - c.setProperty( obj , "_skip" , JSVAL_ZERO ); - - if ( argc > 8 && JSVAL_IS_NUMBER( argv[8] ) ) - c.setProperty( obj , "_batchSize" , argv[8] ); - else - c.setProperty( obj , "_batchSize" , JSVAL_ZERO ); - - if ( argc > 9 && JSVAL_IS_NUMBER( argv[9] ) ) - c.setProperty( obj , "_options" , argv[8] ); - else - c.setProperty( obj , "_options" , JSVAL_ZERO ); - - - c.setProperty( obj , "_cursor" , JSVAL_NULL ); - c.setProperty( obj , "_numReturned" , JSVAL_ZERO ); - c.setProperty( obj , "_special" , JSVAL_FALSE ); - - return JS_TRUE; - } - - JSBool dbquery_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) { - if ( flags & JSRESOLVE_ASSIGNING ) - return JS_TRUE; - - if ( ! JSVAL_IS_NUMBER( id ) ) - return JS_TRUE; - - jsval val = JSVAL_VOID; - assert( JS_CallFunctionName( cx , obj , "arrayAccess" , 1 , &id , &val ) ); - Convertor c(cx); - c.setProperty( obj , c.toString( id ).c_str() , val ); - *objp = obj; - return JS_TRUE; - } - - JSClass dbquery_class = { - "DBQuery" , JSCLASS_NEW_RESOLVE , - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, (JSResolveOp)(&dbquery_resolve) , JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS - }; - - // ---- other stuff ---- - - void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ) { - - assert( JS_InitClass( cx , global , 0 , &mongo_class , local ? mongo_local_constructor : mongo_external_constructor , 0 , 0 , mongo_functions , 0 , 0 ) ); - - assert( JS_InitClass( cx , global , 0 , &object_id_class , object_id_constructor , 0 , 0 , object_id_functions , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &db_class , db_constructor , 2 , 0 , 0 , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &db_collection_class , db_collection_constructor , 4 , 0 , 0 , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &internal_cursor_class , internal_cursor_constructor , 0 , 0 , internal_cursor_functions , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &dbquery_class , dbquery_constructor , 0 , 0 , 0 , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &dbpointer_class , dbpointer_constructor , 0 , 0 , dbpointer_functions , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) ); -// assert( JS_InitClass( cx , global , 0 , &uuid_class , uuid_constructor , 0 , 0 , uuid_functions , 0 , 0 ) ); - - assert( JS_InitClass( cx , global , 0 , ×tamp_class , timestamp_constructor , 0 , 0 , 0 , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &numberlong_class , numberlong_constructor , 0 , 0 , numberlong_functions , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &numberint_class , numberint_constructor , 0 , 0 , numberint_functions , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) ); - - assert( JS_InitClass( cx , global , 0 , &map_class , map_constructor , 0 , 0 , map_functions , 0 , 0 ) ); - - assert( JS_InitClass( cx , global , 0 , &bson_ro_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &bson_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) ); - - static const char *dbrefName = "DBRef"; - dbref_class.name = dbrefName; - assert( JS_InitClass( cx , global , 0 , &dbref_class , dbref_constructor , 2 , 0 , bson_functions , 0 , 0 ) ); - - scope->execCoreFiles(); - } - - bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , jsval val , JSObject * o ) { - - if ( JS_InstanceOf( c->_context , o , &object_id_class , 0 ) ) { - OID oid; - oid.init( c->getString( o , "str" ) ); - b.append( name , oid ); - return true; - } - - if ( JS_InstanceOf( c->_context , o , &minkey_class , 0 ) ) { - b.appendMinKey( name ); - return true; - } - - if ( JS_InstanceOf( c->_context , o , &maxkey_class , 0 ) ) { - b.appendMaxKey( name ); - return true; - } - - if ( JS_InstanceOf( c->_context , o , ×tamp_class , 0 ) ) { - b.appendTimestamp( name , (unsigned long long)c->getNumber( o , "t" ) , (unsigned int )c->getNumber( o , "i" ) ); - return true; - } - - if ( JS_InstanceOf( c->_context , o , &numberlong_class , 0 ) ) { - b.append( name , c->toNumberLongUnsafe( o ) ); - return true; - } - - if ( JS_InstanceOf( c->_context , o , &numberint_class , 0 ) ) { - b.append( name , c->toNumberInt( o ) ); - return true; - } - - if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ) { - b.appendDBRef( name , c->getString( o , "ns" ) , c->toOID( c->getProperty( o , "id" ) ) ); - return true; - } - - if ( JS_InstanceOf( c->_context , o , &bindata_class , 0 ) ) { - void *holder = JS_GetPrivate( c->_context , o ); - const char *data = ( ( BinDataHolder * )( holder ) )->c_; - b.appendBinData( name , - (int)(c->getNumber( o , "len" )) , (BinDataType)((char)(c->getNumber( o , "type" ) ) ) , - data - ); - return true; - } - -#if defined( SM16 ) || defined( MOZJS ) -#warning dates do not work in your version of spider monkey - { - jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o ); - if ( d ) { - b.appendDate( name , Date_t(d) ); - return true; - } - } -#elif defined( XULRUNNER ) - if ( JS_InstanceOf( c->_context , o, globalSMEngine->_dateClass , 0 ) ) { - jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o ); - b.appendDate( name , Date_t(d) ); - return true; - } -#else - if ( JS_InstanceOf( c->_context , o, &js_DateClass , 0 ) ) { - jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o ); - long long d2 = (long long)d; - b.appendDate( name , Date_t((unsigned long long)d2) ); - return true; - } -#endif - - - if ( JS_InstanceOf( c->_context , o , &dbquery_class , 0 ) || - JS_InstanceOf( c->_context , o , &mongo_class , 0 ) || - JS_InstanceOf( c->_context , o , &db_collection_class , 0 ) ) { - b.append( name , c->toString( val ) ); - return true; - } - -#if defined( XULRUNNER ) - if ( JS_InstanceOf( c->_context , o , globalSMEngine->_regexClass , 0 ) ) { - c->appendRegex( b , name , c->toString( val ) ); - return true; - } -#elif defined( SM18 ) - if ( JS_InstanceOf( c->_context , o , &js_RegExpClass , 0 ) ) { - c->appendRegex( b , name , c->toString( val ) ); - return true; - } -#endif - - return false; - } - - bool isDate( JSContext * cx , JSObject * o ) { -#if defined( SM16 ) || defined( MOZJS ) || defined( XULRUNNER ) - return js_DateGetMsecSinceEpoch( cx , o ) != 0; -#else - return JS_InstanceOf( cx , o, &js_DateClass, 0 ); -#endif - } - -} diff --git a/scripting/utils.cpp b/scripting/utils.cpp deleted file mode 100644 index 612b173fdf8..00000000000 --- a/scripting/utils.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// utils.cpp -/* - * Copyright (C) 2010 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 "pch.h" -#include "engine.h" -#include "../util/md5.hpp" -#include "../util/version.h" - -namespace mongo { - - void installBenchmarkSystem( Scope& scope ); - - BSONObj jsmd5( const BSONObj &a, void* data ) { - uassert( 10261 , "js md5 needs a string" , a.firstElement().type() == String ); - const char * s = a.firstElement().valuestrsafe(); - - md5digest d; - md5_state_t st; - md5_init(&st); - md5_append( &st , (const md5_byte_t*)s , strlen( s ) ); - md5_finish(&st, d); - - return BSON( "" << digestToString( d ) ); - } - - BSONObj JSVersion( const BSONObj& args, void* data ) { - cout << "version: " << versionString << endl; - if ( strstr( versionString , "+" ) ) - printGitVersion(); - return BSONObj(); - } - - - BSONObj JSSleep(const mongo::BSONObj &args, void* data) { - assert( args.nFields() == 1 ); - assert( args.firstElement().isNumber() ); - int ms = int( args.firstElement().number() ); - { - auto_ptr< ScriptEngine::Unlocker > u = globalScriptEngine->newThreadUnlocker(); - sleepmillis( ms ); - } - - BSONObjBuilder b; - b.appendUndefined( "" ); - return b.obj(); - } - - // --------------------------------- - // ---- installer -------- - // --------------------------------- - - void installGlobalUtils( Scope& scope ) { - scope.injectNative( "hex_md5" , jsmd5 ); - scope.injectNative( "version" , JSVersion ); - scope.injectNative( "sleep" , JSSleep ); - - installBenchmarkSystem( scope ); - } - -} - - diff --git a/scripting/v8_db.cpp b/scripting/v8_db.cpp deleted file mode 100644 index f2f53338763..00000000000 --- a/scripting/v8_db.cpp +++ /dev/null @@ -1,1049 +0,0 @@ -// v8_db.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. - */ - -#if defined(_WIN32) -/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with - our usage of boost */ -#include "boost/cstdint.hpp" -using namespace boost; -#define V8STDINT_H_ -#endif - -#include "v8_wrapper.h" -#include "v8_utils.h" -#include "engine_v8.h" -#include "v8_db.h" -#include "util/base64.h" -#include "util/text.h" -#include "../client/syncclusterconnection.h" -#include "../s/d_logic.h" -#include <iostream> - -using namespace std; -using namespace v8; - -namespace mongo { - -#define DDD(x) - - v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( V8Scope* scope, bool local ) { - v8::Handle<v8::FunctionTemplate> mongo; - if ( local ) { - mongo = scope->createV8Function(mongoConsLocal); - } - else { - mongo = scope->createV8Function(mongoConsExternal); - } - mongo->InstanceTemplate()->SetInternalFieldCount( 1 ); - v8::Handle<v8::Template> proto = mongo->PrototypeTemplate(); - scope->injectV8Function("find", mongoFind, proto); - scope->injectV8Function("insert", mongoInsert, proto); - scope->injectV8Function("remove", mongoRemove, proto); - scope->injectV8Function("update", mongoUpdate, proto); - - v8::Handle<FunctionTemplate> ic = scope->createV8Function(internalCursorCons); - ic->InstanceTemplate()->SetInternalFieldCount( 1 ); - v8::Handle<v8::Template> icproto = ic->PrototypeTemplate(); - scope->injectV8Function("next", internalCursorNext, icproto); - scope->injectV8Function("hasNext", internalCursorHasNext, icproto); - scope->injectV8Function("objsLeftInBatch", internalCursorObjsLeftInBatch, icproto); - proto->Set( scope->getV8Str( "internalCursor" ) , ic ); - - return mongo; - } - - v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit); - v8::Local<v8::Template> proto = numberLong->PrototypeTemplate(); - scope->injectV8Function("valueOf", numberLongValueOf, proto); - scope->injectV8Function("toNumber", numberLongToNumber, proto); - scope->injectV8Function("toString", numberLongToString, proto); - - return numberLong; - } - - v8::Handle<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> numberInt = scope->createV8Function(numberIntInit); - v8::Local<v8::Template> proto = numberInt->PrototypeTemplate(); - scope->injectV8Function("valueOf", numberIntValueOf, proto); - scope->injectV8Function("toNumber", numberIntToNumber, proto); - scope->injectV8Function("toString", numberIntToString, proto); - - return numberInt; - } - - v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit); - binData->InstanceTemplate()->SetInternalFieldCount(1); - v8::Local<v8::Template> proto = binData->PrototypeTemplate(); - scope->injectV8Function("toString", binDataToString, proto); - scope->injectV8Function("base64", binDataToBase64, proto); - scope->injectV8Function("hex", binDataToHex, proto); - return binData; - } - - v8::Handle<v8::FunctionTemplate> getUUIDFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(uuidInit); - templ->InstanceTemplate()->SetInternalFieldCount(1); - v8::Local<v8::Template> proto = templ->PrototypeTemplate(); - scope->injectV8Function("toString", binDataToString, proto); - scope->injectV8Function("base64", binDataToBase64, proto); - scope->injectV8Function("hex", binDataToHex, proto); - return templ; - } - - v8::Handle<v8::FunctionTemplate> getMD5FunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(md5Init); - templ->InstanceTemplate()->SetInternalFieldCount(1); - v8::Local<v8::Template> proto = templ->PrototypeTemplate(); - scope->injectV8Function("toString", binDataToString, proto); - scope->injectV8Function("base64", binDataToBase64, proto); - scope->injectV8Function("hex", binDataToHex, proto); - return templ; - } - - v8::Handle<v8::FunctionTemplate> getHexDataFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(hexDataInit); - templ->InstanceTemplate()->SetInternalFieldCount(1); - v8::Local<v8::Template> proto = templ->PrototypeTemplate(); - scope->injectV8Function("toString", binDataToString, proto); - scope->injectV8Function("base64", binDataToBase64, proto); - scope->injectV8Function("hex", binDataToHex, proto); - return templ; - } - - v8::Handle<v8::FunctionTemplate> getTimestampFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> ts = scope->createV8Function(dbTimestampInit); - /* v8::Local<v8::Template> proto = */ ts->PrototypeTemplate(); - ts->InstanceTemplate()->SetInternalFieldCount( 1 ); - - return ts; - } - -// void installDBTypes( V8Scope* scope, Handle<ObjectTemplate>& global ) { -// v8::Handle<v8::FunctionTemplate> db = scope->createV8Function(dbInit); -// db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback ); -// global->Set(v8::String::New("DB") , db ); -// -// v8::Handle<v8::FunctionTemplate> dbCollection = scope->createV8Function(collectionInit); -// dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback ); -// global->Set(v8::String::New("DBCollection") , dbCollection ); -// -// -// v8::Handle<v8::FunctionTemplate> dbQuery = scope->createV8Function(dbQueryInit); -// dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess ); -// global->Set(v8::String::New("DBQuery") , dbQuery ); -// -// global->Set( v8::String::New("ObjectId") , newV8Function< objectIdInit >(scope) ); -// -// global->Set( v8::String::New("DBRef") , newV8Function< dbRefInit >(scope) ); -// -// global->Set( v8::String::New("DBPointer") , newV8Function< dbPointerInit >(scope) ); -// -// global->Set( v8::String::New("BinData") , getBinDataFunctionTemplate(scope) ); -// -// global->Set( v8::String::New("NumberLong") , getNumberLongFunctionTemplate(scope) ); -// -// global->Set( v8::String::New("Timestamp") , getTimestampFunctionTemplate(scope) ); -// } - - void installDBTypes( V8Scope* scope, v8::Handle<v8::Object>& global ) { - v8::Handle<v8::FunctionTemplate> db = scope->createV8Function(dbInit); - db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback ); - global->Set(scope->getV8Str("DB") , db->GetFunction() ); - v8::Handle<v8::FunctionTemplate> dbCollection = scope->createV8Function(collectionInit); - dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback ); - global->Set(scope->getV8Str("DBCollection") , dbCollection->GetFunction() ); - - - v8::Handle<v8::FunctionTemplate> dbQuery = scope->createV8Function(dbQueryInit); - dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess ); - global->Set(scope->getV8Str("DBQuery") , dbQuery->GetFunction() ); - - scope->injectV8Function("ObjectId", objectIdInit, global); - scope->injectV8Function("DBRef", dbRefInit, global); - scope->injectV8Function("DBPointer", dbPointerInit, global); - - global->Set( scope->getV8Str("BinData") , getBinDataFunctionTemplate(scope)->GetFunction() ); - global->Set( scope->getV8Str("UUID") , getUUIDFunctionTemplate(scope)->GetFunction() ); - global->Set( scope->getV8Str("MD5") , getMD5FunctionTemplate(scope)->GetFunction() ); - global->Set( scope->getV8Str("HexData") , getHexDataFunctionTemplate(scope)->GetFunction() ); - global->Set( scope->getV8Str("NumberLong") , getNumberLongFunctionTemplate(scope)->GetFunction() ); - global->Set( scope->getV8Str("NumberInt") , getNumberIntFunctionTemplate(scope)->GetFunction() ); - global->Set( scope->getV8Str("Timestamp") , getTimestampFunctionTemplate(scope)->GetFunction() ); - - BSONObjBuilder b; - b.appendMaxKey( "" ); - b.appendMinKey( "" ); - BSONObj o = b.obj(); - BSONObjIterator i( o ); - global->Set( scope->getV8Str("MaxKey"), scope->mongoToV8Element( i.next() ) ); - global->Set( scope->getV8Str("MinKey"), scope->mongoToV8Element( i.next() ) ); - - global->Get( scope->getV8Str( "Object" ) )->ToObject()->Set( scope->getV8Str("bsonsize") , scope->createV8Function(bsonsize)->GetFunction() ); - } - - void destroyConnection( Persistent<Value> self, void* parameter) { - delete static_cast<DBClientBase*>(parameter); - self.Dispose(); - self.Clear(); - } - - Handle<Value> mongoConsExternal(V8Scope* scope, const Arguments& args) { - - char host[255]; - - if ( args.Length() > 0 && args[0]->IsString() ) { - assert( args[0]->ToString()->Utf8Length() < 250 ); - args[0]->ToString()->WriteAscii( host ); - } - else { - strcpy( host , "127.0.0.1" ); - } - - string errmsg; - ConnectionString cs = ConnectionString::parse( host , errmsg ); - if ( ! cs.isValid() ) - return v8::ThrowException( v8::String::New( errmsg.c_str() ) ); - - - DBClientWithCommands * conn; - { - V8Unlock ul; - conn = cs.connect( errmsg ); - } - if ( ! conn ) - return v8::ThrowException( v8::String::New( errmsg.c_str() ) ); - - Persistent<v8::Object> self = Persistent<v8::Object>::New( args.Holder() ); - self.MakeWeak( conn , destroyConnection ); - - { - V8Unlock ul; - ScriptEngine::runConnectCallback( *conn ); - } - - args.This()->SetInternalField( 0 , External::New( conn ) ); - args.This()->Set( scope->getV8Str( "slaveOk" ) , Boolean::New( false ) ); - args.This()->Set( scope->getV8Str( "host" ) , scope->getV8Str( host ) ); - - return v8::Undefined(); - } - - Handle<Value> mongoConsLocal(V8Scope* scope, const Arguments& args) { - - if ( args.Length() > 0 ) - return v8::ThrowException( v8::String::New( "local Mongo constructor takes no args" ) ); - - DBClientBase * conn; - { - V8Unlock ul; - conn = createDirectClient(); - } - - Persistent<v8::Object> self = Persistent<v8::Object>::New( args.This() ); - self.MakeWeak( conn , destroyConnection ); - - // NOTE I don't believe the conn object will ever be freed. - args.This()->SetInternalField( 0 , External::New( conn ) ); - args.This()->Set( scope->getV8Str( "slaveOk" ) , Boolean::New( false ) ); - args.This()->Set( scope->getV8Str( "host" ) , scope->getV8Str( "EMBEDDED" ) ); - - return v8::Undefined(); - } - - - // --- - -#ifdef _WIN32 -#define GETNS char * ns = new char[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns ); -#else -#define GETNS char ns[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns ); -#endif - - DBClientBase * getConnection( const Arguments& args ) { - Local<External> c = External::Cast( *(args.This()->GetInternalField( 0 )) ); - DBClientBase * conn = (DBClientBase*)(c->Value()); - assert( conn ); - return conn; - } - - // ---- real methods - - void destroyCursor( Persistent<Value> self, void* parameter) { - delete static_cast<mongo::DBClientCursor*>(parameter); - self.Dispose(); - self.Clear(); - } - - /** - 0 - namespace - 1 - query - 2 - fields - 3 - limit - 4 - skip - */ - Handle<Value> mongoFind(V8Scope* scope, const Arguments& args) { - HandleScope handle_scope; - - jsassert( args.Length() == 7 , "find needs 7 args" ); - jsassert( args[1]->IsObject() , "needs to be an object" ); - DBClientBase * conn = getConnection( args ); - GETNS; - - BSONObj q = scope->v8ToMongo( args[1]->ToObject() ); - DDD( "query:" << q ); - - BSONObj fields; - bool haveFields = args[2]->IsObject() && args[2]->ToObject()->GetPropertyNames()->Length() > 0; - if ( haveFields ) - fields = scope->v8ToMongo( args[2]->ToObject() ); - - Local<v8::Object> mongo = args.This(); - Local<v8::Value> slaveOkVal = mongo->Get( scope->getV8Str( "slaveOk" ) ); - jsassert( slaveOkVal->IsBoolean(), "slaveOk member invalid" ); - bool slaveOk = slaveOkVal->BooleanValue(); - - try { - auto_ptr<mongo::DBClientCursor> cursor; - int nToReturn = (int)(args[3]->ToNumber()->Value()); - int nToSkip = (int)(args[4]->ToNumber()->Value()); - int batchSize = (int)(args[5]->ToNumber()->Value()); - int options = (int)(args[6]->ToNumber()->Value()); - { - V8Unlock u; - cursor = conn->query( ns, q , nToReturn , nToSkip , haveFields ? &fields : 0, options | ( slaveOk ? QueryOption_SlaveOk : 0 ) , batchSize ); - if ( ! cursor.get() ) - return v8::ThrowException( v8::String::New( "error doing query: failed" ) ); - } - v8::Function * cons = (v8::Function*)( *( mongo->Get( scope->getV8Str( "internalCursor" ) ) ) ); - assert( cons ); - - Persistent<v8::Object> c = Persistent<v8::Object>::New( cons->NewInstance() ); - c.MakeWeak( cursor.get() , destroyCursor ); - - c->SetInternalField( 0 , External::New( cursor.release() ) ); - return handle_scope.Close(c); - } - catch ( ... ) { - return v8::ThrowException( v8::String::New( "socket error on query" ) ); - } - } - - v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments& args) { - jsassert( args.Length() == 2 , "insert needs 2 args" ); - jsassert( args[1]->IsObject() , "have to insert an object" ); - - if ( args.This()->Get( scope->getV8Str( "readOnly" ) )->BooleanValue() ) - return v8::ThrowException( v8::String::New( "js db in read only mode" ) ); - - DBClientBase * conn = getConnection( args ); - GETNS; - - v8::Handle<v8::Object> in = args[1]->ToObject(); - - if ( ! in->Has( scope->getV8Str( "_id" ) ) ) { - v8::Handle<v8::Value> argv[1]; - in->Set( scope->getV8Str( "_id" ) , scope->getObjectIdCons()->NewInstance( 0 , argv ) ); - } - - BSONObj o = scope->v8ToMongo( in ); - - DDD( "want to save : " << o.jsonString() ); - try { - V8Unlock u; - conn->insert( ns , o ); - } - catch ( ... ) { - return v8::ThrowException( v8::String::New( "socket error on insert" ) ); - } - - return v8::Undefined(); - } - - v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments& args) { - jsassert( args.Length() == 2 || args.Length() == 3 , "remove needs 2 args" ); - jsassert( args[1]->IsObject() , "have to remove an object template" ); - - if ( args.This()->Get( scope->getV8Str( "readOnly" ) )->BooleanValue() ) - return v8::ThrowException( v8::String::New( "js db in read only mode" ) ); - - DBClientBase * conn = getConnection( args ); - GETNS; - - v8::Handle<v8::Object> in = args[1]->ToObject(); - BSONObj o = scope->v8ToMongo( in ); - - bool justOne = false; - if ( args.Length() > 2 ) { - justOne = args[2]->BooleanValue(); - } - - DDD( "want to remove : " << o.jsonString() ); - try { - V8Unlock u; - conn->remove( ns , o , justOne ); - } - catch ( ... ) { - return v8::ThrowException( v8::String::New( "socket error on remove" ) ); - } - - return v8::Undefined(); - } - - v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments& args) { - jsassert( args.Length() >= 3 , "update needs at least 3 args" ); - jsassert( args[1]->IsObject() , "1st param to update has to be an object" ); - jsassert( args[2]->IsObject() , "2nd param to update has to be an object" ); - - if ( args.This()->Get( scope->getV8Str( "readOnly" ) )->BooleanValue() ) - return v8::ThrowException( v8::String::New( "js db in read only mode" ) ); - - DBClientBase * conn = getConnection( args ); - GETNS; - - v8::Handle<v8::Object> q = args[1]->ToObject(); - v8::Handle<v8::Object> o = args[2]->ToObject(); - - bool upsert = args.Length() > 3 && args[3]->IsBoolean() && args[3]->ToBoolean()->Value(); - bool multi = args.Length() > 4 && args[4]->IsBoolean() && args[4]->ToBoolean()->Value(); - - try { - BSONObj q1 = scope->v8ToMongo( q ); - BSONObj o1 = scope->v8ToMongo( o ); - V8Unlock u; - conn->update( ns , q1 , o1 , upsert, multi ); - } - catch ( ... ) { - return v8::ThrowException( v8::String::New( "socket error on remove" ) ); - } - - return v8::Undefined(); - } - - - - - // --- cursor --- - - mongo::DBClientCursor * getCursor( const Arguments& args ) { - Local<External> c = External::Cast( *(args.This()->GetInternalField( 0 ) ) ); - - mongo::DBClientCursor * cursor = (mongo::DBClientCursor*)(c->Value()); - return cursor; - } - - v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Arguments& args) { - return v8::Undefined(); - } - - v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor * cursor = getCursor( args ); - if ( ! cursor ) - return v8::Undefined(); - BSONObj o; - { - V8Unlock u; - o = cursor->next(); - } - bool ro = false; - if (args.This()->Has(scope->V8STR_RO)) - ro = args.This()->Get(scope->V8STR_RO)->BooleanValue(); - return scope->mongoToLZV8( o, false, ro ); - } - - v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor * cursor = getCursor( args ); - if ( ! cursor ) - return Boolean::New( false ); - bool ret; - { - V8Unlock u; - ret = cursor->more(); - } - return Boolean::New( ret ); - } - - v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor * cursor = getCursor( args ); - if ( ! cursor ) - return v8::Number::New( (double) 0 ); - int ret; - { - V8Unlock u; - ret = cursor->objsLeftInBatch(); - } - return v8::Number::New( (double) ret ); - } - -// v8::Handle<v8::Value> internalCursorReadOnly(V8Scope* scope, const v8::Arguments& args) { -// Local<v8::Object> cursor = args.This(); -// cursor->Set(scope->V8STR_RO, v8::Undefined()); -// return cursor; -// } - - // --- DB ---- - - v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args) { - assert( args.Length() == 2 ); - - args.This()->Set( scope->getV8Str( "_mongo" ) , args[0] ); - args.This()->Set( scope->getV8Str( "_name" ) , args[1] ); - - for ( int i=0; i<args.Length(); i++ ) - assert( ! args[i]->IsUndefined() ); - - return v8::Undefined(); - } - - v8::Handle<v8::Value> collectionInit( V8Scope* scope, const v8::Arguments& args ) { - assert( args.Length() == 4 ); - - args.This()->Set( scope->getV8Str( "_mongo" ) , args[0] ); - args.This()->Set( scope->getV8Str( "_db" ) , args[1] ); - args.This()->Set( scope->getV8Str( "_shortName" ) , args[2] ); - args.This()->Set( scope->getV8Str( "_fullName" ) , args[3] ); - - if ( haveLocalShardingInfo( toSTLString( args[3] ) ) ) - return v8::ThrowException( v8::String::New( "can't use sharded collection from db.eval" ) ); - - for ( int i=0; i<args.Length(); i++ ) - assert( ! args[i]->IsUndefined() ); - - return v8::Undefined(); - } - - v8::Handle<v8::Value> dbQueryInit( V8Scope* scope, const v8::Arguments& args ) { - - v8::Handle<v8::Object> t = args.This(); - - assert( args.Length() >= 4 ); - - t->Set( scope->getV8Str( "_mongo" ) , args[0] ); - t->Set( scope->getV8Str( "_db" ) , args[1] ); - t->Set( scope->getV8Str( "_collection" ) , args[2] ); - t->Set( scope->getV8Str( "_ns" ) , args[3] ); - - if ( args.Length() > 4 && args[4]->IsObject() ) - t->Set( scope->getV8Str( "_query" ) , args[4] ); - else - t->Set( scope->getV8Str( "_query" ) , v8::Object::New() ); - - if ( args.Length() > 5 && args[5]->IsObject() ) - t->Set( scope->getV8Str( "_fields" ) , args[5] ); - else - t->Set( scope->getV8Str( "_fields" ) , v8::Null() ); - - - if ( args.Length() > 6 && args[6]->IsNumber() ) - t->Set( scope->getV8Str( "_limit" ) , args[6] ); - else - t->Set( scope->getV8Str( "_limit" ) , Number::New( 0 ) ); - - if ( args.Length() > 7 && args[7]->IsNumber() ) - t->Set( scope->getV8Str( "_skip" ) , args[7] ); - else - t->Set( scope->getV8Str( "_skip" ) , Number::New( 0 ) ); - - if ( args.Length() > 8 && args[8]->IsNumber() ) - t->Set( scope->getV8Str( "_batchSize" ) , args[8] ); - else - t->Set( scope->getV8Str( "_batchSize" ) , Number::New( 0 ) ); - - if ( args.Length() > 9 && args[9]->IsNumber() ) - t->Set( scope->getV8Str( "_options" ) , args[9] ); - else - t->Set( scope->getV8Str( "_options" ) , Number::New( 0 ) ); - - - t->Set( scope->getV8Str( "_cursor" ) , v8::Null() ); - t->Set( scope->getV8Str( "_numReturned" ) , v8::Number::New(0) ); - t->Set( scope->getV8Str( "_special" ) , Boolean::New(false) ); - - return v8::Undefined(); - } - - v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info) { - DDD( "collectionFallback [" << name << "]" ); - - v8::Handle<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get( name ); - if ( ! real->IsUndefined() ) - return real; - - string sname = toSTLString( name ); - if ( sname[0] == '_' ) { - if ( ! ( info.This()->HasRealNamedProperty( name ) ) ) - return v8::Undefined(); - return info.This()->GetRealNamedPropertyInPrototypeChain( name ); - } - - v8::Handle<v8::Value> getCollection = info.This()->GetPrototype()->ToObject()->Get( v8::String::New( "getCollection" ) ); - assert( getCollection->IsFunction() ); - - v8::Function * f = (v8::Function*)(*getCollection); - v8::Handle<v8::Value> argv[1]; - argv[0] = name; - - return f->Call( info.This() , 1 , argv ); - } - - v8::Handle<v8::Value> dbQueryIndexAccess( unsigned int index , const v8::AccessorInfo& info ) { - v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get( v8::String::New( "arrayAccess" ) ); - assert( arrayAccess->IsFunction() ); - - v8::Function * f = (v8::Function*)(*arrayAccess); - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::Number::New( index ); - - return f->Call( info.This() , 1 , argv ); - } - - v8::Handle<v8::Value> objectIdInit( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - - if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) { - v8::Function * f = scope->getObjectIdCons(); - it = f->NewInstance(); - } - - OID oid; - - if ( args.Length() == 0 ) { - oid.init(); - } - else { - string s = toSTLString( args[0] ); - try { - Scope::validateObjectIdString( s ); - } - catch ( const MsgAssertionException &m ) { - string error = m.toString(); - return v8::ThrowException( v8::String::New( error.c_str() ) ); - } - oid.init( s ); - } - - it->Set( scope->getV8Str( "str" ) , v8::String::New( oid.str().c_str() ) ); - - return it; - } - - v8::Handle<v8::Value> dbRefInit( V8Scope* scope, const v8::Arguments& args ) { - - if (args.Length() != 2 && args.Length() != 0) { - return v8::ThrowException( v8::String::New( "DBRef needs 2 arguments" ) ); - } - - v8::Handle<v8::Object> it = args.This(); - - if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) { - v8::Function* f = scope->getNamedCons( "DBRef" ); - it = f->NewInstance(); - } - - if ( args.Length() == 2 ) { - it->Set( scope->getV8Str( "$ref" ) , args[0] ); - it->Set( scope->getV8Str( "$id" ) , args[1] ); - } - - return it; - } - - v8::Handle<v8::Value> dbPointerInit( V8Scope* scope, const v8::Arguments& args ) { - - if (args.Length() != 2) { - return v8::ThrowException( v8::String::New( "DBPointer needs 2 arguments" ) ); - } - - v8::Handle<v8::Object> it = args.This(); - - if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) { - v8::Function* f = scope->getNamedCons( "DBPointer" ); - it = f->NewInstance(); - } - - it->Set( scope->getV8Str( "ns" ) , args[0] ); - it->Set( scope->getV8Str( "id" ) , args[1] ); - it->SetHiddenValue( scope->getV8Str( "__DBPointer" ), v8::Number::New( 1 ) ); - - return it; - } - - v8::Handle<v8::Value> dbTimestampInit( V8Scope* scope, const v8::Arguments& args ) { - - v8::Handle<v8::Object> it = args.This(); - - if ( args.Length() == 0 ) { - it->Set( scope->getV8Str( "t" ) , v8::Number::New( 0 ) ); - it->Set( scope->getV8Str( "i" ) , v8::Number::New( 0 ) ); - } - else if ( args.Length() == 2 ) { - it->Set( scope->getV8Str( "t" ) , args[0] ); - it->Set( scope->getV8Str( "i" ) , args[1] ); - } - else { - return v8::ThrowException( v8::String::New( "Timestamp needs 0 or 2 arguments" ) ); - } - - it->SetInternalField( 0, v8::Uint32::New( Timestamp ) ); - - return it; - } - - - v8::Handle<v8::Value> binDataInit( V8Scope* scope, const v8::Arguments& args ) { - v8::Local<v8::Object> it = args.This(); - - Handle<Value> type; - Handle<Value> len; - int rlen; - char* data; - if (args.Length() == 3) { - // 3 args: len, type, data - - if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) { - v8::Function* f = scope->getNamedCons( "BinData" ); - it = f->NewInstance(); - } - - len = args[0]; - rlen = len->IntegerValue(); - type = args[1]; - v8::String::Utf8Value utf( args[ 2 ] ); - char* tmp = *utf; - data = new char[rlen]; - memcpy(data, tmp, rlen); - } - else if ( args.Length() == 2 ) { - // 2 args: type, base64 string - - if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) { - v8::Function* f = scope->getNamedCons( "BinData" ); - it = f->NewInstance(); - } - - type = args[0]; - v8::String::Utf8Value utf( args[ 1 ] ); - string decoded = base64::decode( *utf ); - const char* tmp = decoded.data(); - rlen = decoded.length(); - data = new char[rlen]; - memcpy(data, tmp, rlen); - len = v8::Number::New(rlen); -// it->Set( scope->getV8Str( "data" ), v8::String::New( decoded.data(), decoded.length() ) ); - } - else { - return v8::ThrowException( v8::String::New( "BinData needs 2 or 3 arguments" ) ); - } - - it->Set( scope->getV8Str( "len" ) , len ); - it->Set( scope->getV8Str( "type" ) , type ); - it->SetHiddenValue( scope->V8STR_BINDATA, v8::Number::New( 1 ) ); - Persistent<v8::Object> res = scope->wrapArrayObject(it, data); - return res; - } - - v8::Handle<v8::Value> binDataToString( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - int len = it->Get( scope->V8STR_LEN )->Int32Value(); - int type = it->Get( scope->V8STR_TYPE )->Int32Value(); - Local<External> c = External::Cast( *(it->GetInternalField( 0 )) ); - char* data = (char*)(c->Value()); - - stringstream ss; - ss << "BinData(" << type << ",\""; - base64::encode( ss, data, len ); - ss << "\")"; - string ret = ss.str(); - return v8::String::New( ret.c_str() ); - } - - v8::Handle<v8::Value> binDataToBase64( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - int len = Handle<v8::Number>::Cast(it->Get(scope->V8STR_LEN))->Int32Value(); - Local<External> c = External::Cast( *(it->GetInternalField( 0 )) ); - char* data = (char*)(c->Value()); - stringstream ss; - base64::encode( ss, (const char *)data, len ); - return v8::String::New(ss.str().c_str()); - } - - v8::Handle<v8::Value> binDataToHex( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - int len = Handle<v8::Number>::Cast(it->Get(scope->V8STR_LEN))->Int32Value(); - Local<External> c = External::Cast( *(it->GetInternalField( 0 )) ); - char* data = (char*)(c->Value()); - stringstream ss; - ss.setf (ios_base::hex , ios_base::basefield); - ss.fill ('0'); - ss.setf (ios_base::right , ios_base::adjustfield); - for( int i = 0; i < len; i++ ) { - unsigned v = (unsigned char) data[i]; - ss << setw(2) << v; - } - return v8::String::New(ss.str().c_str()); - } - - static v8::Handle<v8::Value> hexToBinData( V8Scope* scope, v8::Local<v8::Object> it, int type, string hexstr ) { - int len = hexstr.length() / 2; - char* data = new char[len]; - const char* src = hexstr.c_str(); - for( int i = 0; i < 16; i++ ) { - data[i] = fromHex(src + i * 2); - } - - it->Set( scope->V8STR_LEN , v8::Number::New(len) ); - it->Set( scope->V8STR_TYPE , v8::Number::New(type) ); - it->SetHiddenValue( scope->V8STR_BINDATA, v8::Number::New( 1 ) ); - Persistent<v8::Object> res = scope->wrapArrayObject(it, data); - return res; - } - - v8::Handle<v8::Value> uuidInit( V8Scope* scope, const v8::Arguments& args ) { - if (args.Length() != 1) { - return v8::ThrowException( v8::String::New( "UUIS needs 1 argument" ) ); - } - v8::String::Utf8Value utf( args[ 0 ] ); - if( utf.length() != 32 ) { - return v8::ThrowException( v8::String::New( "UUIS string must have 32 characters" ) ); - } - - return hexToBinData(scope, args.This(), bdtUUID, *utf); - } - -// v8::Handle<v8::Value> uuidToString( V8Scope* scope, const v8::Arguments& args ) { -// v8::Handle<v8::Object> it = args.This(); -// Local<External> c = External::Cast( *(it->GetInternalField( 0 )) ); -// char* data = (char*)(c->Value()); -// -// stringstream ss; -// ss << "UUID(\"" << toHex(data, 16) << "\")"; -// return v8::String::New( ss.str().c_str() ); -// } - - v8::Handle<v8::Value> md5Init( V8Scope* scope, const v8::Arguments& args ) { - if (args.Length() != 1) { - return v8::ThrowException( v8::String::New( "MD5 needs 1 argument" ) ); - } - v8::String::Utf8Value utf( args[ 0 ] ); - if( utf.length() != 32 ) { - return v8::ThrowException( v8::String::New( "MD5 string must have 32 characters" ) ); - } - - return hexToBinData(scope, args.This(), MD5Type, *utf); - } - - v8::Handle<v8::Value> hexDataInit( V8Scope* scope, const v8::Arguments& args ) { - if (args.Length() != 2) { - return v8::ThrowException( v8::String::New( "HexData needs 2 arguments" ) ); - } - v8::String::Utf8Value utf( args[ 1 ] ); - return hexToBinData(scope, args.This(), args[0]->IntegerValue(), *utf); - } - - v8::Handle<v8::Value> numberLongInit( V8Scope* scope, const v8::Arguments& args ) { - - if (args.Length() != 0 && args.Length() != 1 && args.Length() != 3) { - return v8::ThrowException( v8::String::New( "NumberLong needs 0, 1 or 3 arguments" ) ); - } - - v8::Handle<v8::Object> it = args.This(); - - if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) { - v8::Function* f = scope->getNamedCons( "NumberLong" ); - it = f->NewInstance(); - } - - if ( args.Length() == 0 ) { - it->Set( scope->getV8Str( "floatApprox" ), v8::Number::New( 0 ) ); - } - else if ( args.Length() == 1 ) { - if ( args[ 0 ]->IsNumber() ) { - it->Set( scope->getV8Str( "floatApprox" ), args[ 0 ] ); - } - else { - v8::String::Utf8Value data( args[ 0 ] ); - string num = *data; - const char *numStr = num.c_str(); - long long n; - try { - n = parseLL( numStr ); - } - catch ( const AssertionException & ) { - return v8::ThrowException( v8::String::New( "could not convert string to long long" ) ); - } - unsigned long long val = n; - if ( (long long)val == (long long)(double)(long long)(val) ) { - it->Set( scope->getV8Str( "floatApprox" ), v8::Number::New( (double)(long long)( val ) ) ); - } - else { - it->Set( scope->getV8Str( "floatApprox" ), v8::Number::New( (double)(long long)( val ) ) ); - it->Set( scope->getV8Str( "top" ), v8::Integer::New( val >> 32 ) ); - it->Set( scope->getV8Str( "bottom" ), v8::Integer::New( (unsigned long)(val & 0x00000000ffffffff) ) ); - } - } - } - else { - it->Set( scope->getV8Str( "floatApprox" ) , args[0] ); - it->Set( scope->getV8Str( "top" ) , args[1] ); - it->Set( scope->getV8Str( "bottom" ) , args[2] ); - } - it->SetHiddenValue( scope->V8STR_NUMBERLONG, v8::Number::New( 1 ) ); - - return it; - } - - long long numberLongVal( const v8::Handle< v8::Object > &it ) { - if ( !it->Has( v8::String::New( "top" ) ) ) - return (long long)( it->Get( v8::String::New( "floatApprox" ) )->NumberValue() ); - return - (long long) - ( (unsigned long long)( it->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) + - (unsigned)( it->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() ); - } - - v8::Handle<v8::Value> numberLongValueOf( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - long long val = numberLongVal( it ); - return v8::Number::New( double( val ) ); - } - - v8::Handle<v8::Value> numberLongToNumber( V8Scope* scope, const v8::Arguments& args ) { - return numberLongValueOf( scope, args ); - } - - v8::Handle<v8::Value> numberLongToString( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - - stringstream ss; - long long val = numberLongVal( it ); - const long long limit = 2LL << 30; - - if ( val <= -limit || limit <= val ) - ss << "NumberLong(\"" << val << "\")"; - else - ss << "NumberLong(" << val << ")"; - - string ret = ss.str(); - return v8::String::New( ret.c_str() ); - } - - v8::Handle<v8::Value> numberIntInit( V8Scope* scope, const v8::Arguments& args ) { - - if (args.Length() != 0 && args.Length() != 1) { - return v8::ThrowException( v8::String::New( "NumberInt needs 0, 1 argument" ) ); - } - - v8::Handle<v8::Object> it = args.This(); - - if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ) { - v8::Function* f = scope->getNamedCons( "NumberInt" ); - it = f->NewInstance(); - } - - if ( args.Length() == 0 ) { - it->SetHiddenValue( scope->V8STR_NUMBERINT, v8::Number::New( 0 ) ); - } - else if ( args.Length() == 1 ) { - it->SetHiddenValue( scope->V8STR_NUMBERINT, args[0]->ToInt32() ); - } - - return it; - } - - v8::Handle<v8::Value> numberIntValueOf( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - int val = it->GetHiddenValue( scope->V8STR_NUMBERINT )->Int32Value(); - return v8::Number::New( double( val ) ); - } - - v8::Handle<v8::Value> numberIntToNumber( V8Scope* scope, const v8::Arguments& args ) { - return numberIntValueOf( scope, args ); - } - - v8::Handle<v8::Value> numberIntToString( V8Scope* scope, const v8::Arguments& args ) { - v8::Handle<v8::Object> it = args.This(); - - stringstream ss; - int val = it->GetHiddenValue( scope->V8STR_NUMBERINT )->Int32Value(); - ss << "NumberInt(" << val << ")"; - - string ret = ss.str(); - return v8::String::New( ret.c_str() ); - } - - v8::Handle<v8::Value> bsonsize( V8Scope* scope, const v8::Arguments& args ) { - - if ( args.Length() != 1 ) - return v8::ThrowException( v8::String::New( "bsonsize needs 1 argument" ) ); - - if ( args[0]->IsNull() ) - return v8::Number::New(0); - - if ( ! args[ 0 ]->IsObject() ) - return v8::ThrowException( v8::String::New( "argument to bsonsize has to be an object" ) ); - - return v8::Number::New( scope->v8ToMongo( args[ 0 ]->ToObject() ).objsize() ); - } - - // to be called with v8 mutex - void enableV8Interrupt() { - if ( globalScriptEngine->haveGetInterruptSpecCallback() ) { - __interruptSpecToThreadId[ globalScriptEngine->getInterruptSpec() ] = v8::V8::GetCurrentThreadId(); - } - } - - // to be called with v8 mutex - void disableV8Interrupt() { - if ( globalScriptEngine->haveGetInterruptSpecCallback() ) { - __interruptSpecToThreadId.erase( globalScriptEngine->getInterruptSpec() ); - } - } - - namespace v8Locks { - boost::mutex& __v8Mutex = *( new boost::mutex ); - ThreadLocalValue< bool > __locked; - - RecursiveLock::RecursiveLock() : _unlock() { - if ( !__locked.get() ) { - __v8Mutex.lock(); - __locked.set( true ); - _unlock = true; - } - } - RecursiveLock::~RecursiveLock() { - if ( _unlock ) { - __v8Mutex.unlock(); - __locked.set( false ); - } - } - - RecursiveUnlock::RecursiveUnlock() : _lock() { - if ( __locked.get() ) { - __v8Mutex.unlock(); - __locked.set( false ); - _lock = true; - } - } - RecursiveUnlock::~RecursiveUnlock() { - if ( _lock ) { - __v8Mutex.lock(); - __locked.set( true ); - } - } - } // namespace v8Locks -} diff --git a/scripting/v8_db.h b/scripting/v8_db.h deleted file mode 100644 index 08d15d02906..00000000000 --- a/scripting/v8_db.h +++ /dev/null @@ -1,96 +0,0 @@ -// v8_db.h - -/* 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. - */ - -#pragma once - -#include <v8.h> -#include <cstring> -#include <cstdio> -#include <cstdlib> - -#include "engine_v8.h" -#include "../client/dbclient.h" - -namespace mongo { - - // These functions may depend on the caller creating a handle scope and context scope. - - v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( V8Scope * scope, bool local ); -// void installDBTypes( V8Scope * scope, v8::Handle<v8::ObjectTemplate>& global ); - void installDBTypes( V8Scope * scope, v8::Handle<v8::Object>& global ); - - // the actual globals - - mongo::DBClientBase * getConnection( const v8::Arguments& args ); - - // Mongo members - v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> mongoConsExternal(V8Scope* scope, const v8::Arguments& args); - - v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments& args); - - - v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, const v8::Arguments& args); - - // DB members - - v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> objectIdInit( V8Scope* scope, const v8::Arguments& args ); - - v8::Handle<v8::Value> dbRefInit( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> dbPointerInit( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> dbTimestampInit( V8Scope* scope, const v8::Arguments& args ); - - v8::Handle<v8::Value> binDataInit( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> binDataToString( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> binDataToBase64( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> binDataToHex( V8Scope* scope, const v8::Arguments& args ); - - v8::Handle<v8::Value> uuidInit( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> md5Init( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> hexDataInit( V8Scope* scope, const v8::Arguments& args ); - - v8::Handle<v8::Value> numberLongInit( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> numberLongToNumber(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> numberLongToString(V8Scope* scope, const v8::Arguments& args); - - v8::Handle<v8::Value> numberIntInit( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Arguments& args); - v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Arguments& args); - - v8::Handle<v8::Value> dbQueryInit( V8Scope* scope, const v8::Arguments& args ); - v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::AccessorInfo& info ); - - v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info); - - v8::Handle<v8::Value> bsonsize( V8Scope* scope, const v8::Arguments& args ); - - // call with v8 mutex: - void enableV8Interrupt(); - void disableV8Interrupt(); - -} - diff --git a/scripting/v8_utils.cpp b/scripting/v8_utils.cpp deleted file mode 100644 index 0f575cf31a3..00000000000 --- a/scripting/v8_utils.cpp +++ /dev/null @@ -1,291 +0,0 @@ -// 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. - */ - -#if defined(_WIN32) -/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with - our usage of boost */ -#include "boost/cstdint.hpp" -using namespace boost; -#define V8STDINT_H_ -#endif - -#include "v8_utils.h" -#include "v8_db.h" -#include <iostream> -#include <map> -#include <sstream> -#include <vector> -#include <boost/smart_ptr.hpp> -#include <boost/thread/thread.hpp> -#include <boost/thread/xtime.hpp> -#include "engine_v8.h" - -using namespace std; -using namespace v8; - -namespace mongo { - - std::string toSTLString( const Handle<v8::Value> & o ) { - v8::String::Utf8Value str(o); - const char * foo = *str; - std::string s(foo); - return s; - } - - std::string toSTLString( const v8::TryCatch * try_catch ) { - - stringstream ss; - - //while ( try_catch ){ // disabled for v8 bleeding edge - - 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; - } - - //try_catch = try_catch->next_; - //} - - return ss.str(); - } - - - 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; - } - - //if ( try_catch->next_ ) // disabled for v8 bleeding edge - // s << try_catch->next_; - - return s; - } - - void ReportException(v8::TryCatch* try_catch) { - cout << try_catch << endl; - } - - Handle< Context > baseContext_; - - class JSThreadConfig { - public: - JSThreadConfig( const Arguments &args, bool newScope = false ) : started_(), done_(), newScope_( newScope ) { - jsassert( args.Length() > 0, "need at least one argument" ); - jsassert( args[ 0 ]->IsFunction(), "first argument must be a function" ); - Local< v8::Function > f = v8::Function::Cast( *args[ 0 ] ); - f_ = Persistent< v8::Function >::New( f ); - 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" ); - V8Unlock 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()() { - V8Lock l; - HandleScope handle_scope; - Handle< Context > context; - Handle< v8::Function > fun; - auto_ptr< V8Scope > scope; - if ( config_.newScope_ ) { - scope.reset( dynamic_cast< V8Scope * >( globalScriptEngine->newScope() ) ); - 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 ); - boost::scoped_array< Local< Value > > argv( new Local< Value >[ config_.args_.size() ] ); - for( unsigned int i = 0; i < config_.args_.size(); ++i ) - argv[ i ] = Local< Value >::New( config_.args_[ i ] ); - 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(); - } - config_.returnData_ = Persistent< Value >::New( ret ); - } - private: - JSThreadConfig &config_; - }; - - bool started_; - bool done_; - bool newScope_; - Persistent< v8::Function > f_; - vector< Persistent< Value > > args_; - auto_ptr< boost::thread > thread_; - Persistent< Value > returnData_; - }; - - Handle< Value > ThreadInit( V8Scope* scope, const Arguments &args ) { - Handle<v8::Object> it = args.This(); - // NOTE I believe the passed JSThreadConfig will never be freed. If this - // policy is changed, JSThread may no longer be able to store JSThreadConfig - // by reference. - it->SetHiddenValue( v8::String::New( "_JSThreadConfig" ), External::New( new JSThreadConfig( args ) ) ); - return v8::Undefined(); - } - - Handle< Value > ScopedThreadInit( V8Scope* scope, const Arguments &args ) { - Handle<v8::Object> it = args.This(); - // NOTE I believe the passed JSThreadConfig will never be freed. If this - // policy is changed, JSThread may no longer be able to store JSThreadConfig - // by reference. - it->SetHiddenValue( v8::String::New( "_JSThreadConfig" ), External::New( new JSThreadConfig( args, true ) ) ); - return v8::Undefined(); - } - - JSThreadConfig *thisConfig( V8Scope* scope, const Arguments &args ) { - Local< External > c = External::Cast( *(args.This()->GetHiddenValue( v8::String::New( "_JSThreadConfig" ) ) ) ); - JSThreadConfig *config = (JSThreadConfig *)( c->Value() ); - return config; - } - - Handle< Value > ThreadStart( V8Scope* scope, const Arguments &args ) { - thisConfig( scope, args )->start(); - return v8::Undefined(); - } - - Handle< Value > ThreadJoin( V8Scope* scope, const Arguments &args ) { - thisConfig( scope, args )->join(); - return v8::Undefined(); - } - - Handle< Value > ThreadReturnData( V8Scope* scope, const Arguments &args ) { - HandleScope handle_scope; - return handle_scope.Close( thisConfig( scope, args )->returnData() ); - } - - Handle< Value > ThreadInject( V8Scope* scope, 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(); - - scope->injectV8Function("init", ThreadInit, o); - scope->injectV8Function("start", ThreadStart, o); - scope->injectV8Function("join", ThreadJoin, o); - scope->injectV8Function("returnData", ThreadReturnData, o); - - return v8::Undefined(); - } - - Handle< Value > ScopedThreadInject( V8Scope* scope, 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(); - - scope->injectV8Function("init", ScopedThreadInit, o); - // inheritance takes care of other member functions - - return v8::Undefined(); - } - - void installFork( V8Scope* scope, v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context ) { - if ( baseContext_.IsEmpty() ) // if this is the shell, first call will be with shell context, otherwise don't expect to use fork() anyway - baseContext_ = context; - scope->injectV8Function("_threadInject", ThreadInject, global); - scope->injectV8Function("_scopedThreadInject", ScopedThreadInject, global); - } - -} diff --git a/scripting/v8_utils.h b/scripting/v8_utils.h deleted file mode 100644 index ca5d317885f..00000000000 --- a/scripting/v8_utils.h +++ /dev/null @@ -1,43 +0,0 @@ -// v8_utils.h - -/* 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. - */ - -#pragma once - -#include <v8.h> - -#include <cstring> -#include <cstdio> -#include <cstdlib> -#include <assert.h> -#include <iostream> - -namespace mongo { - - void ReportException(v8::TryCatch* handler); - -#define jsassert(x,msg) assert(x) - - std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o ); - std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatch> * try_catch ); - - std::string toSTLString( const v8::Handle<v8::Value> & o ); - std::string toSTLString( const v8::TryCatch * try_catch ); - - class V8Scope; - void installFork( V8Scope* scope, v8::Handle< v8::Object > &global, v8::Handle< v8::Context > &context ); -} - diff --git a/scripting/v8_wrapper.cpp b/scripting/v8_wrapper.cpp deleted file mode 100644 index 7c28a39cceb..00000000000 --- a/scripting/v8_wrapper.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// v8_wrapper.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. - */ - -#if defined(_WIN32) -/** this is a hack - v8stdint.h defined uint16_t etc. on _WIN32 only, and that collides with - our usage of boost */ -#include "boost/cstdint.hpp" -using namespace boost; -#define V8STDINT_H_ -#endif - -#include "v8_wrapper.h" -#include "v8_utils.h" -#include "v8_db.h" -#include "engine_v8.h" - -#include <iostream> - -using namespace std; -using namespace v8; - -namespace mongo { - -#define DDD(x) - - // --- object wrapper --- - - class WrapperHolder { - public: - WrapperHolder( V8Scope* scope, const BSONObj * o , bool readOnly , bool iDelete ) - : _scope(scope), _o(o), _readOnly( readOnly ), _iDelete( iDelete ) { - } - - ~WrapperHolder() { - if ( _o && _iDelete ) { - delete _o; - } - _o = 0; - } - - v8::Handle<v8::Value> get( v8::Local<v8::String> name ) { - const string& s = toSTLString( name ); - const BSONElement& e = _o->getField( s ); - return _scope->mongoToV8Element(e); - } - - V8Scope* _scope; - const BSONObj * _o; - bool _readOnly; - bool _iDelete; - }; - - WrapperHolder * createWrapperHolder( V8Scope* scope, const BSONObj * o , bool readOnly , bool iDelete ) { - return new WrapperHolder( scope, o , readOnly , iDelete ); - } - - WrapperHolder * getWrapper( v8::Handle<v8::Object> o ) { - Handle<v8::Value> t = o->GetRealNamedProperty( v8::String::New( "_wrapper" ) ); - assert( t->IsExternal() ); - Local<External> c = External::Cast( *t ); - WrapperHolder * w = (WrapperHolder*)(c->Value()); - assert( w ); - return w; - } - - - Handle<Value> wrapperCons(V8Scope* scope, const Arguments& args) { - if ( ! ( args.Length() == 1 && args[0]->IsExternal() ) ) - return v8::ThrowException( v8::String::New( "wrapperCons needs 1 External arg" ) ); - - args.This()->Set( v8::String::New( "_wrapper" ) , args[0] ); - - return v8::Undefined(); - } - - v8::Handle<v8::Value> wrapperGetHandler( v8::Local<v8::String> name, const v8::AccessorInfo &info) { - return getWrapper( info.This() )->get( name ); - } - - v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> t = scope->createV8Function(wrapperCons); - t->InstanceTemplate()->SetNamedPropertyHandler( wrapperGetHandler ); - return t; - } -} diff --git a/scripting/v8_wrapper.h b/scripting/v8_wrapper.h deleted file mode 100644 index 22f14e6ae94..00000000000 --- a/scripting/v8_wrapper.h +++ /dev/null @@ -1,34 +0,0 @@ -// v8_wrapper.h - -/* 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. - */ - -#pragma once - -#include <v8.h> -#include <cstring> -#include <cstdio> -#include <cstdlib> -#include "../db/jsobj.h" -#include "engine_v8.h" - -namespace mongo { - - v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate(V8Scope* scope); - - class WrapperHolder; - WrapperHolder * createWrapperHolder( V8Scope* scope, const BSONObj * o , bool readOnly , bool iDelete ); - -} |
