summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/engine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting/engine.cpp')
-rw-r--r--src/mongo/scripting/engine.cpp763
1 files changed, 401 insertions, 362 deletions
diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp
index 36c9104388a..f8340b1e906 100644
--- a/src/mongo/scripting/engine.cpp
+++ b/src/mongo/scripting/engine.cpp
@@ -47,38 +47,31 @@
namespace mongo {
- using std::endl;
- using std::set;
- using std::shared_ptr;
- using std::string;
- using std::unique_ptr;
+using std::endl;
+using std::set;
+using std::shared_ptr;
+using std::string;
+using std::unique_ptr;
- AtomicInt64 Scope::_lastVersion(1);
+AtomicInt64 Scope::_lastVersion(1);
namespace {
- // 2 GB is the largest support Javascript file size.
- const fileofs kMaxJsFileLength = fileofs(2) * 1024 * 1024 * 1024;
+// 2 GB is the largest support Javascript file size.
+const fileofs kMaxJsFileLength = fileofs(2) * 1024 * 1024 * 1024;
} // namespace
- ScriptEngine::ScriptEngine() : _scopeInitCallback() {
- }
-
- ScriptEngine::~ScriptEngine() {
- }
+ScriptEngine::ScriptEngine() : _scopeInitCallback() {}
- Scope::Scope() : _localDBName(""),
- _loadedVersion(0),
- _numTimesUsed(0),
- _lastRetIsNativeCode(false) {
- }
+ScriptEngine::~ScriptEngine() {}
- Scope::~Scope() {
+Scope::Scope()
+ : _localDBName(""), _loadedVersion(0), _numTimesUsed(0), _lastRetIsNativeCode(false) {}
- }
+Scope::~Scope() {}
- void Scope::append(BSONObjBuilder& builder, const char* fieldName, const char* scopeName) {
- int t = type(scopeName);
- switch (t) {
+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;
@@ -105,410 +98,456 @@ namespace {
builder.appendNull(fieldName);
break;
case Date:
- builder.appendDate(fieldName,
- Date_t::fromMillisSinceEpoch(static_cast<long long>(
- getNumber(scopeName))));
+ builder.appendDate(
+ fieldName,
+ Date_t::fromMillisSinceEpoch(static_cast<long long>(getNumber(scopeName))));
break;
case Code:
builder.appendCode(fieldName, getString(scopeName));
break;
default:
- uassert(10206, str::stream() << "can't append type from: " << t, 0);
- }
+ uassert(10206, str::stream() << "can't append type from: " << t, 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);
- }
+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,
- int timeoutMs) {
+bool Scope::execFile(const string& filename, bool printResult, bool reportError, int timeoutMs) {
#ifdef _WIN32
- boost::filesystem::path p(toWideString(filename.c_str()));
+ boost::filesystem::path p(toWideString(filename.c_str()));
#else
- boost::filesystem::path p(filename);
+ boost::filesystem::path p(filename);
#endif
- if (!exists(p)) {
- error() << "file [" << filename << "] doesn't exist" << endl;
- return false;
- }
-
- // iterate directories and recurse using all *.js files in the directory
- if (boost::filesystem::is_directory(p)) {
- boost::filesystem::directory_iterator end;
- bool empty = true;
-
- for (boost::filesystem::directory_iterator it (p); it != end; it++) {
- empty = false;
- boost::filesystem::path sub(*it);
- if (!str::endsWith(sub.string().c_str(), ".js"))
- continue;
- if (!execFile(sub.string(), printResult, reportError, timeoutMs))
- return false;
- }
+ if (!exists(p)) {
+ error() << "file [" << filename << "] doesn't exist" << endl;
+ return false;
+ }
- if (empty) {
- error() << "directory [" << filename << "] doesn't have any *.js files" << endl;
+ // iterate directories and recurse using all *.js files in the directory
+ if (boost::filesystem::is_directory(p)) {
+ boost::filesystem::directory_iterator end;
+ bool empty = true;
+
+ for (boost::filesystem::directory_iterator it(p); it != end; it++) {
+ empty = false;
+ boost::filesystem::path sub(*it);
+ if (!str::endsWith(sub.string().c_str(), ".js"))
+ continue;
+ if (!execFile(sub.string(), printResult, reportError, timeoutMs))
return false;
- }
-
- return true;
}
- File f;
- f.open(filename.c_str(), true);
-
- if (!f.is_open() || f.bad())
- return false;
-
- fileofs fo = f.len();
- if (fo > kMaxJsFileLength) {
- warning() << "attempted to execute javascript file larger than 2GB" << endl;
+ if (empty) {
+ error() << "directory [" << filename << "] doesn't have any *.js files" << endl;
return false;
}
- unsigned len = static_cast<unsigned>(fo);
- std::unique_ptr<char[]> data (new char[len+1]);
- data[len] = 0;
- f.read(0, data.get(), len);
-
- 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, len - offset);
- return exec(code, filename, printResult, reportError, timeoutMs);
+ return true;
}
- class Scope::StoredFuncModLogOpHandler : public RecoveryUnit::Change {
- public:
- void commit() {
- _lastVersion.fetchAndAdd(1);
- }
- void rollback() { }
- };
+ File f;
+ f.open(filename.c_str(), true);
- void Scope::storedFuncMod(OperationContext* txn) {
- txn->recoveryUnit()->registerChange(new StoredFuncModLogOpHandler());
- }
+ if (!f.is_open() || f.bad())
+ return false;
- void Scope::validateObjectIdString(const string& str) {
- uassert(10448, "invalid object id: length", str.size() == 24);
- for (size_t i = 0; i < str.size(); i++)
- uassert(10430, "invalid object id: not hex", std::isxdigit(str.at(i)));
+ fileofs fo = f.len();
+ if (fo > kMaxJsFileLength) {
+ warning() << "attempted to execute javascript file larger than 2GB" << endl;
+ return false;
+ }
+ unsigned len = static_cast<unsigned>(fo);
+ std::unique_ptr<char[]> data(new char[len + 1]);
+ data[len] = 0;
+ f.read(0, data.get(), len);
+
+ 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();
}
- void Scope::loadStored(OperationContext* txn, bool ignoreNotConnected) {
- if (_localDBName.size() == 0) {
- if (ignoreNotConnected)
- return;
- uassert(10208, "need to have locallyConnected already", _localDBName.size());
- }
-
- int64_t lastVersion = _lastVersion.load();
- if (_loadedVersion == lastVersion)
- return;
+ StringData code(data.get() + offset, len - offset);
+ return exec(code, filename, printResult, reportError, timeoutMs);
+}
- _loadedVersion = lastVersion;
- string coll = _localDBName + ".system.js";
+class Scope::StoredFuncModLogOpHandler : public RecoveryUnit::Change {
+public:
+ void commit() {
+ _lastVersion.fetchAndAdd(1);
+ }
+ void rollback() {}
+};
- unique_ptr<DBClientBase> directDBClient(createDirectClient(txn));
- unique_ptr<DBClientCursor> c = directDBClient->query(coll, Query(), 0, 0, NULL,
- QueryOption_SlaveOk, 0);
- massert(16669, "unable to get db client cursor from query", c.get());
+void Scope::storedFuncMod(OperationContext* txn) {
+ txn->recoveryUnit()->registerChange(new StoredFuncModLogOpHandler());
+}
- set<string> thisTime;
- while (c->more()) {
- BSONObj o = c->nextSafe();
- BSONElement n = o["_id"];
- BSONElement v = o["value"];
+void Scope::validateObjectIdString(const string& str) {
+ uassert(10448, "invalid object id: length", str.size() == 24);
+ for (size_t i = 0; i < str.size(); i++)
+ uassert(10430, "invalid object id: not hex", std::isxdigit(str.at(i)));
+}
- uassert(10209, str::stream() << "name has to be a string: " << n, n.type() == String);
- uassert(10210, "value has to be set", v.type() != EOO);
+void Scope::loadStored(OperationContext* txn, bool ignoreNotConnected) {
+ if (_localDBName.size() == 0) {
+ if (ignoreNotConnected)
+ return;
+ uassert(10208, "need to have locallyConnected already", _localDBName.size());
+ }
- try {
- setElement(n.valuestr(), v);
- thisTime.insert(n.valuestr());
- _storedNames.insert(n.valuestr());
- }
- catch (const DBException& setElemEx) {
- error() << "unable to load stored JavaScript function " << n.valuestr()
- << "(): " << setElemEx.what() << endl;
- }
+ int64_t lastVersion = _lastVersion.load();
+ if (_loadedVersion == lastVersion)
+ return;
+
+ _loadedVersion = lastVersion;
+ string coll = _localDBName + ".system.js";
+
+ unique_ptr<DBClientBase> directDBClient(createDirectClient(txn));
+ unique_ptr<DBClientCursor> c =
+ directDBClient->query(coll, Query(), 0, 0, NULL, QueryOption_SlaveOk, 0);
+ massert(16669, "unable to get db client cursor from query", 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);
+
+ try {
+ setElement(n.valuestr(), v);
+ thisTime.insert(n.valuestr());
+ _storedNames.insert(n.valuestr());
+ } catch (const DBException& setElemEx) {
+ error() << "unable to load stored JavaScript function " << n.valuestr()
+ << "(): " << setElemEx.what() << endl;
}
+ }
- // remove things from scope that were removed from the system.js collection
- for (set<string>::iterator i = _storedNames.begin(); i != _storedNames.end(); ) {
- if (thisTime.count(*i) == 0) {
- string toDelete = str::stream() << "delete " << *i;
- _storedNames.erase(i++);
- execSetup(toDelete, "clean up scope");
- }
- else {
- ++i;
- }
+ // remove things from scope that were removed from the system.js collection
+ for (set<string>::iterator i = _storedNames.begin(); i != _storedNames.end();) {
+ if (thisTime.count(*i) == 0) {
+ string toDelete = str::stream() << "delete " << *i;
+ _storedNames.erase(i++);
+ execSetup(toDelete, "clean up scope");
+ } else {
+ ++i;
}
}
+}
- 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++;
+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++;
}
-
- FunctionCacheMap::iterator i = _cachedFunctions.find(code);
- if (i != _cachedFunctions.end())
- return i->second;
- // NB: we calculate the function number for v8 so the cache can be utilized to
- // lookup the source on an exception, but SpiderMonkey uses the value
- // returned by JS_CompileFunction.
- ScriptingFunction defaultFunctionNumber = getFunctionCache().size() + 1;
- ScriptingFunction& actualFunctionNumber = _cachedFunctions[code];
- actualFunctionNumber = _createFunction(code, defaultFunctionNumber);
- return actualFunctionNumber;
- }
-
- namespace JSFiles {
- extern const JSFile collection;
- extern const JSFile db;
- extern const JSFile explain_query;
- extern const JSFile explainable;
- extern const JSFile mongo;
- extern const JSFile mr;
- extern const JSFile query;
- extern const JSFile upgrade_check;
- extern const JSFile utils;
- extern const JSFile utils_sh;
- extern const JSFile utils_auth;
- extern const JSFile bulk_api;
- }
-
- void Scope::execCoreFiles() {
- execSetup(JSFiles::utils);
- execSetup(JSFiles::utils_sh);
- execSetup(JSFiles::utils_auth);
- execSetup(JSFiles::db);
- execSetup(JSFiles::mongo);
- execSetup(JSFiles::mr);
- execSetup(JSFiles::query);
- execSetup(JSFiles::bulk_api);
- execSetup(JSFiles::collection);
- execSetup(JSFiles::explain_query);
- execSetup(JSFiles::explainable);
- execSetup(JSFiles::upgrade_check);
}
-namespace {
- class ScopeCache {
- public:
- void release(const string& poolName, const std::shared_ptr<Scope>& scope) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
-
- if (scope->hasOutOfMemoryException()) {
- // make some room
- log() << "Clearing all idle JS contexts due to out of memory" << endl;
- _pools.clear();
- return;
- }
-
- if (scope->getTimesUsed() > kMaxScopeReuse)
- return; // used too many times to save
+ FunctionCacheMap::iterator i = _cachedFunctions.find(code);
+ if (i != _cachedFunctions.end())
+ return i->second;
+ // NB: we calculate the function number for v8 so the cache can be utilized to
+ // lookup the source on an exception, but SpiderMonkey uses the value
+ // returned by JS_CompileFunction.
+ ScriptingFunction defaultFunctionNumber = getFunctionCache().size() + 1;
+ ScriptingFunction& actualFunctionNumber = _cachedFunctions[code];
+ actualFunctionNumber = _createFunction(code, defaultFunctionNumber);
+ return actualFunctionNumber;
+}
- if (!scope->getError().empty())
- return; // not saving errored scopes
+namespace JSFiles {
+extern const JSFile collection;
+extern const JSFile db;
+extern const JSFile explain_query;
+extern const JSFile explainable;
+extern const JSFile mongo;
+extern const JSFile mr;
+extern const JSFile query;
+extern const JSFile upgrade_check;
+extern const JSFile utils;
+extern const JSFile utils_sh;
+extern const JSFile utils_auth;
+extern const JSFile bulk_api;
+}
- if (_pools.size() >= kMaxPoolSize) {
- // prefer to keep recently-used scopes
- _pools.pop_back();
- }
+void Scope::execCoreFiles() {
+ execSetup(JSFiles::utils);
+ execSetup(JSFiles::utils_sh);
+ execSetup(JSFiles::utils_auth);
+ execSetup(JSFiles::db);
+ execSetup(JSFiles::mongo);
+ execSetup(JSFiles::mr);
+ execSetup(JSFiles::query);
+ execSetup(JSFiles::bulk_api);
+ execSetup(JSFiles::collection);
+ execSetup(JSFiles::explain_query);
+ execSetup(JSFiles::explainable);
+ execSetup(JSFiles::upgrade_check);
+}
- scope->reset();
- ScopeAndPool toStore = {scope, poolName};
- _pools.push_front(toStore);
+namespace {
+class ScopeCache {
+public:
+ void release(const string& poolName, const std::shared_ptr<Scope>& scope) {
+ stdx::lock_guard<stdx::mutex> lk(_mutex);
+
+ if (scope->hasOutOfMemoryException()) {
+ // make some room
+ log() << "Clearing all idle JS contexts due to out of memory" << endl;
+ _pools.clear();
+ return;
}
- std::shared_ptr<Scope> tryAcquire(OperationContext* txn, const string& poolName) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
-
- for (Pools::iterator it = _pools.begin(); it != _pools.end(); ++it) {
- if (it->poolName == poolName) {
- std::shared_ptr<Scope> scope = it->scope;
- _pools.erase(it);
- scope->incTimesUsed();
- scope->reset();
- scope->registerOperation(txn);
- return scope;
- }
- }
+ if (scope->getTimesUsed() > kMaxScopeReuse)
+ return; // used too many times to save
- return std::shared_ptr<Scope>();
+ if (!scope->getError().empty())
+ return; // not saving errored scopes
+
+ if (_pools.size() >= kMaxPoolSize) {
+ // prefer to keep recently-used scopes
+ _pools.pop_back();
}
- private:
- struct ScopeAndPool {
- std::shared_ptr<Scope> scope;
- string poolName;
- };
+ scope->reset();
+ ScopeAndPool toStore = {scope, poolName};
+ _pools.push_front(toStore);
+ }
+
+ std::shared_ptr<Scope> tryAcquire(OperationContext* txn, const string& poolName) {
+ stdx::lock_guard<stdx::mutex> lk(_mutex);
+
+ for (Pools::iterator it = _pools.begin(); it != _pools.end(); ++it) {
+ if (it->poolName == poolName) {
+ std::shared_ptr<Scope> scope = it->scope;
+ _pools.erase(it);
+ scope->incTimesUsed();
+ scope->reset();
+ scope->registerOperation(txn);
+ return scope;
+ }
+ }
- // Note: if these numbers change, reconsider choice of datastructure for _pools
- static const unsigned kMaxPoolSize = 10;
- static const int kMaxScopeReuse = 10;
+ return std::shared_ptr<Scope>();
+ }
- typedef std::deque<ScopeAndPool> Pools; // More-recently used Scopes are kept at the front.
- Pools _pools; // protected by _mutex
- stdx::mutex _mutex;
+private:
+ struct ScopeAndPool {
+ std::shared_ptr<Scope> scope;
+ string poolName;
};
- ScopeCache scopeCache;
-} // anonymous namespace
+ // Note: if these numbers change, reconsider choice of datastructure for _pools
+ static const unsigned kMaxPoolSize = 10;
+ static const int kMaxScopeReuse = 10;
- class PooledScope : public Scope {
- public:
- PooledScope(const std::string& pool, const std::shared_ptr<Scope>& real)
- : _pool(pool)
- , _real(real) {
+ typedef std::deque<ScopeAndPool> Pools; // More-recently used Scopes are kept at the front.
+ Pools _pools; // protected by _mutex
+ stdx::mutex _mutex;
+};
- }
+ScopeCache scopeCache;
+} // anonymous namespace
- virtual ~PooledScope() {
- scopeCache.release(_pool, _real);
- }
+class PooledScope : public Scope {
+public:
+ PooledScope(const std::string& pool, const std::shared_ptr<Scope>& real)
+ : _pool(pool), _real(real) {}
- // wrappers for the derived (_real) scope
- void reset() { _real->reset(); }
- void registerOperation(OperationContext* txn) { _real->registerOperation(txn); }
- void unregisterOperation() { _real->unregisterOperation(); }
- void init(const BSONObj* data) { _real->init(data); }
- void localConnectForDbEval(OperationContext* txn, const char* dbName) {
- invariant(!"localConnectForDbEval should only be called from dbEval");
- }
- void setLocalDB(const string& dbName) { _real->setLocalDB(dbName); }
- void loadStored(OperationContext* txn, bool ignoreNotConnected = false) {
- _real->loadStored(txn, ignoreNotConnected);
- }
- void externalSetup() { _real->externalSetup(); }
- void gc() { _real->gc(); }
- bool isKillPending() const { return _real->isKillPending(); }
- int type(const char* field) { return _real->type(field); }
- string getError() { return _real->getError(); }
- bool hasOutOfMemoryException() { return _real->hasOutOfMemoryException(); }
- void rename(const char* from, const char* to) { _real->rename(from, to); }
- 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); }
- void setNumber(const char* field, double val) { _real->setNumber(field, val); }
- void setString(const char* field, StringData val) { _real->setString(field, val); }
- void setElement(const char* field, const BSONElement& val) {
- _real->setElement(field, val);
- }
- void setObject(const char* field, const BSONObj& obj, bool readOnly = true) {
- _real->setObject(field, obj, readOnly);
- }
- bool isLastRetNativeCode() { return _real->isLastRetNativeCode(); }
-
- void setBoolean(const char* field, bool val) { _real->setBoolean(field, val); }
- void setFunction(const char* field, const char* code) { _real->setFunction(field, code); }
- ScriptingFunction createFunction(const char* code) { return _real->createFunction(code); }
- 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,
- readOnlyArgs, readOnlyRecv);
- }
- bool exec(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,
- int timeoutMs = 0) {
- return _real->execFile(filename, printResult, reportError, timeoutMs);
- }
- void injectNative(const char* field, NativeFunction func, void* data) {
- _real->injectNative(field, func, data);
- }
- void append(BSONObjBuilder& builder, const char* fieldName, const char* scopeName) {
- _real->append(builder, fieldName, scopeName);
- }
-
- protected:
- FunctionCacheMap& getFunctionCache() { return _real->getFunctionCache(); }
+ virtual ~PooledScope() {
+ scopeCache.release(_pool, _real);
+ }
- ScriptingFunction _createFunction(const char* code, ScriptingFunction functionNumber = 0) {
- return _real->_createFunction(code, functionNumber);
- }
+ // wrappers for the derived (_real) scope
+ void reset() {
+ _real->reset();
+ }
+ void registerOperation(OperationContext* txn) {
+ _real->registerOperation(txn);
+ }
+ void unregisterOperation() {
+ _real->unregisterOperation();
+ }
+ void init(const BSONObj* data) {
+ _real->init(data);
+ }
+ void localConnectForDbEval(OperationContext* txn, const char* dbName) {
+ invariant(!"localConnectForDbEval should only be called from dbEval");
+ }
+ void setLocalDB(const string& dbName) {
+ _real->setLocalDB(dbName);
+ }
+ void loadStored(OperationContext* txn, bool ignoreNotConnected = false) {
+ _real->loadStored(txn, ignoreNotConnected);
+ }
+ void externalSetup() {
+ _real->externalSetup();
+ }
+ void gc() {
+ _real->gc();
+ }
+ bool isKillPending() const {
+ return _real->isKillPending();
+ }
+ int type(const char* field) {
+ return _real->type(field);
+ }
+ string getError() {
+ return _real->getError();
+ }
+ bool hasOutOfMemoryException() {
+ return _real->hasOutOfMemoryException();
+ }
+ void rename(const char* from, const char* to) {
+ _real->rename(from, to);
+ }
+ 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);
+ }
+ void setNumber(const char* field, double val) {
+ _real->setNumber(field, val);
+ }
+ void setString(const char* field, StringData val) {
+ _real->setString(field, val);
+ }
+ void setElement(const char* field, const BSONElement& val) {
+ _real->setElement(field, val);
+ }
+ void setObject(const char* field, const BSONObj& obj, bool readOnly = true) {
+ _real->setObject(field, obj, readOnly);
+ }
+ bool isLastRetNativeCode() {
+ return _real->isLastRetNativeCode();
+ }
- private:
- string _pool;
- std::shared_ptr<Scope> _real;
- };
+ void setBoolean(const char* field, bool val) {
+ _real->setBoolean(field, val);
+ }
+ void setFunction(const char* field, const char* code) {
+ _real->setFunction(field, code);
+ }
+ ScriptingFunction createFunction(const char* code) {
+ return _real->createFunction(code);
+ }
+ 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, readOnlyArgs, readOnlyRecv);
+ }
+ bool exec(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, int timeoutMs = 0) {
+ return _real->execFile(filename, printResult, reportError, timeoutMs);
+ }
+ void injectNative(const char* field, NativeFunction func, void* data) {
+ _real->injectNative(field, func, data);
+ }
+ void append(BSONObjBuilder& builder, const char* fieldName, const char* scopeName) {
+ _real->append(builder, fieldName, scopeName);
+ }
- /** Get a scope from the pool of scopes matching the supplied pool name */
- unique_ptr<Scope> ScriptEngine::getPooledScope(OperationContext* txn,
- const string& db,
- const string& scopeType) {
- const string fullPoolName = db + scopeType;
- std::shared_ptr<Scope> s = scopeCache.tryAcquire(txn, fullPoolName);
- if (!s) {
- s.reset(newScope());
- s->registerOperation(txn);
- }
+protected:
+ FunctionCacheMap& getFunctionCache() {
+ return _real->getFunctionCache();
+ }
- unique_ptr<Scope> p;
- p.reset(new PooledScope(fullPoolName, s));
- p->setLocalDB(db);
- p->loadStored(txn, true);
- return p;
+ ScriptingFunction _createFunction(const char* code, ScriptingFunction functionNumber = 0) {
+ return _real->_createFunction(code, functionNumber);
}
- void (*ScriptEngine::_connectCallback)(DBClientWithCommands&) = 0;
- ScriptEngine* globalScriptEngine = 0;
+private:
+ string _pool;
+ std::shared_ptr<Scope> _real;
+};
+
+/** Get a scope from the pool of scopes matching the supplied pool name */
+unique_ptr<Scope> ScriptEngine::getPooledScope(OperationContext* txn,
+ const string& db,
+ const string& scopeType) {
+ const string fullPoolName = db + scopeType;
+ std::shared_ptr<Scope> s = scopeCache.tryAcquire(txn, fullPoolName);
+ if (!s) {
+ s.reset(newScope());
+ s->registerOperation(txn);
+ }
- bool hasJSReturn(const string& code) {
- size_t x = code.find("return");
- if (x == string::npos)
- return false;
+ unique_ptr<Scope> p;
+ p.reset(new PooledScope(fullPoolName, s));
+ p->setLocalDB(db);
+ p->loadStored(txn, true);
+ return p;
+}
- int quoteCount = 0;
- int singleQuoteCount = 0;
- for (size_t i = 0; i < x; i++) {
- if (code[i] == '"') {
- quoteCount++;
- } else if(code[i] == '\'') {
- singleQuoteCount++;
- }
+void (*ScriptEngine::_connectCallback)(DBClientWithCommands&) = 0;
+ScriptEngine* globalScriptEngine = 0;
+
+bool hasJSReturn(const string& code) {
+ size_t x = code.find("return");
+ if (x == string::npos)
+ return false;
+
+ int quoteCount = 0;
+ int singleQuoteCount = 0;
+ for (size_t i = 0; i < x; i++) {
+ if (code[i] == '"') {
+ quoteCount++;
+ } else if (code[i] == '\'') {
+ singleQuoteCount++;
}
- // if we are in either single quotes or double quotes return false
- if (quoteCount % 2 != 0 || singleQuoteCount % 2 != 0) {
- return false;
- }
-
- // return is at start OR preceded by space
- // AND return is not followed by digit or letter
- return (x == 0 || isspace(code[x-1])) &&
- !(isalpha(code[x+6]) || isdigit(code[x+6]));
+ }
+ // if we are in either single quotes or double quotes return false
+ if (quoteCount % 2 != 0 || singleQuoteCount % 2 != 0) {
+ return false;
}
- 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 is at start OR preceded by space
+ // AND return is not followed by digit or letter
+ return (x == 0 || isspace(code[x - 1])) && !(isalpha(code[x + 6]) || isdigit(code[x + 6]));
+}
+
+const char* jsSkipWhiteSpace(const char* raw) {
+ while (raw[0]) {
+ while (isspace(*raw)) {
+ ++raw;
}
- return raw;
+ if (raw[0] != '/' || raw[1] != '/')
+ break;
+ while (raw[0] && raw[0] != '\n')
+ raw++;
}
+ return raw;
+}
}