Files
palemoon27/js/src/builtin/Object.cpp
T
roytam1 9d253b796d import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1235610 - Add xpctall support for Bitrig and DragonFly. r=glandium (02b7d40eb3)
- Bug 1252072 - Prevent ASan instrumentation for unsafe xpcom functions. r=froydnj (881bfc18c0)
- Bug 1245099 - Fixed uninitialized variable warning. r=bsmedberg (6481aad0d5)
- Bug 1196370 - Remove the clang assembly workaround from bug 1028613. r=ehsan (b8b6f1a5e8)
- Bug 1234860 - move win32 NS_InvokeByIndex implementation to a separate assembly file; r=aklotz,ted.m (73ca54348f)
- Bug 1248534 (part 1) - Remove XPT encoding support. r=khuey. (e84c4dfd32)
- Bug 1248534 (part 2) - Remove unused XPT flags. r=khuey. (264be694d0)
- Bug 1248534 (part 3) - Remove almost all support for XPT annotations. r=khuey. (97a0d86e0b)
- Bug 1248534 (part 4) - Remove unused fields from XPTConstValue. r=khuey. (9eccf9eba5)
- Bug 1248534 (part 5) - Remove XPTDatapool. r=khuey. (139dab7a9f)
- Bug 1248534 (part 6) - Stack-allocate XPTState. r=khuey. (f32ce7cc9e)
- Bug 1249174 (part 7) - Only define XPTArena::name if XPT_ARENA_LOGGING is defined. r=khuey. (174a20c0a3)
- Bug 1248534 (part 8) - Remove useless XPT freeing code. r=khuey. (a5ade9739b)
- Bug 1248534 (part 9) - Remove XPT arena logging code. r=khuey. (a996dfad76)
- Bug 1251458 - Reinstate annotation handling in .xpt files. r=khuey. (fbe8d573bd)
- Bug 1249174 (part 1) - Don't store the unused XPTTypeDescriptorTags::argnum2 field in memory. r=khuey. (e1d12965eb)
- Bug 1253877 - Baldr: cast -1 to uint8 to avoid 'shifting negative' error on CLOSED TREE (r=bustage) (cdf8031264)
- Bug 1258599 - OdinMonkey: MIPS: Only reserving stack for argument registers on O32. r=huangwenjun06 (a77220a9fa)
- Bug 1253137 - Baldr: fold if_else into if to match ml-proto (r=sunfish) (54e89aafe3)
- Bug 1249174 (part 2) - Shrink xptiInterfaceEntry by reordering its fields. r=khuey. (c4d7c15e6e)
- Bug 1253137 - Baldr: update br_table syntax to match ml-proto (r=sunfish) (e7f253e4d8)
- Bug 1249174 (part 3) - Don't store the unused XPTInterfaceDirectoryEntry::name_space field in memory. r=khuey. (c308508e74)
- Bug 1249174 (part 4) - Don't store unused XPTHeader fields in memory. r=khuey. (2dfd238a54)
- Bug 1249174 (part 5) - Remove the useless BLK_HDR::size field. r=khuey. (1a362c278e)
- Bug 1249174 (part 6) - Shrink XPTTypeDescriptor. r=khuey. (5a313327fe)
- Bug 1249174 (part 7.5) - Avoid wasted space around XPT strings. r=khuey. (ac3653802c)
- Bug 1249174 (part 8) - Shrink XPTInterfaceDescriptor. r=khuey. (e9dc929d37)
- Bug 1254188 - Baldr: handle recycled phis when closing a loop with a  value (r=bbouvier) (2cb0895472)
- Bug 1254167: Don't allow folding to full range for atomic accesses; r=sunfish (96a851efda)
- Bug 1256637: Set definition before returning early in EmitBrTable; r=luke (0fdb365e82)
- Bug 1255695 - BaldrMonkey: Implement unaligned accesses. r=luke (205b798249)
- Bug 1238121 - Properly guard Profiler's RAII classes r=BenWa f=mystor (a3db8e6bc9)
- Bug 1251787 - Remove remaining references to MOZILLA_XPCOMRT_API from tools. r=mstange (1632205437)
- align profiler (c2638ecf1f)
- Bug 1235502 - Fix -Wunreachable-code warning in tools/profiler/. r=BenWa (0d564937c9)
- Bug 1221846 - Properly close the tasktracer property in the GeckoSampler JSON blob. r=BenWa (dca640fb03)
- Bug 1239498 - Use Stackwalk64 on win x64. r=jrmuizel (50ffe8a649)
- Bug 1258269: Declare logging string-literals in exception_handler.cc as 'const char[]' to fix build warning & for consistency. r=ted (a9454e735f)
- parts of Bug 1151175 - Update libvpx update.py for 1.4.0. (8700fa48ab)
- Bug 1249590 - Bullet-proofing AsyncShutdown wrt exceptions;r=froydnj (7e512f1029)
- Bug 1021151 - avoid memory leak in NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT, use nsAutoPtr instead of naked ptr. r=Ehsan (d0eb754af2)
- Bug 1255223 - Null crash when logging weak maps. r=mccr8. (0d02b4b469)
- Bug 1250134 - assert mJSRuntime when IsGrayJS() is true. r=mccr8 (a3987102ed)
- Bug 1254131: Fix non-unified wasm build. r=luke (cacb86e4d0)
- Bug 1246929 - Skip installing functions and properties on builtins for the self-hosting global. r=Waldo (aa04041de9)
- Bug 986294 - Remove Proxy.create from addon-sdk. r=mossop (da3d6c40ea)
- Bug 1243805 - Replace Proxy.create with new Proxy in devtools l10n code. r=jryans (9886f857be)
- Bug 1245141 - Use new Proxy for AddonManager.addonTypes. r=mossop (6cc8dd0870)
- Bug 1253866 - Remove Proxy.create from crash tests. r=bz (76a421cf97)
- Bug 892903 - Remove Proxy.create and Proxy.createFunction. r=efaust (7b572deb10)
- Bug 1049041 - Remove scary warning about mutating [[Prototype]]. r=efaust. (367ac3f6c5)
- Bug 1257445 - #ifdef on __GLIBC__ for sched_getcpu, which is a glibc feature. r=jimb (171a1729c1)
- Followup for bug 1257445 - Remove the AutoStopwatch::getCPU implementation using sched_getcpu. r=jimb (221f52d4c4)
- Bug 1178317 - eliminate large static constructor from ShimInterfaceInfo.cpp; r=poiru (aa66704aee)
- Bug 1247580 P1 Allow old nsIX509Cert serialized objects to be read off disk. r=bz (1f8bc280a6)
- Bug 1247580 P2 Add gtest to ensure we can continue to deserialize old security info strings. r=bz (323059ac29)
- Bug 864842 - Show error for browsing Windows drive without media, r=michal (1e438bdf2f)
- Bug 1233283 - Remove unless tmp from ReadDir in nsLocalFileWin. r=froydnj (1565d1cc14)
- Bug 1258498: Use fallible allocation in nsScriptableInputStream::ReadBytes. r= froydnj (eebcb8050d)
- Bug 1236108: Add temp directory for sandboxed content processes to directory r=bsmedberg (8384b33c10)
- Bug 1255362 - Null-check GetContainer() before using it in image-related ConfigureLayer() methods. r=mstange a=Tomcat (beab7149f1)
- Bug 1205473 - Add a state bit to optimize building event regions. r=mattwoodrow (c8b1eb9839)
- Bug 1222886 - Remove unused nsCSSParser::{SetStyleSheet,SetChildLoader,SetQuirkMode} methods. r=bzbarsky (c9c6621083)
- Bug 1247327. Fix WebGL acceptance rates in telemetry. r=milan (de10319664)
- Bug 1249664 - Save dropped-down state in nsPresState. r=dbaron (734c3ee18b)
- Bug 1256745 - Cancel the DidPaint timer in SetShell(nullptr). r=mattwoodrow (d2a4202512)
- Bug 1238846 (part 1) - Remove some dead code in nsLayoutUtils. r=mattwoodrow. (e6af806e52)
- Bug 1252414 - Handle lost_context for webgl ClearBuffer*. r=jgilbert (e6728605ff)
- Bug 1228687: ScopedResolveTexturesForDraw needs the context to be current, so make those calls earlier. r=jgilbert (6e731187c5)
- Bug 1247532 - Annotate intentional switch fallthrough to suppress -Wimplicit-fallthrough warning in dom/canvas/. r=jgilbert (d7faec1848)
- Bug 1249483 - Stop filling A with 1.0 on readback from no-alpha. - r=jrmuizel (9bf71fb220)
2024-03-10 21:39:46 +08:00

1242 lines
36 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/Object.h"
#include "mozilla/ArrayUtils.h"
#include "jscntxt.h"
#include "builtin/Eval.h"
#include "frontend/BytecodeCompiler.h"
#include "jit/InlinableNatives.h"
#include "js/UniquePtr.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
#include "vm/NativeObject-inl.h"
#include "vm/Shape-inl.h"
using namespace js;
using js::frontend::IsIdentifier;
using mozilla::ArrayLength;
bool
js::obj_construct(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, nullptr);
if (args.isConstructing() && (&args.newTarget().toObject() != &args.callee())) {
RootedObject newTarget(cx, &args.newTarget().toObject());
obj = CreateThis(cx, &PlainObject::class_, newTarget);
if (!obj)
return false;
} else if (args.length() > 0 && !args[0].isNullOrUndefined()) {
obj = ToObject(cx, args[0]);
if (!obj)
return false;
} else {
/* Make an object whether this was called with 'new' or not. */
if (!NewObjectScriptedCall(cx, &obj))
return false;
}
args.rval().setObject(*obj);
return true;
}
/* ES5 15.2.4.7. */
bool
js::obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue idValue = args.get(0);
// As an optimization, provide a fast path when rooting is not necessary and
// we can safely retrieve the attributes from the object's shape.
/* Steps 1-2. */
jsid id;
if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
JSObject* obj = &args.thisv().toObject();
/* Step 3. */
Shape* shape;
if (obj->isNative() &&
NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &shape))
{
/* Step 4. */
if (!shape) {
args.rval().setBoolean(false);
return true;
}
/* Step 5. */
unsigned attrs = GetShapeAttributes(obj, shape);
args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
return true;
}
}
/* Step 1. */
RootedId idRoot(cx);
if (!ToPropertyKey(cx, idValue, &idRoot))
return false;
/* Step 2. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Step 3. */
Rooted<PropertyDescriptor> desc(cx);
if (!GetOwnPropertyDescriptor(cx, obj, idRoot, &desc))
return false;
/* Steps 4-5. */
args.rval().setBoolean(desc.object() && desc.enumerable());
return true;
}
#if JS_HAS_TOSOURCE
static bool
obj_toSource(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_CHECK_RECURSION(cx, return false);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
JSString* str = ObjectToSource(cx, obj);
if (!str)
return false;
args.rval().setString(str);
return true;
}
/*
* Given a function source string, return the offset and length of the part
* between '(function $name' and ')'.
*/
template <typename CharT>
static bool
ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen)
{
const CharT* const start = chars.start().get();
const CharT* const end = chars.end().get();
const CharT* s = start;
uint8_t parenChomp = 0;
if (s[0] == '(') {
s++;
parenChomp = 1;
}
/* Try to jump "function" keyword. */
s = js_strchr_limit(s, ' ', end);
if (!s)
return false;
/*
* Jump over the function's name: it can't be encoded as part
* of an ECMA getter or setter.
*/
s = js_strchr_limit(s, '(', end);
if (!s)
return false;
if (*s == ' ')
s++;
*outOffset = s - start;
*outLen = end - s - parenChomp;
MOZ_ASSERT(*outOffset + *outLen <= chars.length());
return true;
}
JSString*
js::ObjectToSource(JSContext* cx, HandleObject obj)
{
/* If outermost, we need parentheses to be an expression, not a block. */
bool outermost = (cx->cycleDetectorSet.count() == 0);
AutoCycleDetector detector(cx, obj);
if (!detector.init())
return nullptr;
if (detector.foundCycle())
return NewStringCopyZ<CanGC>(cx, "{}");
StringBuffer buf(cx);
if (outermost && !buf.append('('))
return nullptr;
if (!buf.append('{'))
return nullptr;
RootedValue v0(cx), v1(cx);
MutableHandleValue val[2] = {&v0, &v1};
RootedString str0(cx), str1(cx);
MutableHandleString gsop[2] = {&str0, &str1};
AutoIdVector idv(cx);
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv))
return nullptr;
bool comma = false;
for (size_t i = 0; i < idv.length(); ++i) {
RootedId id(cx, idv[i]);
Rooted<PropertyDescriptor> desc(cx);
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
return nullptr;
int valcnt = 0;
if (desc.object()) {
if (desc.isAccessorDescriptor()) {
if (desc.hasGetterObject() && desc.getterObject()) {
val[valcnt].setObject(*desc.getterObject());
gsop[valcnt].set(cx->names().get);
valcnt++;
}
if (desc.hasSetterObject() && desc.setterObject()) {
val[valcnt].setObject(*desc.setterObject());
gsop[valcnt].set(cx->names().set);
valcnt++;
}
} else {
valcnt = 1;
val[0].set(desc.value());
gsop[0].set(nullptr);
}
}
/* Convert id to a string. */
RootedString idstr(cx);
if (JSID_IS_SYMBOL(id)) {
RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
idstr = ValueToSource(cx, v);
if (!idstr)
return nullptr;
} else {
RootedValue idv(cx, IdToValue(id));
idstr = ToString<CanGC>(cx, idv);
if (!idstr)
return nullptr;
/*
* If id is a string that's not an identifier, or if it's a negative
* integer, then it must be quoted.
*/
if (JSID_IS_ATOM(id)
? !IsIdentifier(JSID_TO_ATOM(id))
: JSID_TO_INT(id) < 0)
{
idstr = QuoteString(cx, idstr, char16_t('\''));
if (!idstr)
return nullptr;
}
}
for (int j = 0; j < valcnt; j++) {
/* Convert val[j] to its canonical source form. */
JSString* valsource = ValueToSource(cx, val[j]);
if (!valsource)
return nullptr;
RootedLinearString valstr(cx, valsource->ensureLinear(cx));
if (!valstr)
return nullptr;
size_t voffset = 0;
size_t vlength = valstr->length();
/*
* Remove '(function ' from the beginning of valstr and ')' from the
* end so that we can put "get" in front of the function definition.
*/
if (gsop[j] && IsFunctionObject(val[j])) {
bool success;
JS::AutoCheckCannotGC nogc;
if (valstr->hasLatin1Chars())
success = ArgsAndBodySubstring(valstr->latin1Range(nogc), &voffset, &vlength);
else
success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength);
if (!success)
gsop[j].set(nullptr);
}
if (comma && !buf.append(", "))
return nullptr;
comma = true;
if (gsop[j]) {
if (!buf.append(gsop[j]) || !buf.append(' '))
return nullptr;
}
if (JSID_IS_SYMBOL(id) && !buf.append('['))
return nullptr;
if (!buf.append(idstr))
return nullptr;
if (JSID_IS_SYMBOL(id) && !buf.append(']'))
return nullptr;
if (!buf.append(gsop[j] ? ' ' : ':'))
return nullptr;
if (!buf.appendSubstring(valstr, voffset, vlength))
return nullptr;
}
}
if (!buf.append('}'))
return nullptr;
if (outermost && !buf.append(')'))
return nullptr;
return buf.finishString();
}
#endif /* JS_HAS_TOSOURCE */
JSString*
JS_BasicObjectToString(JSContext* cx, HandleObject obj)
{
// Some classes are really common, don't allocate new strings for them.
// The ordering below is based on the measurements in bug 966264.
if (obj->is<PlainObject>())
return cx->names().objectObject;
if (obj->is<StringObject>())
return cx->names().objectString;
if (obj->is<ArrayObject>())
return cx->names().objectArray;
if (obj->is<JSFunction>())
return cx->names().objectFunction;
if (obj->is<NumberObject>())
return cx->names().objectNumber;
const char* className = GetObjectClassName(cx, obj);
if (strcmp(className, "Window") == 0)
return cx->names().objectWindow;
StringBuffer sb(cx);
if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
!sb.append("]"))
{
return nullptr;
}
return sb.finishString();
}
/* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
bool
js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Step 1. */
if (args.thisv().isUndefined()) {
args.rval().setString(cx->names().objectUndefined);
return true;
}
/* Step 2. */
if (args.thisv().isNull()) {
args.rval().setString(cx->names().objectNull);
return true;
}
/* Step 3. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Steps 4-5. */
JSString* str = JS_BasicObjectToString(cx, obj);
if (!str)
return false;
args.rval().setString(str);
return true;
}
bool
js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
args.rval().setObject(*obj);
return true;
}
static bool
obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 2) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"Object.setPrototypeOf", "1", "");
return false;
}
/* Step 1-2. */
if (args[0].isNullOrUndefined()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
args[0].isNull() ? "null" : "undefined", "object");
return false;
}
/* Step 3. */
if (!args[1].isObjectOrNull()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
"Object.setPrototypeOf", "an object or null", InformalValueTypeName(args[1]));
return false;
}
/* Step 4. */
if (!args[0].isObject()) {
args.rval().set(args[0]);
return true;
}
/* Step 5-7. */
RootedObject obj(cx, &args[0].toObject());
RootedObject newProto(cx, args[1].toObjectOrNull());
if (!SetPrototype(cx, obj, newProto))
return false;
/* Step 8. */
args.rval().set(args[0]);
return true;
}
#if JS_HAS_OBJ_WATCHPOINT
bool
js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, JS::Value old,
JS::Value* nvp, void* closure)
{
RootedObject obj(cx, obj_);
RootedId id(cx, id_);
/* Avoid recursion on (obj, id) already being watched on cx. */
AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
if (resolving.alreadyStarted())
return true;
JSObject* callable = (JSObject*)closure;
Value argv[] = { IdToValue(id), old, *nvp };
RootedValue rv(cx);
if (!Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, &rv))
return false;
*nvp = rv;
return true;
}
static bool
obj_watch(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
if (!GlobalObject::warnOnceAboutWatch(cx, obj))
return false;
if (args.length() <= 1) {
ReportMissingArg(cx, args.calleev(), 1);
return false;
}
RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2));
if (!callable)
return false;
RootedId propid(cx);
if (!ValueToId<CanGC>(cx, args[0], &propid))
return false;
if (!WatchProperty(cx, obj, propid, callable))
return false;
args.rval().setUndefined();
return true;
}
static bool
obj_unwatch(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
if (!GlobalObject::warnOnceAboutWatch(cx, obj))
return false;
RootedId id(cx);
if (args.length() != 0) {
if (!ValueToId<CanGC>(cx, args[0], &id))
return false;
} else {
id = JSID_VOID;
}
if (!UnwatchProperty(cx, obj, id))
return false;
args.rval().setUndefined();
return true;
}
#endif /* JS_HAS_OBJ_WATCHPOINT */
/* ECMA 15.2.4.5. */
bool
js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue idValue = args.get(0);
// As an optimization, provide a fast path when rooting is not necessary and
// we can safely retrieve the object's shape.
/* Step 1, 2. */
jsid id;
if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
JSObject* obj = &args.thisv().toObject();
Shape* prop;
if (obj->isNative() &&
NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
{
args.rval().setBoolean(!!prop);
return true;
}
}
/* Step 1. */
RootedId idRoot(cx);
if (!ToPropertyKey(cx, idValue, &idRoot))
return false;
/* Step 2. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Step 3. */
bool found;
if (!HasOwnProperty(cx, obj, idRoot, &found))
return false;
/* Step 4,5. */
args.rval().setBoolean(found);
return true;
}
/* ES5 15.2.4.6. */
static bool
obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Step 1. */
if (args.length() < 1 || !args[0].isObject()) {
args.rval().setBoolean(false);
return true;
}
/* Step 2. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Step 3. */
bool isDelegate;
if (!IsDelegate(cx, obj, args[0], &isDelegate))
return false;
args.rval().setBoolean(isDelegate);
return true;
}
PlainObject*
js::ObjectCreateImpl(JSContext* cx, HandleObject proto, NewObjectKind newKind,
HandleObjectGroup group)
{
// Give the new object a small number of fixed slots, like we do for empty
// object literals ({}).
gc::AllocKind allocKind = GuessObjectGCKind(0);
if (!proto) {
// Object.create(null) is common, optimize it by using an allocation
// site specific ObjectGroup. Because GetCallerInitGroup is pretty
// slow, the caller can pass in the group if it's known and we use that
// instead.
RootedObjectGroup ngroup(cx, group);
if (!ngroup) {
ngroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Null);
if (!ngroup)
return nullptr;
}
MOZ_ASSERT(!ngroup->proto().toObjectOrNull());
return NewObjectWithGroup<PlainObject>(cx, ngroup, allocKind, newKind);
}
return NewObjectWithGivenProto<PlainObject>(cx, proto, allocKind, newKind);
}
PlainObject*
js::ObjectCreateWithTemplate(JSContext* cx, HandlePlainObject templateObj)
{
RootedObject proto(cx, templateObj->getProto());
RootedObjectGroup group(cx, templateObj->group());
return ObjectCreateImpl(cx, proto, GenericObject, group);
}
// ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
bool
js::obj_create(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
if (args.length() == 0) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"Object.create", "0", "s");
return false;
}
if (!args[0].isObjectOrNull()) {
RootedValue v(cx, args[0]);
UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
if (!bytes)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
bytes.get(), "not an object or null");
return false;
}
// Step 2.
RootedObject proto(cx, args[0].toObjectOrNull());
RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
if (!obj)
return false;
// Step 3.
if (args.hasDefined(1)) {
RootedValue val(cx, args[1]);
RootedObject props(cx, ToObject(cx, val));
if (!props || !DefineProperties(cx, obj, props))
return false;
}
// Step 4.
args.rval().setObject(*obj);
return true;
}
// ES6 draft rev27 (2014/08/24) 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)
bool
js::obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Steps 1-2.
RootedObject obj(cx, ToObject(cx, args.get(0)));
if (!obj)
return false;
// Steps 3-4.
RootedId id(cx);
if (!ToPropertyKey(cx, args.get(1), &id))
return false;
// Steps 5-7.
Rooted<PropertyDescriptor> desc(cx);
return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
FromPropertyDescriptor(cx, desc, args.rval());
}
enum EnumerableOwnPropertiesKind {
Keys,
Values,
KeysAndValues
};
// ES7 proposal 2015-12-14
// http://tc39.github.io/proposal-object-values-entries/#EnumerableOwnProperties
static bool
EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args, EnumerableOwnPropertiesKind kind)
{
// Step 1. (Step 1 of Object.{keys,values,entries}, really.)
RootedObject obj(cx, ToObject(cx, args.get(0)));
if (!obj)
return false;
// Step 2.
AutoIdVector ids(cx);
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &ids))
return false;
// Step 3.
AutoValueVector properties(cx);
size_t len = ids.length();
if (!properties.resize(len))
return false;
RootedId id(cx);
RootedValue key(cx);
RootedValue value(cx);
RootedNativeObject nobj(cx);
if (obj->is<NativeObject>())
nobj = &obj->as<NativeObject>();
RootedShape shape(cx);
Rooted<PropertyDescriptor> desc(cx);
// Step 4.
size_t out = 0;
for (size_t i = 0; i < len; i++) {
id = ids[i];
// Step 4.a. (Symbols were filtered out in step 2.)
MOZ_ASSERT(!JSID_IS_SYMBOL(id));
if (kind != Values) {
if (!IdToStringOrSymbol(cx, id, &key))
return false;
}
// Step 4.a.i.
if (nobj) {
if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) {
value = nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
} else {
shape = nobj->lookup(cx, id);
if (!shape || !(GetShapeAttributes(nobj, shape) & JSPROP_ENUMERATE))
continue;
if (!shape->isAccessorShape()) {
if (!NativeGetExistingProperty(cx, nobj, nobj, shape, &value))
return false;
} else if (!GetProperty(cx, obj, obj, id, &value)) {
return false;
}
}
} else {
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
return false;
// Step 4.a.ii. (inverted.)
if (!desc.object() || !desc.enumerable())
continue;
// Step 4.a.ii.1.
// (Omitted because Object.keys doesn't use this implementation.)
// Step 4.a.ii.2.a.
if (obj->isNative() && desc.hasValue())
value = desc.value();
else if (!GetProperty(cx, obj, obj, id, &value))
return false;
}
// Steps 4.a.ii.2.b-c.
if (kind == Values)
properties[out++].set(value);
else if (!NewValuePair(cx, key, value, properties[out++]))
return false;
}
// Step 5.
// (Implemented in step 2.)
// Step 3 of Object.{keys,values,entries}
JSObject* aobj = NewDenseCopiedArray(cx, out, properties.begin());
if (!aobj)
return false;
args.rval().setObject(*aobj);
return true;
}
// ES7 proposal 2015-12-14
// http://tc39.github.io/proposal-object-values-entries/#Object.keys
static bool
obj_keys(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY);
}
// ES7 proposal 2015-12-14
// http://tc39.github.io/proposal-object-values-entries/#Object.values
static bool
obj_values(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return EnumerableOwnProperties(cx, args, Values);
}
// ES7 proposal 2015-12-14
// http://tc39.github.io/proposal-object-values-entries/#Object.entries
static bool
obj_entries(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return EnumerableOwnProperties(cx, args, KeysAndValues);
}
/* ES6 draft 15.2.3.16 */
static bool
obj_is(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool same;
if (!SameValue(cx, args.get(0), args.get(1), &same))
return false;
args.rval().setBoolean(same);
return true;
}
bool
js::IdToStringOrSymbol(JSContext* cx, HandleId id, MutableHandleValue result)
{
if (JSID_IS_INT(id)) {
JSString* str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
if (!str)
return false;
result.setString(str);
} else if (JSID_IS_ATOM(id)) {
result.setString(JSID_TO_STRING(id));
} else {
result.setSymbol(JSID_TO_SYMBOL(id));
}
return true;
}
/* ES6 draft rev 25 (2014 May 22) 19.1.2.8.1 */
bool
js::GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags)
{
// Steps 1-2.
RootedObject obj(cx, ToObject(cx, args.get(0)));
if (!obj)
return false;
// Steps 3-10.
AutoIdVector keys(cx);
if (!GetPropertyKeys(cx, obj, flags, &keys))
return false;
// Step 11.
AutoValueVector vals(cx);
if (!vals.resize(keys.length()))
return false;
for (size_t i = 0, len = keys.length(); i < len; i++) {
MOZ_ASSERT_IF(JSID_IS_SYMBOL(keys[i]), flags & JSITER_SYMBOLS);
MOZ_ASSERT_IF(!JSID_IS_SYMBOL(keys[i]), !(flags & JSITER_SYMBOLSONLY));
if (!IdToStringOrSymbol(cx, keys[i], vals[i]))
return false;
}
JSObject* aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
if (!aobj)
return false;
args.rval().setObject(*aobj);
return true;
}
bool
js::obj_getOwnPropertyNames(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN);
}
/* ES6 draft rev 25 (2014 May 22) 19.1.2.8 */
static bool
obj_getOwnPropertySymbols(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return GetOwnPropertyKeys(cx, args,
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY);
}
/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.4: Object.defineProperty(O, P, Attributes) */
bool
js::obj_defineProperty(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Steps 1-3.
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperty", &obj))
return false;
RootedId id(cx);
if (!ToPropertyKey(cx, args.get(1), &id))
return false;
// Steps 4-5.
Rooted<PropertyDescriptor> desc(cx);
if (!ToPropertyDescriptor(cx, args.get(2), true, &desc))
return false;
// Steps 6-8.
if (!DefineProperty(cx, obj, id, desc))
return false;
args.rval().setObject(*obj);
return true;
}
/* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
static bool
obj_defineProperties(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Steps 1 and 7. */
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperties", &obj))
return false;
args.rval().setObject(*obj);
/* Step 2. */
if (args.length() < 2) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"Object.defineProperties", "0", "s");
return false;
}
RootedValue val(cx, args[1]);
RootedObject props(cx, ToObject(cx, val));
if (!props)
return false;
/* Steps 3-6. */
return DefineProperties(cx, obj, props);
}
// ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
static bool
obj_preventExtensions(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().set(args.get(0));
// Step 1.
if (!args.get(0).isObject())
return true;
// Steps 2-5.
RootedObject obj(cx, &args.get(0).toObject());
return PreventExtensions(cx, obj);
}
// ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
static bool
obj_freeze(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().set(args.get(0));
// Step 1.
if (!args.get(0).isObject())
return true;
// Steps 2-5.
RootedObject obj(cx, &args.get(0).toObject());
return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen);
}
// ES6 draft rev27 (2014/08/24) 19.1.2.12 Object.isFrozen(O)
static bool
obj_isFrozen(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
bool frozen = true;
// Step 2.
if (args.get(0).isObject()) {
RootedObject obj(cx, &args.get(0).toObject());
if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Frozen, &frozen))
return false;
}
args.rval().setBoolean(frozen);
return true;
}
// ES6 draft rev27 (2014/08/24) 19.1.2.17 Object.seal(O)
static bool
obj_seal(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().set(args.get(0));
// Step 1.
if (!args.get(0).isObject())
return true;
// Steps 2-5.
RootedObject obj(cx, &args.get(0).toObject());
return SetIntegrityLevel(cx, obj, IntegrityLevel::Sealed);
}
// ES6 draft rev27 (2014/08/24) 19.1.2.13 Object.isSealed(O)
static bool
obj_isSealed(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
bool sealed = true;
// Step 2.
if (args.get(0).isObject()) {
RootedObject obj(cx, &args.get(0).toObject());
if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Sealed, &sealed))
return false;
}
args.rval().setBoolean(sealed);
return true;
}
static bool
ProtoGetter(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue thisv = args.thisv();
if (thisv.isNullOrUndefined()) {
ReportIncompatible(cx, args);
return false;
}
if (thisv.isPrimitive() && !BoxNonStrictThis(cx, args))
return false;
RootedObject obj(cx, &args.thisv().toObject());
RootedObject proto(cx);
if (!GetPrototype(cx, obj, &proto))
return false;
args.rval().setObjectOrNull(proto);
return true;
}
namespace js {
size_t sSetProtoCalled = 0;
} // namespace js
static bool
ProtoSetter(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue thisv = args.thisv();
if (thisv.isNullOrUndefined()) {
ReportIncompatible(cx, args);
return false;
}
if (thisv.isPrimitive()) {
// Mutating a boxed primitive's [[Prototype]] has no side effects.
args.rval().setUndefined();
return true;
}
if (!cx->runningWithTrustedPrincipals())
++sSetProtoCalled;
Rooted<JSObject*> obj(cx, &args.thisv().toObject());
/* Do nothing if __proto__ isn't being set to an object or null. */
if (args.length() == 0 || !args[0].isObjectOrNull()) {
args.rval().setUndefined();
return true;
}
Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull());
if (!SetPrototype(cx, obj, newProto))
return false;
args.rval().setUndefined();
return true;
}
static const JSFunctionSpec object_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, obj_toSource, 0,0),
#endif
JS_FN(js_toString_str, obj_toString, 0,0),
JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0),
JS_FN(js_valueOf_str, obj_valueOf, 0,0),
#if JS_HAS_OBJ_WATCHPOINT
JS_FN(js_watch_str, obj_watch, 2,0),
JS_FN(js_unwatch_str, obj_unwatch, 1,0),
#endif
JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
#if JS_OLD_GETTER_SETTER_METHODS
JS_SELF_HOSTED_FN(js_defineGetter_str, "ObjectDefineGetter", 2,0),
JS_SELF_HOSTED_FN(js_defineSetter_str, "ObjectDefineSetter", 2,0),
JS_SELF_HOSTED_FN(js_lookupGetter_str, "ObjectLookupGetter", 1,0),
JS_SELF_HOSTED_FN(js_lookupSetter_str, "ObjectLookupSetter", 1,0),
#endif
JS_FS_END
};
static const JSPropertySpec object_properties[] = {
#if JS_HAS_OBJ_PROTO_PROP
JS_PSGS("__proto__", ProtoGetter, ProtoSetter, 0),
#endif
JS_PS_END
};
static const JSFunctionSpec object_static_methods[] = {
JS_SELF_HOSTED_FN("assign", "ObjectStaticAssign", 2, 0),
JS_SELF_HOSTED_FN("getPrototypeOf", "ObjectGetPrototypeOf", 1, 0),
JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0),
JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2, 0),
JS_FN("keys", obj_keys, 1, 0),
JS_FN("values", obj_values, 1, 0),
JS_FN("entries", obj_entries, 1, 0),
JS_FN("is", obj_is, 2, 0),
JS_FN("defineProperty", obj_defineProperty, 3, 0),
JS_FN("defineProperties", obj_defineProperties, 2, 0),
JS_INLINABLE_FN("create", obj_create, 2, 0, ObjectCreate),
JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1, 0),
JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1, 0),
JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, 0),
JS_FN("preventExtensions", obj_preventExtensions, 1, 0),
JS_FN("freeze", obj_freeze, 1, 0),
JS_FN("isFrozen", obj_isFrozen, 1, 0),
JS_FN("seal", obj_seal, 1, 0),
JS_FN("isSealed", obj_isSealed, 1, 0),
JS_FS_END
};
static JSObject*
CreateObjectConstructor(JSContext* cx, JSProtoKey key)
{
Rooted<GlobalObject*> self(cx, cx->global());
if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
return nullptr;
/* Create the Object function now that we have a [[Prototype]] for it. */
return NewNativeConstructor(cx, obj_construct, 1, HandlePropertyName(cx->names().Object),
gc::AllocKind::FUNCTION, SingletonObject);
}
static JSObject*
CreateObjectPrototype(JSContext* cx, JSProtoKey key)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
MOZ_ASSERT(cx->global()->isNative());
/*
* Create |Object.prototype| first, mirroring CreateBlankProto but for the
* prototype of the created object.
*/
RootedPlainObject objectProto(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr,
SingletonObject));
if (!objectProto)
return nullptr;
bool succeeded;
if (!SetImmutablePrototype(cx, objectProto, &succeeded))
return nullptr;
MOZ_ASSERT(succeeded,
"should have been able to make a fresh Object.prototype's "
"[[Prototype]] immutable");
/*
* The default 'new' type of Object.prototype is required by type inference
* to have unknown properties, to simplify handling of e.g. heterogenous
* objects in JSON and script literals.
*/
if (!JSObject::setNewGroupUnknown(cx, &PlainObject::class_, objectProto))
return nullptr;
return objectProto;
}
static bool
FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
{
Rooted<GlobalObject*> global(cx, cx->global());
/* ES5 15.1.2.1. */
RootedId evalId(cx, NameToId(cx->names().eval));
JSObject* evalobj = DefineFunction(cx, global, evalId, IndirectEval, 1,
JSFUN_STUB_GSOPS | JSPROP_RESOLVING);
if (!evalobj)
return false;
global->setOriginalEval(evalobj);
Rooted<NativeObject*> holder(cx, GlobalObject::getIntrinsicsHolder(cx, global));
if (!holder)
return false;
/*
* The global object should have |Object.prototype| as its [[Prototype]].
* Eventually we'd like to have standard classes be there from the start,
* and thus we would know we were always setting what had previously been a
* null [[Prototype]], but right now some code assumes it can set the
* [[Prototype]] before standard classes have been initialized. For now,
* only set the [[Prototype]] if it hasn't already been set.
*/
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
if (global->shouldSplicePrototype(cx)) {
if (!global->splicePrototype(cx, global->getClass(), tagged))
return false;
}
return true;
}
const Class PlainObject::class_ = {
js_Object_str,
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
nullptr, /* finalize */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
{
CreateObjectConstructor,
CreateObjectPrototype,
object_static_methods,
nullptr,
object_methods,
object_properties,
FinishObjectClassInit
}
};
const Class* const js::ObjectClassPtr = &PlainObject::class_;