summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/engine_v8.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting/engine_v8.cpp')
-rw-r--r--src/mongo/scripting/engine_v8.cpp829
1 files changed, 399 insertions, 430 deletions
diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp
index 487bec5996d..7ffebec383e 100644
--- a/src/mongo/scripting/engine_v8.cpp
+++ b/src/mongo/scripting/engine_v8.cpp
@@ -33,38 +33,47 @@ namespace mongo {
extern const JSFile assert;
}
- /**
- * Unwraps a BSONObj from the JS wrapper
- */
- static BSONObj unwrapBSONObj(const v8::Handle<v8::Object>& obj) {
+ // 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 BSONObj();
- }
+ if (field.IsEmpty() || !field->IsExternal())
+ return 0;
void* ptr = field->Value();
- return ((BSONHolder*)ptr)->_obj;
+ return (BSONHolder*)ptr;
}
- static BSONHolder* unwrapHolder(const v8::Handle<v8::Object>& obj) {
- 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(const v8::Handle<v8::Object>& obj) {
- return obj->GetInternalField(1).As<v8::Object>();
+ 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>();
}
- v8::Persistent<v8::Object> V8Scope::wrapBSONObject(v8::Local<v8::Object> obj,
- BSONHolder* data) {
- data->_scope = this;
- obj->SetInternalField(0, v8::External::New(data)); // Holder
+ 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, data);
- return p;
+ bsonHolderTracker.track(p, holder);
}
static v8::Handle<v8::Value> namedGet(v8::Local<v8::String> name,
@@ -72,14 +81,16 @@ namespace mongo {
v8::HandleScope handle_scope;
v8::Handle<v8::Value> val;
try {
- v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
+ 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(info.Holder());
+ BSONHolder* holder = unwrapHolder(scope, info.Holder());
if (!holder || holder->_removed.count(key))
return handle_scope.Close(v8::Handle<v8::Value>());
@@ -88,8 +99,6 @@ namespace mongo {
if (elmt.eoo())
return handle_scope.Close(v8::Handle<v8::Value>());
- v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
- V8Scope* scope = (V8Scope*)(scp->Value());
val = scope->mongoToV8Element(elmt, holder->_readOnly);
if (obj.objsize() > 128 || val->IsObject()) {
@@ -115,59 +124,39 @@ namespace mongo {
static v8::Handle<v8::Value> namedGetRO(v8::Local<v8::String> name,
const v8::AccessorInfo &info) {
return namedGet(name, info);
- // Rest of function is unused but left in to ease backporting of SERVER-9267
-
- v8::HandleScope handle_scope;
- v8::Handle<v8::Value> val;
- string key = toSTLString(name);
- try {
- BSONObj obj = unwrapBSONObj(info.Holder());
- BSONElement elmt = obj.getField(key.c_str());
- if (elmt.eoo())
- return handle_scope.Close(v8::Handle<v8::Value>());
- v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
- V8Scope* scope = (V8Scope*)(scp->Value());
- val = scope->mongoToV8Element(elmt, true);
- }
- catch (const DBException &dbEx) {
- return v8AssertionException(dbEx.toString());
- }
- catch (...) {
- return v8AssertionException(string("error getting read-only property ") + key);
- }
- return handle_scope.Close(val);
}
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);
- BSONHolder* holder = unwrapHolder(info.Holder());
+ 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(info.Holder());
+ 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;
- BSONHolder* holder = unwrapHolder(info.Holder());
+ 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;
- v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
- V8Scope* scope = (V8Scope*)(scp->Value());
- unordered_set<string> added;
+ 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();
- string sname = f.fieldName();
- if (holder->_removed.count(sname))
+ StringData sname (f.fieldName(), f.fieldNameSize()-1);
+ if (holder->_removed.count(sname.toString()))
continue;
v8::Handle<v8::String> name = scope->v8StringData(sname);
@@ -176,12 +165,13 @@ namespace mongo {
}
- v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
+ 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>();
- string sname = toSTLString(name);
+ V8String sname (name);
if (added.count(sname))
continue;
out->Set(outIndex++, name);
@@ -192,12 +182,14 @@ namespace mongo {
v8::Handle<v8::Boolean> namedDelete(v8::Local<v8::String> name, const v8::AccessorInfo& info) {
v8::HandleScope handle_scope;
string key = toSTLString(name);
- BSONHolder* holder = unwrapHolder(info.Holder());
+ 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(info.Holder());
+ v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder());
+ if (realObject.IsEmpty()) return v8::Handle<v8::Boolean>();
realObject->Delete(name);
return v8::True();
}
@@ -206,16 +198,16 @@ namespace mongo {
v8::HandleScope handle_scope;
v8::Handle<v8::Value> val;
try {
- v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
+ 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;
- v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
- V8Scope* scope = (V8Scope*)(scp->Value());
- BSONHolder* holder = unwrapHolder(info.Holder());
+ 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>());
@@ -245,56 +237,34 @@ namespace mongo {
v8::Handle<v8::Boolean> indexedDelete(uint32_t index, const v8::AccessorInfo& info) {
string key = str::stream() << index;
- BSONHolder* holder = unwrapHolder(info.Holder());
+ 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(info.Holder());
+ 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);
- // Rest of function is unused but left in-place to ease backporting of SERVER-9267
-
- v8::HandleScope handle_scope;
- v8::Handle<v8::Value> val;
- try {
- string key = str::stream() << index;
- v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
- V8Scope* scope = (V8Scope*)(scp->Value());
-
- BSONObj obj = unwrapBSONObj(info.Holder());
- BSONElement elmt = obj.getField(key);
-
- if (elmt.eoo())
- return handle_scope.Close(v8::Handle<v8::Value>());
-
- val = scope->mongoToV8Element(elmt, true);
- }
- catch (const DBException &dbEx) {
- return v8AssertionException(dbEx.toString());
- }
- catch (...) {
- return v8AssertionException(str::stream()
- << "error getting read-only indexed property "
- << index);
- }
- return handle_scope.Close(val);
}
static v8::Handle<v8::Value> indexedSet(uint32_t index, v8::Local<v8::Value> value_obj,
const v8::AccessorInfo& info) {
string key = str::stream() << index;
- BSONHolder* holder = unwrapHolder(info.Holder());
+ 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(info.Holder());
+ 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;
}
@@ -302,15 +272,13 @@ namespace mongo {
v8::Handle<v8::Value> NamedReadOnlySet(v8::Local<v8::String> property,
v8::Local<v8::Value> value,
const v8::AccessorInfo& info) {
- string key = toSTLString(property);
- cout << "cannot write property " << key << " to read-only object" << endl;
+ 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) {
- string key = toSTLString(property);
- cout << "cannot delete property " << key << " from read-only object" << endl;
+ cout << "cannot delete property " << V8String(property) << " from read-only object" << endl;
return v8::Boolean::New(false);
}
@@ -451,7 +419,8 @@ namespace mongo {
void V8Scope::kill() {
mongo::mutex::scoped_lock interruptLock(_interruptLock);
if (!_inNativeExecution) {
- // set the TERMINATE flag on the stack guard for this isolate
+ // Set the TERMINATE flag on the stack guard for this isolate.
+ // This won't happen between calls to nativePrologue and nativeEpilogue().
v8::V8::TerminateExecution(_isolate);
LOG(1) << "killing v8 scope. isolate: " << _isolate << endl;
}
@@ -498,6 +467,8 @@ namespace mongo {
_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);
@@ -509,46 +480,33 @@ namespace mongo {
// 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
- lzObjectTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
- lzObjectTemplate->SetInternalFieldCount(2);
- lzObjectTemplate->SetNamedPropertyHandler(namedGet, namedSet, 0, namedDelete,
- namedEnumerator, v8::External::New(this));
- lzObjectTemplate->SetIndexedPropertyHandler(indexedGet, indexedSet, 0, indexedDelete,
- namedEnumerator, v8::External::New(this));
- lzObjectTemplate->NewInstance()->GetPrototype()->ToObject()->ForceSet(
- v8::String::New("_bson"),
- v8::Boolean::New(true),
- v8::DontEnum);
-
- roObjectTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
- roObjectTemplate->SetInternalFieldCount(2);
- roObjectTemplate->SetNamedPropertyHandler(namedGetRO, NamedReadOnlySet, 0,
- NamedReadOnlyDelete, namedEnumerator,
- v8::External::New(this));
- roObjectTemplate->SetIndexedPropertyHandler(indexedGetRO, IndexedReadOnlySet, 0,
- IndexedReadOnlyDelete, 0,
- v8::External::New(this));
- roObjectTemplate->NewInstance()->GetPrototype()->ToObject()->ForceSet(
- v8::String::New("_bson"),
- v8::Boolean::New(true),
- v8::DontEnum);
-
- // initialize lazy array template
- // unfortunately it is not possible to create true v8 array from a template
- // this means we use an object template and copy methods over
- // this it creates issues when calling certain methods that check array type
- lzArrayTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
- lzArrayTemplate->SetInternalFieldCount(1);
- lzArrayTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0,
- v8::External::New(this));
- lzArrayTemplate->NewInstance()->GetPrototype()->ToObject()->ForceSet(
- v8::String::New("_bson"),
- v8::Boolean::New(true),
- v8::DontEnum);
-
- internalFieldObjects = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
- internalFieldObjects->SetInternalFieldCount(1);
+ _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
@@ -582,11 +540,9 @@ namespace mongo {
_funcs[ i ].Dispose();
_funcs.clear();
_global.Dispose();
- lzObjectTemplate.Dispose();
- lzArrayTemplate.Dispose();
- roObjectTemplate.Dispose();
- internalFieldObjects.Dispose();
_context.Dispose();
+ // Note: This block is unnecessary since we destroy the v8 Heap (Isolate) immediately
+ // after. Leaving in for now, but nothing new should be added.
}
_isolate->Dispose();
// set the isolate to NULL so ObjTracker destructors know that v8 is no longer reachable
@@ -617,13 +573,13 @@ namespace mongo {
v8::HandleScope handle_scope;
try {
v8::Local<v8::External> f =
- v8::External::Cast(*args.Callee()->Get(v8::String::New("_native_function")));
+ v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_native_function")));
NativeFunction function = (NativeFunction)(f->Value());
v8::Local<v8::External> data =
- v8::External::Cast(*args.Callee()->Get(v8::String::New("_native_data")));
+ v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_native_data")));
BSONObjBuilder b;
for (int i = 0; i < args.Length(); ++i)
- scope->v8ToMongoElement(b, str::stream() << i, args[i]);
+ scope->v8ToMongoElement(b, BSONObjBuilder::numStr(i), args[i]);
BSONObj nativeArgs = b.obj();
ret = function(nativeArgs, data->Value());
}
@@ -641,15 +597,14 @@ namespace mongo {
v8::Handle<v8::Value> V8Scope::v8Callback(const v8::Arguments &args) {
v8::HandleScope handle_scope;
- v8::Local<v8::External> scp = v8::External::Cast(*args.Data());
- V8Scope* scope = (V8Scope*)(scp->Value());
+ V8Scope* scope = getScope(args.GetIsolate());
if (!scope->nativePrologue())
// execution terminated
return v8::Undefined();
v8::Local<v8::External> f =
- v8::External::Cast(*args.Callee()->Get(v8::String::New("_v8_function")));
+ v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_v8_function")));
v8Function function = (v8Function)(f->Value());
v8::Handle<v8::Value> ret;
string exceptionText;
@@ -784,38 +739,105 @@ namespace mongo {
v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) {
v8::Handle<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit);
- v8::Local<v8::Template> proto = numberLong->PrototypeTemplate();
- scope->injectV8Function("valueOf", numberLongValueOf, proto);
- scope->injectV8Function("toNumber", numberLongToNumber, proto);
- scope->injectV8Function("toString", numberLongToString, proto);
+ 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::Local<v8::Template> proto = numberInt->PrototypeTemplate();
- scope->injectV8Function("valueOf", numberIntValueOf, proto);
- scope->injectV8Function("toNumber", numberIntToNumber, proto);
- scope->injectV8Function("toString", numberIntToString, proto);
+ 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> getBinDataFunctionTemplate(V8Scope* scope) {
v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit);
binData->InstanceTemplate()->SetInternalFieldCount(1);
- v8::Local<v8::Template> proto = binData->PrototypeTemplate();
- scope->injectV8Function("toString", binDataToString, proto);
- scope->injectV8Function("base64", binDataToBase64, proto);
- scope->injectV8Function("hex", binDataToHex, proto);
+ 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);
- ts->InstanceTemplate()->SetInternalFieldCount(1);
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::String::Utf8Value exceptionText(try_catch->Exception());
@@ -904,7 +926,7 @@ namespace mongo {
// --- functions -----
- bool hasFunctionIdentifier(const string& code) {
+ bool hasFunctionIdentifier(const StringData& code) {
if (code.size() < 9 || code.find("function") != 0 )
return false;
@@ -973,20 +995,20 @@ namespace mongo {
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
- v8::Handle<v8::Value> args[24];
+ // 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<24; i++) {
+ for (int i=0; i<nargs; i++) {
BSONElement next = it.next();
args[i] = mongoToV8Element(next, readOnlyArgs);
}
- setObject("args", *argsObject, readOnlyArgs); // for backwards compatibility
- }
- else {
- _global->ForceSet(v8::String::New("args"), v8::Undefined());
}
v8::Handle<v8::Object> v8recv;
@@ -1024,14 +1046,14 @@ namespace mongo {
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(v8StringData("_v8_function"))) {
+ if (!resultObject.IsEmpty() && resultObject->Has(strLitToV8("_v8_function"))) {
log() << "storing native function as return value" << endl;
_lastRetIsNativeCode = true;
}
else {
_lastRetIsNativeCode = false;
}
- _global->ForceSet(v8::String::New("__returnValue"), result);
+ _global->ForceSet(strLitToV8("__returnValue"), result);
}
return 0;
@@ -1080,11 +1102,11 @@ namespace mongo {
if (checkV8ErrorState(result, try_catch, reportError, assertOnError))
return false;
- _global->ForceSet(v8StringData("__lastres__"), result);
+ _global->ForceSet(strLitToV8("__lastres__"), result);
if (printResult && !result->IsUndefined()) {
// appears to only be used by shell
- cout << toSTLString(result) << endl;
+ cout << V8String(result) << endl;
}
return true;
@@ -1098,36 +1120,51 @@ namespace mongo {
void V8Scope::injectNative(const char *field, NativeFunction func, v8::Handle<v8::Object>& obj,
void* data) {
v8::Handle<v8::FunctionTemplate> ft = createV8Function(nativeCallback);
- ft->Set(v8::String::New("_native_function"), v8::External::New((void*)func));
- ft->Set(v8::String::New("_native_data"), v8::External::New(data));
- ft->SetClassName(v8StringData(field));
- obj->ForceSet(v8StringData(field), ft->GetFunction());
+ ft->Set(strLitToV8("_native_function"),
+ v8::External::New((void*)func),
+ v8::PropertyAttribute(v8::DontEnum | v8::ReadOnly));
+ ft->Set(strLitToV8("_native_data"),
+ v8::External::New(data),
+ v8::PropertyAttribute(v8::DontEnum | v8::ReadOnly));
+ injectV8Function(field, ft, obj);
}
- void V8Scope::injectV8Function(const char *field, v8Function func) {
- injectV8Function(field, func, _global);
+ v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Function(const char *field, v8Function func) {
+ return injectV8Function(field, func, _global);
}
- void V8Scope::injectV8Function(const char *field, v8Function func,
- v8::Handle<v8::Object>& obj) {
- v8::Handle<v8::FunctionTemplate> ft = createV8Function(func);
- ft->SetClassName(v8StringData(field));
- v8::Handle<v8::Function> f = ft->GetFunction();
- obj->ForceSet(v8StringData(field), f);
+ 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;
}
- void V8Scope::injectV8Function(const char *field, v8Function func,
- v8::Handle<v8::Template>& t) {
+ 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);
- ft->SetClassName(v8StringData(field));
v8::Handle<v8::Function> f = ft->GetFunction();
- t->Set(v8StringData(field), f);
+ f->SetName(field);
+ proto->Set(field, f);
+ return ft;
}
v8::Handle<v8::FunctionTemplate> V8Scope::createV8Function(v8Function func) {
- v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(v8Callback,
- v8::External::New(this));
- ft->Set(v8::String::New("_v8_function"), v8::External::New(reinterpret_cast<void*>(func)),
+ v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(v8Callback);
+ ft->Set(strLitToV8("_v8_function"), v8::External::New(reinterpret_cast<void*>(func)),
static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::ReadOnly));
return ft;
}
@@ -1140,6 +1177,7 @@ namespace mongo {
}
void V8Scope::localConnect(const char * dbName) {
+ typedef v8::Persistent<v8::FunctionTemplate> FTPtr;
{
V8_SIMPLE_HEADER
if (_connectState == EXTERNAL)
@@ -1162,8 +1200,8 @@ namespace mongo {
injectV8Function("load", load);
// install the Mongo function object and instantiate the 'db' global
- _global->ForceSet(v8StringData("Mongo"),
- getMongoFunctionTemplate(this, true)->GetFunction());
+ _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",
@@ -1175,6 +1213,7 @@ namespace mongo {
}
void V8Scope::externalSetup() {
+ typedef v8::Persistent<v8::FunctionTemplate> FTPtr;
V8_SIMPLE_HEADER
if (_connectState == EXTERNAL)
return;
@@ -1191,56 +1230,61 @@ namespace mongo {
injectV8Function("load", load);
// install the Mongo function object
- _global->ForceSet(v8StringData("Mongo"),
- getMongoFunctionTemplate(this, false)->GetFunction());
+ _MongoFT = FTPtr::New(getMongoFunctionTemplate(this, false));
+ injectV8Function("Mongo", MongoFT(), _global);
execCoreFiles();
_connectState = EXTERNAL;
}
void V8Scope::installDBAccess() {
- v8::Handle<v8::FunctionTemplate> db = createV8Function(dbInit);
- db->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, collectionSetter);
- _global->ForceSet(v8StringData("DB"), db->GetFunction());
+ 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);
- v8::Handle<v8::FunctionTemplate> dbCollection = createV8Function(collectionInit);
- dbCollection->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter,
- collectionSetter);
- _global->ForceSet(v8StringData("DBCollection"), dbCollection->GetFunction());
+ injectV8Function("DB", DBFT(), _global);
+ injectV8Function("DBQuery", DBQueryFT(), _global);
+ injectV8Function("DBCollection", DBCollectionFT(), _global);
- v8::Handle<v8::FunctionTemplate> dbQuery = createV8Function(dbQueryInit);
- dbQuery->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess);
- _global->ForceSet(v8StringData("DBQuery"), dbQuery->GetFunction());
+ // The internal cursor type isn't exposed to the users at all
+ _InternalCursorFT = FTPtr::New(getInternalCursorFunctionTemplate(this));
}
void V8Scope::installBSONTypes() {
- injectV8Function("ObjectId", objectIdInit, _global);
- injectV8Function("DBRef", dbRefInit, _global);
- injectV8Function("DBPointer", dbPointerInit, _global);
-
- _global->ForceSet(v8StringData("BinData"),
- getBinDataFunctionTemplate(this)->GetFunction());
- _global->ForceSet(v8StringData("UUID"),
- createV8Function(uuidInit)->GetFunction());
- _global->ForceSet(v8StringData("MD5"),
- createV8Function(md5Init)->GetFunction());
- _global->ForceSet(v8StringData("HexData"),
- createV8Function(hexDataInit)->GetFunction());
- _global->ForceSet(v8StringData("NumberLong"),
- getNumberLongFunctionTemplate(this)->GetFunction());
- _global->ForceSet(v8StringData("NumberInt"),
- getNumberIntFunctionTemplate(this)->GetFunction());
- _global->ForceSet(v8StringData("Timestamp"),
- getTimestampFunctionTemplate(this)->GetFunction());
-
- BSONObjBuilder b;
- b.appendMaxKey("");
- b.appendMinKey("");
- BSONObj o = b.obj();
- BSONObjIterator i(o);
- _global->ForceSet(v8StringData("MaxKey"), mongoToV8Element(i.next()), v8::ReadOnly);
- _global->ForceSet(v8StringData("MinKey"), mongoToV8Element(i.next()), v8::ReadOnly);
- _global->Get(v8StringData("Object"))->ToObject()->ForceSet(
- v8StringData("bsonsize"),
+ 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));
+ _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);
+ 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);
+
+ _global->Get(strLitToV8("Object"))->ToObject()->ForceSet(
+ strLitToV8("bsonsize"),
createV8Function(bsonsize)->GetFunction());
}
@@ -1276,7 +1320,7 @@ namespace mongo {
v8::Local<v8::Value> V8Scope::newId(const OID &id) {
v8::HandleScope handle_scope;
- v8::Function* idCons = this->getObjectIdCons();
+ v8::Handle<v8::Function> idCons = ObjectIdFT()->GetFunction();
v8::Handle<v8::Value> argv[1];
argv[0] = v8::String::New(id.str().c_str());
return handle_scope.Close(idCons->NewInstance(1, argv));
@@ -1285,83 +1329,35 @@ namespace mongo {
/**
* converts a BSONObj to a Lazy V8 object
*/
- v8::Persistent<v8::Object> V8Scope::mongoToLZV8(const BSONObj& m, bool readOnly) {
- v8::Local<v8::Object> o;
- BSONHolder* own = new BSONHolder(m);
- own->_readOnly = readOnly;
-
- if (readOnly) {
- o = roObjectTemplate->NewInstance();
- massert(16497, str::stream() << "V8: NULL RO Object template instantiated. "
- << (v8::V8::IsExecutionTerminating() ?
- "v8 execution is terminating." :
- "v8 still executing."),
- *o != NULL);
- } else {
- o = lzObjectTemplate->NewInstance();
- massert(16496, str::stream() << "V8: NULL Object template instantiated. "
- << (v8::V8::IsExecutionTerminating() ?
- "v8 execution is terminating." :
- "v8 still executing."),
- *o != NULL);
- static string ref = "$ref";
- if (ref == m.firstElement().fieldName()) {
- const BSONElement& id = m["$id"];
- if (!id.eoo()) {
- v8::Function* dbRef = getNamedCons("DBRef");
- o->SetPrototype(dbRef->NewInstance()->GetPrototype());
+ 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;
}
}
- return wrapBSONObject(o, own);
-
- }
+ 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);
- v8::Handle<v8::Value> minKeyToJson(const v8::Arguments& args) {
- return v8::String::New("{ \"$minKey\" : 1 }");
- }
-
- v8::Handle<v8::Value> minKeyToString(const v8::Arguments& args) {
- return v8::String::New("[object MinKey]");
- }
-
- v8::Local<v8::Object> V8Scope::newMinKeyInstance() {
- v8::Local<v8::ObjectTemplate> myTemplate = v8::Local<v8::ObjectTemplate>::New(
- v8::ObjectTemplate::New());
- myTemplate->SetInternalFieldCount(1);
- myTemplate->SetCallAsFunctionHandler(minKeyToJson);
-
- v8::Local<v8::Object> instance = myTemplate->NewInstance();
- instance->ForceSet(v8::String::New("tojson"),
- v8::FunctionTemplate::New(minKeyToJson)->GetFunction(), v8::ReadOnly);
- instance->ForceSet(v8::String::New("toString"),
- v8::FunctionTemplate::New(minKeyToJson)->GetFunction(), v8::ReadOnly);
- instance->SetInternalField(0, v8::Uint32::New( mongo::MinKey ));
- return instance;
- }
-
- v8::Handle<v8::Value> maxKeyToJson(const v8::Arguments& args) {
- return v8::String::New("{ \"$maxKey\" : 1 }");
- }
-
- v8::Handle<v8::Value> maxKeyToString(const v8::Arguments& args) {
- return v8::String::New("[object MaxKey]");
- }
-
- v8::Local<v8::Object> V8Scope::newMaxKeyInstance() {
- v8::Local<v8::ObjectTemplate> myTemplate = v8::Local<v8::ObjectTemplate>::New(
- v8::ObjectTemplate::New());
- myTemplate->SetInternalFieldCount(1);
- myTemplate->SetCallAsFunctionHandler(maxKeyToJson);
-
- v8::Local<v8::Object> instance = myTemplate->NewInstance();
- instance->ForceSet(v8::String::New("tojson"),
- v8::FunctionTemplate::New(maxKeyToJson)->GetFunction(), v8::ReadOnly);
- instance->ForceSet(v8::String::New("toString"),
- v8::FunctionTemplate::New(maxKeyToJson)->GetFunction(), v8::ReadOnly);
- instance->SetInternalField(0, v8::Uint32::New( mongo::MaxKey ));
- return instance;
+ wrapBSONObject(o, m, readOnly);
+ return o;
}
v8::Handle<v8::Value> V8Scope::mongoToV8Element(const BSONElement &elem, bool readOnly) {
@@ -1410,10 +1406,24 @@ namespace mongo {
case mongo::jstNULL:
case mongo::Undefined: // duplicate sm behavior
return v8::Null();
- case mongo::RegEx:
- argv[0] = v8::String::New(elem.regex());
- argv[1] = v8::String::New(elem.regexFlags());
- return getNamedCons("RegExp")->NewInstance(2, argv);
+ 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);
@@ -1421,14 +1431,12 @@ namespace mongo {
base64::encode(ss, data, len);
argv[0] = v8::Number::New(elem.binDataType());
argv[1] = v8::String::New(ss.str().c_str());
- return getNamedCons("BinData")->NewInstance(2, argv);
+ return BinDataFT()->GetFunction()->NewInstance(2, argv);
}
case mongo::Timestamp:
- instance = internalFieldObjects->NewInstance();
- instance->ForceSet(v8::String::New("t"), v8::Number::New(elem.timestampTime() / 1000 ));
- instance->ForceSet(v8::String::New("i"), v8::Number::New(elem.timestampInc()));
- instance->SetInternalField(0, v8::Uint32::New(elem.type()));
- return instance;
+ argv[0] = v8::Number::New(elem.timestampTime() / 1000);
+ argv[1] = v8::Number::New(elem.timestampInc());
+ return TimestampFT()->GetFunction()->NewInstance(2,argv);
case mongo::NumberLong:
nativeUnsignedLong = elem.numberLong();
// values above 2^53 are not accurately represented in JS
@@ -1436,23 +1444,23 @@ namespace mongo {
(long long)(double)(long long)(nativeUnsignedLong) &&
nativeUnsignedLong < 9007199254740992ULL) {
argv[0] = v8::Number::New((double)(long long)(nativeUnsignedLong));
- return getNamedCons("NumberLong")->NewInstance(1, argv);
+ 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 getNamedCons("NumberLong")->NewInstance(3, argv);
+ return NumberLongFT()->GetFunction()->NewInstance(3, argv);
}
case mongo::MinKey:
- return newMinKeyInstance();
+ return MinKeyFT()->GetFunction()->NewInstance();
case mongo::MaxKey:
- return newMaxKeyInstance();
+ return MaxKeyFT()->GetFunction()->NewInstance();
case mongo::DBRef:
argv[0] = v8StringData(elem.dbrefNS());
argv[1] = newId(elem.dbrefOID());
- return getNamedCons("DBPointer")->NewInstance(2, argv);
+ return DBPointerFT()->GetFunction()->NewInstance(2, argv);
default:
massert(16661, str::stream() << "can't handle type: " << elem.type()
<< " " << elem.toString(), false);
@@ -1462,13 +1470,14 @@ namespace mongo {
}
void V8Scope::v8ToMongoNumber(BSONObjBuilder& b,
- const string& elementName,
- v8::Handle<v8::Value> value,
+ const StringData& elementName,
+ v8::Handle<v8::Number> value,
BSONObj* originalParent) {
- double val = value->ToNumber()->Value();
+ 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);
@@ -1478,136 +1487,103 @@ namespace mongo {
b.append(elementName, val);
}
- void V8Scope::v8ToMongoNumberLong(BSONObjBuilder& b,
- const string& elementName,
- v8::Handle<v8::Object> obj) {
- // TODO might be nice to potentially speed this up with an indexed internal
- // field, but I don't yet know how to use an ObjectTemplate with a
- // constructor.
- long long val;
- if (!obj->Has(v8StringData("top"))) {
- val = static_cast<int64_t>(obj->Get(v8StringData("floatApprox"))->NumberValue());
- }
- else {
- val = static_cast<int64_t>((
- static_cast<uint64_t>(obj->Get(v8StringData("top"))->ToInt32()->Value()) << 32) +
- static_cast<uint32_t>(obj->Get(v8StringData("bottom"))->ToInt32()->Value()));
- }
- b.append(elementName, val);
- }
-
- void V8Scope::v8ToMongoInternal(BSONObjBuilder& b,
- const string& elementName,
- v8::Handle<v8::Object> obj) {
- uint32_t bsonType = obj->GetInternalField(0)->ToUint32()->Value();
- switch(bsonType) {
- case Timestamp:
- b.appendTimestamp(elementName,
- Date_t(static_cast<uint64_t>(
- obj->Get(v8::String::New("t"))->ToNumber()->Value() * 1000 )),
- obj->Get(v8::String::New("i"))->ToInt32()->Value());
- return;
- case MinKey:
- b.appendMinKey(elementName);
- return;
- case MaxKey:
- b.appendMaxKey(elementName);
- return;
- default:
- massert(16665, "invalid internal field", false);
- }
- }
-
void V8Scope::v8ToMongoRegex(BSONObjBuilder& b,
- const string& elementName,
- v8::Handle<v8::Object> v8Regex) {
- string regex = toSTLString(v8Regex);
+ const StringData& elementName,
+ v8::Handle<v8::RegExp> v8Regex) {
+ V8String v8RegexString (v8Regex);
+ StringData regex = v8RegexString;
regex = regex.substr(1);
- string r = regex.substr(0 ,regex.rfind("/"));
- string o = regex.substr(regex.rfind("/") + 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,
- const string& elementName,
+ const StringData& elementName,
v8::Handle<v8::Object> obj) {
- OID oid;
- v8::Local<v8::Value> theid = obj->Get(v8StringData("id"));
- oid.init(toSTLString(theid->ToObject()->Get(v8StringData("str"))));
- string ns = toSTLString(obj->Get(v8StringData("ns")));
+ 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,
- const string& elementName,
+ const StringData& elementName,
v8::Handle<v8::Object> obj) {
- int len = obj->Get(v8StringData("len"))->ToInt32()->Value();
+
+ verify(BinDataFT()->HasInstance(obj));
+ verify(obj->InternalFieldCount() == 1);
+ int len = obj->Get(strLitToV8("len"))->ToInt32()->Value();
b.appendBinData(elementName,
len,
- mongo::BinDataType(obj->Get(v8StringData("type"))->ToInt32()->Value()),
+ mongo::BinDataType(obj->Get(strLitToV8("type"))->ToInt32()->Value()),
base64::decode(toSTLString(obj->GetInternalField(0))).c_str());
}
- void V8Scope::v8ToMongoObjectID(BSONObjBuilder& b,
- const string& elementName,
- v8::Handle<v8::Object> obj) {
- OID oid;
- oid.init(toSTLString(obj->Get(v8StringData("str"))));
- b.appendOID(elementName, &oid);
+ 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,
- const string& elementName,
+ const StringData& elementName,
v8::Handle<v8::Value> value,
int depth,
BSONObj* originalParent) {
- // The user could potentially modify the fields of these special objects,
- // wreaking havoc when we attempt to reinterpret them. Not doing any validation
- // for now...
- v8::Local<v8::Object> obj = value->ToObject();
- v8::Local<v8::Value> proto = obj->GetPrototype();
-
- if (obj->InternalFieldCount() && obj->GetInternalField(0)->IsNumber()) {
- v8ToMongoInternal(b, elementName, obj);
- return;
- }
-
- if (proto->IsRegExp())
- v8ToMongoRegex(b, elementName, obj);
- else if (proto->IsObject() &&
- proto->ToObject()->HasRealNamedProperty(v8::String::New("isObjectId")))
- v8ToMongoObjectID(b, elementName, obj);
- else if (!obj->GetHiddenValue(v8::String::New("__NumberLong")).IsEmpty())
- v8ToMongoNumberLong(b, elementName, obj);
- else if (!obj->GetHiddenValue(v8::String::New("__NumberInt")).IsEmpty())
- b.append(elementName,
- obj->GetHiddenValue(v8::String::New("__NumberInt"))->Int32Value());
- else if (!value->ToObject()->GetHiddenValue(v8::String::New("__DBPointer")).IsEmpty())
+ 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 (DBPointerFT()->HasInstance(value)) {
v8ToMongoDBRef(b, elementName, obj);
- else if (!value->ToObject()->GetHiddenValue(v8::String::New("__BinData")).IsEmpty())
+ } else if (BinDataFT()->HasInstance(value)) {
v8ToMongoBinData(b, elementName, obj);
- else {
+ } else if (TimestampFT()->HasInstance(value)) {
+ OpTime 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, const string& sname,
+ void V8Scope::v8ToMongoElement(BSONObjBuilder & b, const StringData& sname,
v8::Handle<v8::Value> value, int depth,
BSONObj* originalParent) {
if (value->IsString()) {
- b.append(sname, toSTLString(value));
+ b.append(sname, V8String(value));
return;
}
if (value->IsFunction()) {
uassert(16716, "cannot convert native function to BSON",
- !value->ToObject()->Has(v8StringData("_v8_function")));
- b.appendCode(sname, toSTLString(value));
+ !value->ToObject()->Has(strLitToV8("_v8_function")));
+ b.appendCode(sname, V8String(value));
return;
}
if (value->IsNumber()) {
- v8ToMongoNumber(b, sname, value, originalParent);
+ v8ToMongoNumber(b, sname, value.As<v8::Number>(), originalParent);
return;
}
if (value->IsArray()) {
@@ -1651,9 +1627,9 @@ namespace mongo {
BSONObj V8Scope::v8ToMongo(v8::Handle<v8::Object> o, int depth) {
BSONObj originalBSON;
- if (o->Has(v8::String::New("_bson"))) {
- originalBSON = unwrapBSONObj(o);
- BSONHolder* holder = unwrapHolder(o);
+ 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;
@@ -1665,19 +1641,20 @@ namespace mongo {
// 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(v8::String::New("_id"))) {
- v8ToMongoElement(b, "_id", o->Get(v8::String::New("_id")), 0, &originalBSON);
+ 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();
- v8::Local<v8::Value> value = o->Get(name);
- const string sname = toSTLString(name);
- if (depth == 0 && sname == "_id")
+
+ 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);
}
return b.obj();
@@ -1685,14 +1662,6 @@ namespace mongo {
// --- random utils ----
- v8::Function * V8Scope::getNamedCons(const char * name) {
- return v8::Function::Cast(*(v8::Context::GetCurrent()->Global()->Get(v8StringData(name))));
- }
-
- v8::Function * V8Scope::getObjectIdCons() {
- return getNamedCons("ObjectId");
- }
-
v8::Handle<v8::Value> V8Scope::Print(V8Scope* scope, const v8::Arguments& args) {
stringstream ss;
v8::HandleScope handle_scope;
@@ -1703,7 +1672,7 @@ namespace mongo {
else
ss << " ";
- if (!*args[i]) {
+ if (args[i].IsEmpty()) {
// failed to get object to convert
ss << "[unknown type]";
continue;