blob: 3b8630d4615255663779d9d55217f510177586ca [file] [log] [blame]
/* -*- 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/Util.h"
#include "jscntxt.h"
#include "jsobj.h"
#include "frontend/BytecodeCompiler.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
using namespace js;
using namespace js::types;
using js::frontend::IsIdentifier;
using mozilla::ArrayLength;
// Duplicated in jsobj.cpp
static bool
DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
{
AutoIdVector ids(cx);
AutoPropDescArrayRooter descs(cx);
if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
return false;
bool dummy;
for (size_t i = 0, len = ids.length(); i < len; i++) {
if (!DefineProperty(cx, obj, ids.handleAt(i), descs[i], true, &dummy))
return false;
}
return true;
}
JSBool
js::obj_construct(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, NULL);
if (args.length() > 0) {
/* If argv[0] is null or undefined, obj comes back null. */
if (!js_ValueToObjectOrNull(cx, args[0], &obj))
return false;
}
if (!obj) {
/* Make an object whether this was called with 'new' or not. */
JS_ASSERT(!args.length() || args[0].isNullOrUndefined());
if (!NewObjectScriptedCall(cx, &obj))
return false;
}
args.rval().setObject(*obj);
return true;
}
/* ES5 15.2.4.7. */
static JSBool
obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Step 1. */
RootedId id(cx);
if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(0), &id))
return false;
/* Step 2. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Steps 3. */
RootedObject pobj(cx);
RootedShape prop(cx);
if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &prop))
return false;
/* Step 4. */
if (!prop) {
args.rval().setBoolean(false);
return true;
}
if (pobj != obj) {
vp->setBoolean(false);
return true;
}
/* Step 5. */
unsigned attrs;
if (!JSObject::getGenericAttributes(cx, pobj, id, &attrs))
return false;
args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
return true;
}
#if JS_HAS_TOSOURCE
static JSBool
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;
/* 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 false;
if (detector.foundCycle()) {
JSString *str = js_NewStringCopyZ<CanGC>(cx, "{}");
if (!str)
return false;
args.rval().setString(str);
return true;
}
StringBuffer buf(cx);
if (outermost && !buf.append('('))
return false;
if (!buf.append('{'))
return false;
RootedValue v0(cx), v1(cx);
MutableHandleValue val[2] = {&v0, &v1};
RootedString str0(cx), str1(cx);
MutableHandleString gsop[2] = {&str0, &str1};
AutoIdVector idv(cx);
if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &idv))
return false;
bool comma = false;
for (size_t i = 0; i < idv.length(); ++i) {
RootedId id(cx, idv[i]);
RootedObject obj2(cx);
RootedShape shape(cx);
if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape))
return false;
/* Decide early whether we prefer get/set or old getter/setter syntax. */
int valcnt = 0;
if (shape) {
bool doGet = true;
if (obj2->isNative() && !IsImplicitDenseElement(shape)) {
unsigned attrs = shape->attributes();
if (attrs & JSPROP_GETTER) {
doGet = false;
val[valcnt].set(shape->getterValue());
gsop[valcnt].set(cx->names().get);
valcnt++;
}
if (attrs & JSPROP_SETTER) {
doGet = false;
val[valcnt].set(shape->setterValue());
gsop[valcnt].set(cx->names().set);
valcnt++;
}
}
if (doGet) {
valcnt = 1;
gsop[0].set(NULL);
if (!JSObject::getGeneric(cx, obj, obj, id, val[0]))
return false;
}
}
/* Convert id to a linear string. */
RootedValue idv(cx, IdToValue(id));
JSString *s = ToString<CanGC>(cx, idv);
if (!s)
return false;
Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx));
if (!idstr)
return false;
/*
* 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(idstr)
: (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0))
{
s = js_QuoteString(cx, idstr, jschar('\''));
if (!s || !(idstr = s->ensureLinear(cx)))
return false;
}
for (int j = 0; j < valcnt; j++) {
/*
* Censor an accessor descriptor getter or setter part if it's
* undefined.
*/
if (gsop[j] && val[j].isUndefined())
continue;
/* Convert val[j] to its canonical source form. */
RootedString valstr(cx, ValueToSource(cx, val[j]));
if (!valstr)
return false;
const jschar *vchars = valstr->getChars(cx);
if (!vchars)
return false;
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])) {
const jschar *start = vchars;
const jschar *end = vchars + vlength;
uint8_t parenChomp = 0;
if (vchars[0] == '(') {
vchars++;
parenChomp = 1;
}
/* Try to jump "function" keyword. */
if (vchars)
vchars = js_strchr_limit(vchars, ' ', end);
/*
* Jump over the function's name: it can't be encoded as part
* of an ECMA getter or setter.
*/
if (vchars)
vchars = js_strchr_limit(vchars, '(', end);
if (vchars) {
if (*vchars == ' ')
vchars++;
vlength = end - vchars - parenChomp;
} else {
gsop[j].set(NULL);
vchars = start;
}
}
if (comma && !buf.append(", "))
return false;
comma = true;
if (gsop[j])
if (!buf.append(gsop[j]) || !buf.append(' '))
return false;
if (!buf.append(idstr))
return false;
if (!buf.append(gsop[j] ? ' ' : ':'))
return false;
if (!buf.append(vchars, vlength))
return false;
}
}
if (!buf.append('}'))
return false;
if (outermost && !buf.append(')'))
return false;
JSString *str = buf.finishString();
if (!str)
return false;
args.rval().setString(str);
return true;
}
#endif /* JS_HAS_TOSOURCE */
JSString *
JS_BasicObjectToString(JSContext *cx, HandleObject obj)
{
const char *className = JSObject::className(cx, obj);
StringBuffer sb(cx);
if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
!sb.append("]"))
{
return NULL;
}
return sb.finishString();
}
/* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
static JSBool
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 JSBool
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, NULL, args.rval());
}
static JSBool
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;
}
#if OLD_GETTER_SETTER_METHODS
enum DefineType { Getter, Setter };
template<DefineType Type>
static bool
DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!BoxNonStrictThis(cx, args))
return false;
if (args.length() < 2 || !js_IsCallable(args[1])) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_GETTER_OR_SETTER,
Type == Getter ? js_getter_str : js_setter_str);
return false;
}
RootedId id(cx);
if (!ValueToId<CanGC>(cx, args.handleAt(0), &id))
return false;
RootedObject descObj(cx, NewBuiltinClassInstance(cx, &ObjectClass));
if (!descObj)
return false;
JSAtomState &names = cx->names();
RootedValue trueVal(cx, BooleanValue(true));
/* enumerable: true */
if (!JSObject::defineProperty(cx, descObj, names.enumerable, trueVal))
return false;
/* configurable: true */
if (!JSObject::defineProperty(cx, descObj, names.configurable, trueVal))
return false;
/* enumerable: true */
PropertyName *acc = (Type == Getter) ? names.get : names.set;
RootedValue accessorVal(cx, args[1]);
if (!JSObject::defineProperty(cx, descObj, acc, accessorVal))
return false;
RootedObject thisObj(cx, &args.thisv().toObject());
JSBool dummy;
RootedValue descObjValue(cx, ObjectValue(*descObj));
if (!DefineOwnProperty(cx, thisObj, id, descObjValue, &dummy))
return false;
args.rval().setUndefined();
return true;
}
JS_FRIEND_API(JSBool)
js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp)
{
return DefineAccessor<Getter>(cx, argc, vp);
}
JS_FRIEND_API(JSBool)
js::obj_defineSetter(JSContext *cx, unsigned argc, Value *vp)
{
return DefineAccessor<Setter>(cx, argc, vp);
}
static JSBool
obj_lookupGetter(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedId id(cx);
if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(0), &id))
return JS_FALSE;
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return JS_FALSE;
if (obj->isProxy()) {
// The vanilla getter lookup code below requires that the object is
// native. Handle proxies separately.
args.rval().setUndefined();
AutoPropertyDescriptorRooter desc(cx);
if (!Proxy::getPropertyDescriptor(cx, obj, id, &desc, 0))
return JS_FALSE;
if (desc.obj && (desc.attrs & JSPROP_GETTER) && desc.getter)
args.rval().set(CastAsObjectJsval(desc.getter));
return JS_TRUE;
}
RootedObject pobj(cx);
RootedShape shape(cx);
if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
return JS_FALSE;
args.rval().setUndefined();
if (shape) {
if (pobj->isNative() && !IsImplicitDenseElement(shape)) {
if (shape->hasGetterValue())
args.rval().set(shape->getterValue());
}
}
return JS_TRUE;
}
static JSBool
obj_lookupSetter(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedId id(cx);
if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(0), &id))
return JS_FALSE;
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return JS_FALSE;
if (obj->isProxy()) {
// The vanilla setter lookup code below requires that the object is
// native. Handle proxies separately.
args.rval().setUndefined();
AutoPropertyDescriptorRooter desc(cx);
if (!Proxy::getPropertyDescriptor(cx, obj, id, &desc, 0))
return JS_FALSE;
if (desc.obj && (desc.attrs & JSPROP_SETTER) && desc.setter)
args.rval().set(CastAsObjectJsval(desc.setter));
return JS_TRUE;
}
RootedObject pobj(cx);
RootedShape shape(cx);
if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
return JS_FALSE;
args.rval().setUndefined();
if (shape) {
if (pobj->isNative() && !IsImplicitDenseElement(shape)) {
if (shape->hasSetterValue())
args.rval().set(shape->setterValue());
}
}
return JS_TRUE;
}
#endif /* OLD_GETTER_SETTER_METHODS */
/* ES5 15.2.3.2. */
JSBool
obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Step 1. */
if (args.length() == 0) {
js_ReportMissingArg(cx, args.calleev(), 0);
return false;
}
if (args[0].isPrimitive()) {
RootedValue val(cx, args[0]);
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
if (!bytes)
return false;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
js_free(bytes);
return false;
}
/* Step 2. */
/*
* Implement [[Prototype]]-getting -- particularly across compartment
* boundaries -- by calling a cached __proto__ getter function.
*/
InvokeArgs args2(cx);
if (!args2.init(0))
return false;
args2.setCallee(cx->global()->protoGetter());
args2.setThis(args[0]);
if (!Invoke(cx, args2))
return false;
args.rval().set(args2.rval());
return true;
}
#if JS_HAS_OBJ_WATCHPOINT
static JSBool
obj_watch_handler(JSContext *cx, JSObject *obj_, jsid id_, jsval old,
jsval *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 };
return Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, nvp);
}
static JSBool
obj_watch(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() <= 1) {
js_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.handleAt(0), &propid))
return false;
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
RootedValue tmp(cx);
unsigned attrs;
if (!CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
return false;
args.rval().setUndefined();
return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
}
static JSBool
obj_unwatch(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
args.rval().setUndefined();
RootedId id(cx);
if (argc != 0) {
if (!ValueToId<CanGC>(cx, args.handleAt(0), &id))
return false;
} else {
id = JSID_VOID;
}
return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
}
#endif /* JS_HAS_OBJ_WATCHPOINT */
/* ECMA 15.2.4.5. */
static JSBool
obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue idValue = args.handleOrUndefinedAt(0);
/* Step 1, 2. */
jsid id;
if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
JSObject *obj = &args.thisv().toObject(), *obj2;
Shape *prop;
if (!obj->isProxy() &&
HasOwnProperty<NoGC>(cx, obj->getOps()->lookupGeneric, obj, id, &obj2, &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;
/* Non-standard code for proxies. */
RootedObject obj2(cx);
RootedShape prop(cx);
if (obj->isProxy()) {
bool has;
if (!Proxy::hasOwn(cx, obj, idRoot, &has))
return false;
args.rval().setBoolean(has);
return true;
}
/* Step 3. */
if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, idRoot, &obj2, &prop))
return false;
/* Step 4,5. */
args.rval().setBoolean(!!prop);
return true;
}
/* ES5 15.2.4.6. */
static JSBool
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;
}
/* ES5 15.2.3.5: Object.create(O [, Properties]) */
static JSBool
obj_create(JSContext *cx, unsigned argc, Value *vp)
{
if (argc == 0) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
"Object.create", "0", "s");
return false;
}
CallArgs args = CallArgsFromVp(argc, vp);
RootedValue v(cx, args[0]);
if (!v.isObjectOrNull()) {
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
if (!bytes)
return false;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
bytes, "not an object or null");
js_free(bytes);
return false;
}
RootedObject proto(cx, v.toObjectOrNull());
/*
* Use the callee's global as the parent of the new object to avoid dynamic
* scoping (i.e., using the caller's global).
*/
RootedObject obj(cx, NewObjectWithGivenProto(cx, &ObjectClass, proto, &args.callee().global()));
if (!obj)
return false;
/* Don't track types or array-ness for objects created here. */
MarkTypeObjectUnknownProperties(cx, obj->type());
/* 15.2.3.5 step 4. */
if (args.hasDefined(1)) {
if (args[1].isPrimitive()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
return false;
}
RootedObject props(cx, &args[1].toObject());
if (!DefineProperties(cx, obj, props))
return false;
}
/* 5. Return obj. */
args.rval().setObject(*obj);
return true;
}
static JSBool
obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyDescriptor", &obj))
return JS_FALSE;
RootedId id(cx);
if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(1), &id))
return JS_FALSE;
return GetOwnPropertyDescriptor(cx, obj, id, args.rval());
}
static JSBool
obj_keys(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.keys", &obj))
return false;
AutoIdVector props(cx);
if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
return false;
AutoValueVector vals(cx);
if (!vals.reserve(props.length()))
return false;
for (size_t i = 0, len = props.length(); i < len; i++) {
jsid id = props[i];
if (JSID_IS_STRING(id)) {
vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
} else if (JSID_IS_INT(id)) {
JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
if (!str)
return false;
vals.infallibleAppend(StringValue(str));
} else {
JS_ASSERT(JSID_IS_OBJECT(id));
}
}
JS_ASSERT(props.length() <= UINT32_MAX);
JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
if (!aobj)
return false;
args.rval().setObject(*aobj);
return true;
}
/* ES6 draft 15.2.3.16 */
static JSBool
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;
}
static JSBool
obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyNames", &obj))
return false;
AutoIdVector keys(cx);
if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
return false;
AutoValueVector vals(cx);
if (!vals.resize(keys.length()))
return false;
for (size_t i = 0, len = keys.length(); i < len; i++) {
jsid id = keys[i];
if (JSID_IS_INT(id)) {
JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
if (!str)
return false;
vals[i].setString(str);
} else if (JSID_IS_ATOM(id)) {
vals[i].setString(JSID_TO_STRING(id));
} else {
vals[i].setObject(*JSID_TO_OBJECT(id));
}
}
JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
if (!aobj)
return false;
args.rval().setObject(*aobj);
return true;
}
/* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
static JSBool
obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperty", &obj))
return false;
RootedId id(cx);
if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(1), &id))
return JS_FALSE;
JSBool junk;
if (!DefineOwnProperty(cx, obj, id, args.handleOrUndefinedAt(2), &junk))
return false;
args.rval().setObject(*obj);
return true;
}
/* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
static JSBool
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, js_GetErrorMessage, NULL, 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);
}
static JSBool
obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.isExtensible", &obj))
return false;
args.rval().setBoolean(obj->isExtensible());
return true;
}
static JSBool
obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.preventExtensions", &obj))
return false;
args.rval().setObject(*obj);
if (!obj->isExtensible())
return true;
return JSObject::preventExtensions(cx, obj);
}
static JSBool
obj_freeze(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.freeze", &obj))
return false;
args.rval().setObject(*obj);
return JSObject::freeze(cx, obj);
}
static JSBool
obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.preventExtensions", &obj))
return false;
bool frozen;
if (!JSObject::isFrozen(cx, obj, &frozen))
return false;
args.rval().setBoolean(frozen);
return true;
}
static JSBool
obj_seal(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.seal", &obj))
return false;
args.rval().setObject(*obj);
return JSObject::seal(cx, obj);
}
static JSBool
obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.isSealed", &obj))
return false;
bool sealed;
if (!JSObject::isSealed(cx, obj, &sealed))
return false;
args.rval().setBoolean(sealed);
return true;
}
const JSFunctionSpec js::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 OLD_GETTER_SETTER_METHODS
JS_FN(js_defineGetter_str, js::obj_defineGetter, 2,0),
JS_FN(js_defineSetter_str, js::obj_defineSetter, 2,0),
JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
#endif
JS_FS_END
};
const JSFunctionSpec js::object_static_methods[] = {
JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
JS_FN("keys", obj_keys, 1,0),
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("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
};