diff options
Diffstat (limited to 'src/mongo/scripting/v8_db.cpp')
| -rw-r--r-- | src/mongo/scripting/v8_db.cpp | 449 |
1 files changed, 278 insertions, 171 deletions
diff --git a/src/mongo/scripting/v8_db.cpp b/src/mongo/scripting/v8_db.cpp index 16b565c42a1..ea6cfe33591 100644 --- a/src/mongo/scripting/v8_db.cpp +++ b/src/mongo/scripting/v8_db.cpp @@ -33,9 +33,6 @@ using namespace std; -#define GETNS boost::scoped_array<char> ns(new char[args[0]->ToString()->Utf8Length()+1]); \ - args[0]->ToString()->WriteUtf8(ns.get()); - namespace mongo { namespace { @@ -61,18 +58,33 @@ namespace mongo { _mongoPrototypeManipulators.push_back(manipulator); } - static v8::Handle<v8::Value> newInstance(v8::Function* f, const v8::Arguments& args) { + 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; - int argc = args.Length(); + 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[24]; - for (int i = 0; i < argc && i < 24; ++i) { + 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) @@ -80,26 +92,18 @@ namespace mongo { else mongo = scope->createV8Function(mongoConsExternal); mongo->InstanceTemplate()->SetInternalFieldCount(1); - v8::Handle<v8::Template> proto = mongo->PrototypeTemplate(); - scope->injectV8Function("find", mongoFind, proto); - scope->injectV8Function("insert", mongoInsert, proto); - scope->injectV8Function("remove", mongoRemove, proto); - scope->injectV8Function("update", mongoUpdate, proto); - scope->injectV8Function("auth", mongoAuth, proto); - scope->injectV8Function("logout", mongoLogout, proto); + v8::Handle<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); fassert(16468, _mongoPrototypeManipulatorsFrozen); for (size_t i = 0; i < _mongoPrototypeManipulators.size(); ++i) _mongoPrototypeManipulators[i](scope, mongo); - v8::Handle<v8::FunctionTemplate> ic = scope->createV8Function(internalCursorCons); - ic->InstanceTemplate()->SetInternalFieldCount(1); - v8::Handle<v8::Template> icproto = ic->PrototypeTemplate(); - scope->injectV8Function("next", internalCursorNext, icproto); - scope->injectV8Function("hasNext", internalCursorHasNext, icproto); - scope->injectV8Function("objsLeftInBatch", internalCursorObjsLeftInBatch, icproto); - scope->injectV8Function("readOnly", internalCursorReadOnly, icproto); - proto->Set(scope->v8StringData("internalCursor"), ic); return mongo; } @@ -114,6 +118,11 @@ namespace mongo { strcpy(host, "127.0.0.1"); } + // 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())); + string errmsg; ConnectionString cs = ConnectionString::parse(host, errmsg); if (!cs.isValid()) { @@ -126,7 +135,7 @@ namespace mongo { return v8AssertionException(errmsg); } - v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.Holder()); + v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This()); scope->dbClientWithCommandsTracker.track(self, conn); ScriptEngine::runConnectCallback(*conn); @@ -141,6 +150,11 @@ namespace mongo { 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(); v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This()); scope->dbClientBaseTracker.track(self, conn); @@ -152,7 +166,9 @@ namespace mongo { return v8::Undefined(); } - DBClientBase* getConnection(const v8::Arguments& args) { + 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))); DBClientBase* conn = (DBClientBase*)(c->Value()); massert(16667, "Unable to get db client connection", conn); @@ -165,8 +181,8 @@ namespace mongo { 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") - DBClientBase * conn = getConnection(args); - GETNS; + 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() && @@ -174,24 +190,18 @@ namespace mongo { if (haveFields) fields = scope->v8ToMongo(args[2]->ToObject()); - v8::Local<v8::Object> mongo = args.This(); auto_ptr<mongo::DBClientCursor> cursor; - int nToReturn = (int)(args[3]->ToNumber()->Value()); - int nToSkip = (int)(args[4]->ToNumber()->Value()); - int batchSize = (int)(args[5]->ToNumber()->Value()); - int options = (int)(args[6]->ToNumber()->Value()); - cursor = conn->query(ns.get(), q, nToReturn, nToSkip, haveFields ? &fields : 0, + 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::Function* cons = (v8::Function*)(*(mongo->Get(scope->v8StringData("internalCursor")))); - - if (!cons) { - return v8AssertionException("could not create a cursor"); - } - + v8::Handle<v8::Function> cons = scope->InternalCursorFT()->GetFunction(); v8::Persistent<v8::Object> c = v8::Persistent<v8::Object>::New(cons->NewInstance()); c->SetInternalField(0, v8::External::New(cursor.get())); scope->dbClientCursorTracker.track(c, cursor.release()); @@ -202,12 +212,14 @@ namespace mongo { 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"); } - DBClientBase * conn = getConnection(args); - GETNS; + DBClientBase * conn = getConnection(scope, args); + const string ns = toSTLString(args[0]); v8::Handle<v8::Integer> flags = args[2]->ToInteger(); @@ -225,21 +237,21 @@ namespace mongo { if (!el->Has(scope->v8StringData("_id"))) { v8::Handle<v8::Value> argv[1]; el->ForceSet(scope->v8StringData("_id"), - scope->getObjectIdCons()->NewInstance(0, argv)); + scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv)); } bos.push_back(scope->v8ToMongo(el)); } - conn->insert(ns.get(), bos, flags->Int32Value()); + 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->getObjectIdCons()->NewInstance(0, argv)); + scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv)); } BSONObj o = scope->v8ToMongo(in); - conn->insert(ns.get(), o); + conn->insert(ns, o); } return v8::Undefined(); } @@ -248,12 +260,14 @@ namespace mongo { 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"); } - DBClientBase * conn = getConnection(args); - GETNS; + DBClientBase * conn = getConnection(scope, args); + const string ns = toSTLString(args[0]); v8::Handle<v8::Object> in = args[1]->ToObject(); BSONObj o = scope->v8ToMongo(in); @@ -263,7 +277,7 @@ namespace mongo { justOne = args[2]->BooleanValue(); } - conn->remove(ns.get(), o, justOne); + conn->remove(ns, o, justOne); return v8::Undefined(); } @@ -272,12 +286,14 @@ namespace mongo { 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"); } - DBClientBase * conn = getConnection(args); - GETNS; + 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(); @@ -287,12 +303,12 @@ namespace mongo { BSONObj q1 = scope->v8ToMongo(q); BSONObj o1 = scope->v8ToMongo(o); - conn->update(ns.get(), q1, o1, upsert, multi); + conn->update(ns, q1, o1, upsert, multi); return v8::Undefined(); } v8::Handle<v8::Value> mongoAuth(V8Scope* scope, const v8::Arguments& args) { - DBClientWithCommands* conn = getConnection(args); + DBClientWithCommands* conn = getConnection(scope, args); if (NULL == conn) return v8AssertionException("no connection"); @@ -321,7 +337,7 @@ namespace mongo { v8::Handle<v8::Value> mongoLogout(V8Scope* scope, const v8::Arguments& args) { argumentCheck(args.Length() == 1, "logout needs 1 arg") - DBClientBase* conn = getConnection(args); + DBClientBase* conn = getConnection(scope, args); const string db = toSTLString(args[0]); BSONObj ret; conn->logout(db, ret); @@ -331,9 +347,11 @@ namespace mongo { /** * get cursor from v8 argument */ - mongo::DBClientCursor* getCursor(const v8::Arguments& args) { + 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 = (mongo::DBClientCursor*)(c->Value()); + mongo::DBClientCursor* cursor = static_cast<mongo::DBClientCursor*>(c->Value()); return cursor; } @@ -345,7 +363,7 @@ namespace mongo { * cursor.next() */ v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor* cursor = getCursor(args); + mongo::DBClientCursor* cursor = getCursor(scope, args); if (! cursor) return v8::Undefined(); BSONObj o = cursor->next(); @@ -359,7 +377,7 @@ namespace mongo { * cursor.hasNext() */ v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor* cursor = getCursor(args); + mongo::DBClientCursor* cursor = getCursor(scope, args); if (! cursor) return v8::Boolean::New(false); return v8::Boolean::New(cursor->more()); @@ -370,7 +388,7 @@ namespace mongo { */ v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor* cursor = getCursor(args); + mongo::DBClientCursor* cursor = getCursor(scope, args); if (! cursor) return v8::Number::New(0.0); return v8::Number::New(static_cast<double>(cursor->objsLeftInBatch())); @@ -380,12 +398,21 @@ namespace mongo { * 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]); @@ -404,8 +431,20 @@ namespace mongo { } 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]); @@ -415,14 +454,17 @@ namespace mongo { return v8AssertionException("can't use sharded collection from db.eval"); } - for (int i = 0; i < args.Length(); i++) { - argumentCheck(!args[i]->IsUndefined(), - "collection constructor called with undefined argument") - } 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(); @@ -471,86 +513,126 @@ namespace mongo { v8::Handle<v8::Value> collectionSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { - // 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>(); + 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"); } - // dont set - return value; } v8::Handle<v8::Value> collectionGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { - 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 - if (haveLocalShardingInfo(toSTLString( - prop->ToObject()->GetRealNamedProperty(v8::String::New("_fullName"))))) { - return v8AssertionException("can't use sharded collection from db.eval"); + 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 + if (haveLocalShardingInfo(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>(); } - 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::Function* f = (v8::Function*)(*getCollection); - v8::Handle<v8::Value> argv[1]; - argv[0] = name; - v8::Local<v8::Value> coll = f->Call(info.This(), 1, argv); - if (coll.IsEmpty()) { - if (tryCatch.HasCaught()) { - return v8::ThrowException(tryCatch.Exception()); + // 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"); } - return v8::Handle<v8::Value>(); - } - // cache collection for reuse, don't enumerate - info.This()->ForceSet(name, coll, v8::DontEnum); - return coll; + 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) { - v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get( - v8::String::New("arrayAccess")); - massert(16660, "arrayAccess is not a function", arrayAccess->IsFunction()); + 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::Function* f = (v8::Function*)(*arrayAccess); - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::Number::New(index); + 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); + 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) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getObjectIdCons(); + 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(); @@ -571,58 +653,65 @@ namespace mongo { } v8::Handle<v8::Value> dbRefInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("DBRef"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->DBRefFT()->GetFunction(); return newInstance(f, args); } - // We use a DBRef object to display DBRefs converted from BSON, so this clumsy hack - // lets the constructor serve two masters with different requirements. The downside - // is that this internal usage is available to direct calls from the shell, so DBRef() - // is accepted and produces DBRef(undefined, undefined). - // TODO(tad) fix this - if (args.Length() == 0) { - return it; - } + v8::Handle<v8::Object> it = args.This(); + verify(scope->DBRefFT()->HasInstance(it)); argumentCheck(args.Length() == 2, "DBRef needs 2 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]); return it; } v8::Handle<v8::Value> dbPointerInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("DBPointer"); + 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]); - it->SetHiddenValue(scope->v8StringData("__DBPointer"), v8::Number::New(1)); return it; } v8::Handle<v8::Value> dbTimestampInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("Timestamp"); + 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 = ((2039LL-1970LL) *365*24*60*60); //seconds between 1970-2038 if( t > largestVal ) return v8AssertionException( str::stream() - << "The first argument must be in seconds;" + << "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]); @@ -631,29 +720,31 @@ namespace mongo { return v8AssertionException("Timestamp needs 0 or 2 arguments"); } - it->SetInternalField(0, v8::Uint32::New(Timestamp)); - return it; } v8::Handle<v8::Value> binDataInit(V8Scope* scope, const v8::Arguments& args) { - v8::Local<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("BinData"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); return newInstance(f, args); } - v8::Handle<v8::Value> type; + v8::Local<v8::Object> it = args.This(); + verify(scope->BinDataFT()->HasInstance(it)); + if (args.Length() == 2) { // 2 args: type, base64 string - type = args[0]; + 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())); it->ForceSet(scope->v8StringData("type"), type); - it->SetHiddenValue(v8::String::New("__BinData"), v8::Number::New(1)); it->SetInternalField(0, args[1]); } else if (args.Length() != 0) { @@ -665,21 +756,27 @@ namespace mongo { 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)); int len = v8::Handle<v8::Number>::Cast(it->Get(v8::String::New("len")))->Int32Value(); + verify(it->InternalFieldCount() == 1); string data = base64::decode(toSTLString(it->GetInternalField(0))); stringstream ss; ss.setf (ios_base::hex, ios_base::basefield); @@ -694,17 +791,18 @@ namespace mongo { static v8::Handle<v8::Value> hexToBinData(V8Scope* scope, v8::Local<v8::Object> it, int type, string hexstr) { + verify(scope->BinDataFT()->HasInstance(it)); + int len = hexstr.length() / 2; - scoped_array<char> data(new char[16]); + scoped_array<char> data(new char[len]); const char* src = hexstr.c_str(); - for(int i = 0; i < 16; i++) { + for(int i = 0; i < len; i++) { data[i] = fromHex(src + i * 2); } - string encoded = base64::encode(data.get(), 16); + string encoded = base64::encode(data.get(), len); it->ForceSet(v8::String::New("len"), v8::Number::New(len)); it->ForceSet(v8::String::New("type"), v8::Number::New(type)); - it->SetHiddenValue(v8::String::New("__BinData"), v8::Number::New(1)); it->SetInternalField(0, v8::String::New(encoded.c_str(), encoded.length())); return it; } @@ -714,7 +812,7 @@ namespace mongo { v8::String::Utf8Value utf(args[0]); argumentCheck(utf.length() == 32, "UUID string must have 32 characters") - v8::Function* f = scope->getNamedCons("BinData"); + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); v8::Local<v8::Object> it = f->NewInstance(); return hexToBinData(scope, it, bdtUUID, *utf); } @@ -724,7 +822,7 @@ namespace mongo { v8::String::Utf8Value utf(args[0]); argumentCheck(utf.length() == 32, "MD5 string must have 32 characters") - v8::Function* f = scope->getNamedCons("BinData"); + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); v8::Local<v8::Object> it = f->NewInstance(); return hexToBinData(scope, it, MD5Type, *utf); } @@ -732,21 +830,23 @@ namespace mongo { v8::Handle<v8::Value> hexDataInit(V8Scope* scope, const v8::Arguments& args) { argumentCheck(args.Length() == 2, "HexData needs 2 arguments") v8::String::Utf8Value utf(args[1]); - v8::Function* f = scope->getNamedCons("BinData"); + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); v8::Local<v8::Object> it = f->NewInstance(); return hexToBinData(scope, it, args[0]->IntegerValue(), *utf); } v8::Handle<v8::Value> numberLongInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("NumberLong"); + 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)); } @@ -784,15 +884,15 @@ namespace mongo { } } else { - it->ForceSet(scope->v8StringData("floatApprox"), args[0]); - it->ForceSet(scope->v8StringData("top"), args[1]); - it->ForceSet(scope->v8StringData("bottom"), args[2]); + it->ForceSet(scope->v8StringData("floatApprox"), args[0]->ToNumber()); + it->ForceSet(scope->v8StringData("top"), args[1]->ToUint32()); + it->ForceSet(scope->v8StringData("bottom"), args[2]->ToUint32()); } - it->SetHiddenValue(v8::String::New("__NumberLong"), v8::Number::New(1)); return it; } - long long numberLongVal(const v8::Handle<v8::Object>& 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 @@ -803,7 +903,7 @@ namespace mongo { v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); - long long val = numberLongVal(it); + long long val = numberLongVal(scope, it); return v8::Number::New(double(val)); } @@ -815,7 +915,7 @@ namespace mongo { v8::Handle<v8::Object> it = args.This(); stringstream ss; - long long val = numberLongVal(it); + long long val = numberLongVal(scope, it); const long long limit = 2LL << 30; if (val <= -limit || limit <= val) @@ -828,12 +928,14 @@ namespace mongo { } v8::Handle<v8::Value> numberIntInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("NumberInt"); + 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)); @@ -844,10 +946,16 @@ namespace mongo { 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(); - int val = it->GetHiddenValue(v8::String::New("__NumberInt"))->Int32Value(); - return v8::Number::New(double(val)); + return v8::Integer::New(numberIntVal(scope, it)); } v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Arguments& args) { @@ -856,8 +964,7 @@ namespace mongo { v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); - - int val = it->GetHiddenValue(v8::String::New("__NumberInt"))->Int32Value(); + int val = numberIntVal(scope, it); string ret = str::stream() << "NumberInt(" << val << ")"; return v8::String::New(ret.c_str()); } |
