diff options
author | Jason Carey <jcarey@argv.me> | 2015-10-06 20:08:03 -0400 |
---|---|---|
committer | Jason Carey <jcarey@argv.me> | 2015-10-06 20:14:01 -0400 |
commit | 95060c27ed2dddcb6343a88f7aa405ed8a935ad7 (patch) | |
tree | ea6d2287cbdb7154d22af6335ae1f56ac7b6aba0 /src | |
parent | b7104c6f2f597335c6b9890ff6b80243625a6258 (diff) | |
download | mongo-95060c27ed2dddcb6343a88f7aa405ed8a935ad7.tar.gz |
SERVER-19977 Intern JS Strings
Rather than supplying const char*'s throughout our use of the MozJS api,
intern the strings and root their associated ids (to save on string
parsing).
Diffstat (limited to 'src')
22 files changed, 341 insertions, 92 deletions
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript index 24bc437dec6..4f92296f1c7 100644 --- a/src/mongo/scripting/SConscript +++ b/src/mongo/scripting/SConscript @@ -121,6 +121,7 @@ if usemozjs: 'mozjs/global.cpp', 'mozjs/idwrapper.cpp', 'mozjs/implscope.cpp', + 'mozjs/internedstring.cpp', 'mozjs/jscustomallocator.cpp', 'mozjs/jsstringwrapper.cpp', 'mozjs/jsthread.cpp', diff --git a/src/mongo/scripting/mozjs/bindata.cpp b/src/mongo/scripting/mozjs/bindata.cpp index e9f10cae125..ba07c3164ad 100644 --- a/src/mongo/scripting/mozjs/bindata.cpp +++ b/src/mongo/scripting/mozjs/bindata.cpp @@ -34,6 +34,7 @@ #include <cctype> #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" @@ -157,7 +158,7 @@ void BinDataInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) { str::stream ss; - ss << "BinData(" << o.getNumber("type") << ",\"" << *str << "\")"; + ss << "BinData(" << o.getNumber(InternedString::type) << ",\"" << *str << "\")"; ValueReader(cx, args.rval()).fromStringData(ss.operator std::string()); } @@ -215,8 +216,8 @@ void BinDataInfo::construct(JSContext* cx, JS::CallArgs args) { JS::RootedValue len(cx); len.setInt32(tmpBase64.length()); - o.defineProperty("len", len, JSPROP_READONLY); - o.defineProperty("type", type, JSPROP_READONLY); + o.defineProperty(InternedString::len, len, JSPROP_READONLY); + o.defineProperty(InternedString::type, type, JSPROP_READONLY); JS_SetPrivate(thisv, new std::string(std::move(str))); diff --git a/src/mongo/scripting/mozjs/bson.cpp b/src/mongo/scripting/mozjs/bson.cpp index 8155e083e9c..f2c30407845 100644 --- a/src/mongo/scripting/mozjs/bson.cpp +++ b/src/mongo/scripting/mozjs/bson.cpp @@ -35,6 +35,7 @@ #include "mongo/scripting/mozjs/idwrapper.h" #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" @@ -251,7 +252,7 @@ void BSONInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObj JS::RootedValue value(cx); value.setBoolean(true); - ObjectWrapper(cx, proto).defineProperty("_bson", value, 0); + ObjectWrapper(cx, proto).defineProperty(InternedString::_bson, value, 0); } } // namespace mozjs diff --git a/src/mongo/scripting/mozjs/cursor.cpp b/src/mongo/scripting/mozjs/cursor.cpp index 072e2ffb999..4264fdfc23c 100644 --- a/src/mongo/scripting/mozjs/cursor.cpp +++ b/src/mongo/scripting/mozjs/cursor.cpp @@ -32,6 +32,7 @@ #include "mongo/scripting/mozjs/bson.h" #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/wrapconstrainedmethod.h" @@ -81,7 +82,7 @@ void CursorInfo::Functions::next::call(JSContext* cx, JS::CallArgs args) { ObjectWrapper o(cx, args.thisv()); BSONObj bson = cursor->next(); - bool ro = o.hasField("_ro") ? o.getBoolean("_ro") : false; + bool ro = o.hasField(InternedString::_ro) ? o.getBoolean(InternedString::_ro) : false; // getOwned because cursor->next() gives us unowned bson from an internal // buffer and we need to make a copy @@ -111,7 +112,7 @@ void CursorInfo::Functions::objsLeftInBatch::call(JSContext* cx, JS::CallArgs ar } void CursorInfo::Functions::readOnly::call(JSContext* cx, JS::CallArgs args) { - ObjectWrapper(cx, args.thisv()).setBoolean("_ro", true); + ObjectWrapper(cx, args.thisv()).setBoolean(InternedString::_ro, true); args.rval().set(args.thisv()); } diff --git a/src/mongo/scripting/mozjs/db.cpp b/src/mongo/scripting/mozjs/db.cpp index 9a5d45b0984..181ee12befa 100644 --- a/src/mongo/scripting/mozjs/db.cpp +++ b/src/mongo/scripting/mozjs/db.cpp @@ -34,6 +34,7 @@ #include "mongo/db/operation_context.h" #include "mongo/scripting/mozjs/idwrapper.h" #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" @@ -85,7 +86,7 @@ void DBInfo::getProperty(JSContext* cx, // no hit, create new collection JS::RootedValue getCollection(cx); - parentWrapper.getValue("getCollection", &getCollection); + parentWrapper.getValue(InternedString::getCollection, &getCollection); if (!(getCollection.isObject() && JS_ObjectIsFunction(cx, getCollection.toObjectOrNull()))) { uasserted(ErrorCodes::BadValue, "getCollection is not a function"); @@ -124,8 +125,8 @@ void DBInfo::construct(JSContext* cx, JS::CallArgs args) { scope->getProto<DBInfo>().newObject(&thisv); ObjectWrapper o(cx, thisv); - o.setValue("_mongo", args.get(0)); - o.setValue("_name", args.get(1)); + o.setValue(InternedString::_mongo, args.get(0)); + o.setValue(InternedString::_name, args.get(1)); std::string dbName = ValueWriter(cx, args.get(1)).toString(); diff --git a/src/mongo/scripting/mozjs/dbcollection.cpp b/src/mongo/scripting/mozjs/dbcollection.cpp index c4814251839..2cc03a752e1 100644 --- a/src/mongo/scripting/mozjs/dbcollection.cpp +++ b/src/mongo/scripting/mozjs/dbcollection.cpp @@ -67,10 +67,10 @@ void DBCollectionInfo::construct(JSContext* cx, JS::CallArgs args) { scope->getProto<DBCollectionInfo>().newObject(&thisv); ObjectWrapper o(cx, thisv); - o.setValue("_mongo", args.get(0)); - o.setValue("_db", args.get(1)); - o.setValue("_shortName", args.get(2)); - o.setValue("_fullName", args.get(3)); + o.setValue(InternedString::_mongo, args.get(0)); + o.setValue(InternedString::_db, args.get(1)); + o.setValue(InternedString::_shortName, args.get(2)); + o.setValue(InternedString::_fullName, args.get(3)); std::string fullName = ValueWriter(cx, args.get(3)).toString(); diff --git a/src/mongo/scripting/mozjs/dbpointer.cpp b/src/mongo/scripting/mozjs/dbpointer.cpp index 20c3882ba72..61a355c92c0 100644 --- a/src/mongo/scripting/mozjs/dbpointer.cpp +++ b/src/mongo/scripting/mozjs/dbpointer.cpp @@ -31,6 +31,7 @@ #include "mongo/scripting/mozjs/dbpointer.h" #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/util/mongoutils/str.h" @@ -56,8 +57,8 @@ void DBPointerInfo::construct(JSContext* cx, JS::CallArgs args) { scope->getProto<DBPointerInfo>().newObject(&thisv); ObjectWrapper o(cx, thisv); - o.setValue("ns", args.get(0)); - o.setValue("id", args.get(1)); + o.setValue(InternedString::ns, args.get(0)); + o.setValue(InternedString::id, args.get(1)); args.rval().setObjectOrNull(thisv); } diff --git a/src/mongo/scripting/mozjs/dbquery.cpp b/src/mongo/scripting/mozjs/dbquery.cpp index d3c68538884..c2543814505 100644 --- a/src/mongo/scripting/mozjs/dbquery.cpp +++ b/src/mongo/scripting/mozjs/dbquery.cpp @@ -32,6 +32,7 @@ #include "mongo/scripting/mozjs/idwrapper.h" #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" namespace mongo { @@ -49,10 +50,10 @@ void DBQueryInfo::construct(JSContext* cx, JS::CallArgs args) { scope->getProto<DBQueryInfo>().newObject(&thisv); ObjectWrapper o(cx, thisv); - o.setValue("_mongo", args.get(0)); - o.setValue("_db", args.get(1)); - o.setValue("_collection", args.get(2)); - o.setValue("_ns", args.get(3)); + o.setValue(InternedString::_mongo, args.get(0)); + o.setValue(InternedString::_db, args.get(1)); + o.setValue(InternedString::_collection, args.get(2)); + o.setValue(InternedString::_ns, args.get(3)); JS::RootedObject emptyObj(cx); JS::RootedValue emptyObjVal(cx); @@ -62,44 +63,44 @@ void DBQueryInfo::construct(JSContext* cx, JS::CallArgs args) { nullVal.setNull(); if (args.length() > 4 && args.get(4).isObject()) { - o.setValue("_query", args.get(4)); + o.setValue(InternedString::_query, args.get(4)); } else { - o.setValue("_query", emptyObjVal); + o.setValue(InternedString::_query, emptyObjVal); } if (args.length() > 5 && args.get(5).isObject()) { - o.setValue("_fields", args.get(5)); + o.setValue(InternedString::_fields, args.get(5)); } else { - o.setValue("_fields", nullVal); + o.setValue(InternedString::_fields, nullVal); } if (args.length() > 6 && args.get(6).isNumber()) { - o.setValue("_limit", args.get(6)); + o.setValue(InternedString::_limit, args.get(6)); } else { - o.setNumber("_limit", 0); + o.setNumber(InternedString::_limit, 0); } if (args.length() > 7 && args.get(7).isNumber()) { - o.setValue("_skip", args.get(7)); + o.setValue(InternedString::_skip, args.get(7)); } else { - o.setNumber("_skip", 0); + o.setNumber(InternedString::_skip, 0); } if (args.length() > 8 && args.get(8).isNumber()) { - o.setValue("_batchSize", args.get(8)); + o.setValue(InternedString::_batchSize, args.get(8)); } else { - o.setNumber("_batchSize", 0); + o.setNumber(InternedString::_batchSize, 0); } if (args.length() > 9 && args.get(9).isNumber()) { - o.setValue("_options", args.get(9)); + o.setValue(InternedString::_options, args.get(9)); } else { - o.setNumber("_options", 0); + o.setNumber(InternedString::_options, 0); } - o.setValue("_cursor", nullVal); - o.setNumber("_numReturned", 0); - o.setBoolean("_special", false); + o.setValue(InternedString::_cursor, nullVal); + o.setNumber(InternedString::_numReturned, 0); + o.setBoolean(InternedString::_special, false); args.rval().setObjectOrNull(thisv); } @@ -126,7 +127,7 @@ void DBQueryInfo::getProperty(JSContext* cx, ObjectWrapper parentWrapper(cx, parent); JS::RootedValue arrayAccess(cx); - parentWrapper.getValue("arrayAccess", &arrayAccess); + parentWrapper.getValue(InternedString::arrayAccess, &arrayAccess); if (arrayAccess.isObject() && JS_ObjectIsFunction(cx, arrayAccess.toObjectOrNull())) { JS::AutoValueArray<1> args(cx); diff --git a/src/mongo/scripting/mozjs/dbref.cpp b/src/mongo/scripting/mozjs/dbref.cpp index 4200ebaa67f..e41a0d7af33 100644 --- a/src/mongo/scripting/mozjs/dbref.cpp +++ b/src/mongo/scripting/mozjs/dbref.cpp @@ -31,6 +31,7 @@ #include "mongo/scripting/mozjs/dbref.h" #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" namespace mongo { @@ -51,14 +52,14 @@ void DBRefInfo::construct(JSContext* cx, JS::CallArgs args) { scope->getProto<DBRefInfo>().newObject(&thisv); ObjectWrapper o(cx, thisv); - o.setValue("$ref", args.get(0)); - o.setValue("$id", args.get(1)); + o.setValue(InternedString::dollar_ref, args.get(0)); + o.setValue(InternedString::dollar_id, args.get(1)); if (args.length() == 3) { if (!args.get(2).isString()) uasserted(ErrorCodes::BadValue, "DBRef 3rd parameter must be a string"); - o.setValue("$db", args.get(2)); + o.setValue(InternedString::dollar_db, args.get(2)); } args.rval().setObjectOrNull(thisv); diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp index debb02f968b..a6621637973 100644 --- a/src/mongo/scripting/mozjs/implscope.cpp +++ b/src/mongo/scripting/mozjs/implscope.cpp @@ -242,6 +242,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine) _globalProto(_context), _global(_globalProto.getProto()), _funcs(), + _internedStrings(_context), _pendingKill(false), _opId(0), _opCtx(nullptr), diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h index 1222ed38a24..56eda215fff 100644 --- a/src/mongo/scripting/mozjs/implscope.h +++ b/src/mongo/scripting/mozjs/implscope.h @@ -44,6 +44,7 @@ #include "mongo/scripting/mozjs/engine.h" #include "mongo/scripting/mozjs/error.h" #include "mongo/scripting/mozjs/global.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/jsthread.h" #include "mongo/scripting/mozjs/maxkey.h" #include "mongo/scripting/mozjs/minkey.h" @@ -296,6 +297,10 @@ public: void advanceGeneration() override; + JS::HandleId getInternedStringId(InternedString name) { + return _internedStrings.getInternedString(name); + } + private: void _MozJSCreateFunction(const char* raw, ScriptingFunction functionNumber, @@ -348,6 +353,7 @@ private: WrapType<GlobalInfo> _globalProto; JS::HandleObject _global; std::vector<JS::PersistentRootedValue> _funcs; + InternedStringTable _internedStrings; std::atomic<bool> _pendingKill; std::string _error; unsigned int _opId; // op id for this scope diff --git a/src/mongo/scripting/mozjs/internedstring.cpp b/src/mongo/scripting/mozjs/internedstring.cpp new file mode 100644 index 00000000000..61510823f2c --- /dev/null +++ b/src/mongo/scripting/mozjs/internedstring.cpp @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2015 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/platform/basic.h" + +#include "mongo/scripting/mozjs/internedstring.h" + +#include "mongo/scripting/mozjs/implscope.h" + +namespace mongo { +namespace mozjs { + +InternedStringTable::InternedStringTable(JSContext* cx) { + JSAutoRequest ar(cx); + + int i = 0; + +#define MONGO_MOZJS_INTERNED_STRING(name, str) \ + do { \ + auto s = JS_InternString(cx, str); \ + if (!s) { \ + uasserted(ErrorCodes::JSInterpreterFailure, "Failed to JS_InternString"); \ + } \ + _internedStrings[i++].init(cx, INTERNED_STRING_TO_JSID(cx, s)); \ + } while (0); +#include "mongo/scripting/mozjs/internedstring.defs" +#undef MONGO_MOZJS_INTERNED_STRING +} + +InternedStringTable::~InternedStringTable() { + for (auto&& x : _internedStrings) { + x.reset(); + } +} + +InternedStringId::InternedStringId(JSContext* cx, InternedString id) + : _id(cx, getScope(cx)->getInternedStringId(id)) {} + +} // namespace mozjs +} // namespace mongo diff --git a/src/mongo/scripting/mozjs/internedstring.defs b/src/mongo/scripting/mozjs/internedstring.defs new file mode 100644 index 00000000000..377e86f1aaa --- /dev/null +++ b/src/mongo/scripting/mozjs/internedstring.defs @@ -0,0 +1,49 @@ +/** + * This file uses X-macros to populate an enum as well as intern a number of + * strings for the InternedStringTable type. I.e. it's included multiple times + * with different definitions of MONGO_MOZJS_INTERNED_STRING. + */ + +MONGO_MOZJS_INTERNED_STRING(arrayAccess, "arrayAccess") +MONGO_MOZJS_INTERNED_STRING(_batchSize, "_batchSize") +MONGO_MOZJS_INTERNED_STRING(bottom, "bottom") +MONGO_MOZJS_INTERNED_STRING(_bson, "_bson") +MONGO_MOZJS_INTERNED_STRING(_collection, "_collection") +MONGO_MOZJS_INTERNED_STRING(constructor, "constructor") +MONGO_MOZJS_INTERNED_STRING(_cursor, "_cursor") +MONGO_MOZJS_INTERNED_STRING(_db, "_db") +MONGO_MOZJS_INTERNED_STRING(dollar_db, "$db") +MONGO_MOZJS_INTERNED_STRING(dollar_id, "$id") +MONGO_MOZJS_INTERNED_STRING(dollar_ref, "$ref") +MONGO_MOZJS_INTERNED_STRING(_fields, "_fields") +MONGO_MOZJS_INTERNED_STRING(floatApprox, "floatApprox") +MONGO_MOZJS_INTERNED_STRING(_fullName, "_fullName") +MONGO_MOZJS_INTERNED_STRING(getCollection, "getCollection") +MONGO_MOZJS_INTERNED_STRING(host, "host") +MONGO_MOZJS_INTERNED_STRING(_id, "_id") +MONGO_MOZJS_INTERNED_STRING(id, "id") +MONGO_MOZJS_INTERNED_STRING(i, "i") +MONGO_MOZJS_INTERNED_STRING(_JSThreadConfig, "_JSThreadConfig") +MONGO_MOZJS_INTERNED_STRING(len, "len") +MONGO_MOZJS_INTERNED_STRING(_limit, "_limit") +MONGO_MOZJS_INTERNED_STRING(MaxKey, "MaxKey") +MONGO_MOZJS_INTERNED_STRING(MinKey, "MinKey") +MONGO_MOZJS_INTERNED_STRING(_mongo, "_mongo") +MONGO_MOZJS_INTERNED_STRING(_name, "_name") +MONGO_MOZJS_INTERNED_STRING(name, "name") +MONGO_MOZJS_INTERNED_STRING(_ns, "_ns") +MONGO_MOZJS_INTERNED_STRING(ns, "ns") +MONGO_MOZJS_INTERNED_STRING(_numReturned, "_numReturned") +MONGO_MOZJS_INTERNED_STRING(_options, "_options") +MONGO_MOZJS_INTERNED_STRING(_query, "_query") +MONGO_MOZJS_INTERNED_STRING(readOnly, "readOnly") +MONGO_MOZJS_INTERNED_STRING(_ro, "_ro") +MONGO_MOZJS_INTERNED_STRING(_shortName, "_shortName") +MONGO_MOZJS_INTERNED_STRING(singleton, "singleton") +MONGO_MOZJS_INTERNED_STRING(_skip, "_skip") +MONGO_MOZJS_INTERNED_STRING(slaveOk, "slaveOk") +MONGO_MOZJS_INTERNED_STRING(_special, "_special") +MONGO_MOZJS_INTERNED_STRING(str, "str") +MONGO_MOZJS_INTERNED_STRING(top, "top") +MONGO_MOZJS_INTERNED_STRING(t, "t") +MONGO_MOZJS_INTERNED_STRING(type, "type") diff --git a/src/mongo/scripting/mozjs/internedstring.h b/src/mongo/scripting/mozjs/internedstring.h new file mode 100644 index 00000000000..79c19c38d51 --- /dev/null +++ b/src/mongo/scripting/mozjs/internedstring.h @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2015 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 <array> +#include <jsapi.h> + +namespace mongo { +namespace mozjs { + +/** + * An enum that includes members for each interned string we own. These values + * can be used with InteredStringId to get a handle to an id that matches that + * identifier, or directly in ObjectWrapper. + */ +enum class InternedString { +#define MONGO_MOZJS_INTERNED_STRING(name, str) name, +#include "mongo/scripting/mozjs/internedstring.defs" +#undef MONGO_MOZJS_INTERNED_STRING + NUM_IDS, +}; + +/** + * Provides a handle to an interned string id. + */ +class InternedStringId { +public: + InternedStringId(JSContext* cx, InternedString id); + + operator JS::HandleId() { + return _id; + } + + operator jsid() { + return _id; + } + +private: + JS::RootedId _id; +}; + +/** + * The scope global interned string table. This owns persistent roots to each + * id and can lookup ids by enum identifier + */ +class InternedStringTable { +public: + explicit InternedStringTable(JSContext* cx); + ~InternedStringTable(); + + JS::HandleId getInternedString(InternedString id) { + return _internedStrings[static_cast<std::size_t>(id)]; + } + +private: + std::array<JS::PersistentRootedId, static_cast<std::size_t>(InternedString::NUM_IDS)> + _internedStrings; +}; + +} // namespace mozjs +} // namespace mongo diff --git a/src/mongo/scripting/mozjs/jsthread.cpp b/src/mongo/scripting/mozjs/jsthread.cpp index 35dd5537b5b..2e5ea289d84 100644 --- a/src/mongo/scripting/mozjs/jsthread.cpp +++ b/src/mongo/scripting/mozjs/jsthread.cpp @@ -225,7 +225,7 @@ namespace { JSThreadConfig* getConfig(JSContext* cx, JS::CallArgs args) { JS::RootedValue value(cx); - ObjectWrapper(cx, args.thisv()).getValue("_JSThreadConfig", &value); + ObjectWrapper(cx, args.thisv()).getValue(InternedString::_JSThreadConfig, &value); if (!value.isObject()) uasserted(ErrorCodes::BadValue, "_JSThreadConfig not an object"); @@ -255,7 +255,7 @@ void JSThreadInfo::Functions::init::call(JSContext* cx, JS::CallArgs args) { JSThreadConfig* config = new JSThreadConfig(cx, args); JS_SetPrivate(obj, config); - ObjectWrapper(cx, args.thisv()).setObject("_JSThreadConfig", obj); + ObjectWrapper(cx, args.thisv()).setObject(InternedString::_JSThreadConfig, obj); args.rval().setUndefined(); } diff --git a/src/mongo/scripting/mozjs/maxkey.cpp b/src/mongo/scripting/mozjs/maxkey.cpp index 78dae6a9719..6730da09b0d 100644 --- a/src/mongo/scripting/mozjs/maxkey.cpp +++ b/src/mongo/scripting/mozjs/maxkey.cpp @@ -30,6 +30,7 @@ #include "mongo/scripting/mozjs/maxkey.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/implscope.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" @@ -44,10 +45,6 @@ const JSFunctionSpec MaxKeyInfo::methods[2] = { const char* const MaxKeyInfo::className = "MaxKey"; -namespace { -const char* const kSingleton = "singleton"; -} // namespace - void MaxKeyInfo::construct(JSContext* cx, JS::CallArgs args) { call(cx, args); } @@ -64,14 +61,14 @@ void MaxKeyInfo::call(JSContext* cx, JS::CallArgs args) { JS::RootedValue val(cx); - if (!o.hasField(kSingleton)) { + if (!o.hasField(InternedString::singleton)) { JS::RootedObject thisv(cx); scope->getProto<MaxKeyInfo>().newObject(&thisv); val.setObjectOrNull(thisv); - o.setValue(kSingleton, val); + o.setValue(InternedString::singleton, val); } else { - o.getValue(kSingleton, &val); + o.getValue(InternedString::singleton, &val); if (!getScope(cx)->getProto<MaxKeyInfo>().instanceOf(val)) uasserted(ErrorCodes::BadValue, "MaxKey singleton not of type MaxKey"); @@ -90,8 +87,8 @@ void MaxKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleO JS::RootedValue value(cx); getScope(cx)->getProto<MaxKeyInfo>().newObject(&value); - ObjectWrapper(cx, global).setValue("MaxKey", value); - protoWrapper.setValue(kSingleton, value); + ObjectWrapper(cx, global).setValue(InternedString::MaxKey, value); + protoWrapper.setValue(InternedString::singleton, value); } } // namespace mozjs diff --git a/src/mongo/scripting/mozjs/minkey.cpp b/src/mongo/scripting/mozjs/minkey.cpp index 13644e0220a..0325a93dcb7 100644 --- a/src/mongo/scripting/mozjs/minkey.cpp +++ b/src/mongo/scripting/mozjs/minkey.cpp @@ -30,6 +30,7 @@ #include "mongo/scripting/mozjs/minkey.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/implscope.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" @@ -44,10 +45,6 @@ const JSFunctionSpec MinKeyInfo::methods[2] = { const char* const MinKeyInfo::className = "MinKey"; -namespace { -const char* const kSingleton = "singleton"; -} // namespace - void MinKeyInfo::construct(JSContext* cx, JS::CallArgs args) { call(cx, args); } @@ -64,14 +61,14 @@ void MinKeyInfo::call(JSContext* cx, JS::CallArgs args) { JS::RootedValue val(cx); - if (!o.hasField(kSingleton)) { + if (!o.hasField(InternedString::singleton)) { JS::RootedObject thisv(cx); scope->getProto<MinKeyInfo>().newObject(&thisv); val.setObjectOrNull(thisv); - o.setValue(kSingleton, val); + o.setValue(InternedString::singleton, val); } else { - o.getValue(kSingleton, &val); + o.getValue(InternedString::singleton, &val); if (!getScope(cx)->getProto<MinKeyInfo>().instanceOf(val)) uasserted(ErrorCodes::BadValue, "MinKey singleton not of type MinKey"); @@ -90,8 +87,8 @@ void MinKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleO JS::RootedValue value(cx); getScope(cx)->getProto<MinKeyInfo>().newObject(&value); - ObjectWrapper(cx, global).setValue("MinKey", value); - protoWrapper.setValue(kSingleton, value); + ObjectWrapper(cx, global).setValue(InternedString::MinKey, value); + protoWrapper.setValue(InternedString::singleton, value); } } // namespace mozjs diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp index 684870aa5af..e6cad5fad6e 100644 --- a/src/mongo/scripting/mozjs/mongo.cpp +++ b/src/mongo/scripting/mozjs/mongo.cpp @@ -240,7 +240,7 @@ void MongoBase::Functions::insert::call(JSContext* cx, JS::CallArgs args) { ObjectWrapper o(cx, args.thisv()); - if (o.hasField("readOnly") && o.getBoolean("readOnly")) + if (o.hasField(InternedString::readOnly) && o.getBoolean(InternedString::readOnly)) uasserted(ErrorCodes::BadValue, "js db in read only mode"); auto conn = getConnection(args); @@ -257,10 +257,10 @@ void MongoBase::Functions::insert::call(JSContext* cx, JS::CallArgs args) { ObjectWrapper ele(cx, elementObj); - if (!ele.hasField("_id")) { + if (!ele.hasField(InternedString::_id)) { JS::RootedValue value(cx); scope->getProto<OIDInfo>().newInstance(&value); - ele.setValue("_id", value); + ele.setValue(InternedString::_id, value); } return ValueWriter(cx, value).toBSON(); @@ -589,8 +589,8 @@ void MongoLocalInfo::construct(JSContext* cx, JS::CallArgs args) { JS_SetPrivate(thisv, new std::shared_ptr<DBClientBase>(conn.release())); - o.setBoolean("slaveOk", false); - o.setString("host", "EMBEDDED"); + o.setBoolean(InternedString::slaveOk, false); + o.setString(InternedString::host, "EMBEDDED"); args.rval().setObjectOrNull(thisv); } @@ -622,8 +622,8 @@ void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) { JS_SetPrivate(thisv, new std::shared_ptr<DBClientBase>(conn.release())); - o.setBoolean("slaveOk", false); - o.setString("host", host); + o.setBoolean(InternedString::slaveOk, false); + o.setString(InternedString::host, host); args.rval().setObjectOrNull(thisv); } diff --git a/src/mongo/scripting/mozjs/numberlong.cpp b/src/mongo/scripting/mozjs/numberlong.cpp index 0051d61eb94..68d747e85c3 100644 --- a/src/mongo/scripting/mozjs/numberlong.cpp +++ b/src/mongo/scripting/mozjs/numberlong.cpp @@ -53,12 +53,6 @@ const JSFunctionSpec NumberLongInfo::methods[5] = { const char* const NumberLongInfo::className = "NumberLong"; -namespace { -const char* const kTop = "top"; -const char* const kBottom = "bottom"; -const char* const kFloatApprox = "floatApprox"; -} // namespace - long long NumberLongInfo::ToNumberLong(JSContext* cx, JS::HandleValue thisv) { JS::RootedObject obj(cx, thisv.toObjectOrNull()); return ToNumberLong(cx, obj); @@ -67,18 +61,18 @@ long long NumberLongInfo::ToNumberLong(JSContext* cx, JS::HandleValue thisv) { long long NumberLongInfo::ToNumberLong(JSContext* cx, JS::HandleObject thisv) { ObjectWrapper o(cx, thisv); - if (!o.hasField(kTop)) { - if (!o.hasField(kFloatApprox)) + if (!o.hasField(InternedString::top)) { + if (!o.hasField(InternedString::floatApprox)) uasserted(ErrorCodes::InternalError, "No top and no floatApprox fields"); - return o.getNumberLongLong(kFloatApprox); + return o.getNumberLongLong(InternedString::floatApprox); } - if (!o.hasField(kBottom)) + if (!o.hasField(InternedString::bottom)) uasserted(ErrorCodes::InternalError, "top but no bottom field"); - return ((unsigned long long)((long long)o.getNumberLongLong(kTop) << 32) + - (unsigned)(o.getNumberLongLong(kBottom))); + return ((unsigned long long)((long long)o.getNumberLongLong(InternedString::top) << 32) + + (unsigned)(o.getNumberLongLong(InternedString::bottom))); } void NumberLongInfo::Functions::valueOf::call(JSContext* cx, JS::CallArgs args) { @@ -142,11 +136,11 @@ void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) { JS::RootedValue bottom(cx); if (args.length() == 0) { - o.setNumber(kFloatApprox, 0); + o.setNumber(InternedString::floatApprox, 0); } else if (args.length() == 1) { auto arg = args.get(0); if (arg.isNumber()) { - o.setValue(kFloatApprox, arg); + o.setValue(InternedString::floatApprox, arg); } else { std::string str = ValueWriter(cx, arg).toString(); long long val; @@ -161,11 +155,11 @@ void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) { // values above 2^53 are not accurately represented in JS if (val == INT64_MIN || std::abs(val) >= 9007199254740992LL) { auto val64 = static_cast<unsigned long long>(val); - o.setNumber(kFloatApprox, val); - o.setNumber(kTop, val64 >> 32); - o.setNumber(kBottom, val64 & 0x00000000ffffffff); + o.setNumber(InternedString::floatApprox, val); + o.setNumber(InternedString::top, val64 >> 32); + o.setNumber(InternedString::bottom, val64 & 0x00000000ffffffff); } else { - o.setNumber(kFloatApprox, val); + o.setNumber(InternedString::floatApprox, val); } } } else { @@ -182,9 +176,9 @@ void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) { static_cast<double>(static_cast<uint32_t>(args.get(2).toNumber()))) uasserted(ErrorCodes::BadValue, "bottom must be a 32 bit unsigned number"); - o.setValue(kFloatApprox, args.get(0)); - o.setValue(kTop, args.get(1)); - o.setValue(kBottom, args.get(2)); + o.setValue(InternedString::floatApprox, args.get(0)); + o.setValue(InternedString::top, args.get(1)); + o.setValue(InternedString::bottom, args.get(2)); } args.rval().setObjectOrNull(thisv); diff --git a/src/mongo/scripting/mozjs/objectwrapper.cpp b/src/mongo/scripting/mozjs/objectwrapper.cpp index 014c668e9c7..8e1bc5aa92e 100644 --- a/src/mongo/scripting/mozjs/objectwrapper.cpp +++ b/src/mongo/scripting/mozjs/objectwrapper.cpp @@ -30,6 +30,8 @@ #include "mongo/scripting/mozjs/objectwrapper.h" +#include <js/Conversions.h> + #include "mongo/base/error_codes.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/platform/decimal128.h" @@ -62,6 +64,13 @@ void ObjectWrapper::Key::get(JSContext* cx, JS::HandleObject o, JS::MutableHandl return; break; } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + + if (JS_GetPropertyById(cx, o, id, value)) + return; + break; + } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to get value on a JSObject"); @@ -84,6 +93,13 @@ void ObjectWrapper::Key::set(JSContext* cx, JS::HandleObject o, JS::HandleValue return; break; } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + + if (JS_SetPropertyById(cx, o, id, value)) + return; + break; + } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to set value on a JSObject"); @@ -109,6 +125,13 @@ void ObjectWrapper::Key::define(JSContext* cx, return; break; } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + + if (JS_DefinePropertyById(cx, o, id, value, attrs)) + return; + break; + } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to define value on a JSObject"); @@ -133,6 +156,13 @@ bool ObjectWrapper::Key::has(JSContext* cx, JS::HandleObject o) { return has; break; } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + + if (JS_HasPropertyById(cx, o, id, &has)) + return has; + break; + } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to has value on a JSObject"); @@ -156,6 +186,12 @@ void ObjectWrapper::Key::del(JSContext* cx, JS::HandleObject o) { return; break; } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + + if (JS_DeleteProperty(cx, o, IdWrapper(cx, id).toString().c_str())) + break; + } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to delete value on a JSObject"); @@ -171,6 +207,10 @@ std::string ObjectWrapper::Key::toString(JSContext* cx) { JS::RootedId id(cx, _id); return IdWrapper(cx, id).toString(); } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + return IdWrapper(cx, id).toString(); + } } throwCurrentJSException( @@ -470,9 +510,9 @@ std::string ObjectWrapper::getClassName() { return jsclass->name; JS::RootedValue ctor(_context); - getValue("constructor", &ctor); + getValue(InternedString::constructor, &ctor); - return ObjectWrapper(_context, ctor).getString("name"); + return ObjectWrapper(_context, ctor).getString(InternedString::name); } } // namespace mozjs diff --git a/src/mongo/scripting/mozjs/objectwrapper.h b/src/mongo/scripting/mozjs/objectwrapper.h index 44f1d1ff71e..efc8039bf26 100644 --- a/src/mongo/scripting/mozjs/objectwrapper.h +++ b/src/mongo/scripting/mozjs/objectwrapper.h @@ -34,6 +34,7 @@ #include "mongo/bson/bsonobjbuilder.h" #include "mongo/platform/decimal128.h" #include "mongo/scripting/mozjs/exception.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/lifetimestack.h" namespace mongo { @@ -68,12 +69,14 @@ public: Field, Index, Id, + InternedString, }; public: Key(const char* field) : _field(field), _type(Type::Field) {} Key(uint32_t idx) : _idx(idx), _type(Type::Index) {} Key(JS::HandleId id) : _id(id), _type(Type::Id) {} + Key(InternedString id) : _internedString(id), _type(Type::InternedString) {} private: void get(JSContext* cx, JS::HandleObject o, JS::MutableHandleValue value); @@ -87,6 +90,7 @@ public: const char* _field; uint32_t _idx; jsid _id; + InternedString _internedString; }; Type _type; }; diff --git a/src/mongo/scripting/mozjs/timestamp.cpp b/src/mongo/scripting/mozjs/timestamp.cpp index 8659d8a4d85..198ee3af5b2 100644 --- a/src/mongo/scripting/mozjs/timestamp.cpp +++ b/src/mongo/scripting/mozjs/timestamp.cpp @@ -31,6 +31,7 @@ #include "mongo/scripting/mozjs/timestamp.h" #include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/internedstring.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" @@ -49,8 +50,8 @@ void TimestampInfo::construct(JSContext* cx, JS::CallArgs args) { ObjectWrapper o(cx, thisv); if (args.length() == 0) { - o.setNumber("t", 0); - o.setNumber("i", 0); + o.setNumber(InternedString::t, 0); + o.setNumber(InternedString::i, 0); } else if (args.length() == 2) { if (!args.get(0).isNumber()) uasserted(ErrorCodes::BadValue, "Timestamp time must be a number"); @@ -64,8 +65,8 @@ void TimestampInfo::construct(JSContext* cx, JS::CallArgs args) { str::stream() << "The first argument must be in seconds; " << t << " is too large (max " << largestVal << ")"); - o.setValue("t", args.get(0)); - o.setValue("i", args.get(1)); + o.setValue(InternedString::t, args.get(0)); + o.setValue(InternedString::i, args.get(1)); } else { uasserted(ErrorCodes::BadValue, "Timestamp needs 0 or 2 arguments"); } |