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