mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
500383702e
- Bug 1043863 - OpenedConnection.prototype.executeBeforeShutdown. r=mak (35dec49b1) - Bug 1157946 - Early return if aResponse.sources is undefined;r=fitzgen (c730d5693) - Bug 1157914 - Don't re-render the graphs until selection is done; r=jsantell (6af87a63b) - Bug 1160332 - Correctly check that the mouse is active in graphs to clean up rerender jank, a rebase error from 1157914. r=vp (7d761d64a) - Bug 1150011 - Fix GC hash table checks to work in release builds r=nbp (b7f1436a4) - Bug 996982 - Fix Debugger script delazification logic to account for relazified clones. (r=bz) (df51ca106) - Bug 1157963 - Don't delazify functions about to be finalized. (r=jimb) (a72665ff0) - Bug 1155618 - Fix some places where OOM errors are not reported to th…e context r=terrence (37a0f3a0b) - Bug 1069979 - Don't throw away timezone-strings with dots in Date() r=evilpie (9443f6175) - Bug 1160356 - Make Date.UTC conform to ES3-6 in converting *all* arguments to number before computing the return value. r=evilpie (f5d7c9fba) - Bug 1160356 - Reorganize the code for the Date function/constructor into three separate methods, to be more consistent with ES6's definition of it. Don't change the actual algorithm yet -- this is just code motion. r=evilpie (9f4ed070f) - Bug 1160356 - Make new Date(arg1, arg2, ...) conform to ES3-6 in converting *all* arguments to number before computing the return value. r=evilpie (92da1cef7) - pointer style (d43325489) - Bug 1160535 part 1 - Give JSFunction its own AllocKind. r=terrence (952baab50) - Bug 1160535 part 2 - Do function relazification as part of a new GC phase instead of during marking. r=terrence (3fa0357e6) - Bug 1160535 part 3 - Make the LazyScript -> JSScript pointer weak. r=jonco,terrence (cf1276e6a) - Bug 1160535 part 4 - Remove an assert in XDRInterpretedFunction that's now bogus. r=nbp (8220d24b0) - Bug 1160535 part 5 - Remove the now bogus !maybeScript check in CreateLazyScriptsForCompartment. r=shu (f833671cd) - Bug 1163091 - Handle unboxed arrays in jsarray.cpp fast paths, r=jandem. (daaa46019) - reapply (better) to jsarray.cpp Bug 1389974 Fix False positive rooting (bf526deb2) - Bug 1165904 - Don't call methods on null pointers to fix some -fsanitize=null errors. r=terrence (0d48886e6) - Bug 1162622 - Check fewer traced things as it's too slow to check all edges; r=sfink (0cd9b9634) - Bug 1162296 - Use generic value traversal when scanning unboxed memory; r=jonco (57904891a) - Bug 1162590 - Change the name of the 2-arg traverse to traverseEdge; r=sfink (fd4d9fe3c) - Bug 1150639 - Use a stricter off-thread check in triggerZoneGC; r=bhackett (fad3ddb51) - Bug 1149752 - Cancel GC caused by user inactivity if the user becomes active again r=terrence r=smaug (43129e9ec) - Bug 1154441 - imported patch budget, r=terrence (b3cbb9a23) - pointer style (8cc66c5eb) - pointer style (dc3509b86) - Bug 1155455 - Relax assertion to take account of breakpoints added during incremental sweeping r=terrence (8a70639be) - Bug 1150253 - Part 1: SpiderMonkey should call an embedder-provided callback (57af26988) - Bug 1137536, part 3 - Move the top level DeferredFinalize functions into their own file. r=smaug (ff9931f27) - Bug 1150253 - Part 2: Gecko should provide a callback for SpiderMonkey to enqueue the onGarbageCollection hook runnable; r=mccr8 (80006d63b) - Bug 1150253 - Part 3: Migrate onGarbageCollection tests; r=sfink --HG-- rename : js/src/jit-test/tests/debug/Memory-onGarbageCollection-04.js => js/xpconnect/tests/unit/test_onGarbageCollection-04.js (4e55ef74e) - Bug 1160567 - Assert that object derived types are not exposed in the API; r=jonco (f45b01740) - Bug 1160163 - Refactor arena decommit so we don't have to pass dummy thing kind to allocateArena() r=terrence (11bf82efb) - Bug 1157382 - Fix possible data race caused by accessing the mark bits of cells in another runtime r=terrence (ed6e851d0) - Bug 1163059 - Add a more convenient wrapper for isAtomsZone; r=sfink (7f2c4352a) - Bug 1163790 - Part 1: Share unboxed trace list traversal between tenuring and marking; r=bhackett (be44e89d3) - pointer style (ab055fdfa) - Bug 1163790 - Part 2: Share inlined Class tracing between marking and tenuring; r=bhackett (8af0179a9) - poitner style and whitespace cleanup (7e2c17317) - Bug 891107 - Part 1: Show information about value, type, function, and argument number in type conversion error messages in js-ctypes. r=jorendorff (2e461d5d3) - Bug 1143281 - Check argument type in StructType.prototype.addressOfField. r=jorendorff (2d218c368) - Bug 891107 - Part 2: Report argument length error as TypeError in js-ctypes. r=jorendorff (0e2fa7f9a) - Bug 891107 - Part 3: Report argument type error as TypeError in js-ctypes. r=jorendorff (19d4604da) - Bug 987514, part 1 - Move the contents of jsreflect.h into the sole file that includes it (jsreflect.cpp). No change in behavior. r=Waldo. (b5d7dd976) - pointer style (949c6d6b9) - Bug 1157415 - Tweak XPConnect stack size for 32bit Windows. (r=bholley) (59a75dcbf) - Bug 1158223 - Tweak XPConnect stack size on 64bit Windows. (r=bholley) (e6ccaf2a9) - No bug - Bump the Windows stack frame size. r=shu (3fcdcb6d1) - Bug 1010556 - Bump ASAN kTrustedScriptBuffer constant, to account for the new frame size. r=bholley (3b9ae31f6) - Bug 1067610 - Refactor backtracking allocator to handle grouped registers better, r=sunfish. (a4ea2aa7e) - Bug 1167815 - Switch toMoveOperand to pass by value. r=bhackett (0156abb47) - pointer style (0ebc66f85) - Bug 1168807 - Move MacroAssemblerSpecific::framePushed_ fields to the generic MacroAssembler. r=jandem (28caaac48) - Bug 1168750 - SharedStubs: (part1) Rename BaselineRegisters.h and BaselineHelpers.h, r=jandem (ac876a39d) - Bug 1155618 - Report allocation failure to context for baseline ICStubs r=jandem (04ef0592b) - Bug 1157231 - Optimize calls to own property setters. r=efaust (734e9a9ff) - Bug 1141865 - Part 1: Make two ICCall_Fallback, one for constructing invocations. (r=jandem) (707c2db57) - pointer style (f73948d66) - Bug 1157624: A few AsmJSValidate cleanups; r=luke (1f801ed0d) - Bug 1157624: Remove asm.js ternary optimizations and activate the FoldTest optimization pass for asm.js; r=luke (3be4020ca) - Bug 1157624: A few free asm.js tests; r=luke (63a07c8c8) - Bug 1155211 - SIMD: rename lane mutators - load/store. r=bbouvier (e0ded7107) - Bug 1147403 part 1 - Move Sprinter into its own header and add a FILE & LifoAlloc variants. r=h4writer (2d80c984b) - pointer style (b2dffa01f) - Bug 1159899: IonMonkey: Fix folding of ~~x, r=nbp (f77eae89d) - Bug 1154971 - ValueNumbering: Skip finding the leader if the simplified instruction existed before the simplification. r=sunfish (59262ac7f) - coding style and reshuffle (6b9ad2920) - Bug 1147403 part 2 - IonMonkey: Use GenericPrinter& instead of FILE* for *::dump functions. r=h4writer (a7f0e88fb) - pointer style (62208cb4e) - Bug 979094 - Fix ending location of variable declaration. r=jimb (af438060c) - Bug 748550 - Remove support for |for (... = ... in/of ...)| now that ES6 has removed it. r=jorendorff (23683a4c8) - Bug 1155472 - Reorder the various statement items in Parser::statement to correspond to the ordering in the Statement grammar production. r=efaust (d828017c1) - Bug 1151931 - Part 1: Avoid warning about unreachable code after return statement in some asm.js tests. r=Waldo (ab85fb859) - Bug 1153656 - Test class only if available in semicolon-less-return.js. r=efaust (0b0e6bbc6) - Bug 1151931 - Part 2: Warn about unreachable code after return statement. r=Waldo (a2ec0aa5f) - Bug 1158547 - Removes the useless declaration in Parser.cpp. r=jorendorff (9ac67193b) - pointer style (f72b5c598) - Bug 1155472 - Add the ES6 grammar parametrization to all the Parser methods, so that the permissibility of |in|, |yield| as keyword, &c. is specified directly, not by inspecting instantaneous statefulness. Don't change the statefulness yet, tho -- stop relying on it (where appropriate) in a later patch. r=efaust (589e7d8c1)
1204 lines
34 KiB
C++
1204 lines
34 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 "mozilla/UniquePtr.h"
|
|
|
|
#include "jscntxt.h"
|
|
|
|
#include "builtin/Eval.h"
|
|
#include "frontend/BytecodeCompiler.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;
|
|
using mozilla::UniquePtr;
|
|
|
|
bool
|
|
js::obj_construct(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
RootedObject obj(cx, nullptr);
|
|
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 (!ValueToId<CanGC>(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. */
|
|
static bool
|
|
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;
|
|
}
|
|
|
|
/* ES5 15.2.4.3. */
|
|
static bool
|
|
obj_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
JS_CHECK_RECURSION(cx, return false);
|
|
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
/* Step 1. */
|
|
RootedObject obj(cx, ToObject(cx, args.thisv()));
|
|
if (!obj)
|
|
return false;
|
|
|
|
/* Steps 2-4. */
|
|
RootedId id(cx, NameToId(cx->names().toString));
|
|
return obj->callMethod(cx, id, 0, nullptr, args.rval());
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// ES6 draft rev27 (2014/08/24) 19.1.2.9 Object.getPrototypeOf(O)
|
|
bool
|
|
js::obj_getPrototypeOf(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;
|
|
|
|
/* Step 3. */
|
|
RootedObject proto(cx);
|
|
if (!GetPrototype(cx, obj, &proto))
|
|
return false;
|
|
args.rval().setObjectOrNull(proto);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
RootedObject setPrototypeOf(cx, &args.callee());
|
|
if (!GlobalObject::warnOnceAboutPrototypeMutation(cx, setPrototypeOf))
|
|
return false;
|
|
|
|
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 (!ValueToId<CanGC>(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]);
|
|
UniquePtr<char[], JS::FreePolicy> 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 (!ValueToId<CanGC>(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());
|
|
}
|
|
|
|
// ES6 draft rev27 (2014/08/24) 19.1.2.14 Object.keys(O)
|
|
static bool
|
|
obj_keys(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY);
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
namespace js {
|
|
|
|
/* ES6 draft rev 25 (2014 May 22) 19.1.2.8.1 */
|
|
bool
|
|
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;
|
|
}
|
|
|
|
} // namespace js
|
|
|
|
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 (!ValueToId<CanGC>(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 (!StandardDefineProperty(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 draft rev27 (2014/08/24) 19.1.2.11 Object.isExtensible(O)
|
|
static bool
|
|
obj_isExtensible(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
// Step 1.
|
|
bool extensible = false;
|
|
|
|
// Step 2.
|
|
if (args.get(0).isObject()) {
|
|
RootedObject obj(cx, &args.get(0).toObject());
|
|
if (!IsExtensible(cx, obj, &extensible))
|
|
return false;
|
|
}
|
|
args.rval().setBoolean(extensible);
|
|
return true;
|
|
}
|
|
|
|
// 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);
|
|
|
|
// Do this here, rather than after the this-check so even likely-buggy
|
|
// use of the __proto__ setter on unacceptable values, where no subsequent
|
|
// use occurs on an acceptable value, will trigger a warning.
|
|
RootedObject callee(cx, &args.callee());
|
|
if (!GlobalObject::warnOnceAboutPrototypeMutation(cx, callee))
|
|
return false;
|
|
|
|
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_FN(js_toLocaleString_str, obj_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,JSPROP_DEFINE_LATE),
|
|
JS_SELF_HOSTED_FN(js_defineSetter_str, "ObjectDefineSetter", 2,JSPROP_DEFINE_LATE),
|
|
JS_SELF_HOSTED_FN(js_lookupGetter_str, "ObjectLookupGetter", 1,JSPROP_DEFINE_LATE),
|
|
JS_SELF_HOSTED_FN(js_lookupSetter_str, "ObjectLookupSetter", 1,JSPROP_DEFINE_LATE),
|
|
#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,JSPROP_DEFINE_LATE),
|
|
JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
|
|
JS_FN("setPrototypeOf", obj_setPrototypeOf, 2,0),
|
|
JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
|
|
JS_FN("keys", obj_keys, 1,0),
|
|
JS_SELF_HOSTED_FN("values", "ObjectValues", 1,JSPROP_DEFINE_LATE),
|
|
JS_SELF_HOSTED_FN("entries", "ObjectEntries", 1,JSPROP_DEFINE_LATE),
|
|
JS_FN("is", obj_is, 2,0),
|
|
JS_FN("defineProperty", obj_defineProperty, 3,0),
|
|
JS_FN("defineProperties", obj_defineProperties, 2,0),
|
|
JS_FN("create", obj_create, 2,0),
|
|
JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
|
|
JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1,0),
|
|
JS_FN("isExtensible", obj_isExtensible, 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;
|
|
|
|
/*
|
|
* 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*> self(cx, cx->global());
|
|
|
|
/* ES5 15.1.2.1. */
|
|
RootedId evalId(cx, NameToId(cx->names().eval));
|
|
JSObject* evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS);
|
|
if (!evalobj)
|
|
return false;
|
|
self->setOriginalEval(evalobj);
|
|
|
|
RootedObject intrinsicsHolder(cx);
|
|
bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(self);
|
|
if (isSelfHostingGlobal) {
|
|
intrinsicsHolder = self;
|
|
} else {
|
|
intrinsicsHolder = NewObjectWithGivenProto<PlainObject>(cx, proto, TenuredObject);
|
|
if (!intrinsicsHolder)
|
|
return false;
|
|
}
|
|
self->setIntrinsicsHolder(intrinsicsHolder);
|
|
/* Define a property 'global' with the current global as its value. */
|
|
RootedValue global(cx, ObjectValue(*self));
|
|
if (!DefineProperty(cx, intrinsicsHolder, cx->names().global, global,
|
|
nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Define self-hosted functions after setting the intrinsics holder
|
|
* (which is needed to define self-hosted functions)
|
|
*/
|
|
if (!isSelfHostingGlobal) {
|
|
if (!JS_DefineFunctions(cx, ctor, object_static_methods, OnlyDefineLateProperties))
|
|
return false;
|
|
if (!JS_DefineFunctions(cx, proto, object_methods, OnlyDefineLateProperties))
|
|
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 (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, self->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, /* convert */
|
|
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_;
|