summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/engine_v8.cpp
diff options
context:
space:
mode:
authorApollon Oikonomopoulos <apoikos@debian.org>2016-01-14 00:10:06 +0200
committerApollon Oikonomopoulos <apollon@skroutz.gr>2016-01-14 00:10:06 +0200
commit374e1947abcd3e127a2a613aff73ecffdb9199ea (patch)
treed83973c3c9802450acd5b5e86fe0d4e8e60a3a1b /src/mongo/scripting/engine_v8.cpp
parent65585c90b12d6523bea75a2aebaae2a2fdf9e641 (diff)
Imported Upstream version 2.6.11upstream/2.6.11
Diffstat (limited to 'src/mongo/scripting/engine_v8.cpp')
-rw-r--r--src/mongo/scripting/engine_v8.cpp301
1 files changed, 168 insertions, 133 deletions
diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp
index 5aa40b1abf0..827f4cbd241 100644
--- a/src/mongo/scripting/engine_v8.cpp
+++ b/src/mongo/scripting/engine_v8.cpp
@@ -13,10 +13,23 @@
*
* 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/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
*/
#include "mongo/scripting/engine_v8.h"
+#include "mongo/base/init.h"
#include "mongo/platform/unordered_set.h"
#include "mongo/scripting/v8_db.h"
#include "mongo/scripting/v8_utils.h"
@@ -27,6 +40,10 @@ using namespace mongoutils;
namespace mongo {
+#ifndef _MSC_EXTENSIONS
+ const int V8Scope::objectDepthLimit;
+#endif
+
// Generated symbols for JS files
namespace JSFiles {
extern const JSFile types;
@@ -303,78 +320,77 @@ namespace mongo {
template <typename _GCState>
void gcCallback(v8::GCType type, v8::GCCallbackFlags flags) {
- const int verbosity = 1; // log level for stat collection
- if (logLevel < verbosity)
- // don't collect stats unless verbose
- return;
-
- v8::HeapStatistics stats;
- v8::V8::GetHeapStatistics(&stats);
- log() << "V8 GC " << _GCState::name
- << " heap stats - "
- << " total: " << stats.total_heap_size()
- << " exec: " << stats.total_heap_size_executable()
- << " used: " << stats.used_heap_size()<< " limit: "
- << stats.heap_size_limit()
- << endl;
- }
-
- V8ScriptEngine::V8ScriptEngine() :
- _globalInterruptLock("GlobalV8InterruptLock"),
- _opToScopeMap(),
- _deadlineMonitor() {
- }
-
- V8ScriptEngine::~V8ScriptEngine() {
- }
-
- void ScriptEngine::setup() {
- if (!globalScriptEngine) {
- globalScriptEngine = new V8ScriptEngine();
- }
- }
-
- std::string ScriptEngine::getInterpreterVersionString() {
- return "V8 3.12.19";
- }
-
- void V8ScriptEngine::interrupt(unsigned opId) {
- mongo::mutex::scoped_lock intLock(_globalInterruptLock);
- OpIdToScopeMap::iterator iScope = _opToScopeMap.find(opId);
- if (iScope == _opToScopeMap.end()) {
- // got interrupt request for a scope that no longer exists
- LOG(1) << "received interrupt request for unknown op: " << opId
- << printKnownOps_inlock() << endl;
- return;
- }
- LOG(1) << "interrupting op: " << opId << printKnownOps_inlock() << endl;
- iScope->second->kill();
- }
-
- void V8ScriptEngine::interruptAll() {
- mongo::mutex::scoped_lock interruptLock(_globalInterruptLock);
- for (OpIdToScopeMap::iterator iScope = _opToScopeMap.begin();
- iScope != _opToScopeMap.end(); ++iScope) {
- iScope->second->kill();
- }
- }
-
- void V8Scope::registerOpId() {
- scoped_lock giLock(_engine->_globalInterruptLock);
- if (_engine->haveGetCurrentOpIdCallback()) {
- // this scope has an associated operation
- _opId = _engine->getCurrentOpId();
- _engine->_opToScopeMap[_opId] = this;
- }
- else
- // no associated op id (e.g. running from shell)
- _opId = 0;
- LOG(2) << "V8Scope " << this << " registered for op " << _opId << endl;
- }
-
- void V8Scope::unregisterOpId() {
- scoped_lock giLock(_engine->_globalInterruptLock);
- LOG(2) << "V8Scope " << this << " unregistered for op " << _opId << endl;
+ if (!logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)))
+ // don't collect stats unless verbose
+ return;
+
+ v8::HeapStatistics stats;
+ v8::V8::GetHeapStatistics(&stats);
+ log() << "V8 GC " << _GCState::name
+ << " heap stats - "
+ << " total: " << stats.total_heap_size()
+ << " exec: " << stats.total_heap_size_executable()
+ << " used: " << stats.used_heap_size()<< " limit: "
+ << stats.heap_size_limit()
+ << endl;
+ }
+
+ V8ScriptEngine::V8ScriptEngine() :
+ _globalInterruptLock("GlobalV8InterruptLock"),
+ _opToScopeMap(),
+ _deadlineMonitor() {
+ }
+
+ V8ScriptEngine::~V8ScriptEngine() {
+ }
+
+ void ScriptEngine::setup() {
+ if (!globalScriptEngine) {
+ globalScriptEngine = new V8ScriptEngine();
+ }
+ }
+
+ std::string ScriptEngine::getInterpreterVersionString() {
+ return "V8 3.12.19";
+ }
+
+ void V8ScriptEngine::interrupt(unsigned opId) {
+ mongo::mutex::scoped_lock intLock(_globalInterruptLock);
+ OpIdToScopeMap::iterator iScope = _opToScopeMap.find(opId);
+ if (iScope == _opToScopeMap.end()) {
+ // got interrupt request for a scope that no longer exists
+ LOG(1) << "received interrupt request for unknown op: " << opId
+ << printKnownOps_inlock() << endl;
+ return;
+ }
+ LOG(1) << "interrupting op: " << opId << printKnownOps_inlock() << endl;
+ iScope->second->kill();
+ }
+
+ void V8ScriptEngine::interruptAll() {
+ mongo::mutex::scoped_lock interruptLock(_globalInterruptLock);
+ for (OpIdToScopeMap::iterator iScope = _opToScopeMap.begin();
+ iScope != _opToScopeMap.end(); ++iScope) {
+ iScope->second->kill();
+ }
+ }
+
+ void V8Scope::registerOpId() {
+ scoped_lock giLock(_engine->_globalInterruptLock);
+ if (_engine->haveGetCurrentOpIdCallback()) {
+ // this scope has an associated operation
+ _opId = _engine->getCurrentOpId();
+ _engine->_opToScopeMap[_opId] = this;
+ }
+ else
+ // no associated op id (e.g. running from shell)
+ _opId = 0;
+ LOG(2) << "V8Scope " << static_cast<const void*>(this) << " registered for op " << _opId << endl;
+ }
+
+ void V8Scope::unregisterOpId() {
+ scoped_lock giLock(_engine->_globalInterruptLock);
+ LOG(2) << "V8Scope " << static_cast<const void*>(this) << " unregistered for op " << _opId << endl;
if (_engine->haveGetCurrentOpIdCallback() || _opId != 0) {
// scope is currently associated with an operation id
V8ScriptEngine::OpIdToScopeMap::iterator it = _engine->_opToScopeMap.find(_opId);
@@ -387,12 +403,12 @@ namespace mongo {
v8::Locker l(_isolate);
mongo::mutex::scoped_lock cbEnterLock(_interruptLock);
if (v8::V8::IsExecutionTerminating(_isolate)) {
- LOG(2) << "v8 execution interrupted. isolate: " << _isolate << endl;
+ LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate) << endl;
return false;
}
if (_pendingKill || globalScriptEngine->interrupted()) {
// kill flag was set before entering our callback
- LOG(2) << "marked for death while leaving callback. isolate: " << _isolate << endl;
+ LOG(2) << "marked for death while leaving callback. isolate: " << static_cast<const void*>(_isolate) << endl;
v8::V8::TerminateExecution(_isolate);
return false;
}
@@ -405,11 +421,11 @@ namespace mongo {
mongo::mutex::scoped_lock cbLeaveLock(_interruptLock);
_inNativeExecution = false;
if (v8::V8::IsExecutionTerminating(_isolate)) {
- LOG(2) << "v8 execution interrupted. isolate: " << _isolate << endl;
+ LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate) << endl;
return false;
}
if (_pendingKill || globalScriptEngine->interrupted()) {
- LOG(2) << "marked for death while leaving callback. isolate: " << _isolate << endl;
+ LOG(2) << "marked for death while leaving callback. isolate: " << static_cast<const void*>(_isolate) << endl;
v8::V8::TerminateExecution(_isolate);
return false;
}
@@ -422,9 +438,9 @@ namespace mongo {
// Set the TERMINATE flag on the stack guard for this isolate.
// This won't happen between calls to nativePrologue and nativeEpilogue().
v8::V8::TerminateExecution(_isolate);
- LOG(1) << "killing v8 scope. isolate: " << _isolate << endl;
+ LOG(1) << "killing v8 scope. isolate: " << static_cast<const void*>(_isolate) << endl;
}
- LOG(1) << "marking v8 scope for death. isolate: " << _isolate << endl;
+ LOG(1) << "marking v8 scope for death. isolate: " << static_cast<const void*>(_isolate) << endl;
_pendingKill = true;
}
@@ -438,7 +454,7 @@ namespace mongo {
*/
std::string V8ScriptEngine::printKnownOps_inlock() {
stringstream out;
- if (logLevel > 1) {
+ if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2))) {
out << " known ops: " << endl;
for(OpIdToScopeMap::iterator iSc = _opToScopeMap.begin();
iSc != _opToScopeMap.end(); ++iSc) {
@@ -458,7 +474,7 @@ namespace mongo {
_pendingKill(false) {
// create new isolate and enter it via a scope
- _isolate = v8::Isolate::New();
+ _isolate.set(v8::Isolate::New());
v8::Isolate::Scope iscope(_isolate);
// lock the isolate and enter the context
@@ -529,24 +545,12 @@ namespace mongo {
// install global utility functions
installGlobalUtils(*this);
+ // Don't add anything that can throw after this line otherwise we won't be unregistered.
registerOpId();
}
V8Scope::~V8Scope() {
unregisterOpId();
- {
- V8_SIMPLE_HEADER
- for(unsigned i = 0; i < _funcs.size(); ++i)
- _funcs[ i ].Dispose();
- _funcs.clear();
- _global.Dispose();
- _context.Dispose();
- // Note: This block is unnecessary since we destroy the v8 Heap (Isolate) immediately
- // after. Leaving in for now, but nothing new should be added.
- }
- _isolate->Dispose();
- // set the isolate to NULL so ObjTracker destructors know that v8 is no longer reachable
- _isolate = NULL;
}
bool V8Scope::hasOutOfMemoryException() {
@@ -560,7 +564,7 @@ namespace mongo {
v8::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)) {
+ if (!scope->execFile(filename, false, true)) {
return v8AssertionException(string("error loading js file: ") + filename);
}
}
@@ -572,11 +576,11 @@ namespace mongo {
string exceptionText;
v8::HandleScope handle_scope;
try {
- v8::Local<v8::External> f =
- v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_native_function")));
+ v8::Local<v8::External> f = args.Callee()->GetHiddenValue(
+ scope->strLitToV8("_native_function")).As<v8::External>();
NativeFunction function = (NativeFunction)(f->Value());
- v8::Local<v8::External> data =
- v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_native_data")));
+ v8::Local<v8::External> data = args.Callee()->GetHiddenValue(
+ scope->strLitToV8("_native_data")).As<v8::External>();
BSONObjBuilder b;
for (int i = 0; i < args.Length(); ++i)
scope->v8ToMongoElement(b, BSONObjBuilder::numStr(i), args[i]);
@@ -603,8 +607,7 @@ namespace mongo {
// execution terminated
return v8::Undefined();
- v8::Local<v8::External> f =
- v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_v8_function")));
+ v8::Local<v8::External> f = v8::Local<v8::External>::Cast(args.Data());
v8Function function = (v8Function)(f->Value());
v8::Handle<v8::Value> ret;
string exceptionText;
@@ -646,9 +649,9 @@ namespace mongo {
_global->ForceSet(v8StringData(field), v8::Number::New(val));
}
- void V8Scope::setString(const char * field, const char * val) {
+ void V8Scope::setString(const char * field, const StringData& val) {
V8_SIMPLE_HEADER
- _global->ForceSet(v8StringData(field), v8::String::New(val));
+ _global->ForceSet(v8StringData(field), v8::String::New(val.rawData(), val.size()));
}
void V8Scope::setBoolean(const char * field, bool val) {
@@ -840,8 +843,7 @@ namespace mongo {
std::string V8Scope::v8ExceptionToSTLString(const v8::TryCatch* try_catch) {
stringstream ss;
- v8::String::Utf8Value exceptionText(try_catch->Exception());
- ss << *exceptionText;
+ ss << StringData(V8String((try_catch->Exception())));
// get the exception message
v8::Handle<v8::Message> message = try_catch->Message();
@@ -951,8 +953,9 @@ namespace mongo {
string fn = str::stream() << "_funcs" << functionNumber;
code = str::stream() << fn << " = " << code;
- v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(code.c_str()),
- v8::String::New(fn.c_str()));
+ v8::Handle<v8::Script> script = v8::Script::Compile(
+ v8::String::New(code.c_str(), code.length()),
+ v8::String::New(fn.c_str()));
// throw on error
checkV8ErrorState(script, try_catch);
@@ -1066,7 +1069,7 @@ namespace mongo {
v8::Handle<v8::Script> script =
v8::Script::Compile(v8::String::New(code.rawData(), code.size()),
- v8::String::New(name.c_str()));
+ v8::String::New(name.c_str(), name.length()));
if (checkV8ErrorState(script, try_catch, reportError, assertOnError))
return false;
@@ -1117,16 +1120,15 @@ namespace mongo {
injectNative(field, func, _global, data);
}
- void V8Scope::injectNative(const char *field, NativeFunction func, v8::Handle<v8::Object>& obj,
+ void V8Scope::injectNative(const char* field,
+ NativeFunction nativeFunc,
+ v8::Handle<v8::Object>& obj,
void* data) {
v8::Handle<v8::FunctionTemplate> ft = createV8Function(nativeCallback);
- ft->Set(strLitToV8("_native_function"),
- v8::External::New((void*)func),
- v8::PropertyAttribute(v8::DontEnum | v8::ReadOnly));
- ft->Set(strLitToV8("_native_data"),
- v8::External::New(data),
- v8::PropertyAttribute(v8::DontEnum | v8::ReadOnly));
injectV8Function(field, ft, obj);
+ v8::Handle<v8::Function> func = ft->GetFunction();
+ func->SetHiddenValue(strLitToV8("_native_function"), v8::External::New((void*)nativeFunc));
+ func->SetHiddenValue(strLitToV8("_native_data"), v8::External::New(data));
}
v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Function(const char *field, v8Function func) {
@@ -1163,8 +1165,9 @@ namespace mongo {
}
v8::Handle<v8::FunctionTemplate> V8Scope::createV8Function(v8Function func) {
- v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(v8Callback);
- ft->Set(strLitToV8("_v8_function"), v8::External::New(reinterpret_cast<void*>(func)),
+ v8::Handle<v8::Value> funcHandle = v8::External::New(reinterpret_cast<void*>(func));
+ v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(v8Callback, funcHandle);
+ ft->Set(strLitToV8("_v8_function"), v8::Boolean::New(true),
static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::ReadOnly));
return ft;
}
@@ -1196,9 +1199,6 @@ namespace mongo {
// install db access functions in the global object
installDBAccess();
- // add global load() helper
- injectV8Function("load", load);
-
// install the Mongo function object and instantiate the 'db' global
_MongoFT = FTPtr::New(getMongoFunctionTemplate(this, true));
injectV8Function("Mongo", MongoFT(), _global);
@@ -1283,9 +1283,14 @@ namespace mongo {
injectV8Function("MD5", md5Init);
injectV8Function("HexData", hexDataInit);
+ injectV8Function("bsonWoCompare", bsonWoCompare);
+
_global->Get(strLitToV8("Object"))->ToObject()->ForceSet(
strLitToV8("bsonsize"),
createV8Function(bsonsize)->GetFunction());
+ _global->Get(strLitToV8("Object"))->ToObject()->ForceSet(
+ strLitToV8("invalidForStorage"),
+ createV8Function(v8ObjectInvalidForStorage)->GetFunction());
}
@@ -1300,12 +1305,13 @@ namespace mongo {
registerOpId();
}
- v8::Local<v8::Value> V8Scope::newFunction(const char *code) {
+ v8::Local<v8::Value> V8Scope::newFunction(const StringData& code) {
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
string codeStr = str::stream() << "____MongoToV8_newFunction_temp = " << code;
- v8::Local<v8::Script> compiled = v8::Script::New(v8::String::New(codeStr.c_str()));
+ v8::Local<v8::Script> compiled = v8::Script::New(v8::String::New(codeStr.c_str(),
+ codeStr.length()));
// throw on compile error
checkV8ErrorState(compiled, try_catch);
@@ -1322,7 +1328,8 @@ namespace mongo {
v8::HandleScope handle_scope;
v8::Handle<v8::Function> idCons = ObjectIdFT()->GetFunction();
v8::Handle<v8::Value> argv[1];
- argv[0] = v8::String::New(id.str().c_str());
+ const string& idString = id.str();
+ argv[0] = v8::String::New(idString.c_str(), idString.length());
return handle_scope.Close(idCons->NewInstance(1, argv));
}
@@ -1367,14 +1374,14 @@ namespace mongo {
switch (elem.type()) {
case mongo::Code:
- return newFunction(elem.valuestr());
+ return newFunction(StringData(elem.valuestr(), elem.valuestrsize() - 1));
case CodeWScope:
if (!elem.codeWScopeObject().isEmpty())
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
- return newFunction(elem.codeWScopeCode());
+ return newFunction(StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1));
case mongo::Symbol:
case mongo::String:
- return v8::String::New(elem.valuestr());
+ return v8::String::New(elem.valuestr(), elem.valuestrsize() - 1);
case mongo::jstOID:
return newId(elem.__oid());
case mongo::NumberDouble:
@@ -1433,10 +1440,21 @@ namespace mongo {
argv[1] = v8::String::New(ss.str().c_str());
return BinDataFT()->GetFunction()->NewInstance(2, argv);
}
- case mongo::Timestamp:
+ case mongo::Timestamp: {
+ v8::TryCatch tryCatch;
+
argv[0] = v8::Number::New(elem.timestampTime() / 1000);
argv[1] = v8::Number::New(elem.timestampInc());
- return TimestampFT()->GetFunction()->NewInstance(2,argv);
+
+ v8::Handle<v8::Value> ret = TimestampFT()->GetFunction()->NewInstance(2,argv);
+ uassert(17355, str::stream() << "Error converting " << elem.toString(false)
+ << " in field " << elem.fieldName()
+ << " to a JS Timestamp object: "
+ << toSTLString(tryCatch.Exception()),
+ !tryCatch.HasCaught());
+
+ return ret;
+ }
case mongo::NumberLong:
nativeUnsignedLong = elem.numberLong();
// values above 2^53 are not accurately represented in JS
@@ -1514,11 +1532,11 @@ namespace mongo {
verify(BinDataFT()->HasInstance(obj));
verify(obj->InternalFieldCount() == 1);
- int len = obj->Get(strLitToV8("len"))->ToInt32()->Value();
+ std::string binData(base64::decode(toSTLString(obj->GetInternalField(0))));
b.appendBinData(elementName,
- len,
+ binData.size(),
mongo::BinDataType(obj->Get(strLitToV8("type"))->ToInt32()->Value()),
- base64::decode(toSTLString(obj->GetInternalField(0))).c_str());
+ binData.c_str());
}
OID V8Scope::v8ToMongoObjectID(v8::Handle<v8::Object> obj) {
@@ -1572,6 +1590,10 @@ namespace mongo {
void V8Scope::v8ToMongoElement(BSONObjBuilder & b, const StringData& sname,
v8::Handle<v8::Value> value, int depth,
BSONObj* originalParent) {
+ uassert(17279,
+ str::stream() << "Exceeded depth limit of " << objectDepthLimit
+ << " when converting js object to BSON. Do you have a cycle?",
+ depth < objectDepthLimit);
// Null char should be at the end, not in the string
uassert(16985,
@@ -1665,13 +1687,22 @@ namespace mongo {
v8::Local<v8::Value> value = o->Get(name);
v8ToMongoElement(b, sname, value, depth + 1, &originalBSON);
}
- return b.obj();
+
+ const int sizeWithEOO = b.len() + 1/*EOO*/ - 4/*BSONObj::Holder ref count*/;
+ uassert(17260, str::stream() << "Converting from JavaScript to BSON failed: "
+ << "Object size " << sizeWithEOO << " exceeds limit of "
+ << BSONObjMaxInternalSize << " bytes.",
+ sizeWithEOO <= BSONObjMaxInternalSize);
+
+ return b.obj(); // Would give an uglier error than above for oversized objects.
}
// --- random utils ----
+ static logger::MessageLogDomain* jsPrintLogDomain;
v8::Handle<v8::Value> V8Scope::Print(V8Scope* scope, const v8::Arguments& args) {
- stringstream ss;
+ LogstreamBuilder builder(jsPrintLogDomain, getThreadName(), logger::LogSeverity::Log());
+ std::ostream& ss = builder.stream();
v8::HandleScope handle_scope;
bool first = true;
for (int i = 0; i < args.Length(); i++) {
@@ -1695,7 +1726,6 @@ namespace mongo {
ss << *str;
}
ss << "\n";
- Logstream::logLockless(ss.str());
return handle_scope.Close(v8::Undefined());
}
@@ -1735,4 +1765,9 @@ namespace mongo {
*v8::String::Utf8Value(args[0]->ToString())));
}
+ MONGO_INITIALIZER(JavascriptPrintDomain)(InitializerContext*) {
+ jsPrintLogDomain = logger::globalLogManager()->getNamedDomain("javascriptOutput");
+ return Status::OK();
+ }
+
} // namespace mongo