summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Value.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/Value.cpp')
-rw-r--r--js/src/vm/Value.cpp264
1 files changed, 264 insertions, 0 deletions
diff --git a/js/src/vm/Value.cpp b/js/src/vm/Value.cpp
new file mode 100644
index 0000000000..8fcad7ee83
--- /dev/null
+++ b/js/src/vm/Value.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "js/Value.h"
+
+#include "mozilla/Assertions.h"
+
+#include <inttypes.h>
+
+#include "js/Conversions.h" // JS::NumberToString, JS::MaximumNumberToStringLength
+#include "js/Printer.h" // js::GenericPrinter, js::Fprinter
+#include "vm/BigIntType.h" // JS::BigInt
+#include "vm/JSObject.h" // JSObject
+#include "vm/JSONPrinter.h" // js::JSONPrinter
+#include "vm/StringType.h" // JSString
+#include "vm/SymbolType.h" // JS::Symbol
+
+static const JS::Value JSVAL_NULL =
+ JS::Value::fromTagAndPayload(JSVAL_TAG_NULL, 0);
+static const JS::Value JSVAL_FALSE =
+ JS::Value::fromTagAndPayload(JSVAL_TAG_BOOLEAN, false);
+static const JS::Value JSVAL_TRUE =
+ JS::Value::fromTagAndPayload(JSVAL_TAG_BOOLEAN, true);
+static const JS::Value JSVAL_VOID =
+ JS::Value::fromTagAndPayload(JSVAL_TAG_UNDEFINED, 0);
+static const mozilla::Maybe<JS::Value> JSVAL_NOTHING;
+
+namespace JS {
+
+const HandleValue NullHandleValue =
+ HandleValue::fromMarkedLocation(&JSVAL_NULL);
+const HandleValue UndefinedHandleValue =
+ HandleValue::fromMarkedLocation(&JSVAL_VOID);
+const HandleValue TrueHandleValue =
+ HandleValue::fromMarkedLocation(&JSVAL_TRUE);
+const HandleValue FalseHandleValue =
+ HandleValue::fromMarkedLocation(&JSVAL_FALSE);
+const Handle<mozilla::Maybe<Value>> NothingHandleValue =
+ Handle<mozilla::Maybe<Value>>::fromMarkedLocation(&JSVAL_NOTHING);
+
+} // namespace JS
+
+void js::ReportBadValueTypeAndCrash(const JS::Value& value) {
+ MOZ_CRASH_UNSAFE_PRINTF("JS::Value has illegal type: 0x%" PRIx64,
+ value.asRawBits());
+}
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+void JS::Value::dump() const {
+ js::Fprinter out(stderr);
+ dump(out);
+}
+
+void JS::Value::dump(js::GenericPrinter& out) const {
+ js::JSONPrinter json(out);
+ dump(json);
+ out.put("\n");
+}
+
+void JS::Value::dump(js::JSONPrinter& json) const {
+ json.beginObject();
+ dumpFields(json);
+ json.endObject();
+}
+
+template <typename KnownF, typename UnknownF>
+void WhyMagicToString(JSWhyMagic why, KnownF known, UnknownF unknown) {
+ switch (why) {
+ case JS_ELEMENTS_HOLE:
+ known("JS_ELEMENTS_HOLE");
+ break;
+ case JS_NO_ITER_VALUE:
+ known("JS_NO_ITER_VALUE");
+ break;
+ case JS_GENERATOR_CLOSING:
+ known("JS_GENERATOR_CLOSING");
+ break;
+ case JS_ARG_POISON:
+ known("JS_ARG_POISON");
+ break;
+ case JS_SERIALIZE_NO_NODE:
+ known("JS_SERIALIZE_NO_NODE");
+ break;
+ case JS_IS_CONSTRUCTING:
+ known("JS_IS_CONSTRUCTING");
+ break;
+ case JS_HASH_KEY_EMPTY:
+ known("JS_HASH_KEY_EMPTY");
+ break;
+ case JS_ION_ERROR:
+ known("JS_ION_ERROR");
+ break;
+ case JS_ION_BAILOUT:
+ known("JS_ION_BAILOUT");
+ break;
+ case JS_OPTIMIZED_OUT:
+ known("JS_OPTIMIZED_OUT");
+ break;
+ case JS_UNINITIALIZED_LEXICAL:
+ known("JS_UNINITIALIZED_LEXICAL");
+ break;
+ case JS_MISSING_ARGUMENTS:
+ known("JS_MISSING_ARGUMENTS");
+ break;
+ case JS_GENERIC_MAGIC:
+ known("JS_GENERIC_MAGIC");
+ break;
+ case JS_ERROR_WITHOUT_CAUSE:
+ known("JS_ERROR_WITHOUT_CAUSE");
+ break;
+ default:
+ unknown(int(why));
+ break;
+ }
+}
+
+void JS::Value::dumpFields(js::JSONPrinter& json) const {
+ if (isDouble()) {
+ json.property("type", "double");
+ double d = toDouble();
+
+ if (mozilla::IsNegativeZero(d)) {
+ // Negative zero needs special handling.
+ json.property("value", "-0");
+ } else {
+ // Infinity, -Infinity, NaN are handled by JS::NumberToString.
+ char buf[JS::MaximumNumberToStringLength];
+ JS::NumberToString(d, buf);
+ json.property("value", buf);
+ }
+
+ json.formatProperty("private", "0x%p", toPrivateUnchecked());
+ } else {
+ auto tag = toTag();
+ switch (tag) {
+ case JSVAL_TAG_INT32:
+ json.property("type", "int32");
+ json.property("value", toInt32());
+ break;
+ case JSVAL_TAG_UNDEFINED:
+ json.property("type", "undefined");
+ break;
+ case JSVAL_TAG_NULL:
+ json.property("type", "null");
+ break;
+ case JSVAL_TAG_BOOLEAN:
+ json.property("type", "boolean");
+ json.boolProperty("value", toBoolean());
+ break;
+ case JSVAL_TAG_MAGIC:
+ json.property("type", "magic");
+ WhyMagicToString(
+ whyMagic(), [&](const char* name) { json.property("value", name); },
+ [&](int value) {
+ json.formatProperty("value", "Unknown(%d)", value);
+ });
+ break;
+ case JSVAL_TAG_STRING:
+ json.property("type", "string");
+ toString()->dumpFields(json);
+ break;
+ case JSVAL_TAG_SYMBOL:
+ json.property("type", "symbol");
+ toSymbol()->dumpFields(json);
+ break;
+ case JSVAL_TAG_PRIVATE_GCTHING:
+ json.property("type", "private GCThing");
+ json.formatProperty("address", "0x%p", toGCThing());
+ break;
+ case JSVAL_TAG_BIGINT:
+ json.property("type", "bigint");
+ toBigInt()->dumpFields(json);
+ break;
+# ifdef ENABLE_RECORD_TUPLE
+ case JSVAL_TAG_EXTENDED_PRIMITIVE: {
+ json.property("type", "extended primitive");
+ JSObject* obj = &toExtendedPrimitive();
+ json.property("class", obj->getClass()->name);
+ json.formatProperty("address", "(JSObject*)0x%p", obj);
+ break;
+ }
+# endif
+ case JSVAL_TAG_OBJECT:
+ json.property("type", "object");
+ toObject().dumpFields(json);
+ break;
+ default:
+ json.formatProperty("type", "unknown tag(%08x)", tag);
+ break;
+ }
+ }
+}
+
+void JS::Value::dumpStringContent(js::GenericPrinter& out) const {
+ if (isDouble()) {
+ double d = toDouble();
+
+ if (mozilla::IsNegativeZero(d)) {
+ // Negative zero needs special handling.
+ out.put("-0");
+ } else {
+ // Infinity, -Infinity, NaN are handled by JS::NumberToString.
+ char buf[JS::MaximumNumberToStringLength];
+ JS::NumberToString(d, buf);
+ out.put(buf);
+ }
+
+ out.printf(" / <private @ 0x%p>", toPrivateUnchecked());
+ } else {
+ auto tag = toTag();
+ switch (tag) {
+ case JSVAL_TAG_INT32:
+ out.printf("%d", toInt32());
+ break;
+ case JSVAL_TAG_UNDEFINED:
+ out.put("undefined");
+ break;
+ case JSVAL_TAG_NULL:
+ out.put("null");
+ break;
+ case JSVAL_TAG_BOOLEAN:
+ if (toBoolean()) {
+ out.put("true");
+ } else {
+ out.put("false");
+ }
+ break;
+ case JSVAL_TAG_MAGIC:
+ out.put("<magic ");
+ WhyMagicToString(
+ whyMagic(), [&](const char* name) { out.put(name); },
+ [&](int value) { out.printf("Unknown(%d)", value); });
+ out.put(">");
+ break;
+ case JSVAL_TAG_STRING:
+ toString()->dumpStringContent(out);
+ break;
+ case JSVAL_TAG_SYMBOL:
+ toSymbol()->dumpStringContent(out);
+ break;
+ case JSVAL_TAG_PRIVATE_GCTHING:
+ out.printf("<private GCThing @ 0x%p>", toGCThing());
+ break;
+ case JSVAL_TAG_BIGINT:
+ toBigInt()->dumpStringContent(out);
+ break;
+# ifdef ENABLE_RECORD_TUPLE
+ case JSVAL_TAG_EXTENDED_PRIMITIVE:
+ toExtendedPrimitive().dumpStringContent(out);
+ break;
+# endif
+ case JSVAL_TAG_OBJECT:
+ toObject().dumpStringContent(out);
+ break;
+ default:
+ out.printf("Unknown(%08x)", tag);
+ break;
+ }
+ }
+}
+#endif