js/src/builtin/Object.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:5470f0fe9ca0
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
13 #include "frontend/BytecodeCompiler.h"
14 #include "vm/StringBuffer.h"
15
16 #include "jsobjinlines.h"
17
18 #include "vm/ObjectImpl-inl.h"
19
20 using namespace js;
21 using namespace js::types;
22
23 using js::frontend::IsIdentifier;
24 using mozilla::ArrayLength;
25
26
27 bool
28 js::obj_construct(JSContext *cx, unsigned argc, Value *vp)
29 {
30 CallArgs args = CallArgsFromVp(argc, vp);
31
32 RootedObject obj(cx, nullptr);
33 if (args.length() > 0 && !args[0].isNullOrUndefined()) {
34 obj = ToObject(cx, args[0]);
35 if (!obj)
36 return false;
37 } else {
38 /* Make an object whether this was called with 'new' or not. */
39 if (!NewObjectScriptedCall(cx, &obj))
40 return false;
41 }
42
43 args.rval().setObject(*obj);
44 return true;
45 }
46
47 /* ES5 15.2.4.7. */
48 static bool
49 obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
50 {
51 CallArgs args = CallArgsFromVp(argc, vp);
52
53 /* Step 1. */
54 RootedId id(cx);
55 if (!ValueToId<CanGC>(cx, args.get(0), &id))
56 return false;
57
58 /* Step 2. */
59 RootedObject obj(cx, ToObject(cx, args.thisv()));
60 if (!obj)
61 return false;
62
63 /* Steps 3. */
64 RootedObject pobj(cx);
65 RootedShape prop(cx);
66 if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &prop))
67 return false;
68
69 /* Step 4. */
70 if (!prop) {
71 args.rval().setBoolean(false);
72 return true;
73 }
74
75 if (pobj != obj) {
76 args.rval().setBoolean(false);
77 return true;
78 }
79
80 /* Step 5. */
81 unsigned attrs;
82 if (!JSObject::getGenericAttributes(cx, pobj, id, &attrs))
83 return false;
84
85 args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
86 return true;
87 }
88
89 #if JS_HAS_TOSOURCE
90 static bool
91 obj_toSource(JSContext *cx, unsigned argc, Value *vp)
92 {
93 CallArgs args = CallArgsFromVp(argc, vp);
94 JS_CHECK_RECURSION(cx, return false);
95
96 RootedObject obj(cx, ToObject(cx, args.thisv()));
97 if (!obj)
98 return false;
99
100 JSString *str = ObjectToSource(cx, obj);
101 if (!str)
102 return false;
103
104 args.rval().setString(str);
105 return true;
106 }
107
108 JSString *
109 js::ObjectToSource(JSContext *cx, HandleObject obj)
110 {
111 /* If outermost, we need parentheses to be an expression, not a block. */
112 bool outermost = (cx->cycleDetectorSet.count() == 0);
113
114 AutoCycleDetector detector(cx, obj);
115 if (!detector.init())
116 return nullptr;
117 if (detector.foundCycle())
118 return js_NewStringCopyZ<CanGC>(cx, "{}");
119
120 StringBuffer buf(cx);
121 if (outermost && !buf.append('('))
122 return nullptr;
123 if (!buf.append('{'))
124 return nullptr;
125
126 RootedValue v0(cx), v1(cx);
127 MutableHandleValue val[2] = {&v0, &v1};
128
129 RootedString str0(cx), str1(cx);
130 MutableHandleString gsop[2] = {&str0, &str1};
131
132 AutoIdVector idv(cx);
133 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &idv))
134 return nullptr;
135
136 bool comma = false;
137 for (size_t i = 0; i < idv.length(); ++i) {
138 RootedId id(cx, idv[i]);
139 RootedObject obj2(cx);
140 RootedShape shape(cx);
141 if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape))
142 return nullptr;
143
144 /* Decide early whether we prefer get/set or old getter/setter syntax. */
145 int valcnt = 0;
146 if (shape) {
147 bool doGet = true;
148 if (obj2->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) {
149 unsigned attrs = shape->attributes();
150 if (attrs & JSPROP_GETTER) {
151 doGet = false;
152 val[valcnt].set(shape->getterValue());
153 gsop[valcnt].set(cx->names().get);
154 valcnt++;
155 }
156 if (attrs & JSPROP_SETTER) {
157 doGet = false;
158 val[valcnt].set(shape->setterValue());
159 gsop[valcnt].set(cx->names().set);
160 valcnt++;
161 }
162 }
163 if (doGet) {
164 valcnt = 1;
165 gsop[0].set(nullptr);
166 if (!JSObject::getGeneric(cx, obj, obj, id, val[0]))
167 return nullptr;
168 }
169 }
170
171 /* Convert id to a linear string. */
172 RootedValue idv(cx, IdToValue(id));
173 JSString *s = ToString<CanGC>(cx, idv);
174 if (!s)
175 return nullptr;
176 Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx));
177 if (!idstr)
178 return nullptr;
179
180 /*
181 * If id is a string that's not an identifier, or if it's a negative
182 * integer, then it must be quoted.
183 */
184 if (JSID_IS_ATOM(id)
185 ? !IsIdentifier(idstr)
186 : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0))
187 {
188 s = js_QuoteString(cx, idstr, jschar('\''));
189 if (!s || !(idstr = s->ensureLinear(cx)))
190 return nullptr;
191 }
192
193 for (int j = 0; j < valcnt; j++) {
194 /*
195 * Censor an accessor descriptor getter or setter part if it's
196 * undefined.
197 */
198 if (gsop[j] && val[j].isUndefined())
199 continue;
200
201 /* Convert val[j] to its canonical source form. */
202 RootedString valstr(cx, ValueToSource(cx, val[j]));
203 if (!valstr)
204 return nullptr;
205 const jschar *vchars = valstr->getChars(cx);
206 if (!vchars)
207 return nullptr;
208 size_t vlength = valstr->length();
209
210 /*
211 * Remove '(function ' from the beginning of valstr and ')' from the
212 * end so that we can put "get" in front of the function definition.
213 */
214 if (gsop[j] && IsFunctionObject(val[j])) {
215 const jschar *start = vchars;
216 const jschar *end = vchars + vlength;
217
218 uint8_t parenChomp = 0;
219 if (vchars[0] == '(') {
220 vchars++;
221 parenChomp = 1;
222 }
223
224 /* Try to jump "function" keyword. */
225 if (vchars)
226 vchars = js_strchr_limit(vchars, ' ', end);
227
228 /*
229 * Jump over the function's name: it can't be encoded as part
230 * of an ECMA getter or setter.
231 */
232 if (vchars)
233 vchars = js_strchr_limit(vchars, '(', end);
234
235 if (vchars) {
236 if (*vchars == ' ')
237 vchars++;
238 vlength = end - vchars - parenChomp;
239 } else {
240 gsop[j].set(nullptr);
241 vchars = start;
242 }
243 }
244
245 if (comma && !buf.append(", "))
246 return nullptr;
247 comma = true;
248
249 if (gsop[j])
250 if (!buf.append(gsop[j]) || !buf.append(' '))
251 return nullptr;
252
253 if (!buf.append(idstr))
254 return nullptr;
255 if (!buf.append(gsop[j] ? ' ' : ':'))
256 return nullptr;
257
258 if (!buf.append(vchars, vlength))
259 return nullptr;
260 }
261 }
262
263 if (!buf.append('}'))
264 return nullptr;
265 if (outermost && !buf.append(')'))
266 return nullptr;
267
268 return buf.finishString();
269 }
270 #endif /* JS_HAS_TOSOURCE */
271
272 JSString *
273 JS_BasicObjectToString(JSContext *cx, HandleObject obj)
274 {
275 // Some classes are really common, don't allocate new strings for them.
276 // The ordering below is based on the measurements in bug 966264.
277 if (obj->is<JSObject>())
278 return cx->names().objectObject;
279 if (obj->is<StringObject>())
280 return cx->names().objectString;
281 if (obj->is<ArrayObject>())
282 return cx->names().objectArray;
283 if (obj->is<JSFunction>())
284 return cx->names().objectFunction;
285 if (obj->is<NumberObject>())
286 return cx->names().objectNumber;
287
288 const char *className = JSObject::className(cx, obj);
289
290 if (strcmp(className, "Window") == 0)
291 return cx->names().objectWindow;
292
293 StringBuffer sb(cx);
294 if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
295 !sb.append("]"))
296 {
297 return nullptr;
298 }
299 return sb.finishString();
300 }
301
302 /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
303 static bool
304 obj_toString(JSContext *cx, unsigned argc, Value *vp)
305 {
306 CallArgs args = CallArgsFromVp(argc, vp);
307
308 /* Step 1. */
309 if (args.thisv().isUndefined()) {
310 args.rval().setString(cx->names().objectUndefined);
311 return true;
312 }
313
314 /* Step 2. */
315 if (args.thisv().isNull()) {
316 args.rval().setString(cx->names().objectNull);
317 return true;
318 }
319
320 /* Step 3. */
321 RootedObject obj(cx, ToObject(cx, args.thisv()));
322 if (!obj)
323 return false;
324
325 /* Steps 4-5. */
326 JSString *str = JS_BasicObjectToString(cx, obj);
327 if (!str)
328 return false;
329 args.rval().setString(str);
330 return true;
331 }
332
333 /* ES5 15.2.4.3. */
334 static bool
335 obj_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
336 {
337 JS_CHECK_RECURSION(cx, return false);
338
339 CallArgs args = CallArgsFromVp(argc, vp);
340
341 /* Step 1. */
342 RootedObject obj(cx, ToObject(cx, args.thisv()));
343 if (!obj)
344 return false;
345
346 /* Steps 2-4. */
347 RootedId id(cx, NameToId(cx->names().toString));
348 return obj->callMethod(cx, id, 0, nullptr, args.rval());
349 }
350
351 static bool
352 obj_valueOf(JSContext *cx, unsigned argc, Value *vp)
353 {
354 CallArgs args = CallArgsFromVp(argc, vp);
355 RootedObject obj(cx, ToObject(cx, args.thisv()));
356 if (!obj)
357 return false;
358 args.rval().setObject(*obj);
359 return true;
360 }
361
362 #if JS_OLD_GETTER_SETTER_METHODS
363
364 enum DefineType { GetterAccessor, SetterAccessor };
365
366 template<DefineType Type>
367 static bool
368 DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
369 {
370 CallArgs args = CallArgsFromVp(argc, vp);
371 if (!BoxNonStrictThis(cx, args))
372 return false;
373
374 if (args.length() < 2 || !js_IsCallable(args[1])) {
375 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
376 JSMSG_BAD_GETTER_OR_SETTER,
377 Type == GetterAccessor ? js_getter_str : js_setter_str);
378 return false;
379 }
380
381 RootedId id(cx);
382 if (!ValueToId<CanGC>(cx, args[0], &id))
383 return false;
384
385 RootedObject descObj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
386 if (!descObj)
387 return false;
388
389 JSAtomState &names = cx->names();
390 RootedValue trueVal(cx, BooleanValue(true));
391
392 /* enumerable: true */
393 if (!JSObject::defineProperty(cx, descObj, names.enumerable, trueVal))
394 return false;
395
396 /* configurable: true */
397 if (!JSObject::defineProperty(cx, descObj, names.configurable, trueVal))
398 return false;
399
400 /* enumerable: true */
401 PropertyName *acc = (Type == GetterAccessor) ? names.get : names.set;
402 RootedValue accessorVal(cx, args[1]);
403 if (!JSObject::defineProperty(cx, descObj, acc, accessorVal))
404 return false;
405
406 RootedObject thisObj(cx, &args.thisv().toObject());
407
408 bool dummy;
409 RootedValue descObjValue(cx, ObjectValue(*descObj));
410 if (!DefineOwnProperty(cx, thisObj, id, descObjValue, &dummy))
411 return false;
412
413 args.rval().setUndefined();
414 return true;
415 }
416
417 JS_FRIEND_API(bool)
418 js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp)
419 {
420 return DefineAccessor<GetterAccessor>(cx, argc, vp);
421 }
422
423 JS_FRIEND_API(bool)
424 js::obj_defineSetter(JSContext *cx, unsigned argc, Value *vp)
425 {
426 return DefineAccessor<SetterAccessor>(cx, argc, vp);
427 }
428
429 static bool
430 obj_lookupGetter(JSContext *cx, unsigned argc, Value *vp)
431 {
432 CallArgs args = CallArgsFromVp(argc, vp);
433
434 RootedId id(cx);
435 if (!ValueToId<CanGC>(cx, args.get(0), &id))
436 return false;
437 RootedObject obj(cx, ToObject(cx, args.thisv()));
438 if (!obj)
439 return false;
440 if (obj->is<ProxyObject>()) {
441 // The vanilla getter lookup code below requires that the object is
442 // native. Handle proxies separately.
443 args.rval().setUndefined();
444 Rooted<PropertyDescriptor> desc(cx);
445 if (!Proxy::getPropertyDescriptor(cx, obj, id, &desc))
446 return false;
447 if (desc.object() && desc.hasGetterObject() && desc.getterObject())
448 args.rval().setObject(*desc.getterObject());
449 return true;
450 }
451 RootedObject pobj(cx);
452 RootedShape shape(cx);
453 if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
454 return false;
455 args.rval().setUndefined();
456 if (shape) {
457 if (pobj->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) {
458 if (shape->hasGetterValue())
459 args.rval().set(shape->getterValue());
460 }
461 }
462 return true;
463 }
464
465 static bool
466 obj_lookupSetter(JSContext *cx, unsigned argc, Value *vp)
467 {
468 CallArgs args = CallArgsFromVp(argc, vp);
469
470 RootedId id(cx);
471 if (!ValueToId<CanGC>(cx, args.get(0), &id))
472 return false;
473 RootedObject obj(cx, ToObject(cx, args.thisv()));
474 if (!obj)
475 return false;
476 if (obj->is<ProxyObject>()) {
477 // The vanilla setter lookup code below requires that the object is
478 // native. Handle proxies separately.
479 args.rval().setUndefined();
480 Rooted<PropertyDescriptor> desc(cx);
481 if (!Proxy::getPropertyDescriptor(cx, obj, id, &desc))
482 return false;
483 if (desc.object() && desc.hasSetterObject() && desc.setterObject())
484 args.rval().setObject(*desc.setterObject());
485 return true;
486 }
487 RootedObject pobj(cx);
488 RootedShape shape(cx);
489 if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
490 return false;
491 args.rval().setUndefined();
492 if (shape) {
493 if (pobj->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) {
494 if (shape->hasSetterValue())
495 args.rval().set(shape->setterValue());
496 }
497 }
498 return true;
499 }
500 #endif /* JS_OLD_GETTER_SETTER_METHODS */
501
502 /* ES5 15.2.3.2. */
503 static bool
504 obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
505 {
506 CallArgs args = CallArgsFromVp(argc, vp);
507
508 /* Step 1. */
509 if (args.length() == 0) {
510 js_ReportMissingArg(cx, args.calleev(), 0);
511 return false;
512 }
513
514 if (args[0].isPrimitive()) {
515 RootedValue val(cx, args[0]);
516 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
517 if (!bytes)
518 return false;
519 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
520 JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
521 js_free(bytes);
522 return false;
523 }
524
525 /* Step 2. */
526
527 /*
528 * Implement [[Prototype]]-getting -- particularly across compartment
529 * boundaries -- by calling a cached __proto__ getter function.
530 */
531 InvokeArgs args2(cx);
532 if (!args2.init(0))
533 return false;
534 args2.setCallee(cx->global()->protoGetter());
535 args2.setThis(args[0]);
536 if (!Invoke(cx, args2))
537 return false;
538 args.rval().set(args2.rval());
539 return true;
540 }
541
542 static bool
543 obj_setPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
544 {
545 CallArgs args = CallArgsFromVp(argc, vp);
546
547 RootedObject setPrototypeOf(cx, &args.callee());
548 if (!GlobalObject::warnOnceAboutPrototypeMutation(cx, setPrototypeOf))
549 return false;
550
551 if (args.length() < 2) {
552 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
553 "Object.setPrototypeOf", "1", "");
554 return false;
555 }
556
557 /* Step 1-2. */
558 if (args[0].isNullOrUndefined()) {
559 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
560 args[0].isNull() ? "null" : "undefined", "object");
561 return false;
562 }
563
564 /* Step 3. */
565 if (!args[1].isObjectOrNull()) {
566 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
567 "Object.setPrototypeOf", "an object or null", InformalValueTypeName(args[1]));
568 return false;
569 }
570
571 /* Step 4. */
572 if (!args[0].isObject()) {
573 args.rval().set(args[0]);
574 return true;
575 }
576
577 /* Step 5-6. */
578 RootedObject obj(cx, &args[0].toObject());
579 RootedObject newProto(cx, args[1].toObjectOrNull());
580
581 bool success;
582 if (!JSObject::setProto(cx, obj, newProto, &success))
583 return false;
584
585 /* Step 7. */
586 if (!success) {
587 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OBJECT_NOT_EXTENSIBLE, "object");
588 return false;
589 }
590
591 /* Step 8. */
592 args.rval().set(args[0]);
593 return true;
594 }
595
596 #if JS_HAS_OBJ_WATCHPOINT
597
598 bool
599 js::WatchHandler(JSContext *cx, JSObject *obj_, jsid id_, JS::Value old,
600 JS::Value *nvp, void *closure)
601 {
602 RootedObject obj(cx, obj_);
603 RootedId id(cx, id_);
604
605 /* Avoid recursion on (obj, id) already being watched on cx. */
606 AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
607 if (resolving.alreadyStarted())
608 return true;
609
610 JSObject *callable = (JSObject *)closure;
611 Value argv[] = { IdToValue(id), old, *nvp };
612 RootedValue rv(cx);
613 if (!Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, &rv))
614 return false;
615
616 *nvp = rv;
617 return true;
618 }
619
620 static bool
621 obj_watch(JSContext *cx, unsigned argc, Value *vp)
622 {
623 CallArgs args = CallArgsFromVp(argc, vp);
624
625 RootedObject obj(cx, ToObject(cx, args.thisv()));
626 if (!obj)
627 return false;
628
629 if (!GlobalObject::warnOnceAboutWatch(cx, obj))
630 return false;
631
632 if (args.length() <= 1) {
633 js_ReportMissingArg(cx, args.calleev(), 1);
634 return false;
635 }
636
637 RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2));
638 if (!callable)
639 return false;
640
641 RootedId propid(cx);
642 if (!ValueToId<CanGC>(cx, args[0], &propid))
643 return false;
644
645 if (!JSObject::watch(cx, obj, propid, callable))
646 return false;
647
648 args.rval().setUndefined();
649 return true;
650 }
651
652 static bool
653 obj_unwatch(JSContext *cx, unsigned argc, Value *vp)
654 {
655 CallArgs args = CallArgsFromVp(argc, vp);
656
657 RootedObject obj(cx, ToObject(cx, args.thisv()));
658 if (!obj)
659 return false;
660
661 if (!GlobalObject::warnOnceAboutWatch(cx, obj))
662 return false;
663
664 RootedId id(cx);
665 if (args.length() != 0) {
666 if (!ValueToId<CanGC>(cx, args[0], &id))
667 return false;
668 } else {
669 id = JSID_VOID;
670 }
671
672 if (!JSObject::unwatch(cx, obj, id))
673 return false;
674
675 args.rval().setUndefined();
676 return true;
677 }
678
679 #endif /* JS_HAS_OBJ_WATCHPOINT */
680
681 /* ECMA 15.2.4.5. */
682 static bool
683 obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
684 {
685 CallArgs args = CallArgsFromVp(argc, vp);
686
687 HandleValue idValue = args.get(0);
688
689 /* Step 1, 2. */
690 jsid id;
691 if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
692 JSObject *obj = &args.thisv().toObject(), *obj2;
693 Shape *prop;
694 if (!obj->is<ProxyObject>() &&
695 HasOwnProperty<NoGC>(cx, obj->getOps()->lookupGeneric, obj, id, &obj2, &prop))
696 {
697 args.rval().setBoolean(!!prop);
698 return true;
699 }
700 }
701
702 /* Step 1. */
703 RootedId idRoot(cx);
704 if (!ValueToId<CanGC>(cx, idValue, &idRoot))
705 return false;
706
707 /* Step 2. */
708 RootedObject obj(cx, ToObject(cx, args.thisv()));
709 if (!obj)
710 return false;
711
712 /* Non-standard code for proxies. */
713 if (obj->is<ProxyObject>()) {
714 bool has;
715 if (!Proxy::hasOwn(cx, obj, idRoot, &has))
716 return false;
717 args.rval().setBoolean(has);
718 return true;
719 }
720
721 /* Step 3. */
722 bool found;
723 if (!HasOwnProperty(cx, obj, idRoot, &found))
724 return false;
725
726 /* Step 4,5. */
727 args.rval().setBoolean(found);
728 return true;
729 }
730
731 /* ES5 15.2.4.6. */
732 static bool
733 obj_isPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
734 {
735 CallArgs args = CallArgsFromVp(argc, vp);
736
737 /* Step 1. */
738 if (args.length() < 1 || !args[0].isObject()) {
739 args.rval().setBoolean(false);
740 return true;
741 }
742
743 /* Step 2. */
744 RootedObject obj(cx, ToObject(cx, args.thisv()));
745 if (!obj)
746 return false;
747
748 /* Step 3. */
749 bool isDelegate;
750 if (!IsDelegate(cx, obj, args[0], &isDelegate))
751 return false;
752 args.rval().setBoolean(isDelegate);
753 return true;
754 }
755
756 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
757 static bool
758 obj_create(JSContext *cx, unsigned argc, Value *vp)
759 {
760 CallArgs args = CallArgsFromVp(argc, vp);
761 if (args.length() == 0) {
762 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
763 "Object.create", "0", "s");
764 return false;
765 }
766
767 RootedValue v(cx, args[0]);
768 if (!v.isObjectOrNull()) {
769 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
770 if (!bytes)
771 return false;
772 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
773 bytes, "not an object or null");
774 js_free(bytes);
775 return false;
776 }
777
778 RootedObject proto(cx, v.toObjectOrNull());
779
780 /*
781 * Use the callee's global as the parent of the new object to avoid dynamic
782 * scoping (i.e., using the caller's global).
783 */
784 RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, &args.callee().global()));
785 if (!obj)
786 return false;
787
788 /* 15.2.3.5 step 4. */
789 if (args.hasDefined(1)) {
790 if (args[1].isPrimitive()) {
791 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
792 return false;
793 }
794
795 RootedObject props(cx, &args[1].toObject());
796 if (!DefineProperties(cx, obj, props))
797 return false;
798 }
799
800 /* 5. Return obj. */
801 args.rval().setObject(*obj);
802 return true;
803 }
804
805 static bool
806 obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
807 {
808 CallArgs args = CallArgsFromVp(argc, vp);
809 RootedObject obj(cx);
810 if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyDescriptor", &obj))
811 return false;
812 RootedId id(cx);
813 if (!ValueToId<CanGC>(cx, args.get(1), &id))
814 return false;
815 return GetOwnPropertyDescriptor(cx, obj, id, args.rval());
816 }
817
818 static bool
819 obj_keys(JSContext *cx, unsigned argc, Value *vp)
820 {
821 CallArgs args = CallArgsFromVp(argc, vp);
822 RootedObject obj(cx);
823 if (!GetFirstArgumentAsObject(cx, args, "Object.keys", &obj))
824 return false;
825
826 AutoIdVector props(cx);
827 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
828 return false;
829
830 AutoValueVector vals(cx);
831 if (!vals.reserve(props.length()))
832 return false;
833 for (size_t i = 0, len = props.length(); i < len; i++) {
834 jsid id = props[i];
835 if (JSID_IS_STRING(id)) {
836 vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
837 } else if (JSID_IS_INT(id)) {
838 JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
839 if (!str)
840 return false;
841 vals.infallibleAppend(StringValue(str));
842 } else {
843 JS_ASSERT(JSID_IS_OBJECT(id));
844 }
845 }
846
847 JS_ASSERT(props.length() <= UINT32_MAX);
848 JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
849 if (!aobj)
850 return false;
851
852 args.rval().setObject(*aobj);
853 return true;
854 }
855
856 /* ES6 draft 15.2.3.16 */
857 static bool
858 obj_is(JSContext *cx, unsigned argc, Value *vp)
859 {
860 CallArgs args = CallArgsFromVp(argc, vp);
861
862 bool same;
863 if (!SameValue(cx, args.get(0), args.get(1), &same))
864 return false;
865
866 args.rval().setBoolean(same);
867 return true;
868 }
869
870 static bool
871 obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
872 {
873 CallArgs args = CallArgsFromVp(argc, vp);
874 RootedObject obj(cx);
875 if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyNames", &obj))
876 return false;
877
878 AutoIdVector keys(cx);
879 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
880 return false;
881
882 AutoValueVector vals(cx);
883 if (!vals.resize(keys.length()))
884 return false;
885
886 for (size_t i = 0, len = keys.length(); i < len; i++) {
887 jsid id = keys[i];
888 if (JSID_IS_INT(id)) {
889 JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
890 if (!str)
891 return false;
892 vals[i].setString(str);
893 } else if (JSID_IS_ATOM(id)) {
894 vals[i].setString(JSID_TO_STRING(id));
895 } else {
896 vals[i].setObject(*JSID_TO_OBJECT(id));
897 }
898 }
899
900 JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
901 if (!aobj)
902 return false;
903
904 args.rval().setObject(*aobj);
905 return true;
906 }
907
908 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
909 static bool
910 obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
911 {
912 CallArgs args = CallArgsFromVp(argc, vp);
913 RootedObject obj(cx);
914 if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperty", &obj))
915 return false;
916
917 RootedId id(cx);
918 if (!ValueToId<CanGC>(cx, args.get(1), &id))
919 return false;
920
921 bool junk;
922 if (!DefineOwnProperty(cx, obj, id, args.get(2), &junk))
923 return false;
924
925 args.rval().setObject(*obj);
926 return true;
927 }
928
929 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
930 static bool
931 obj_defineProperties(JSContext *cx, unsigned argc, Value *vp)
932 {
933 CallArgs args = CallArgsFromVp(argc, vp);
934
935 /* Steps 1 and 7. */
936 RootedObject obj(cx);
937 if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperties", &obj))
938 return false;
939 args.rval().setObject(*obj);
940
941 /* Step 2. */
942 if (args.length() < 2) {
943 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
944 "Object.defineProperties", "0", "s");
945 return false;
946 }
947 RootedValue val(cx, args[1]);
948 RootedObject props(cx, ToObject(cx, val));
949 if (!props)
950 return false;
951
952 /* Steps 3-6. */
953 return DefineProperties(cx, obj, props);
954 }
955
956 static bool
957 obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
958 {
959 CallArgs args = CallArgsFromVp(argc, vp);
960 RootedObject obj(cx);
961 if (!GetFirstArgumentAsObject(cx, args, "Object.isExtensible", &obj))
962 return false;
963
964 bool extensible;
965 if (!JSObject::isExtensible(cx, obj, &extensible))
966 return false;
967 args.rval().setBoolean(extensible);
968 return true;
969 }
970
971 static bool
972 obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
973 {
974 CallArgs args = CallArgsFromVp(argc, vp);
975 RootedObject obj(cx);
976 if (!GetFirstArgumentAsObject(cx, args, "Object.preventExtensions", &obj))
977 return false;
978
979 args.rval().setObject(*obj);
980
981 bool extensible;
982 if (!JSObject::isExtensible(cx, obj, &extensible))
983 return false;
984 if (!extensible)
985 return true;
986
987 return JSObject::preventExtensions(cx, obj);
988 }
989
990 static bool
991 obj_freeze(JSContext *cx, unsigned argc, Value *vp)
992 {
993 CallArgs args = CallArgsFromVp(argc, vp);
994 RootedObject obj(cx);
995 if (!GetFirstArgumentAsObject(cx, args, "Object.freeze", &obj))
996 return false;
997
998 args.rval().setObject(*obj);
999
1000 return JSObject::freeze(cx, obj);
1001 }
1002
1003 static bool
1004 obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
1005 {
1006 CallArgs args = CallArgsFromVp(argc, vp);
1007 RootedObject obj(cx);
1008 if (!GetFirstArgumentAsObject(cx, args, "Object.preventExtensions", &obj))
1009 return false;
1010
1011 bool frozen;
1012 if (!JSObject::isFrozen(cx, obj, &frozen))
1013 return false;
1014 args.rval().setBoolean(frozen);
1015 return true;
1016 }
1017
1018 static bool
1019 obj_seal(JSContext *cx, unsigned argc, Value *vp)
1020 {
1021 CallArgs args = CallArgsFromVp(argc, vp);
1022 RootedObject obj(cx);
1023 if (!GetFirstArgumentAsObject(cx, args, "Object.seal", &obj))
1024 return false;
1025
1026 args.rval().setObject(*obj);
1027
1028 return JSObject::seal(cx, obj);
1029 }
1030
1031 static bool
1032 obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
1033 {
1034 CallArgs args = CallArgsFromVp(argc, vp);
1035 RootedObject obj(cx);
1036 if (!GetFirstArgumentAsObject(cx, args, "Object.isSealed", &obj))
1037 return false;
1038
1039 bool sealed;
1040 if (!JSObject::isSealed(cx, obj, &sealed))
1041 return false;
1042 args.rval().setBoolean(sealed);
1043 return true;
1044 }
1045
1046 const JSFunctionSpec js::object_methods[] = {
1047 #if JS_HAS_TOSOURCE
1048 JS_FN(js_toSource_str, obj_toSource, 0,0),
1049 #endif
1050 JS_FN(js_toString_str, obj_toString, 0,0),
1051 JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
1052 JS_FN(js_valueOf_str, obj_valueOf, 0,0),
1053 #if JS_HAS_OBJ_WATCHPOINT
1054 JS_FN(js_watch_str, obj_watch, 2,0),
1055 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
1056 #endif
1057 JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
1058 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
1059 JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
1060 #if JS_OLD_GETTER_SETTER_METHODS
1061 JS_FN(js_defineGetter_str, js::obj_defineGetter, 2,0),
1062 JS_FN(js_defineSetter_str, js::obj_defineSetter, 2,0),
1063 JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
1064 JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
1065 #endif
1066 JS_FS_END
1067 };
1068
1069 const JSFunctionSpec js::object_static_methods[] = {
1070 JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
1071 JS_FN("setPrototypeOf", obj_setPrototypeOf, 2,0),
1072 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
1073 JS_FN("keys", obj_keys, 1,0),
1074 JS_FN("is", obj_is, 2,0),
1075 JS_FN("defineProperty", obj_defineProperty, 3,0),
1076 JS_FN("defineProperties", obj_defineProperties, 2,0),
1077 JS_FN("create", obj_create, 2,0),
1078 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
1079 JS_FN("isExtensible", obj_isExtensible, 1,0),
1080 JS_FN("preventExtensions", obj_preventExtensions, 1,0),
1081 JS_FN("freeze", obj_freeze, 1,0),
1082 JS_FN("isFrozen", obj_isFrozen, 1,0),
1083 JS_FN("seal", obj_seal, 1,0),
1084 JS_FN("isSealed", obj_isSealed, 1,0),
1085 JS_FS_END
1086 };

mercurial