summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct28
-rw-r--r--distsrc/THIRD-PARTY-NOTICES52
-rw-r--r--src/mongo/SConscript3
-rw-r--r--src/mongo/scripting/SConscript23
-rw-r--r--src/mongo/scripting/engine_v8-3.25.cpp1913
-rw-r--r--src/mongo/scripting/engine_v8-3.25.h653
-rw-r--r--src/mongo/scripting/engine_v8.cpp1880
-rw-r--r--src/mongo/scripting/engine_v8.h629
-rw-r--r--src/mongo/scripting/v8-3.25_db.cpp1079
-rw-r--r--src/mongo/scripting/v8-3.25_db.h155
-rw-r--r--src/mongo/scripting/v8-3.25_profiler.cpp78
-rw-r--r--src/mongo/scripting/v8-3.25_profiler.h58
-rw-r--r--src/mongo/scripting/v8-3.25_utils.cpp420
-rw-r--r--src/mongo/scripting/v8-3.25_utils.h117
-rw-r--r--src/mongo/scripting/v8_db.cpp1195
-rw-r--r--src/mongo/scripting/v8_db.h146
-rw-r--r--src/mongo/scripting/v8_profiler.cpp78
-rw-r--r--src/mongo/scripting/v8_profiler.h58
-rw-r--r--src/mongo/scripting/v8_utils.cpp406
-rw-r--r--src/mongo/scripting/v8_utils.h117
-rw-r--r--src/third_party/SConscript27
21 files changed, 7 insertions, 9108 deletions
diff --git a/SConstruct b/SConstruct
index 26fcc887a3b..1ccc0506d6c 100644
--- a/SConstruct
+++ b/SConstruct
@@ -116,7 +116,7 @@ def use_system_version_of_library(name):
# add a new C++ library dependency that may be shimmed out to the system, add it to the below
# list.
def using_system_version_of_cxx_libraries():
- cxx_library_names = ["tcmalloc", "boost", "v8"]
+ cxx_library_names = ["tcmalloc", "boost"]
return True in [use_system_version_of_library(x) for x in cxx_library_names]
def make_variant_dir_generator():
@@ -213,11 +213,10 @@ add_option('wiredtiger',
type='choice',
)
-# library choices
-js_engine_choices = ['v8-3.12', 'v8-3.25', 'mozjs', 'none']
+js_engine_choices = ['mozjs', 'none']
add_option('js-engine',
choices=js_engine_choices,
- default=js_engine_choices[2],
+ default=js_engine_choices[0],
help='JavaScript scripting engine implementation',
type='choice',
)
@@ -358,11 +357,6 @@ add_option('use-system-zlib',
nargs=0,
)
-add_option('use-system-v8',
- help='use system version of v8 library',
- nargs=0,
-)
-
add_option('use-system-stemmer',
help='use system version of stemmer',
nargs=0)
@@ -786,14 +780,9 @@ jsEngine = get_option( "js-engine")
serverJs = get_option( "server-js" ) == "on"
-usev8 = (jsEngine.startswith('v8'))
-
usemozjs = (jsEngine.startswith('mozjs'))
-v8version = jsEngine[3:] if jsEngine.startswith('v8-') else 'none'
-v8suffix = '' if v8version == '3.12' else '-' + v8version
-
-if not serverJs and not usev8 and not usemozjs:
+if not serverJs and not usemozjs:
print("Warning: --server-js=off is not needed with --js-engine=none")
# We defer building the env until we have determined whether we want certain values. Some values
@@ -2234,13 +2223,6 @@ def doConfigure(myenv):
if env.TargetOSIs('solaris'):
conf.CheckLib( "nsl" )
- if usev8 and use_system_version_of_library("v8"):
- if debugBuild:
- v8_lib_choices = ["v8_g", "v8"]
- else:
- v8_lib_choices = ["v8"]
- conf.FindSysLibDep( "v8", v8_lib_choices )
-
conf.env['MONGO_BUILD_SASL_CLIENT'] = bool(has_option("use-sasl-client"))
if conf.env['MONGO_BUILD_SASL_CLIENT'] and not conf.CheckLibWithHeader(
"sasl2",
@@ -2441,9 +2423,7 @@ Export("env")
Export("get_option")
Export("has_option use_system_version_of_library")
Export("serverJs")
-Export("usev8")
Export("usemozjs")
-Export("v8version v8suffix")
Export("boostSuffix")
Export('module_sconscripts')
Export("debugBuild optBuild")
diff --git a/distsrc/THIRD-PARTY-NOTICES b/distsrc/THIRD-PARTY-NOTICES
index 37349340d22..353772aee41 100644
--- a/distsrc/THIRD-PARTY-NOTICES
+++ b/distsrc/THIRD-PARTY-NOTICES
@@ -42,58 +42,6 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
-2) License Notice for V8
--------------------------
-
-http://code.google.com/p/v8/source/browse/trunk/LICENSE
-
-This license applies to all parts of V8 that are not externally
-maintained libraries. The externally maintained libraries used by V8
-are:
-
- - Jscre, located under third_party/jscre. This code is copyrighted
- by the University of Cambridge and Apple Inc. and released under a
- 2-clause BSD license.
-
- - Dtoa, located under third_party/dtoa. This code is copyrighted by
- David M. Gay and released under an MIT license.
-
- - Strongtalk assembler, the basis of the files assembler-arm-inl.h,
- assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h,
- assembler-ia32.cc, assembler-ia32.h, assembler.cc and assembler.h.
- This code is copyrighted by Sun Microsystems Inc. and released
- under a 3-clause BSD license.
-
-These libraries have their own licenses; we recommend you read them,
-as their terms may differ from the terms below.
-
-Copyright 2006-2008, Google Inc. All rights reserved.
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
- * Neither the name of Google Inc. nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
3) License Notice for PCRE
--------------------------
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index bab71bd6ac8..947bd37f6bd 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -14,7 +14,6 @@ import SCons.Action
Import("env")
Import("has_option")
Import("get_option")
-Import("usev8")
Import("usemozjs")
Import("use_system_version_of_library")
@@ -348,7 +347,7 @@ if env.TargetOSIs('osx') or env["_HAVEPCAP"]:
# --- shell ---
-if not has_option('noshell') and (usev8 or usemozjs):
+if not has_option('noshell') and usemozjs:
shell_core_env = env.Clone()
if has_option("safeshell"):
shell_core_env.Append(CPPDEFINES=["MONGO_SAFE_SHELL"])
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript
index 5db8dd52228..e6bc59b5464 100644
--- a/src/mongo/scripting/SConscript
+++ b/src/mongo/scripting/SConscript
@@ -3,9 +3,7 @@
Import([
'env',
'serverJs',
- 'usev8',
'usemozjs',
- 'v8suffix',
])
env.Library(
@@ -54,26 +52,7 @@ env.Library(
],
)
-if usev8:
- scriptingEnv = env.Clone()
- scriptingEnv.InjectThirdPartyIncludePaths(libraries=['v8'])
- scriptingEnv.Library(
- target='scripting',
- source=[
- 'engine_v8' + v8suffix + '.cpp',
- 'v8' + v8suffix + '_db.cpp',
- 'v8' + v8suffix + '_utils.cpp',
- 'v8' + v8suffix + '_profiler.cpp',
- ],
- LIBDEPS=[
- 'bson_template_evaluator',
- 'scripting_common',
- '$BUILD_DIR/third_party/shim_v8',
- '$BUILD_DIR/mongo/shell/mongojs',
- '$BUILD_DIR/mongo/db/service_context',
- ],
- )
-elif usemozjs:
+if usemozjs:
scriptingEnv = env.Clone()
scriptingEnv.InjectThirdPartyIncludePaths(libraries=['mozjs'])
diff --git a/src/mongo/scripting/engine_v8-3.25.cpp b/src/mongo/scripting/engine_v8-3.25.cpp
deleted file mode 100644
index b7f241c8291..00000000000
--- a/src/mongo/scripting/engine_v8-3.25.cpp
+++ /dev/null
@@ -1,1913 +0,0 @@
-/* Copyright 2014 MongoDB 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/>.
- *
- * 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.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/scripting/engine_v8-3.25.h"
-
-#include <iostream>
-
-#include "mongo/base/init.h"
-#include "mongo/db/service_context.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/platform/unordered_set.h"
-#include "mongo/scripting/v8-3.25_db.h"
-#include "mongo/scripting/v8-3.25_utils.h"
-#include "mongo/util/base64.h"
-#include "mongo/util/log.h"
-#include "mongo/util/mongoutils/str.h"
-
-using namespace mongoutils;
-
-namespace mongo {
-
-using std::cout;
-using std::endl;
-using std::map;
-using std::string;
-using std::stringstream;
-
-#ifndef _MSC_EXTENSIONS
-const int V8Scope::objectDepthLimit;
-#endif
-
-// Generated symbols for JS files
-namespace JSFiles {
-extern const JSFile types;
-extern const JSFile assert;
-}
-
-// The unwrapXXX functions extract internal fields from an object wrapped by wrapBSONObject.
-// These functions are currently only used in places that should always have the correct
-// type of object, however it may be possible for users to come up with a way to make these
-// called with the wrong type so calling code should always check the returns.
-static BSONHolder* unwrapHolder(V8Scope* scope, const v8::Local<v8::Object>& obj) {
- // Warning: can't throw exceptions in this context.
- if (!scope->LazyBsonFT()->HasInstance(obj))
- return NULL;
-
- v8::Local<v8::External> field = v8::Local<v8::External>::Cast(obj->GetInternalField(0));
- if (field.IsEmpty() || !field->IsExternal())
- return 0;
- void* ptr = field->Value();
- return (BSONHolder*)ptr;
-}
-
-static BSONObj unwrapBSONObj(V8Scope* scope, const v8::Local<v8::Object>& obj) {
- // Warning: can't throw exceptions in this context.
- BSONHolder* holder = unwrapHolder(scope, obj);
- return holder ? holder->_obj : BSONObj();
-}
-
-static v8::Local<v8::Object> unwrapObject(V8Scope* scope, const v8::Local<v8::Object>& obj) {
- // Warning: can't throw exceptions in this context.
- if (!scope->LazyBsonFT()->HasInstance(obj))
- return v8::Local<v8::Object>();
-
- return obj->GetInternalField(1).As<v8::Object>();
-}
-
-void V8Scope::wrapBSONObject(v8::Local<v8::Object> obj, BSONObj data, bool readOnly) {
- verify(LazyBsonFT()->HasInstance(obj));
-
- // Nothing below throws
- BSONHolder* holder = new BSONHolder(this, data, readOnly);
- obj->SetInternalField(0, v8::External::New(_isolate, holder)); // Holder
- obj->SetInternalField(1, v8::Object::New(_isolate)); // Object
- bsonHolderTracker.track(_isolate, obj, holder);
-}
-
-static void namedGet(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::HandleScope handle_scope(info.GetIsolate());
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- try {
- V8Scope* scope = getScope(info.GetIsolate());
- v8::Local<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return;
- if (realObject->HasOwnProperty(name)) {
- // value already cached or added
- result.Set(realObject->Get(name));
- return;
- }
-
- string key = toSTLString(name);
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder || holder->_removed.count(key))
- return;
-
- BSONObj obj = holder->_obj;
- BSONElement elmt = obj.getField(key.c_str());
- if (elmt.eoo())
- return;
-
- val = scope->mongoToV8Element(elmt, holder->_readOnly);
- result.Set(val);
-
- if (obj.objsize() > 128 || val->IsObject()) {
- // Only cache if expected to help (large BSON) or is required due to js semantics
- realObject->Set(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
- holder->_modified = true;
- }
- } catch (const DBException& dbEx) {
- result.Set(v8AssertionException(dbEx.toString()));
- } catch (...) {
- result.Set(v8AssertionException(string("error getting property ") + toSTLString(name)));
- }
-}
-
-static void namedGetRO(v8::Local<v8::String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- namedGet(name, info);
-}
-
-static void namedSet(v8::Local<v8::String> name,
- v8::Local<v8::Value> value_obj,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- string key = toSTLString(name);
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return;
- holder->_removed.erase(key);
- holder->_modified = true;
-
- v8::Local<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return;
- realObject->Set(name, value_obj);
- result.Set(value_obj);
-}
-
-static void namedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
- v8::HandleScope handle_scope(info.GetIsolate());
- v8::Local<v8::Array> val;
- v8::ReturnValue<v8::Array> result = info.GetReturnValue();
- result.Set(val);
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return;
- BSONObj obj = holder->_obj;
- v8::Local<v8::Array> out = v8::Array::New(scope->getIsolate());
- int outIndex = 0;
-
- unordered_set<StringData, StringData::Hasher> added;
- // note here that if keys are parseable number, v8 will access them using index
- for (BSONObjIterator it(obj); it.more();) {
- const BSONElement& f = it.next();
- StringData sname(f.fieldName(), f.fieldNameSize() - 1);
- if (holder->_removed.count(sname.toString()))
- continue;
-
- v8::Local<v8::String> name = scope->v8StringData(sname);
- added.insert(sname);
- out->Set(outIndex++, name);
- }
-
-
- v8::Local<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return;
- v8::Local<v8::Array> fields = realObject->GetOwnPropertyNames();
- const int len = fields->Length();
- for (int field = 0; field < len; field++) {
- v8::Local<v8::String> name = fields->Get(field).As<v8::String>();
- V8String sname(name);
- if (added.count(sname))
- continue;
- out->Set(outIndex++, name);
- }
- result.Set(out);
-}
-
-void namedDelete(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
- v8::HandleScope handle_scope(info.GetIsolate());
- v8::Local<v8::Boolean> val;
- v8::ReturnValue<v8::Boolean> result = info.GetReturnValue();
- result.Set(val);
- string key = toSTLString(name);
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return;
- holder->_removed.insert(key);
- holder->_modified = true;
-
- v8::Local<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return;
- realObject->Delete(name);
- result.Set(v8::True(scope->getIsolate()));
-}
-
-static void indexedGet(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::HandleScope handle_scope(info.GetIsolate());
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- try {
- V8Scope* scope = getScope(info.GetIsolate());
- v8::Local<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return;
- if (realObject->Has(index)) {
- // value already cached or added
- result.Set(realObject->Get(index));
- }
- string key = str::stream() << index;
-
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return;
- if (holder->_removed.count(key))
- return;
-
- BSONObj obj = holder->_obj;
- BSONElement elmt = obj.getField(key);
- if (elmt.eoo())
- return;
- val = scope->mongoToV8Element(elmt, holder->_readOnly);
- result.Set(val);
- realObject->Set(index, 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
- holder->_modified = true;
- }
- } catch (const DBException& dbEx) {
- result.Set(v8AssertionException(dbEx.toString()));
- } catch (...) {
- result.Set(
- v8AssertionException(str::stream() << "error getting indexed property " << index));
- }
-}
-
-void indexedDelete(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
- v8::Local<v8::Boolean> val;
- v8::ReturnValue<v8::Boolean> result = info.GetReturnValue();
- result.Set(val);
- string key = str::stream() << index;
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return;
- holder->_removed.insert(key);
- holder->_modified = true;
-
- // also delete in JS obj
- v8::Local<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return;
- realObject->Delete(index);
- result.Set(v8::True(scope->getIsolate()));
-}
-
-static void indexedGetRO(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
- indexedGet(index, info);
-}
-
-static void indexedSet(uint32_t index,
- v8::Local<v8::Value> value_obj,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- string key = str::stream() << index;
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return;
- holder->_removed.erase(key);
- holder->_modified = true;
-
- v8::Local<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return;
- realObject->Set(index, value_obj);
- result.Set(value_obj);
-}
-
-void NamedReadOnlySet(v8::Local<v8::String> property,
- v8::Local<v8::Value> value,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- cout << "cannot write property " << V8String(property) << " to read-only object" << endl;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(value);
-}
-
-void NamedReadOnlyDelete(v8::Local<v8::String> property,
- const v8::PropertyCallbackInfo<v8::Boolean>& info) {
- v8::ReturnValue<v8::Boolean> result = info.GetReturnValue();
- result.Set(v8::Boolean::New(info.GetIsolate(), false));
- cout << "cannot delete property " << V8String(property) << " from read-only object" << endl;
-}
-
-void IndexedReadOnlySet(uint32_t index,
- v8::Local<v8::Value> value,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- cout << "cannot write property " << index << " to read-only array" << endl;
-}
-
-void IndexedReadOnlyDelete(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
- v8::ReturnValue<v8::Boolean> result = info.GetReturnValue();
- result.Set(v8::Boolean::New(info.GetIsolate(), false));
- cout << "cannot delete property " << index << " from read-only array" << endl;
-}
-
-/**
- * GC Prologue and Epilogue constants (used to display description constants)
- */
-struct GCPrologueState {
- static const char* name;
-};
-const char* GCPrologueState::name = "prologue";
-struct GCEpilogueState {
- static const char* name;
-};
-const char* GCEpilogueState::name = "epilogue";
-
-template <typename _GCState>
-void gcCallback(v8::GCType type, v8::GCCallbackFlags flags) {
- if (!shouldLog(logger::LogSeverity::Debug(1)))
- // don't collect stats unless verbose
- return;
-
- v8::HeapStatistics stats;
- v8::Isolate::GetCurrent()->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();
-
- if (hasGlobalServiceContext()) {
- getGlobalServiceContext()->registerKillOpListener(globalScriptEngine);
- }
- }
-}
-
-std::string ScriptEngine::getInterpreterVersionString() {
- return "V8 3.25.28";
-}
-
-void V8ScriptEngine::interrupt(unsigned opId) {
- stdx::lock_guard<stdx::mutex> 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() {
- stdx::lock_guard<stdx::mutex> interruptLock(_globalInterruptLock);
- for (OpIdToScopeMap::iterator iScope = _opToScopeMap.begin(); iScope != _opToScopeMap.end();
- ++iScope) {
- iScope->second->kill();
- }
-}
-
-void V8Scope::registerOperation(OperationContext* txn) {
- stdx::lock_guard<stdx::mutex> giLock(_engine->_globalInterruptLock);
- invariant(_opId == 0);
- _opId = txn->getOpID();
- _engine->_opToScopeMap[_opId] = this;
- LOG(2) << "V8Scope " << static_cast<const void*>(this) << " registered for op " << _opId;
- Status status = txn->checkForInterruptNoAssert();
- if (!status.isOK()) {
- kill();
- }
-}
-
-void V8Scope::unregisterOperation() {
- stdx::lock_guard<stdx::mutex> giLock(_engine->_globalInterruptLock);
- LOG(2) << "V8Scope " << static_cast<const void*>(this) << " unregistered for op " << _opId
- << endl;
- if (_opId != 0) {
- // scope is currently associated with an operation id
- V8ScriptEngine::OpIdToScopeMap::iterator it = _engine->_opToScopeMap.find(_opId);
- if (it != _engine->_opToScopeMap.end())
- _engine->_opToScopeMap.erase(it);
- _opId = 0;
- }
-}
-
-bool V8Scope::nativePrologue() {
- v8::Locker l(_isolate);
- stdx::lock_guard<stdx::mutex> cbEnterLock(_interruptLock);
- if (v8::V8::IsExecutionTerminating(_isolate)) {
- LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate)
- << endl;
- return false;
- }
- if (isKillPending()) {
- // kill flag was set before entering our callback
- LOG(2) << "marked for death while leaving callback. isolate: "
- << static_cast<const void*>(_isolate) << endl;
- v8::V8::TerminateExecution(_isolate);
- return false;
- }
- _inNativeExecution = true;
- return true;
-}
-
-bool V8Scope::nativeEpilogue() {
- v8::Locker l(_isolate);
- stdx::lock_guard<stdx::mutex> cbLeaveLock(_interruptLock);
- _inNativeExecution = false;
- if (v8::V8::IsExecutionTerminating(_isolate)) {
- LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate)
- << endl;
- return false;
- }
- if (isKillPending()) {
- LOG(2) << "marked for death while leaving callback. isolate: "
- << static_cast<const void*>(_isolate) << endl;
- v8::V8::TerminateExecution(_isolate);
- return false;
- }
- return true;
-}
-
-void V8Scope::kill() {
- stdx::lock_guard<stdx::mutex> interruptLock(_interruptLock);
- if (!_inNativeExecution) {
- // 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: " << static_cast<const void*>(_isolate) << endl;
- }
- LOG(1) << "marking v8 scope for death. isolate: " << static_cast<const void*>(_isolate)
- << endl;
- _pendingKill = true;
-}
-
-/** check if there is a pending killOp request */
-bool V8Scope::isKillPending() const {
- return _pendingKill;
-}
-
-OperationContext* V8Scope::getOpContext() const {
- return _opCtx;
-}
-
-/**
- * Display a list of all known ops (for verbose output)
- */
-std::string V8ScriptEngine::printKnownOps_inlock() {
- stringstream out;
- if (shouldLog(logger::LogSeverity::Debug(2))) {
- out << " known ops: " << endl;
- for (OpIdToScopeMap::iterator iSc = _opToScopeMap.begin(); iSc != _opToScopeMap.end();
- ++iSc) {
- out << " " << iSc->first << endl;
- }
- }
- return out.str();
-}
-
-
-V8Scope::V8Scope(V8ScriptEngine* engine)
- : _engine(engine),
- _connectState(NOT),
- _cpuProfiler(),
- _interruptLock("ScopeInterruptLock"),
- _inNativeExecution(true),
- _pendingKill(false),
- _opId(0),
- _opCtx(NULL) {
- // create new isolate and enter it via a scope
- _isolate.set(v8::Isolate::New());
- v8::Isolate::Scope iscope(_isolate);
-
- // lock the isolate and enter the context
- v8::Locker l(_isolate);
- v8::HandleScope handle_scope(_isolate);
- v8::Local<v8::Context> context(v8::Context::New(_isolate));
- _context.Set(_isolate, context);
- v8::Context::Scope context_scope(context);
-
- invariant(_isolate->GetNumberOfDataSlots() >= 1U);
- uint32_t slot = 0;
- _isolate->SetData(slot, this);
-
- // display heap statistics on MarkAndSweep GC run
- v8::V8::AddGCPrologueCallback(gcCallback<GCPrologueState>, v8::kGCTypeMarkSweepCompact);
- v8::V8::AddGCEpilogueCallback(gcCallback<GCEpilogueState>, v8::kGCTypeMarkSweepCompact);
-
- // create a global (rooted) object
- _global.Set(_isolate, context->Global());
-
- // Grab the RegExp constructor before user code gets a chance to change it. This ensures
- // we can always construct proper RegExps from C++.
- v8::Local<v8::Value> regexp = getGlobal()->Get(strLitToV8("RegExp"));
- verify(regexp->IsFunction());
- _jsRegExpConstructor.Set(_isolate, regexp.As<v8::Function>());
-
- // initialize lazy object template
- _LazyBsonFT.Set(_isolate, v8::FunctionTemplate::New(_isolate));
- LazyBsonFT()->InstanceTemplate()->SetInternalFieldCount(2);
- LazyBsonFT()->InstanceTemplate()->SetNamedPropertyHandler(
- namedGet, namedSet, NULL, namedDelete, namedEnumerator);
- LazyBsonFT()->InstanceTemplate()->SetIndexedPropertyHandler(
- indexedGet, indexedSet, NULL, indexedDelete, namedEnumerator);
- LazyBsonFT()->PrototypeTemplate()->Set(
- strLitToV8("_bson"), v8::Boolean::New(_isolate, true), v8::DontEnum);
-
- _ROBsonFT.Set(_isolate, v8::FunctionTemplate::New(_isolate));
- ROBsonFT()->Inherit(LazyBsonFT()); // This makes LazyBsonFT()->HasInstance() true
- ROBsonFT()->InstanceTemplate()->SetInternalFieldCount(2);
- ROBsonFT()->InstanceTemplate()->SetNamedPropertyHandler(
- namedGetRO, NamedReadOnlySet, NULL, NamedReadOnlyDelete, namedEnumerator);
- ROBsonFT()->InstanceTemplate()->SetIndexedPropertyHandler(
- indexedGetRO, IndexedReadOnlySet, NULL, IndexedReadOnlyDelete, NULL);
- ROBsonFT()->PrototypeTemplate()->Set(
- strLitToV8("_bson"), v8::Boolean::New(_isolate, true), v8::DontEnum);
-
- injectV8Function("print", Print);
- injectV8Function("version", Version); // TODO: remove
- injectV8Function("gc", GCV8);
- // injectV8Function("startCpuProfiler", startCpuProfiler);
- // injectV8Function("stopCpuProfiler", stopCpuProfiler);
- // injectV8Function("getCpuProfile", getCpuProfile);
-
- // install BSON functions in the global object
- installBSONTypes();
-
- // load JS helpers (dependancy: installBSONTypes)
- execSetup(JSFiles::assert);
- execSetup(JSFiles::types);
-
- // install process-specific utilities in the global scope (dependancy: types.js, assert.js)
- if (_engine->_scopeInitCallback)
- _engine->_scopeInitCallback(*this);
-
- // install global utility functions
- installGlobalUtils(*this);
-}
-
-V8Scope::~V8Scope() {
- unregisterOperation();
-}
-
-bool V8Scope::hasOutOfMemoryException() {
- V8_SIMPLE_HEADER
- if (!_context.IsEmpty()) {
- return getContext()->HasOutOfMemoryException();
- }
- return false;
-}
-
-v8::Local<v8::Value> V8Scope::load(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Context::Scope context_scope(scope->getContext());
- for (int i = 0; i < args.Length(); ++i) {
- std::string filename(toSTLString(args[i]));
- if (!scope->execFile(filename, false, true)) {
- return v8AssertionException(string("error loading js file: ") + filename);
- }
- }
- return v8::True(scope->getIsolate());
-}
-
-v8::Local<v8::Value> V8Scope::nativeCallback(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- BSONObj ret;
- string exceptionText;
- v8::EscapableHandleScope handle_scope(args.GetIsolate());
- try {
- 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 =
- 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]);
- BSONObj nativeArgs = b.obj();
- ret = function(nativeArgs, data->Value());
- } catch (const std::exception& e) {
- exceptionText = e.what();
- } catch (...) {
- exceptionText = "unknown exception in V8Scope::nativeCallback";
- }
- if (!exceptionText.empty()) {
- return v8AssertionException(exceptionText);
- }
- v8::Local<v8::Value> result_handle(scope->mongoToV8Element(ret.firstElement()));
- return handle_scope.Escape(result_handle);
-}
-
-void V8Scope::v8Callback(const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = args.GetReturnValue();
- result.Set(val);
-
- v8::HandleScope handle_scope(args.GetIsolate());
- V8Scope* scope = getScope(args.GetIsolate());
-
- if (!scope->nativePrologue()) {
- // execution terminated
- result.Set(v8::Undefined(args.GetIsolate()));
- return;
- }
-
- v8::Local<v8::External> f = v8::Local<v8::External>::Cast(args.Data());
- v8Function function = (v8Function)(f->Value());
- v8::Local<v8::Value> ret;
- string exceptionText;
-
- try {
- // execute the native function
- ret = function(scope, args);
- } catch (const std::exception& e) {
- exceptionText = e.what();
- } catch (...) {
- exceptionText = "unknown exception in V8Scope::v8Callback";
- }
-
- if (!scope->nativeEpilogue()) {
- // execution terminated
- result.Set(v8::Undefined(args.GetIsolate()));
- return;
- }
-
- if (!exceptionText.empty()) {
- result.Set(v8AssertionException(exceptionText));
- return;
- }
- result.Set(ret);
-}
-
-void V8Scope::init(const BSONObj* data) {
- 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
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(v8StringData(field), v8::Number::New(_isolate, val));
-}
-
-void V8Scope::setString(const char* field, StringData val) {
- V8_SIMPLE_HEADER
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(v8StringData(field), v8StringData(val));
-}
-
-void V8Scope::setBoolean(const char* field, bool val) {
- V8_SIMPLE_HEADER
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(v8StringData(field), v8::Boolean::New(_isolate, val));
-}
-
-void V8Scope::setElement(const char* field, const BSONElement& e) {
- V8_SIMPLE_HEADER
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(v8StringData(field), mongoToV8Element(e));
-}
-
-void V8Scope::setObject(const char* field, const BSONObj& obj, bool readOnly) {
- V8_SIMPLE_HEADER
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(v8StringData(field), mongoToLZV8(obj, readOnly ? v8::ReadOnly : v8::None));
-}
-
-int V8Scope::type(const char* field) {
- V8_SIMPLE_HEADER
- v8::Local<v8::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;
-
- uasserted(12509, str::stream() << "unable to get type of field " << field);
-}
-
-v8::Local<v8::Value> V8Scope::get(const char* field) {
- return getGlobal()->Get(v8StringData(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
- v8::Local<v8::Value> v = get(field);
- if (v->IsNull() || v->IsUndefined())
- return BSONObj();
- uassert(10231, "not an object", v->IsObject());
- return v8ToMongo(v->ToObject());
-}
-
-v8::Local<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) {
- v8::Local<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit);
- v8::Local<v8::ObjectTemplate> proto = numberLong->PrototypeTemplate();
- scope->injectV8Method("valueOf", numberLongValueOf, proto);
- scope->injectV8Method("toNumber", numberLongToNumber, proto);
- scope->injectV8Method("toString", numberLongToString, proto);
- return numberLong;
-}
-
-v8::Local<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) {
- v8::Local<v8::FunctionTemplate> numberInt = scope->createV8Function(numberIntInit);
- v8::Local<v8::ObjectTemplate> proto = numberInt->PrototypeTemplate();
- scope->injectV8Method("valueOf", numberIntValueOf, proto);
- scope->injectV8Method("toNumber", numberIntToNumber, proto);
- scope->injectV8Method("toString", numberIntToString, proto);
- return numberInt;
-}
-
-v8::Local<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) {
- v8::Local<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit);
- binData->InstanceTemplate()->SetInternalFieldCount(1);
- v8::Local<v8::ObjectTemplate> proto = binData->PrototypeTemplate();
- scope->injectV8Method("toString", binDataToString, proto);
- scope->injectV8Method("base64", binDataToBase64, proto);
- scope->injectV8Method("hex", binDataToHex, proto);
- return binData;
-}
-
-v8::Local<v8::FunctionTemplate> getTimestampFunctionTemplate(V8Scope* scope) {
- v8::Local<v8::FunctionTemplate> ts = scope->createV8Function(dbTimestampInit);
- return ts;
-}
-
-v8::Local<v8::Value> minKeyToJson(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- // MinKey can't just be an object like {$minKey:1} since insert() checks for fields that
- // start with $ and raises an error. See DBCollection.prototype._validateForStorage().
- return scope->strLitToV8("{ \"$minKey\" : 1 }");
-}
-
-void minKeyCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = args.GetReturnValue();
- result.Set(val);
- // The idea here is that MinKey and MaxKey are singleton callable objects
- // that return the singleton when called. This enables all instances to
- // compare == and === to MinKey even if created by "new MinKey()" in JS.
- V8Scope* scope = getScope(args.GetIsolate());
-
- v8::Local<v8::Function> func = scope->MinKeyFT()->GetFunction();
- v8::Local<v8::String> name = scope->strLitToV8("singleton");
- v8::Local<v8::Value> singleton = func->GetHiddenValue(name);
- if (!singleton.IsEmpty()) {
- result.Set(singleton);
- return;
- }
-
- if (!args.IsConstructCall()) {
- result.Set(func->NewInstance());
- return;
- }
-
- verify(scope->MinKeyFT()->HasInstance(args.This()));
-
- func->SetHiddenValue(name, args.This());
- result.Set(v8::Undefined(args.GetIsolate()));
-}
-
-v8::Local<v8::FunctionTemplate> getMinKeyFunctionTemplate(V8Scope* scope) {
- v8::Local<v8::FunctionTemplate> myTemplate =
- v8::FunctionTemplate::New(scope->getIsolate(), minKeyCall);
- myTemplate->InstanceTemplate()->SetCallAsFunctionHandler(minKeyCall);
- myTemplate->PrototypeTemplate()->Set(scope->v8StringData("tojson"),
- scope->createV8Function(minKeyToJson)->GetFunction());
- myTemplate->SetClassName(scope->strLitToV8("MinKey"));
- return myTemplate;
-}
-
-v8::Local<v8::Value> maxKeyToJson(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- return scope->strLitToV8("{ \"$maxKey\" : 1 }");
-}
-
-void maxKeyCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = args.GetReturnValue();
- result.Set(val);
- // See comment in minKeyCall.
- V8Scope* scope = getScope(args.GetIsolate());
-
- v8::Local<v8::Function> func = scope->MaxKeyFT()->GetFunction();
- v8::Local<v8::String> name = scope->strLitToV8("singleton");
- v8::Local<v8::Value> singleton = func->GetHiddenValue(name);
- if (!singleton.IsEmpty()) {
- result.Set(singleton);
- return;
- }
-
- if (!args.IsConstructCall()) {
- result.Set(func->NewInstance());
- return;
- }
-
- verify(scope->MaxKeyFT()->HasInstance(args.This()));
-
- func->SetHiddenValue(name, args.This());
- result.Set(v8::Undefined(args.GetIsolate()));
-}
-
-v8::Local<v8::FunctionTemplate> getMaxKeyFunctionTemplate(V8Scope* scope) {
- v8::Local<v8::FunctionTemplate> myTemplate =
- v8::FunctionTemplate::New(scope->getIsolate(), maxKeyCall);
- myTemplate->InstanceTemplate()->SetCallAsFunctionHandler(maxKeyCall);
- myTemplate->PrototypeTemplate()->Set(scope->v8StringData("tojson"),
- scope->createV8Function(maxKeyToJson)->GetFunction());
- myTemplate->SetClassName(scope->strLitToV8("MaxKey"));
- return myTemplate;
-}
-
-std::string V8Scope::v8ExceptionToSTLString(const v8::TryCatch* try_catch) {
- stringstream ss;
- v8::Local<v8::Value> stackTrace = try_catch->StackTrace();
- if (!stackTrace.IsEmpty()) {
- ss << StringData(V8String(stackTrace));
- } else {
- ss << StringData(V8String((try_catch->Exception())));
- }
-
- // get the exception message
- v8::Local<v8::Message> message = try_catch->Message();
- if (message.IsEmpty())
- return ss.str();
-
- // get the resource (e.g. file or internal call)
- v8::String::Utf8Value resourceName(message->GetScriptResourceName());
- if (!*resourceName)
- return ss.str();
-
- string resourceNameString = *resourceName;
- if (resourceNameString.compare("undefined") == 0)
- return ss.str();
- if (resourceNameString.find("_funcs") == 0) {
- // script loaded from __createFunction
- string code;
- // find the source script based on the resource name supplied to v8::Script::Compile().
- // this is accomplished by converting the integer after the '_funcs' prefix.
- unsigned int funcNum = str::toUnsigned(resourceNameString.substr(6));
- for (map<string, ScriptingFunction>::iterator it = getFunctionCache().begin();
- it != getFunctionCache().end();
- ++it) {
- if (it->second == funcNum) {
- code = it->first;
- break;
- }
- }
- if (!code.empty()) {
- // append surrounding code (padded with up to 20 characters on each side)
- int startPos = message->GetStartPosition();
- const int kPadding = 20;
- if (startPos - kPadding < 0)
- // lower bound exceeded
- startPos = 0;
- else
- startPos -= kPadding;
-
- int displayRange = message->GetEndPosition();
- if (displayRange + kPadding > static_cast<int>(code.length()))
- // upper bound exceeded
- displayRange -= startPos;
- else
- // compensate for startPos padding
- displayRange = (displayRange - startPos) + kPadding;
-
- if (startPos > static_cast<int>(code.length()) ||
- displayRange > static_cast<int>(code.length()))
- return ss.str();
-
- string codeNear = code.substr(startPos, displayRange);
- for (size_t newLine = codeNear.find('\n'); newLine != string::npos;
- newLine = codeNear.find('\n')) {
- if (static_cast<int>(newLine) > displayRange - kPadding) {
- // truncate at first newline past the reported end position
- codeNear = codeNear.substr(0, newLine - 1);
- break;
- }
- // convert newlines to spaces
- codeNear.replace(newLine, 1, " ");
- }
- // trim leading chars
- codeNear = str::ltrim(codeNear);
- ss << " near '" << codeNear << "' ";
- const int linenum = message->GetLineNumber();
- if (linenum != 1)
- ss << " (line " << linenum << ")";
- }
- } else if (resourceNameString.find("(shell") == 0) {
- // script loaded from shell input -- simply print the error
- } else {
- // script loaded from file
- ss << " at " << *resourceName;
- const int linenum = message->GetLineNumber();
- if (linenum != 1)
- ss << ":" << linenum;
- }
- return ss.str();
-}
-
-// --- functions -----
-
-bool hasFunctionIdentifier(StringData code) {
- if (code.size() < 9 || code.find("function") != 0)
- return false;
-
- return code[8] == ' ' || code[8] == '(';
-}
-
-v8::Local<v8::Function> V8Scope::__createFunction(const char* raw,
- ScriptingFunction functionNumber) {
- v8::EscapableHandleScope handle_scope(_isolate);
- v8::TryCatch try_catch;
- 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 + "}";
- }
-
- string fn = str::stream() << "_funcs" << functionNumber;
- code = str::stream() << fn << " = " << code;
-
- v8::Local<v8::Script> script = v8::Script::Compile(v8StringData(code), v8StringData(fn));
-
- // throw on error
- checkV8ErrorState(script, try_catch);
-
- v8::Local<v8::Value> result = script->Run();
-
- // throw on error
- checkV8ErrorState(result, try_catch);
-
- return handle_scope.Escape(v8::Local<v8::Function>::Cast(getGlobal()->Get(v8StringData(fn))));
-}
-
-ScriptingFunction V8Scope::_createFunction(const char* raw, ScriptingFunction functionNumber) {
- V8_SIMPLE_HEADER
- v8::Local<v8::Value> ret = __createFunction(raw, functionNumber);
- v8::Eternal<v8::Value> f(_isolate, ret);
- uassert(10232, "not a function", ret->IsFunction());
- _funcs.push_back(f);
- return functionNumber;
-}
-
-void V8Scope::setFunction(const char* field, const char* code) {
- V8_SIMPLE_HEADER
- getGlobal()->ForceSet(v8StringData(field),
- __createFunction(code, getFunctionCache().size() + 1));
-}
-
-void V8Scope::rename(const char* from, const char* to) {
- V8_SIMPLE_HEADER;
- v8::Local<v8::String> f = v8StringData(from);
- v8::Local<v8::String> t = v8StringData(to);
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(t, global->Get(f));
- global->ForceSet(f, v8::Undefined(_isolate));
-}
-
-int V8Scope::invoke(ScriptingFunction func,
- const BSONObj* argsObject,
- const BSONObj* recv,
- int timeoutMs,
- bool ignoreReturn,
- bool readOnlyArgs,
- bool readOnlyRecv) {
- V8_SIMPLE_HEADER
- v8::Local<v8::Value> funcValue = _funcs[func - 1].Get(_isolate);
- v8::TryCatch try_catch;
- v8::Local<v8::Value> result;
-
- // TODO SERVER-8016: properly allocate handles on the stack
- static const int MAX_ARGS = 24;
- const int nargs = argsObject ? argsObject->nFields() : 0;
- uassert(16862, "Too many arguments. Max is 24", nargs <= MAX_ARGS);
-
- v8::Local<v8::Value> args[MAX_ARGS];
- if (nargs) {
- BSONObjIterator it(*argsObject);
- for (int i = 0; i < nargs; i++) {
- BSONElement next = it.next();
- args[i] = mongoToV8Element(next, readOnlyArgs);
- }
- }
-
- v8::Local<v8::Object> v8recv;
- if (recv != 0)
- v8recv = mongoToLZV8(*recv, readOnlyRecv);
- else
- v8recv = getGlobal();
-
- if (!nativeEpilogue()) {
- _error = "JavaScript execution terminated";
- error() << _error << endl;
- uasserted(16711, _error);
- }
-
- if (timeoutMs)
- // start the deadline timer for this script
- _engine->getDeadlineMonitor()->startDeadline(this, timeoutMs);
-
- result = ((v8::Function*)(*funcValue))->Call(v8recv, nargs, nargs ? args : NULL);
-
- if (timeoutMs)
- // stop the deadline timer for this script
- _engine->getDeadlineMonitor()->stopDeadline(this);
-
- if (!nativePrologue()) {
- _error = "JavaScript execution terminated";
- error() << _error << endl;
- uasserted(16712, _error);
- }
-
- // throw on error
- checkV8ErrorState(result, try_catch);
-
- if (!ignoreReturn) {
- v8::Local<v8::Object> resultObject = result->ToObject();
- // must validate the handle because TerminateExecution may have
- // been thrown after the above checks
- if (!resultObject.IsEmpty() && resultObject->Has(strLitToV8("_v8_function"))) {
- log() << "storing native function as return value" << endl;
- _lastRetIsNativeCode = true;
- } else {
- _lastRetIsNativeCode = false;
- }
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(strLitToV8("__returnValue"), result);
- }
-
- return 0;
-}
-
-bool V8Scope::exec(StringData code,
- const string& name,
- bool printResult,
- bool reportError,
- bool assertOnError,
- int timeoutMs) {
- V8_SIMPLE_HEADER
- v8::TryCatch try_catch;
-
- v8::Local<v8::Script> script = v8::Script::Compile(v8StringData(code), v8StringData(name));
-
- if (checkV8ErrorState(script, try_catch, reportError, assertOnError))
- return false;
-
- if (!nativeEpilogue()) {
- _error = "JavaScript execution terminated";
- if (reportError)
- error() << _error << endl;
- if (assertOnError)
- uasserted(13475, _error);
- return false;
- }
-
- if (timeoutMs)
- // start the deadline timer for this script
- _engine->getDeadlineMonitor()->startDeadline(this, timeoutMs);
-
- v8::Local<v8::Value> result = script->Run();
-
- if (timeoutMs)
- // stopt the deadline timer for this script
- _engine->getDeadlineMonitor()->stopDeadline(this);
-
- if (!nativePrologue()) {
- _error = "JavaScript execution terminated";
- if (reportError)
- error() << _error << endl;
- if (assertOnError)
- uasserted(16721, _error);
- return false;
- }
-
- if (checkV8ErrorState(result, try_catch, reportError, assertOnError))
- return false;
-
- v8::Local<v8::Object> global = getGlobal();
- global->ForceSet(strLitToV8("__lastres__"), result);
-
- if (printResult && !result->IsUndefined()) {
- // appears to only be used by shell
- cout << V8String(result) << endl;
- }
-
- return true;
-}
-
-void V8Scope::injectNative(const char* field, NativeFunction func, void* data) {
- V8_SIMPLE_HEADER // required due to public access
- v8::Local<v8::Object> global = getGlobal();
- injectNative(field, func, global, data);
-}
-
-void V8Scope::injectNative(const char* field,
- NativeFunction nativeFunc,
- v8::Local<v8::Object>& obj,
- void* data) {
- v8::Local<v8::FunctionTemplate> ft = createV8Function(nativeCallback);
- injectV8Function(field, ft, obj);
- v8::Local<v8::Function> func = ft->GetFunction();
- func->SetHiddenValue(strLitToV8("_native_function"),
- v8::External::New(_isolate, (void*)nativeFunc));
- func->SetHiddenValue(strLitToV8("_native_data"), v8::External::New(_isolate, data));
-}
-
-v8::Local<v8::FunctionTemplate> V8Scope::injectV8Function(const char* field, v8Function func) {
- v8::Local<v8::Object> global = getGlobal();
- return injectV8Function(field, func, global);
-}
-
-v8::Local<v8::FunctionTemplate> V8Scope::injectV8Function(const char* field,
- v8Function func,
- v8::Local<v8::Object> obj) {
- return injectV8Function(field, createV8Function(func), obj);
-}
-
-v8::Local<v8::FunctionTemplate> V8Scope::injectV8Function(const char* fieldCStr,
- v8::Local<v8::FunctionTemplate> ft,
- v8::Local<v8::Object> obj) {
- v8::Local<v8::String> field = v8StringData(fieldCStr);
- ft->SetClassName(field);
- v8::Local<v8::Function> func = ft->GetFunction();
- func->SetName(field);
- obj->ForceSet(field, func);
- return ft;
-}
-
-v8::Local<v8::FunctionTemplate> V8Scope::injectV8Method(const char* fieldCStr,
- v8Function func,
- v8::Local<v8::ObjectTemplate>& proto) {
- v8::Local<v8::String> field = v8StringData(fieldCStr);
- v8::Local<v8::FunctionTemplate> ft = createV8Function(func);
- v8::Local<v8::Function> f = ft->GetFunction();
- f->SetName(field);
- proto->Set(field, f);
- return ft;
-}
-
-v8::Local<v8::FunctionTemplate> V8Scope::createV8Function(v8Function func) {
- v8::Local<v8::Value> funcHandle = v8::External::New(_isolate, reinterpret_cast<void*>(func));
- v8::Local<v8::FunctionTemplate> ft =
- v8::FunctionTemplate::New(_isolate, v8Callback, funcHandle);
- ft->Set(strLitToV8("_v8_function"),
- v8::Boolean::New(_isolate, true),
- static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::ReadOnly));
- return ft;
-}
-
-void V8Scope::gc() {
- V8_SIMPLE_HEADER
- // trigger low memory notification. for more granular control over garbage
- // collection cycle, @see v8::V8::IdleNotification.
- v8::V8::LowMemoryNotification();
-}
-
-void V8Scope::localConnectForDbEval(OperationContext* txn, const char* dbName) {
- {
- V8_SIMPLE_HEADER;
-
- invariant(_opCtx == NULL);
- _opCtx = txn;
-
- if (_connectState == EXTERNAL)
- uasserted(12510, "externalSetup already called, can't call localConnect");
- if (_connectState == LOCAL) {
- if (_localDBName == dbName)
- return;
- uasserted(12511,
- str::stream() << "localConnect previously called with name " << _localDBName);
- }
-
- // NOTE: order is important here. the following methods must be called after
- // the above conditional statements.
-
- // install db access functions in the global object
- installDBAccess();
-
- // install the Mongo function object and instantiate the 'db' global
- _MongoFT.Set(_isolate, getMongoFunctionTemplate(this, true));
- v8::Local<v8::Object> global = getGlobal();
- injectV8Function("Mongo", MongoFT(), global);
- 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(txn);
-}
-
-void V8Scope::externalSetup() {
- V8_SIMPLE_HEADER
- if (_connectState == EXTERNAL)
- return;
- if (_connectState == LOCAL)
- uasserted(12512, "localConnect already called, can't call externalSetup");
-
- // install db access functions in the global object
- installDBAccess();
-
- // install thread-related functions (e.g. _threadInject)
- installFork(this, getGlobal(), getContext());
-
- // install 'load' helper function
- injectV8Function("load", load);
-
- // install the Mongo function object
- _MongoFT.Set(_isolate, getMongoFunctionTemplate(this, false));
- injectV8Function("Mongo", MongoFT(), getGlobal());
- execCoreFiles();
- _connectState = EXTERNAL;
-}
-
-void V8Scope::installDBAccess() {
- _DBFT.Set(_isolate, createV8Function(dbInit));
- _DBQueryFT.Set(_isolate, createV8Function(dbQueryInit));
- _DBCollectionFT.Set(_isolate, createV8Function(collectionInit));
-
- // These must be done before calling injectV8Function
- DBFT()->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, collectionSetter);
- DBQueryFT()->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess);
- DBCollectionFT()->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter,
- collectionSetter);
-
- v8::Local<v8::Object> global = getGlobal();
- injectV8Function("DB", DBFT(), global);
- injectV8Function("DBQuery", DBQueryFT(), global);
- injectV8Function("DBCollection", DBCollectionFT(), global);
-
- // The internal cursor type isn't exposed to the users at all
- _InternalCursorFT.Set(_isolate, getInternalCursorFunctionTemplate(this));
-}
-
-void V8Scope::installBSONTypes() {
- _ObjectIdFT.Set(_isolate, injectV8Function("ObjectId", objectIdInit));
- _DBRefFT.Set(_isolate, injectV8Function("DBRef", dbRefInit));
- _DBPointerFT.Set(_isolate, injectV8Function("DBPointer", dbPointerInit));
-
- _BinDataFT.Set(_isolate, getBinDataFunctionTemplate(this));
- _NumberLongFT.Set(_isolate, getNumberLongFunctionTemplate(this));
- _NumberIntFT.Set(_isolate, getNumberIntFunctionTemplate(this));
- _TimestampFT.Set(_isolate, getTimestampFunctionTemplate(this));
- _MinKeyFT.Set(_isolate, getMinKeyFunctionTemplate(this));
- _MaxKeyFT.Set(_isolate, getMaxKeyFunctionTemplate(this));
-
- v8::Local<v8::Object> global = getGlobal();
- injectV8Function("BinData", BinDataFT(), global);
- injectV8Function("NumberLong", NumberLongFT(), global);
- injectV8Function("NumberInt", NumberIntFT(), global);
- injectV8Function("Timestamp", TimestampFT(), global);
-
- // These are instances created from the functions, not the functions themselves
- global->ForceSet(strLitToV8("MinKey"), MinKeyFT()->GetFunction()->NewInstance());
- global->ForceSet(strLitToV8("MaxKey"), MaxKeyFT()->GetFunction()->NewInstance());
-
- // These all create BinData objects so we don't need to hold on to them.
- injectV8Function("UUID", uuidInit);
- 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());
-}
-
-
-// ----- internal -----
-
-void V8Scope::reset() {
- V8_SIMPLE_HEADER
- unregisterOperation();
- _error = "";
- _pendingKill = false;
- _inNativeExecution = true;
-}
-
-v8::Local<v8::Value> V8Scope::newFunction(StringData code) {
- v8::EscapableHandleScope handle_scope(_isolate);
- v8::TryCatch try_catch;
- string codeStr = str::stream() << "____MongoToV8_newFunction_temp = " << code;
-
- v8::Local<v8::Script> compiled = v8::Script::Compile(v8StringData(codeStr));
-
- // throw on compile error
- checkV8ErrorState(compiled, try_catch);
-
- v8::Local<v8::Value> ret = compiled->Run();
-
- // throw on run/assignment error
- checkV8ErrorState(ret, try_catch);
-
- return handle_scope.Escape(ret);
-}
-
-v8::Local<v8::Value> V8Scope::newId(const OID& id) {
- v8::EscapableHandleScope handle_scope(_isolate);
- v8::Local<v8::Function> idCons = ObjectIdFT()->GetFunction();
- v8::Local<v8::Value> argv[1];
- const string& idString = id.toString();
- argv[0] = v8StringData(idString);
- return handle_scope.Escape(idCons->NewInstance(1, argv));
-}
-
-/**
- * converts a BSONObj to a Lazy V8 object
- */
-v8::Local<v8::Object> V8Scope::mongoToLZV8(const BSONObj& m, bool readOnly) {
- if (m.firstElementType() == String && str::equals(m.firstElementFieldName(), "$ref")) {
- BSONObjIterator it(m);
- const BSONElement ref = it.next();
- const BSONElement id = it.next();
- if (id.ok() && str::equals(id.fieldName(), "$id")) {
- v8::Local<v8::Value> args[] = {mongoToV8Element(ref, readOnly),
- mongoToV8Element(id, readOnly)};
- v8::Local<v8::Object> dbRef = DBRefFT()->GetFunction()->NewInstance(2, args);
- while (it.more()) {
- BSONElement elem = it.next();
- dbRef->Set(v8StringData(elem.fieldName()), mongoToV8Element(elem, readOnly));
- }
- return dbRef;
- }
- }
-
- v8::Local<v8::FunctionTemplate> templ = readOnly ? ROBsonFT() : LazyBsonFT();
- v8::Local<v8::Object> o = templ->GetFunction()->NewInstance();
- massert(16496,
- str::stream() << "V8: NULL Object template instantiated. "
- << (v8::V8::IsExecutionTerminating() ? "v8 execution is terminating."
- : "v8 still executing."),
- *o != NULL);
-
- wrapBSONObject(o, m, readOnly);
- return o;
-}
-
-v8::Local<v8::Value> V8Scope::mongoToV8Element(const BSONElement& elem, bool readOnly) {
- v8::Local<v8::Value> argv[3]; // arguments for v8 instance constructors
- v8::Local<v8::Object> instance; // instance of v8 type
- uint64_t nativeUnsignedLong; // native representation of NumberLong
-
- switch (elem.type()) {
- case mongo::Code:
- return newFunction(elem.valueStringData());
- case CodeWScope:
- if (!elem.codeWScopeObject().isEmpty())
- warning() << "CodeWScope doesn't transfer to db.eval" << endl;
- return newFunction(StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1));
- case mongo::Symbol:
- case mongo::String: {
- return v8StringData(elem.valueStringData());
- }
- case mongo::jstOID:
- return newId(elem.__oid());
- case mongo::NumberDouble:
- case mongo::NumberInt:
- return v8::Number::New(_isolate, elem.number());
- case mongo::Array: {
- // NB: This comment may no longer be accurate.
- // 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
-
- // It is faster to allow the v8::Array to grow than call nFields() on the array
- v8::Local<v8::Array> array = v8::Array::New(_isolate);
- int i = 0;
- BSONForEach(subElem, elem.embeddedObject()) {
- array->Set(i++, mongoToV8Element(subElem, readOnly));
- }
- return array;
- }
- case mongo::Object:
- return mongoToLZV8(elem.embeddedObject(), readOnly);
- case mongo::Date:
- return v8::Date::New(_isolate, static_cast<double>(elem.date.toMillisSinceEpoch()));
- case mongo::Bool:
- return v8::Boolean::New(_isolate, elem.boolean());
- case mongo::EOO:
- case mongo::jstNULL:
- case mongo::Undefined: // duplicate sm behavior
- return v8::Null(_isolate);
- case mongo::RegEx: {
- // TODO parse into a custom type that can support any patterns and flags SERVER-9803
- v8::TryCatch tryCatch;
-
- v8::Local<v8::Value> args[] = {v8StringData(elem.regex()),
- v8StringData(elem.regexFlags())};
-
- v8::Local<v8::Value> ret = _jsRegExpConstructor.Get(_isolate)->NewInstance(2, args);
- uassert(16863,
- str::stream() << "Error converting " << elem.toString(false) << " in field "
- << elem.fieldName() << " to a JS RegExp object: "
- << toSTLString(tryCatch.Exception()),
- !tryCatch.HasCaught());
-
- return ret;
- }
- case mongo::BinData: {
- int len;
- const char* data = elem.binData(len);
- stringstream ss;
- base64::encode(ss, data, len);
- argv[0] = v8::Number::New(_isolate, elem.binDataType());
- argv[1] = v8StringData(ss.str());
- return BinDataFT()->GetFunction()->NewInstance(2, argv);
- }
- case mongo::bsonTimestamp: {
- v8::TryCatch tryCatch;
-
- argv[0] = v8::Number::New(_isolate, elem.timestampTime() / 1000);
- argv[1] = v8::Number::New(_isolate, elem.timestampInc());
-
- v8::Local<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
- if ((long long)nativeUnsignedLong ==
- (long long)(double)(long long)(nativeUnsignedLong) &&
- nativeUnsignedLong < 9007199254740992ULL) {
- argv[0] = v8::Number::New(_isolate, (double)(long long)(nativeUnsignedLong));
- return NumberLongFT()->GetFunction()->NewInstance(1, argv);
- } else {
- argv[0] = v8::Number::New(_isolate, (double)(long long)(nativeUnsignedLong));
- argv[1] = v8::Integer::New(_isolate, nativeUnsignedLong >> 32);
- argv[2] = v8::Integer::New(
- _isolate, (unsigned long)(nativeUnsignedLong & 0x00000000ffffffff));
- return NumberLongFT()->GetFunction()->NewInstance(3, argv);
- }
- case mongo::MinKey:
- return MinKeyFT()->GetFunction()->NewInstance();
- case mongo::MaxKey:
- return MaxKeyFT()->GetFunction()->NewInstance();
- case mongo::DBRef:
- argv[0] = v8StringData(elem.dbrefNS());
- argv[1] = newId(elem.dbrefOID());
- return DBPointerFT()->GetFunction()->NewInstance(2, argv);
- default:
- massert(16661,
- str::stream() << "can't handle type: " << elem.type() << " " << elem.toString(),
- false);
- break;
- }
- return v8::Undefined(_isolate);
-}
-
-void V8Scope::v8ToMongoNumber(BSONObjBuilder& b,
- StringData elementName,
- v8::Local<v8::Number> value,
- BSONObj* originalParent) {
- double val = value->Value();
- // if previous type was integer, keep it
- int intval = static_cast<int>(val);
- if (val == intval && originalParent) {
- // This makes copying an object of numbers O(n**2) :(
- BSONElement elmt = originalParent->getField(elementName);
- if (elmt.type() == mongo::NumberInt) {
- b.append(elementName, intval);
- return;
- }
- }
- b.append(elementName, val);
-}
-
-void V8Scope::v8ToMongoRegex(BSONObjBuilder& b,
- StringData elementName,
- v8::Local<v8::RegExp> v8Regex) {
- V8String v8RegexString(v8Regex);
- StringData regex = v8RegexString;
- regex = regex.substr(1);
- StringData r = regex.substr(0, regex.rfind('/'));
- StringData o = regex.substr(regex.rfind('/') + 1);
- b.appendRegex(elementName, r, o);
-}
-
-void V8Scope::v8ToMongoDBRef(BSONObjBuilder& b, StringData elementName, v8::Local<v8::Object> obj) {
- verify(DBPointerFT()->HasInstance(obj));
- v8::Local<v8::Value> theid = obj->Get(strLitToV8("id"));
- OID oid = v8ToMongoObjectID(theid->ToObject());
- string ns = toSTLString(obj->Get(strLitToV8("ns")));
- b.appendDBRef(elementName, ns, oid);
-}
-
-void V8Scope::v8ToMongoBinData(BSONObjBuilder& b,
- StringData elementName,
- v8::Local<v8::Object> obj) {
- verify(BinDataFT()->HasInstance(obj));
- verify(obj->InternalFieldCount() == 1);
- std::string binData(base64::decode(toSTLString(obj->GetInternalField(0))));
- b.appendBinData(elementName,
- binData.size(),
- mongo::BinDataType(obj->Get(strLitToV8("type"))->ToInt32()->Value()),
- binData.c_str());
-}
-
-OID V8Scope::v8ToMongoObjectID(v8::Local<v8::Object> obj) {
- verify(ObjectIdFT()->HasInstance(obj));
- const string hexStr = toSTLString(obj->Get(strLitToV8("str")));
-
- // OID parser doesn't have user-friendly error messages
- uassert(16864, "ObjectID.str must be exactly 24 chars long", hexStr.size() == 24);
- uassert(16865,
- "ObjectID.str must only have hex characters [0-1a-fA-F]",
- count_if(hexStr.begin(), hexStr.end(), ::isxdigit) == 24);
-
- return OID(hexStr);
-}
-
-void V8Scope::v8ToMongoObject(BSONObjBuilder& b,
- StringData elementName,
- v8::Local<v8::Value> value,
- int depth,
- BSONObj* originalParent) {
- verify(value->IsObject());
- v8::Local<v8::Object> obj = value.As<v8::Object>();
-
- if (value->IsRegExp()) {
- v8ToMongoRegex(b, elementName, obj.As<v8::RegExp>());
- } else if (ObjectIdFT()->HasInstance(value)) {
- b.append(elementName, v8ToMongoObjectID(obj));
- } else if (NumberLongFT()->HasInstance(value)) {
- b.append(elementName, numberLongVal(this, obj));
- } else if (NumberIntFT()->HasInstance(value)) {
- b.append(elementName, numberIntVal(this, obj));
- } else if (DBPointerFT()->HasInstance(value)) {
- v8ToMongoDBRef(b, elementName, obj);
- } else if (BinDataFT()->HasInstance(value)) {
- v8ToMongoBinData(b, elementName, obj);
- } else if (TimestampFT()->HasInstance(value)) {
- Timestamp ot(obj->Get(strLitToV8("t"))->Uint32Value(),
- obj->Get(strLitToV8("i"))->Uint32Value());
- b.append(elementName, ot);
- } else if (MinKeyFT()->HasInstance(value)) {
- b.appendMinKey(elementName);
- } else if (MaxKeyFT()->HasInstance(value)) {
- b.appendMaxKey(elementName);
- } else {
- // nested object or array
- BSONObj sub = v8ToMongo(obj, depth);
- b.append(elementName, sub);
- }
-}
-
-void V8Scope::v8ToMongoElement(BSONObjBuilder& b,
- StringData sname,
- v8::Local<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,
- str::stream() << "JavaScript property (name) contains a null char "
- << "which is not allowed in BSON. " << originalParent->jsonString(),
- (string::npos == sname.find('\0')));
-
- if (value->IsString()) {
- b.append(sname, V8String(value));
- return;
- }
- if (value->IsFunction()) {
- uassert(16716,
- "cannot convert native function to BSON",
- !value->ToObject()->Has(strLitToV8("_v8_function")));
- b.appendCode(sname, V8String(value));
- return;
- }
- if (value->IsNumber()) {
- v8ToMongoNumber(b, sname, value.As<v8::Number>(), originalParent);
- return;
- }
- if (value->IsArray()) {
- // Note: can't use BSONArrayBuilder because need to call recursively
- BSONObjBuilder arrBuilder(b.subarrayStart(sname));
- v8::Local<v8::Array> array = value.As<v8::Array>();
- const int len = array->Length();
- for (int i = 0; i < len; i++) {
- const string name = BSONObjBuilder::numStr(i);
- v8ToMongoElement(arrBuilder, name, array->Get(i), depth + 1, originalParent);
- }
- 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()) {
- v8ToMongoObject(b, sname, value, depth, originalParent);
- 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;
- }
- uasserted(16662,
- str::stream() << "unable to convert JavaScript property to mongo element " << sname);
-}
-
-BSONObj V8Scope::v8ToMongo(v8::Local<v8::Object> o, int depth) {
- BSONObj originalBSON;
- if (LazyBsonFT()->HasInstance(o)) {
- originalBSON = unwrapBSONObj(this, o);
- BSONHolder* holder = unwrapHolder(this, o);
- if (holder && !holder->_modified) {
- // object was not modified, use bson as is
- return originalBSON;
- }
- }
-
- BSONObjBuilder b;
-
- // We special case the _id field in top-level objects and move it to the front.
- // This matches other drivers behavior and makes finding the _id field quicker in BSON.
- if (depth == 0) {
- if (o->HasOwnProperty(strLitToV8("_id"))) {
- v8ToMongoElement(b, "_id", o->Get(strLitToV8("_id")), 0, &originalBSON);
- }
- }
-
- v8::Local<v8::Array> names = o->GetOwnPropertyNames();
- for (unsigned int i = 0; i < names->Length(); i++) {
- v8::Local<v8::String> name = names->Get(i)->ToString();
-
- if (depth == 0 && name->StrictEquals(strLitToV8("_id")))
- continue; // already handled above
-
- V8String sname(name);
- v8::Local<v8::Value> value = o->Get(name);
- v8ToMongoElement(b, sname, value, depth + 1, &originalBSON);
- }
-
- 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::Local<v8::Value> V8Scope::Print(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- LogstreamBuilder builder(jsPrintLogDomain, getThreadName(), logger::LogSeverity::Log());
- std::ostream& ss = builder.stream();
- v8::EscapableHandleScope handle_scope(args.GetIsolate());
- bool first = true;
- for (int i = 0; i < args.Length(); i++) {
- if (first)
- first = false;
- else
- ss << " ";
-
- if (args[i].IsEmpty()) {
- // failed to get object to convert
- ss << "[unknown type]";
- continue;
- }
- if (args[i]->IsExternal()) {
- // object is External
- ss << "[mongo internal]";
- continue;
- }
-
- v8::String::Utf8Value str(args[i]);
- ss << *str;
- }
- ss << "\n";
- return handle_scope.Escape(v8::Local<v8::Value>(v8::Undefined(scope->getIsolate())));
-}
-
-v8::Local<v8::Value> V8Scope::Version(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::EscapableHandleScope handle_scope(args.GetIsolate());
- return handle_scope.Escape(scope->v8StringData(v8::V8::GetVersion()));
-}
-
-v8::Local<v8::Value> V8Scope::GCV8(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- // trigger low memory notification. for more granular control over garbage
- // collection cycle, @see v8::V8::IdleNotification.
- v8::V8::LowMemoryNotification();
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> V8Scope::startCpuProfiler(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (args.Length() != 1 || !args[0]->IsString()) {
- return v8AssertionException("startCpuProfiler takes a string argument");
- }
- scope->_cpuProfiler.start(scope->_isolate, *v8::String::Utf8Value(args[0]->ToString()));
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> V8Scope::stopCpuProfiler(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (args.Length() != 1 || !args[0]->IsString()) {
- return v8AssertionException("stopCpuProfiler takes a string argument");
- }
- scope->_cpuProfiler.stop(scope->_isolate, *v8::String::Utf8Value(args[0]->ToString()));
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> V8Scope::getCpuProfile(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (args.Length() != 1 || !args[0]->IsString()) {
- return v8AssertionException("getCpuProfile takes a string argument");
- }
- return scope->mongoToLZV8(
- scope->_cpuProfiler.fetch(*v8::String::Utf8Value(args[0]->ToString())));
-}
-
-/**
- * Check for an error condition (e.g. empty handle, JS exception, OOM) after executing
- * a v8 operation.
- * @resultHandle handle storing the result of the preceding v8 operation
- * @try_catch the active v8::TryCatch exception handler
- * @param reportError if true, log an error message
- * @param assertOnError if true, throw an exception if an error is detected
- * if false, return value indicates error state
- * @return true if an error was detected and assertOnError is set to false
- * false if no error was detected
- */
-template <typename _HandleType>
-bool V8Scope::checkV8ErrorState(const _HandleType& resultHandle,
- const v8::TryCatch& try_catch,
- bool reportError,
- bool assertOnError) {
- bool haveError = false;
-
- if (try_catch.HasCaught() && try_catch.CanContinue()) {
- // normal JS exception
- _error = v8ExceptionToSTLString(&try_catch);
- haveError = true;
- } else if (hasOutOfMemoryException()) {
- // out of memory exception (treated as terminal)
- _error = "JavaScript execution failed -- v8 is out of memory";
- haveError = true;
- } else if (resultHandle.IsEmpty() || try_catch.HasCaught()) {
- // terminal exception (due to empty handle, termination, etc.)
- _error = "JavaScript execution failed";
- haveError = true;
- }
-
- if (haveError) {
- if (reportError)
- error() << _error << endl;
- if (assertOnError)
- uasserted(16722, _error);
- return true;
- }
-
- return false;
-}
-
-MONGO_INITIALIZER(JavascriptPrintDomain)(InitializerContext*) {
- jsPrintLogDomain = logger::globalLogManager()->getNamedDomain("javascriptOutput");
- return Status::OK();
-}
-
-} // namespace mongo
diff --git a/src/mongo/scripting/engine_v8-3.25.h b/src/mongo/scripting/engine_v8-3.25.h
deleted file mode 100644
index 0865ab59ac3..00000000000
--- a/src/mongo/scripting/engine_v8-3.25.h
+++ /dev/null
@@ -1,653 +0,0 @@
-// engine_v8.h
-
-/* Copyright 2014 MongoDB 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/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <v8.h>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "mongo/base/disallow_copying.h"
-#include "mongo/base/string_data.h"
-#include "mongo/client/dbclientinterface.h"
-#include "mongo/client/dbclientcursor.h"
-#include "mongo/platform/unordered_map.h"
-#include "mongo/scripting/engine.h"
-#include "mongo/scripting/deadline_monitor.h"
-#include "mongo/scripting/v8-3.25_profiler.h"
-
-/**
- * V8_SIMPLE_HEADER must be placed in any function called from a public API
- * that work with v8 handles (and/or must be within the V8Scope's isolate
- * and context). Be sure to close the handle_scope if returning a v8::Handle!
- */
-#define V8_SIMPLE_HEADER \
- v8::Locker v8lock(_isolate); /* acquire isolate lock */ \
- v8::Isolate::Scope iscope(_isolate); /* enter the isolate; exit when out of scope */ \
- v8::HandleScope handle_scope(_isolate); /* make the current scope own local */ \
- /* handles */ \
- v8::Context::Scope context_scope(getContext()); /* enter the context; exit when */ \
- /* out of scope */
-
-namespace mongo {
-
-class V8ScriptEngine;
-class V8Scope;
-class BSONHolder;
-class JSThreadConfig;
-
-typedef v8::Local<v8::Value>(*v8Function)(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-/**
- * The ObjTracker class keeps track of all weakly referenced v8 objects. This is
- * required because v8 does not invoke the WeakReferenceCallback when shutting down
- * the context/isolate. To track a new object, add an ObjTracker<MyObjType> member
- * variable to the V8Scope (if one does not already exist for that type). Instead
- * of calling v8::Persistent::MakeWeak() directly, simply invoke track() with the
- * persistent handle and the pointer to be freed.
- */
-template <typename _ObjType>
-class ObjTracker {
-public:
- /** Track an object to be freed when it is no longer referenced in JavaScript.
- * Return handle to object instance shared pointer.
- * @param instanceHandle persistent handle to the weakly referenced object
- * @param rawData pointer to the object instance
- */
- v8::Local<v8::External> track(v8::Isolate* isolate,
- v8::Local<v8::Value> instanceHandle,
- _ObjType* instance) {
- TrackedPtr* collectionHandle = new TrackedPtr(isolate, instanceHandle, instance, this);
- _container.insert(collectionHandle);
- collectionHandle->_instanceHandle.SetWeak(collectionHandle, deleteOnCollect);
- return v8::External::New(isolate, &(collectionHandle->_objPtr));
- }
- /**
- * Free any remaining objects and their TrackedPtrs. Invoked when the
- * V8Scope is destructed.
- */
- ~ObjTracker() {
- typename std::set<TrackedPtr*>::iterator it = _container.begin();
- while (it != _container.end()) {
- delete *it;
- _container.erase(it++);
- }
- }
-
-private:
- /**
- * Simple struct which contains a pointer to the tracked object, and a pointer
- * to the ObjTracker which owns it. This is the argument supplied to v8's
- * WeakReferenceCallback and MakeWeak().
- */
- struct TrackedPtr {
- public:
- TrackedPtr(v8::Isolate* isolate,
- v8::Local<v8::Value>& instanceHandle,
- _ObjType* instance,
- ObjTracker<_ObjType>* tracker)
- : _instanceHandle(isolate, instanceHandle), _objPtr(instance), _tracker(tracker) {}
- v8::Persistent<v8::Value> _instanceHandle;
- std::shared_ptr<_ObjType> _objPtr;
- ObjTracker<_ObjType>* _tracker;
- };
-
- /**
- * v8 callback for weak persistent handles that have been marked for removal by the
- * garbage collector. Signature conforms to v8's WeakReferenceCallback.
- * @param data Weak callback data. Contains pointer to the TrackedPtr instance.
- */
- static void deleteOnCollect(const v8::WeakCallbackData<v8::Value, TrackedPtr>& data) {
- std::unique_ptr<TrackedPtr> trackedPtr(data.GetParameter());
- invariant(trackedPtr.get());
-
- trackedPtr->_tracker->_container.erase(trackedPtr.get());
- trackedPtr->_instanceHandle.Reset();
- }
-
- // container for all TrackedPtrs created by this ObjTracker instance
- std::set<TrackedPtr*> _container;
-};
-
-/**
- * A V8Scope represents a unit of javascript execution environment; specifically a single
- * isolate and a single context executing in a single mongo thread. A V8Scope can be reused
- * in another thread only after reset() has been called.
- *
- * NB:
- * - v8 objects/handles/etc. cannot be shared between V8Scopes
- * - in mongod, each scope is associated with an opId (for KillOp support)
- * - any public functions that call the v8 API should use a V8_SIMPLE_HEADER
- * - the caller of any public function that returns a v8 type or has a v8 handle argument
- * must enter the isolate, context, and set up the appropriate handle scope
- */
-class V8Scope : public Scope {
-public:
- V8Scope(V8ScriptEngine* engine);
- ~V8Scope();
-
- virtual void init(const BSONObj* data);
-
- /**
- * Reset the state of this scope for use by another thread or operation
- */
- virtual void reset();
-
- /**
- * Terminate this scope
- */
- virtual void kill();
-
- /** check if there is a pending killOp request */
- bool isKillPending() const;
-
- /**
- * Obtains the operation context associated with this Scope, so it can be given to the
- * DBDirectClient used by the V8 engine's connection. Only needed for dbEval.
- */
- OperationContext* getOpContext() const;
-
- /**
- * Register this scope with the mongo op id. If executing outside the
- * context of a mongo operation (e.g. from the shell), killOp will not
- * be supported.
- */
- virtual void registerOperation(OperationContext* txn);
-
- /**
- * Unregister this scope with the mongo op id.
- */
- virtual void unregisterOperation();
-
- /**
- * Connect to a local database, create a Mongo object instance, and load any
- * server-side js into the global object
- */
- virtual void localConnectForDbEval(OperationContext* txn, const char* dbName);
-
- virtual void externalSetup();
-
- virtual void installDBAccess();
-
- virtual void installBSONTypes();
-
- virtual std::string getError() {
- return _error;
- }
-
- virtual bool hasOutOfMemoryException();
-
- /**
- * Run the garbage collector on this scope (native function). @see GCV8 for the
- * javascript binding version.
- */
- void gc();
-
- /**
- * get a global property. caller must set up the v8 state.
- */
- v8::Local<v8::Value> get(const char* field);
-
- virtual double getNumber(const char* field);
- virtual int getNumberInt(const char* field);
- virtual long long getNumberLongLong(const char* field);
- virtual std::string getString(const char* field);
- virtual bool getBoolean(const char* field);
- virtual BSONObj getObject(const char* field);
-
- virtual void setNumber(const char* field, double val);
- virtual void setString(const char* field, StringData 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 int type(const char* field);
-
- virtual void rename(const char* from, const char* to);
-
- 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(StringData code,
- const std::string& name,
- bool printResult,
- bool reportError,
- bool assertOnError,
- int timeoutMs);
-
- // functions to create v8 object and function templates
- virtual void injectNative(const char* field, NativeFunction func, void* data = 0);
- void injectNative(const char* field,
- NativeFunction func,
- v8::Local<v8::Object>& obj,
- void* data = 0);
-
- // These functions inject a function (either an unwrapped function pointer or a pre-wrapped
- // FunctionTemplate) into the provided object. If no object is provided, the function will
- // be injected at global scope. These functions take care of setting the function and class
- // name on the returned FunctionTemplate.
- v8::Local<v8::FunctionTemplate> injectV8Function(const char* name, v8Function func);
- v8::Local<v8::FunctionTemplate> injectV8Function(const char* name,
- v8Function func,
- v8::Local<v8::Object> obj);
- v8::Local<v8::FunctionTemplate> injectV8Function(const char* name,
- v8::Local<v8::FunctionTemplate> ft,
- v8::Local<v8::Object> obj);
-
- // Injects a method into the provided prototype
- v8::Local<v8::FunctionTemplate> injectV8Method(const char* name,
- v8Function func,
- v8::Local<v8::ObjectTemplate>& proto);
- v8::Local<v8::FunctionTemplate> createV8Function(v8Function func);
- virtual ScriptingFunction _createFunction(const char* code,
- ScriptingFunction functionNumber = 0);
- v8::Local<v8::Function> __createFunction(const char* code,
- ScriptingFunction functionNumber = 0);
-
- /**
- * Convert BSON types to v8 Javascript types
- */
- v8::Local<v8::Object> mongoToLZV8(const mongo::BSONObj& m, bool readOnly = false);
- v8::Local<v8::Value> mongoToV8Element(const BSONElement& f, bool readOnly = false);
-
- /**
- * Convert v8 Javascript types to BSON types
- */
- mongo::BSONObj v8ToMongo(v8::Local<v8::Object> obj, int depth = 0);
- void v8ToMongoElement(BSONObjBuilder& b,
- StringData sname,
- v8::Local<v8::Value> value,
- int depth = 0,
- BSONObj* originalParent = 0);
- void v8ToMongoObject(BSONObjBuilder& b,
- StringData sname,
- v8::Local<v8::Value> value,
- int depth,
- BSONObj* originalParent);
- void v8ToMongoNumber(BSONObjBuilder& b,
- StringData elementName,
- v8::Local<v8::Number> value,
- BSONObj* originalParent);
- void v8ToMongoRegex(BSONObjBuilder& b, StringData elementName, v8::Local<v8::RegExp> v8Regex);
- void v8ToMongoDBRef(BSONObjBuilder& b, StringData elementName, v8::Local<v8::Object> obj);
- void v8ToMongoBinData(BSONObjBuilder& b, StringData elementName, v8::Local<v8::Object> obj);
- OID v8ToMongoObjectID(v8::Local<v8::Object> obj);
-
- v8::Local<v8::Value> newId(const OID& id);
-
- /**
- * Convert a JavaScript exception to a stl string. Requires
- * access to the V8Scope instance to report source context information.
- */
- std::string v8ExceptionToSTLString(const v8::TryCatch* try_catch);
-
- /**
- * Create a V8 string with a local handle
- */
- inline v8::Local<v8::String> v8StringData(StringData str) {
- return v8::String::NewFromUtf8(
- _isolate, str.rawData(), v8::String::kNormalString, str.size());
- }
-
- /**
- * Get the isolate this scope belongs to (can be called from any thread, but v8 requires
- * the new thread enter the isolate and context. Only one thread can enter the isolate.
- */
- v8::Isolate* getIsolate() const {
- return _isolate;
- }
-
- /**
- * Get the JS context this scope executes within.
- */
- v8::Local<v8::Context> getContext() {
- return _context.Get(_isolate);
- }
-
- /**
- * Get the global JS object
- */
- v8::Local<v8::Object> getGlobal() {
- return _global.Get(_isolate);
- }
-
- ObjTracker<BSONHolder> bsonHolderTracker;
- ObjTracker<DBClientBase> dbClientBaseTracker;
- // Track both cursor and connection.
- // This ensures the connection outlives the cursor.
- struct DBConnectionAndCursor {
- std::shared_ptr<DBClientBase> conn;
- std::shared_ptr<DBClientCursor> cursor;
- DBConnectionAndCursor(std::shared_ptr<DBClientBase> conn,
- std::shared_ptr<DBClientCursor> cursor)
- : conn(conn), cursor(cursor) {}
- };
- ObjTracker<DBConnectionAndCursor> dbConnectionAndCursor;
- ObjTracker<JSThreadConfig> jsThreadConfigTracker;
-
- // These are all named after the JS constructor name + FT
- v8::Local<v8::FunctionTemplate> ObjectIdFT() {
- return _ObjectIdFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> DBRefFT() {
- return _DBRefFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> DBPointerFT() {
- return _DBPointerFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> BinDataFT() {
- return _BinDataFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> NumberLongFT() {
- return _NumberLongFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> NumberIntFT() {
- return _NumberIntFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> TimestampFT() {
- return _TimestampFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> MinKeyFT() {
- return _MinKeyFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> MaxKeyFT() {
- return _MaxKeyFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> MongoFT() {
- return _MongoFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> DBFT() {
- return _DBFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> DBCollectionFT() {
- return _DBCollectionFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> DBQueryFT() {
- return _DBQueryFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> InternalCursorFT() {
- return _InternalCursorFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> LazyBsonFT() {
- return _LazyBsonFT.Get(_isolate);
- }
- v8::Local<v8::FunctionTemplate> ROBsonFT() {
- return _ROBsonFT.Get(_isolate);
- }
-
- template <size_t N>
- v8::Local<v8::String> strLitToV8(const char(&str)[N]) {
- // Note that _strLitMap is keyed on string pointer not string
- // value. This is OK because each string literal has a constant
- // pointer for the program's lifetime. This works best if (but does
- // not require) the linker interns all string literals giving
- // identical strings used in different places the same pointer.
-
- StrLitMap::iterator it = _strLitMap.find(str);
- if (it != _strLitMap.end())
- return it->second.Get(_isolate);
-
- StringData sd(str, StringData::LiteralTag());
- v8::Local<v8::String> v8Str = v8StringData(sd);
-
- // Eternal should last as long as V8Scope exists.
- _strLitMap[str].Set(_isolate, v8Str);
-
- return v8Str;
- }
-
-private:
- /**
- * Recursion limit when converting from JS objects to BSON.
- */
- static const int objectDepthLimit = 150;
-
- /**
- * Attach data to obj such that the data has the same lifetime as the Object obj points to.
- * obj must have been created by either LazyBsonFT or ROBsonFT.
- */
- void wrapBSONObject(v8::Local<v8::Object> obj, BSONObj data, bool readOnly);
-
- /**
- * Trampoline to call a c++ function with a specific signature (V8Scope*,
- * v8::FunctionCallbackInfo<v8::Value>&).
- * Handles interruption, exceptions, etc.
- */
- static void v8Callback(const v8::FunctionCallbackInfo<v8::Value>& args);
-
- /**
- * Interpreter agnostic 'Native Callback' trampoline. Note this is only called
- * from v8Callback().
- */
- static v8::Local<v8::Value> nativeCallback(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
- /**
- * v8-specific implementations of basic global functions
- */
- static v8::Local<v8::Value> load(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
- static v8::Local<v8::Value> Print(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
- static v8::Local<v8::Value> Version(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
- static v8::Local<v8::Value> GCV8(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
- static v8::Local<v8::Value> startCpuProfiler(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
- static v8::Local<v8::Value> stopCpuProfiler(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
- static v8::Local<v8::Value> getCpuProfile(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
- /** Signal that this scope has entered a native (C++) execution context.
- * @return false if execution has been interrupted
- */
- bool nativePrologue();
-
- /** Signal that this scope has completed native execution and is returning to v8.
- * @return false if execution has been interrupted
- */
- bool nativeEpilogue();
-
- /**
- * Create a new function; primarily used for BSON/V8 conversion.
- */
- v8::Local<v8::Value> newFunction(StringData code);
-
- template <typename _HandleType>
- bool checkV8ErrorState(const _HandleType& resultHandle,
- const v8::TryCatch& try_catch,
- bool reportError = true,
- bool assertOnError = true);
-
- V8ScriptEngine* _engine;
-
- v8::Eternal<v8::Context> _context;
- v8::Eternal<v8::Object> _global;
- std::string _error;
- std::vector<v8::Eternal<v8::Value>> _funcs;
-
- enum ConnectState { NOT, LOCAL, EXTERNAL };
- ConnectState _connectState;
-
- // These are all named after the JS constructor name + FT
- v8::Eternal<v8::FunctionTemplate> _ObjectIdFT;
- v8::Eternal<v8::FunctionTemplate> _DBRefFT;
- v8::Eternal<v8::FunctionTemplate> _DBPointerFT;
- v8::Eternal<v8::FunctionTemplate> _BinDataFT;
- v8::Eternal<v8::FunctionTemplate> _NumberLongFT;
- v8::Eternal<v8::FunctionTemplate> _NumberIntFT;
- v8::Eternal<v8::FunctionTemplate> _TimestampFT;
- v8::Eternal<v8::FunctionTemplate> _MinKeyFT;
- v8::Eternal<v8::FunctionTemplate> _MaxKeyFT;
- v8::Eternal<v8::FunctionTemplate> _MongoFT;
- v8::Eternal<v8::FunctionTemplate> _DBFT;
- v8::Eternal<v8::FunctionTemplate> _DBCollectionFT;
- v8::Eternal<v8::FunctionTemplate> _DBQueryFT;
- v8::Eternal<v8::FunctionTemplate> _InternalCursorFT;
- v8::Eternal<v8::FunctionTemplate> _LazyBsonFT;
- v8::Eternal<v8::FunctionTemplate> _ROBsonFT;
-
- v8::Eternal<v8::Function> _jsRegExpConstructor;
-
- /// Like v8::Isolate* but calls Dispose() in destructor.
- class IsolateHolder {
- MONGO_DISALLOW_COPYING(IsolateHolder);
-
- public:
- IsolateHolder() : _isolate(NULL) {}
- ~IsolateHolder() {
- if (_isolate) {
- _isolate->Dispose();
- _isolate = NULL;
- }
- }
-
- void set(v8::Isolate* isolate) {
- fassert(17184, !_isolate);
- _isolate = isolate;
- }
-
- v8::Isolate* operator->() const {
- return _isolate;
- };
- operator v8::Isolate*() const {
- return _isolate;
- };
-
- private:
- v8::Isolate* _isolate;
- };
-
- IsolateHolder _isolate; // NOTE: this must be destructed before the ObjTrackers
-
- V8CpuProfiler _cpuProfiler;
-
- // See comments in strLitToV8
- typedef unordered_map<const char*, v8::Eternal<v8::String>> StrLitMap;
- StrLitMap _strLitMap;
-
- stdx::mutex _interruptLock; // protects interruption-related flags
- bool _inNativeExecution; // protected by _interruptLock
- bool _pendingKill; // protected by _interruptLock
- unsigned int _opId; // op id for this scope
- OperationContext* _opCtx; // Op context for DbEval
-};
-
-/// Helper to extract V8Scope for an Isolate
-inline V8Scope* getScope(v8::Isolate* isolate) {
- invariant(isolate);
- invariant(isolate->GetNumberOfDataSlots() >= 1U);
- uint32_t slot = 0;
- return static_cast<V8Scope*>(isolate->GetData(slot));
-}
-
-class V8ScriptEngine : public ScriptEngine {
-public:
- V8ScriptEngine();
- virtual ~V8ScriptEngine();
- virtual Scope* createScope() {
- return new V8Scope(this);
- }
- virtual void runTest() {}
- bool utf8Ok() const {
- return true;
- }
-
- /**
- * Interrupt a single active v8 execution context
- * NB: To interrupt a context, we must acquire the following locks (in order):
- * - mutex to protect the the map of all scopes (_globalInterruptLock)
- * - mutex to protect the scope that's being interrupted (_interruptLock)
- * The scope will be removed from the map upon destruction, and the op id
- * will be updated if the scope is ever reused from a pool.
- */
- virtual void interrupt(unsigned opId);
-
- /**
- * Interrupt all v8 contexts (and isolates). @see interrupt().
- */
- virtual void interruptAll();
-
-private:
- friend class V8Scope;
-
- std::string printKnownOps_inlock();
-
- /**
- * Get the deadline monitor instance for the v8 ScriptEngine
- */
- DeadlineMonitor<V8Scope>* getDeadlineMonitor() {
- return &_deadlineMonitor;
- }
-
- typedef std::map<unsigned, V8Scope*> OpIdToScopeMap;
- stdx::mutex _globalInterruptLock; // protects map of all operation ids -> scope
- OpIdToScopeMap _opToScopeMap; // map of mongo op ids to scopes (protected by
- // _globalInterruptLock).
- DeadlineMonitor<V8Scope> _deadlineMonitor;
-};
-
-class BSONHolder {
- MONGO_DISALLOW_COPYING(BSONHolder);
-
-public:
- BSONHolder(V8Scope* scope, BSONObj obj, bool readOnly)
- : _scope(scope), _obj(obj.getOwned()), _modified(false), _readOnly(readOnly) {
- invariant(scope);
- if (_scope->getIsolate()) {
- // give hint v8's GC
- _scope->getIsolate()->AdjustAmountOfExternalAllocatedMemory(_obj.objsize());
- }
- }
- ~BSONHolder() {
- if (_scope->getIsolate()) {
- // if v8 is still up, send hint to GC
- _scope->getIsolate()->AdjustAmountOfExternalAllocatedMemory(-_obj.objsize());
- }
- }
- const V8Scope* _scope;
- const BSONObj _obj;
- bool _modified;
- const bool _readOnly;
- std::set<std::string> _removed;
-};
-
-extern ScriptEngine* globalScriptEngine;
-}
diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp
deleted file mode 100644
index e4463106ece..00000000000
--- a/src/mongo/scripting/engine_v8.cpp
+++ /dev/null
@@ -1,1880 +0,0 @@
-// engine_v8.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * 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.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/scripting/engine_v8.h"
-
-#include <iostream>
-
-#include "mongo/base/init.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/service_context.h"
-#include "mongo/platform/decimal128.h"
-#include "mongo/platform/unordered_set.h"
-#include "mongo/scripting/v8_db.h"
-#include "mongo/scripting/v8_utils.h"
-#include "mongo/util/base64.h"
-#include "mongo/util/log.h"
-#include "mongo/util/mongoutils/str.h"
-
-using namespace mongoutils;
-
-namespace mongo {
-
-using std::cout;
-using std::endl;
-using std::map;
-using std::string;
-using std::stringstream;
-
-#ifndef _MSC_EXTENSIONS
-const int V8Scope::objectDepthLimit;
-#endif
-
-// Generated symbols for JS files
-namespace JSFiles {
-extern const JSFile types;
-extern const JSFile assert;
-}
-
-// The unwrapXXX functions extract internal fields from an object wrapped by wrapBSONObject.
-// These functions are currently only used in places that should always have the correct
-// type of object, however it may be possible for users to come up with a way to make these
-// called with the wrong type so calling code should always check the returns.
-static BSONHolder* unwrapHolder(V8Scope* scope, const v8::Handle<v8::Object>& obj) {
- // Warning: can't throw exceptions in this context.
- if (!scope->LazyBsonFT()->HasInstance(obj))
- return NULL;
-
- v8::Handle<v8::External> field = v8::Handle<v8::External>::Cast(obj->GetInternalField(0));
- if (field.IsEmpty() || !field->IsExternal())
- return 0;
- void* ptr = field->Value();
- return (BSONHolder*)ptr;
-}
-
-static BSONObj unwrapBSONObj(V8Scope* scope, const v8::Handle<v8::Object>& obj) {
- // Warning: can't throw exceptions in this context.
- BSONHolder* holder = unwrapHolder(scope, obj);
- return holder ? holder->_obj : BSONObj();
-}
-
-static v8::Handle<v8::Object> unwrapObject(V8Scope* scope, const v8::Handle<v8::Object>& obj) {
- // Warning: can't throw exceptions in this context.
- if (!scope->LazyBsonFT()->HasInstance(obj))
- return v8::Handle<v8::Object>();
-
- return obj->GetInternalField(1).As<v8::Object>();
-}
-
-void V8Scope::wrapBSONObject(v8::Handle<v8::Object> obj, BSONObj data, bool readOnly) {
- verify(LazyBsonFT()->HasInstance(obj));
-
- // Nothing below throws
- BSONHolder* holder = new BSONHolder(data);
- holder->_readOnly = readOnly;
- holder->_scope = this;
- obj->SetInternalField(0, v8::External::New(holder)); // Holder
- obj->SetInternalField(1, v8::Object::New()); // Object
- v8::Persistent<v8::Object> p = v8::Persistent<v8::Object>::New(obj);
- bsonHolderTracker.track(p, holder);
-}
-
-static v8::Handle<v8::Value> namedGet(v8::Local<v8::String> name, const v8::AccessorInfo& info) {
- v8::HandleScope handle_scope;
- v8::Handle<v8::Value> val;
- try {
- V8Scope* scope = getScope(info.GetIsolate());
- v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return v8::Handle<v8::Value>();
- if (realObject->HasOwnProperty(name)) {
- // value already cached or added
- return handle_scope.Close(realObject->Get(name));
- }
-
- string key = toSTLString(name);
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder || holder->_removed.count(key))
- return handle_scope.Close(v8::Handle<v8::Value>());
-
- BSONObj obj = holder->_obj;
- BSONElement elmt = obj.getField(key.c_str());
- if (elmt.eoo())
- return handle_scope.Close(v8::Handle<v8::Value>());
-
- val = scope->mongoToV8Element(elmt, holder->_readOnly);
-
- if (obj.objsize() > 128 || val->IsObject()) {
- // Only cache if expected to help (large BSON) or is required due to js semantics
- realObject->Set(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
- holder->_modified = true;
- }
- } catch (const DBException& dbEx) {
- return v8AssertionException(dbEx.toString());
- } catch (...) {
- return v8AssertionException(string("error getting property ") + toSTLString(name));
- }
- return handle_scope.Close(val);
-}
-
-static v8::Handle<v8::Value> namedGetRO(v8::Local<v8::String> name, const v8::AccessorInfo& info) {
- return namedGet(name, info);
-}
-
-static v8::Handle<v8::Value> namedSet(v8::Local<v8::String> name,
- v8::Local<v8::Value> value_obj,
- const v8::AccessorInfo& info) {
- string key = toSTLString(name);
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return v8::Handle<v8::Value>();
- holder->_removed.erase(key);
- holder->_modified = true;
-
- v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return v8::Handle<v8::Value>();
- realObject->Set(name, value_obj);
- return value_obj;
-}
-
-static v8::Handle<v8::Array> namedEnumerator(const v8::AccessorInfo& info) {
- v8::HandleScope handle_scope;
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return v8::Handle<v8::Array>();
- BSONObj obj = holder->_obj;
- v8::Handle<v8::Array> out = v8::Array::New();
- int outIndex = 0;
-
- unordered_set<StringData, StringData::Hasher> added;
- // note here that if keys are parseable number, v8 will access them using index
- for (BSONObjIterator it(obj); it.more();) {
- const BSONElement& f = it.next();
- StringData sname(f.fieldName(), f.fieldNameSize() - 1);
- if (holder->_removed.count(sname.toString()))
- continue;
-
- v8::Handle<v8::String> name = scope->v8StringData(sname);
- added.insert(sname);
- out->Set(outIndex++, name);
- }
-
-
- v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return v8::Handle<v8::Array>();
- v8::Handle<v8::Array> fields = realObject->GetOwnPropertyNames();
- const int len = fields->Length();
- for (int field = 0; field < len; field++) {
- v8::Handle<v8::String> name = fields->Get(field).As<v8::String>();
- V8String sname(name);
- if (added.count(sname))
- continue;
- out->Set(outIndex++, name);
- }
- return handle_scope.Close(out);
-}
-
-v8::Handle<v8::Boolean> namedDelete(v8::Local<v8::String> name, const v8::AccessorInfo& info) {
- v8::HandleScope handle_scope;
- string key = toSTLString(name);
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return v8::Handle<v8::Boolean>();
- holder->_removed.insert(key);
- holder->_modified = true;
-
- v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return v8::Handle<v8::Boolean>();
- realObject->Delete(name);
- return v8::True();
-}
-
-static v8::Handle<v8::Value> indexedGet(uint32_t index, const v8::AccessorInfo& info) {
- v8::HandleScope handle_scope;
- v8::Handle<v8::Value> val;
- try {
- V8Scope* scope = getScope(info.GetIsolate());
- v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return v8::Handle<v8::Value>();
- if (realObject->Has(index)) {
- // value already cached or added
- return handle_scope.Close(realObject->Get(index));
- }
- string key = str::stream() << index;
-
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return v8::Handle<v8::Value>();
- if (holder->_removed.count(key))
- return handle_scope.Close(v8::Handle<v8::Value>());
-
- BSONObj obj = holder->_obj;
- BSONElement elmt = obj.getField(key);
- if (elmt.eoo())
- return handle_scope.Close(v8::Handle<v8::Value>());
- val = scope->mongoToV8Element(elmt, holder->_readOnly);
- realObject->Set(index, 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
- holder->_modified = true;
- }
- } catch (const DBException& dbEx) {
- return v8AssertionException(dbEx.toString());
- } catch (...) {
- return v8AssertionException(str::stream() << "error getting indexed property " << index);
- }
- return handle_scope.Close(val);
-}
-
-v8::Handle<v8::Boolean> indexedDelete(uint32_t index, const v8::AccessorInfo& info) {
- string key = str::stream() << index;
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return v8::Handle<v8::Boolean>();
- holder->_removed.insert(key);
- holder->_modified = true;
-
- // also delete in JS obj
- v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return v8::Handle<v8::Boolean>();
- realObject->Delete(index);
- return v8::True();
-}
-
-static v8::Handle<v8::Value> indexedGetRO(uint32_t index, const v8::AccessorInfo& info) {
- return indexedGet(index, info);
-}
-
-static v8::Handle<v8::Value> indexedSet(uint32_t index,
- v8::Local<v8::Value> value_obj,
- const v8::AccessorInfo& info) {
- string key = str::stream() << index;
- V8Scope* scope = getScope(info.GetIsolate());
- BSONHolder* holder = unwrapHolder(scope, info.Holder());
- if (!holder)
- return v8::Handle<v8::Value>();
- holder->_removed.erase(key);
- holder->_modified = true;
-
- v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
- if (realObject.IsEmpty())
- return v8::Handle<v8::Value>();
- realObject->Set(index, value_obj);
- return value_obj;
-}
-
-v8::Handle<v8::Value> NamedReadOnlySet(v8::Local<v8::String> property,
- v8::Local<v8::Value> value,
- const v8::AccessorInfo& info) {
- cout << "cannot write property " << V8String(property) << " to read-only object" << endl;
- return value;
-}
-
-v8::Handle<v8::Boolean> NamedReadOnlyDelete(v8::Local<v8::String> property,
- const v8::AccessorInfo& info) {
- cout << "cannot delete property " << V8String(property) << " from read-only object" << endl;
- return v8::Boolean::New(false);
-}
-
-v8::Handle<v8::Value> IndexedReadOnlySet(uint32_t index,
- v8::Local<v8::Value> value,
- const v8::AccessorInfo& info) {
- cout << "cannot write property " << index << " to read-only array" << endl;
- return value;
-}
-
-v8::Handle<v8::Boolean> IndexedReadOnlyDelete(uint32_t index, const v8::AccessorInfo& info) {
- cout << "cannot delete property " << index << " from read-only array" << endl;
- return v8::Boolean::New(false);
-}
-
-/**
- * GC Prologue and Epilogue constants (used to display description constants)
- */
-struct GCPrologueState {
- static const char* name;
-};
-const char* GCPrologueState::name = "prologue";
-struct GCEpilogueState {
- static const char* name;
-};
-const char* GCEpilogueState::name = "epilogue";
-
-template <typename _GCState>
-void gcCallback(v8::GCType type, v8::GCCallbackFlags flags) {
- if (!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() : _opToScopeMap(), _deadlineMonitor() {}
-
-V8ScriptEngine::~V8ScriptEngine() {}
-
-void ScriptEngine::setup() {
- if (!globalScriptEngine) {
- globalScriptEngine = new V8ScriptEngine();
-
- if (hasGlobalServiceContext()) {
- getGlobalServiceContext()->registerKillOpListener(globalScriptEngine);
- }
- }
-}
-
-std::string ScriptEngine::getInterpreterVersionString() {
- return "V8 3.12.19";
-}
-
-void V8ScriptEngine::interrupt(unsigned opId) {
- stdx::lock_guard<stdx::mutex> 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() {
- stdx::lock_guard<stdx::mutex> interruptLock(_globalInterruptLock);
- for (OpIdToScopeMap::iterator iScope = _opToScopeMap.begin(); iScope != _opToScopeMap.end();
- ++iScope) {
- iScope->second->kill();
- }
-}
-
-void V8Scope::registerOperation(OperationContext* txn) {
- stdx::lock_guard<stdx::mutex> giLock(_engine->_globalInterruptLock);
- invariant(_opId == 0);
- _opId = txn->getOpID();
- _engine->_opToScopeMap[_opId] = this;
- LOG(2) << "V8Scope " << static_cast<const void*>(this) << " registered for op " << _opId;
- Status status = txn->checkForInterruptNoAssert();
- if (!status.isOK()) {
- kill();
- }
-}
-
-void V8Scope::unregisterOperation() {
- stdx::lock_guard<stdx::mutex> giLock(_engine->_globalInterruptLock);
- LOG(2) << "V8Scope " << static_cast<const void*>(this) << " unregistered for op " << _opId
- << endl;
- if (_opId != 0) {
- // scope is currently associated with an operation id
- V8ScriptEngine::OpIdToScopeMap::iterator it = _engine->_opToScopeMap.find(_opId);
- if (it != _engine->_opToScopeMap.end())
- _engine->_opToScopeMap.erase(it);
- _opId = 0;
- }
-}
-
-bool V8Scope::nativePrologue() {
- v8::Locker l(_isolate);
- stdx::lock_guard<stdx::mutex> cbEnterLock(_interruptLock);
- if (v8::V8::IsExecutionTerminating(_isolate)) {
- LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate)
- << endl;
- return false;
- }
- if (isKillPending()) {
- // kill flag was set before entering our callback
- LOG(2) << "marked for death while leaving callback. isolate: "
- << static_cast<const void*>(_isolate) << endl;
- v8::V8::TerminateExecution(_isolate);
- return false;
- }
- _inNativeExecution = true;
- return true;
-}
-
-bool V8Scope::nativeEpilogue() {
- v8::Locker l(_isolate);
- stdx::lock_guard<stdx::mutex> cbLeaveLock(_interruptLock);
- _inNativeExecution = false;
- if (v8::V8::IsExecutionTerminating(_isolate)) {
- LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate)
- << endl;
- return false;
- }
- if (isKillPending()) {
- LOG(2) << "marked for death while leaving callback. isolate: "
- << static_cast<const void*>(_isolate) << endl;
- v8::V8::TerminateExecution(_isolate);
- return false;
- }
- return true;
-}
-
-void V8Scope::kill() {
- stdx::lock_guard<stdx::mutex> interruptLock(_interruptLock);
- if (!_inNativeExecution) {
- // 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: " << static_cast<const void*>(_isolate) << endl;
- }
- LOG(1) << "marking v8 scope for death. isolate: " << static_cast<const void*>(_isolate)
- << endl;
- _pendingKill = true;
-}
-
-/** check if there is a pending killOp request */
-bool V8Scope::isKillPending() const {
- return _pendingKill;
-}
-
-OperationContext* V8Scope::getOpContext() const {
- return _opCtx;
-}
-
-/**
- * Display a list of all known ops (for verbose output)
- */
-std::string V8ScriptEngine::printKnownOps_inlock() {
- stringstream out;
- if (shouldLog(logger::LogSeverity::Debug(2))) {
- out << " known ops: " << endl;
- for (OpIdToScopeMap::iterator iSc = _opToScopeMap.begin(); iSc != _opToScopeMap.end();
- ++iSc) {
- out << " " << iSc->first << endl;
- }
- }
- return out.str();
-}
-
-
-V8Scope::V8Scope(V8ScriptEngine* engine)
- : _engine(engine),
- _connectState(NOT),
- _cpuProfiler(),
- _inNativeExecution(true),
- _pendingKill(false),
- _opId(0),
- _opCtx(NULL) {
- // create new isolate and enter it via a scope
- _isolate.set(v8::Isolate::New());
- v8::Isolate::Scope iscope(_isolate);
-
- // lock the isolate and enter the context
- v8::Locker l(_isolate);
- v8::HandleScope handleScope;
- _context = v8::Context::New();
- v8::Context::Scope context_scope(_context);
-
- _isolate->SetData(this);
-
- // display heap statistics on MarkAndSweep GC run
- v8::V8::AddGCPrologueCallback(gcCallback<GCPrologueState>, v8::kGCTypeMarkSweepCompact);
- v8::V8::AddGCEpilogueCallback(gcCallback<GCEpilogueState>, v8::kGCTypeMarkSweepCompact);
-
- // if the isolate runs out of heap space, raise a flag on the StackGuard instead of
- // calling abort()
- v8::V8::IgnoreOutOfMemoryException();
-
- // create a global (rooted) object
- _global = v8::Persistent<v8::Object>::New(_context->Global());
-
- // Grab the RegExp constructor before user code gets a chance to change it. This ensures
- // we can always construct proper RegExps from C++.
- v8::Handle<v8::Value> regexp = _global->Get(strLitToV8("RegExp"));
- verify(regexp->IsFunction());
- _jsRegExpConstructor = v8::Persistent<v8::Function>::New(regexp.As<v8::Function>());
-
- // initialize lazy object template
- _LazyBsonFT = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
- LazyBsonFT()->InstanceTemplate()->SetInternalFieldCount(2);
- LazyBsonFT()->InstanceTemplate()->SetNamedPropertyHandler(
- namedGet, namedSet, NULL, namedDelete, namedEnumerator);
- LazyBsonFT()->InstanceTemplate()->SetIndexedPropertyHandler(
- indexedGet, indexedSet, NULL, indexedDelete, namedEnumerator);
- LazyBsonFT()->PrototypeTemplate()->Set(
- strLitToV8("_bson"), v8::Boolean::New(true), v8::DontEnum);
-
- _ROBsonFT = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
- ROBsonFT()->Inherit(LazyBsonFT()); // This makes LazyBsonFT()->HasInstance() true
- ROBsonFT()->InstanceTemplate()->SetInternalFieldCount(2);
- ROBsonFT()->InstanceTemplate()->SetNamedPropertyHandler(
- namedGetRO, NamedReadOnlySet, NULL, NamedReadOnlyDelete, namedEnumerator);
- ROBsonFT()->InstanceTemplate()->SetIndexedPropertyHandler(
- indexedGetRO, IndexedReadOnlySet, NULL, IndexedReadOnlyDelete, NULL);
- ROBsonFT()->PrototypeTemplate()->Set(strLitToV8("_bson"), v8::Boolean::New(true), v8::DontEnum);
-
- injectV8Function("print", Print);
- injectV8Function("version", Version); // TODO: remove
- injectV8Function("gc", GCV8);
- // injectV8Function("startCpuProfiler", startCpuProfiler);
- // injectV8Function("stopCpuProfiler", stopCpuProfiler);
- // injectV8Function("getCpuProfile", getCpuProfile);
-
- // install BSON functions in the global object
- installBSONTypes();
-
- // load JS helpers (dependancy: installBSONTypes)
- execSetup(JSFiles::assert);
- execSetup(JSFiles::types);
-
- // install process-specific utilities in the global scope (dependancy: types.js, assert.js)
- if (_engine->_scopeInitCallback)
- _engine->_scopeInitCallback(*this);
-
- // install global utility functions
- installGlobalUtils(*this);
-}
-
-V8Scope::~V8Scope() {
- unregisterOperation();
-}
-
-bool V8Scope::hasOutOfMemoryException() {
- V8_SIMPLE_HEADER
- if (!_context.IsEmpty())
- return _context->HasOutOfMemoryException();
- return false;
-}
-
-v8::Handle<v8::Value> V8Scope::load(V8Scope* scope, const v8::Arguments& args) {
- 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)) {
- return v8AssertionException(string("error loading js file: ") + filename);
- }
- }
- return v8::True();
-}
-
-v8::Handle<v8::Value> V8Scope::nativeCallback(V8Scope* scope, const v8::Arguments& args) {
- BSONObj ret;
- string exceptionText;
- v8::HandleScope handle_scope;
- try {
- 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 =
- 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]);
- BSONObj nativeArgs = b.obj();
- ret = function(nativeArgs, data->Value());
- } catch (const std::exception& e) {
- exceptionText = e.what();
- } catch (...) {
- exceptionText = "unknown exception in V8Scope::nativeCallback";
- }
- if (!exceptionText.empty()) {
- return v8AssertionException(exceptionText);
- }
- return handle_scope.Close(scope->mongoToV8Element(ret.firstElement()));
-}
-
-v8::Handle<v8::Value> V8Scope::v8Callback(const v8::Arguments& args) {
- v8::HandleScope handle_scope;
- V8Scope* scope = getScope(args.GetIsolate());
-
- if (!scope->nativePrologue())
- // execution terminated
- return v8::Undefined();
-
- v8::Local<v8::External> f = v8::Local<v8::External>::Cast(args.Data());
- v8Function function = (v8Function)(f->Value());
- v8::Handle<v8::Value> ret;
- string exceptionText;
-
- try {
- // execute the native function
- ret = function(scope, args);
- } catch (const std::exception& e) {
- exceptionText = e.what();
- } catch (...) {
- exceptionText = "unknown exception in V8Scope::v8Callback";
- }
-
- if (!scope->nativeEpilogue())
- // execution terminated
- return v8::Undefined();
-
- if (!exceptionText.empty()) {
- return v8AssertionException(exceptionText);
- }
- return handle_scope.Close(ret);
-}
-
-void V8Scope::init(const BSONObj* data) {
- 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->ForceSet(v8StringData(field), v8::Number::New(val));
-}
-
-void V8Scope::setString(const char* field, StringData val) {
- V8_SIMPLE_HEADER
- _global->ForceSet(v8StringData(field), v8::String::New(val.rawData(), val.size()));
-}
-
-void V8Scope::setBoolean(const char* field, bool val) {
- V8_SIMPLE_HEADER
- _global->ForceSet(v8StringData(field), v8::Boolean::New(val));
-}
-
-void V8Scope::setElement(const char* field, const BSONElement& e) {
- V8_SIMPLE_HEADER
- _global->ForceSet(v8StringData(field), mongoToV8Element(e));
-}
-
-void V8Scope::setObject(const char* field, const BSONObj& obj, bool readOnly) {
- V8_SIMPLE_HEADER
- _global->ForceSet(v8StringData(field), mongoToLZV8(obj, readOnly ? v8::ReadOnly : v8::None));
-}
-
-int V8Scope::type(const char* field) {
- V8_SIMPLE_HEADER
- v8::Handle<v8::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;
-
- uasserted(12509, str::stream() << "unable to get type of field " << field);
-}
-
-v8::Handle<v8::Value> V8Scope::get(const char* field) {
- return _global->Get(v8StringData(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();
-}
-
-Decimal128 V8Scope::getNumberDecimal(const char* field) {
- V8_SIMPLE_HEADER
- return Decimal128(toSTLString(get(field)->ToString()));
-}
-
-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
- v8::Handle<v8::Value> v = get(field);
- if (v->IsNull() || v->IsUndefined())
- return BSONObj();
- uassert(10231, "not an object", v->IsObject());
- return v8ToMongo(v->ToObject());
-}
-
-v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit);
- v8::Handle<v8::ObjectTemplate> proto = numberLong->PrototypeTemplate();
- scope->injectV8Method("valueOf", numberLongValueOf, proto);
- scope->injectV8Method("toNumber", numberLongToNumber, proto);
- scope->injectV8Method("toString", numberLongToString, proto);
- return numberLong;
-}
-
-v8::Handle<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> numberInt = scope->createV8Function(numberIntInit);
- v8::Handle<v8::ObjectTemplate> proto = numberInt->PrototypeTemplate();
- scope->injectV8Method("valueOf", numberIntValueOf, proto);
- scope->injectV8Method("toNumber", numberIntToNumber, proto);
- scope->injectV8Method("toString", numberIntToString, proto);
- return numberInt;
-}
-
-v8::Handle<v8::FunctionTemplate> getNumberDecimalFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> numberDecimal = scope->createV8Function(numberDecimalInit);
- v8::Handle<v8::ObjectTemplate> proto = numberDecimal->PrototypeTemplate();
- scope->injectV8Method("toString", numberDecimalToString, proto);
- return numberDecimal;
-}
-
-v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit);
- binData->InstanceTemplate()->SetInternalFieldCount(1);
- v8::Handle<v8::ObjectTemplate> proto = binData->PrototypeTemplate();
- scope->injectV8Method("toString", binDataToString, proto);
- scope->injectV8Method("base64", binDataToBase64, proto);
- scope->injectV8Method("hex", binDataToHex, proto);
- return binData;
-}
-
-v8::Handle<v8::FunctionTemplate> getTimestampFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> ts = scope->createV8Function(dbTimestampInit);
- return ts;
-}
-
-v8::Handle<v8::Value> minKeyToJson(V8Scope* scope, const v8::Arguments& args) {
- // MinKey can't just be an object like {$minKey:1} since insert() checks for fields that
- // start with $ and raises an error. See DBCollection.prototype._validateForStorage().
- return scope->strLitToV8("{ \"$minKey\" : 1 }");
-}
-
-v8::Handle<v8::Value> minKeyCall(const v8::Arguments& args) {
- // The idea here is that MinKey and MaxKey are singleton callable objects
- // that return the singleton when called. This enables all instances to
- // compare == and === to MinKey even if created by "new MinKey()" in JS.
- V8Scope* scope = getScope(args.GetIsolate());
-
- v8::Handle<v8::Function> func = scope->MinKeyFT()->GetFunction();
- v8::Handle<v8::String> name = scope->strLitToV8("singleton");
- v8::Handle<v8::Value> singleton = func->GetHiddenValue(name);
- if (!singleton.IsEmpty())
- return singleton;
-
- if (!args.IsConstructCall())
- return func->NewInstance();
-
- verify(scope->MinKeyFT()->HasInstance(args.This()));
-
- func->SetHiddenValue(name, args.This());
- return v8::Undefined();
-}
-
-v8::Handle<v8::FunctionTemplate> getMinKeyFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> myTemplate = v8::FunctionTemplate::New(minKeyCall);
- myTemplate->InstanceTemplate()->SetCallAsFunctionHandler(minKeyCall);
- myTemplate->PrototypeTemplate()->Set("tojson",
- scope->createV8Function(minKeyToJson)->GetFunction());
- myTemplate->SetClassName(scope->strLitToV8("MinKey"));
- return myTemplate;
-}
-
-v8::Handle<v8::Value> maxKeyToJson(V8Scope* scope, const v8::Arguments& args) {
- return scope->strLitToV8("{ \"$maxKey\" : 1 }");
-}
-
-v8::Handle<v8::Value> maxKeyCall(const v8::Arguments& args) {
- // See comment in minKeyCall.
- V8Scope* scope = getScope(args.GetIsolate());
-
- v8::Handle<v8::Function> func = scope->MaxKeyFT()->GetFunction();
- v8::Handle<v8::String> name = scope->strLitToV8("singleton");
- v8::Handle<v8::Value> singleton = func->GetHiddenValue(name);
- if (!singleton.IsEmpty())
- return singleton;
-
- if (!args.IsConstructCall())
- return func->NewInstance();
-
- verify(scope->MaxKeyFT()->HasInstance(args.This()));
-
- func->SetHiddenValue(name, args.This());
- return v8::Undefined();
-}
-
-v8::Handle<v8::FunctionTemplate> getMaxKeyFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> myTemplate = v8::FunctionTemplate::New(maxKeyCall);
- myTemplate->InstanceTemplate()->SetCallAsFunctionHandler(maxKeyCall);
- myTemplate->PrototypeTemplate()->Set("tojson",
- scope->createV8Function(maxKeyToJson)->GetFunction());
- myTemplate->SetClassName(scope->strLitToV8("MaxKey"));
- return myTemplate;
-}
-
-std::string V8Scope::v8ExceptionToSTLString(const v8::TryCatch* try_catch) {
- stringstream ss;
- v8::Local<v8::Value> stackTrace = try_catch->StackTrace();
- if (!stackTrace.IsEmpty()) {
- ss << StringData(V8String(stackTrace));
- } else {
- ss << StringData(V8String((try_catch->Exception())));
- }
-
- // get the exception message
- v8::Handle<v8::Message> message = try_catch->Message();
- if (message.IsEmpty())
- return ss.str();
-
- // get the resource (e.g. file or internal call)
- v8::String::Utf8Value resourceName(message->GetScriptResourceName());
- if (!*resourceName)
- return ss.str();
-
- string resourceNameString = *resourceName;
- if (resourceNameString.compare("undefined") == 0)
- return ss.str();
- if (resourceNameString.find("_funcs") == 0) {
- // script loaded from __createFunction
- string code;
- // find the source script based on the resource name supplied to v8::Script::Compile().
- // this is accomplished by converting the integer after the '_funcs' prefix.
- unsigned int funcNum = str::toUnsigned(resourceNameString.substr(6));
- for (map<string, ScriptingFunction>::iterator it = getFunctionCache().begin();
- it != getFunctionCache().end();
- ++it) {
- if (it->second == funcNum) {
- code = it->first;
- break;
- }
- }
- if (!code.empty()) {
- // append surrounding code (padded with up to 20 characters on each side)
- int startPos = message->GetStartPosition();
- const int kPadding = 20;
- if (startPos - kPadding < 0)
- // lower bound exceeded
- startPos = 0;
- else
- startPos -= kPadding;
-
- int displayRange = message->GetEndPosition();
- if (displayRange + kPadding > static_cast<int>(code.length()))
- // upper bound exceeded
- displayRange -= startPos;
- else
- // compensate for startPos padding
- displayRange = (displayRange - startPos) + kPadding;
-
- if (startPos > static_cast<int>(code.length()) ||
- displayRange > static_cast<int>(code.length()))
- return ss.str();
-
- string codeNear = code.substr(startPos, displayRange);
- for (size_t newLine = codeNear.find('\n'); newLine != string::npos;
- newLine = codeNear.find('\n')) {
- if (static_cast<int>(newLine) > displayRange - kPadding) {
- // truncate at first newline past the reported end position
- codeNear = codeNear.substr(0, newLine - 1);
- break;
- }
- // convert newlines to spaces
- codeNear.replace(newLine, 1, " ");
- }
- // trim leading chars
- codeNear = str::ltrim(codeNear);
- ss << " near '" << codeNear << "' ";
- const int linenum = message->GetLineNumber();
- if (linenum != 1)
- ss << " (line " << linenum << ")";
- }
- } else if (resourceNameString.find("(shell") == 0) {
- // script loaded from shell input -- simply print the error
- } else {
- // script loaded from file
- ss << " at " << *resourceName;
- const int linenum = message->GetLineNumber();
- if (linenum != 1)
- ss << ":" << linenum;
- }
- return ss.str();
-}
-
-// --- functions -----
-
-bool hasFunctionIdentifier(StringData code) {
- if (code.size() < 9 || code.find("function") != 0)
- return false;
-
- return code[8] == ' ' || code[8] == '(';
-}
-
-v8::Local<v8::Function> V8Scope::__createFunction(const char* raw,
- ScriptingFunction functionNumber) {
- v8::HandleScope handle_scope;
- v8::TryCatch try_catch;
- 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 + "}";
- }
-
- 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(), code.length()), v8::String::New(fn.c_str()));
-
- // throw on error
- checkV8ErrorState(script, try_catch);
-
- v8::Local<v8::Value> result = script->Run();
-
- // throw on error
- checkV8ErrorState(result, try_catch);
-
- return handle_scope.Close(
- v8::Handle<v8::Function>(v8::Function::Cast(*_global->Get(v8::String::New(fn.c_str())))));
-}
-
-ScriptingFunction V8Scope::_createFunction(const char* raw, ScriptingFunction functionNumber) {
- V8_SIMPLE_HEADER
- v8::Local<v8::Value> ret = __createFunction(raw, functionNumber);
- v8::Persistent<v8::Value> f = v8::Persistent<v8::Value>::New(ret);
- uassert(10232, "not a function", f->IsFunction());
- _funcs.push_back(f);
- return functionNumber;
-}
-
-void V8Scope::setFunction(const char* field, const char* code) {
- V8_SIMPLE_HEADER
- _global->ForceSet(v8StringData(field), __createFunction(code, getFunctionCache().size() + 1));
-}
-
-void V8Scope::rename(const char* from, const char* to) {
- V8_SIMPLE_HEADER;
- v8::Handle<v8::String> f = v8StringData(from);
- v8::Handle<v8::String> t = v8StringData(to);
- _global->ForceSet(t, _global->Get(f));
- _global->ForceSet(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
- v8::Handle<v8::Value> funcValue = _funcs[func - 1];
- v8::TryCatch try_catch;
- v8::Local<v8::Value> result;
-
- // TODO SERVER-8016: properly allocate handles on the stack
- static const int MAX_ARGS = 24;
- const int nargs = argsObject ? argsObject->nFields() : 0;
- uassert(16862, "Too many arguments. Max is 24", nargs <= MAX_ARGS);
-
- v8::Handle<v8::Value> args[MAX_ARGS];
- if (nargs) {
- BSONObjIterator it(*argsObject);
- for (int i = 0; i < nargs; i++) {
- BSONElement next = it.next();
- args[i] = mongoToV8Element(next, readOnlyArgs);
- }
- }
-
- v8::Handle<v8::Object> v8recv;
- if (recv != 0)
- v8recv = mongoToLZV8(*recv, readOnlyRecv);
- else
- v8recv = _global;
-
- if (!nativeEpilogue()) {
- _error = "JavaScript execution terminated";
- error() << _error << endl;
- uasserted(16711, _error);
- }
-
- if (timeoutMs)
- // start the deadline timer for this script
- _engine->getDeadlineMonitor()->startDeadline(this, timeoutMs);
-
- result = ((v8::Function*)(*funcValue))->Call(v8recv, nargs, nargs ? args : NULL);
-
- if (timeoutMs)
- // stop the deadline timer for this script
- _engine->getDeadlineMonitor()->stopDeadline(this);
-
- if (!nativePrologue()) {
- _error = "JavaScript execution terminated";
- error() << _error << endl;
- uasserted(16712, _error);
- }
-
- // throw on error
- checkV8ErrorState(result, try_catch);
-
- if (!ignoreReturn) {
- v8::Handle<v8::Object> resultObject = result->ToObject();
- // must validate the handle because TerminateExecution may have
- // been thrown after the above checks
- if (!resultObject.IsEmpty() && resultObject->Has(strLitToV8("_v8_function"))) {
- log() << "storing native function as return value" << endl;
- _lastRetIsNativeCode = true;
- } else {
- _lastRetIsNativeCode = false;
- }
- _global->ForceSet(strLitToV8("__returnValue"), result);
- }
-
- return 0;
-}
-
-bool V8Scope::exec(StringData code,
- const string& name,
- bool printResult,
- bool reportError,
- bool assertOnError,
- int timeoutMs) {
- V8_SIMPLE_HEADER
- v8::TryCatch try_catch;
-
- v8::Handle<v8::Script> script = v8::Script::Compile(
- v8::String::New(code.rawData(), code.size()), v8::String::New(name.c_str(), name.length()));
-
- if (checkV8ErrorState(script, try_catch, reportError, assertOnError))
- return false;
-
- if (!nativeEpilogue()) {
- _error = "JavaScript execution terminated";
- if (reportError)
- error() << _error << endl;
- if (assertOnError)
- uasserted(13475, _error);
- return false;
- }
-
- if (timeoutMs)
- // start the deadline timer for this script
- _engine->getDeadlineMonitor()->startDeadline(this, timeoutMs);
-
- v8::Handle<v8::Value> result = script->Run();
-
- if (timeoutMs)
- // stopt the deadline timer for this script
- _engine->getDeadlineMonitor()->stopDeadline(this);
-
- if (!nativePrologue()) {
- _error = "JavaScript execution terminated";
- if (reportError)
- error() << _error << endl;
- if (assertOnError)
- uasserted(16721, _error);
- return false;
- }
-
- if (checkV8ErrorState(result, try_catch, reportError, assertOnError))
- return false;
-
- _global->ForceSet(strLitToV8("__lastres__"), result);
-
- if (printResult && !result->IsUndefined()) {
- // appears to only be used by shell
- cout << V8String(result) << endl;
- }
-
- return true;
-}
-
-void V8Scope::injectNative(const char* field, NativeFunction func, void* data) {
- V8_SIMPLE_HEADER // required due to public access
- injectNative(field, func, _global, data);
-}
-
-void V8Scope::injectNative(const char* field,
- NativeFunction nativeFunc,
- v8::Handle<v8::Object>& obj,
- void* data) {
- v8::Handle<v8::FunctionTemplate> ft = createV8Function(nativeCallback);
- 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) {
- return injectV8Function(field, func, _global);
-}
-
-v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Function(const char* field,
- v8Function func,
- v8::Handle<v8::Object>& obj) {
- return injectV8Function(field, createV8Function(func), obj);
-}
-
-v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Function(const char* fieldCStr,
- v8::Handle<v8::FunctionTemplate> ft,
- v8::Handle<v8::Object>& obj) {
- v8::Handle<v8::String> field = v8StringData(fieldCStr);
- ft->SetClassName(field);
- v8::Handle<v8::Function> func = ft->GetFunction();
- func->SetName(field);
- obj->ForceSet(field, func);
- return ft;
-}
-
-v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Method(const char* fieldCStr,
- v8Function func,
- v8::Handle<v8::ObjectTemplate>& proto) {
- v8::Handle<v8::String> field = v8StringData(fieldCStr);
- v8::Handle<v8::FunctionTemplate> ft = createV8Function(func);
- v8::Handle<v8::Function> f = ft->GetFunction();
- f->SetName(field);
- proto->Set(field, f);
- return ft;
-}
-
-v8::Handle<v8::FunctionTemplate> V8Scope::createV8Function(v8Function 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;
-}
-
-void V8Scope::gc() {
- V8_SIMPLE_HEADER
- // trigger low memory notification. for more granular control over garbage
- // collection cycle, @see v8::V8::IdleNotification.
- v8::V8::LowMemoryNotification();
-}
-
-void V8Scope::localConnectForDbEval(OperationContext* txn, const char* dbName) {
- typedef v8::Persistent<v8::FunctionTemplate> FTPtr;
- {
- V8_SIMPLE_HEADER;
-
- invariant(_opCtx == NULL);
- _opCtx = txn;
-
- if (_connectState == EXTERNAL)
- uasserted(12510, "externalSetup already called, can't call localConnect");
- if (_connectState == LOCAL) {
- if (_localDBName == dbName)
- return;
- uasserted(12511,
- str::stream() << "localConnect previously called with name " << _localDBName);
- }
-
- // NOTE: order is important here. the following methods must be called after
- // the above conditional statements.
-
- // install db access functions in the global object
- installDBAccess();
-
- // install the Mongo function object and instantiate the 'db' global
- _MongoFT = FTPtr::New(getMongoFunctionTemplate(this, true));
- injectV8Function("Mongo", MongoFT(), _global);
- 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(txn);
-}
-
-void V8Scope::externalSetup() {
- typedef v8::Persistent<v8::FunctionTemplate> FTPtr;
- V8_SIMPLE_HEADER
- if (_connectState == EXTERNAL)
- return;
- if (_connectState == LOCAL)
- uasserted(12512, "localConnect already called, can't call externalSetup");
-
- // install db access functions in the global object
- installDBAccess();
-
- // install thread-related functions (e.g. _threadInject)
- installFork(this, _global, _context);
-
- // install 'load' helper function
- injectV8Function("load", load);
-
- // install the Mongo function object
- _MongoFT = FTPtr::New(getMongoFunctionTemplate(this, false));
- injectV8Function("Mongo", MongoFT(), _global);
- execCoreFiles();
- _connectState = EXTERNAL;
-}
-
-void V8Scope::installDBAccess() {
- typedef v8::Persistent<v8::FunctionTemplate> FTPtr;
- _DBFT = FTPtr::New(createV8Function(dbInit));
- _DBQueryFT = FTPtr::New(createV8Function(dbQueryInit));
- _DBCollectionFT = FTPtr::New(createV8Function(collectionInit));
-
- // These must be done before calling injectV8Function
- DBFT()->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, collectionSetter);
- DBQueryFT()->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess);
- DBCollectionFT()->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter,
- collectionSetter);
-
- injectV8Function("DB", DBFT(), _global);
- injectV8Function("DBQuery", DBQueryFT(), _global);
- injectV8Function("DBCollection", DBCollectionFT(), _global);
-
- // The internal cursor type isn't exposed to the users at all
- _InternalCursorFT = FTPtr::New(getInternalCursorFunctionTemplate(this));
-}
-
-void V8Scope::installBSONTypes() {
- typedef v8::Persistent<v8::FunctionTemplate> FTPtr;
- _ObjectIdFT = FTPtr::New(injectV8Function("ObjectId", objectIdInit));
- _DBRefFT = FTPtr::New(injectV8Function("DBRef", dbRefInit));
- _DBPointerFT = FTPtr::New(injectV8Function("DBPointer", dbPointerInit));
-
- _BinDataFT = FTPtr::New(getBinDataFunctionTemplate(this));
- _NumberLongFT = FTPtr::New(getNumberLongFunctionTemplate(this));
- _NumberIntFT = FTPtr::New(getNumberIntFunctionTemplate(this));
- _NumberDecimalFT = FTPtr::New(getNumberDecimalFunctionTemplate(this));
- _TimestampFT = FTPtr::New(getTimestampFunctionTemplate(this));
- _MinKeyFT = FTPtr::New(getMinKeyFunctionTemplate(this));
- _MaxKeyFT = FTPtr::New(getMaxKeyFunctionTemplate(this));
-
- injectV8Function("BinData", BinDataFT(), _global);
- injectV8Function("NumberLong", NumberLongFT(), _global);
- injectV8Function("NumberInt", NumberIntFT(), _global);
- if (Decimal128::enabled) {
- injectV8Function("NumberDecimal", NumberDecimalFT(), _global);
- }
- injectV8Function("Timestamp", TimestampFT(), _global);
-
- // These are instances created from the functions, not the functions themselves
- _global->ForceSet(strLitToV8("MinKey"), MinKeyFT()->GetFunction()->NewInstance());
- _global->ForceSet(strLitToV8("MaxKey"), MaxKeyFT()->GetFunction()->NewInstance());
-
- // These all create BinData objects so we don't need to hold on to them.
- injectV8Function("UUID", uuidInit);
- 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());
-}
-
-
-// ----- internal -----
-
-void V8Scope::reset() {
- V8_SIMPLE_HEADER
- unregisterOperation();
- _error = "";
- _pendingKill = false;
- _inNativeExecution = true;
-}
-
-v8::Local<v8::Value> V8Scope::newFunction(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(), codeStr.length()));
-
- // throw on compile error
- checkV8ErrorState(compiled, try_catch);
-
- v8::Local<v8::Value> ret = compiled->Run();
-
- // throw on run/assignment error
- checkV8ErrorState(ret, try_catch);
-
- return handle_scope.Close(ret);
-}
-
-v8::Local<v8::Value> V8Scope::newId(const OID& id) {
- v8::HandleScope handle_scope;
- v8::Handle<v8::Function> idCons = ObjectIdFT()->GetFunction();
- v8::Handle<v8::Value> argv[1];
- const string& idString = id.toString();
- argv[0] = v8::String::New(idString.c_str(), idString.length());
- return handle_scope.Close(idCons->NewInstance(1, argv));
-}
-
-/**
- * converts a BSONObj to a Lazy V8 object
- */
-v8::Handle<v8::Object> V8Scope::mongoToLZV8(const BSONObj& m, bool readOnly) {
- if (m.firstElementType() == String && str::equals(m.firstElementFieldName(), "$ref")) {
- BSONObjIterator it(m);
- const BSONElement ref = it.next();
- const BSONElement id = it.next();
- if (id.ok() && str::equals(id.fieldName(), "$id")) {
- v8::Handle<v8::Value> args[] = {mongoToV8Element(ref, readOnly),
- mongoToV8Element(id, readOnly)};
- v8::Local<v8::Object> dbRef = DBRefFT()->GetFunction()->NewInstance(2, args);
- while (it.more()) {
- BSONElement elem = it.next();
- dbRef->Set(v8StringData(elem.fieldName()), mongoToV8Element(elem, readOnly));
- }
- return dbRef;
- }
- }
-
- v8::Handle<v8::FunctionTemplate> templ = readOnly ? ROBsonFT() : LazyBsonFT();
- v8::Handle<v8::Object> o = templ->GetFunction()->NewInstance();
- massert(16496,
- str::stream() << "V8: NULL Object template instantiated. "
- << (v8::V8::IsExecutionTerminating() ? "v8 execution is terminating."
- : "v8 still executing."),
- *o != NULL);
-
- wrapBSONObject(o, m, readOnly);
- return o;
-}
-
-v8::Handle<v8::Value> V8Scope::mongoToV8Element(const BSONElement& elem, bool readOnly) {
- v8::Handle<v8::Value> argv[3]; // arguments for v8 instance constructors
- v8::Local<v8::Object> instance; // instance of v8 type
- uint64_t nativeUnsignedLong; // native representation of NumberLong
- Decimal128 nativeDecimal; // native representation of NumberDecimal
-
-
- switch (elem.type()) {
- case mongo::Code:
- return newFunction(elem.valueStringData());
- case CodeWScope:
- if (!elem.codeWScopeObject().isEmpty())
- warning() << "CodeWScope doesn't transfer to db.eval" << endl;
- return newFunction(StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1));
- case mongo::Symbol:
- case mongo::String:
- return v8::String::New(elem.valuestr(), elem.valuestrsize() - 1);
- case mongo::jstOID:
- return newId(elem.__oid());
- case mongo::NumberDouble:
- case mongo::NumberInt:
- return v8::Number::New(elem.number());
- case mongo::Array: {
- // NB: This comment may no longer be accurate.
- // 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
-
- // It is faster to allow the v8::Array to grow than call nFields() on the array
- v8::Handle<v8::Array> array = v8::Array::New();
- int i = 0;
- BSONForEach(subElem, elem.embeddedObject()) {
- array->Set(i++, mongoToV8Element(subElem, readOnly));
- }
- return array;
- }
- case mongo::Object:
- return mongoToLZV8(elem.embeddedObject(), readOnly);
- case mongo::Date:
- return v8::Date::New(static_cast<double>(elem.date().toMillisSinceEpoch()));
- case mongo::Bool:
- return v8::Boolean::New(elem.boolean());
- case mongo::EOO:
- case mongo::jstNULL:
- case mongo::Undefined: // duplicate sm behavior
- return v8::Null();
- case mongo::RegEx: {
- // TODO parse into a custom type that can support any patterns and flags SERVER-9803
- v8::TryCatch tryCatch;
-
- v8::Handle<v8::Value> args[] = {v8::String::New(elem.regex()),
- v8::String::New(elem.regexFlags())};
-
- v8::Handle<v8::Value> ret = _jsRegExpConstructor->NewInstance(2, args);
- uassert(16863,
- str::stream() << "Error converting " << elem.toString(false) << " in field "
- << elem.fieldName() << " to a JS RegExp object: "
- << toSTLString(tryCatch.Exception()),
- !tryCatch.HasCaught());
-
- return ret;
- }
- case mongo::BinData: {
- int len;
- const char* data = elem.binData(len);
- stringstream ss;
- base64::encode(ss, data, len);
- argv[0] = v8::Number::New(elem.binDataType());
- argv[1] = v8::String::New(ss.str().c_str());
- return BinDataFT()->GetFunction()->NewInstance(2, argv);
- }
- case mongo::bsonTimestamp: {
- v8::TryCatch tryCatch;
-
- argv[0] = v8::Number::New(elem.timestampTime().toMillisSinceEpoch() / 1000);
- argv[1] = v8::Number::New(elem.timestampInc());
-
- 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
- if ((long long)nativeUnsignedLong ==
- (long long)(double)(long long)(nativeUnsignedLong) &&
- nativeUnsignedLong < 9007199254740992ULL) {
- argv[0] = v8::Number::New((double)(long long)(nativeUnsignedLong));
- return NumberLongFT()->GetFunction()->NewInstance(1, argv);
- } else {
- argv[0] = v8::Number::New((double)(long long)(nativeUnsignedLong));
- argv[1] = v8::Integer::New(nativeUnsignedLong >> 32);
- argv[2] =
- v8::Integer::New((unsigned long)(nativeUnsignedLong & 0x00000000ffffffff));
- return NumberLongFT()->GetFunction()->NewInstance(3, argv);
- }
- case mongo::NumberDecimal: {
- nativeDecimal = elem.numberDecimal();
- // Store number decimals as strings
- // Note: This prevents shell arithmetic, which is performed for number longs
- // by converting them to doubles, which is imprecise. Until there is a better
- // method to handle non-double shell arithmetic, decimals will remain
- // as a non-numeric js type.
- std::string decString = nativeDecimal.toString();
- argv[0] = v8::String::New(decString.c_str());
- return NumberDecimalFT()->GetFunction()->NewInstance(1, argv);
- }
- case mongo::MinKey:
- return MinKeyFT()->GetFunction()->NewInstance();
- case mongo::MaxKey:
- return MaxKeyFT()->GetFunction()->NewInstance();
- case mongo::DBRef:
- argv[0] = v8StringData(elem.dbrefNS());
- argv[1] = newId(elem.dbrefOID());
- return DBPointerFT()->GetFunction()->NewInstance(2, argv);
- default:
- massert(16661,
- str::stream() << "can't handle type: " << elem.type() << " " << elem.toString(),
- false);
- break;
- }
- return v8::Undefined();
-}
-
-void V8Scope::v8ToMongoNumber(BSONObjBuilder& b,
- StringData elementName,
- v8::Handle<v8::Number> value,
- BSONObj* originalParent) {
- double val = value->Value();
- // if previous type was integer, keep it
- int intval = static_cast<int>(val);
- if (val == intval && originalParent) {
- // This makes copying an object of numbers O(n**2) :(
- BSONElement elmt = originalParent->getField(elementName);
- if (elmt.type() == mongo::NumberInt) {
- b.append(elementName, intval);
- return;
- }
- }
- b.append(elementName, val);
-}
-
-void V8Scope::v8ToMongoRegex(BSONObjBuilder& b,
- StringData elementName,
- v8::Handle<v8::RegExp> v8Regex) {
- V8String v8RegexString(v8Regex);
- StringData regex = v8RegexString;
- regex = regex.substr(1);
- StringData r = regex.substr(0, regex.rfind('/'));
- StringData o = regex.substr(regex.rfind('/') + 1);
- b.appendRegex(elementName, r, o);
-}
-
-void V8Scope::v8ToMongoDBRef(BSONObjBuilder& b,
- StringData elementName,
- v8::Handle<v8::Object> obj) {
- verify(DBPointerFT()->HasInstance(obj));
- v8::Local<v8::Value> theid = obj->Get(strLitToV8("id"));
- OID oid = v8ToMongoObjectID(theid->ToObject());
- string ns = toSTLString(obj->Get(strLitToV8("ns")));
- b.appendDBRef(elementName, ns, oid);
-}
-
-void V8Scope::v8ToMongoBinData(BSONObjBuilder& b,
- StringData elementName,
- v8::Handle<v8::Object> obj) {
- verify(BinDataFT()->HasInstance(obj));
- verify(obj->InternalFieldCount() == 1);
- std::string binData(base64::decode(toSTLString(obj->GetInternalField(0))));
- b.appendBinData(elementName,
- binData.size(),
- mongo::BinDataType(obj->Get(strLitToV8("type"))->ToInt32()->Value()),
- binData.c_str());
-}
-
-OID V8Scope::v8ToMongoObjectID(v8::Handle<v8::Object> obj) {
- verify(ObjectIdFT()->HasInstance(obj));
- const string hexStr = toSTLString(obj->Get(strLitToV8("str")));
-
- // OID parser doesn't have user-friendly error messages
- uassert(16864, "ObjectID.str must be exactly 24 chars long", hexStr.size() == 24);
- uassert(16865,
- "ObjectID.str must only have hex characters [0-1a-fA-F]",
- count_if(hexStr.begin(), hexStr.end(), ::isxdigit) == 24);
-
- return OID(hexStr);
-}
-
-void V8Scope::v8ToMongoObject(BSONObjBuilder& b,
- StringData elementName,
- v8::Handle<v8::Value> value,
- int depth,
- BSONObj* originalParent) {
- verify(value->IsObject());
- v8::Handle<v8::Object> obj = value.As<v8::Object>();
-
- if (value->IsRegExp()) {
- v8ToMongoRegex(b, elementName, obj.As<v8::RegExp>());
- } else if (ObjectIdFT()->HasInstance(value)) {
- b.append(elementName, v8ToMongoObjectID(obj));
- } else if (NumberLongFT()->HasInstance(value)) {
- b.append(elementName, numberLongVal(this, obj));
- } else if (NumberIntFT()->HasInstance(value)) {
- b.append(elementName, numberIntVal(this, obj));
- } else if (NumberDecimalFT()->HasInstance(value)) {
- b.append(elementName, numberDecimalVal(this, obj));
- } else if (DBPointerFT()->HasInstance(value)) {
- v8ToMongoDBRef(b, elementName, obj);
- } else if (BinDataFT()->HasInstance(value)) {
- v8ToMongoBinData(b, elementName, obj);
- } else if (TimestampFT()->HasInstance(value)) {
- Timestamp ot(obj->Get(strLitToV8("t"))->Uint32Value(),
- obj->Get(strLitToV8("i"))->Uint32Value());
- b.append(elementName, ot);
- } else if (MinKeyFT()->HasInstance(value)) {
- b.appendMinKey(elementName);
- } else if (MaxKeyFT()->HasInstance(value)) {
- b.appendMaxKey(elementName);
- } else {
- // nested object or array
- BSONObj sub = v8ToMongo(obj, depth);
- b.append(elementName, sub);
- }
-}
-
-void V8Scope::v8ToMongoElement(BSONObjBuilder& b,
- 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,
- str::stream() << "JavaScript property (name) contains a null char "
- << "which is not allowed in BSON. " << originalParent->jsonString(),
- (string::npos == sname.find('\0')));
-
- if (value->IsString()) {
- b.append(sname, V8String(value));
- return;
- }
- if (value->IsFunction()) {
- uassert(16716,
- "cannot convert native function to BSON",
- !value->ToObject()->Has(strLitToV8("_v8_function")));
- b.appendCode(sname, V8String(value));
- return;
- }
- if (value->IsNumber()) {
- v8ToMongoNumber(b, sname, value.As<v8::Number>(), originalParent);
- return;
- }
- if (value->IsArray()) {
- // Note: can't use BSONArrayBuilder because need to call recursively
- BSONObjBuilder arrBuilder(b.subarrayStart(sname));
- v8::Handle<v8::Array> array = value.As<v8::Array>();
- const int len = array->Length();
- for (int i = 0; i < len; i++) {
- const string name = BSONObjBuilder::numStr(i);
- v8ToMongoElement(arrBuilder, name, array->Get(i), depth + 1, originalParent);
- }
- return;
- }
- if (value->IsDate()) {
- long long dateval = (long long)(v8::Date::Cast(*value)->NumberValue());
- b.appendDate(sname, Date_t::fromMillisSinceEpoch(dateval));
- return;
- }
- if (value->IsExternal())
- return;
- if (value->IsObject()) {
- v8ToMongoObject(b, sname, value, depth, originalParent);
- 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;
- }
- uasserted(16662,
- str::stream() << "unable to convert JavaScript property to mongo element " << sname);
-}
-
-BSONObj V8Scope::v8ToMongo(v8::Handle<v8::Object> o, int depth) {
- BSONObj originalBSON;
- if (LazyBsonFT()->HasInstance(o)) {
- originalBSON = unwrapBSONObj(this, o);
- BSONHolder* holder = unwrapHolder(this, o);
- if (holder && !holder->_modified) {
- // object was not modified, use bson as is
- return originalBSON;
- }
- }
-
- BSONObjBuilder b;
-
- // We special case the _id field in top-level objects and move it to the front.
- // This matches other drivers behavior and makes finding the _id field quicker in BSON.
- if (depth == 0) {
- if (o->HasOwnProperty(strLitToV8("_id"))) {
- v8ToMongoElement(b, "_id", o->Get(strLitToV8("_id")), 0, &originalBSON);
- }
- }
-
- v8::Local<v8::Array> names = o->GetOwnPropertyNames();
-
- for (unsigned int i = 0; i < names->Length(); i++) {
- v8::Local<v8::String> name = names->Get(i)->ToString();
-
- if (depth == 0 && name->StrictEquals(strLitToV8("_id")))
- continue; // already handled above
-
- V8String sname(name);
- v8::Local<v8::Value> value = o->Get(name);
- v8ToMongoElement(b, sname, value, depth + 1, &originalBSON);
- }
-
- 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) {
- 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++) {
- if (first)
- first = false;
- else
- ss << " ";
-
- if (args[i].IsEmpty()) {
- // failed to get object to convert
- ss << "[unknown type]";
- continue;
- }
- if (args[i]->IsExternal()) {
- // object is External
- ss << "[mongo internal]";
- continue;
- }
-
- v8::String::Utf8Value str(args[i]);
- ss << *str;
- }
- ss << "\n";
- return handle_scope.Close(v8::Undefined());
-}
-
-v8::Handle<v8::Value> V8Scope::Version(V8Scope* scope, const v8::Arguments& args) {
- v8::HandleScope handle_scope;
- return handle_scope.Close(v8::String::New(v8::V8::GetVersion()));
-}
-
-v8::Handle<v8::Value> V8Scope::GCV8(V8Scope* scope, const v8::Arguments& args) {
- // trigger low memory notification. for more granular control over garbage
- // collection cycle, @see v8::V8::IdleNotification.
- v8::V8::LowMemoryNotification();
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> V8Scope::startCpuProfiler(V8Scope* scope, const v8::Arguments& args) {
- if (args.Length() != 1 || !args[0]->IsString()) {
- return v8AssertionException("startCpuProfiler takes a string argument");
- }
- scope->_cpuProfiler.start(*v8::String::Utf8Value(args[0]->ToString()));
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> V8Scope::stopCpuProfiler(V8Scope* scope, const v8::Arguments& args) {
- if (args.Length() != 1 || !args[0]->IsString()) {
- return v8AssertionException("stopCpuProfiler takes a string argument");
- }
- scope->_cpuProfiler.stop(*v8::String::Utf8Value(args[0]->ToString()));
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> V8Scope::getCpuProfile(V8Scope* scope, const v8::Arguments& args) {
- if (args.Length() != 1 || !args[0]->IsString()) {
- return v8AssertionException("getCpuProfile takes a string argument");
- }
- return scope->mongoToLZV8(
- scope->_cpuProfiler.fetch(*v8::String::Utf8Value(args[0]->ToString())));
-}
-
-/**
- * Check for an error condition (e.g. empty handle, JS exception, OOM) after executing
- * a v8 operation.
- * @resultHandle handle storing the result of the preceding v8 operation
- * @try_catch the active v8::TryCatch exception handler
- * @param reportError if true, log an error message
- * @param assertOnError if true, throw an exception if an error is detected
- * if false, return value indicates error state
- * @return true if an error was detected and assertOnError is set to false
- * false if no error was detected
- */
-template <typename _HandleType>
-bool V8Scope::checkV8ErrorState(const _HandleType& resultHandle,
- const v8::TryCatch& try_catch,
- bool reportError,
- bool assertOnError) {
- bool haveError = false;
-
- if (try_catch.HasCaught() && try_catch.CanContinue()) {
- // normal JS exception
- _error = v8ExceptionToSTLString(&try_catch);
- haveError = true;
- } else if (hasOutOfMemoryException()) {
- // out of memory exception (treated as terminal)
- _error = "JavaScript execution failed -- v8 is out of memory";
- haveError = true;
- } else if (resultHandle.IsEmpty() || try_catch.HasCaught()) {
- // terminal exception (due to empty handle, termination, etc.)
- _error = "JavaScript execution failed";
- haveError = true;
- }
-
- if (haveError) {
- if (reportError)
- error() << _error << std::endl;
- if (assertOnError)
- uasserted(16722, _error);
- return true;
- }
-
- return false;
-}
-
-MONGO_INITIALIZER(JavascriptPrintDomain)(InitializerContext*) {
- jsPrintLogDomain = logger::globalLogManager()->getNamedDomain("javascriptOutput");
- return Status::OK();
-}
-
-} // namespace mongo
diff --git a/src/mongo/scripting/engine_v8.h b/src/mongo/scripting/engine_v8.h
deleted file mode 100644
index 2cf5b218e3c..00000000000
--- a/src/mongo/scripting/engine_v8.h
+++ /dev/null
@@ -1,629 +0,0 @@
-// engine_v8.h
-
-/* Copyright 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <v8.h>
-#include <vector>
-
-#include "mongo/base/disallow_copying.h"
-#include "mongo/base/string_data.h"
-#include "mongo/client/dbclientinterface.h"
-#include "mongo/client/dbclientcursor.h"
-#include "mongo/platform/unordered_map.h"
-#include "mongo/scripting/engine.h"
-#include "mongo/scripting/deadline_monitor.h"
-#include "mongo/scripting/v8_profiler.h"
-
-/**
- * V8_SIMPLE_HEADER must be placed in any function called from a public API
- * that work with v8 handles (and/or must be within the V8Scope's isolate
- * and context). Be sure to close the handle_scope if returning a v8::Handle!
- */
-#define V8_SIMPLE_HEADER \
- v8::Locker v8lock(_isolate); /* acquire isolate lock */ \
- v8::Isolate::Scope iscope(_isolate); /* enter the isolate; exit when out of scope */ \
- v8::HandleScope handle_scope; /* make the current scope own local handles */ \
- v8::Context::Scope context_scope(_context); /* enter the context; exit when out of scope */
-
-namespace mongo {
-
-class V8ScriptEngine;
-class V8Scope;
-class BSONHolder;
-class JSThreadConfig;
-
-typedef v8::Handle<v8::Value>(*v8Function)(V8Scope* scope, const v8::Arguments& args);
-
-/**
- * The ObjTracker class keeps track of all weakly referenced v8 objects. This is
- * required because v8 does not invoke the WeakReferenceCallback when shutting down
- * the context/isolate. To track a new object, add an ObjTracker<MyObjType> member
- * variable to the V8Scope (if one does not already exist for that type). Instead
- * of calling v8::Persistent::MakeWeak() directly, simply invoke track() with the
- * persistent handle and the pointer to be freed.
- */
-template <typename _ObjType>
-class ObjTracker {
-public:
- /** Track an object to be freed when it is no longer referenced in JavaScript.
- * Return handle to object instance shared pointer.
- * @param instanceHandle persistent handle to the weakly referenced object
- * @param rawData pointer to the object instance
- */
- v8::Local<v8::External> track(v8::Persistent<v8::Value> instanceHandle, _ObjType* instance) {
- TrackedPtr* collectionHandle = new TrackedPtr(instance, this);
- _container.insert(collectionHandle);
- instanceHandle.MakeWeak(collectionHandle, deleteOnCollect);
- return v8::External::New(&(collectionHandle->_objPtr));
- }
- /**
- * Free any remaining objects and their TrackedPtrs. Invoked when the
- * V8Scope is destructed.
- */
- ~ObjTracker() {
- typename std::set<TrackedPtr*>::iterator it = _container.begin();
- while (it != _container.end()) {
- delete *it;
- _container.erase(it++);
- }
- }
-
-private:
- /**
- * Simple struct which contains a pointer to the tracked object, and a pointer
- * to the ObjTracker which owns it. This is the argument supplied to v8's
- * WeakReferenceCallback and MakeWeak().
- */
- struct TrackedPtr {
- public:
- TrackedPtr(_ObjType* instance, ObjTracker<_ObjType>* tracker)
- : _objPtr(instance), _tracker(tracker) {}
- std::shared_ptr<_ObjType> _objPtr;
- ObjTracker<_ObjType>* _tracker;
- };
-
- /**
- * v8 callback for weak persistent handles that have been marked for removal by the
- * garbage collector. Signature conforms to v8's WeakReferenceCallback.
- * @param instanceHandle persistent handle to the weakly referenced object
- * @param rawData pointer to the TrackedPtr instance
- */
- static void deleteOnCollect(v8::Persistent<v8::Value> instanceHandle, void* rawData) {
- TrackedPtr* trackedPtr = static_cast<TrackedPtr*>(rawData);
- trackedPtr->_tracker->_container.erase(trackedPtr);
- delete trackedPtr;
- instanceHandle.Dispose();
- }
-
- // container for all TrackedPtrs created by this ObjTracker instance
- std::set<TrackedPtr*> _container;
-};
-
-/**
- * A V8Scope represents a unit of javascript execution environment; specifically a single
- * isolate and a single context executing in a single mongo thread. A V8Scope can be reused
- * in another thread only after reset() has been called.
- *
- * NB:
- * - v8 objects/handles/etc. cannot be shared between V8Scopes
- * - in mongod, each scope is associated with an opId (for KillOp support)
- * - any public functions that call the v8 API should use a V8_SIMPLE_HEADER
- * - the caller of any public function that returns a v8 type or has a v8 handle argument
- * must enter the isolate, context, and set up the appropriate handle scope
- */
-class V8Scope : public Scope {
-public:
- V8Scope(V8ScriptEngine* engine);
- ~V8Scope();
-
- virtual void init(const BSONObj* data);
-
- /**
- * Reset the state of this scope for use by another thread or operation
- */
- virtual void reset();
-
- /**
- * Terminate this scope
- */
- virtual void kill();
-
- /** check if there is a pending killOp request */
- bool isKillPending() const;
-
- /**
- * Register this scope with the mongo op id. If executing outside the
- * context of a mongo operation (e.g. from the shell), killOp will not
- * be supported.
- */
- virtual void registerOperation(OperationContext* txn);
-
- /**
- * Unregister this scope with the mongo op id.
- */
- virtual void unregisterOperation();
-
- /**
- * Obtains the operation context associated with this Scope, so it can be given to the
- * DBDirectClient used by the V8 engine's connection. Only needed for dbEval.
- */
- OperationContext* getOpContext() const;
-
- /**
- * Connect to a local database, create a Mongo object instance, and load any
- * server-side js into the global object
- */
- virtual void localConnectForDbEval(OperationContext* txn, const char* dbName);
-
- virtual void externalSetup();
-
- virtual void installDBAccess();
-
- virtual void installBSONTypes();
-
- virtual std::string getError() {
- return _error;
- }
-
- virtual bool hasOutOfMemoryException();
-
- /**
- * Run the garbage collector on this scope (native function). @see GCV8 for the
- * javascript binding version.
- */
- void gc();
-
- /**
- * get a global property. caller must set up the v8 state.
- */
- v8::Handle<v8::Value> get(const char* field);
-
- virtual double getNumber(const char* field);
- virtual int getNumberInt(const char* field);
- virtual long long getNumberLongLong(const char* field);
- virtual Decimal128 getNumberDecimal(const char* field);
- virtual std::string getString(const char* field);
- virtual bool getBoolean(const char* field);
- virtual BSONObj getObject(const char* field);
-
- virtual void setNumber(const char* field, double val);
- virtual void setString(const char* field, StringData 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 int type(const char* field);
-
- virtual void rename(const char* from, const char* to);
-
- 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(StringData code,
- const std::string& name,
- bool printResult,
- bool reportError,
- bool assertOnError,
- int timeoutMs);
-
- // functions to create v8 object and function templates
- virtual void injectNative(const char* field, NativeFunction func, void* data = 0);
- void injectNative(const char* field,
- NativeFunction func,
- v8::Handle<v8::Object>& obj,
- void* data = 0);
-
- // These functions inject a function (either an unwrapped function pointer or a pre-wrapped
- // FunctionTemplate) into the provided object. If no object is provided, the function will
- // be injected at global scope. These functions take care of setting the function and class
- // name on the returned FunctionTemplate.
- v8::Handle<v8::FunctionTemplate> injectV8Function(const char* name, v8Function func);
- v8::Handle<v8::FunctionTemplate> injectV8Function(const char* name,
- v8Function func,
- v8::Handle<v8::Object>& obj);
- v8::Handle<v8::FunctionTemplate> injectV8Function(const char* name,
- v8::Handle<v8::FunctionTemplate> ft,
- v8::Handle<v8::Object>& obj);
-
- // Injects a method into the provided prototype
- v8::Handle<v8::FunctionTemplate> injectV8Method(const char* name,
- v8Function func,
- v8::Handle<v8::ObjectTemplate>& proto);
- v8::Handle<v8::FunctionTemplate> createV8Function(v8Function func);
- virtual ScriptingFunction _createFunction(const char* code,
- ScriptingFunction functionNumber = 0);
- v8::Local<v8::Function> __createFunction(const char* code,
- ScriptingFunction functionNumber = 0);
-
- /**
- * Convert BSON types to v8 Javascript types
- */
- v8::Handle<v8::Object> mongoToLZV8(const mongo::BSONObj& m, bool readOnly = false);
- v8::Handle<v8::Value> mongoToV8Element(const BSONElement& f, bool readOnly = false);
-
- /**
- * Convert v8 Javascript types to BSON types
- */
- mongo::BSONObj v8ToMongo(v8::Handle<v8::Object> obj, int depth = 0);
- void v8ToMongoElement(BSONObjBuilder& b,
- StringData sname,
- v8::Handle<v8::Value> value,
- int depth = 0,
- BSONObj* originalParent = 0);
- void v8ToMongoObject(BSONObjBuilder& b,
- StringData sname,
- v8::Handle<v8::Value> value,
- int depth,
- BSONObj* originalParent);
- void v8ToMongoNumber(BSONObjBuilder& b,
- StringData elementName,
- v8::Handle<v8::Number> value,
- BSONObj* originalParent);
- void v8ToMongoRegex(BSONObjBuilder& b, StringData elementName, v8::Handle<v8::RegExp> v8Regex);
- void v8ToMongoDBRef(BSONObjBuilder& b, StringData elementName, v8::Handle<v8::Object> obj);
- void v8ToMongoBinData(BSONObjBuilder& b, StringData elementName, v8::Handle<v8::Object> obj);
- OID v8ToMongoObjectID(v8::Handle<v8::Object> obj);
-
- v8::Local<v8::Value> newId(const OID& id);
-
- /**
- * Convert a JavaScript exception to a stl string. Requires
- * access to the V8Scope instance to report source context information.
- */
- std::string v8ExceptionToSTLString(const v8::TryCatch* try_catch);
-
- /**
- * Create a V8 std::string with a local handle
- */
- static inline v8::Handle<v8::String> v8StringData(StringData str) {
- return v8::String::New(str.rawData(), str.size());
- }
-
- /**
- * Get the isolate this scope belongs to (can be called from any thread, but v8 requires
- * the new thread enter the isolate and context. Only one thread can enter the isolate.
- */
- v8::Isolate* getIsolate() {
- return _isolate;
- }
-
- /**
- * Get the JS context this scope executes within.
- */
- v8::Persistent<v8::Context> getContext() {
- return _context;
- }
-
- /**
- * Get the global JS object
- */
- v8::Persistent<v8::Object> getGlobal() {
- return _global;
- }
-
- ObjTracker<BSONHolder> bsonHolderTracker;
- ObjTracker<DBClientBase> dbClientBaseTracker;
- // Track both cursor and connection.
- // This ensures the connection outlives the cursor.
- struct DBConnectionAndCursor {
- std::shared_ptr<DBClientBase> conn;
- std::shared_ptr<DBClientCursor> cursor;
- DBConnectionAndCursor(std::shared_ptr<DBClientBase> conn,
- std::shared_ptr<DBClientCursor> cursor)
- : conn(conn), cursor(cursor) {}
- };
- ObjTracker<DBConnectionAndCursor> dbConnectionAndCursor;
- ObjTracker<JSThreadConfig> jsThreadConfigTracker;
-
- // These are all named after the JS constructor name + FT
- v8::Handle<v8::FunctionTemplate> ObjectIdFT() const {
- return _ObjectIdFT;
- }
- v8::Handle<v8::FunctionTemplate> DBRefFT() const {
- return _DBRefFT;
- }
- v8::Handle<v8::FunctionTemplate> DBPointerFT() const {
- return _DBPointerFT;
- }
- v8::Handle<v8::FunctionTemplate> BinDataFT() const {
- return _BinDataFT;
- }
- v8::Handle<v8::FunctionTemplate> NumberLongFT() const {
- return _NumberLongFT;
- }
- v8::Handle<v8::FunctionTemplate> NumberIntFT() const {
- return _NumberIntFT;
- }
- v8::Handle<v8::FunctionTemplate> NumberDecimalFT() const {
- return _NumberDecimalFT;
- }
- v8::Handle<v8::FunctionTemplate> TimestampFT() const {
- return _TimestampFT;
- }
- v8::Handle<v8::FunctionTemplate> MinKeyFT() const {
- return _MinKeyFT;
- }
- v8::Handle<v8::FunctionTemplate> MaxKeyFT() const {
- return _MaxKeyFT;
- }
- v8::Handle<v8::FunctionTemplate> MongoFT() const {
- return _MongoFT;
- }
- v8::Handle<v8::FunctionTemplate> DBFT() const {
- return _DBFT;
- }
- v8::Handle<v8::FunctionTemplate> DBCollectionFT() const {
- return _DBCollectionFT;
- }
- v8::Handle<v8::FunctionTemplate> DBQueryFT() const {
- return _DBQueryFT;
- }
- v8::Handle<v8::FunctionTemplate> InternalCursorFT() const {
- return _InternalCursorFT;
- }
- v8::Handle<v8::FunctionTemplate> LazyBsonFT() const {
- return _LazyBsonFT;
- }
- v8::Handle<v8::FunctionTemplate> ROBsonFT() const {
- return _ROBsonFT;
- }
-
- template <size_t N>
- v8::Handle<v8::String> strLitToV8(const char(&str)[N]) {
- // Note that _strLitMap is keyed on std::string pointer not string
- // value. This is OK because each std::string literal has a constant
- // pointer for the program's lifetime. This works best if (but does
- // not require) the linker interns all std::string literals giving
- // identical strings used in different places the same pointer.
-
- StrLitMap::iterator it = _strLitMap.find(str);
- if (it != _strLitMap.end())
- return it->second;
-
- StringData sd(str, StringData::LiteralTag());
- v8::Handle<v8::String> v8Str = v8StringData(sd);
-
- // We never need to Dispose since this should last as long as V8Scope exists
- _strLitMap[str] = v8::Persistent<v8::String>::New(v8Str);
-
- return v8Str;
- }
-
-private:
- /**
- * Recursion limit when converting from JS objects to BSON.
- */
- static const int objectDepthLimit = 150;
-
- /**
- * Attach data to obj such that the data has the same lifetime as the Object obj points to.
- * obj must have been created by either LazyBsonFT or ROBsonFT.
- */
- void wrapBSONObject(v8::Handle<v8::Object> obj, BSONObj data, bool readOnly);
-
- /**
- * Trampoline to call a c++ function with a specific signature (V8Scope*, v8::Arguments&).
- * Handles interruption, exceptions, etc.
- */
- static v8::Handle<v8::Value> v8Callback(const v8::Arguments& args);
-
- /**
- * Interpreter agnostic 'Native Callback' trampoline. Note this is only called
- * from v8Callback().
- */
- static v8::Handle<v8::Value> nativeCallback(V8Scope* scope, const v8::Arguments& args);
-
- /**
- * v8-specific implementations of basic global functions
- */
- static v8::Handle<v8::Value> load(V8Scope* scope, const v8::Arguments& args);
- static v8::Handle<v8::Value> Print(V8Scope* scope, const v8::Arguments& args);
- static v8::Handle<v8::Value> Version(V8Scope* scope, const v8::Arguments& args);
- static v8::Handle<v8::Value> GCV8(V8Scope* scope, const v8::Arguments& args);
-
- static v8::Handle<v8::Value> startCpuProfiler(V8Scope* scope, const v8::Arguments& args);
- static v8::Handle<v8::Value> stopCpuProfiler(V8Scope* scope, const v8::Arguments& args);
- static v8::Handle<v8::Value> getCpuProfile(V8Scope* scope, const v8::Arguments& args);
-
- /** Signal that this scope has entered a native (C++) execution context.
- * @return false if execution has been interrupted
- */
- bool nativePrologue();
-
- /** Signal that this scope has completed native execution and is returning to v8.
- * @return false if execution has been interrupted
- */
- bool nativeEpilogue();
-
- /**
- * Create a new function; primarily used for BSON/V8 conversion.
- */
- v8::Local<v8::Value> newFunction(StringData code);
-
- template <typename _HandleType>
- bool checkV8ErrorState(const _HandleType& resultHandle,
- const v8::TryCatch& try_catch,
- bool reportError = true,
- bool assertOnError = true);
-
- V8ScriptEngine* _engine;
-
- v8::Persistent<v8::Context> _context;
- v8::Persistent<v8::Object> _global;
- std::string _error;
- std::vector<v8::Persistent<v8::Value>> _funcs;
-
- enum ConnectState { NOT, LOCAL, EXTERNAL };
- ConnectState _connectState;
-
- // These are all named after the JS constructor name + FT
- v8::Persistent<v8::FunctionTemplate> _ObjectIdFT;
- v8::Persistent<v8::FunctionTemplate> _DBRefFT;
- v8::Persistent<v8::FunctionTemplate> _DBPointerFT;
- v8::Persistent<v8::FunctionTemplate> _BinDataFT;
- v8::Persistent<v8::FunctionTemplate> _NumberLongFT;
- v8::Persistent<v8::FunctionTemplate> _NumberIntFT;
- v8::Persistent<v8::FunctionTemplate> _NumberDecimalFT;
- v8::Persistent<v8::FunctionTemplate> _TimestampFT;
- v8::Persistent<v8::FunctionTemplate> _MinKeyFT;
- v8::Persistent<v8::FunctionTemplate> _MaxKeyFT;
- v8::Persistent<v8::FunctionTemplate> _MongoFT;
- v8::Persistent<v8::FunctionTemplate> _DBFT;
- v8::Persistent<v8::FunctionTemplate> _DBCollectionFT;
- v8::Persistent<v8::FunctionTemplate> _DBQueryFT;
- v8::Persistent<v8::FunctionTemplate> _InternalCursorFT;
- v8::Persistent<v8::FunctionTemplate> _LazyBsonFT;
- v8::Persistent<v8::FunctionTemplate> _ROBsonFT;
-
- v8::Persistent<v8::Function> _jsRegExpConstructor;
-
- /// Like v8::Isolate* but calls Dispose() in destructor.
- class IsolateHolder {
- MONGO_DISALLOW_COPYING(IsolateHolder);
-
- public:
- IsolateHolder() : _isolate(NULL) {}
- ~IsolateHolder() {
- if (_isolate) {
- _isolate->Dispose();
- _isolate = NULL;
- }
- }
-
- void set(v8::Isolate* isolate) {
- fassert(17184, !_isolate);
- _isolate = isolate;
- }
-
- v8::Isolate* operator->() const {
- return _isolate;
- };
- operator v8::Isolate*() const {
- return _isolate;
- };
-
- private:
- v8::Isolate* _isolate;
- };
-
- IsolateHolder _isolate; // NOTE: this must be destructed before the ObjTrackers
-
- V8CpuProfiler _cpuProfiler;
-
- // See comments in strLitToV8
- typedef unordered_map<const char*, v8::Handle<v8::String>> StrLitMap;
- StrLitMap _strLitMap;
-
- stdx::mutex _interruptLock; // protects interruption-related flags
- bool _inNativeExecution; // protected by _interruptLock
- bool _pendingKill; // protected by _interruptLock
- unsigned int _opId; // op id for this scope
- OperationContext* _opCtx; // Op context for DbEval
-};
-
-/// Helper to extract V8Scope for an Isolate
-inline V8Scope* getScope(v8::Isolate* isolate) {
- return static_cast<V8Scope*>(isolate->GetData());
-}
-
-class V8ScriptEngine : public ScriptEngine {
-public:
- V8ScriptEngine();
- virtual ~V8ScriptEngine();
- virtual Scope* createScope() {
- return new V8Scope(this);
- }
- virtual void runTest() {}
- bool utf8Ok() const {
- return true;
- }
-
- /**
- * Interrupt a single active v8 execution context
- * NB: To interrupt a context, we must acquire the following locks (in order):
- * - mutex to protect the the map of all scopes (_globalInterruptLock)
- * - mutex to protect the scope that's being interrupted (_interruptLock)
- * The scope will be removed from the map upon destruction, and the op id
- * will be updated if the scope is ever reused from a pool.
- */
- virtual void interrupt(unsigned opId);
-
- /**
- * Interrupt all v8 contexts (and isolates). @see interrupt().
- */
- virtual void interruptAll();
-
-private:
- friend class V8Scope;
-
- std::string printKnownOps_inlock();
-
- /**
- * Get the deadline monitor instance for the v8 ScriptEngine
- */
- DeadlineMonitor<V8Scope>* getDeadlineMonitor() {
- return &_deadlineMonitor;
- }
-
- typedef std::map<unsigned, V8Scope*> OpIdToScopeMap;
- stdx::mutex _globalInterruptLock; // protects map of all operation ids -> scope
- OpIdToScopeMap _opToScopeMap; // map of mongo op ids to scopes (protected by
- // _globalInterruptLock).
- DeadlineMonitor<V8Scope> _deadlineMonitor;
-};
-
-class BSONHolder {
- MONGO_DISALLOW_COPYING(BSONHolder);
-
-public:
- explicit BSONHolder(BSONObj obj) : _scope(NULL), _obj(obj.getOwned()), _modified(false) {
- // give hint v8's GC
- v8::V8::AdjustAmountOfExternalAllocatedMemory(_obj.objsize());
- }
- ~BSONHolder() {
- if (_scope && _scope->getIsolate())
- // if v8 is still up, send hint to GC
- v8::V8::AdjustAmountOfExternalAllocatedMemory(-_obj.objsize());
- }
- V8Scope* _scope;
- BSONObj _obj;
- bool _modified;
- bool _readOnly;
- std::set<std::string> _removed;
-};
-
-extern ScriptEngine* globalScriptEngine;
-}
diff --git a/src/mongo/scripting/v8-3.25_db.cpp b/src/mongo/scripting/v8-3.25_db.cpp
deleted file mode 100644
index e4fd6d890c2..00000000000
--- a/src/mongo/scripting/v8-3.25_db.cpp
+++ /dev/null
@@ -1,1079 +0,0 @@
-// v8_db.cpp
-
-/* Copyright 2014 MongoDB 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/>.
- *
- * 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/v8-3.25_db.h"
-
-#include <iostream>
-#include <iomanip>
-
-#include "mongo/base/init.h"
-#include "mongo/client/sasl_client_authenticate.h"
-#include "mongo/db/namespace_string.h"
-#include "mongo/s/d_state.h"
-#include "mongo/scripting/engine_v8-3.25.h"
-#include "mongo/scripting/v8-3.25_utils.h"
-#include "mongo/util/assert_util.h"
-#include "mongo/util/base64.h"
-#include "mongo/util/hex.h"
-#include "mongo/util/text.h"
-
-using namespace std;
-using std::unique_ptr;
-using std::shared_ptr;
-
-namespace mongo {
-
-namespace {
-std::vector<V8FunctionPrototypeManipulatorFn> _mongoPrototypeManipulators;
-bool _mongoPrototypeManipulatorsFrozen = false;
-
-MONGO_INITIALIZER(V8MongoPrototypeManipulatorRegistry)(InitializerContext* context) {
- return Status::OK();
-}
-
-MONGO_INITIALIZER_WITH_PREREQUISITES(V8MongoPrototypeManipulatorRegistrationDone,
- ("V8MongoPrototypeManipulatorRegistry"))
-(InitializerContext* context) {
- _mongoPrototypeManipulatorsFrozen = true;
- return Status::OK();
-}
-
-} // namespace
-
-void v8RegisterMongoPrototypeManipulator(const V8FunctionPrototypeManipulatorFn& manipulator) {
- fassert(16467, !_mongoPrototypeManipulatorsFrozen);
- _mongoPrototypeManipulators.push_back(manipulator);
-}
-
-static v8::Local<v8::Value> newInstance(v8::Local<v8::Function> f,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- // need to translate arguments into an array
- v8::EscapableHandleScope handle_scope(args.GetIsolate());
- const int argc = args.Length();
- static const int MAX_ARGC = 24;
- uassert(16858, "Too many arguments. Max is 24", argc <= MAX_ARGC);
-
- // TODO SERVER-8016: properly allocate handles on the stack
- v8::Local<v8::Value> argv[MAX_ARGC];
- for (int i = 0; i < argc; ++i) {
- argv[i] = args[i];
- }
- return handle_scope.Escape(f->NewInstance(argc, argv));
-}
-
-v8::Local<v8::FunctionTemplate> getInternalCursorFunctionTemplate(V8Scope* scope) {
- v8::Local<v8::FunctionTemplate> ic = scope->createV8Function(internalCursorCons);
- ic->InstanceTemplate()->SetInternalFieldCount(1);
- v8::Local<v8::ObjectTemplate> icproto = ic->PrototypeTemplate();
- scope->injectV8Method("next", internalCursorNext, icproto);
- scope->injectV8Method("hasNext", internalCursorHasNext, icproto);
- scope->injectV8Method("objsLeftInBatch", internalCursorObjsLeftInBatch, icproto);
- scope->injectV8Method("readOnly", internalCursorReadOnly, icproto);
- return ic;
-}
-
-v8::Local<v8::FunctionTemplate> getMongoFunctionTemplate(V8Scope* scope, bool local) {
- v8::Local<v8::FunctionTemplate> mongo;
- if (local)
- mongo = scope->createV8Function(mongoConsLocal);
- else
- mongo = scope->createV8Function(mongoConsExternal);
- mongo->InstanceTemplate()->SetInternalFieldCount(1);
- v8::Local<v8::ObjectTemplate> proto = mongo->PrototypeTemplate();
- scope->injectV8Method("find", mongoFind, proto);
- scope->injectV8Method("insert", mongoInsert, proto);
- scope->injectV8Method("remove", mongoRemove, proto);
- scope->injectV8Method("update", mongoUpdate, proto);
- scope->injectV8Method("auth", mongoAuth, proto);
- scope->injectV8Method("logout", mongoLogout, proto);
- scope->injectV8Method("cursorFromId", mongoCursorFromId, proto);
-
- fassert(16468, _mongoPrototypeManipulatorsFrozen);
- for (size_t i = 0; i < _mongoPrototypeManipulators.size(); ++i)
- _mongoPrototypeManipulators[i](scope, mongo);
-
- return mongo;
-}
-
-
-v8::Local<v8::Value> mongoConsExternal(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- string host = "127.0.0.1";
- if (args.Length() > 0 && args[0]->IsString()) {
- v8::String::Utf8Value utf(args[0]);
- host = string(*utf);
- }
-
- // only allow function template to be used by a constructor
- uassert(16859, "Mongo function is only usable as a constructor", args.IsConstructCall());
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- auto statusWithHost = ConnectionString::parse(host);
- if (!status.isOK()) {
- return v8AssertionException(statusWithHost.getStatus().reason());
- }
-
- const ConnectionString cs(statusWithHost.getValue());
-
- string errmsg;
- DBClientBase* conn;
- conn = cs.connect(errmsg);
- if (!conn) {
- return v8AssertionException(errmsg);
- }
-
- v8::Local<v8::External> connHandle =
- scope->dbClientBaseTracker.track(scope->getIsolate(), args.This(), conn);
-
- ScriptEngine::runConnectCallback(*conn);
-
- args.This()->SetInternalField(0, connHandle);
- args.This()->ForceSet(scope->v8StringData("slaveOk"),
- v8::Boolean::New(scope->getIsolate(), false));
- args.This()->ForceSet(scope->v8StringData("host"), scope->v8StringData(host));
-
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> mongoConsLocal(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 0, "local Mongo constructor takes no args")
-
- // only allow function template to be used by a constructor
- uassert(16860, "Mongo function is only usable as a constructor", args.IsConstructCall());
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- DBClientBase* conn = createDirectClient(scope->getOpContext());
- v8::Local<v8::External> connHandle =
- scope->dbClientBaseTracker.track(scope->getIsolate(), args.This(), conn);
-
- args.This()->SetInternalField(0, connHandle);
- args.This()->ForceSet(scope->v8StringData("slaveOk"),
- v8::Boolean::New(scope->getIsolate(), false));
- args.This()->ForceSet(scope->v8StringData("host"), scope->v8StringData("EMBEDDED"));
-
- return v8::Undefined(scope->getIsolate());
-}
-
-std::shared_ptr<mongo::DBClientBase> getConnection(
- V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- verify(scope->MongoFT()->HasInstance(args.This()));
- verify(args.This()->InternalFieldCount() == 1);
- v8::Local<v8::External> c = v8::Local<v8::External>::Cast(args.This()->GetInternalField(0));
- std::shared_ptr<DBClientBase>* conn = static_cast<std::shared_ptr<DBClientBase>*>(c->Value());
- massert(16667, "Unable to get db client connection", conn && conn->get());
- return *conn;
-}
-
-/**
- * JavaScript binding for Mongo.prototype.find(namespace, query, fields, limit, skip)
- */
-v8::Local<v8::Value> mongoFind(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 7, "find needs 7 args")
- argumentCheck(args[1]->IsObject(), "needs to be an object")
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
- BSONObj fields;
- BSONObj q = scope->v8ToMongo(args[1]->ToObject());
- bool haveFields = args[2]->IsObject() && args[2]->ToObject()->GetPropertyNames()->Length() > 0;
- if (haveFields)
- fields = scope->v8ToMongo(args[2]->ToObject());
-
- std::shared_ptr<mongo::DBClientCursor> cursor;
- int nToReturn = args[3]->Int32Value();
- int nToSkip = args[4]->Int32Value();
- int batchSize = args[5]->Int32Value();
- int options = args[6]->Int32Value();
- cursor =
- conn->query(ns, q, nToReturn, nToSkip, haveFields ? &fields : NULL, options, batchSize);
- if (!cursor.get()) {
- return v8AssertionException("error doing query: failed");
- }
-
- v8::Local<v8::Function> cons = scope->InternalCursorFT()->GetFunction();
- v8::Local<v8::Object> c = cons->NewInstance();
- c->SetInternalField(0, v8::External::New(scope->getIsolate(), cursor.get()));
- scope->dbConnectionAndCursor.track(
- scope->getIsolate(), c, new V8Scope::DBConnectionAndCursor(conn, cursor));
- return c;
-}
-
-v8::Local<v8::Value> mongoCursorFromId(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 2 || args.Length() == 3, "cursorFromId needs 2 or 3 args")
- argumentCheck(scope->NumberLongFT()->HasInstance(args[1]), "2nd arg must be a NumberLong")
- argumentCheck(args[2]->IsUndefined() || args[2]->IsNumber(),
- "3rd arg must be a js Number")
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
- long long cursorId = numberLongVal(scope, args[1]->ToObject());
-
- std::shared_ptr<mongo::DBClientCursor> cursor(
- new DBClientCursor(conn.get(), ns, cursorId, 0, 0));
-
- if (!args[2]->IsUndefined())
- cursor->setBatchSize(args[2]->Int32Value());
-
- v8::Local<v8::Function> cons = scope->InternalCursorFT()->GetFunction();
- v8::Local<v8::Object> c = cons->NewInstance();
- c->SetInternalField(0, v8::External::New(scope->getIsolate(), cursor.get()));
- scope->dbConnectionAndCursor.track(
- scope->getIsolate(), c, new V8Scope::DBConnectionAndCursor(conn, cursor));
- return c;
-}
-
-v8::Local<v8::Value> mongoInsert(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 3, "insert needs 3 args")
- argumentCheck(args[1]->IsObject(), "attempted to insert a non-object")
-
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) {
- return v8AssertionException("js db in read only mode");
- }
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
-
- v8::Local<v8::Integer> flags = args[2]->ToInteger();
-
- if (args[1]->IsArray()) {
- v8::Local<v8::Array> arr = v8::Local<v8::Array>::Cast(args[1]);
- vector<BSONObj> bos;
- uint32_t len = arr->Length();
- argumentCheck(len > 0, "attempted to insert an empty array")
-
- for (uint32_t i = 0; i < len; i++) {
- v8::Local<v8::Object> el = arr->CloneElementAt(i);
- argumentCheck(!el.IsEmpty(), "attempted to insert an array of non-object types")
-
- // Set ID on the element if necessary
- if (!el->Has(scope->v8StringData("_id"))) {
- v8::Local<v8::Value> argv[1];
- el->ForceSet(scope->v8StringData("_id"),
- scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv));
- }
- bos.push_back(scope->v8ToMongo(el));
- }
- conn->insert(ns, bos, flags->Int32Value());
- } else {
- v8::Local<v8::Object> in = args[1]->ToObject();
- if (!in->Has(scope->v8StringData("_id"))) {
- v8::Local<v8::Value> argv[1];
- in->ForceSet(scope->v8StringData("_id"),
- scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv));
- }
- BSONObj o = scope->v8ToMongo(in);
- conn->insert(ns, o);
- }
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> mongoRemove(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 2 || args.Length() == 3, "remove needs 2 or 3 args")
- argumentCheck(args[1]->IsObject(), "attempted to remove a non-object")
-
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) {
- return v8AssertionException("js db in read only mode");
- }
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
-
- v8::Local<v8::Object> in = args[1]->ToObject();
- BSONObj o = scope->v8ToMongo(in);
-
- bool justOne = false;
- if (args.Length() > 2) {
- justOne = args[2]->BooleanValue();
- }
-
- conn->remove(ns, o, justOne);
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> mongoUpdate(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() >= 3, "update needs at least 3 args")
- argumentCheck(args[1]->IsObject(), "1st param to update has to be an object")
- argumentCheck(args[2]->IsObject(), "2nd param to update has to be an object")
-
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) {
- return v8AssertionException("js db in read only mode");
- }
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
-
- v8::Local<v8::Object> q = args[1]->ToObject();
- v8::Local<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();
-
- BSONObj q1 = scope->v8ToMongo(q);
- BSONObj o1 = scope->v8ToMongo(o);
- conn->update(ns, q1, o1, upsert, multi);
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> mongoAuth(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- if (NULL == conn)
- return v8AssertionException("no connection");
-
- BSONObj params;
- switch (args.Length()) {
- case 1:
- params = scope->v8ToMongo(args[0]->ToObject());
- break;
- case 3:
- params = BSON(saslCommandMechanismFieldName
- << "MONGODB-CR" << saslCommandUserDBFieldName << toSTLString(args[0])
- << saslCommandUserFieldName << toSTLString(args[1])
- << saslCommandPasswordFieldName << toSTLString(args[2]));
- break;
- default:
- return v8AssertionException("mongoAuth takes 1 object or 3 string arguments");
- }
- try {
- conn->auth(params);
- } catch (const DBException& ex) {
- return v8AssertionException(ex.toString());
- }
- return v8::Boolean::New(scope->getIsolate(), true);
-}
-
-v8::Local<v8::Value> mongoLogout(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 1, "logout needs 1 arg") std::shared_ptr<DBClientBase> conn =
- getConnection(scope, args);
- const string db = toSTLString(args[0]);
- BSONObj ret;
- conn->logout(db, ret);
- return scope->mongoToLZV8(ret, false);
-}
-
-/**
- * get cursor from v8 argument
- */
-mongo::DBClientCursor* getCursor(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- verify(scope->InternalCursorFT()->HasInstance(args.This()));
- verify(args.This()->InternalFieldCount() == 1);
- v8::Local<v8::External> c = v8::Local<v8::External>::Cast(args.This()->GetInternalField(0));
- mongo::DBClientCursor* cursor = static_cast<mongo::DBClientCursor*>(c->Value());
- return cursor;
-}
-
-v8::Local<v8::Value> internalCursorCons(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- return v8::Undefined(scope->getIsolate());
-}
-
-/**
- * cursor.next()
- */
-v8::Local<v8::Value> internalCursorNext(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- mongo::DBClientCursor* cursor = getCursor(scope, args);
- if (!cursor)
- return v8::Undefined(scope->getIsolate());
- BSONObj o = cursor->next();
- bool ro = false;
- if (args.This()->Has(v8::String::NewFromUtf8(scope->getIsolate(), "_ro")))
- ro = args.This()->Get(v8::String::NewFromUtf8(scope->getIsolate(), "_ro"))->BooleanValue();
- return scope->mongoToLZV8(o, ro);
-}
-
-/**
- * cursor.hasNext()
- */
-v8::Local<v8::Value> internalCursorHasNext(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- mongo::DBClientCursor* cursor = getCursor(scope, args);
- if (!cursor)
- return v8::Boolean::New(scope->getIsolate(), false);
- return v8::Boolean::New(scope->getIsolate(), cursor->more());
-}
-
-/**
- * cursor.objsLeftInBatch()
- */
-v8::Local<v8::Value> internalCursorObjsLeftInBatch(
- V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- mongo::DBClientCursor* cursor = getCursor(scope, args);
- if (!cursor)
- return v8::Number::New(scope->getIsolate(), 0.0);
- return v8::Number::New(scope->getIsolate(), static_cast<double>(cursor->objsLeftInBatch()));
-}
-
-/**
- * cursor.readOnly()
- */
-v8::Local<v8::Value> internalCursorReadOnly(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- verify(scope->InternalCursorFT()->HasInstance(args.This()));
-
- v8::Local<v8::Object> cursor = args.This();
- cursor->ForceSet(v8::String::NewFromUtf8(scope->getIsolate(), "_ro"),
- v8::Boolean::New(scope->getIsolate(), true));
- return cursor;
-}
-
-v8::Local<v8::Value> dbInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->DBFT()->GetFunction();
- return newInstance(f, args);
- }
-
- verify(scope->DBFT()->HasInstance(args.This()));
-
- argumentCheck(args.Length() == 2, "db constructor requires 2 arguments")
-
- args.This()->ForceSet(scope->v8StringData("_mongo"), args[0]);
- args.This()->ForceSet(scope->v8StringData("_name"), args[1]);
-
- for (int i = 0; i < args.Length(); i++) {
- argumentCheck(!args[i]->IsUndefined(), "db initializer called with undefined argument")
- }
-
- string dbName = toSTLString(args[1]);
- if (!NamespaceString::validDBName(dbName)) {
- return v8AssertionException(str::stream() << "[" << dbName
- << "] is not a valid database name");
- }
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> collectionInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->DBCollectionFT()->GetFunction();
- return newInstance(f, args);
- }
-
- verify(scope->DBCollectionFT()->HasInstance(args.This()));
-
- argumentCheck(args.Length() == 4, "collection constructor requires 4 arguments")
-
- for (int i = 0; i < args.Length(); i++) {
- argumentCheck(!args[i]->IsUndefined(),
- "collection constructor called with undefined argument")
- }
-
- args.This()->ForceSet(scope->v8StringData("_mongo"), args[0]);
- args.This()->ForceSet(scope->v8StringData("_db"), args[1]);
- args.This()->ForceSet(scope->v8StringData("_shortName"), args[2]);
- args.This()->ForceSet(v8::String::NewFromUtf8(scope->getIsolate(), "_fullName"), args[3]);
-
- auto context = scope->getOpContext();
- if (context && haveLocalShardingInfo(context->getClient(), toSTLString(args[3]))) {
- return v8AssertionException("can't use sharded collection from db.eval");
- }
-
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> dbQueryInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->DBQueryFT()->GetFunction();
- return newInstance(f, args);
- }
-
- verify(scope->DBQueryFT()->HasInstance(args.This()));
-
- argumentCheck(args.Length() >= 4, "dbQuery constructor requires at least 4 arguments")
-
- v8::Local<v8::Object> t = args.This();
- t->ForceSet(scope->v8StringData("_mongo"), args[0]);
- t->ForceSet(scope->v8StringData("_db"), args[1]);
- t->ForceSet(scope->v8StringData("_collection"), args[2]);
- t->ForceSet(scope->v8StringData("_ns"), args[3]);
-
- if (args.Length() > 4 && args[4]->IsObject())
- t->ForceSet(scope->v8StringData("_query"), args[4]);
- else
- t->ForceSet(scope->v8StringData("_query"), v8::Object::New(scope->getIsolate()));
-
- if (args.Length() > 5 && args[5]->IsObject())
- t->ForceSet(scope->v8StringData("_fields"), args[5]);
- else
- t->ForceSet(scope->v8StringData("_fields"), v8::Null(scope->getIsolate()));
-
- if (args.Length() > 6 && args[6]->IsNumber())
- t->ForceSet(scope->v8StringData("_limit"), args[6]);
- else
- t->ForceSet(scope->v8StringData("_limit"), v8::Number::New(scope->getIsolate(), 0));
-
- if (args.Length() > 7 && args[7]->IsNumber())
- t->ForceSet(scope->v8StringData("_skip"), args[7]);
- else
- t->ForceSet(scope->v8StringData("_skip"), v8::Number::New(scope->getIsolate(), 0));
-
- if (args.Length() > 8 && args[8]->IsNumber())
- t->ForceSet(scope->v8StringData("_batchSize"), args[8]);
- else
- t->ForceSet(scope->v8StringData("_batchSize"), v8::Number::New(scope->getIsolate(), 0));
-
- if (args.Length() > 9 && args[9]->IsNumber())
- t->ForceSet(scope->v8StringData("_options"), args[9]);
- else
- t->ForceSet(scope->v8StringData("_options"), v8::Number::New(scope->getIsolate(), 0));
-
- t->ForceSet(scope->v8StringData("_cursor"), v8::Null(scope->getIsolate()));
- t->ForceSet(scope->v8StringData("_numReturned"), v8::Number::New(scope->getIsolate(), 0));
- t->ForceSet(scope->v8StringData("_special"), v8::Boolean::New(scope->getIsolate(), false));
-
- return v8::Undefined(scope->getIsolate());
-}
-
-void collectionSetter(v8::Local<v8::String> name,
- v8::Local<v8::Value> value,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- try {
- V8Scope* scope = getScope(info.GetIsolate());
-
- // Both DB and Collection objects use this setter
- verify(scope->DBCollectionFT()->HasInstance(info.This()) ||
- scope->DBFT()->HasInstance(info.This()));
-
- // a collection name cannot be overwritten by a variable
- string sname = toSTLString(name);
- if (sname.length() == 0 || sname[0] == '_') {
- // if starts with '_' we allow overwrite
- return;
- }
- // dont set
- result.Set(value);
- } catch (const DBException& dbEx) {
- result.Set(v8AssertionException(dbEx.toString()));
- } catch (...) {
- result.Set(v8AssertionException("unknown error in collationSetter"));
- }
-}
-
-void collectionGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- try {
- V8Scope* scope = getScope(info.GetIsolate());
-
- // Both DB and Collection objects use this getter
- verify(scope->DBCollectionFT()->HasInstance(info.This()) ||
- scope->DBFT()->HasInstance(info.This()));
-
- v8::TryCatch tryCatch;
-
- // first look in prototype, may be a function
- v8::Local<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get(name);
- if (!real->IsUndefined()) {
- result.Set(real);
- return;
- }
-
- // 2nd look into real values, may be cached collection object
- string sname = toSTLString(name);
- if (info.This()->HasRealNamedProperty(name)) {
- v8::Local<v8::Value> prop = info.This()->GetRealNamedProperty(name);
- if (prop->IsObject() &&
- prop->ToObject()->HasRealNamedProperty(
- v8::String::NewFromUtf8(scope->getIsolate(), "_fullName"))) {
- // need to check every time that the collection did not get sharded
- auto context = scope->getOpContext();
- if (context &&
- haveLocalShardingInfo(
- context->getClient(),
- toSTLString(prop->ToObject()->GetRealNamedProperty(
- v8::String::NewFromUtf8(scope->getIsolate(), "_fullName"))))) {
- result.Set(v8AssertionException("can't use sharded collection from db.eval"));
- return;
- }
- }
- result.Set(prop);
- return;
- } else if (sname.length() == 0 || sname[0] == '_') {
- // if starts with '_' we dont return collection, one must use getCollection()
- return;
- }
-
- // no hit, create new collection
- v8::Local<v8::Value> getCollection = info.This()->GetPrototype()->ToObject()->Get(
- v8::String::NewFromUtf8(scope->getIsolate(), "getCollection"));
- if (!getCollection->IsFunction()) {
- result.Set(v8AssertionException("getCollection is not a function"));
- return;
- }
-
- v8::Local<v8::Function> f = getCollection.As<v8::Function>();
- v8::Local<v8::Value> argv[1];
- argv[0] = name;
- v8::Local<v8::Value> coll = f->Call(info.This(), 1, argv);
- if (coll.IsEmpty()) {
- result.Set(tryCatch.ReThrow());
- return;
- }
-
- uassert(16861,
- "getCollection returned something other than a collection",
- scope->DBCollectionFT()->HasInstance(coll));
-
- // cache collection for reuse, don't enumerate
- info.This()->ForceSet(name, coll, v8::DontEnum);
- result.Set(coll);
- } catch (const DBException& dbEx) {
- result.Set(v8AssertionException(dbEx.toString()));
- } catch (...) {
- result.Set(v8AssertionException("unknown error in collectionGetter"));
- }
-}
-
-void dbQueryIndexAccess(unsigned int index, const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::Local<v8::Value> val;
- v8::ReturnValue<v8::Value> result = info.GetReturnValue();
- result.Set(val);
- try {
- V8Scope* scope = getScope(info.GetIsolate());
- verify(scope->DBQueryFT()->HasInstance(info.This()));
-
- v8::Local<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get(
- v8::String::NewFromUtf8(scope->getIsolate(), "arrayAccess"));
- massert(16660, "arrayAccess is not a function", arrayAccess->IsFunction());
-
- v8::Local<v8::Function> f = arrayAccess.As<v8::Function>();
- v8::Local<v8::Value> argv[1];
- argv[0] = v8::Number::New(scope->getIsolate(), index);
-
- result.Set(f->Call(info.This(), 1, argv));
- } catch (const DBException& dbEx) {
- result.Set(v8AssertionException(dbEx.toString()));
- } catch (...) {
- result.Set(v8AssertionException("unknown error in dbQueryIndexAccess"));
- }
-}
-
-v8::Local<v8::Value> objectIdInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->ObjectIdFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->ObjectIdFT()->HasInstance(it));
-
- OID oid;
- if (args.Length() == 0) {
- oid.init();
- } else {
- string s = toSTLString(args[0]);
- try {
- Scope::validateObjectIdString(s);
- } catch (const MsgAssertionException& m) {
- return v8AssertionException(m.toString());
- }
- oid.init(s);
- }
-
- it->ForceSet(scope->v8StringData("str"),
- v8::String::NewFromUtf8(scope->getIsolate(), oid.toString().c_str()));
- return it;
-}
-
-v8::Local<v8::Value> dbRefInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->DBRefFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->DBRefFT()->HasInstance(it));
-
- argumentCheck(args.Length() >= 2 && args.Length() <= 3, "DBRef needs 2 or 3 arguments")
- argumentCheck(args[0]->IsString(), "DBRef 1st parameter must be a string")
- it->ForceSet(scope->v8StringData("$ref"), args[0]);
- it->ForceSet(scope->v8StringData("$id"), args[1]);
-
- if (args.Length() == 3) {
- argumentCheck(args[2]->IsString(), "DBRef 3rd parameter must be a string")
- it->ForceSet(scope->v8StringData("$db"), args[2]);
- }
-
- return it;
-}
-
-v8::Local<v8::Value> dbPointerInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->DBPointerFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->DBPointerFT()->HasInstance(it));
-
- argumentCheck(args.Length() == 2, "DBPointer needs 2 arguments")
- argumentCheck(args[0]->IsString(), "DBPointer 1st parameter must be a string")
- argumentCheck(scope->ObjectIdFT()->HasInstance(args[1]),
- "DBPointer 2nd parameter must be an ObjectId")
-
- it->ForceSet(scope->v8StringData("ns"), args[0]);
- it->ForceSet(scope->v8StringData("id"), args[1]);
- return it;
-}
-
-v8::Local<v8::Value> dbTimestampInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->TimestampFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->TimestampFT()->HasInstance(it));
-
- if (args.Length() == 0) {
- it->ForceSet(scope->v8StringData("t"), v8::Number::New(scope->getIsolate(), 0));
- it->ForceSet(scope->v8StringData("i"), v8::Number::New(scope->getIsolate(), 0));
- } else if (args.Length() == 2) {
- if (!args[0]->IsNumber()) {
- return v8AssertionException("Timestamp time must be a number");
- }
- if (!args[1]->IsNumber()) {
- return v8AssertionException("Timestamp increment must be a number");
- }
- int64_t t = args[0]->IntegerValue();
- int64_t largestVal = int64_t(OpTime::max().getSecs());
- if (t > largestVal)
- return v8AssertionException(str::stream() << "The first argument must be in seconds; "
- << t << " is too large (max " << largestVal
- << ")");
- it->ForceSet(scope->v8StringData("t"), args[0]);
- it->ForceSet(scope->v8StringData("i"), args[1]);
- } else {
- return v8AssertionException("Timestamp needs 0 or 2 arguments");
- }
-
- return it;
-}
-
-v8::Local<v8::Value> binDataInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->BinDataFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
-
- argumentCheck(args.Length() == 2, "BinData takes 2 arguments -- BinData(subtype,data)");
-
- // 2 args: type, base64 string
- v8::Local<v8::Value> type = args[0];
- if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) {
- return v8AssertionException(
- "BinData subtype must be a Number between 0 and 255 inclusive)");
- }
- v8::String::Utf8Value utf(args[1]);
- // uassert if invalid base64 string
- string tmpBase64 = base64::decode(*utf);
- // length property stores the decoded length
- it->ForceSet(scope->v8StringData("len"),
- v8::Number::New(scope->getIsolate(), tmpBase64.length()),
- v8::PropertyAttribute::ReadOnly);
- it->ForceSet(scope->v8StringData("type"), type, v8::PropertyAttribute::ReadOnly);
- it->SetInternalField(0, args[1]);
-
- return it;
-}
-
-v8::Local<v8::Value> binDataToString(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
- int type = it->Get(v8::String::NewFromUtf8(scope->getIsolate(), "type"))->Int32Value();
-
- stringstream ss;
- verify(it->InternalFieldCount() == 1);
- ss << "BinData(" << type << ",\"" << toSTLString(it->GetInternalField(0)) << "\")";
- return v8::String::NewFromUtf8(scope->getIsolate(), ss.str().c_str());
-}
-
-v8::Local<v8::Value> binDataToBase64(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
- verify(it->InternalFieldCount() == 1);
- return it->GetInternalField(0);
-}
-
-v8::Local<v8::Value> binDataToHex(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
- verify(it->InternalFieldCount() == 1);
- string data = base64::decode(toSTLString(it->GetInternalField(0)));
- stringstream ss;
- ss.setf(ios_base::hex, ios_base::basefield);
- ss.fill('0');
- ss.setf(ios_base::right, ios_base::adjustfield);
- for (std::string::iterator it = data.begin(); it != data.end(); ++it) {
- unsigned v = (unsigned char)*it;
- ss << setw(2) << v;
- }
- return v8::String::NewFromUtf8(scope->getIsolate(), ss.str().c_str());
-}
-
-static v8::Local<v8::Value> hexToBinData(V8Scope* scope, int type, string hexstr) {
- // SERVER-9686: This function does not correctly check to make sure hexstr is actually made
- // up of valid hex digits, and fails in the hex utility functions
-
- int len = hexstr.length() / 2;
- unique_ptr<char[]> data(new char[len]);
- const char* src = hexstr.c_str();
- for (int i = 0; i < len; i++) {
- data[i] = fromHex(src + i * 2);
- }
-
- string encoded = base64::encode(data.get(), len);
- v8::Local<v8::Value> argv[2];
- argv[0] = v8::Number::New(scope->getIsolate(), type);
- argv[1] = v8::String::NewFromUtf8(scope->getIsolate(), encoded.c_str());
- return scope->BinDataFT()->GetFunction()->NewInstance(2, argv);
-}
-
-v8::Local<v8::Value> uuidInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 1, "UUID needs 1 argument") v8::String::Utf8Value utf(args[0]);
- argumentCheck(utf.length() == 32,
- "UUID string must have 32 characters") return hexToBinData(scope, bdtUUID, *utf);
-}
-
-v8::Local<v8::Value> md5Init(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 1, "MD5 needs 1 argument") v8::String::Utf8Value utf(args[0]);
- argumentCheck(utf.length() == 32,
- "MD5 string must have 32 characters") return hexToBinData(scope, MD5Type, *utf);
-}
-
-v8::Local<v8::Value> hexDataInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 2, "HexData needs 2 arguments") v8::Local<v8::Value> type =
- args[0];
- if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) {
- return v8AssertionException("HexData subtype must be a Number between 0 and 255 inclusive");
- }
- v8::String::Utf8Value utf(args[1]);
- return hexToBinData(scope, type->Int32Value(), *utf);
-}
-
-v8::Local<v8::Value> numberLongInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->NumberLongFT()->GetFunction();
- return newInstance(f, args);
- }
-
- argumentCheck(args.Length() == 0 || args.Length() == 1 || args.Length() == 3,
- "NumberLong needs 0, 1 or 3 arguments")
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->NumberLongFT()->HasInstance(it));
-
- if (args.Length() == 0) {
- it->ForceSet(scope->v8StringData("floatApprox"), v8::Number::New(scope->getIsolate(), 0));
- } else if (args.Length() == 1) {
- if (args[0]->IsNumber()) {
- it->ForceSet(scope->v8StringData("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 v8AssertionException(string("could not convert \"") + num +
- "\" to NumberLong");
- }
- unsigned long long val = n;
- // values above 2^53 are not accurately represented in JS
- if ((long long)val == (long long)(double)(long long)(val) &&
- val < 9007199254740992ULL) {
- it->ForceSet(scope->v8StringData("floatApprox"),
- v8::Number::New(scope->getIsolate(), (double)(long long)(val)));
- } else {
- it->ForceSet(scope->v8StringData("floatApprox"),
- v8::Number::New(scope->getIsolate(), (double)(long long)(val)));
- it->ForceSet(scope->v8StringData("top"),
- v8::Integer::New(scope->getIsolate(), val >> 32));
- it->ForceSet(scope->v8StringData("bottom"),
- v8::Integer::New(scope->getIsolate(),
- (unsigned long)(val & 0x00000000ffffffff)));
- }
- }
- } else {
- it->ForceSet(scope->v8StringData("floatApprox"), args[0]->ToNumber());
- it->ForceSet(scope->v8StringData("top"), args[1]->ToUint32());
- it->ForceSet(scope->v8StringData("bottom"), args[2]->ToUint32());
- }
- return it;
-}
-
-long long numberLongVal(V8Scope* scope, const v8::Local<v8::Object>& it) {
- verify(scope->NumberLongFT()->HasInstance(it));
- if (!it->Has(v8::String::NewFromUtf8(scope->getIsolate(), "top")))
- return (long long)(it->Get(v8::String::NewFromUtf8(scope->getIsolate(), "floatApprox"))
- ->NumberValue());
- return (long long)((unsigned long long)(it->Get(v8::String::NewFromUtf8(scope->getIsolate(),
- "top"))
- ->ToInt32()
- ->Value())
- << 32) +
- (unsigned)(it->Get(v8::String::NewFromUtf8(scope->getIsolate(), "bottom"))
- ->ToInt32()
- ->Value());
-}
-
-v8::Local<v8::Value> numberLongValueOf(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Object> it = args.This();
- long long val = numberLongVal(scope, it);
- return v8::Number::New(scope->getIsolate(), double(val));
-}
-
-v8::Local<v8::Value> numberLongToNumber(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- return numberLongValueOf(scope, args);
-}
-
-v8::Local<v8::Value> numberLongToString(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Object> it = args.This();
-
- stringstream ss;
- long long val = numberLongVal(scope, 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::NewFromUtf8(scope->getIsolate(), ret.c_str());
-}
-
-v8::Local<v8::Value> numberIntInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (!args.IsConstructCall()) {
- v8::Local<v8::Function> f = scope->NumberIntFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->NumberIntFT()->HasInstance(it));
-
- argumentCheck(args.Length() == 0 || args.Length() == 1,
- "NumberInt needs 0 or 1 arguments") if (args.Length() == 0) {
- it->SetHiddenValue(v8::String::NewFromUtf8(scope->getIsolate(), "__NumberInt"),
- v8::Number::New(scope->getIsolate(), 0));
- }
- else if (args.Length() == 1) {
- it->SetHiddenValue(v8::String::NewFromUtf8(scope->getIsolate(), "__NumberInt"),
- args[0]->ToInt32());
- }
- return it;
-}
-
-int numberIntVal(V8Scope* scope, const v8::Local<v8::Object>& it) {
- verify(scope->NumberIntFT()->HasInstance(it));
- v8::Local<v8::Value> value =
- it->GetHiddenValue(v8::String::NewFromUtf8(scope->getIsolate(), "__NumberInt"));
- verify(!value.IsEmpty());
- return value->Int32Value();
-}
-
-v8::Local<v8::Value> numberIntValueOf(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Object> it = args.This();
- return v8::Integer::New(scope->getIsolate(), numberIntVal(scope, it));
-}
-
-v8::Local<v8::Value> numberIntToNumber(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- return numberIntValueOf(scope, args);
-}
-
-v8::Local<v8::Value> numberIntToString(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::Object> it = args.This();
- int val = numberIntVal(scope, it);
- string ret = str::stream() << "NumberInt(" << val << ")";
- return v8::String::NewFromUtf8(scope->getIsolate(), ret.c_str());
-}
-
-v8::Local<v8::Value> v8ObjectInvalidForStorage(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 1, "invalidForStorage needs 1 argument") if (args[0]->IsNull()) {
- return v8::Null(scope->getIsolate());
- }
- argumentCheck(args[0]->IsObject(), "argument to invalidForStorage has to be an object")
- Status validForStorage = scope->v8ToMongo(args[0]->ToObject()).storageValid(true);
- if (validForStorage.isOK()) {
- return v8::Null(scope->getIsolate());
- }
-
- std::string errmsg = str::stream() << validForStorage.codeString() << ": "
- << validForStorage.reason();
- return v8::String::NewFromUtf8(scope->getIsolate(), errmsg.c_str());
-}
-
-v8::Local<v8::Value> bsonsize(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 1, "bsonsize needs 1 argument") if (args[0]->IsNull()) {
- return v8::Number::New(scope->getIsolate(), 0);
- }
- argumentCheck(args[0]->IsObject(), "argument to bsonsize has to be an object") return v8::
- Number::New(scope->getIsolate(), scope->v8ToMongo(args[0]->ToObject()).objsize());
-}
-
-v8::Local<v8::Value> bsonWoCompare(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- argumentCheck(args.Length() == 2, "bsonWoCompare needs 2 argument");
-
- argumentCheck(args[0]->IsObject(), "first argument to bsonWoCompare has to be an object");
- argumentCheck(args[1]->IsObject(), "second argument to bsonWoCompare has to be an object");
-
- BSONObj firstObject(scope->v8ToMongo(args[0]->ToObject()));
- BSONObj secondObject(scope->v8ToMongo(args[1]->ToObject()));
-
- return v8::Number::New(scope->getIsolate(), firstObject.woCompare(secondObject));
-}
-}
diff --git a/src/mongo/scripting/v8-3.25_db.h b/src/mongo/scripting/v8-3.25_db.h
deleted file mode 100644
index 024efb55d26..00000000000
--- a/src/mongo/scripting/v8-3.25_db.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// v8_db.h
-
-/* Copyright 2014 MongoDB 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/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <v8.h>
-
-#include "mongo/scripting/engine_v8-3.25.h"
-#include "mongo/stdx/functional.h"
-
-namespace mongo {
-
-// Internal Cursor
-v8::Local<v8::FunctionTemplate> getInternalCursorFunctionTemplate(V8Scope* scope);
-
-// Mongo constructors
-v8::Local<v8::Value> mongoConsLocal(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> mongoConsExternal(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::FunctionTemplate> getMongoFunctionTemplate(V8Scope* scope, bool local);
-
-// Mongo member functions
-v8::Local<v8::Value> mongoFind(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> mongoInsert(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> mongoRemove(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> mongoUpdate(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> mongoAuth(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> mongoLogout(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> mongoCursorFromId(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// Cursor object
-v8::Local<v8::Value> internalCursorCons(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> internalCursorNext(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> internalCursorHasNext(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> internalCursorReadOnly(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// BinData object
-v8::Local<v8::Value> binDataInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> binDataToString(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> binDataToBase64(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> binDataToHex(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// NumberLong object
-long long numberLongVal(V8Scope* scope, const v8::Local<v8::Object>& it);
-v8::Local<v8::Value> numberLongInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> numberLongToNumber(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> numberLongValueOf(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> numberLongToString(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// NumberInt object
-int numberIntVal(V8Scope* scope, const v8::Local<v8::Object>& it);
-v8::Local<v8::Value> numberIntInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> numberIntToNumber(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> numberIntValueOf(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-v8::Local<v8::Value> numberIntToString(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// DBQuery object
-v8::Local<v8::Value> dbQueryInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-void dbQueryIndexAccess(::uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info);
-
-// db constructor
-v8::Local<v8::Value> dbInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// collection constructor
-v8::Local<v8::Value> collectionInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// ObjectId constructor
-v8::Local<v8::Value> objectIdInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// DBRef constructor
-v8::Local<v8::Value> dbRefInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// DBPointer constructor
-v8::Local<v8::Value> dbPointerInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// Timestamp constructor
-v8::Local<v8::Value> dbTimestampInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// UUID constructor
-v8::Local<v8::Value> uuidInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// MD5 constructor
-v8::Local<v8::Value> md5Init(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// HexData constructor
-v8::Local<v8::Value> hexDataInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// Object.invalidForStorage()
-v8::Local<v8::Value> v8ObjectInvalidForStorage(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// Object.bsonsize()
-v8::Local<v8::Value> bsonsize(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// global method
-// Accepts 2 objects, converts them to BSONObj and calls woCompare on the first against the
-// second.
-v8::Local<v8::Value> bsonWoCompare(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args);
-
-// 'db.collection' property handlers
-void collectionGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info);
-void collectionSetter(v8::Local<v8::String> name,
- v8::Local<v8::Value> value,
- const v8::PropertyCallbackInfo<v8::Value>& info);
-
-typedef stdx::function<void(V8Scope*, const v8::Local<v8::FunctionTemplate>&)>
- V8FunctionPrototypeManipulatorFn;
-
-void v8RegisterMongoPrototypeManipulator(const V8FunctionPrototypeManipulatorFn& manipulator);
-}
diff --git a/src/mongo/scripting/v8-3.25_profiler.cpp b/src/mongo/scripting/v8-3.25_profiler.cpp
deleted file mode 100644
index d559c1e2ed1..00000000000
--- a/src/mongo/scripting/v8-3.25_profiler.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright 2013 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/>.
- *
- * 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.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
-
-#include "mongo/scripting/v8-3.25_profiler.h"
-
-#include "mongo/util/log.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-void V8CpuProfiler::start(v8::Isolate* isolate, const StringData name) {
- isolate->GetCpuProfiler()->StartCpuProfiling(
- v8::String::NewFromUtf8(isolate, name.toString().c_str()));
-}
-
-void V8CpuProfiler::stop(v8::Isolate* isolate, const StringData name) {
- _cpuProfiles.insert(
- make_pair(name.toString(),
- isolate->GetCpuProfiler()->StopCpuProfiling(v8::String::NewFromUtf8(
- isolate, name.toString().c_str(), v8::String::kNormalString, name.size()))));
-}
-
-void V8CpuProfiler::traverseDepthFirst(const v8::CpuProfileNode* cpuProfileNode,
- BSONArrayBuilder& arrayBuilder) {
- if (cpuProfileNode == NULL)
- return;
- BSONObjBuilder frameObjBuilder;
- frameObjBuilder.append("Function", *v8::String::Utf8Value(cpuProfileNode->GetFunctionName()));
- frameObjBuilder.append("Source",
- *v8::String::Utf8Value(cpuProfileNode->GetScriptResourceName()));
- frameObjBuilder.appendNumber("Line", cpuProfileNode->GetLineNumber());
- if (cpuProfileNode->GetChildrenCount()) {
- BSONArrayBuilder subArrayBuilder(frameObjBuilder.subarrayStart("Children"));
- for (int i = 0; i < cpuProfileNode->GetChildrenCount(); ++i) {
- traverseDepthFirst(cpuProfileNode->GetChild(i), subArrayBuilder);
- }
- subArrayBuilder.done();
- }
- arrayBuilder << frameObjBuilder.obj();
-}
-
-const BSONArray V8CpuProfiler::fetch(const StringData name) {
- BSONArrayBuilder arrayBuilder;
- CpuProfileMap::const_iterator iProf = _cpuProfiles.find(name.toString());
- if (iProf == _cpuProfiles.end())
- return arrayBuilder.arr();
- const v8::CpuProfile* cpuProfile = iProf->second;
- if (cpuProfile == NULL)
- return arrayBuilder.arr();
- traverseDepthFirst(cpuProfile->GetTopDownRoot(), arrayBuilder);
- return arrayBuilder.arr();
-}
-}
diff --git a/src/mongo/scripting/v8-3.25_profiler.h b/src/mongo/scripting/v8-3.25_profiler.h
deleted file mode 100644
index 99d045662ff..00000000000
--- a/src/mongo/scripting/v8-3.25_profiler.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright 2013 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/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <v8.h>
-#include <v8-profiler.h>
-#include <map>
-
-#include "mongo/base/string_data.h"
-#include "mongo/db/jsobj.h"
-
-namespace mongo {
-
-/** Collect CPU Profiling data from v8. */
-class V8CpuProfiler {
-public:
- /** Start the CPU profiler */
- void start(v8::Isolate* isolate, const StringData name);
-
- /** Stop the CPU profiler */
- void stop(v8::Isolate* isolate, const StringData name);
-
- /** Get the current cpu profile */
- const BSONArray fetch(const StringData name);
-
-private:
- void traverseDepthFirst(const v8::CpuProfileNode* cpuProfileNode,
- BSONArrayBuilder& arrayBuilder);
-
- typedef std::map<std::string, const v8::CpuProfile*> CpuProfileMap;
- CpuProfileMap _cpuProfiles;
-};
-}
diff --git a/src/mongo/scripting/v8-3.25_utils.cpp b/src/mongo/scripting/v8-3.25_utils.cpp
deleted file mode 100644
index a2f745b0d1a..00000000000
--- a/src/mongo/scripting/v8-3.25_utils.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
-// v8_utils.cpp
-
-/* Copyright 2014 MongoDB 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/>.
- *
- * 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.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
-
-#include "mongo/scripting/v8-3.25_utils.h"
-
-#include <cstdint>
-#include <iostream>
-#include <map>
-#include <sstream>
-#include <vector>
-
-#include "mongo/scripting/engine_v8-3.25.h"
-#include "mongo/scripting/v8-3.25_db.h"
-#include "mongo/stdx/condition_variable.h"
-#include "mongo/stdx/mutex.h"
-#include "mongo/stdx/thread.h"
-#include "mongo/util/log.h"
-#include "mongo/util/mongoutils/str.h"
-
-using namespace std;
-using std::unique_ptr;
-
-namespace mongo {
-
-std::string toSTLString(const v8::Local<v8::Value>& o) {
- return StringData(V8String(o)).toString();
-}
-
-/** Get the properties of an object (and its prototype) as a comma-delimited string */
-std::string v8ObjectToString(const v8::Local<v8::Object>& o) {
- v8::Local<v8::Array> properties = o->GetPropertyNames();
- v8::String::Utf8Value str(properties);
- massert(16696, "error converting js type to Utf8Value", *str);
- return std::string(*str, str.length());
-}
-
-std::ostream& operator<<(std::ostream& s, const v8::Local<v8::Value>& o) {
- v8::String::Utf8Value str(o);
- s << *str;
- return s;
-}
-
-std::ostream& operator<<(std::ostream& s, const v8::TryCatch* try_catch) {
- v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
- v8::String::Utf8Value exceptionText(try_catch->Exception());
- v8::Local<v8::Message> message = try_catch->Message();
-
- if (message.IsEmpty()) {
- s << *exceptionText << endl;
- } else {
- v8::String::Utf8Value filename(message->GetScriptResourceName());
- int linenum = message->GetLineNumber();
- cout << *filename << ":" << linenum << " " << *exceptionText << 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;
- }
- return s;
-}
-
-class JSThreadConfig {
-public:
- JSThreadConfig(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args,
- bool newScope = false)
- : _started(), _done(), _sharedData(new SharedData()) {
- jsassert(args.Length() > 0, "need at least one argument");
- jsassert(args[0]->IsFunction(), "first argument must be a function");
-
- // arguments need to be copied into the isolate, go through bson
- BSONObjBuilder b;
- for (int i = 0; i < args.Length(); ++i) {
- scope->v8ToMongoElement(b, "arg" + BSONObjBuilder::numStr(i), args[i]);
- }
- _sharedData->_args = b.obj();
- }
-
- ~JSThreadConfig() {}
-
- void start() {
- jsassert(!_started, "Thread already started");
- JSThread jt(*this);
- _thread.reset(new stdx::thread(jt));
- _started = true;
- }
- void join() {
- jsassert(_started && !_done, "Thread not running");
- _thread->join();
- _done = true;
- }
-
- /**
- * Returns true if the JSThread terminated as a result of an error
- * during its execution, and false otherwise. This operation does
- * not block, nor does it require join() to have been called.
- */
- bool hasFailed() const {
- jsassert(_started, "Thread not started");
- return _sharedData->getErrored();
- }
-
- BSONObj returnData() {
- if (!_done)
- join();
- return _sharedData->_returnData;
- }
-
-private:
- /*
- * JSThreadConfig doesn't always outlive its JSThread (for example, if the parent thread
- * garbage collects the JSThreadConfig before the JSThread has finished running), so any
- * data shared between them has to go in a shared_ptr.
- */
- class SharedData {
- public:
- SharedData() : _errored(false) {}
- BSONObj _args;
- BSONObj _returnData;
- void setErrored(bool value) {
- stdx::lock_guard<stdx::mutex> lck(_erroredMutex);
- _errored = value;
- }
- bool getErrored() {
- stdx::lock_guard<stdx::mutex> lck(_erroredMutex);
- return _errored;
- }
-
- private:
- stdx::mutex _erroredMutex;
- bool _errored;
- };
-
- class JSThread {
- public:
- JSThread(JSThreadConfig& config) : _sharedData(config._sharedData) {}
-
- void operator()() {
- try {
- unique_ptr<V8Scope> scope(static_cast<V8Scope*>(globalScriptEngine->newScope()));
- v8::Locker v8lock(scope->getIsolate());
- v8::Isolate::Scope iscope(scope->getIsolate());
- v8::HandleScope handle_scope(scope->getIsolate());
- v8::Context::Scope context_scope(scope->getContext());
-
- BSONObj args = _sharedData->_args;
- v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
- v8::Local<v8::Value>(scope->mongoToV8Element(args.firstElement(), true)));
- int argc = args.nFields() - 1;
-
- // TODO SERVER-8016: properly allocate handles on the stack
- v8::Local<v8::Value> argv[24];
- BSONObjIterator it(args);
- it.next();
- for (int i = 0; i < argc && i < 24; ++i) {
- argv[i] = v8::Local<v8::Value>::New(scope->getIsolate(),
- scope->mongoToV8Element(*it, true));
- it.next();
- }
- v8::TryCatch try_catch;
- v8::Local<v8::Value> ret = f->Call(scope->getGlobal(), argc, argv);
- if (ret.IsEmpty() || try_catch.HasCaught()) {
- string e = scope->v8ExceptionToSTLString(&try_catch);
- log() << "js thread raised js exception: " << e << endl;
- ret = v8::Undefined(scope->getIsolate());
- _sharedData->setErrored(true);
- }
- // ret is translated to BSON to switch isolate
- BSONObjBuilder b;
- scope->v8ToMongoElement(b, "ret", ret);
- _sharedData->_returnData = b.obj();
- } catch (const DBException& e) {
- // Keeping behavior the same as for js exceptions.
- log() << "js thread threw c++ exception: " << e.toString();
- _sharedData->setErrored(true);
- _sharedData->_returnData = BSON("ret" << BSONUndefined);
- } catch (const std::exception& e) {
- log() << "js thread threw c++ exception: " << e.what();
- _sharedData->setErrored(true);
- _sharedData->_returnData = BSON("ret" << BSONUndefined);
- } catch (...) {
- log() << "js thread threw c++ non-exception";
- _sharedData->setErrored(true);
- _sharedData->_returnData = BSON("ret" << BSONUndefined);
- }
- }
-
- private:
- std::shared_ptr<SharedData> _sharedData;
- };
-
- bool _started;
- bool _done;
- unique_ptr<stdx::thread> _thread;
- std::shared_ptr<SharedData> _sharedData;
-};
-
-class CountDownLatchHolder {
-private:
- struct Latch {
- Latch(int32_t count) : count(count) {}
- stdx::condition_variable cv;
- stdx::mutex mutex;
- int32_t count;
- };
-
- std::shared_ptr<Latch> get(int32_t desc) {
- stdx::lock_guard<stdx::mutex> lock(_mutex);
- Map::iterator iter = _latches.find(desc);
- jsassert(iter != _latches.end(), "not a valid CountDownLatch descriptor");
- return iter->second;
- }
-
- typedef std::map<int32_t, std::shared_ptr<Latch>> Map;
- Map _latches;
- stdx::mutex _mutex;
- int32_t _counter;
-
-public:
- CountDownLatchHolder() : _counter(0) {}
- int32_t make(int32_t count) {
- jsassert(count >= 0, "argument must be >= 0");
- stdx::lock_guard<stdx::mutex> lock(_mutex);
- int32_t desc = ++_counter;
- _latches.insert(std::make_pair(desc, std::make_shared<Latch>(count)));
- return desc;
- }
- void await(int32_t desc) {
- std::shared_ptr<Latch> latch = get(desc);
- stdx::unique_lock<stdx::mutex> lock(latch->mutex);
- while (latch->count != 0) {
- latch->cv.wait(lock);
- }
- }
- void countDown(int32_t desc) {
- std::shared_ptr<Latch> latch = get(desc);
- stdx::unique_lock<stdx::mutex> lock(latch->mutex);
- if (latch->count > 0) {
- latch->count--;
- }
- if (latch->count == 0) {
- latch->cv.notify_all();
- }
- }
- int32_t getCount(int32_t desc) {
- std::shared_ptr<Latch> latch = get(desc);
- stdx::unique_lock<stdx::mutex> lock(latch->mutex);
- return latch->count;
- }
-};
-namespace {
-CountDownLatchHolder globalCountDownLatchHolder;
-}
-
-v8::Local<v8::Value> CountDownLatchNew(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- int32_t count = v8::Local<v8::Integer>::Cast(args[0])->Value();
- return v8::Int32::New(scope->getIsolate(), globalCountDownLatchHolder.make(count));
-}
-
-v8::Local<v8::Value> CountDownLatchAwait(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- globalCountDownLatchHolder.await(args[0]->ToInt32()->Value());
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> CountDownLatchCountDown(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- globalCountDownLatchHolder.countDown(args[0]->ToInt32()->Value());
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> CountDownLatchGetCount(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- return v8::Int32::New(scope->getIsolate(),
- globalCountDownLatchHolder.getCount(args[0]->ToInt32()->Value()));
-}
-
-JSThreadConfig* thisConfig(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Local<v8::External> c = v8::Local<v8::External>::Cast(args.This()->GetHiddenValue(
- v8::String::NewFromUtf8(scope->getIsolate(), "_JSThreadConfig")));
- JSThreadConfig* config = static_cast<std::shared_ptr<JSThreadConfig>*>(c->Value())->get();
- return config;
-}
-
-v8::Local<v8::Value> ThreadInit(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- JSThreadConfig* config = new JSThreadConfig(scope, args);
- v8::Local<v8::External> handle =
- scope->jsThreadConfigTracker.track(scope->getIsolate(), args.This(), config);
- args.This()->SetHiddenValue(v8::String::NewFromUtf8(scope->getIsolate(), "_JSThreadConfig"),
- handle);
-
- invariant(thisConfig(scope, args) == config);
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> ScopedThreadInit(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- // NOTE: ScopedThread and Thread behave identically because a new V8 Isolate is always
- // created.
- return ThreadInit(scope, args);
-}
-
-v8::Local<v8::Value> ThreadStart(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- thisConfig(scope, args)->start();
- return v8::Undefined(scope->getIsolate());
-}
-
-v8::Local<v8::Value> ThreadJoin(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- thisConfig(scope, args)->join();
- return v8::Undefined(scope->getIsolate());
-}
-
-// Indicates to the caller that the thread terminated as a result of an error.
-v8::Local<v8::Value> ThreadHasFailed(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- bool hasFailed = thisConfig(scope, args)->hasFailed();
- return v8::Boolean::New(scope->getIsolate(), hasFailed);
-}
-
-v8::Local<v8::Value> ThreadReturnData(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- BSONObj data = thisConfig(scope, args)->returnData();
- return scope->mongoToV8Element(data.firstElement(), true);
-}
-
-v8::Local<v8::Value> ThreadInject(V8Scope* scope, const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::EscapableHandleScope handle_scope(args.GetIsolate());
- jsassert(args.Length() == 1, "threadInject takes exactly 1 argument");
- jsassert(args[0]->IsObject(), "threadInject needs to be passed a prototype");
- v8::Local<v8::Object> o = args[0]->ToObject();
-
- // install method on the Thread object
- scope->injectV8Function("init", ThreadInit, o);
- scope->injectV8Function("start", ThreadStart, o);
- scope->injectV8Function("join", ThreadJoin, o);
- scope->injectV8Function("hasFailed", ThreadHasFailed, o);
- scope->injectV8Function("returnData", ThreadReturnData, o);
- return handle_scope.Escape(v8::Local<v8::Value>());
-}
-
-v8::Local<v8::Value> ScopedThreadInject(V8Scope* scope,
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::EscapableHandleScope handle_scope(args.GetIsolate());
- jsassert(args.Length() == 1, "threadInject takes exactly 1 argument");
- jsassert(args[0]->IsObject(), "threadInject needs to be passed a prototype");
- v8::Local<v8::Object> o = args[0]->ToObject();
-
- scope->injectV8Function("init", ScopedThreadInit, o);
- // inheritance takes care of other member functions
-
- return handle_scope.Escape(v8::Local<v8::Value>());
-}
-
-void installFork(V8Scope* scope, v8::Local<v8::Object> global, v8::Local<v8::Context> context) {
- scope->injectV8Function("_threadInject", ThreadInject, global);
- scope->injectV8Function("_scopedThreadInject", ScopedThreadInject, global);
-
- scope->setObject("CountDownLatch", BSONObj(), false);
- v8::Handle<v8::Object> cdl = scope->get("CountDownLatch").As<v8::Object>();
- scope->injectV8Function("_new", CountDownLatchNew, cdl);
- scope->injectV8Function("_await", CountDownLatchAwait, cdl);
- scope->injectV8Function("_countDown", CountDownLatchCountDown, cdl);
- scope->injectV8Function("_getCount", CountDownLatchGetCount, cdl);
-}
-
-v8::Local<v8::Value> v8AssertionException(const char* errorMessage) {
- v8::Isolate* isolate = v8::Isolate::GetCurrent();
- return isolate->ThrowException(
- v8::Exception::Error(v8::String::NewFromUtf8(isolate, errorMessage)));
-}
-v8::Local<v8::Value> v8AssertionException(const std::string& errorMessage) {
- return v8AssertionException(errorMessage.c_str());
-}
-}
diff --git a/src/mongo/scripting/v8-3.25_utils.h b/src/mongo/scripting/v8-3.25_utils.h
deleted file mode 100644
index 3e13af74b1e..00000000000
--- a/src/mongo/scripting/v8-3.25_utils.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// v8_utils.h
-
-/* Copyright 2014 MongoDB 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/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <cstdio>
-#include <cstdlib>
-#include <string>
-#include <v8.h>
-
-#include <mongo/base/string_data.h>
-#include <mongo/util/assert_util.h>
-
-namespace mongo {
-
-#define jsassert(x, msg) uassert(16664, (msg), (x))
-
-#define argumentCheck(mustBeTrue, errorMessage) \
- if (!(mustBeTrue)) { \
- return v8AssertionException((errorMessage)); \
- }
-
-std::ostream& operator<<(std::ostream& s, const v8::Local<v8::Value>& o);
-std::ostream& operator<<(std::ostream& s, const v8::Local<v8::TryCatch>* try_catch);
-
-/** Simple v8 object to string conversion helper */
-std::string toSTLString(const v8::Local<v8::Value>& o);
-
-/** Like toSTLString but doesn't allocate a new std::string
- *
- * This owns the string's memory so you need to be careful not to let the
- * converted StringDatas outlive the V8Scope object. These rules are the
- * same as converting from a std::string into a StringData.
- *
- * Safe:
- * void someFunction(StringData argument);
- * v8::Local<v8::String> aString;
- *
- * someFunction(V8String(aString)); // passing down stack as temporary
- *
- * V8String named (aString);
- * someFunction(named); // passing up stack as named value
- *
- * StringData sd = named; // scope of sd is less than named
- *
- * Unsafe:
- * StringData _member;
- *
- * StringData returningFunction() {
- * StringData sd = V8String(aString); // sd outlives the temporary
- *
- * V8String named(aString)
- * _member = named; // _member outlives named scope
- *
- * return V8String(aString); // passing up stack
- * }
- */
-class V8String {
-public:
- explicit V8String(const v8::Local<v8::Value>& o) : _str(o) {
- massert(16686, "error converting js type to Utf8Value", *_str);
- }
- operator StringData() const {
- return StringData(*_str, _str.length());
- }
-
-private:
- v8::String::Utf8Value _str;
-};
-
-/** Get the properties of an object (and it's prototype) as a comma-delimited string */
-std::string v8ObjectToString(const v8::Local<v8::Object>& o);
-
-class V8Scope;
-void installFork(V8Scope* scope, v8::Local<v8::Object> global, v8::Local<v8::Context> context);
-
-/** Throw a V8 exception from Mongo callback code; message text will be preceded by "Error: ".
- * Note: this function should be used for text that did not originate from the JavaScript
- * engine. Errors from the JavaScript engine will already have a prefix such as
- * ReferenceError, TypeError or SyntaxError.
- * Note: call only from a native function called from JavaScript (a callback).
- * The V8 ThrowException routine will note a JavaScript exception that will be
- * "thrown" in JavaScript when we return from the native function.
- * Note: it's required to return immediately to V8's execution control without calling any
- * V8 API functions. In this state, an empty handle may (will) be returned.
- * @param errorMessage Error message text.
- * @return Empty handle to be returned from callback function.
- */
-v8::Local<v8::Value> v8AssertionException(const char* errorMessage);
-v8::Local<v8::Value> v8AssertionException(const std::string& errorMessage);
-}
diff --git a/src/mongo/scripting/v8_db.cpp b/src/mongo/scripting/v8_db.cpp
deleted file mode 100644
index d0f533d316b..00000000000
--- a/src/mongo/scripting/v8_db.cpp
+++ /dev/null
@@ -1,1195 +0,0 @@
-// v8_db.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * 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/v8_db.h"
-
-#include <iostream>
-#include <iomanip>
-
-#include "mongo/base/init.h"
-#include "mongo/base/status_with.h"
-#include "mongo/client/native_sasl_client_session.h"
-#include "mongo/client/sasl_client_authenticate.h"
-#include "mongo/client/sasl_scramsha1_client_conversation.h"
-#include "mongo/db/namespace_string.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/s/d_state.h"
-#include "mongo/scripting/engine_v8.h"
-#include "mongo/scripting/v8_utils.h"
-#include "mongo/util/assert_util.h"
-#include "mongo/util/base64.h"
-#include "mongo/util/hex.h"
-#include "mongo/util/text.h"
-
-using namespace std;
-using std::unique_ptr;
-using std::shared_ptr;
-
-namespace mongo {
-
-namespace {
-std::vector<V8FunctionPrototypeManipulatorFn> _mongoPrototypeManipulators;
-bool _mongoPrototypeManipulatorsFrozen = false;
-
-MONGO_INITIALIZER(V8MongoPrototypeManipulatorRegistry)(InitializerContext* context) {
- return Status::OK();
-}
-
-MONGO_INITIALIZER_WITH_PREREQUISITES(V8MongoPrototypeManipulatorRegistrationDone,
- ("V8MongoPrototypeManipulatorRegistry"))
-(InitializerContext* context) {
- _mongoPrototypeManipulatorsFrozen = true;
- return Status::OK();
-}
-
-} // namespace
-
-void v8RegisterMongoPrototypeManipulator(const V8FunctionPrototypeManipulatorFn& manipulator) {
- fassert(16467, !_mongoPrototypeManipulatorsFrozen);
- _mongoPrototypeManipulators.push_back(manipulator);
-}
-
-static v8::Handle<v8::Value> newInstance(v8::Handle<v8::Function> f, const v8::Arguments& args) {
- // need to translate arguments into an array
- v8::HandleScope handle_scope;
- const int argc = args.Length();
- static const int MAX_ARGC = 24;
- uassert(16858, "Too many arguments. Max is 24", argc <= MAX_ARGC);
-
- // TODO SERVER-8016: properly allocate handles on the stack
- v8::Handle<v8::Value> argv[MAX_ARGC];
- for (int i = 0; i < argc; ++i) {
- argv[i] = args[i];
- }
- return handle_scope.Close(f->NewInstance(argc, argv));
-}
-
-v8::Handle<v8::FunctionTemplate> getInternalCursorFunctionTemplate(V8Scope* scope) {
- v8::Handle<v8::FunctionTemplate> ic = scope->createV8Function(internalCursorCons);
- ic->InstanceTemplate()->SetInternalFieldCount(1);
- v8::Handle<v8::ObjectTemplate> icproto = ic->PrototypeTemplate();
- scope->injectV8Method("next", internalCursorNext, icproto);
- scope->injectV8Method("hasNext", internalCursorHasNext, icproto);
- scope->injectV8Method("objsLeftInBatch", internalCursorObjsLeftInBatch, icproto);
- scope->injectV8Method("readOnly", internalCursorReadOnly, icproto);
- return ic;
-}
-
-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::ObjectTemplate> proto = mongo->PrototypeTemplate();
- scope->injectV8Method("runCommand", mongoRunCommand, proto);
- scope->injectV8Method("find", mongoFind, proto);
- scope->injectV8Method("insert", mongoInsert, proto);
- scope->injectV8Method("remove", mongoRemove, proto);
- scope->injectV8Method("update", mongoUpdate, proto);
- scope->injectV8Method("auth", mongoAuth, proto);
- scope->injectV8Method("logout", mongoLogout, proto);
- scope->injectV8Method("cursorFromId", mongoCursorFromId, proto);
- scope->injectV8Method("copyDatabaseWithSCRAM", mongoCopyDatabaseWithSCRAM, proto);
- scope->injectV8Method("getClientRPCProtocols", mongoGetClientRPCProtocols, proto);
- scope->injectV8Method("setClientRPCProtocols", mongoSetClientRPCProtocols, proto);
- scope->injectV8Method("getServerRPCProtocols", mongoGetServerRPCProtocols, proto);
-
- fassert(16468, _mongoPrototypeManipulatorsFrozen);
- for (size_t i = 0; i < _mongoPrototypeManipulators.size(); ++i)
- _mongoPrototypeManipulators[i](scope, mongo);
-
- return mongo;
-}
-
-
-v8::Handle<v8::Value> mongoConsExternal(V8Scope* scope, const v8::Arguments& args) {
- string host = "127.0.0.1";
- if (args.Length() > 0 && args[0]->IsString()) {
- v8::String::Utf8Value utf(args[0]);
- host = string(*utf);
- }
-
- // only allow function template to be used by a constructor
- uassert(16859, "Mongo function is only usable as a constructor", args.IsConstructCall());
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- auto statusWithHost = ConnectionString::parse(host);
- if (!statusWithHost.isOK()) {
- return v8AssertionException(statusWithHost.getStatus().reason());
- }
-
- const ConnectionString cs(statusWithHost.getValue());
-
- string errmsg;
- DBClientBase* conn;
- conn = cs.connect(errmsg);
- if (!conn) {
- return v8AssertionException(errmsg);
- }
-
- v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This());
- v8::Local<v8::External> connHandle = scope->dbClientBaseTracker.track(self, conn);
-
- ScriptEngine::runConnectCallback(*conn);
-
- args.This()->SetInternalField(0, connHandle);
- args.This()->ForceSet(scope->v8StringData("slaveOk"), v8::Boolean::New(false));
- args.This()->ForceSet(scope->v8StringData("host"), scope->v8StringData(host));
-
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 0, "local Mongo constructor takes no args")
-
- // only allow function template to be used by a constructor
- uassert(16860, "Mongo function is only usable as a constructor", args.IsConstructCall());
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- DBClientBase* conn = createDirectClient(scope->getOpContext());
- v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This());
- v8::Local<v8::External> connHandle = scope->dbClientBaseTracker.track(self, conn);
-
- args.This()->SetInternalField(0, connHandle);
- args.This()->ForceSet(scope->v8StringData("slaveOk"), v8::Boolean::New(false));
- args.This()->ForceSet(scope->v8StringData("host"), scope->v8StringData("EMBEDDED"));
-
- return v8::Undefined();
-}
-
-std::shared_ptr<DBClientBase> getConnection(V8Scope* scope, const v8::Arguments& args) {
- verify(scope->MongoFT()->HasInstance(args.This()));
- verify(args.This()->InternalFieldCount() == 1);
- v8::Local<v8::External> c = v8::External::Cast(*(args.This()->GetInternalField(0)));
- std::shared_ptr<DBClientBase>* conn = static_cast<std::shared_ptr<DBClientBase>*>(c->Value());
- massert(16667, "Unable to get db client connection", conn && conn->get());
- return *conn;
-}
-
-/**
- * JavaScript binding for Mongo.prototype.runCommand(database, cmdObj, queryOptions)
- */
-v8::Handle<v8::Value> mongoRunCommand(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 3, "runCommand needs 3 args");
- argumentCheck(args[0]->IsString(), "the database parameter to runCommand must be a string");
- argumentCheck(args[1]->IsObject(), "the cmdObj parameter to runCommand must be an object");
- argumentCheck(args[2]->IsNumber(), "the options parameter to runCommand must be a number");
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string database = toSTLString(args[0]);
- BSONObj cmdObj = scope->v8ToMongo(args[1]->ToObject());
- int queryOptions = args[2]->Int32Value();
- BSONObj cmdRes;
- conn->runCommand(database, cmdObj, cmdRes, queryOptions);
- // the returned object is not read only as some of our tests depend on modifying it.
- return scope->mongoToLZV8(cmdRes, false /* read only */);
-}
-
-/**
- * JavaScript binding for Mongo.prototype.find(namespace, query, fields, limit, skip)
- */
-v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 7, "find needs 7 args")
- argumentCheck(args[1]->IsObject(), "needs to be an object")
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
- BSONObj fields;
- BSONObj q = scope->v8ToMongo(args[1]->ToObject());
- bool haveFields = args[2]->IsObject() && args[2]->ToObject()->GetPropertyNames()->Length() > 0;
- if (haveFields)
- fields = scope->v8ToMongo(args[2]->ToObject());
-
- std::shared_ptr<mongo::DBClientCursor> cursor;
- int nToReturn = args[3]->Int32Value();
- int nToSkip = args[4]->Int32Value();
- int batchSize = args[5]->Int32Value();
- int options = args[6]->Int32Value();
- cursor =
- conn->query(ns, q, nToReturn, nToSkip, haveFields ? &fields : NULL, options, batchSize);
- if (!cursor.get()) {
- return v8AssertionException("error doing query: failed");
- }
-
- v8::Handle<v8::Function> cons = scope->InternalCursorFT()->GetFunction();
- v8::Handle<v8::Object> c = cons->NewInstance();
- c->SetInternalField(0, v8::External::New(cursor.get()));
- scope->dbConnectionAndCursor.track(v8::Persistent<v8::Value>::New(c),
- new V8Scope::DBConnectionAndCursor(conn, cursor));
- return c;
-}
-
-v8::Handle<v8::Value> mongoCursorFromId(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 2 || args.Length() == 3, "cursorFromId needs 2 or 3 args")
- argumentCheck(scope->NumberLongFT()->HasInstance(args[1]), "2nd arg must be a NumberLong")
- argumentCheck(args[2]->IsUndefined() || args[2]->IsNumber(),
- "3rd arg must be a js Number")
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
- long long cursorId = numberLongVal(scope, args[1]->ToObject());
-
- std::shared_ptr<mongo::DBClientCursor> cursor(
- new DBClientCursor(conn.get(), ns, cursorId, 0, 0));
-
- if (!args[2]->IsUndefined())
- cursor->setBatchSize(args[2]->Int32Value());
-
- v8::Handle<v8::Function> cons = scope->InternalCursorFT()->GetFunction();
- v8::Handle<v8::Object> c = cons->NewInstance();
- c->SetInternalField(0, v8::External::New(cursor.get()));
- scope->dbConnectionAndCursor.track(v8::Persistent<v8::Value>::New(c),
- new V8Scope::DBConnectionAndCursor(conn, cursor));
- return c;
-}
-
-v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 3, "insert needs 3 args")
- argumentCheck(args[1]->IsObject(), "attempted to insert a non-object")
-
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) {
- return v8AssertionException("js db in read only mode");
- }
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
-
- v8::Handle<v8::Integer> flags = args[2]->ToInteger();
-
- if (args[1]->IsArray()) {
- v8::Local<v8::Array> arr = v8::Array::Cast(*args[1]);
- vector<BSONObj> bos;
- uint32_t len = arr->Length();
- argumentCheck(len > 0, "attempted to insert an empty array")
-
- for (uint32_t i = 0; i < len; i++) {
- v8::Local<v8::Object> el = arr->CloneElementAt(i);
- argumentCheck(!el.IsEmpty(), "attempted to insert an array of non-object types")
-
- // Set ID on the element if necessary
- if (!el->Has(scope->v8StringData("_id"))) {
- v8::Handle<v8::Value> argv[1];
- el->ForceSet(scope->v8StringData("_id"),
- scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv));
- }
- bos.push_back(scope->v8ToMongo(el));
- }
- conn->insert(ns, bos, flags->Int32Value());
- } else {
- v8::Handle<v8::Object> in = args[1]->ToObject();
- if (!in->Has(scope->v8StringData("_id"))) {
- v8::Handle<v8::Value> argv[1];
- in->ForceSet(scope->v8StringData("_id"),
- scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv));
- }
- BSONObj o = scope->v8ToMongo(in);
- conn->insert(ns, o);
- }
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 2 || args.Length() == 3, "remove needs 2 or 3 args")
- argumentCheck(args[1]->IsObject(), "attempted to remove a non-object")
-
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) {
- return v8AssertionException("js db in read only mode");
- }
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
-
- v8::Handle<v8::Object> in = args[1]->ToObject();
- BSONObj o = scope->v8ToMongo(in);
-
- bool justOne = false;
- if (args.Length() > 2) {
- justOne = args[2]->BooleanValue();
- }
-
- conn->remove(ns, o, justOne);
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() >= 3, "update needs at least 3 args")
- argumentCheck(args[1]->IsObject(), "1st param to update has to be an object")
- argumentCheck(args[2]->IsObject(), "2nd param to update has to be an object")
-
- verify(scope->MongoFT()->HasInstance(args.This()));
-
- if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) {
- return v8AssertionException("js db in read only mode");
- }
-
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- const string ns = toSTLString(args[0]);
-
- 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();
-
- BSONObj q1 = scope->v8ToMongo(q);
- BSONObj o1 = scope->v8ToMongo(o);
- conn->update(ns, q1, o1, upsert, multi);
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> mongoAuth(V8Scope* scope, const v8::Arguments& args) {
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- if (NULL == conn)
- return v8AssertionException("no connection");
-
- BSONObj params;
- switch (args.Length()) {
- case 1:
- params = scope->v8ToMongo(args[0]->ToObject());
- break;
- case 3:
- params = BSON(saslCommandMechanismFieldName
- << "MONGODB-CR" << saslCommandUserDBFieldName << toSTLString(args[0])
- << saslCommandUserFieldName << toSTLString(args[1])
- << saslCommandPasswordFieldName << toSTLString(args[2]));
- break;
- default:
- return v8AssertionException("mongoAuth takes 1 object or 3 string arguments");
- }
- try {
- conn->auth(params);
- } catch (const DBException& ex) {
- return v8AssertionException(ex.toString());
- }
- return v8::Boolean::New(true);
-}
-
-v8::Handle<v8::Value> mongoLogout(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 1, "logout needs 1 arg") std::shared_ptr<DBClientBase> conn =
- getConnection(scope, args);
- const string db = toSTLString(args[0]);
- BSONObj ret;
- conn->logout(db, ret);
- return scope->mongoToLZV8(ret, false);
-}
-
-v8::Handle<v8::Value> mongoCopyDatabaseWithSCRAM(V8Scope* scope, const v8::Arguments& args) {
- std::shared_ptr<DBClientBase> conn = getConnection(scope, args);
- if (NULL == conn)
- return v8AssertionException("no connection");
-
- argumentCheck(args.Length() == 5, "copyDatabase needs 5 arg");
-
- // copyDatabase(fromdb, todb, fromhost, username, password);
- std::string fromDb = toSTLString(args[0]);
- std::string toDb = toSTLString(args[1]);
- std::string fromHost = toSTLString(args[2]);
- std::string user = toSTLString(args[3]);
- std::string hashedPwd = DBClientWithCommands::createPasswordDigest(user, toSTLString(args[4]));
-
- std::unique_ptr<SaslClientSession> session(new NativeSaslClientSession());
-
- session->setParameter(SaslClientSession::parameterMechanism, "SCRAM-SHA-1");
- session->setParameter(SaslClientSession::parameterUser, user);
- session->setParameter(SaslClientSession::parameterPassword, hashedPwd);
- session->initialize();
-
- BSONObj saslFirstCommandPrefix =
- BSON("copydbsaslstart" << 1 << "fromhost" << fromHost << "fromdb" << fromDb
- << saslCommandMechanismFieldName << "SCRAM-SHA-1");
-
- BSONObj saslFollowupCommandPrefix =
- BSON("copydb" << 1 << "fromhost" << fromHost << "fromdb" << fromDb << "todb" << toDb);
-
- BSONObj saslCommandPrefix = saslFirstCommandPrefix;
- BSONObj inputObj = BSON(saslCommandPayloadFieldName << "");
- bool isServerDone = false;
-
- while (!session->isDone()) {
- std::string payload;
- BSONType type;
-
- Status status = saslExtractPayload(inputObj, &payload, &type);
- if (!status.isOK()) {
- return v8AssertionException(status.reason());
- }
-
- std::string responsePayload;
- status = session->step(payload, &responsePayload);
- if (!status.isOK()) {
- return v8AssertionException(status.reason());
- }
-
- BSONObjBuilder commandBuilder;
-
- commandBuilder.appendElements(saslCommandPrefix);
- commandBuilder.appendBinData(saslCommandPayloadFieldName,
- int(responsePayload.size()),
- BinDataGeneral,
- responsePayload.c_str());
- BSONElement conversationId = inputObj[saslCommandConversationIdFieldName];
- if (!conversationId.eoo())
- commandBuilder.append(conversationId);
-
- BSONObj command = commandBuilder.obj();
-
- bool ok = conn->runCommand("admin", command, inputObj);
-
- ErrorCodes::Error code =
- ErrorCodes::fromInt(inputObj[saslCommandCodeFieldName].numberInt());
-
- if (!ok || code != ErrorCodes::OK) {
- if (code == ErrorCodes::OK)
- code = ErrorCodes::UnknownError;
-
- return scope->mongoToLZV8(inputObj, true);
- }
-
- isServerDone = inputObj[saslCommandDoneFieldName].trueValue();
- saslCommandPrefix = saslFollowupCommandPrefix;
- }
-
- if (!isServerDone) {
- return v8AssertionException("copydb client finished before server.");
- }
-
- return scope->mongoToLZV8(inputObj, true);
-}
-
-v8::Handle<v8::Value> mongoGetClientRPCProtocols(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 0, "getClientRPCProtocols takes no args");
- auto conn = getConnection(scope, args);
- auto clientRPCProtocols = rpc::toString(conn->getClientRPCProtocols());
- if (!clientRPCProtocols.isOK()) {
- return v8AssertionException(clientRPCProtocols.getStatus().reason());
- }
-
- // make an owned copy so we can safely pass a null-terminated string to v8
- auto protoStr = clientRPCProtocols.getValue().toString();
- return v8::String::New(protoStr.c_str());
-}
-
-v8::Handle<v8::Value> mongoSetClientRPCProtocols(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 1, "setClientRPCProtocols needs 1 arg");
- argumentCheck(args[0]->IsString(), "first argument to setClientRPCProtocols must be a string");
- auto conn = getConnection(scope, args);
- const auto rpcProtosStr = toSTLString(args[0]);
- auto clientRPCProtocols = rpc::parseProtocolSet(rpcProtosStr);
- if (!clientRPCProtocols.isOK()) {
- return v8AssertionException(clientRPCProtocols.getStatus().reason());
- }
- conn->setClientRPCProtocols(clientRPCProtocols.getValue());
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> mongoGetServerRPCProtocols(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 0, "getServerRPCProtocols takes no args");
- auto conn = getConnection(scope, args);
- auto serverRPCProtocols = rpc::toString(conn->getServerRPCProtocols());
- if (!serverRPCProtocols.isOK()) {
- return v8AssertionException(serverRPCProtocols.getStatus().reason());
- }
- auto protoStr = serverRPCProtocols.getValue().toString();
- return v8::String::New(protoStr.c_str());
-}
-
-/**
- * get cursor from v8 argument
- */
-mongo::DBClientCursor* getCursor(V8Scope* scope, const v8::Arguments& args) {
- verify(scope->InternalCursorFT()->HasInstance(args.This()));
- verify(args.This()->InternalFieldCount() == 1);
- v8::Local<v8::External> c = v8::External::Cast(*(args.This()->GetInternalField(0)));
- mongo::DBClientCursor* cursor = static_cast<mongo::DBClientCursor*>(c->Value());
- return cursor;
-}
-
-v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Arguments& args) {
- return v8::Undefined();
-}
-
-/**
- * cursor.next()
- */
-v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Arguments& args) {
- mongo::DBClientCursor* cursor = getCursor(scope, args);
- if (!cursor)
- return v8::Undefined();
- BSONObj o = cursor->next();
- bool ro = false;
- if (args.This()->Has(v8::String::New("_ro")))
- ro = args.This()->Get(v8::String::New("_ro"))->BooleanValue();
- return scope->mongoToLZV8(o, ro);
-}
-
-/**
- * cursor.hasNext()
- */
-v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args) {
- mongo::DBClientCursor* cursor = getCursor(scope, args);
- if (!cursor)
- return v8::Boolean::New(false);
- return v8::Boolean::New(cursor->more());
-}
-
-/**
- * cursor.objsLeftInBatch()
- */
-v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, const v8::Arguments& args) {
- mongo::DBClientCursor* cursor = getCursor(scope, args);
- if (!cursor)
- return v8::Number::New(0.0);
- return v8::Number::New(static_cast<double>(cursor->objsLeftInBatch()));
-}
-
-/**
- * cursor.readOnly()
- */
-v8::Handle<v8::Value> internalCursorReadOnly(V8Scope* scope, const v8::Arguments& args) {
- verify(scope->InternalCursorFT()->HasInstance(args.This()));
-
- v8::Local<v8::Object> cursor = args.This();
- cursor->ForceSet(v8::String::New("_ro"), v8::Boolean::New(true));
- return cursor;
-}
-
-v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->DBFT()->GetFunction();
- return newInstance(f, args);
- }
-
- verify(scope->DBFT()->HasInstance(args.This()));
-
- argumentCheck(args.Length() == 2, "db constructor requires 2 arguments")
-
- args.This()->ForceSet(scope->v8StringData("_mongo"), args[0]);
- args.This()->ForceSet(scope->v8StringData("_name"), args[1]);
-
- for (int i = 0; i < args.Length(); i++) {
- argumentCheck(!args[i]->IsUndefined(), "db initializer called with undefined argument")
- }
-
- string dbName = toSTLString(args[1]);
- if (!NamespaceString::validDBName(dbName)) {
- return v8AssertionException(str::stream() << "[" << dbName
- << "] is not a valid database name");
- }
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->DBCollectionFT()->GetFunction();
- return newInstance(f, args);
- }
-
- verify(scope->DBCollectionFT()->HasInstance(args.This()));
-
- argumentCheck(args.Length() == 4, "collection constructor requires 4 arguments")
-
- for (int i = 0; i < args.Length(); i++) {
- argumentCheck(!args[i]->IsUndefined(),
- "collection constructor called with undefined argument")
- }
-
- args.This()->ForceSet(scope->v8StringData("_mongo"), args[0]);
- args.This()->ForceSet(scope->v8StringData("_db"), args[1]);
- args.This()->ForceSet(scope->v8StringData("_shortName"), args[2]);
- args.This()->ForceSet(v8::String::New("_fullName"), args[3]);
-
- auto context = scope->getOpContext();
- if (context && haveLocalShardingInfo(context->getClient(), toSTLString(args[3]))) {
- return v8AssertionException("can't use sharded collection from db.eval");
- }
-
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> dbQueryInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->DBQueryFT()->GetFunction();
- return newInstance(f, args);
- }
-
- verify(scope->DBQueryFT()->HasInstance(args.This()));
-
- argumentCheck(args.Length() >= 4, "dbQuery constructor requires at least 4 arguments")
-
- v8::Handle<v8::Object> t = args.This();
- t->ForceSet(scope->v8StringData("_mongo"), args[0]);
- t->ForceSet(scope->v8StringData("_db"), args[1]);
- t->ForceSet(scope->v8StringData("_collection"), args[2]);
- t->ForceSet(scope->v8StringData("_ns"), args[3]);
-
- if (args.Length() > 4 && args[4]->IsObject())
- t->ForceSet(scope->v8StringData("_query"), args[4]);
- else
- t->ForceSet(scope->v8StringData("_query"), v8::Object::New());
-
- if (args.Length() > 5 && args[5]->IsObject())
- t->ForceSet(scope->v8StringData("_fields"), args[5]);
- else
- t->ForceSet(scope->v8StringData("_fields"), v8::Null());
-
- if (args.Length() > 6 && args[6]->IsNumber())
- t->ForceSet(scope->v8StringData("_limit"), args[6]);
- else
- t->ForceSet(scope->v8StringData("_limit"), v8::Number::New(0));
-
- if (args.Length() > 7 && args[7]->IsNumber())
- t->ForceSet(scope->v8StringData("_skip"), args[7]);
- else
- t->ForceSet(scope->v8StringData("_skip"), v8::Number::New(0));
-
- if (args.Length() > 8 && args[8]->IsNumber())
- t->ForceSet(scope->v8StringData("_batchSize"), args[8]);
- else
- t->ForceSet(scope->v8StringData("_batchSize"), v8::Number::New(0));
-
- if (args.Length() > 9 && args[9]->IsNumber())
- t->ForceSet(scope->v8StringData("_options"), args[9]);
- else
- t->ForceSet(scope->v8StringData("_options"), v8::Number::New(0));
-
- t->ForceSet(scope->v8StringData("_cursor"), v8::Null());
- t->ForceSet(scope->v8StringData("_numReturned"), v8::Number::New(0));
- t->ForceSet(scope->v8StringData("_special"), v8::Boolean::New(false));
-
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> collectionSetter(v8::Local<v8::String> name,
- v8::Local<v8::Value> value,
- const v8::AccessorInfo& info) {
- try {
- V8Scope* scope = getScope(info.GetIsolate());
-
- // Both DB and Collection objects use this setter
- verify(scope->DBCollectionFT()->HasInstance(info.This()) ||
- scope->DBFT()->HasInstance(info.This()));
-
- // a collection name cannot be overwritten by a variable
- string sname = toSTLString(name);
- if (sname.length() == 0 || sname[0] == '_') {
- // if starts with '_' we allow overwrite
- return v8::Handle<v8::Value>();
- }
- // dont set
- return value;
- } catch (const DBException& dbEx) {
- return v8AssertionException(dbEx.toString());
- } catch (...) {
- return v8AssertionException("unknown error in collationSetter");
- }
-}
-
-v8::Handle<v8::Value> collectionGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) {
- try {
- V8Scope* scope = getScope(info.GetIsolate());
-
- // Both DB and Collection objects use this getter
- verify(scope->DBCollectionFT()->HasInstance(info.This()) ||
- scope->DBFT()->HasInstance(info.This()));
-
- v8::TryCatch tryCatch;
-
- // first look in prototype, may be a function
- v8::Handle<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get(name);
- if (!real->IsUndefined())
- return real;
-
- // 2nd look into real values, may be cached collection object
- string sname = toSTLString(name);
- if (info.This()->HasRealNamedProperty(name)) {
- v8::Local<v8::Value> prop = info.This()->GetRealNamedProperty(name);
- if (prop->IsObject() &&
- prop->ToObject()->HasRealNamedProperty(v8::String::New("_fullName"))) {
- // need to check every time that the collection did not get sharded
- auto context = scope->getOpContext();
- if (context &&
- haveLocalShardingInfo(context->getClient(),
- toSTLString(prop->ToObject()->GetRealNamedProperty(
- v8::String::New("_fullName"))))) {
- return v8AssertionException("can't use sharded collection from db.eval");
- }
- }
- return prop;
- } else if (sname.length() == 0 || sname[0] == '_') {
- // if starts with '_' we dont return collection, one must use getCollection()
- return v8::Handle<v8::Value>();
- }
-
- // no hit, create new collection
- v8::Handle<v8::Value> getCollection =
- info.This()->GetPrototype()->ToObject()->Get(v8::String::New("getCollection"));
- if (!getCollection->IsFunction()) {
- return v8AssertionException("getCollection is not a function");
- }
-
- v8::Handle<v8::Function> f = getCollection.As<v8::Function>();
- v8::Handle<v8::Value> argv[1];
- argv[0] = name;
- v8::Local<v8::Value> coll = f->Call(info.This(), 1, argv);
- if (coll.IsEmpty())
- return tryCatch.ReThrow();
-
- uassert(16861,
- "getCollection returned something other than a collection",
- scope->DBCollectionFT()->HasInstance(coll));
-
- // cache collection for reuse, don't enumerate
- info.This()->ForceSet(name, coll, v8::DontEnum);
- return coll;
- } catch (const DBException& dbEx) {
- return v8AssertionException(dbEx.toString());
- } catch (...) {
- return v8AssertionException("unknown error in collectionGetter");
- }
-}
-
-v8::Handle<v8::Value> dbQueryIndexAccess(unsigned int index, const v8::AccessorInfo& info) {
- try {
- V8Scope* scope = getScope(info.GetIsolate());
- verify(scope->DBQueryFT()->HasInstance(info.This()));
-
- v8::Handle<v8::Value> arrayAccess =
- info.This()->GetPrototype()->ToObject()->Get(v8::String::New("arrayAccess"));
- massert(16660, "arrayAccess is not a function", arrayAccess->IsFunction());
-
- v8::Handle<v8::Function> f = arrayAccess.As<v8::Function>();
- v8::Handle<v8::Value> argv[1];
- argv[0] = v8::Number::New(index);
-
- return f->Call(info.This(), 1, argv);
- } catch (const DBException& dbEx) {
- return v8AssertionException(dbEx.toString());
- } catch (...) {
- return v8AssertionException("unknown error in dbQueryIndexAccess");
- }
-}
-
-v8::Handle<v8::Value> objectIdInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->ObjectIdFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Handle<v8::Object> it = args.This();
- verify(scope->ObjectIdFT()->HasInstance(it));
-
- OID oid;
- if (args.Length() == 0) {
- oid.init();
- } else {
- string s = toSTLString(args[0]);
- try {
- Scope::validateObjectIdString(s);
- } catch (const MsgAssertionException& m) {
- return v8AssertionException(m.toString());
- }
- oid.init(s);
- }
-
- it->ForceSet(scope->v8StringData("str"), v8::String::New(oid.toString().c_str()));
- return it;
-}
-
-v8::Handle<v8::Value> dbRefInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->DBRefFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Handle<v8::Object> it = args.This();
- verify(scope->DBRefFT()->HasInstance(it));
-
- argumentCheck(args.Length() >= 2 && args.Length() <= 3, "DBRef needs 2 or 3 arguments")
- argumentCheck(args[0]->IsString(), "DBRef 1st parameter must be a string")
- it->ForceSet(scope->v8StringData("$ref"), args[0]);
- it->ForceSet(scope->v8StringData("$id"), args[1]);
-
- if (args.Length() == 3) {
- argumentCheck(args[2]->IsString(), "DBRef 3rd parameter must be a string")
- it->ForceSet(scope->v8StringData("$db"), args[2]);
- }
-
- return it;
-}
-
-v8::Handle<v8::Value> dbPointerInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->DBPointerFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Handle<v8::Object> it = args.This();
- verify(scope->DBPointerFT()->HasInstance(it));
-
- argumentCheck(args.Length() == 2, "DBPointer needs 2 arguments")
- argumentCheck(args[0]->IsString(), "DBPointer 1st parameter must be a string")
- argumentCheck(scope->ObjectIdFT()->HasInstance(args[1]),
- "DBPointer 2nd parameter must be an ObjectId")
-
- it->ForceSet(scope->v8StringData("ns"), args[0]);
- it->ForceSet(scope->v8StringData("id"), args[1]);
- return it;
-}
-
-v8::Handle<v8::Value> dbTimestampInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->TimestampFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Handle<v8::Object> it = args.This();
- verify(scope->TimestampFT()->HasInstance(it));
-
- if (args.Length() == 0) {
- it->ForceSet(scope->v8StringData("t"), v8::Number::New(0));
- it->ForceSet(scope->v8StringData("i"), v8::Number::New(0));
- } else if (args.Length() == 2) {
- if (!args[0]->IsNumber()) {
- return v8AssertionException("Timestamp time must be a number");
- }
- if (!args[1]->IsNumber()) {
- return v8AssertionException("Timestamp increment must be a number");
- }
- int64_t t = args[0]->IntegerValue();
- int64_t largestVal = int64_t(Timestamp::max().getSecs());
- if (t > largestVal)
- return v8AssertionException(str::stream() << "The first argument must be in seconds; "
- << t << " is too large (max " << largestVal
- << ")");
- it->ForceSet(scope->v8StringData("t"), args[0]);
- it->ForceSet(scope->v8StringData("i"), args[1]);
- } else {
- return v8AssertionException("Timestamp needs 0 or 2 arguments");
- }
-
- return it;
-}
-
-v8::Handle<v8::Value> binDataInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Local<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
-
- argumentCheck(args.Length() == 2, "BinData takes 2 arguments -- BinData(subtype,data)");
-
- // 2 args: type, base64 string
- v8::Handle<v8::Value> type = args[0];
- if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) {
- return v8AssertionException(
- "BinData subtype must be a Number between 0 and 255 inclusive)");
- }
- v8::String::Utf8Value utf(args[1]);
- // uassert if invalid base64 string
- string tmpBase64 = base64::decode(*utf);
- // length property stores the decoded length
- it->ForceSet(scope->v8StringData("len"),
- v8::Number::New(tmpBase64.length()),
- v8::PropertyAttribute::ReadOnly);
- it->ForceSet(scope->v8StringData("type"), type, v8::PropertyAttribute::ReadOnly);
- it->SetInternalField(0, args[1]);
-
- return it;
-}
-
-v8::Handle<v8::Value> binDataToString(V8Scope* scope, const v8::Arguments& args) {
- v8::Handle<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
- int type = it->Get(v8::String::New("type"))->Int32Value();
-
- stringstream ss;
- verify(it->InternalFieldCount() == 1);
- ss << "BinData(" << type << ",\"" << toSTLString(it->GetInternalField(0)) << "\")";
- return v8::String::New(ss.str().c_str());
-}
-
-v8::Handle<v8::Value> binDataToBase64(V8Scope* scope, const v8::Arguments& args) {
- v8::Handle<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
- verify(it->InternalFieldCount() == 1);
- return it->GetInternalField(0);
-}
-
-v8::Handle<v8::Value> binDataToHex(V8Scope* scope, const v8::Arguments& args) {
- v8::Handle<v8::Object> it = args.This();
- verify(scope->BinDataFT()->HasInstance(it));
- verify(it->InternalFieldCount() == 1);
- string data = base64::decode(toSTLString(it->GetInternalField(0)));
- stringstream ss;
- ss.setf(ios_base::hex, ios_base::basefield);
- ss.fill('0');
- ss.setf(ios_base::right, ios_base::adjustfield);
- for (std::string::iterator it = data.begin(); it != data.end(); ++it) {
- unsigned v = (unsigned char)*it;
- ss << setw(2) << v;
- }
- return v8::String::New(ss.str().c_str());
-}
-
-static v8::Handle<v8::Value> hexToBinData(V8Scope* scope, int type, string hexstr) {
- // SERVER-9686: This function does not correctly check to make sure hexstr is actually made
- // up of valid hex digits, and fails in the hex utility functions
-
- int len = hexstr.length() / 2;
- unique_ptr<char[]> data(new char[len]);
- const char* src = hexstr.c_str();
- for (int i = 0; i < len; i++) {
- data[i] = fromHex(src + i * 2);
- }
-
- string encoded = base64::encode(data.get(), len);
- v8::Handle<v8::Value> argv[2];
- argv[0] = v8::Number::New(type);
- argv[1] = v8::String::New(encoded.c_str());
- return scope->BinDataFT()->GetFunction()->NewInstance(2, argv);
-}
-
-v8::Handle<v8::Value> uuidInit(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 1, "UUID needs 1 argument") v8::String::Utf8Value utf(args[0]);
- argumentCheck(utf.length() == 32,
- "UUID string must have 32 characters") return hexToBinData(scope, bdtUUID, *utf);
-}
-
-v8::Handle<v8::Value> md5Init(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 1, "MD5 needs 1 argument") v8::String::Utf8Value utf(args[0]);
- argumentCheck(utf.length() == 32,
- "MD5 string must have 32 characters") return hexToBinData(scope, MD5Type, *utf);
-}
-
-v8::Handle<v8::Value> hexDataInit(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 2, "HexData needs 2 arguments") v8::Handle<v8::Value> type =
- args[0];
- if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) {
- return v8AssertionException("HexData subtype must be a Number between 0 and 255 inclusive");
- }
- v8::String::Utf8Value utf(args[1]);
- return hexToBinData(scope, type->Int32Value(), *utf);
-}
-
-v8::Handle<v8::Value> numberLongInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->NumberLongFT()->GetFunction();
- return newInstance(f, args);
- }
-
- argumentCheck(args.Length() == 0 || args.Length() == 1 || args.Length() == 3,
- "NumberLong needs 0, 1 or 3 arguments")
-
- v8::Handle<v8::Object> it = args.This();
- verify(scope->NumberLongFT()->HasInstance(it));
-
- if (args.Length() == 0) {
- it->ForceSet(scope->v8StringData("floatApprox"), v8::Number::New(0));
- } else if (args.Length() == 1) {
- if (args[0]->IsNumber()) {
- it->ForceSet(scope->v8StringData("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 v8AssertionException(string("could not convert \"") + num +
- "\" to NumberLong");
- }
- unsigned long long val = n;
- // values above 2^53 are not accurately represented in JS
- if ((long long)val == (long long)(double)(long long)(val) &&
- val < 9007199254740992ULL) {
- it->ForceSet(scope->v8StringData("floatApprox"),
- v8::Number::New((double)(long long)(val)));
- } else {
- it->ForceSet(scope->v8StringData("floatApprox"),
- v8::Number::New((double)(long long)(val)));
- it->ForceSet(scope->v8StringData("top"), v8::Integer::New(val >> 32));
- it->ForceSet(scope->v8StringData("bottom"),
- v8::Integer::New((unsigned long)(val & 0x00000000ffffffff)));
- }
- }
- } else {
- it->ForceSet(scope->v8StringData("floatApprox"), args[0]->ToNumber());
- it->ForceSet(scope->v8StringData("top"), args[1]->ToUint32());
- it->ForceSet(scope->v8StringData("bottom"), args[2]->ToUint32());
- }
- return it;
-}
-
-long long numberLongVal(V8Scope* scope, const v8::Handle<v8::Object>& it) {
- verify(scope->NumberLongFT()->HasInstance(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(scope, 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(scope, 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.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->NumberIntFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Handle<v8::Object> it = args.This();
- verify(scope->NumberIntFT()->HasInstance(it));
-
- argumentCheck(args.Length() == 0 || args.Length() == 1,
- "NumberInt needs 0 or 1 arguments") if (args.Length() == 0) {
- it->SetHiddenValue(v8::String::New("__NumberInt"), v8::Number::New(0));
- }
- else if (args.Length() == 1) {
- it->SetHiddenValue(v8::String::New("__NumberInt"), args[0]->ToInt32());
- }
- return it;
-}
-
-int numberIntVal(V8Scope* scope, const v8::Handle<v8::Object>& it) {
- verify(scope->NumberIntFT()->HasInstance(it));
- v8::Handle<v8::Value> value = it->GetHiddenValue(v8::String::New("__NumberInt"));
- verify(!value.IsEmpty());
- return value->Int32Value();
-}
-
-v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Arguments& args) {
- v8::Handle<v8::Object> it = args.This();
- return v8::Integer::New(numberIntVal(scope, it));
-}
-
-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();
- int val = numberIntVal(scope, it);
- string ret = str::stream() << "NumberInt(" << val << ")";
- return v8::String::New(ret.c_str());
-}
-
-Decimal128 numberDecimalVal(V8Scope* scope, const v8::Handle<v8::Object>& it) {
- verify(scope->NumberDecimalFT()->HasInstance(it));
- return Decimal128(toSTLString(it->Get(v8::String::New("val"))));
-}
-
-v8::Handle<v8::Value> numberDecimalInit(V8Scope* scope, const v8::Arguments& args) {
- if (!args.IsConstructCall()) {
- v8::Handle<v8::Function> f = scope->NumberDecimalFT()->GetFunction();
- return newInstance(f, args);
- }
-
- v8::Handle<v8::Object> it = args.This();
- verify(scope->NumberDecimalFT()->HasInstance(it));
- argumentCheck(args.Length() == 1, "NumberDecimal needs 1 argument");
- argumentCheck(args[0]->IsString(), "NumberDecimal 1st parameter must be a string");
-
- it->ForceSet(scope->v8StringData("val"), args[0]);
- return it;
-}
-
-v8::Handle<v8::Value> numberDecimalToString(V8Scope* scope, const v8::Arguments& args) {
- v8::Handle<v8::Object> it = args.This();
- Decimal128 val = numberDecimalVal(scope, it);
- string ret = str::stream() << "NumberDecimal(\"" << val.toString() << "\")";
- return v8::String::New(ret.c_str());
-}
-
-v8::Handle<v8::Value> v8ObjectInvalidForStorage(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 1, "invalidForStorage needs 1 argument") if (args[0]->IsNull()) {
- return v8::Null();
- }
- argumentCheck(args[0]->IsObject(), "argument to invalidForStorage has to be an object")
- Status validForStorage = scope->v8ToMongo(args[0]->ToObject()).storageValid(true);
- if (validForStorage.isOK()) {
- return v8::Null();
- }
-
- std::string errmsg = str::stream() << validForStorage.codeString() << ": "
- << validForStorage.reason();
- return v8::String::New(errmsg.c_str());
-}
-
-v8::Handle<v8::Value> bsonsize(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 1, "bsonsize needs 1 argument") if (args[0]->IsNull()) {
- return v8::Number::New(0);
- }
- argumentCheck(args[0]->IsObject(), "argument to bsonsize has to be an object") return v8::
- Number::New(scope->v8ToMongo(args[0]->ToObject()).objsize());
-}
-
-v8::Handle<v8::Value> bsonWoCompare(V8Scope* scope, const v8::Arguments& args) {
- argumentCheck(args.Length() == 2, "bsonWoCompare needs 2 argument");
-
- argumentCheck(args[0]->IsObject(), "first argument to bsonWoCompare has to be an object");
- argumentCheck(args[1]->IsObject(), "second argument to bsonWoCompare has to be an object");
-
- BSONObj firstObject(scope->v8ToMongo(args[0]->ToObject()));
- BSONObj secondObject(scope->v8ToMongo(args[1]->ToObject()));
-
- return v8::Number::New(firstObject.woCompare(secondObject));
-}
-}
diff --git a/src/mongo/scripting/v8_db.h b/src/mongo/scripting/v8_db.h
deleted file mode 100644
index 8bd965d9182..00000000000
--- a/src/mongo/scripting/v8_db.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// v8_db.h
-
-/* Copyright 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <v8.h>
-
-#include "mongo/scripting/engine_v8.h"
-#include "mongo/stdx/functional.h"
-
-namespace mongo {
-
-// Internal Cursor
-v8::Handle<v8::FunctionTemplate> getInternalCursorFunctionTemplate(V8Scope* scope);
-
-// Mongo constructors
-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::FunctionTemplate> getMongoFunctionTemplate(V8Scope* scope, bool local);
-
-// Mongo member functions
-v8::Handle<v8::Value> mongoRunCommand(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> mongoAuth(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> mongoLogout(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> mongoCursorFromId(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> mongoCopyDatabaseWithSCRAM(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> mongoGetClientRPCProtocols(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> mongoSetClientRPCProtocols(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> mongoGetServerRPCProtocols(V8Scope* scope, const v8::Arguments& args);
-
-
-// Cursor object
-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);
-v8::Handle<v8::Value> internalCursorReadOnly(V8Scope* scope, const v8::Arguments& args);
-
-// BinData object
-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);
-
-// NumberLong object
-long long numberLongVal(V8Scope* scope, const v8::Handle<v8::Object>& it);
-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);
-
-// NumberInt object
-int numberIntVal(V8Scope* scope, const v8::Handle<v8::Object>& it);
-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);
-
-// NumberDecimal object
-Decimal128 numberDecimalVal(V8Scope* scope, const v8::Handle<v8::Object>& it);
-v8::Handle<v8::Value> numberDecimalInit(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> numberDecimalToString(V8Scope* scope, const v8::Arguments& args);
-
-// DBQuery object
-v8::Handle<v8::Value> dbQueryInit(V8Scope* scope, const v8::Arguments& args);
-v8::Handle<v8::Value> dbQueryIndexAccess(::uint32_t index, const v8::AccessorInfo& info);
-
-// db constructor
-v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args);
-
-// collection constructor
-v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Arguments& args);
-
-// ObjectId constructor
-v8::Handle<v8::Value> objectIdInit(V8Scope* scope, const v8::Arguments& args);
-
-// DBRef constructor
-v8::Handle<v8::Value> dbRefInit(V8Scope* scope, const v8::Arguments& args);
-
-// DBPointer constructor
-v8::Handle<v8::Value> dbPointerInit(V8Scope* scope, const v8::Arguments& args);
-
-// Timestamp constructor
-v8::Handle<v8::Value> dbTimestampInit(V8Scope* scope, const v8::Arguments& args);
-
-// UUID constructor
-v8::Handle<v8::Value> uuidInit(V8Scope* scope, const v8::Arguments& args);
-
-// MD5 constructor
-v8::Handle<v8::Value> md5Init(V8Scope* scope, const v8::Arguments& args);
-
-// HexData constructor
-v8::Handle<v8::Value> hexDataInit(V8Scope* scope, const v8::Arguments& args);
-
-// Object.invalidForStorage()
-v8::Handle<v8::Value> v8ObjectInvalidForStorage(V8Scope* scope, const v8::Arguments& args);
-
-// Object.bsonsize()
-v8::Handle<v8::Value> bsonsize(V8Scope* scope, const v8::Arguments& args);
-
-// global method
-// Accepts 2 objects, converts them to BSONObj and calls woCompare on the first against the
-// second.
-v8::Handle<v8::Value> bsonWoCompare(V8Scope* scope, const v8::Arguments& args);
-
-// 'db.collection' property handlers
-v8::Handle<v8::Value> collectionGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info);
-v8::Handle<v8::Value> collectionSetter(v8::Local<v8::String> name,
- v8::Local<v8::Value> value,
- const v8::AccessorInfo& info);
-
-typedef stdx::function<void(V8Scope*, const v8::Handle<v8::FunctionTemplate>&)>
- V8FunctionPrototypeManipulatorFn;
-
-void v8RegisterMongoPrototypeManipulator(const V8FunctionPrototypeManipulatorFn& manipulator);
-}
diff --git a/src/mongo/scripting/v8_profiler.cpp b/src/mongo/scripting/v8_profiler.cpp
deleted file mode 100644
index 761cb2bf332..00000000000
--- a/src/mongo/scripting/v8_profiler.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright 2013 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/>.
- *
- * 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.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
-
-#include "mongo/scripting/v8_profiler.h"
-
-#include "mongo/util/log.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-void V8CpuProfiler::start(const StringData name) {
- v8::CpuProfiler::StartProfiling(v8::String::New(name.toString().c_str()));
-}
-
-void V8CpuProfiler::stop(const StringData name) {
- _cpuProfiles.insert(make_pair(
- name.toString(),
- v8::CpuProfiler::StopProfiling(v8::String::New(name.toString().c_str(), name.size()))));
-}
-
-void V8CpuProfiler::traverseDepthFirst(const v8::CpuProfileNode* cpuProfileNode,
- BSONArrayBuilder& arrayBuilder) {
- if (cpuProfileNode == NULL)
- return;
- BSONObjBuilder frameObjBuilder;
- frameObjBuilder.append("Function", *v8::String::Utf8Value(cpuProfileNode->GetFunctionName()));
- frameObjBuilder.append("Source",
- *v8::String::Utf8Value(cpuProfileNode->GetScriptResourceName()));
- frameObjBuilder.appendNumber("Line", cpuProfileNode->GetLineNumber());
- frameObjBuilder.appendNumber("SelfTime", cpuProfileNode->GetSelfTime());
- frameObjBuilder.appendNumber("TotalTime", cpuProfileNode->GetTotalTime());
- if (cpuProfileNode->GetChildrenCount()) {
- BSONArrayBuilder subArrayBuilder(frameObjBuilder.subarrayStart("Children"));
- for (int i = 0; i < cpuProfileNode->GetChildrenCount(); ++i) {
- traverseDepthFirst(cpuProfileNode->GetChild(i), subArrayBuilder);
- }
- subArrayBuilder.done();
- }
- arrayBuilder << frameObjBuilder.obj();
-}
-
-const BSONArray V8CpuProfiler::fetch(const StringData name) {
- BSONArrayBuilder arrayBuilder;
- CpuProfileMap::const_iterator iProf = _cpuProfiles.find(name.toString());
- if (iProf == _cpuProfiles.end())
- return arrayBuilder.arr();
- const v8::CpuProfile* cpuProfile = iProf->second;
- if (cpuProfile == NULL)
- return arrayBuilder.arr();
- traverseDepthFirst(cpuProfile->GetTopDownRoot(), arrayBuilder);
- return arrayBuilder.arr();
-}
-}
diff --git a/src/mongo/scripting/v8_profiler.h b/src/mongo/scripting/v8_profiler.h
deleted file mode 100644
index 41efce4f42a..00000000000
--- a/src/mongo/scripting/v8_profiler.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright 2013 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/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <v8.h>
-#include <v8-profiler.h>
-#include <map>
-
-#include "mongo/base/string_data.h"
-#include "mongo/db/jsobj.h"
-
-namespace mongo {
-
-/** Collect CPU Profiling data from v8. */
-class V8CpuProfiler {
-public:
- /** Start the CPU profiler */
- void start(const StringData name);
-
- /** Stop the CPU profiler */
- void stop(const StringData name);
-
- /** Get the current cpu profile */
- const BSONArray fetch(const StringData name);
-
-private:
- void traverseDepthFirst(const v8::CpuProfileNode* cpuProfileNode,
- BSONArrayBuilder& arrayBuilder);
-
- typedef std::map<std::string, const v8::CpuProfile*> CpuProfileMap;
- CpuProfileMap _cpuProfiles;
-};
-}
diff --git a/src/mongo/scripting/v8_utils.cpp b/src/mongo/scripting/v8_utils.cpp
deleted file mode 100644
index c7610fb0c20..00000000000
--- a/src/mongo/scripting/v8_utils.cpp
+++ /dev/null
@@ -1,406 +0,0 @@
-// v8_utils.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * 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.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
-
-#include "mongo/scripting/v8_utils.h"
-
-#include <cstdint>
-#include <iostream>
-#include <map>
-#include <sstream>
-#include <vector>
-
-#include "mongo/scripting/engine_v8.h"
-#include "mongo/scripting/v8_db.h"
-#include "mongo/stdx/condition_variable.h"
-#include "mongo/stdx/mutex.h"
-#include "mongo/stdx/thread.h"
-#include "mongo/util/log.h"
-#include "mongo/util/mongoutils/str.h"
-
-using namespace std;
-using std::unique_ptr;
-
-namespace mongo {
-
-std::string toSTLString(const v8::Handle<v8::Value>& o) {
- return StringData(V8String(o)).toString();
-}
-
-/** Get the properties of an object (and its prototype) as a comma-delimited string */
-std::string v8ObjectToString(const v8::Handle<v8::Object>& o) {
- v8::Local<v8::Array> properties = o->GetPropertyNames();
- v8::String::Utf8Value str(properties);
- massert(16696, "error converting js type to Utf8Value", *str);
- return std::string(*str, str.length());
-}
-
-std::ostream& operator<<(std::ostream& s, const v8::Handle<v8::Value>& o) {
- v8::String::Utf8Value str(o);
- s << *str;
- return s;
-}
-
-std::ostream& operator<<(std::ostream& s, const v8::TryCatch* try_catch) {
- v8::HandleScope handle_scope;
- v8::String::Utf8Value exceptionText(try_catch->Exception());
- v8::Handle<v8::Message> message = try_catch->Message();
-
- if (message.IsEmpty()) {
- s << *exceptionText << endl;
- } else {
- v8::String::Utf8Value filename(message->GetScriptResourceName());
- int linenum = message->GetLineNumber();
- cout << *filename << ":" << linenum << " " << *exceptionText << 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;
- }
- return s;
-}
-
-class JSThreadConfig {
-public:
- JSThreadConfig(V8Scope* scope, const v8::Arguments& args)
- : _started(), _done(), _sharedData(new SharedData()) {
- jsassert(args.Length() > 0, "need at least one argument");
- jsassert(args[0]->IsFunction(), "first argument must be a function");
-
- // arguments need to be copied into the isolate, go through bson
- BSONObjBuilder b;
- for (int i = 0; i < args.Length(); ++i) {
- scope->v8ToMongoElement(b, "arg" + BSONObjBuilder::numStr(i), args[i]);
- }
- _sharedData->_args = b.obj();
- }
-
- ~JSThreadConfig() {}
-
- void start() {
- jsassert(!_started, "Thread already started");
- JSThread jt(*this);
- _thread.reset(new stdx::thread(jt));
- _started = true;
- }
- void join() {
- jsassert(_started && !_done, "Thread not running");
- _thread->join();
- _done = true;
- }
-
- /**
- * Returns true if the JSThread terminated as a result of an error
- * during its execution, and false otherwise. This operation does
- * not block, nor does it require join() to have been called.
- */
- bool hasFailed() const {
- jsassert(_started, "Thread not started");
- return _sharedData->getErrored();
- }
-
- BSONObj returnData() {
- if (!_done)
- join();
- return _sharedData->_returnData;
- }
-
-private:
- /*
- * JSThreadConfig doesn't always outlive its JSThread (for example, if the parent thread
- * garbage collects the JSThreadConfig before the JSThread has finished running), so any
- * data shared between them has to go in a shared_ptr.
- */
- class SharedData {
- public:
- SharedData() : _errored(false) {}
- BSONObj _args;
- BSONObj _returnData;
- void setErrored(bool value) {
- stdx::lock_guard<stdx::mutex> lck(_erroredMutex);
- _errored = value;
- }
- bool getErrored() {
- stdx::lock_guard<stdx::mutex> lck(_erroredMutex);
- return _errored;
- }
-
- private:
- stdx::mutex _erroredMutex;
- bool _errored;
- };
-
- class JSThread {
- public:
- JSThread(JSThreadConfig& config) : _sharedData(config._sharedData) {}
-
- void operator()() {
- try {
- unique_ptr<V8Scope> scope(static_cast<V8Scope*>(globalScriptEngine->newScope()));
- v8::Locker v8lock(scope->getIsolate());
- v8::Isolate::Scope iscope(scope->getIsolate());
- v8::HandleScope handle_scope;
- v8::Context::Scope context_scope(scope->getContext());
-
- BSONObj args = _sharedData->_args;
- v8::Local<v8::Function> f =
- v8::Function::Cast(*(scope->mongoToV8Element(args.firstElement(), true)));
- int argc = args.nFields() - 1;
-
- // TODO SERVER-8016: properly allocate handles on the stack
- v8::Local<v8::Value> argv[24];
- BSONObjIterator it(args);
- it.next();
- for (int i = 0; i < argc && i < 24; ++i) {
- argv[i] = v8::Local<v8::Value>::New(scope->mongoToV8Element(*it, true));
- it.next();
- }
- v8::TryCatch try_catch;
- v8::Handle<v8::Value> ret = f->Call(scope->getContext()->Global(), argc, argv);
- if (ret.IsEmpty() || try_catch.HasCaught()) {
- string e = scope->v8ExceptionToSTLString(&try_catch);
- log() << "js thread raised js exception: " << e << endl;
- ret = v8::Undefined();
- _sharedData->setErrored(true);
- }
- // ret is translated to BSON to switch isolate
- BSONObjBuilder b;
- scope->v8ToMongoElement(b, "ret", ret);
- _sharedData->_returnData = b.obj();
- } catch (const DBException& e) {
- // Keeping behavior the same as for js exceptions.
- log() << "js thread threw c++ exception: " << e.toString();
- _sharedData->setErrored(true);
- _sharedData->_returnData = BSON("ret" << BSONUndefined);
- } catch (const std::exception& e) {
- log() << "js thread threw c++ exception: " << e.what();
- _sharedData->setErrored(true);
- _sharedData->_returnData = BSON("ret" << BSONUndefined);
- } catch (...) {
- log() << "js thread threw c++ non-exception";
- _sharedData->setErrored(true);
- _sharedData->_returnData = BSON("ret" << BSONUndefined);
- }
- }
-
- private:
- std::shared_ptr<SharedData> _sharedData;
- };
-
- bool _started;
- bool _done;
- unique_ptr<stdx::thread> _thread;
- std::shared_ptr<SharedData> _sharedData;
-};
-
-class CountDownLatchHolder {
-private:
- struct Latch {
- Latch(int32_t count) : count(count) {}
- stdx::condition_variable cv;
- stdx::mutex mutex;
- int32_t count;
- };
-
- std::shared_ptr<Latch> get(int32_t desc) {
- stdx::lock_guard<stdx::mutex> lock(_mutex);
- Map::iterator iter = _latches.find(desc);
- jsassert(iter != _latches.end(), "not a valid CountDownLatch descriptor");
- return iter->second;
- }
-
- typedef std::map<int32_t, std::shared_ptr<Latch>> Map;
- Map _latches;
- stdx::mutex _mutex;
- int32_t _counter;
-
-public:
- CountDownLatchHolder() : _counter(0) {}
- int32_t make(int32_t count) {
- jsassert(count >= 0, "argument must be >= 0");
- stdx::lock_guard<stdx::mutex> lock(_mutex);
- int32_t desc = ++_counter;
- _latches.insert(std::make_pair(desc, std::make_shared<Latch>(count)));
- return desc;
- }
- void await(int32_t desc) {
- std::shared_ptr<Latch> latch = get(desc);
- stdx::unique_lock<stdx::mutex> lock(latch->mutex);
- while (latch->count != 0) {
- latch->cv.wait(lock);
- }
- }
- void countDown(int32_t desc) {
- std::shared_ptr<Latch> latch = get(desc);
- stdx::unique_lock<stdx::mutex> lock(latch->mutex);
- if (latch->count > 0) {
- latch->count--;
- }
- if (latch->count == 0) {
- latch->cv.notify_all();
- }
- }
- int32_t getCount(int32_t desc) {
- std::shared_ptr<Latch> latch = get(desc);
- stdx::unique_lock<stdx::mutex> lock(latch->mutex);
- return latch->count;
- }
-};
-namespace {
-CountDownLatchHolder globalCountDownLatchHolder;
-}
-
-v8::Handle<v8::Value> CountDownLatchNew(V8Scope* scope, const v8::Arguments& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- int32_t count = v8::Local<v8::Integer>::Cast(args[0])->Value();
- return v8::Int32::New(globalCountDownLatchHolder.make(count));
-}
-
-v8::Handle<v8::Value> CountDownLatchAwait(V8Scope* scope, const v8::Arguments& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- globalCountDownLatchHolder.await(args[0]->ToInt32()->Value());
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> CountDownLatchCountDown(V8Scope* scope, const v8::Arguments& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- globalCountDownLatchHolder.countDown(args[0]->ToInt32()->Value());
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> CountDownLatchGetCount(V8Scope* scope, const v8::Arguments& args) {
- jsassert(args.Length() == 1, "need exactly one argument");
- jsassert(args[0]->IsInt32(), "argument must be an integer");
- return v8::Int32::New(globalCountDownLatchHolder.getCount(args[0]->ToInt32()->Value()));
-}
-
-JSThreadConfig* thisConfig(V8Scope* scope, const v8::Arguments& args) {
- v8::Local<v8::External> c =
- v8::External::Cast(*(args.This()->GetHiddenValue(v8::String::New("_JSThreadConfig"))));
- JSThreadConfig* config = static_cast<std::shared_ptr<JSThreadConfig>*>(c->Value())->get();
- return config;
-}
-
-v8::Handle<v8::Value> ThreadInit(V8Scope* scope, const v8::Arguments& args) {
- v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This());
-
- JSThreadConfig* config = new JSThreadConfig(scope, args);
- v8::Local<v8::External> handle = scope->jsThreadConfigTracker.track(self, config);
- args.This()->SetHiddenValue(v8::String::New("_JSThreadConfig"), handle);
-
- invariant(thisConfig(scope, args) == config);
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> ScopedThreadInit(V8Scope* scope, const v8::Arguments& args) {
- // NOTE: ScopedThread and Thread behave identically because a new V8 Isolate is always
- // created.
- return ThreadInit(scope, args);
-}
-
-v8::Handle<v8::Value> ThreadStart(V8Scope* scope, const v8::Arguments& args) {
- thisConfig(scope, args)->start();
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> ThreadJoin(V8Scope* scope, const v8::Arguments& args) {
- thisConfig(scope, args)->join();
- return v8::Undefined();
-}
-
-// Indicates to the caller that the thread terminated as a result of an error.
-v8::Handle<v8::Value> ThreadHasFailed(V8Scope* scope, const v8::Arguments& args) {
- bool hasFailed = thisConfig(scope, args)->hasFailed();
- return v8::Boolean::New(hasFailed);
-}
-
-v8::Handle<v8::Value> ThreadReturnData(V8Scope* scope, const v8::Arguments& args) {
- BSONObj data = thisConfig(scope, args)->returnData();
- return scope->mongoToV8Element(data.firstElement(), true);
-}
-
-v8::Handle<v8::Value> ThreadInject(V8Scope* scope, const v8::Arguments& args) {
- v8::HandleScope handle_scope;
- jsassert(args.Length() == 1, "threadInject takes exactly 1 argument");
- jsassert(args[0]->IsObject(), "threadInject needs to be passed a prototype");
- v8::Local<v8::Object> o = args[0]->ToObject();
-
- // install method on the Thread object
- scope->injectV8Function("init", ThreadInit, o);
- scope->injectV8Function("start", ThreadStart, o);
- scope->injectV8Function("join", ThreadJoin, o);
- scope->injectV8Function("hasFailed", ThreadHasFailed, o);
- scope->injectV8Function("returnData", ThreadReturnData, o);
- return handle_scope.Close(v8::Handle<v8::Value>());
-}
-
-v8::Handle<v8::Value> ScopedThreadInject(V8Scope* scope, const v8::Arguments& args) {
- v8::HandleScope handle_scope;
- jsassert(args.Length() == 1, "threadInject takes exactly 1 argument");
- jsassert(args[0]->IsObject(), "threadInject needs to be passed a prototype");
- v8::Local<v8::Object> o = args[0]->ToObject();
-
- scope->injectV8Function("init", ScopedThreadInit, o);
- // inheritance takes care of other member functions
-
- return handle_scope.Close(v8::Handle<v8::Value>());
-}
-
-void installFork(V8Scope* scope, v8::Handle<v8::Object>& global, v8::Handle<v8::Context>& context) {
- scope->injectV8Function("_threadInject", ThreadInject, global);
- scope->injectV8Function("_scopedThreadInject", ScopedThreadInject, global);
-
- scope->setObject("CountDownLatch", BSONObj(), false);
- v8::Handle<v8::Object> cdl = scope->get("CountDownLatch").As<v8::Object>();
- scope->injectV8Function("_new", CountDownLatchNew, cdl);
- scope->injectV8Function("_await", CountDownLatchAwait, cdl);
- scope->injectV8Function("_countDown", CountDownLatchCountDown, cdl);
- scope->injectV8Function("_getCount", CountDownLatchGetCount, cdl);
-}
-
-v8::Handle<v8::Value> v8AssertionException(const char* errorMessage) {
- return v8::ThrowException(v8::Exception::Error(v8::String::New(errorMessage)));
-}
-v8::Handle<v8::Value> v8AssertionException(const std::string& errorMessage) {
- return v8AssertionException(errorMessage.c_str());
-}
-}
diff --git a/src/mongo/scripting/v8_utils.h b/src/mongo/scripting/v8_utils.h
deleted file mode 100644
index 47c7a9dfccb..00000000000
--- a/src/mongo/scripting/v8_utils.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// v8_utils.h
-
-/* Copyright 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * 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.
- */
-
-#pragma once
-
-#include <cstdio>
-#include <cstdlib>
-#include <string>
-#include <v8.h>
-
-#include <mongo/base/string_data.h>
-#include <mongo/util/assert_util.h>
-
-namespace mongo {
-
-#define jsassert(x, msg) uassert(16664, (msg), (x))
-
-#define argumentCheck(mustBeTrue, errorMessage) \
- if (!(mustBeTrue)) { \
- return v8AssertionException((errorMessage)); \
- }
-
-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);
-
-/** Simple v8 object to std::string conversion helper */
-std::string toSTLString(const v8::Handle<v8::Value>& o);
-
-/** Like toSTLString but doesn't allocate a new std::string
- *
- * This owns the string's memory so you need to be careful not to let the
- * converted StringDatas outlive the V8Scope object. These rules are the
- * same as converting from a std::string into a StringData.
- *
- * Safe:
- * void someFunction(StringData argument);
- * v8::Handle<v8::String> aString;
- *
- * someFunction(V8String(aString)); // passing down stack as temporary
- *
- * V8String named (aString);
- * someFunction(named); // passing up stack as named value
- *
- * StringData sd = named; // scope of sd is less than named
- *
- * Unsafe:
- * StringData _member;
- *
- * StringData returningFunction() {
- * StringData sd = V8String(aString); // sd outlives the temporary
- *
- * V8String named(aString)
- * _member = named; // _member outlives named scope
- *
- * return V8String(aString); // passing up stack
- * }
- */
-class V8String {
-public:
- explicit V8String(const v8::Handle<v8::Value>& o) : _str(o) {
- massert(16686, "error converting js type to Utf8Value", *_str);
- }
- operator StringData() const {
- return StringData(*_str, _str.length());
- }
-
-private:
- v8::String::Utf8Value _str;
-};
-
-/** Get the properties of an object (and it's prototype) as a comma-delimited std::string */
-std::string v8ObjectToString(const v8::Handle<v8::Object>& o);
-
-class V8Scope;
-void installFork(V8Scope* scope, v8::Handle<v8::Object>& global, v8::Handle<v8::Context>& context);
-
-/** Throw a V8 exception from Mongo callback code; message text will be preceded by "Error: ".
- * Note: this function should be used for text that did not originate from the JavaScript
- * engine. Errors from the JavaScript engine will already have a prefix such as
- * ReferenceError, TypeError or SyntaxError.
- * Note: call only from a native function called from JavaScript (a callback).
- * The V8 ThrowException routine will note a JavaScript exception that will be
- * "thrown" in JavaScript when we return from the native function.
- * Note: it's required to return immediately to V8's execution control without calling any
- * V8 API functions. In this state, an empty handle may (will) be returned.
- * @param errorMessage Error message text.
- * @return Empty handle to be returned from callback function.
- */
-v8::Handle<v8::Value> v8AssertionException(const char* errorMessage);
-v8::Handle<v8::Value> v8AssertionException(const std::string& errorMessage);
-}
diff --git a/src/third_party/SConscript b/src/third_party/SConscript
index 5b533e84967..3c6a9b0d869 100644
--- a/src/third_party/SConscript
+++ b/src/third_party/SConscript
@@ -1,6 +1,6 @@
# -*- mode: python -*-
-Import("env use_system_version_of_library usemozjs usev8 v8suffix boostSuffix")
+Import("env use_system_version_of_library usemozjs boostSuffix")
Import("wiredtiger")
snappySuffix = '-1.1.2'
@@ -33,10 +33,6 @@ if not use_system_version_of_library('zlib'):
thirdPartyIncludePathList.append(
('zlib', '#/src/third_party/zlib' + zlibSuffix))
-if not use_system_version_of_library('v8'):
- thirdPartyIncludePathList.append(
- ('v8', '#/src/third_party/v8' + v8suffix + '/include'))
-
# TODO: figure out if we want to offer system versions of mozjs. Mozilla
# hasn't offered a source tarball since 24, but in theory they could.
#
@@ -194,27 +190,6 @@ zlibEnv.Library(
'shim_zlib.cpp',
])
-if usev8:
- if use_system_version_of_library("v8"):
- v8Env = env.Clone(
- SYSLIBDEPS=[
- env['LIBDEPS_V8_SYSLIBDEP'],
- ])
- else:
- v8Env = env.Clone()
- v8Env.InjectThirdPartyIncludePaths(libraries=['v8'])
- v8Env.SConscript('v8' + v8suffix + '/SConscript', exports={'env' : v8Env })
- v8Env = v8Env.Clone(
- LIBDEPS=[
- 'v8' + v8suffix + '/v8'
- ])
-
- v8Env.Library(
- target="shim_v8",
- source=[
- 'shim_v8.cpp',
- ])
-
if usemozjs:
mozjsEnv = env.Clone()
mozjsEnv.SConscript('mozjs' + mozjsSuffix + '/SConscript', exports={'env' : mozjsEnv })