diff options
author | Raymond Jacobson <raymond.jacobson@10gen.com> | 2015-07-07 12:06:15 -0400 |
---|---|---|
committer | Raymond Jacobson <raymond.jacobson@10gen.com> | 2015-08-13 11:34:59 -0400 |
commit | 9a81b7f1a6a8e0773703f2007c5a7268eb182b91 (patch) | |
tree | f94e060e8ce1e9b2eec91b9360ed3fbe6c86463b /src/mongo/scripting | |
parent | adadd67c2bc8260a6ed1ac74d27edb24c4855c13 (diff) | |
download | mongo-9a81b7f1a6a8e0773703f2007c5a7268eb182b91.tar.gz |
SERVER-19627 Add Decimal128 type support to mongo shell
Diffstat (limited to 'src/mongo/scripting')
-rw-r--r-- | src/mongo/scripting/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/scripting/engine.cpp | 6 | ||||
-rw-r--r-- | src/mongo/scripting/engine.h | 1 | ||||
-rw-r--r-- | src/mongo/scripting/engine_v8.cpp | 35 | ||||
-rw-r--r-- | src/mongo/scripting/engine_v8.h | 5 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.cpp | 11 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.h | 7 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/numberdecimal.cpp | 108 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/numberdecimal.h | 63 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/objectwrapper.cpp | 8 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/objectwrapper.h | 2 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/proxyscope.cpp | 7 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/proxyscope.h | 1 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/valuereader.cpp | 25 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/valuereader.h | 1 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/valuewriter.cpp | 23 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/valuewriter.h | 1 | ||||
-rw-r--r-- | src/mongo/scripting/v8_db.cpp | 27 | ||||
-rw-r--r-- | src/mongo/scripting/v8_db.h | 5 |
19 files changed, 336 insertions, 1 deletions
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript index 25dd86201af..ec55b823568 100644 --- a/src/mongo/scripting/SConscript +++ b/src/mongo/scripting/SConscript @@ -138,6 +138,7 @@ elif usemozjs: 'mozjs/nativefunction.cpp', 'mozjs/numberint.cpp', 'mozjs/numberlong.cpp', + 'mozjs/numberdecimal.cpp', 'mozjs/object.cpp', 'mozjs/objectwrapper.cpp', 'mozjs/oid.cpp', diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp index 39e43acae0b..56288b24cef 100644 --- a/src/mongo/scripting/engine.cpp +++ b/src/mongo/scripting/engine.cpp @@ -87,6 +87,9 @@ void Scope::append(BSONObjBuilder& builder, const char* fieldName, const char* s case NumberLong: builder.append(fieldName, getNumberLongLong(scopeName)); break; + case NumberDecimal: + builder.append(fieldName, getNumberDecimal(scopeName)); + break; case String: builder.append(fieldName, getString(scopeName)); break; @@ -430,6 +433,9 @@ public: double getNumber(const char* field) { return _real->getNumber(field); } + Decimal128 getNumberDecimal(const char* field) { + return _real->getNumberDecimal(field); + } string getString(const char* field) { return _real->getString(field); } diff --git a/src/mongo/scripting/engine.h b/src/mongo/scripting/engine.h index f020aba45c2..8f096dce725 100644 --- a/src/mongo/scripting/engine.h +++ b/src/mongo/scripting/engine.h @@ -80,6 +80,7 @@ public: virtual long long getNumberLongLong(const char* field) { return static_cast<long long>(getNumber(field)); } + virtual Decimal128 getNumberDecimal(const char* field) = 0; virtual void setElement(const char* field, const BSONElement& e) = 0; virtual void setNumber(const char* field, double val) = 0; diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp index 0ef8ebc1ec6..b2ee26f9fcc 100644 --- a/src/mongo/scripting/engine_v8.cpp +++ b/src/mongo/scripting/engine_v8.cpp @@ -36,8 +36,9 @@ #include <iostream> #include "mongo/base/init.h" -#include "mongo/db/service_context.h" #include "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" +#include "mongo/platform/decimal128.h" #include "mongo/platform/unordered_set.h" #include "mongo/scripting/v8_db.h" #include "mongo/scripting/v8_utils.h" @@ -743,6 +744,11 @@ long long V8Scope::getNumberLongLong(const char* field) { return get(field)->ToInteger()->Value(); } +Decimal128 V8Scope::getNumberDecimal(const char* field) { + V8_SIMPLE_HEADER + return Decimal128(toSTLString(get(field)->ToString())); +} + string V8Scope::getString(const char* field) { V8_SIMPLE_HEADER return toSTLString(get(field)); @@ -780,6 +786,13 @@ v8::Handle<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) { return numberInt; } +v8::Handle<v8::FunctionTemplate> getNumberDecimalFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> numberDecimal = scope->createV8Function(numberDecimalInit); + v8::Handle<v8::ObjectTemplate> proto = numberDecimal->PrototypeTemplate(); + scope->injectV8Method("toString", numberDecimalToString, proto); + return numberDecimal; +} + v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) { v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit); binData->InstanceTemplate()->SetInternalFieldCount(1); @@ -1300,6 +1313,7 @@ void V8Scope::installBSONTypes() { _BinDataFT = FTPtr::New(getBinDataFunctionTemplate(this)); _NumberLongFT = FTPtr::New(getNumberLongFunctionTemplate(this)); _NumberIntFT = FTPtr::New(getNumberIntFunctionTemplate(this)); + _NumberDecimalFT = FTPtr::New(getNumberDecimalFunctionTemplate(this)); _TimestampFT = FTPtr::New(getTimestampFunctionTemplate(this)); _MinKeyFT = FTPtr::New(getMinKeyFunctionTemplate(this)); _MaxKeyFT = FTPtr::New(getMaxKeyFunctionTemplate(this)); @@ -1307,6 +1321,9 @@ void V8Scope::installBSONTypes() { injectV8Function("BinData", BinDataFT(), _global); injectV8Function("NumberLong", NumberLongFT(), _global); injectV8Function("NumberInt", NumberIntFT(), _global); + if (experimentalDecimalSupport) { + injectV8Function("NumberDecimal", NumberDecimalFT(), _global); + } injectV8Function("Timestamp", TimestampFT(), _global); // These are instances created from the functions, not the functions themselves @@ -1404,6 +1421,8 @@ v8::Handle<v8::Value> V8Scope::mongoToV8Element(const BSONElement& elem, bool re v8::Handle<v8::Value> argv[3]; // arguments for v8 instance constructors v8::Local<v8::Object> instance; // instance of v8 type uint64_t nativeUnsignedLong; // native representation of NumberLong + Decimal128 nativeDecimal; // native representation of NumberDecimal + switch (elem.type()) { case mongo::Code: @@ -1501,6 +1520,17 @@ v8::Handle<v8::Value> V8Scope::mongoToV8Element(const BSONElement& elem, bool re v8::Integer::New((unsigned long)(nativeUnsignedLong & 0x00000000ffffffff)); return NumberLongFT()->GetFunction()->NewInstance(3, argv); } + case mongo::NumberDecimal: { + nativeDecimal = elem.numberDecimal(); + // Store number decimals as strings + // Note: This prevents shell arithmetic, which is performed for number longs + // by converting them to doubles, which is imprecise. Until there is a better + // method to handle non-double shell arithmetic, decimals will remain + // as a non-numeric js type. + std::string decString = nativeDecimal.toString(); + argv[0] = v8::String::New(decString.c_str()); + return NumberDecimalFT()->GetFunction()->NewInstance(1, argv); + } case mongo::MinKey: return MinKeyFT()->GetFunction()->NewInstance(); case mongo::MaxKey: @@ -1598,6 +1628,8 @@ void V8Scope::v8ToMongoObject(BSONObjBuilder& b, b.append(elementName, numberLongVal(this, obj)); } else if (NumberIntFT()->HasInstance(value)) { b.append(elementName, numberIntVal(this, obj)); + } else if (NumberDecimalFT()->HasInstance(value)) { + b.append(elementName, numberDecimalVal(this, obj)); } else if (DBPointerFT()->HasInstance(value)) { v8ToMongoDBRef(b, elementName, obj); } else if (BinDataFT()->HasInstance(value)) { @@ -1707,6 +1739,7 @@ BSONObj V8Scope::v8ToMongo(v8::Handle<v8::Object> o, int depth) { } v8::Local<v8::Array> names = o->GetOwnPropertyNames(); + for (unsigned int i = 0; i < names->Length(); i++) { v8::Local<v8::String> name = names->Get(i)->ToString(); diff --git a/src/mongo/scripting/engine_v8.h b/src/mongo/scripting/engine_v8.h index d70439c45ff..2cf5b218e3c 100644 --- a/src/mongo/scripting/engine_v8.h +++ b/src/mongo/scripting/engine_v8.h @@ -208,6 +208,7 @@ public: virtual double getNumber(const char* field); virtual int getNumberInt(const char* field); virtual long long getNumberLongLong(const char* field); + virtual Decimal128 getNumberDecimal(const char* field); virtual std::string getString(const char* field); virtual bool getBoolean(const char* field); virtual BSONObj getObject(const char* field); @@ -366,6 +367,9 @@ public: v8::Handle<v8::FunctionTemplate> NumberIntFT() const { return _NumberIntFT; } + v8::Handle<v8::FunctionTemplate> NumberDecimalFT() const { + return _NumberDecimalFT; + } v8::Handle<v8::FunctionTemplate> TimestampFT() const { return _TimestampFT; } @@ -492,6 +496,7 @@ private: v8::Persistent<v8::FunctionTemplate> _BinDataFT; v8::Persistent<v8::FunctionTemplate> _NumberLongFT; v8::Persistent<v8::FunctionTemplate> _NumberIntFT; + v8::Persistent<v8::FunctionTemplate> _NumberDecimalFT; v8::Persistent<v8::FunctionTemplate> _TimestampFT; v8::Persistent<v8::FunctionTemplate> _MinKeyFT; v8::Persistent<v8::FunctionTemplate> _MaxKeyFT; diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp index 398eb70c018..e71137d005a 100644 --- a/src/mongo/scripting/mozjs/implscope.cpp +++ b/src/mongo/scripting/mozjs/implscope.cpp @@ -37,6 +37,7 @@ #include "mongo/base/error_codes.h" #include "mongo/db/operation_context.h" +#include "mongo/platform/decimal128.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" @@ -246,6 +247,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine) _nativeFunctionProto(_context), _numberIntProto(_context), _numberLongProto(_context), + _numberDecimalProto(_context), _objectProto(_context), _oidProto(_context), _regExpProto(_context), @@ -358,6 +360,12 @@ long long MozJSImplScope::getNumberLongLong(const char* field) { return ObjectWrapper(_context, _global).getNumberLongLong(field); } +Decimal128 MozJSImplScope::getNumberDecimal(const char* field) { + MozJSEntry entry(this); + + return ObjectWrapper(_context, _global).getNumberDecimal(field); +} + std::string MozJSImplScope::getString(const char* field) { MozJSEntry entry(this); @@ -661,6 +669,9 @@ void MozJSImplScope::installBSONTypes() { _nativeFunctionProto.install(_global); _numberIntProto.install(_global); _numberLongProto.install(_global); + if (experimentalDecimalSupport) { + _numberDecimalProto.install(_global); + } _objectProto.install(_global); _oidProto.install(_global); _regExpProto.install(_global); diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h index b9a3e53c469..0c813e03960 100644 --- a/src/mongo/scripting/mozjs/implscope.h +++ b/src/mongo/scripting/mozjs/implscope.h @@ -51,6 +51,7 @@ #include "mongo/scripting/mozjs/nativefunction.h" #include "mongo/scripting/mozjs/numberint.h" #include "mongo/scripting/mozjs/numberlong.h" +#include "mongo/scripting/mozjs/numberdecimal.h" #include "mongo/scripting/mozjs/object.h" #include "mongo/scripting/mozjs/oid.h" #include "mongo/scripting/mozjs/regexp.h" @@ -105,6 +106,7 @@ public: double getNumber(const char* field) override; int getNumberInt(const char* field) override; long long getNumberLongLong(const char* field) override; + Decimal128 getNumberDecimal(const char* field) override; std::string getString(const char* field) override; bool getBoolean(const char* field) override; BSONObj getObject(const char* field) override; @@ -220,6 +222,10 @@ public: return _numberLongProto; } + WrapType<NumberDecimalInfo>& getNumberDecimalProto() { + return _numberDecimalProto; + } + WrapType<ObjectInfo>& getObjectProto() { return _objectProto; } @@ -326,6 +332,7 @@ private: WrapType<NativeFunctionInfo> _nativeFunctionProto; WrapType<NumberIntInfo> _numberIntProto; WrapType<NumberLongInfo> _numberLongProto; + WrapType<NumberDecimalInfo> _numberDecimalProto; WrapType<ObjectInfo> _objectProto; WrapType<OIDInfo> _oidProto; WrapType<RegExpInfo> _regExpProto; diff --git a/src/mongo/scripting/mozjs/numberdecimal.cpp b/src/mongo/scripting/mozjs/numberdecimal.cpp new file mode 100644 index 00000000000..1891a67a45b --- /dev/null +++ b/src/mongo/scripting/mozjs/numberdecimal.cpp @@ -0,0 +1,108 @@ +/** + * 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/numberdecimal.h" + +#include "mongo/platform/decimal128.h" +#include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/objectwrapper.h" +#include "mongo/scripting/mozjs/valuereader.h" +#include "mongo/scripting/mozjs/valuewriter.h" +#include "mongo/util/mongoutils/str.h" +#include "mongo/util/text.h" + +namespace mongo { +namespace mozjs { + +const JSFunctionSpec NumberDecimalInfo::methods[2] = { + MONGO_ATTACH_JS_FUNCTION(toString), JS_FS_END, +}; + +const char* const NumberDecimalInfo::className = "NumberDecimal"; + +void NumberDecimalInfo::finalize(JSFreeOp* fop, JSObject* obj) { + auto x = static_cast<Decimal128*>(JS_GetPrivate(obj)); + + if (x) + delete x; +} + +Decimal128 NumberDecimalInfo::ToNumberDecimal(JSContext* cx, JS::HandleValue thisv) { + auto x = static_cast<Decimal128*>(JS_GetPrivate(thisv.toObjectOrNull())); + + return x ? *x : Decimal128(0); +} + +Decimal128 NumberDecimalInfo::ToNumberDecimal(JSContext* cx, JS::HandleObject thisv) { + auto x = static_cast<Decimal128*>(JS_GetPrivate(thisv)); + + return x ? *x : Decimal128(0); +} + +void NumberDecimalInfo::Functions::toString(JSContext* cx, JS::CallArgs args) { + Decimal128 val = NumberDecimalInfo::ToNumberDecimal(cx, args.thisv()); + + str::stream ss; + ss << "NumberDecimal(\"" << val.toString() << "\")"; + + ValueReader(cx, args.rval()).fromStringData(ss.operator std::string()); +} + +void NumberDecimalInfo::construct(JSContext* cx, JS::CallArgs args) { + auto scope = getScope(cx); + + JS::RootedObject thisv(cx); + + scope->getNumberDecimalProto().newObject(&thisv); + + Decimal128 x(0); + + if (args.length() == 0) { + // Do nothing + } else if (args.length() == 1) { + x = ValueWriter(cx, args.get(0)).toDecimal128(); + } else { + uasserted(ErrorCodes::BadValue, "NumberDecimal takes 0 or 1 arguments"); + } + + JS_SetPrivate(thisv, new Decimal128(x)); + + args.rval().setObjectOrNull(thisv); +} + +void NumberDecimalInfo::make(JSContext* cx, JS::MutableHandleValue thisv, Decimal128 decimal) { + auto scope = getScope(cx); + + scope->getNumberDecimalProto().newInstance(thisv); + JS_SetPrivate(thisv.toObjectOrNull(), new Decimal128(decimal)); +} + +} // namespace mozjs +} // namespace mongo diff --git a/src/mongo/scripting/mozjs/numberdecimal.h b/src/mongo/scripting/mozjs/numberdecimal.h new file mode 100644 index 00000000000..76411b45ca4 --- /dev/null +++ b/src/mongo/scripting/mozjs/numberdecimal.h @@ -0,0 +1,63 @@ +/** + * 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 "mongo/platform/decimal128.h" +#include "mongo/scripting/mozjs/wraptype.h" + +namespace mongo { +namespace mozjs { + +/** + * The "NumberDecimal" Javascript object. + * + * Wraps a 'Decimal128' as its private member + */ + +struct NumberDecimalInfo : public BaseInfo { + static void construct(JSContext* cx, JS::CallArgs args); + static void finalize(JSFreeOp* fop, JSObject* obj); + + struct Functions { + MONGO_DEFINE_JS_FUNCTION(toString); + }; + + static const JSFunctionSpec methods[2]; + + static const char* const className; + static const unsigned classFlags = JSCLASS_HAS_PRIVATE; + + static Decimal128 ToNumberDecimal(JSContext* cx, JS::HandleObject object); + static Decimal128 ToNumberDecimal(JSContext* cx, JS::HandleValue value); + + static void make(JSContext* cx, JS::MutableHandleValue value, Decimal128 d); +}; + +} // namespace mozjs +} // namespace mongo
\ No newline at end of file diff --git a/src/mongo/scripting/mozjs/objectwrapper.cpp b/src/mongo/scripting/mozjs/objectwrapper.cpp index 5052bf50d4a..5ca5cdeca97 100644 --- a/src/mongo/scripting/mozjs/objectwrapper.cpp +++ b/src/mongo/scripting/mozjs/objectwrapper.cpp @@ -32,6 +32,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/bsonobjbuilder.h" +#include "mongo/platform/decimal128.h" #include "mongo/scripting/mozjs/idwrapper.h" #include "mongo/scripting/mozjs/implscope.h" #include "mongo/scripting/mozjs/valuereader.h" @@ -199,6 +200,13 @@ long long ObjectWrapper::getNumberLongLong(Key key) { return ValueWriter(_context, x).toInt64(); } +Decimal128 ObjectWrapper::getNumberDecimal(Key key) { + JS::RootedValue x(_context); + getValue(key, &x); + + return ValueWriter(_context, x).toDecimal128(); +} + std::string ObjectWrapper::getString(Key key) { JS::RootedValue x(_context); getValue(key, &x); diff --git a/src/mongo/scripting/mozjs/objectwrapper.h b/src/mongo/scripting/mozjs/objectwrapper.h index efac05a1e71..d8d519acc7f 100644 --- a/src/mongo/scripting/mozjs/objectwrapper.h +++ b/src/mongo/scripting/mozjs/objectwrapper.h @@ -31,6 +31,7 @@ #include <jsapi.h> #include <string> +#include "mongo/platform/decimal128.h" #include "mongo/scripting/mozjs/exception.h" namespace mongo { @@ -95,6 +96,7 @@ public: double getNumber(Key key); int getNumberInt(Key key); long long getNumberLongLong(Key key); + Decimal128 getNumberDecimal(Key key); std::string getString(Key key); bool getBoolean(Key key); BSONObj getObject(Key key); diff --git a/src/mongo/scripting/mozjs/proxyscope.cpp b/src/mongo/scripting/mozjs/proxyscope.cpp index b7c4e490185..833e083001a 100644 --- a/src/mongo/scripting/mozjs/proxyscope.cpp +++ b/src/mongo/scripting/mozjs/proxyscope.cpp @@ -33,6 +33,7 @@ #include "mongo/db/client.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" +#include "mongo/platform/decimal128.h" #include "mongo/scripting/mozjs/implscope.h" #include "mongo/util/quick_exit.h" @@ -126,6 +127,12 @@ long long MozJSProxyScope::getNumberLongLong(const char* field) { return out; } +Decimal128 MozJSProxyScope::getNumberDecimal(const char* field) { + Decimal128 out; + runOnImplThread([&] { out = _implScope->getNumberDecimal(field); }); + return out; +} + std::string MozJSProxyScope::getString(const char* field) { std::string out; runOnImplThread([&] { out = _implScope->getString(field); }); diff --git a/src/mongo/scripting/mozjs/proxyscope.h b/src/mongo/scripting/mozjs/proxyscope.h index 8b874360a68..3d472a5a05e 100644 --- a/src/mongo/scripting/mozjs/proxyscope.h +++ b/src/mongo/scripting/mozjs/proxyscope.h @@ -128,6 +128,7 @@ public: double getNumber(const char* field) override; int getNumberInt(const char* field) override; long long getNumberLongLong(const char* field) override; + Decimal128 getNumberDecimal(const char* field) override; std::string getString(const char* field) override; bool getBoolean(const char* field) override; BSONObj getObject(const char* field) override; diff --git a/src/mongo/scripting/mozjs/valuereader.cpp b/src/mongo/scripting/mozjs/valuereader.cpp index ad45fafc06e..e4d005cb399 100644 --- a/src/mongo/scripting/mozjs/valuereader.cpp +++ b/src/mongo/scripting/mozjs/valuereader.cpp @@ -36,6 +36,7 @@ #include <js/CharacterEncoding.h> #include "mongo/base/error_codes.h" +#include "mongo/platform/decimal128.h" #include "mongo/scripting/mozjs/implscope.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/util/base64.h" @@ -174,6 +175,17 @@ void ValueReader::fromBSONElement(const BSONElement& elem, bool readOnly) { return; } + case mongo::NumberDecimal: { + Decimal128 decimal = elem.numberDecimal(); + JS::AutoValueArray<1> args(_context); + ValueReader(_context, args[0]).fromDecimal128(decimal); + JS::RootedObject obj(_context); + + scope->getNumberDecimalProto().newInstance(args, &obj); + _value.setObjectOrNull(obj); + + return; + } case mongo::MinKey: scope->getMinKeyProto().newInstance(_value); return; @@ -268,5 +280,18 @@ void ValueReader::fromStringData(StringData sd) { _value.setString(jsStr); } +/** + * SpiderMonkey doesn't have a direct entry point to create a Decimal128 + * + * Read NumberDecimal as a string + * Note: This prevents shell arithmetic, which is performed for number longs + * by converting them to doubles, which is imprecise. Until there is a better + * method to handle non-double shell arithmetic, decimals will remain + * as a non-numeric js type. + */ +void ValueReader::fromDecimal128(Decimal128 decimal) { + NumberDecimalInfo::make(_context, _value, decimal); +} + } // namespace mozjs } // namespace mongo diff --git a/src/mongo/scripting/mozjs/valuereader.h b/src/mongo/scripting/mozjs/valuereader.h index be916ccbc25..e96ede814f1 100644 --- a/src/mongo/scripting/mozjs/valuereader.h +++ b/src/mongo/scripting/mozjs/valuereader.h @@ -50,6 +50,7 @@ public: void fromBSONElement(const BSONElement& elem, bool readOnly); void fromBSON(const BSONObj& obj, bool readOnly); void fromStringData(StringData sd); + void fromDecimal128(Decimal128 decimal); private: JSContext* _context; diff --git a/src/mongo/scripting/mozjs/valuewriter.cpp b/src/mongo/scripting/mozjs/valuewriter.cpp index 0efeca6f8dd..72a44583a5b 100644 --- a/src/mongo/scripting/mozjs/valuewriter.cpp +++ b/src/mongo/scripting/mozjs/valuewriter.cpp @@ -139,6 +139,27 @@ int64_t ValueWriter::toInt64() { throwCurrentJSException(_context, ErrorCodes::BadValue, "Failure to convert value to number"); } +Decimal128 ValueWriter::toDecimal128() { + if (_value.isNumber()) { + return Decimal128(toNumber()); + } + + if (getScope(_context)->getNumberIntProto().instanceOf(_value)) + return Decimal128(NumberIntInfo::ToNumberInt(_context, _value)); + + if (getScope(_context)->getNumberLongProto().instanceOf(_value)) + return Decimal128(NumberLongInfo::ToNumberLong(_context, _value)); + + if (getScope(_context)->getNumberDecimalProto().instanceOf(_value)) + return NumberDecimalInfo::ToNumberDecimal(_context, _value); + + if (_value.isString()) { + return Decimal128(toString()); + } + + uasserted(ErrorCodes::BadValue, str::stream() << "Unable to write Decimal128 value."); +} + void ValueWriter::writeThis(BSONObjBuilder* b, StringData sd) { uassert(17279, str::stream() << "Exceeded depth limit of " << 150 @@ -218,6 +239,8 @@ void ValueWriter::_writeObject(BSONObjBuilder* b, StringData sd, JS::HandleObjec b->append(sd, out); } else if (scope->getNumberIntProto().instanceOf(obj)) { b->append(sd, NumberIntInfo::ToNumberInt(_context, obj)); + } else if (scope->getNumberDecimalProto().instanceOf(obj)) { + b->append(sd, NumberDecimalInfo::ToNumberDecimal(_context, obj)); } else if (scope->getDbPointerProto().instanceOf(obj)) { JS::RootedValue id(_context); o.getValue("id", &id); diff --git a/src/mongo/scripting/mozjs/valuewriter.h b/src/mongo/scripting/mozjs/valuewriter.h index 47358398f81..dd0eb98af92 100644 --- a/src/mongo/scripting/mozjs/valuewriter.h +++ b/src/mongo/scripting/mozjs/valuewriter.h @@ -59,6 +59,7 @@ public: double toNumber(); int32_t toInt32(); int64_t toInt64(); + Decimal128 toDecimal128(); bool toBoolean(); /** diff --git a/src/mongo/scripting/v8_db.cpp b/src/mongo/scripting/v8_db.cpp index 4e5fc1d6811..d0f533d316b 100644 --- a/src/mongo/scripting/v8_db.cpp +++ b/src/mongo/scripting/v8_db.cpp @@ -1131,6 +1131,33 @@ v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Arguments& arg return v8::String::New(ret.c_str()); } +Decimal128 numberDecimalVal(V8Scope* scope, const v8::Handle<v8::Object>& it) { + verify(scope->NumberDecimalFT()->HasInstance(it)); + return Decimal128(toSTLString(it->Get(v8::String::New("val")))); +} + +v8::Handle<v8::Value> numberDecimalInit(V8Scope* scope, const v8::Arguments& args) { + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->NumberDecimalFT()->GetFunction(); + return newInstance(f, args); + } + + v8::Handle<v8::Object> it = args.This(); + verify(scope->NumberDecimalFT()->HasInstance(it)); + argumentCheck(args.Length() == 1, "NumberDecimal needs 1 argument"); + argumentCheck(args[0]->IsString(), "NumberDecimal 1st parameter must be a string"); + + it->ForceSet(scope->v8StringData("val"), args[0]); + return it; +} + +v8::Handle<v8::Value> numberDecimalToString(V8Scope* scope, const v8::Arguments& args) { + v8::Handle<v8::Object> it = args.This(); + Decimal128 val = numberDecimalVal(scope, it); + string ret = str::stream() << "NumberDecimal(\"" << val.toString() << "\")"; + return v8::String::New(ret.c_str()); +} + v8::Handle<v8::Value> v8ObjectInvalidForStorage(V8Scope* scope, const v8::Arguments& args) { argumentCheck(args.Length() == 1, "invalidForStorage needs 1 argument") if (args[0]->IsNull()) { return v8::Null(); diff --git a/src/mongo/scripting/v8_db.h b/src/mongo/scripting/v8_db.h index 24d657fb4a5..8bd965d9182 100644 --- a/src/mongo/scripting/v8_db.h +++ b/src/mongo/scripting/v8_db.h @@ -86,6 +86,11 @@ v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Arguments& arg v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Arguments& args); v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Arguments& args); +// NumberDecimal object +Decimal128 numberDecimalVal(V8Scope* scope, const v8::Handle<v8::Object>& it); +v8::Handle<v8::Value> numberDecimalInit(V8Scope* scope, const v8::Arguments& args); +v8::Handle<v8::Value> numberDecimalToString(V8Scope* scope, const v8::Arguments& args); + // DBQuery object v8::Handle<v8::Value> dbQueryInit(V8Scope* scope, const v8::Arguments& args); v8::Handle<v8::Value> dbQueryIndexAccess(::uint32_t index, const v8::AccessorInfo& info); |