From 95060c27ed2dddcb6343a88f7aa405ed8a935ad7 Mon Sep 17 00:00:00 2001 From: Jason Carey Date: Tue, 6 Oct 2015 20:08:03 -0400 Subject: 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). --- src/mongo/scripting/SConscript | 1 + src/mongo/scripting/mozjs/bindata.cpp | 7 ++- src/mongo/scripting/mozjs/bson.cpp | 3 +- src/mongo/scripting/mozjs/cursor.cpp | 5 +- src/mongo/scripting/mozjs/db.cpp | 7 ++- src/mongo/scripting/mozjs/dbcollection.cpp | 8 +-- src/mongo/scripting/mozjs/dbpointer.cpp | 5 +- src/mongo/scripting/mozjs/dbquery.cpp | 41 +++++++------ src/mongo/scripting/mozjs/dbref.cpp | 7 ++- src/mongo/scripting/mozjs/implscope.cpp | 1 + src/mongo/scripting/mozjs/implscope.h | 6 ++ src/mongo/scripting/mozjs/internedstring.cpp | 65 ++++++++++++++++++++ src/mongo/scripting/mozjs/internedstring.defs | 49 +++++++++++++++ src/mongo/scripting/mozjs/internedstring.h | 87 +++++++++++++++++++++++++++ src/mongo/scripting/mozjs/jsthread.cpp | 4 +- src/mongo/scripting/mozjs/maxkey.cpp | 15 ++--- src/mongo/scripting/mozjs/minkey.cpp | 15 ++--- src/mongo/scripting/mozjs/mongo.cpp | 14 ++--- src/mongo/scripting/mozjs/numberlong.cpp | 36 +++++------ src/mongo/scripting/mozjs/objectwrapper.cpp | 44 +++++++++++++- src/mongo/scripting/mozjs/objectwrapper.h | 4 ++ src/mongo/scripting/mozjs/timestamp.cpp | 9 +-- 22 files changed, 341 insertions(+), 92 deletions(-) create mode 100644 src/mongo/scripting/mozjs/internedstring.cpp create mode 100644 src/mongo/scripting/mozjs/internedstring.defs create mode 100644 src/mongo/scripting/mozjs/internedstring.h (limited to 'src') 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 #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().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().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().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().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().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 _globalProto; JS::HandleObject _global; std::vector _funcs; + InternedStringTable _internedStrings; std::atomic _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 . + * + * 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 . + * + * 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 +#include + +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(id)]; + } + +private: + std::array(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().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().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().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().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().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().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().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(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(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(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(static_cast(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 + #include "mongo/base/error_codes.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/platform/decimal128.h" @@ -58,6 +60,13 @@ void ObjectWrapper::Key::get(JSContext* cx, JS::HandleObject o, JS::MutableHandl case Type::Id: { JS::RootedId id(cx, _id); + if (JS_GetPropertyById(cx, o, id, value)) + return; + break; + } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + if (JS_GetPropertyById(cx, o, id, value)) return; break; @@ -80,6 +89,13 @@ void ObjectWrapper::Key::set(JSContext* cx, JS::HandleObject o, JS::HandleValue case Type::Id: { JS::RootedId id(cx, _id); + if (JS_SetPropertyById(cx, o, id, value)) + return; + break; + } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + if (JS_SetPropertyById(cx, o, id, value)) return; break; @@ -105,6 +121,13 @@ void ObjectWrapper::Key::define(JSContext* cx, case Type::Id: { JS::RootedId id(cx, _id); + if (JS_DefinePropertyById(cx, o, id, value, attrs)) + return; + break; + } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + if (JS_DefinePropertyById(cx, o, id, value, attrs)) return; break; @@ -129,6 +152,13 @@ bool ObjectWrapper::Key::has(JSContext* cx, JS::HandleObject o) { case Type::Id: { JS::RootedId id(cx, _id); + if (JS_HasPropertyById(cx, o, id, &has)) + return has; + break; + } + case Type::InternedString: { + InternedStringId id(cx, _internedString); + if (JS_HasPropertyById(cx, o, id, &has)) return has; break; @@ -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"); } -- cgit v1.2.1