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"
42 #include "vm/JSContext-inl.h" // JSContext::check
43 #include "vm/JSObject-inl.h" // IsCallable
44 #include "vm/ObjectOperations-inl.h" // GetProperty
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
, '"');
62 return NewStringCopyZ
<CanGC
>(cx
, chars
.get());
65 static JSString
* SymbolToSource(JSContext
* cx
, JS::Symbol
* symbol
) {
68 RootedString
desc(cx
, symbol
->description());
69 SymbolCode code
= symbol
->code();
70 if (symbol
->isWellKnownSymbol()) {
75 if (code
== SymbolCode::PrivateNameSymbol
) {
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(")) {
89 UniqueChars quoted
= QuoteString(cx
, desc
, '"');
90 if (!quoted
|| !buf
.append(quoted
.get(), strlen(quoted
.get()))) {
94 if (!buf
.append(')')) {
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
)) {
106 MOZ_ASSERT(!value
.isUndefined());
108 RootedString
str(cx
, ValueToSource(cx
, value
));
113 JSStringBuilder
buf(cx
);
114 if (!buf
.append("new ") || !buf
.append(constructor
, strlen(constructor
)) ||
115 !buf
.append('(') || !buf
.append(str
) || !buf
.append(')')) {
119 return buf
.finishString();
122 JSString
* js::ValueToSource(JSContext
* cx
, HandleValue v
) {
123 AutoCheckRecursionLimit
recursion(cx
);
124 if (!recursion
.check(cx
)) {
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
));
153 case JS::ValueType::Int32
:
154 return ToString
<CanGC
>(cx
, v
);
156 case JS::ValueType::BigInt
: {
157 RootedString
str(cx
, ToString
<CanGC
>(cx
, v
));
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");
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
)) {
187 if (IsCallable(fval
)) {
189 if (!js::Call(cx
, fval
, obj
, &v
)) {
193 return ToString
<CanGC
>(cx
, v
);
197 if (!GetBuiltinClass(cx
, obj
, &cls
)) {
201 // All ToSource functions must be able to handle wrapped objects!
203 case ESClass::Function
:
204 return fun_toStringHelper(cx
, obj
, true);
207 return ArrayToSource(cx
, obj
);
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
,
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");
232 return BoxedToSource(cx
, obj
, "Date");
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");
247 MOZ_ASSERT_UNREACHABLE("shouldn't see an unrecognized value type");