Source code

Revision control

Other Tools

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 "builtin/Object.h"
8
9
#include "mozilla/MaybeOneOf.h"
10
11
#include <algorithm>
12
13
#include "builtin/BigInt.h"
14
#include "builtin/Eval.h"
15
#include "builtin/SelfHostingDefines.h"
16
#include "frontend/BytecodeCompiler.h"
17
#include "jit/InlinableNatives.h"
18
#include "js/PropertySpec.h"
19
#include "js/UniquePtr.h"
20
#include "util/StringBuffer.h"
21
#include "vm/AsyncFunction.h"
22
#include "vm/DateObject.h"
23
#include "vm/EqualityOperations.h" // js::SameValue
24
#include "vm/JSContext.h"
25
#include "vm/RegExpObject.h"
26
27
#include "vm/JSObject-inl.h"
28
#include "vm/NativeObject-inl.h"
29
#include "vm/Shape-inl.h"
30
31
#ifdef FUZZING
32
# include "builtin/TestingFunctions.h"
33
#endif
34
35
using namespace js;
36
37
using js::frontend::IsIdentifier;
38
39
bool js::obj_construct(JSContext* cx, unsigned argc, Value* vp) {
40
CallArgs args = CallArgsFromVp(argc, vp);
41
42
RootedObject obj(cx, nullptr);
43
if (args.isConstructing() &&
44
(&args.newTarget().toObject() != &args.callee())) {
45
RootedObject newTarget(cx, &args.newTarget().toObject());
46
obj = CreateThis(cx, &PlainObject::class_, newTarget);
47
if (!obj) {
48
return false;
49
}
50
} else if (args.length() > 0 && !args[0].isNullOrUndefined()) {
51
obj = ToObject(cx, args[0]);
52
if (!obj) {
53
return false;
54
}
55
} else {
56
/* Make an object whether this was called with 'new' or not. */
57
if (!NewObjectScriptedCall(cx, &obj)) {
58
return false;
59
}
60
}
61
62
args.rval().setObject(*obj);
63
return true;
64
}
65
66
/* ES5 15.2.4.7. */
67
bool js::obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp) {
68
CallArgs args = CallArgsFromVp(argc, vp);
69
70
HandleValue idValue = args.get(0);
71
72
// As an optimization, provide a fast path when rooting is not necessary and
73
// we can safely retrieve the attributes from the object's shape.
74
75
/* Steps 1-2. */
76
jsid id;
77
if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
78
JSObject* obj = &args.thisv().toObject();
79
80
/* Step 3. */
81
PropertyResult prop;
82
if (obj->isNative() && NativeLookupOwnProperty<NoGC>(
83
cx, &obj->as<NativeObject>(), id, &prop)) {
84
/* Step 4. */
85
if (!prop) {
86
args.rval().setBoolean(false);
87
return true;
88
}
89
90
/* Step 5. */
91
unsigned attrs = GetPropertyAttributes(obj, prop);
92
args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
93
return true;
94
}
95
}
96
97
/* Step 1. */
98
RootedId idRoot(cx);
99
if (!ToPropertyKey(cx, idValue, &idRoot)) {
100
return false;
101
}
102
103
/* Step 2. */
104
RootedObject obj(cx, ToObject(cx, args.thisv()));
105
if (!obj) {
106
return false;
107
}
108
109
/* Step 3. */
110
Rooted<PropertyDescriptor> desc(cx);
111
if (!GetOwnPropertyDescriptor(cx, obj, idRoot, &desc)) {
112
return false;
113
}
114
115
/* Steps 4-5. */
116
args.rval().setBoolean(desc.object() && desc.enumerable());
117
return true;
118
}
119
120
static bool obj_toSource(JSContext* cx, unsigned argc, Value* vp) {
121
CallArgs args = CallArgsFromVp(argc, vp);
122
123
if (!CheckRecursionLimit(cx)) {
124
return false;
125
}
126
127
RootedObject obj(cx, ToObject(cx, args.thisv()));
128
if (!obj) {
129
return false;
130
}
131
132
JSString* str = ObjectToSource(cx, obj);
133
if (!str) {
134
return false;
135
}
136
137
args.rval().setString(str);
138
return true;
139
}
140
141
template <typename CharT>
142
static bool Consume(const CharT*& s, const CharT* e, const char* chars) {
143
size_t len = strlen(chars);
144
if (s + len >= e) {
145
return false;
146
}
147
if (!EqualChars(s, chars, len)) {
148
return false;
149
}
150
s += len;
151
return true;
152
}
153
154
template <typename CharT>
155
static void ConsumeSpaces(const CharT*& s, const CharT* e) {
156
while (*s == ' ' && s < e) {
157
s++;
158
}
159
}
160
161
/*
162
* Given a function source string, return the offset and length of the part
163
* between '(function $name' and ')'.
164
*/
165
template <typename CharT>
166
static bool ArgsAndBodySubstring(mozilla::Range<const CharT> chars,
167
size_t* outOffset, size_t* outLen) {
168
const CharT* const start = chars.begin().get();
169
const CharT* s = start;
170
const CharT* e = chars.end().get();
171
172
if (s == e) {
173
return false;
174
}
175
176
// Remove enclosing parentheses.
177
if (*s == '(' && *(e - 1) == ')') {
178
s++;
179
e--;
180
}
181
182
// Support the following cases, with spaces between tokens:
183
//
184
// -+---------+-+------------+-+-----+-+- [ - <any> - ] - ( -+-
185
// | | | | | | | |
186
// +- async -+ +- function -+ +- * -+ +- <any> - ( ---------+
187
// | |
188
// +- get ------+
189
// | |
190
// +- set ------+
191
//
192
// This accepts some invalid syntax, but we don't care, since it's only
193
// used by the non-standard toSource, and we're doing a best-effort attempt
194
// here.
195
196
(void)Consume(s, e, "async");
197
ConsumeSpaces(s, e);
198
(void)(Consume(s, e, "function") || Consume(s, e, "get") ||
199
Consume(s, e, "set"));
200
ConsumeSpaces(s, e);
201
(void)Consume(s, e, "*");
202
ConsumeSpaces(s, e);
203
204
// Jump over the function's name.
205
if (Consume(s, e, "[")) {
206
s = js_strchr_limit(s, ']', e);
207
if (!s) {
208
return false;
209
}
210
s++;
211
ConsumeSpaces(s, e);
212
if (*s != '(') {
213
return false;
214
}
215
} else {
216
s = js_strchr_limit(s, '(', e);
217
if (!s) {
218
return false;
219
}
220
}
221
222
*outOffset = s - start;
223
*outLen = e - s;
224
MOZ_ASSERT(*outOffset + *outLen <= chars.length());
225
return true;
226
}
227
228
enum class PropertyKind { Getter, Setter, Method, Normal };
229
230
JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) {
231
/* If outermost, we need parentheses to be an expression, not a block. */
232
bool outermost = cx->cycleDetectorVector().empty();
233
234
AutoCycleDetector detector(cx, obj);
235
if (!detector.init()) {
236
return nullptr;
237
}
238
if (detector.foundCycle()) {
239
return NewStringCopyZ<CanGC>(cx, "{}");
240
}
241
242
JSStringBuilder buf(cx);
243
if (outermost && !buf.append('(')) {
244
return nullptr;
245
}
246
if (!buf.append('{')) {
247
return nullptr;
248
}
249
250
RootedIdVector idv(cx);
251
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv)) {
252
return nullptr;
253
}
254
255
bool comma = false;
256
257
auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val,
258
PropertyKind kind) -> bool {
259
/* Convert id to a string. */
260
RootedString idstr(cx);
261
if (JSID_IS_SYMBOL(id)) {
262
RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
263
idstr = ValueToSource(cx, v);
264
if (!idstr) {
265
return false;
266
}
267
} else {
268
RootedValue idv(cx, IdToValue(id));
269
idstr = ToString<CanGC>(cx, idv);
270
if (!idstr) {
271
return false;
272
}
273
274
/*
275
* If id is a string that's not an identifier, or if it's a
276
* negative integer, then it must be quoted.
277
*/
278
if (JSID_IS_ATOM(id) ? !IsIdentifier(JSID_TO_ATOM(id))
279
: JSID_TO_INT(id) < 0) {
280
UniqueChars quotedId = QuoteString(cx, idstr, '\'');
281
if (!quotedId) {
282
return false;
283
}
284
idstr = NewStringCopyZ<CanGC>(cx, quotedId.get());
285
if (!idstr) {
286
return false;
287
}
288
}
289
}
290
291
RootedString valsource(cx, ValueToSource(cx, val));
292
if (!valsource) {
293
return false;
294
}
295
296
RootedLinearString valstr(cx, valsource->ensureLinear(cx));
297
if (!valstr) {
298
return false;
299
}
300
301
if (comma && !buf.append(", ")) {
302
return false;
303
}
304
comma = true;
305
306
size_t voffset, vlength;
307
308
// Methods and accessors can return exact syntax of source, that fits
309
// into property without adding property name or "get"/"set" prefix.
310
// Use the exact syntax when the following conditions are met:
311
//
312
// * It's a function object
313
// (exclude proxies)
314
// * Function's kind and property's kind are same
315
// (this can be false for dynamically defined properties)
316
// * Function has explicit name
317
// (this can be false for computed property and dynamically defined
318
// properties)
319
// * Function's name and property's name are same
320
// (this can be false for dynamically defined properties)
321
if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
322
kind == PropertyKind::Method) {
323
RootedFunction fun(cx);
324
if (val.toObject().is<JSFunction>()) {
325
fun = &val.toObject().as<JSFunction>();
326
// Method's case should be checked on caller.
327
if (((fun->isGetter() && kind == PropertyKind::Getter) ||
328
(fun->isSetter() && kind == PropertyKind::Setter) ||
329
kind == PropertyKind::Method) &&
330
fun->explicitName()) {
331
bool result;
332
if (!EqualStrings(cx, fun->explicitName(), idstr, &result)) {
333
return false;
334
}
335
336
if (result) {
337
if (!buf.append(valstr)) {
338
return false;
339
}
340
return true;
341
}
342
}
343
}
344
345
{
346
// When falling back try to generate a better string
347
// representation by skipping the prelude, and also removing
348
// the enclosing parentheses.
349
bool success;
350
JS::AutoCheckCannotGC nogc;
351
if (valstr->hasLatin1Chars()) {
352
success = ArgsAndBodySubstring(valstr->latin1Range(nogc), &voffset,
353
&vlength);
354
} else {
355
success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset,
356
&vlength);
357
}
358
if (!success) {
359
kind = PropertyKind::Normal;
360
}
361
}
362
363
if (kind == PropertyKind::Getter) {
364
if (!buf.append("get ")) {
365
return false;
366
}
367
} else if (kind == PropertyKind::Setter) {
368
if (!buf.append("set ")) {
369
return false;
370
}
371
} else if (kind == PropertyKind::Method && fun) {
372
if (fun->isAsync()) {
373
if (!buf.append("async ")) {
374
return false;
375
}
376
}
377
378
if (fun->isGenerator()) {
379
if (!buf.append('*')) {
380
return false;
381
}
382
}
383
}
384
}
385
386
bool needsBracket = JSID_IS_SYMBOL(id);
387
if (needsBracket && !buf.append('[')) {
388
return false;
389
}
390
if (!buf.append(idstr)) {
391
return false;
392
}
393
if (needsBracket && !buf.append(']')) {
394
return false;
395
}
396
397
if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
398
kind == PropertyKind::Method) {
399
if (!buf.appendSubstring(valstr, voffset, vlength)) {
400
return false;
401
}
402
} else {
403
if (!buf.append(':')) {
404
return false;
405
}
406
if (!buf.append(valstr)) {
407
return false;
408
}
409
}
410
return true;
411
};
412
413
RootedId id(cx);
414
Rooted<PropertyDescriptor> desc(cx);
415
RootedValue val(cx);
416
for (size_t i = 0; i < idv.length(); ++i) {
417
id = idv[i];
418
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
419
return nullptr;
420
}
421
422
if (!desc.object()) {
423
continue;
424
}
425
426
if (desc.isAccessorDescriptor()) {
427
if (desc.hasGetterObject() && desc.getterObject()) {
428
val.setObject(*desc.getterObject());
429
if (!AddProperty(id, val, PropertyKind::Getter)) {
430
return nullptr;
431
}
432
}
433
if (desc.hasSetterObject() && desc.setterObject()) {
434
val.setObject(*desc.setterObject());
435
if (!AddProperty(id, val, PropertyKind::Setter)) {
436
return nullptr;
437
}
438
}
439
continue;
440
}
441
442
val.set(desc.value());
443
444
JSFunction* fun;
445
if (IsFunctionObject(val, &fun) && fun->isMethod()) {
446
if (!AddProperty(id, val, PropertyKind::Method)) {
447
return nullptr;
448
}
449
continue;
450
}
451
452
if (!AddProperty(id, val, PropertyKind::Normal)) {
453
return nullptr;
454
}
455
}
456
457
if (!buf.append('}')) {
458
return nullptr;
459
}
460
if (outermost && !buf.append(')')) {
461
return nullptr;
462
}
463
464
return buf.finishString();
465
}
466
467
static bool GetBuiltinTagSlow(JSContext* cx, HandleObject obj,
468
MutableHandleString builtinTag) {
469
// Step 4.
470
bool isArray;
471
if (!IsArray(cx, obj, &isArray)) {
472
return false;
473
}
474
475
// Step 5.
476
if (isArray) {
477
builtinTag.set(cx->names().objectArray);
478
return true;
479
}
480
481
// Steps 6-13.
482
ESClass cls;
483
if (!GetBuiltinClass(cx, obj, &cls)) {
484
return false;
485
}
486
487
switch (cls) {
488
case ESClass::String:
489
builtinTag.set(cx->names().objectString);
490
return true;
491
case ESClass::Arguments:
492
builtinTag.set(cx->names().objectArguments);
493
return true;
494
case ESClass::Error:
495
builtinTag.set(cx->names().objectError);
496
return true;
497
case ESClass::Boolean:
498
builtinTag.set(cx->names().objectBoolean);
499
return true;
500
case ESClass::Number:
501
builtinTag.set(cx->names().objectNumber);
502
return true;
503
case ESClass::Date:
504
builtinTag.set(cx->names().objectDate);
505
return true;
506
case ESClass::RegExp:
507
builtinTag.set(cx->names().objectRegExp);
508
return true;
509
default:
510
if (obj->isCallable()) {
511
// Non-standard: Prevent <object> from showing up as Function.
512
RootedObject unwrapped(cx, CheckedUnwrapDynamic(obj, cx));
513
if (!unwrapped || !unwrapped->getClass()->isDOMClass()) {
514
builtinTag.set(cx->names().objectFunction);
515
return true;
516
}
517
}
518
builtinTag.set(nullptr);
519
return true;
520
}
521
}
522
523
static MOZ_ALWAYS_INLINE JSString* GetBuiltinTagFast(JSObject* obj,
524
const JSClass* clasp,
525
JSContext* cx) {
526
MOZ_ASSERT(clasp == obj->getClass());
527
MOZ_ASSERT(!clasp->isProxy());
528
529
// Optimize the non-proxy case to bypass GetBuiltinClass.
530
if (clasp == &PlainObject::class_) {
531
// This is not handled by GetBuiltinTagSlow, but this case is by far
532
// the most common so we optimize it here.
533
return cx->names().objectObject;
534
}
535
536
if (clasp == &ArrayObject::class_) {
537
return cx->names().objectArray;
538
}
539
540
if (clasp == &JSFunction::class_) {
541
return cx->names().objectFunction;
542
}
543
544
if (clasp == &StringObject::class_) {
545
return cx->names().objectString;
546
}
547
548
if (clasp == &NumberObject::class_) {
549
return cx->names().objectNumber;
550
}
551
552
if (clasp == &BooleanObject::class_) {
553
return cx->names().objectBoolean;
554
}
555
556
if (clasp == &DateObject::class_) {
557
return cx->names().objectDate;
558
}
559
560
if (clasp == &RegExpObject::class_) {
561
return cx->names().objectRegExp;
562
}
563
564
if (obj->is<ArgumentsObject>()) {
565
return cx->names().objectArguments;
566
}
567
568
if (obj->is<ErrorObject>()) {
569
return cx->names().objectError;
570
}
571
572
if (obj->isCallable() && !obj->getClass()->isDOMClass()) {
573
// Non-standard: Prevent <object> from showing up as Function.
574
return cx->names().objectFunction;
575
}
576
577
return nullptr;
578
}
579
580
// ES6 19.1.3.6
581
bool js::obj_toString(JSContext* cx, unsigned argc, Value* vp) {
582
CallArgs args = CallArgsFromVp(argc, vp);
583
584
// Step 1.
585
if (args.thisv().isUndefined()) {
586
args.rval().setString(cx->names().objectUndefined);
587
return true;
588
}
589
590
// Step 2.
591
if (args.thisv().isNull()) {
592
args.rval().setString(cx->names().objectNull);
593
return true;
594
}
595
596
// Step 3.
597
RootedObject obj(cx, ToObject(cx, args.thisv()));
598
if (!obj) {
599
return false;
600
}
601
602
RootedString builtinTag(cx);
603
const JSClass* clasp = obj->getClass();
604
if (MOZ_UNLIKELY(clasp->isProxy())) {
605
if (!GetBuiltinTagSlow(cx, obj, &builtinTag)) {
606
return false;
607
}
608
} else {
609
builtinTag = GetBuiltinTagFast(obj, clasp, cx);
610
#ifdef DEBUG
611
// Assert this fast path is correct and matches BuiltinTagSlow. The
612
// only exception is the PlainObject case: we special-case it here
613
// because it's so common, but BuiltinTagSlow doesn't handle this.
614
RootedString builtinTagSlow(cx);
615
if (!GetBuiltinTagSlow(cx, obj, &builtinTagSlow)) {
616
return false;
617
}
618
if (clasp == &PlainObject::class_) {
619
MOZ_ASSERT(!builtinTagSlow);
620
} else {
621
MOZ_ASSERT(builtinTagSlow == builtinTag);
622
}
623
#endif
624
}
625
626
// Step 14.
627
// Currently omitted for non-standard fallback.
628
629
// Step 15.
630
RootedValue tag(cx);
631
if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toStringTag,
632
&tag)) {
633
return false;
634
}
635
636
// Step 16.
637
if (!tag.isString()) {
638
// Non-standard (bug 1277801): Use ClassName as a fallback in the interim
639
if (!builtinTag) {
640
const char* className = GetObjectClassName(cx, obj);
641
StringBuffer sb(cx);
642
if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
643
!sb.append(']')) {
644
return false;
645
}
646
647
builtinTag = sb.finishAtom();
648
if (!builtinTag) {
649
return false;
650
}
651
}
652
653
args.rval().setString(builtinTag);
654
return true;
655
}
656
657
// Step 17.
658
StringBuffer sb(cx);
659
if (!sb.append("[object ") || !sb.append(tag.toString()) || !sb.append(']')) {
660
return false;
661
}
662
663
JSString* str = sb.finishAtom();
664
if (!str) {
665
return false;
666
}
667
668
args.rval().setString(str);
669
return true;
670
}
671
672
JSString* js::ObjectClassToString(JSContext* cx, HandleObject obj) {
673
const JSClass* clasp = obj->getClass();
674
675
if (JSString* tag = GetBuiltinTagFast(obj, clasp, cx)) {
676
return tag;
677
}
678
679
const char* className = clasp->name;
680
StringBuffer sb(cx);
681
if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
682
!sb.append(']')) {
683
return nullptr;
684
}
685
686
return sb.finishAtom();
687
}
688
689
static bool obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
690
CallArgs args = CallArgsFromVp(argc, vp);
691
692
if (!args.requireAtLeast(cx, "Object.setPrototypeOf", 2)) {
693
return false;
694
}
695
696
/* Step 1-2. */
697
if (args[0].isNullOrUndefined()) {
698
JS_ReportErrorNumberASCII(
699
cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
700
args[0].isNull() ? "null" : "undefined", "object");
701
return false;
702
}
703
704
/* Step 3. */
705
if (!args[1].isObjectOrNull()) {
706
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
707
JSMSG_NOT_EXPECTED_TYPE, "Object.setPrototypeOf",
708
"an object or null",
709
InformalValueTypeName(args[1]));
710
return false;
711
}
712
713
/* Step 4. */
714
if (!args[0].isObject()) {
715
args.rval().set(args[0]);
716
return true;
717
}
718
719
/* Step 5-7. */
720
RootedObject obj(cx, &args[0].toObject());
721
RootedObject newProto(cx, args[1].toObjectOrNull());
722
if (!SetPrototype(cx, obj, newProto)) {
723
return false;
724
}
725
726
/* Step 8. */
727
args.rval().set(args[0]);
728
return true;
729
}
730
731
static bool PropertyIsEnumerable(JSContext* cx, HandleObject obj, HandleId id,
732
bool* enumerable) {
733
PropertyResult prop;
734
if (obj->isNative() &&
735
NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop)) {
736
if (!prop) {
737
*enumerable = false;
738
return true;
739
}
740
741
unsigned attrs = GetPropertyAttributes(obj, prop);
742
*enumerable = (attrs & JSPROP_ENUMERATE) != 0;
743
return true;
744
}
745
746
Rooted<PropertyDescriptor> desc(cx);
747
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
748
return false;
749
}
750
751
*enumerable = desc.object() && desc.enumerable();
752
return true;
753
}
754
755
static bool TryAssignNative(JSContext* cx, HandleObject to, HandleObject from,
756
bool* optimized) {
757
*optimized = false;
758
759
if (!from->isNative() || !to->isNative()) {
760
return true;
761
}
762
763
// Don't use the fast path if |from| may have extra indexed or lazy
764
// properties.
765
NativeObject* fromNative = &from->as<NativeObject>();
766
if (fromNative->getDenseInitializedLength() > 0 || fromNative->isIndexed() ||
767
fromNative->is<TypedArrayObject>() ||
768
fromNative->getClass()->getNewEnumerate() ||
769
fromNative->getClass()->getEnumerate()) {
770
return true;
771
}
772
773
// Get a list of |from| shapes. As long as from->lastProperty() == fromShape
774
// we can use this to speed up both the enumerability check and the GetProp.
775
776
using ShapeVector = GCVector<Shape*, 8>;
777
Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
778
779
RootedShape fromShape(cx, fromNative->lastProperty());
780
for (Shape::Range<NoGC> r(fromShape); !r.empty(); r.popFront()) {
781
// Symbol properties need to be assigned last. For now fall back to the
782
// slow path if we see a symbol property.
783
if (MOZ_UNLIKELY(JSID_IS_SYMBOL(r.front().propidRaw()))) {
784
return true;
785
}
786
if (MOZ_UNLIKELY(!shapes.append(&r.front()))) {
787
return false;
788
}
789
}
790
791
*optimized = true;
792
793
RootedShape shape(cx);
794
RootedValue propValue(cx);
795
RootedId nextKey(cx);
796
RootedValue toReceiver(cx, ObjectValue(*to));
797
798
for (size_t i = shapes.length(); i > 0; i--) {
799
shape = shapes[i - 1];
800
nextKey = shape->propid();
801
802
// Ensure |from| is still native: a getter/setter might have been swapped
803
// with a non-native object.
804
if (MOZ_LIKELY(from->isNative() &&
805
from->as<NativeObject>().lastProperty() == fromShape &&
806
shape->isDataProperty())) {
807
if (!shape->enumerable()) {
808
continue;
809
}
810
propValue = from->as<NativeObject>().getSlot(shape->slot());
811
} else {
812
// |from| changed shape or the property is not a data property, so
813
// we have to do the slower enumerability check and GetProp.
814
bool enumerable;
815
if (!PropertyIsEnumerable(cx, from, nextKey, &enumerable)) {
816
return false;
817
}
818
if (!enumerable) {
819
continue;
820
}
821
if (!GetProperty(cx, from, from, nextKey, &propValue)) {
822
return false;
823
}
824
}
825
826
ObjectOpResult result;
827
if (MOZ_UNLIKELY(
828
!SetProperty(cx, to, nextKey, propValue, toReceiver, result))) {
829
return false;
830
}
831
if (MOZ_UNLIKELY(!result.checkStrict(cx, to, nextKey))) {
832
return false;
833
}
834
}
835
836
return true;
837
}
838
839
static bool AssignSlow(JSContext* cx, HandleObject to, HandleObject from) {
840
// Step 4.b.ii.
841
RootedIdVector keys(cx);
842
if (!GetPropertyKeys(
843
cx, from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &keys)) {
844
return false;
845
}
846
847
// Step 4.c.
848
RootedId nextKey(cx);
849
RootedValue propValue(cx);
850
for (size_t i = 0, len = keys.length(); i < len; i++) {
851
nextKey = keys[i];
852
853
// Step 4.c.i.
854
bool enumerable;
855
if (MOZ_UNLIKELY(!PropertyIsEnumerable(cx, from, nextKey, &enumerable))) {
856
return false;
857
}
858
if (!enumerable) {
859
continue;
860
}
861
862
// Step 4.c.ii.1.
863
if (MOZ_UNLIKELY(!GetProperty(cx, from, from, nextKey, &propValue))) {
864
return false;
865
}
866
867
// Step 4.c.ii.2.
868
if (MOZ_UNLIKELY(!SetProperty(cx, to, nextKey, propValue))) {
869
return false;
870
}
871
}
872
873
return true;
874
}
875
876
JS_PUBLIC_API bool JS_AssignObject(JSContext* cx, JS::HandleObject target,
877
JS::HandleObject src) {
878
bool optimized;
879
if (!TryAssignNative(cx, target, src, &optimized)) {
880
return false;
881
}
882
if (optimized) {
883
return true;
884
}
885
886
return AssignSlow(cx, target, src);
887
}
888
889
// ES2018 draft rev 48ad2688d8f964da3ea8c11163ef20eb126fb8a4
890
// 19.1.2.1 Object.assign(target, ...sources)
891
static bool obj_assign(JSContext* cx, unsigned argc, Value* vp) {
892
CallArgs args = CallArgsFromVp(argc, vp);
893
894
// Step 1.
895
RootedObject to(cx, ToObject(cx, args.get(0)));
896
if (!to) {
897
return false;
898
}
899
900
// Note: step 2 is implicit. If there are 0 arguments, ToObject throws. If
901
// there's 1 argument, the loop below is a no-op.
902
903
// Step 4.
904
RootedObject from(cx);
905
for (size_t i = 1; i < args.length(); i++) {
906
// Step 4.a.
907
if (args[i].isNullOrUndefined()) {
908
continue;
909
}
910
911
// Step 4.b.i.
912
from = ToObject(cx, args[i]);
913
if (!from) {
914
return false;
915
}
916
917
// Steps 4.b.ii, 4.c.
918
if (!JS_AssignObject(cx, to, from)) {
919
return false;
920
}
921
}
922
923
// Step 5.
924
args.rval().setObject(*to);
925
return true;
926
}
927
928
/* ES5 15.2.4.6. */
929
static bool obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
930
CallArgs args = CallArgsFromVp(argc, vp);
931
932
/* Step 1. */
933
if (args.length() < 1 || !args[0].isObject()) {
934
args.rval().setBoolean(false);
935
return true;
936
}
937
938
/* Step 2. */
939
RootedObject obj(cx, ToObject(cx, args.thisv()));
940
if (!obj) {
941
return false;
942
}
943
944
/* Step 3. */
945
bool isPrototype;
946
if (!IsPrototypeOf(cx, obj, &args[0].toObject(), &isPrototype)) {
947
return false;
948
}
949
args.rval().setBoolean(isPrototype);
950
return true;
951
}
952
953
PlainObject* js::ObjectCreateImpl(JSContext* cx, HandleObject proto,
954
NewObjectKind newKind,
955
HandleObjectGroup group) {
956
// Give the new object a small number of fixed slots, like we do for empty
957
// object literals ({}).
958
gc::AllocKind allocKind = GuessObjectGCKind(0);
959
960
if (!proto) {
961
// Object.create(null) is common, optimize it by using an allocation
962
// site specific ObjectGroup. Because GetCallerInitGroup is pretty
963
// slow, the caller can pass in the group if it's known and we use that
964
// instead.
965
RootedObjectGroup ngroup(cx, group);
966
if (!ngroup) {
967
ngroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Null);
968
if (!ngroup) {
969
return nullptr;
970
}
971
}
972
973
MOZ_ASSERT(!ngroup->proto().toObjectOrNull());
974
975
return NewObjectWithGroup<PlainObject>(cx, ngroup, allocKind, newKind);
976
}
977
978
return NewObjectWithGivenProto<PlainObject>(cx, proto, allocKind, newKind);
979
}
980
981
PlainObject* js::ObjectCreateWithTemplate(JSContext* cx,
982
HandlePlainObject templateObj) {
983
RootedObject proto(cx, templateObj->staticPrototype());
984
RootedObjectGroup group(cx, templateObj->group());
985
return ObjectCreateImpl(cx, proto, GenericObject, group);
986
}
987
988
// ES 2017 draft 19.1.2.3.1
989
static bool ObjectDefineProperties(JSContext* cx, HandleObject obj,
990
HandleValue properties,
991
bool* failedOnWindowProxy) {
992
// Step 1. implicit
993
// Step 2.
994
RootedObject props(cx, ToObject(cx, properties));
995
if (!props) {
996
return false;
997
}
998
999
// Step 3.
1000
RootedIdVector keys(cx);
1001
if (!GetPropertyKeys(
1002
cx, props, JSITER_OWNONLY | JSITER_SYMBOLS | JSITER_HIDDEN, &keys)) {
1003
return false;
1004
}
1005
1006
RootedId nextKey(cx);
1007
Rooted<PropertyDescriptor> desc(cx);
1008
RootedValue descObj(cx);
1009
1010
// Step 4.
1011
Rooted<PropertyDescriptorVector> descriptors(cx,
1012
PropertyDescriptorVector(cx));
1013
RootedIdVector descriptorKeys(cx);
1014
1015
// Step 5.
1016
for (size_t i = 0, len = keys.length(); i < len; i++) {
1017
nextKey = keys[i];
1018
1019
// Step 5.a.
1020
if (!GetOwnPropertyDescriptor(cx, props, nextKey, &desc)) {
1021
return false;
1022
}
1023
1024
// Step 5.b.
1025
if (desc.object() && desc.enumerable()) {
1026
if (!GetProperty(cx, props, props, nextKey, &descObj) ||
1027
!ToPropertyDescriptor(cx, descObj, true, &desc) ||
1028
!descriptors.append(desc) || !descriptorKeys.append(nextKey)) {
1029
return false;
1030
}
1031
}
1032
}
1033
1034
// Step 6.
1035
*failedOnWindowProxy = false;
1036
for (size_t i = 0, len = descriptors.length(); i < len; i++) {
1037
ObjectOpResult result;
1038
if (!DefineProperty(cx, obj, descriptorKeys[i], descriptors[i], result)) {
1039
return false;
1040
}
1041
1042
if (!result.ok()) {
1043
if (result.failureCode() == JSMSG_CANT_DEFINE_WINDOW_NC) {
1044
*failedOnWindowProxy = true;
1045
} else if (!result.checkStrict(cx, obj, descriptorKeys[i])) {
1046
return false;
1047
}
1048
}
1049
}
1050
1051
return true;
1052
}
1053
1054
// ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
1055
bool js::obj_create(JSContext* cx, unsigned argc, Value* vp) {
1056
CallArgs args = CallArgsFromVp(argc, vp);
1057
1058
// Step 1.
1059
if (!args.requireAtLeast(cx, "Object.create", 1)) {
1060
return false;
1061
}
1062
1063
if (!args[0].isObjectOrNull()) {
1064
UniqueChars bytes =
1065
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args[0], nullptr);
1066
if (!bytes) {
1067
return false;
1068
}
1069
1070
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1071
JSMSG_UNEXPECTED_TYPE, bytes.get(),
1072
"not an object or null");
1073
return false;
1074
}
1075
1076
// Step 2.
1077
RootedObject proto(cx, args[0].toObjectOrNull());
1078
RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
1079
if (!obj) {
1080
return false;
1081
}
1082
1083
// Step 3.
1084
if (args.hasDefined(1)) {
1085
// we can't ever end up with failures to define on a WindowProxy
1086
// here, because "obj" is never a WindowProxy.
1087
bool failedOnWindowProxy = false;
1088
if (!ObjectDefineProperties(cx, obj, args[1], &failedOnWindowProxy)) {
1089
return false;
1090
}
1091
MOZ_ASSERT(!failedOnWindowProxy, "How did we get a WindowProxy here?");
1092
}
1093
1094
// Step 4.
1095
args.rval().setObject(*obj);
1096
return true;
1097
}
1098
1099
// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1100
// 6.2.4.4 FromPropertyDescriptor ( Desc )
1101
static bool FromPropertyDescriptorToArray(JSContext* cx,
1102
Handle<PropertyDescriptor> desc,
1103
MutableHandleValue vp) {
1104
// Step 1.
1105
if (!desc.object()) {
1106
vp.setUndefined();
1107
return true;
1108
}
1109
1110
// Steps 2-11.
1111
// Retrieve all property descriptor fields and place them into the result
1112
// array. The actual return object is created in self-hosted code for
1113
// performance reasons.
1114
1115
int32_t attrsAndKind = 0;
1116
if (desc.enumerable()) {
1117
attrsAndKind |= ATTR_ENUMERABLE;
1118
}
1119
if (desc.configurable()) {
1120
attrsAndKind |= ATTR_CONFIGURABLE;
1121
}
1122
if (!desc.isAccessorDescriptor()) {
1123
if (desc.writable()) {
1124
attrsAndKind |= ATTR_WRITABLE;
1125
}
1126
attrsAndKind |= DATA_DESCRIPTOR_KIND;
1127
} else {
1128
attrsAndKind |= ACCESSOR_DESCRIPTOR_KIND;
1129
}
1130
1131
RootedArrayObject result(cx);
1132
if (!desc.isAccessorDescriptor()) {
1133
result = NewDenseFullyAllocatedArray(cx, 2);
1134
if (!result) {
1135
return false;
1136
}
1137
result->setDenseInitializedLength(2);
1138
1139
result->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX,
1140
Int32Value(attrsAndKind));
1141
result->initDenseElement(PROP_DESC_VALUE_INDEX, desc.value());
1142
} else {
1143
result = NewDenseFullyAllocatedArray(cx, 3);
1144
if (!result) {
1145
return false;
1146
}
1147
result->setDenseInitializedLength(3);
1148
1149
result->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX,
1150
Int32Value(attrsAndKind));
1151
1152
if (JSObject* get = desc.getterObject()) {
1153
result->initDenseElement(PROP_DESC_GETTER_INDEX, ObjectValue(*get));
1154
} else {
1155
result->initDenseElement(PROP_DESC_GETTER_INDEX, UndefinedValue());
1156
}
1157
1158
if (JSObject* set = desc.setterObject()) {
1159
result->initDenseElement(PROP_DESC_SETTER_INDEX, ObjectValue(*set));
1160
} else {
1161
result->initDenseElement(PROP_DESC_SETTER_INDEX, UndefinedValue());
1162
}
1163
}
1164
1165
vp.setObject(*result);
1166
return true;
1167
}
1168
1169
// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1170
// 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P )
1171
bool js::GetOwnPropertyDescriptorToArray(JSContext* cx, unsigned argc,
1172
Value* vp) {
1173
CallArgs args = CallArgsFromVp(argc, vp);
1174
MOZ_ASSERT(args.length() == 2);
1175
1176
// Step 1.
1177
RootedObject obj(cx, ToObject(cx, args[0]));
1178
if (!obj) {
1179
return false;
1180
}
1181
1182
// Step 2.
1183
RootedId id(cx);
1184
if (!ToPropertyKey(cx, args[1], &id)) {
1185
return false;
1186
}
1187
1188
// Step 3.
1189
Rooted<PropertyDescriptor> desc(cx);
1190
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
1191
return false;
1192
}
1193
1194
// [[GetOwnProperty]] is spec'ed to always return a complete property
1195
// descriptor record (ES2017, 6.1.7.3, invariants of [[GetOwnProperty]]).
1196
desc.assertCompleteIfFound();
1197
1198
// Step 4.
1199
return FromPropertyDescriptorToArray(cx, desc, args.rval());
1200
}
1201
1202
static bool NewValuePair(JSContext* cx, HandleValue val1, HandleValue val2,
1203
MutableHandleValue rval) {
1204
ArrayObject* array = NewDenseFullyAllocatedArray(cx, 2);
1205
if (!array) {
1206
return false;
1207
}
1208
1209
array->setDenseInitializedLength(2);
1210
array->initDenseElement(0, val1);
1211
array->initDenseElement(1, val2);
1212
1213
rval.setObject(*array);
1214
return true;
1215
}
1216
1217
enum class EnumerableOwnPropertiesKind { Keys, Values, KeysAndValues, Names };
1218
1219
static bool HasEnumerableStringNonDataProperties(NativeObject* obj) {
1220
// We also check for enumerability and symbol properties, so uninteresting
1221
// non-data properties like |array.length| don't let us fall into the slow
1222
// path.
1223
for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
1224
Shape* shape = &r.front();
1225
if (!shape->isDataProperty() && shape->enumerable() &&
1226
!JSID_IS_SYMBOL(shape->propid())) {
1227
return true;
1228
}
1229
}
1230
return false;
1231
}
1232
1233
template <EnumerableOwnPropertiesKind kind>
1234
static bool TryEnumerableOwnPropertiesNative(JSContext* cx, HandleObject obj,
1235
MutableHandleValue rval,
1236
bool* optimized) {
1237
*optimized = false;
1238
1239
// Use the fast path if |obj| has neither extra indexed properties nor a
1240
// newEnumerate hook. String objects need to be special-cased, because
1241
// they're only marked as indexed after their enumerate hook ran. And
1242
// because their enumerate hook is slowish, it's more performant to
1243
// exclude them directly instead of executing the hook first.
1244
if (!obj->isNative() || obj->as<NativeObject>().isIndexed() ||
1245
obj->getClass()->getNewEnumerate() || obj->is<StringObject>()) {
1246
return true;
1247
}
1248
1249
HandleNativeObject nobj = obj.as<NativeObject>();
1250
1251
// Resolve lazy properties on |nobj|.
1252
if (JSEnumerateOp enumerate = nobj->getClass()->getEnumerate()) {
1253
if (!enumerate(cx, nobj)) {
1254
return false;
1255
}
1256
1257
// Ensure no extra indexed properties were added through enumerate().
1258
if (nobj->isIndexed()) {
1259
return true;
1260
}
1261
}
1262
1263
*optimized = true;
1264
1265
RootedValueVector properties(cx);
1266
RootedValue key(cx);
1267
RootedValue value(cx);
1268
1269
// We have ensured |nobj| contains no extra indexed properties, so the
1270
// only indexed properties we need to handle here are dense and typed
1271
// array elements.
1272
1273
for (uint32_t i = 0, len = nobj->getDenseInitializedLength(); i < len; i++) {
1274
value.set(nobj->getDenseElement(i));
1275
if (value.isMagic(JS_ELEMENTS_HOLE)) {
1276
continue;
1277
}
1278
1279
JSString* str;
1280
if (kind != EnumerableOwnPropertiesKind::Values) {
1281
static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= JSID_INT_MAX,
1282
"dense elements don't exceed JSID_INT_MAX");
1283
str = Int32ToString<CanGC>(cx, i);
1284
if (!str) {
1285
return false;
1286
}
1287
}
1288
1289
if (kind == EnumerableOwnPropertiesKind::Keys ||
1290
kind == EnumerableOwnPropertiesKind::Names) {
1291
value.setString(str);
1292
} else if (kind == EnumerableOwnPropertiesKind::KeysAndValues) {
1293
key.setString(str);
1294
if (!NewValuePair(cx, key, value, &value)) {
1295
return false;
1296
}
1297
}
1298
1299
if (!properties.append(value)) {
1300
return false;
1301
}
1302
}
1303
1304
if (obj->is<TypedArrayObject>()) {
1305
Handle<TypedArrayObject*> tobj = obj.as<TypedArrayObject>();
1306
uint32_t len = tobj->length();
1307
1308
// Fail early if the typed array contains too many elements for a
1309
// dense array, because we likely OOM anyway when trying to allocate
1310
// more than 2GB for the properties vector. This also means we don't
1311
// need to handle indices greater than MAX_INT32 in the loop below.
1312
if (len > NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
1313
ReportOutOfMemory(cx);
1314
return false;
1315
}
1316
1317
MOZ_ASSERT(properties.empty(), "typed arrays cannot have dense elements");
1318
if (!properties.resize(len)) {
1319
return false;
1320
}
1321
1322
for (uint32_t i = 0; i < len; i++) {
1323
JSString* str;
1324
if (kind != EnumerableOwnPropertiesKind::Values) {
1325
static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= JSID_INT_MAX,
1326
"dense elements don't exceed JSID_INT_MAX");
1327
str = Int32ToString<CanGC>(cx, i);
1328
if (!str) {
1329
return false;
1330
}
1331
}
1332
1333
if (kind == EnumerableOwnPropertiesKind::Keys ||
1334
kind == EnumerableOwnPropertiesKind::Names) {
1335
value.setString(str);
1336
} else if (kind == EnumerableOwnPropertiesKind::Values) {
1337
if (!tobj->getElement<CanGC>(cx, i, &value)) {
1338
return false;
1339
}
1340
} else {
1341
key.setString(str);
1342
if (!tobj->getElement<CanGC>(cx, i, &value)) {
1343
return false;
1344
}
1345
if (!NewValuePair(cx, key, value, &value)) {
1346
return false;
1347
}
1348
}
1349
1350
properties[i].set(value);
1351
}
1352
}
1353
1354
// Up to this point no side-effects through accessor properties are
1355
// possible which could have replaced |obj| with a non-native object.
1356
MOZ_ASSERT(obj->isNative());
1357
1358
if (kind == EnumerableOwnPropertiesKind::Keys ||
1359
kind == EnumerableOwnPropertiesKind::Names ||
1360
!HasEnumerableStringNonDataProperties(nobj)) {
1361
// If |kind == Values| or |kind == KeysAndValues|:
1362
// All enumerable properties with string property keys are data
1363
// properties. This allows us to collect the property values while
1364
// iterating over the shape hierarchy without worrying over accessors
1365
// modifying any state.
1366
1367
size_t elements = properties.length();
1368
constexpr bool onlyEnumerable = kind != EnumerableOwnPropertiesKind::Names;
1369
constexpr AllowGC allowGC =
1370
kind != EnumerableOwnPropertiesKind::KeysAndValues ? AllowGC::NoGC
1371
: AllowGC::CanGC;
1372
mozilla::MaybeOneOf<Shape::Range<NoGC>, Shape::Range<CanGC>> m;
1373
if (allowGC == AllowGC::NoGC) {
1374
m.construct<Shape::Range<NoGC>>(nobj->lastProperty());
1375
} else {
1376
m.construct<Shape::Range<CanGC>>(cx, nobj->lastProperty());
1377
}
1378
for (Shape::Range<allowGC>& r = m.ref<Shape::Range<allowGC>>(); !r.empty();
1379
r.popFront()) {
1380
Shape* shape = &r.front();
1381
jsid id = shape->propid();
1382
if ((onlyEnumerable && !shape->enumerable()) || JSID_IS_SYMBOL(id)) {
1383
continue;
1384
}
1385
MOZ_ASSERT(!JSID_IS_INT(id), "Unexpected indexed property");
1386
MOZ_ASSERT_IF(kind == EnumerableOwnPropertiesKind::Values ||
1387
kind == EnumerableOwnPropertiesKind::KeysAndValues,
1388
shape->isDataProperty());
1389
1390
if (kind == EnumerableOwnPropertiesKind::Keys ||
1391
kind == EnumerableOwnPropertiesKind::Names) {
1392
value.setString(JSID_TO_STRING(id));
1393
} else if (kind == EnumerableOwnPropertiesKind::Values) {
1394
value.set(nobj->getSlot(shape->slot()));
1395
} else {
1396
key.setString(JSID_TO_STRING(id));
1397
value.set(nobj->getSlot(shape->slot()));
1398
if (!NewValuePair(cx, key, value, &value)) {
1399
return false;
1400
}
1401
}
1402
1403
if (!properties.append(value)) {
1404
return false;
1405
}
1406
}
1407
1408
// The (non-indexed) properties were visited in reverse iteration order,
1409
// call std::reverse() to ensure they appear in iteration order.
1410
std::reverse(properties.begin() + elements, properties.end());
1411
} else {
1412
MOZ_ASSERT(kind == EnumerableOwnPropertiesKind::Values ||
1413
kind == EnumerableOwnPropertiesKind::KeysAndValues);
1414
1415
// Get a list of all |obj| shapes. As long as obj->lastProperty()
1416
// is equal to |objShape|, we can use this to speed up both the
1417
// enumerability check and GetProperty.
1418
using ShapeVector = GCVector<Shape*, 8>;
1419
Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
1420
1421
// Collect all non-symbol properties.
1422
RootedShape objShape(cx, nobj->lastProperty());
1423
for (Shape::Range<NoGC> r(objShape); !r.empty(); r.popFront()) {
1424
Shape* shape = &r.front();
1425
if (JSID_IS_SYMBOL(shape->propid())) {
1426
continue;
1427
}
1428
MOZ_ASSERT(!JSID_IS_INT(shape->propid()), "Unexpected indexed property");
1429
1430
if (!shapes.append(shape)) {
1431
return false;
1432
}
1433
}
1434
1435
RootedId id(cx);
1436
for (size_t i = shapes.length(); i > 0; i--) {
1437
Shape* shape = shapes[i - 1];
1438
id = shape->propid();
1439
1440
// Ensure |obj| is still native: a getter might have been swapped with a
1441
// non-native object.
1442
if (obj->isNative() &&
1443
obj->as<NativeObject>().lastProperty() == objShape &&
1444
shape->isDataProperty()) {
1445
if (!shape->enumerable()) {
1446
continue;
1447
}
1448
value = obj->as<NativeObject>().getSlot(shape->slot());
1449
} else {
1450
// |obj| changed shape or the property is not a data property,
1451
// so we have to do the slower enumerability check and
1452
// GetProperty.
1453
bool enumerable;
1454
if (!PropertyIsEnumerable(cx, obj, id, &enumerable)) {
1455
return false;
1456
}
1457
if (!enumerable) {
1458
continue;
1459
}
1460
if (!GetProperty(cx, obj, obj, id, &value)) {
1461
return false;
1462
}
1463
}
1464
1465
if (kind == EnumerableOwnPropertiesKind::KeysAndValues) {
1466
key.setString(JSID_TO_STRING(id));
1467
if (!NewValuePair(cx, key, value, &value)) {
1468
return false;
1469
}
1470
}
1471
1472
if (!properties.append(value)) {
1473
return false;
1474
}
1475
}
1476
}
1477
1478
JSObject* array =
1479
NewDenseCopiedArray(cx, properties.length(), properties.begin());
1480
if (!array) {
1481
return false;
1482
}
1483
1484
rval.setObject(*array);
1485
return true;
1486
}
1487
1488
// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1489
// 7.3.21 EnumerableOwnProperties ( O, kind )
1490
template <EnumerableOwnPropertiesKind kind>
1491
static bool EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args) {
1492
static_assert(kind == EnumerableOwnPropertiesKind::Values ||
1493
kind == EnumerableOwnPropertiesKind::KeysAndValues,
1494
"Only implemented for Object.keys and Object.entries");
1495
1496
// Step 1. (Step 1 of Object.{keys,values,entries}, really.)
1497
RootedObject obj(cx, ToObject(cx, args.get(0)));
1498
if (!obj) {
1499
return false;
1500
}
1501
1502
bool optimized;
1503
if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
1504
&optimized)) {
1505
return false;
1506
}
1507
if (optimized) {
1508
return true;
1509
}
1510
1511
// Typed arrays are always handled in the fast path.
1512
MOZ_ASSERT(!obj->is<TypedArrayObject>());
1513
1514
// Step 2.
1515
RootedIdVector ids(cx);
1516
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &ids)) {
1517
return false;
1518
}
1519
1520
// Step 3.
1521
RootedValueVector properties(cx);
1522
size_t len = ids.length();
1523
if (!properties.resize(len)) {
1524
return false;
1525
}
1526
1527
RootedId id(cx);
1528
RootedValue key(cx);
1529
RootedValue value(cx);
1530
RootedShape shape(cx);
1531
Rooted<PropertyDescriptor> desc(cx);
1532
// Step 4.
1533
size_t out = 0;
1534
for (size_t i = 0; i < len; i++) {
1535
id = ids[i];
1536
1537
// Step 4.a. (Symbols were filtered out in step 2.)
1538
MOZ_ASSERT(!JSID_IS_SYMBOL(id));
1539
1540
if (kind != EnumerableOwnPropertiesKind::Values) {
1541
if (!IdToStringOrSymbol(cx, id, &key)) {
1542
return false;
1543
}
1544
}
1545
1546
// Step 4.a.i.
1547
if (obj->is<NativeObject>()) {
1548
HandleNativeObject nobj = obj.as<NativeObject>();
1549
if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) {
1550
if (!nobj->getDenseOrTypedArrayElement<CanGC>(cx, JSID_TO_INT(id),
1551
&value)) {
1552
return false;
1553
}
1554
} else {
1555
shape = nobj->lookup(cx, id);
1556
if (!shape || !shape->enumerable()) {
1557
continue;
1558
}
1559
if (!shape->isAccessorShape()) {
1560
if (!NativeGetExistingProperty(cx, nobj, nobj, shape, &value)) {
1561
return false;
1562
}
1563
} else if (!GetProperty(cx, obj, obj, id, &value)) {
1564
return false;
1565
}
1566
}
1567
} else {
1568
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
1569
return false;
1570
}
1571
1572
// Step 4.a.ii. (inverted.)
1573
if (!desc.object() || !desc.enumerable()) {
1574
continue;
1575
}
1576
1577
// Step 4.a.ii.1.
1578
// (Omitted because Object.keys doesn't use this implementation.)
1579
1580
// Step 4.a.ii.2.a.
1581
if (!GetProperty(cx, obj, obj, id, &value)) {
1582
return false;
1583
}
1584
}
1585
1586
// Steps 4.a.ii.2.b-c.
1587
if (kind == EnumerableOwnPropertiesKind::Values) {
1588
properties[out++].set(value);
1589
} else if (!NewValuePair(cx, key, value, properties[out++])) {
1590
return false;
1591
}
1592
}
1593
1594
// Step 5.
1595
// (Implemented in step 2.)
1596
1597
// Step 3 of Object.{keys,values,entries}
1598
JSObject* aobj = NewDenseCopiedArray(cx, out, properties.begin());
1599
if (!aobj) {
1600
return false;
1601
}
1602
1603
args.rval().setObject(*aobj);
1604
return true;
1605
}
1606
1607
// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1608
// 19.1.2.16 Object.keys ( O )
1609
static bool obj_keys(JSContext* cx, unsigned argc, Value* vp) {
1610
CallArgs args = CallArgsFromVp(argc, vp);
1611
1612
// Step 1.
1613
RootedObject obj(cx, ToObject(cx, args.get(0)));
1614
if (!obj) {
1615
return false;
1616
}
1617
1618
bool optimized;
1619
static constexpr EnumerableOwnPropertiesKind kind =
1620
EnumerableOwnPropertiesKind::Keys;
1621
if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
1622
&optimized)) {
1623
return false;
1624
}
1625
if (optimized) {
1626
return true;
1627
}
1628
1629
// Steps 2-3.
1630
return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY, args.rval());
1631
}
1632
1633
// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1634
// 19.1.2.21 Object.values ( O )
1635
static bool obj_values(JSContext* cx, unsigned argc, Value* vp) {
1636
CallArgs args = CallArgsFromVp(argc, vp);
1637
1638
// Steps 1-3.
1639
return EnumerableOwnProperties<EnumerableOwnPropertiesKind::Values>(cx, args);
1640
}
1641
1642
// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1643
// 19.1.2.5 Object.entries ( O )
1644
static bool obj_entries(JSContext* cx, unsigned argc, Value* vp) {
1645
CallArgs args = CallArgsFromVp(argc, vp);
1646
1647
// Steps 1-3.
1648
return EnumerableOwnProperties<EnumerableOwnPropertiesKind::KeysAndValues>(
1649
cx, args);
1650
}
1651
1652
/* ES6 draft 15.2.3.16 */
1653
bool js::obj_is(JSContext* cx, unsigned argc, Value* vp) {
1654
CallArgs args = CallArgsFromVp(argc, vp);
1655
1656
bool same;
1657
if (!SameValue(cx, args.get(0), args.get(1), &same)) {
1658
return false;
1659
}
1660
1661
args.rval().setBoolean(same);
1662
return true;
1663
}
1664
1665
bool js::IdToStringOrSymbol(JSContext* cx, HandleId id,
1666
MutableHandleValue result) {
1667
if (JSID_IS_INT(id)) {
1668
JSString* str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
1669
if (!str) {
1670
return false;
1671
}
1672
result.setString(str);
1673
} else if (JSID_IS_ATOM(id)) {
1674
result.setString(JSID_TO_STRING(id));
1675
} else {
1676
result.setSymbol(JSID_TO_SYMBOL(id));
1677
}
1678
return true;
1679
}
1680
1681
// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1682
// 19.1.2.10.1 Runtime Semantics: GetOwnPropertyKeys ( O, Type )
1683
bool js::GetOwnPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags,
1684
MutableHandleValue rval) {
1685
// Step 1 (Performed in caller).
1686
1687
// Steps 2-4.
1688
RootedIdVector keys(cx);
1689
if (!GetPropertyKeys(cx, obj, flags, &keys)) {
1690
return false;
1691
}
1692
1693
// Step 5 (Inlined CreateArrayFromList).
1694
RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, keys.length()));
1695
if (!array) {
1696
return false;
1697
}
1698
1699
array->ensureDenseInitializedLength(cx, 0, keys.length());
1700
1701
RootedValue val(cx);
1702
for (size_t i = 0, len = keys.length(); i < len; i++) {
1703
MOZ_ASSERT_IF(JSID_IS_SYMBOL(keys[i]), flags & JSITER_SYMBOLS);
1704
MOZ_ASSERT_IF(!JSID_IS_SYMBOL(keys[i]), !(flags & JSITER_SYMBOLSONLY));
1705
if (!IdToStringOrSymbol(cx, keys[i], &val)) {
1706
return false;
1707
}
1708
array->initDenseElement(i, val);
1709
}
1710
1711
rval.setObject(*array);
1712
return true;
1713
}
1714
1715
// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1716
// 19.1.2.9 Object.getOwnPropertyNames ( O )
1717
bool js::obj_getOwnPropertyNames(JSContext* cx, unsigned argc, Value* vp) {
1718
CallArgs args = CallArgsFromVp(argc, vp);
1719
1720
RootedObject obj(cx, ToObject(cx, args.get(0)));
1721
if (!obj) {
1722
return false;
1723
}
1724
1725
bool optimized;
1726
static constexpr EnumerableOwnPropertiesKind kind =
1727
EnumerableOwnPropertiesKind::Names;
1728
if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
1729
&optimized)) {
1730
return false;
1731
}
1732
if (optimized) {
1733
return true;
1734
}
1735
1736
return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN,
1737
args.rval());
1738
}
1739
1740
// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1741
// 19.1.2.10 Object.getOwnPropertySymbols ( O )
1742
static bool obj_getOwnPropertySymbols(JSContext* cx, unsigned argc, Value* vp) {
1743
CallArgs args = CallArgsFromVp(argc, vp);
1744
1745
RootedObject