Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / vm / ToSource.cpp
blob50fd8a103753bb09cc43bbbb763decb493191cee
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/. */
7 #include "vm/ToSource.h"
9 #include "mozilla/Assertions.h" // MOZ_ASSERT
10 #include "mozilla/FloatingPoint.h" // mozilla::IsNegativeZero
12 #include <iterator> // std::size
13 #include <stdint.h> // uint32_t
15 #include "builtin/Array.h" // ArrayToSource
16 #include "builtin/Boolean.h" // BooleanToString
17 #include "builtin/Object.h" // ObjectToSource
18 #include "gc/GCEnum.h" // CanGC
19 #include "js/Class.h" // ESClass
20 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
21 #include "js/Object.h" // JS::GetBuiltinClass
22 #include "js/Printer.h" // QuoteString
23 #include "js/Symbol.h" // SymbolCode, JS::WellKnownSymbolLimit
24 #include "js/TypeDecls.h" // Rooted{Function, Object, String, Value}, HandleValue, Latin1Char
25 #include "js/Utility.h" // UniqueChars
26 #include "js/Value.h" // JS::Value
27 #include "util/StringBuffer.h" // JSStringBuilder
28 #include "vm/ErrorObject.h" // ErrorObject, ErrorToSource
29 #include "vm/Interpreter.h" // Call
30 #include "vm/JSContext.h" // JSContext
31 #include "vm/JSFunction.h" // JSFunction, fun_toStringHelper
32 #include "vm/SelfHosting.h" // CallSelfHostedFunction
33 #include "vm/Stack.h" // FixedInvokeArgs
34 #include "vm/StaticStrings.h" // StaticStrings
35 #include "vm/StringType.h" // NewStringCopy{N,Z}, ToString
36 #include "vm/SymbolType.h" // Symbol
37 #ifdef ENABLE_RECORD_TUPLE
38 # include "vm/RecordType.h"
39 # include "vm/TupleType.h"
40 #endif
42 #include "vm/JSContext-inl.h" // JSContext::check
43 #include "vm/JSObject-inl.h" // IsCallable
44 #include "vm/ObjectOperations-inl.h" // GetProperty
46 using namespace js;
48 using mozilla::IsNegativeZero;
50 using JS::GetBuiltinClass;
53 * Convert a JSString to its source expression; returns null after reporting an
54 * error, otherwise returns a new string reference. No Handle needed since the
55 * input is dead after the GC.
57 static JSString* StringToSource(JSContext* cx, JSString* str) {
58 UniqueChars chars = QuoteString(cx, str, '"');
59 if (!chars) {
60 return nullptr;
62 return NewStringCopyZ<CanGC>(cx, chars.get());
65 static JSString* SymbolToSource(JSContext* cx, JS::Symbol* symbol) {
66 using JS::SymbolCode;
68 RootedString desc(cx, symbol->description());
69 SymbolCode code = symbol->code();
70 if (symbol->isWellKnownSymbol()) {
71 // Well-known symbol.
72 return desc;
75 if (code == SymbolCode::PrivateNameSymbol) {
76 MOZ_ASSERT(desc);
77 return desc;
80 MOZ_ASSERT(code == SymbolCode::InSymbolRegistry ||
81 code == SymbolCode::UniqueSymbol);
83 JSStringBuilder buf(cx);
84 if (code == SymbolCode::InSymbolRegistry ? !buf.append("Symbol.for(")
85 : !buf.append("Symbol(")) {
86 return nullptr;
88 if (desc) {
89 UniqueChars quoted = QuoteString(cx, desc, '"');
90 if (!quoted || !buf.append(quoted.get(), strlen(quoted.get()))) {
91 return nullptr;
94 if (!buf.append(')')) {
95 return nullptr;
97 return buf.finishString();
100 static JSString* BoxedToSource(JSContext* cx, HandleObject obj,
101 const char* constructor) {
102 RootedValue value(cx);
103 if (!Unbox(cx, obj, &value)) {
104 return nullptr;
106 MOZ_ASSERT(!value.isUndefined());
108 RootedString str(cx, ValueToSource(cx, value));
109 if (!str) {
110 return nullptr;
113 JSStringBuilder buf(cx);
114 if (!buf.append("new ") || !buf.append(constructor, strlen(constructor)) ||
115 !buf.append('(') || !buf.append(str) || !buf.append(')')) {
116 return nullptr;
119 return buf.finishString();
122 JSString* js::ValueToSource(JSContext* cx, HandleValue v) {
123 AutoCheckRecursionLimit recursion(cx);
124 if (!recursion.check(cx)) {
125 return nullptr;
127 cx->check(v);
129 switch (v.type()) {
130 case JS::ValueType::Undefined:
131 return cx->names().void_0_;
133 case JS::ValueType::String:
134 return StringToSource(cx, v.toString());
136 case JS::ValueType::Symbol:
137 return SymbolToSource(cx, v.toSymbol());
139 case JS::ValueType::Null:
140 return cx->names().null;
142 case JS::ValueType::Boolean:
143 return BooleanToString(cx, v.toBoolean());
145 case JS::ValueType::Double:
146 /* Special case to preserve negative zero, _contra_ toString. */
147 if (IsNegativeZero(v.toDouble())) {
148 static const Latin1Char negativeZero[] = {'-', '0'};
150 return NewStringCopyN<CanGC>(cx, negativeZero, std::size(negativeZero));
152 [[fallthrough]];
153 case JS::ValueType::Int32:
154 return ToString<CanGC>(cx, v);
156 case JS::ValueType::BigInt: {
157 RootedString str(cx, ToString<CanGC>(cx, v));
158 if (!str) {
159 return nullptr;
162 RootedString n(cx, cx->staticStrings().getUnit('n'));
164 return ConcatStrings<CanGC>(cx, str, n);
167 #ifdef ENABLE_RECORD_TUPLE
168 case ValueType::ExtendedPrimitive: {
169 RootedObject obj(cx, &v.toExtendedPrimitive());
170 if (obj->is<TupleType>()) {
171 Rooted<TupleType*> tup(cx, &obj->as<TupleType>());
172 return TupleToSource(cx, tup);
174 if (obj->is<RecordType>()) {
175 return RecordToSource(cx, obj.as<RecordType>());
177 MOZ_CRASH("Unsupported ExtendedPrimitive");
179 #endif
181 case JS::ValueType::Object: {
182 RootedValue fval(cx);
183 RootedObject obj(cx, &v.toObject());
184 if (!GetProperty(cx, obj, obj, cx->names().toSource, &fval)) {
185 return nullptr;
187 if (IsCallable(fval)) {
188 RootedValue v(cx);
189 if (!js::Call(cx, fval, obj, &v)) {
190 return nullptr;
193 return ToString<CanGC>(cx, v);
196 ESClass cls;
197 if (!GetBuiltinClass(cx, obj, &cls)) {
198 return nullptr;
201 // All ToSource functions must be able to handle wrapped objects!
202 switch (cls) {
203 case ESClass::Function:
204 return fun_toStringHelper(cx, obj, true);
206 case ESClass::Array:
207 return ArrayToSource(cx, obj);
209 case ESClass::Error:
210 return ErrorToSource(cx, obj);
212 case ESClass::RegExp: {
213 FixedInvokeArgs<0> args(cx);
214 RootedValue rval(cx);
215 if (!CallSelfHostedFunction(cx, cx->names().dollar_RegExpToString_, v,
216 args, &rval)) {
217 return nullptr;
219 return ToString<CanGC>(cx, rval);
222 case ESClass::Boolean:
223 return BoxedToSource(cx, obj, "Boolean");
225 case ESClass::Number:
226 return BoxedToSource(cx, obj, "Number");
228 case ESClass::String:
229 return BoxedToSource(cx, obj, "String");
231 case ESClass::Date:
232 return BoxedToSource(cx, obj, "Date");
234 default:
235 return ObjectToSource(cx, obj);
239 case JS::ValueType::PrivateGCThing:
240 case JS::ValueType::Magic:
241 MOZ_ASSERT_UNREACHABLE(
242 "internal value types shouldn't leak into places "
243 "wanting source representations");
244 return nullptr;
247 MOZ_ASSERT_UNREACHABLE("shouldn't see an unrecognized value type");
248 return nullptr;