1
1
#include " console.h"
2
- #include " mozilla/Result.h"
2
+ #include < js/Array.h>
3
+ #include < js/PropertyAndElement.h>
4
+ #pragma clang diagnostic push
5
+ #pragma clang diagnostic ignored "-Winvalid-offsetof"
6
+ #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"
7
+ #include < js/experimental/TypedData.h>
8
+ #pragma clang diagnostic pop
3
9
4
10
JS::Result<mozilla::Ok> ToSource (JSContext *cx, std::string &sourceOut, JS::HandleValue val,
5
11
JS::MutableHandleObjectVector visitedObjects);
@@ -23,18 +29,14 @@ JS::Result<mozilla::Ok> PromiseToSource(JSContext *cx, std::string &sourceOut, J
23
29
}
24
30
case JS::PromiseState::Fulfilled: {
25
31
JS::RootedValue value (cx, JS::GetPromiseResult (obj));
26
- std::string source;
27
- MOZ_TRY (ToSource (cx, source, value, visitedObjects));
28
- sourceOut += source;
32
+ MOZ_TRY (ToSource (cx, sourceOut, value, visitedObjects));
29
33
sourceOut += " }" ;
30
34
break ;
31
35
}
32
36
case JS::PromiseState::Rejected: {
33
37
sourceOut += " <rejected> " ;
34
38
JS::RootedValue value (cx, JS::GetPromiseResult (obj));
35
- std::string source;
36
- MOZ_TRY (ToSource (cx, source, value, visitedObjects));
37
- sourceOut += source;
39
+ MOZ_TRY (ToSource (cx, sourceOut, value, visitedObjects));
38
40
sourceOut += " }" ;
39
41
break ;
40
42
}
@@ -84,13 +86,9 @@ JS::Result<mozilla::Ok> MapToSource(JSContext *cx, std::string &sourceOut, JS::H
84
86
entry = &entry_val.toObject ();
85
87
JS_GetElement (cx, entry, 0 , &name_val);
86
88
JS_GetElement (cx, entry, 1 , &value_val);
87
- std::string name;
88
- MOZ_TRY (ToSource (cx, name, name_val, visitedObjects));
89
- sourceOut += name;
89
+ MOZ_TRY (ToSource (cx, sourceOut, name_val, visitedObjects));
90
90
sourceOut += " => " ;
91
- std::string value;
92
- MOZ_TRY (ToSource (cx, value, value_val, visitedObjects));
93
- sourceOut += value;
91
+ MOZ_TRY (ToSource (cx, sourceOut, value_val, visitedObjects));
94
92
}
95
93
sourceOut += " }" ;
96
94
return mozilla::Ok ();
@@ -126,73 +124,138 @@ JS::Result<mozilla::Ok> SetToSource(JSContext *cx, std::string &sourceOut, JS::H
126
124
if (done) {
127
125
break ;
128
126
}
129
- std::string entry;
130
- MOZ_TRY (ToSource (cx, entry, entry_val, visitedObjects));
131
127
if (firstValue) {
132
128
firstValue = false ;
133
129
} else {
134
130
sourceOut += " , " ;
135
131
}
136
- sourceOut += entry ;
132
+ MOZ_TRY ( ToSource (cx, sourceOut, entry_val, visitedObjects)) ;
137
133
}
138
134
sourceOut += " }" ;
139
135
return mozilla::Ok ();
140
136
}
141
137
142
- /* *
143
- * Turn a handle of an Object into a string which represents the object.
144
- * This function will go through every property on the object (including non-enumerable properties)
145
- * Each property name and property value within the object will be converted into it's ToSource
146
- * representation. Note: functions and methods within the object are not included in the output
147
- *
148
- * E.G. The object `{ a: 1, b: 2, c: 3, d(){}, get f(){}, g: function bar() {} }`
149
- * would be represented as "{a: 1, b: {c: 2}, c: 3, f: undefined}"
138
+ JS::Result<mozilla::Ok> ArrayToSource (JSContext *cx, std::string &sourceOut, JS::HandleObject obj,
139
+ JS::MutableHandleObjectVector visitedObjects) {
140
+ sourceOut += " [" ;
141
+ uint32_t len;
142
+ if (!JS::GetArrayLength (cx, obj, &len)) {
143
+ return JS::Result<mozilla::Ok>(JS::Error ());
144
+ }
145
+
146
+ for (int i = 0 ; i < len; i++) {
147
+ JS::RootedValue entry_val (cx);
148
+ JS_GetElement (cx, obj, i, &entry_val);
149
+ if (i > 0 ) {
150
+ sourceOut += " , " ;
151
+ }
152
+ MOZ_TRY (ToSource (cx, sourceOut, entry_val, visitedObjects));
153
+ }
154
+ sourceOut += " ]" ;
155
+ return mozilla::Ok ();
156
+ }
157
+
158
+ /*
159
+ * Logs all enumerable properties, except non-own function and symbol properties
160
+ * Includes handling for getters and setters
150
161
*/
151
162
JS::Result<mozilla::Ok> ObjectToSource (JSContext *cx, std::string &sourceOut, JS::HandleObject obj,
152
163
JS::MutableHandleObjectVector visitedObjects) {
153
- sourceOut += " {" ;
154
164
JS::RootedIdVector ids (cx);
165
+
155
166
if (!js::GetPropertyKeys (cx, obj, 0 , &ids)) {
156
167
return JS::Result<mozilla::Ok>(JS::Error ());
157
168
}
158
169
159
170
JS::RootedValue value (cx);
160
171
size_t length = ids.length ();
172
+
173
+ sourceOut += " {" ;
161
174
bool firstValue = true ;
162
175
for (size_t i = 0 ; i < length; ++i) {
163
176
const auto &id = ids[i];
164
- if (!JS_GetPropertyById (cx, obj, id, &value)) {
165
- return JS::Result<mozilla::Ok>(JS::Error ());
177
+
178
+ JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc (cx);
179
+ JS_GetOwnPropertyDescriptorById (cx, obj, id, &desc);
180
+
181
+ bool getter_setter = !desc.isNothing () && (desc->hasGetter () || desc->hasSetter ());
182
+
183
+ // retrive the value if not a getter or setter
184
+ if (!getter_setter) {
185
+ if (!JS_GetPropertyById (cx, obj, id, &value)) {
186
+ return JS::Result<mozilla::Ok>(JS::Error ());
187
+ }
166
188
}
167
189
168
- if (!value. isObject () || ! JS_ObjectIsFunction (&value. toObject ())) {
169
- if (firstValue ) {
170
- firstValue = false ;
171
- } else {
172
- sourceOut += " , " ;
190
+ // Skip logging non-own function or getter and setter keys
191
+ if (getter_setter || (value. isObject () && JS_ObjectIsFunction (&value. toObject ())) ) {
192
+ bool own_prop ;
193
+ if (! JS_HasOwnPropertyById (cx, obj, id, &own_prop)) {
194
+ return JS::Result<mozilla::Ok>( JS::Error ()) ;
173
195
}
174
- if (id.isSymbol ()) {
175
- JS::RootedValue v (cx, SymbolValue (id.toSymbol ()));
176
- std::string source;
177
- MOZ_TRY (ToSource (cx, source, v, visitedObjects));
178
- sourceOut += source;
179
- } else {
180
- JS::RootedValue idValue (cx, js::IdToValue (id));
181
- std::string source;
182
- MOZ_TRY (ToSource (cx, source, idValue, visitedObjects));
183
- sourceOut += source;
196
+ if (!own_prop) {
197
+ continue ;
184
198
}
185
- sourceOut += " : " ;
186
- std::string source;
187
- MOZ_TRY (ToSource (cx, source, value, visitedObjects));
188
- sourceOut += source;
199
+ }
200
+
201
+ if (firstValue) {
202
+ firstValue = false ;
203
+ sourceOut += " " ;
204
+ } else {
205
+ sourceOut += " , " ;
206
+ }
207
+
208
+ // Key
209
+ if (id.isSymbol ()) {
210
+ JS::RootedValue v (cx, SymbolValue (id.toSymbol ()));
211
+ MOZ_TRY (ToSource (cx, sourceOut, v, visitedObjects));
212
+ } else {
213
+ size_t message_len;
214
+ JS::RootedValue v (cx, js::IdToValue (id));
215
+ auto msg = encode (cx, v, &message_len);
216
+ if (!msg) {
217
+ return JS::Result<mozilla::Ok>(JS::Error ());
218
+ }
219
+ sourceOut += std::string (msg.get (), message_len);
220
+ }
221
+
222
+ sourceOut += " : " ;
223
+
224
+ // Getters and Setters
225
+ if (getter_setter) {
226
+ sourceOut += " [Getter]" ;
227
+ } else {
228
+ MOZ_TRY (ToSource (cx, sourceOut, value, visitedObjects));
189
229
}
190
230
}
191
231
232
+ if (!firstValue) {
233
+ sourceOut += " " ;
234
+ }
192
235
sourceOut += " }" ;
236
+
193
237
return mozilla::Ok ();
194
238
}
195
239
240
+ mozilla::Maybe<std::string> get_class_name (JSContext *cx, JS::HandleObject obj) {
241
+ mozilla::Maybe<std::string> result = {};
242
+ JS::RootedValue constructorVal (cx);
243
+ if (JS_GetProperty (cx, obj, " constructor" , &constructorVal) && constructorVal.isObject ()) {
244
+ JS::RootedValue name (cx);
245
+ JS::RootedObject constructorObj (cx, &constructorVal.toObject ());
246
+ if (JS_GetProperty (cx, constructorObj, " name" , &name) && name.isString ()) {
247
+ size_t message_len;
248
+ auto msg = encode (cx, name, &message_len);
249
+ if (!msg) {
250
+ return result;
251
+ }
252
+ std::string name_str (msg.get (), message_len);
253
+ result.emplace (name_str);
254
+ }
255
+ }
256
+ return result;
257
+ }
258
+
196
259
/* *
197
260
* Turn a handle of any value into a string which represents it.
198
261
*/
@@ -212,6 +275,35 @@ JS::Result<mozilla::Ok> ToSource(JSContext *cx, std::string &sourceOut, JS::Hand
212
275
case JS::ValueType::Object: {
213
276
JS::RootedObject obj (cx, &val.toObject ());
214
277
278
+ if (JS_ObjectIsFunction (obj)) {
279
+ sourceOut += " [Function" ;
280
+ std::string source;
281
+ auto id = JS_GetFunctionId (JS_ValueToFunction (cx, val));
282
+ if (id) {
283
+ sourceOut += " " ;
284
+ JS::RootedString name (cx, id);
285
+ size_t name_len;
286
+ auto msg = encode (cx, name, &name_len);
287
+ if (!msg) {
288
+ return JS::Result<mozilla::Ok>(JS::Error ());
289
+ }
290
+ sourceOut += std::string (msg.get (), name_len);
291
+ }
292
+ sourceOut += " ]" ;
293
+ return mozilla::Ok ();
294
+ }
295
+
296
+ if (JS_IsTypedArrayObject (obj)) {
297
+ // Show the typed array type
298
+ mozilla::Maybe<std::string> name_str = get_class_name (cx, obj);
299
+ if (!name_str.isNothing ()) {
300
+ sourceOut += *name_str;
301
+ sourceOut += " " ;
302
+ }
303
+ MOZ_TRY (ArrayToSource (cx, sourceOut, obj, visitedObjects));
304
+ return mozilla::Ok ();
305
+ }
306
+
215
307
for (const auto &curObject : visitedObjects) {
216
308
if (obj.get () == curObject) {
217
309
sourceOut += " <Circular>" ;
@@ -223,68 +315,74 @@ JS::Result<mozilla::Ok> ToSource(JSContext *cx, std::string &sourceOut, JS::Hand
223
315
return JS::Result<mozilla::Ok>(JS::Error ());
224
316
}
225
317
226
- if (JS_ObjectIsFunction (obj)) {
227
- sourceOut += JS::InformalValueTypeName (val);
228
- return mozilla::Ok ();
229
- }
230
318
js::ESClass cls;
231
319
if (!JS::GetBuiltinClass (cx, obj, &cls)) {
232
320
return JS::Result<mozilla::Ok>(JS::Error ());
233
321
}
234
322
235
- if (cls == js::ESClass::Array || cls == js::ESClass::Date || cls == js::ESClass::Error ||
236
- cls == js::ESClass::RegExp) {
323
+ switch (cls) {
324
+ case js::ESClass::Date:
325
+ case js::ESClass::Error:
326
+ case js::ESClass::RegExp: {
237
327
JS::RootedString source (cx, JS_ValueToSource (cx, val));
238
328
size_t message_len;
239
329
auto msg = encode (cx, source, &message_len);
240
330
if (!msg) {
241
331
return JS::Result<mozilla::Ok>(JS::Error ());
242
332
}
243
- std::string sourceString (msg.get (), message_len);
244
- sourceOut += sourceString;
333
+ sourceOut += std::string (msg.get (), message_len);
245
334
return mozilla::Ok ();
246
- } else if (cls == js::ESClass::Set) {
247
- std::string sourceString;
248
- MOZ_TRY (SetToSource (cx, sourceString, obj, visitedObjects));
249
- sourceOut += sourceString;
335
+ }
336
+ case js::ESClass::Array: {
337
+ MOZ_TRY (ArrayToSource (cx, sourceOut, obj, visitedObjects));
250
338
return mozilla::Ok ();
251
- } else if (cls == js::ESClass::Map) {
252
- std::string sourceString;
253
- MOZ_TRY (MapToSource (cx, sourceString, obj, visitedObjects));
254
- sourceOut += sourceString;
339
+ }
340
+ case js::ESClass::Set: {
341
+ MOZ_TRY (SetToSource (cx, sourceOut, obj, visitedObjects));
255
342
return mozilla::Ok ();
256
- } else if (cls == js::ESClass::Promise) {
257
- std::string sourceString;
258
- MOZ_TRY (PromiseToSource (cx, sourceString, obj, visitedObjects));
259
- sourceOut += sourceString;
343
+ }
344
+ case js::ESClass::Map: {
345
+ MOZ_TRY (MapToSource (cx, sourceOut, obj, visitedObjects));
260
346
return mozilla::Ok ();
261
- } else {
347
+ }
348
+ case js::ESClass::Promise: {
349
+ MOZ_TRY (PromiseToSource (cx, sourceOut, obj, visitedObjects));
350
+ return mozilla::Ok ();
351
+ }
352
+ default : {
353
+ std::string sourceString;
262
354
if (JS::IsWeakMapObject (obj)) {
263
- std::string sourceString = " WeakMap { <items unknown> }" ;
264
- sourceOut += sourceString;
355
+ sourceOut += " WeakMap { <items unknown> }" ;
265
356
return mozilla::Ok ();
266
357
}
267
358
auto cls = JS::GetClass (obj);
268
359
std::string className (cls->name );
269
360
if (className == " WeakSet" ) {
270
- std::string sourceString = " WeakSet { <items unknown> }" ;
271
- sourceOut += sourceString;
361
+ sourceOut += " WeakSet { <items unknown> }" ;
272
362
return mozilla::Ok ();
273
363
}
274
- std::string sourceString;
275
- MOZ_TRY (ObjectToSource (cx, sourceString, obj, visitedObjects));
276
- sourceOut += sourceString;
364
+
365
+ // Lookup the class name if a custom class
366
+ mozilla::Maybe<std::string> name_str = get_class_name (cx, obj);
367
+ if (!name_str.isNothing () && *name_str != " Object" ) {
368
+ sourceOut += *name_str;
369
+ sourceOut += " " ;
370
+ }
371
+
372
+ MOZ_TRY (ObjectToSource (cx, sourceOut, obj, visitedObjects));
277
373
return mozilla::Ok ();
278
374
}
375
+ }
279
376
}
280
377
case JS::ValueType::String: {
281
378
size_t message_len;
282
379
auto msg = encode (cx, val, &message_len);
283
380
if (!msg) {
284
381
return JS::Result<mozilla::Ok>(JS::Error ());
285
382
}
286
- std::string sourceString (msg.get (), message_len);
287
- sourceOut += sourceString;
383
+ sourceOut += ' "' ;
384
+ sourceOut += std::string (msg.get (), message_len);
385
+ sourceOut += ' "' ;
288
386
return mozilla::Ok ();
289
387
}
290
388
default : {
@@ -294,8 +392,7 @@ JS::Result<mozilla::Ok> ToSource(JSContext *cx, std::string &sourceOut, JS::Hand
294
392
if (!msg) {
295
393
return JS::Result<mozilla::Ok>(JS::Error ());
296
394
}
297
- std::string sourceString (msg.get (), message_len);
298
- sourceOut += sourceString;
395
+ sourceOut += std::string (msg.get (), message_len);
299
396
return mozilla::Ok ();
300
397
}
301
398
}
@@ -316,12 +413,15 @@ static bool console_out(JSContext *cx, unsigned argc, JS::Value *vp) {
316
413
if (result.isErr ()) {
317
414
return false ;
318
415
}
319
- std::string message = source;
416
+ // strip quotes for direct string logs
417
+ if (source[0 ] == ' "' && source[source.length () - 1 ] == ' "' ) {
418
+ source = source.substr (1 , source.length () - 2 );
419
+ }
320
420
if (fullLogLine.length ()) {
321
421
fullLogLine += " " ;
322
- fullLogLine += message ;
422
+ fullLogLine += source ;
323
423
} else {
324
- fullLogLine += message ;
424
+ fullLogLine += source ;
325
425
}
326
426
}
327
427
0 commit comments