Value.cpp (7484B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "js/Value.h" 8 9 #include "mozilla/Assertions.h" 10 11 #include <inttypes.h> 12 13 #include "gc/Cell.h" // js::gc::Cell 14 #include "js/Conversions.h" // JS::NumberToString, JS::MaximumNumberToStringLength 15 #include "js/Printer.h" // js::GenericPrinter, js::Fprinter 16 #include "vm/BigIntType.h" // JS::BigInt 17 #include "vm/JSObject.h" // JSObject 18 #include "vm/JSONPrinter.h" // js::JSONPrinter 19 #include "vm/StringType.h" // JSString 20 #include "vm/SymbolType.h" // JS::Symbol 21 22 static const JS::Value JSVAL_NULL = 23 JS::Value::fromTagAndPayload(JSVAL_TAG_NULL, 0); 24 static const JS::Value JSVAL_FALSE = 25 JS::Value::fromTagAndPayload(JSVAL_TAG_BOOLEAN, false); 26 static const JS::Value JSVAL_TRUE = 27 JS::Value::fromTagAndPayload(JSVAL_TAG_BOOLEAN, true); 28 static const JS::Value JSVAL_VOID = 29 JS::Value::fromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); 30 static const mozilla::Maybe<JS::Value> JSVAL_NOTHING; 31 32 namespace JS { 33 34 const HandleValue NullHandleValue = 35 HandleValue::fromMarkedLocation(&JSVAL_NULL); 36 const HandleValue UndefinedHandleValue = 37 HandleValue::fromMarkedLocation(&JSVAL_VOID); 38 const HandleValue TrueHandleValue = 39 HandleValue::fromMarkedLocation(&JSVAL_TRUE); 40 const HandleValue FalseHandleValue = 41 HandleValue::fromMarkedLocation(&JSVAL_FALSE); 42 const Handle<mozilla::Maybe<Value>> NothingHandleValue = 43 Handle<mozilla::Maybe<Value>>::fromMarkedLocation(&JSVAL_NOTHING); 44 45 #ifdef DEBUG 46 void JS::Value::assertTraceKindMatches(js::gc::Cell* cell) const { 47 MOZ_ASSERT(traceKind() == cell->getTraceKind()); 48 } 49 #endif 50 51 } // namespace JS 52 53 void js::ReportBadValueTypeAndCrash(const JS::Value& value) { 54 MOZ_CRASH_UNSAFE_PRINTF("JS::Value has illegal type: 0x%" PRIx64, 55 value.asRawBits()); 56 } 57 58 #if defined(DEBUG) || defined(JS_JITSPEW) 59 void JS::Value::dump() const { 60 js::Fprinter out(stderr); 61 dump(out); 62 } 63 64 void JS::Value::dump(js::GenericPrinter& out) const { 65 js::JSONPrinter json(out); 66 dump(json); 67 out.put("\n"); 68 } 69 70 void JS::Value::dump(js::JSONPrinter& json) const { 71 json.beginObject(); 72 dumpFields(json); 73 json.endObject(); 74 } 75 76 template <typename KnownF, typename UnknownF> 77 void WhyMagicToString(JSWhyMagic why, KnownF known, UnknownF unknown) { 78 switch (why) { 79 case JS_ELEMENTS_HOLE: 80 known("JS_ELEMENTS_HOLE"); 81 break; 82 case JS_NO_ITER_VALUE: 83 known("JS_NO_ITER_VALUE"); 84 break; 85 case JS_GENERATOR_CLOSING: 86 known("JS_GENERATOR_CLOSING"); 87 break; 88 case JS_ARG_POISON: 89 known("JS_ARG_POISON"); 90 break; 91 case JS_SERIALIZE_NO_NODE: 92 known("JS_SERIALIZE_NO_NODE"); 93 break; 94 case JS_IS_CONSTRUCTING: 95 known("JS_IS_CONSTRUCTING"); 96 break; 97 case JS_HASH_KEY_EMPTY: 98 known("JS_HASH_KEY_EMPTY"); 99 break; 100 case JS_ION_ERROR: 101 known("JS_ION_ERROR"); 102 break; 103 case JS_ION_BAILOUT: 104 known("JS_ION_BAILOUT"); 105 break; 106 case JS_OPTIMIZED_OUT: 107 known("JS_OPTIMIZED_OUT"); 108 break; 109 case JS_UNINITIALIZED_LEXICAL: 110 known("JS_UNINITIALIZED_LEXICAL"); 111 break; 112 case JS_MISSING_ARGUMENTS: 113 known("JS_MISSING_ARGUMENTS"); 114 break; 115 case JS_GENERIC_MAGIC: 116 known("JS_GENERIC_MAGIC"); 117 break; 118 case JS_ERROR_WITHOUT_CAUSE: 119 known("JS_ERROR_WITHOUT_CAUSE"); 120 break; 121 default: 122 unknown(int(why)); 123 break; 124 } 125 } 126 127 void JS::Value::dumpFields(js::JSONPrinter& json) const { 128 if (isDouble()) { 129 json.property("type", "double"); 130 double d = toDouble(); 131 132 if (mozilla::IsNegativeZero(d)) { 133 // Negative zero needs special handling. 134 json.property("value", "-0"); 135 } else { 136 // Infinity, -Infinity, NaN are handled by JS::NumberToString. 137 char buf[JS::MaximumNumberToStringLength]; 138 JS::NumberToString(d, buf); 139 json.property("value", buf); 140 } 141 142 json.formatProperty("private", "0x%p", toPrivateUnchecked()); 143 } else { 144 auto tag = toTag(); 145 switch (tag) { 146 case JSVAL_TAG_INT32: 147 json.property("type", "int32"); 148 json.property("value", toInt32()); 149 break; 150 case JSVAL_TAG_UNDEFINED: 151 json.property("type", "undefined"); 152 break; 153 case JSVAL_TAG_NULL: 154 json.property("type", "null"); 155 break; 156 case JSVAL_TAG_BOOLEAN: 157 json.property("type", "boolean"); 158 json.boolProperty("value", toBoolean()); 159 break; 160 case JSVAL_TAG_MAGIC: 161 json.property("type", "magic"); 162 WhyMagicToString( 163 whyMagic(), [&](const char* name) { json.property("value", name); }, 164 [&](int value) { 165 json.formatProperty("value", "Unknown(%d)", value); 166 }); 167 break; 168 case JSVAL_TAG_STRING: 169 json.property("type", "string"); 170 toString()->dumpFields(json); 171 break; 172 case JSVAL_TAG_SYMBOL: 173 json.property("type", "symbol"); 174 toSymbol()->dumpFields(json); 175 break; 176 case JSVAL_TAG_PRIVATE_GCTHING: 177 json.property("type", "private GCThing"); 178 json.formatProperty("address", "0x%p", toGCThing()); 179 break; 180 case JSVAL_TAG_BIGINT: 181 json.property("type", "bigint"); 182 toBigInt()->dumpFields(json); 183 break; 184 case JSVAL_TAG_OBJECT: 185 json.property("type", "object"); 186 toObject().dumpFields(json); 187 break; 188 default: 189 json.formatProperty("type", "unknown tag(%08x)", tag); 190 break; 191 } 192 } 193 } 194 195 void JS::Value::dumpStringContent(js::GenericPrinter& out) const { 196 if (isDouble()) { 197 double d = toDouble(); 198 199 if (mozilla::IsNegativeZero(d)) { 200 // Negative zero needs special handling. 201 out.put("-0"); 202 } else { 203 // Infinity, -Infinity, NaN are handled by JS::NumberToString. 204 char buf[JS::MaximumNumberToStringLength]; 205 JS::NumberToString(d, buf); 206 out.put(buf); 207 } 208 209 out.printf(" / <private @ 0x%p>", toPrivateUnchecked()); 210 } else { 211 auto tag = toTag(); 212 switch (tag) { 213 case JSVAL_TAG_INT32: 214 out.printf("%d", toInt32()); 215 break; 216 case JSVAL_TAG_UNDEFINED: 217 out.put("undefined"); 218 break; 219 case JSVAL_TAG_NULL: 220 out.put("null"); 221 break; 222 case JSVAL_TAG_BOOLEAN: 223 if (toBoolean()) { 224 out.put("true"); 225 } else { 226 out.put("false"); 227 } 228 break; 229 case JSVAL_TAG_MAGIC: 230 out.put("<magic "); 231 WhyMagicToString( 232 whyMagic(), [&](const char* name) { out.put(name); }, 233 [&](int value) { out.printf("Unknown(%d)", value); }); 234 out.put(">"); 235 break; 236 case JSVAL_TAG_STRING: 237 toString()->dumpStringContent(out); 238 break; 239 case JSVAL_TAG_SYMBOL: 240 toSymbol()->dumpStringContent(out); 241 break; 242 case JSVAL_TAG_PRIVATE_GCTHING: 243 out.printf("<private GCThing @ 0x%p>", toGCThing()); 244 break; 245 case JSVAL_TAG_BIGINT: 246 toBigInt()->dumpStringContent(out); 247 break; 248 case JSVAL_TAG_OBJECT: 249 toObject().dumpStringContent(out); 250 break; 251 default: 252 out.printf("Unknown(%08x)", tag); 253 break; 254 } 255 } 256 } 257 #endif