summaryrefslogtreecommitdiff
path: root/src/mongo/scripting
diff options
context:
space:
mode:
authorRaymond Jacobson <raymond.jacobson@10gen.com>2015-07-07 12:06:15 -0400
committerRaymond Jacobson <raymond.jacobson@10gen.com>2015-08-13 11:34:59 -0400
commit9a81b7f1a6a8e0773703f2007c5a7268eb182b91 (patch)
treef94e060e8ce1e9b2eec91b9360ed3fbe6c86463b /src/mongo/scripting
parentadadd67c2bc8260a6ed1ac74d27edb24c4855c13 (diff)
downloadmongo-9a81b7f1a6a8e0773703f2007c5a7268eb182b91.tar.gz
SERVER-19627 Add Decimal128 type support to mongo shell
Diffstat (limited to 'src/mongo/scripting')
-rw-r--r--src/mongo/scripting/SConscript1
-rw-r--r--src/mongo/scripting/engine.cpp6
-rw-r--r--src/mongo/scripting/engine.h1
-rw-r--r--src/mongo/scripting/engine_v8.cpp35
-rw-r--r--src/mongo/scripting/engine_v8.h5
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp11
-rw-r--r--src/mongo/scripting/mozjs/implscope.h7
-rw-r--r--src/mongo/scripting/mozjs/numberdecimal.cpp108
-rw-r--r--src/mongo/scripting/mozjs/numberdecimal.h63
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.cpp8
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.h2
-rw-r--r--src/mongo/scripting/mozjs/proxyscope.cpp7
-rw-r--r--src/mongo/scripting/mozjs/proxyscope.h1
-rw-r--r--src/mongo/scripting/mozjs/valuereader.cpp25
-rw-r--r--src/mongo/scripting/mozjs/valuereader.h1
-rw-r--r--src/mongo/scripting/mozjs/valuewriter.cpp23
-rw-r--r--src/mongo/scripting/mozjs/valuewriter.h1
-rw-r--r--src/mongo/scripting/v8_db.cpp27
-rw-r--r--src/mongo/scripting/v8_db.h5
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);