michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/ArgumentsObject-inl.h" michael@0: michael@0: #include "jsinfer.h" michael@0: michael@0: #ifdef JS_ION michael@0: #include "jit/IonFrames.h" michael@0: #endif michael@0: #include "vm/GlobalObject.h" michael@0: #include "vm/Stack.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "vm/Stack-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::gc; michael@0: michael@0: static void michael@0: CopyStackFrameArguments(const AbstractFramePtr frame, HeapValue *dst, unsigned totalArgs) michael@0: { michael@0: JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit()); michael@0: michael@0: JS_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs); michael@0: michael@0: /* Copy arguments. */ michael@0: Value *src = frame.argv(); michael@0: Value *end = src + totalArgs; michael@0: while (src != end) michael@0: (dst++)->init(*src++); michael@0: } michael@0: michael@0: /* static */ void michael@0: ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, michael@0: ArgumentsData *data) michael@0: { michael@0: JSScript *script = frame.script(); michael@0: if (frame.fun()->isHeavyweight() && script->argsObjAliasesFormals()) { michael@0: obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj())); michael@0: for (AliasedFormalIter fi(script); fi; fi++) michael@0: data->args[fi.frameIndex()] = JS::MagicValueUint32(fi.scopeSlot()); michael@0: } michael@0: } michael@0: michael@0: #if defined(JS_ION) michael@0: /* static */ void michael@0: ArgumentsObject::MaybeForwardToCallObject(jit::IonJSFrameLayout *frame, HandleObject callObj, michael@0: JSObject *obj, ArgumentsData *data) michael@0: { michael@0: JSFunction *callee = jit::CalleeTokenToFunction(frame->calleeToken()); michael@0: JSScript *script = callee->nonLazyScript(); michael@0: if (callee->isHeavyweight() && script->argsObjAliasesFormals()) { michael@0: JS_ASSERT(callObj && callObj->is()); michael@0: obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get())); michael@0: for (AliasedFormalIter fi(script); fi; fi++) michael@0: data->args[fi.frameIndex()] = JS::MagicValueUint32(fi.scopeSlot()); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: struct CopyFrameArgs michael@0: { michael@0: AbstractFramePtr frame_; michael@0: michael@0: CopyFrameArgs(AbstractFramePtr frame) michael@0: : frame_(frame) michael@0: { } michael@0: michael@0: void copyArgs(JSContext *, HeapValue *dst, unsigned totalArgs) const { michael@0: CopyStackFrameArguments(frame_, dst, totalArgs); michael@0: } michael@0: michael@0: /* michael@0: * If a call object exists and the arguments object aliases formals, the michael@0: * call object is the canonical location for formals. michael@0: */ michael@0: void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) { michael@0: ArgumentsObject::MaybeForwardToCallObject(frame_, obj, data); michael@0: } michael@0: }; michael@0: michael@0: #if defined(JS_ION) michael@0: struct CopyIonJSFrameArgs michael@0: { michael@0: jit::IonJSFrameLayout *frame_; michael@0: HandleObject callObj_; michael@0: michael@0: CopyIonJSFrameArgs(jit::IonJSFrameLayout *frame, HandleObject callObj) michael@0: : frame_(frame), callObj_(callObj) michael@0: { } michael@0: michael@0: void copyArgs(JSContext *, HeapValue *dstBase, unsigned totalArgs) const { michael@0: unsigned numActuals = frame_->numActualArgs(); michael@0: unsigned numFormals = jit::CalleeTokenToFunction(frame_->calleeToken())->nargs(); michael@0: JS_ASSERT(numActuals <= totalArgs); michael@0: JS_ASSERT(numFormals <= totalArgs); michael@0: JS_ASSERT(Max(numActuals, numFormals) == totalArgs); michael@0: michael@0: /* Copy all arguments. */ michael@0: Value *src = frame_->argv() + 1; /* +1 to skip this. */ michael@0: Value *end = src + numActuals; michael@0: HeapValue *dst = dstBase; michael@0: while (src != end) michael@0: (dst++)->init(*src++); michael@0: michael@0: if (numActuals < numFormals) { michael@0: HeapValue *dstEnd = dstBase + totalArgs; michael@0: while (dst != dstEnd) michael@0: (dst++)->init(UndefinedValue()); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * If a call object exists and the arguments object aliases formals, the michael@0: * call object is the canonical location for formals. michael@0: */ michael@0: void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) { michael@0: ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data); michael@0: } michael@0: }; michael@0: #endif michael@0: michael@0: struct CopyScriptFrameIterArgs michael@0: { michael@0: ScriptFrameIter &iter_; michael@0: michael@0: CopyScriptFrameIterArgs(ScriptFrameIter &iter) michael@0: : iter_(iter) michael@0: { } michael@0: michael@0: void copyArgs(JSContext *cx, HeapValue *dstBase, unsigned totalArgs) const { michael@0: /* Copy actual arguments. */ michael@0: iter_.unaliasedForEachActual(cx, CopyToHeap(dstBase)); michael@0: michael@0: /* Define formals which are not part of the actuals. */ michael@0: unsigned numActuals = iter_.numActualArgs(); michael@0: unsigned numFormals = iter_.callee()->nargs(); michael@0: JS_ASSERT(numActuals <= totalArgs); michael@0: JS_ASSERT(numFormals <= totalArgs); michael@0: JS_ASSERT(Max(numActuals, numFormals) == totalArgs); michael@0: michael@0: if (numActuals < numFormals) { michael@0: HeapValue *dst = dstBase + numActuals, *dstEnd = dstBase + totalArgs; michael@0: while (dst != dstEnd) michael@0: (dst++)->init(UndefinedValue()); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Ion frames are copying every argument onto the stack, other locations are michael@0: * invalid. michael@0: */ michael@0: void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) { michael@0: if (!iter_.isJit()) michael@0: ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: /* static */ ArgumentsObject * michael@0: ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction callee, unsigned numActuals, michael@0: CopyArgs ©) michael@0: { michael@0: RootedObject proto(cx, callee->global().getOrCreateObjectPrototype(cx)); michael@0: if (!proto) michael@0: return nullptr; michael@0: michael@0: bool strict = callee->strict(); michael@0: const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_; michael@0: michael@0: RootedTypeObject type(cx, cx->getNewType(clasp, proto.get())); michael@0: if (!type) michael@0: return nullptr; michael@0: michael@0: JSObject *metadata = nullptr; michael@0: if (!NewObjectMetadata(cx, &metadata)) michael@0: return nullptr; michael@0: michael@0: RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto), michael@0: proto->getParent(), metadata, FINALIZE_KIND, michael@0: BaseShape::INDEXED)); michael@0: if (!shape) michael@0: return nullptr; michael@0: michael@0: unsigned numFormals = callee->nargs(); michael@0: unsigned numDeletedWords = NumWordsForBitArrayOfLength(numActuals); michael@0: unsigned numArgs = Max(numActuals, numFormals); michael@0: unsigned numBytes = offsetof(ArgumentsData, args) + michael@0: numDeletedWords * sizeof(size_t) + michael@0: numArgs * sizeof(Value); michael@0: michael@0: ArgumentsData *data = (ArgumentsData *)cx->malloc_(numBytes); michael@0: if (!data) michael@0: return nullptr; michael@0: michael@0: JSObject *obj = JSObject::create(cx, FINALIZE_KIND, GetInitialHeap(GenericObject, clasp), michael@0: shape, type); michael@0: if (!obj) { michael@0: js_free(data); michael@0: return nullptr; michael@0: } michael@0: michael@0: data->numArgs = numArgs; michael@0: data->callee.init(ObjectValue(*callee.get())); michael@0: data->script = script; michael@0: michael@0: /* Copy [0, numArgs) into data->slots. */ michael@0: HeapValue *dst = data->args, *dstEnd = data->args + numArgs; michael@0: copy.copyArgs(cx, dst, numArgs); michael@0: michael@0: data->deletedBits = reinterpret_cast(dstEnd); michael@0: ClearAllBitArrayElements(data->deletedBits, numDeletedWords); michael@0: michael@0: obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT)); michael@0: obj->initFixedSlot(DATA_SLOT, PrivateValue(data)); michael@0: michael@0: copy.maybeForwardToCallObject(obj, data); michael@0: michael@0: ArgumentsObject &argsobj = obj->as(); michael@0: JS_ASSERT(argsobj.initialLength() == numActuals); michael@0: JS_ASSERT(!argsobj.hasOverriddenLength()); michael@0: return &argsobj; michael@0: } michael@0: michael@0: ArgumentsObject * michael@0: ArgumentsObject::createExpected(JSContext *cx, AbstractFramePtr frame) michael@0: { michael@0: JS_ASSERT(frame.script()->needsArgsObj()); michael@0: RootedScript script(cx, frame.script()); michael@0: RootedFunction callee(cx, frame.callee()); michael@0: CopyFrameArgs copy(frame); michael@0: ArgumentsObject *argsobj = create(cx, script, callee, frame.numActualArgs(), copy); michael@0: if (!argsobj) michael@0: return nullptr; michael@0: michael@0: frame.initArgsObj(*argsobj); michael@0: return argsobj; michael@0: } michael@0: michael@0: ArgumentsObject * michael@0: ArgumentsObject::createUnexpected(JSContext *cx, ScriptFrameIter &iter) michael@0: { michael@0: RootedScript script(cx, iter.script()); michael@0: RootedFunction callee(cx, iter.callee()); michael@0: CopyScriptFrameIterArgs copy(iter); michael@0: return create(cx, script, callee, iter.numActualArgs(), copy); michael@0: } michael@0: michael@0: ArgumentsObject * michael@0: ArgumentsObject::createUnexpected(JSContext *cx, AbstractFramePtr frame) michael@0: { michael@0: RootedScript script(cx, frame.script()); michael@0: RootedFunction callee(cx, frame.callee()); michael@0: CopyFrameArgs copy(frame); michael@0: return create(cx, script, callee, frame.numActualArgs(), copy); michael@0: } michael@0: michael@0: #if defined(JS_ION) michael@0: ArgumentsObject * michael@0: ArgumentsObject::createForIon(JSContext *cx, jit::IonJSFrameLayout *frame, HandleObject scopeChain) michael@0: { michael@0: jit::CalleeToken token = frame->calleeToken(); michael@0: JS_ASSERT(jit::CalleeTokenIsFunction(token)); michael@0: RootedScript script(cx, jit::ScriptFromCalleeToken(token)); michael@0: RootedFunction callee(cx, jit::CalleeTokenToFunction(token)); michael@0: RootedObject callObj(cx, scopeChain->is() ? scopeChain.get() : nullptr); michael@0: CopyIonJSFrameArgs copy(frame, callObj); michael@0: return create(cx, script, callee, frame->numActualArgs(), copy); michael@0: } michael@0: #endif michael@0: michael@0: static bool michael@0: args_delProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) michael@0: { michael@0: ArgumentsObject &argsobj = obj->as(); michael@0: if (JSID_IS_INT(id)) { michael@0: unsigned arg = unsigned(JSID_TO_INT(id)); michael@0: if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) michael@0: argsobj.markElementDeleted(arg); michael@0: } else if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: argsobj.markLengthOverridden(); michael@0: } else if (JSID_IS_ATOM(id, cx->names().callee)) { michael@0: argsobj.as().clearCallee(); michael@0: } michael@0: *succeeded = true; michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ArgGetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) michael@0: { michael@0: if (!obj->is()) michael@0: return true; michael@0: michael@0: NormalArgumentsObject &argsobj = obj->as(); michael@0: if (JSID_IS_INT(id)) { michael@0: /* michael@0: * arg can exceed the number of arguments if a script changed the michael@0: * prototype to point to another Arguments object with a bigger argc. michael@0: */ michael@0: unsigned arg = unsigned(JSID_TO_INT(id)); michael@0: if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) michael@0: vp.set(argsobj.element(arg)); michael@0: } else if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: if (!argsobj.hasOverriddenLength()) michael@0: vp.setInt32(argsobj.initialLength()); michael@0: } else { michael@0: JS_ASSERT(JSID_IS_ATOM(id, cx->names().callee)); michael@0: if (!argsobj.callee().isMagic(JS_OVERWRITTEN_CALLEE)) michael@0: vp.set(argsobj.callee()); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ArgSetter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) michael@0: { michael@0: if (!obj->is()) michael@0: return true; michael@0: michael@0: unsigned attrs; michael@0: if (!baseops::GetAttributes(cx, obj, id, &attrs)) michael@0: return false; michael@0: JS_ASSERT(!(attrs & JSPROP_READONLY)); michael@0: attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */ michael@0: michael@0: NormalArgumentsObject &argsobj = obj->as(); michael@0: RootedScript script(cx, argsobj.containingScript()); michael@0: michael@0: if (JSID_IS_INT(id)) { michael@0: unsigned arg = unsigned(JSID_TO_INT(id)); michael@0: if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) { michael@0: argsobj.setElement(cx, arg, vp); michael@0: if (arg < script->functionNonDelazifying()->nargs()) michael@0: types::TypeScript::SetArgument(cx, script, arg, vp); michael@0: return true; michael@0: } michael@0: } else { michael@0: JS_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee)); michael@0: } michael@0: michael@0: /* michael@0: * For simplicity we use delete/define to replace the property with one michael@0: * backed by the default Object getter and setter. Note that we rely on michael@0: * args_delProperty to clear the corresponding reserved slot so the GC can michael@0: * collect its value. Note also that we must define the property instead michael@0: * of setting it in case the user has changed the prototype to an object michael@0: * that has a setter for this id. michael@0: */ michael@0: bool succeeded; michael@0: return baseops::DeleteGeneric(cx, obj, id, &succeeded) && michael@0: baseops::DefineGeneric(cx, obj, id, vp, nullptr, nullptr, attrs); michael@0: } michael@0: michael@0: static bool michael@0: args_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) michael@0: { michael@0: objp.set(nullptr); michael@0: michael@0: Rooted argsobj(cx, &obj->as()); michael@0: michael@0: unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; michael@0: if (JSID_IS_INT(id)) { michael@0: uint32_t arg = uint32_t(JSID_TO_INT(id)); michael@0: if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) michael@0: return true; michael@0: michael@0: attrs |= JSPROP_ENUMERATE; michael@0: } else if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: if (argsobj->hasOverriddenLength()) michael@0: return true; michael@0: } else { michael@0: if (!JSID_IS_ATOM(id, cx->names().callee)) michael@0: return true; michael@0: michael@0: if (argsobj->callee().isMagic(JS_OVERWRITTEN_CALLEE)) michael@0: return true; michael@0: } michael@0: michael@0: if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, ArgGetter, ArgSetter, attrs)) michael@0: return false; michael@0: michael@0: objp.set(argsobj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: args_enumerate(JSContext *cx, HandleObject obj) michael@0: { michael@0: Rooted argsobj(cx, &obj->as()); michael@0: RootedId id(cx); michael@0: michael@0: /* michael@0: * Trigger reflection in args_resolve using a series of js_LookupProperty michael@0: * calls. michael@0: */ michael@0: int argc = int(argsobj->initialLength()); michael@0: for (int i = -2; i != argc; i++) { michael@0: id = (i == -2) michael@0: ? NameToId(cx->names().length) michael@0: : (i == -1) michael@0: ? NameToId(cx->names().callee) michael@0: : INT_TO_JSID(i); michael@0: michael@0: RootedObject pobj(cx); michael@0: RootedShape prop(cx); michael@0: if (!baseops::LookupProperty(cx, argsobj, id, &pobj, &prop)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: StrictArgGetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) michael@0: { michael@0: if (!obj->is()) michael@0: return true; michael@0: michael@0: StrictArgumentsObject &argsobj = obj->as(); michael@0: michael@0: if (JSID_IS_INT(id)) { michael@0: /* michael@0: * arg can exceed the number of arguments if a script changed the michael@0: * prototype to point to another Arguments object with a bigger argc. michael@0: */ michael@0: unsigned arg = unsigned(JSID_TO_INT(id)); michael@0: if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) michael@0: vp.set(argsobj.element(arg)); michael@0: } else { michael@0: JS_ASSERT(JSID_IS_ATOM(id, cx->names().length)); michael@0: if (!argsobj.hasOverriddenLength()) michael@0: vp.setInt32(argsobj.initialLength()); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: StrictArgSetter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) michael@0: { michael@0: if (!obj->is()) michael@0: return true; michael@0: michael@0: unsigned attrs; michael@0: if (!baseops::GetAttributes(cx, obj, id, &attrs)) michael@0: return false; michael@0: JS_ASSERT(!(attrs & JSPROP_READONLY)); michael@0: attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */ michael@0: michael@0: Rooted argsobj(cx, &obj->as()); michael@0: michael@0: if (JSID_IS_INT(id)) { michael@0: unsigned arg = unsigned(JSID_TO_INT(id)); michael@0: if (arg < argsobj->initialLength()) { michael@0: argsobj->setElement(cx, arg, vp); michael@0: return true; michael@0: } michael@0: } else { michael@0: JS_ASSERT(JSID_IS_ATOM(id, cx->names().length)); michael@0: } michael@0: michael@0: /* michael@0: * For simplicity we use delete/define to replace the property with one michael@0: * backed by the default Object getter and setter. Note that we rely on michael@0: * args_delProperty to clear the corresponding reserved slot so the GC can michael@0: * collect its value. michael@0: */ michael@0: bool succeeded; michael@0: return baseops::DeleteGeneric(cx, argsobj, id, &succeeded) && michael@0: baseops::DefineGeneric(cx, argsobj, id, vp, nullptr, nullptr, attrs); michael@0: } michael@0: michael@0: static bool michael@0: strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) michael@0: { michael@0: objp.set(nullptr); michael@0: michael@0: Rooted argsobj(cx, &obj->as()); michael@0: michael@0: unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; michael@0: PropertyOp getter = StrictArgGetter; michael@0: StrictPropertyOp setter = StrictArgSetter; michael@0: michael@0: if (JSID_IS_INT(id)) { michael@0: uint32_t arg = uint32_t(JSID_TO_INT(id)); michael@0: if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) michael@0: return true; michael@0: michael@0: attrs |= JSPROP_ENUMERATE; michael@0: } else if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: if (argsobj->hasOverriddenLength()) michael@0: return true; michael@0: } else { michael@0: if (!JSID_IS_ATOM(id, cx->names().callee) && !JSID_IS_ATOM(id, cx->names().caller)) michael@0: return true; michael@0: michael@0: attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; michael@0: getter = CastAsPropertyOp(argsobj->global().getThrowTypeError()); michael@0: setter = CastAsStrictPropertyOp(argsobj->global().getThrowTypeError()); michael@0: } michael@0: michael@0: if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs)) michael@0: return false; michael@0: michael@0: objp.set(argsobj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: strictargs_enumerate(JSContext *cx, HandleObject obj) michael@0: { michael@0: Rooted argsobj(cx, &obj->as()); michael@0: michael@0: /* michael@0: * Trigger reflection in strictargs_resolve using a series of michael@0: * js_LookupProperty calls. michael@0: */ michael@0: RootedObject pobj(cx); michael@0: RootedShape prop(cx); michael@0: RootedId id(cx); michael@0: michael@0: // length michael@0: id = NameToId(cx->names().length); michael@0: if (!baseops::LookupProperty(cx, argsobj, id, &pobj, &prop)) michael@0: return false; michael@0: michael@0: // callee michael@0: id = NameToId(cx->names().callee); michael@0: if (!baseops::LookupProperty(cx, argsobj, id, &pobj, &prop)) michael@0: return false; michael@0: michael@0: // caller michael@0: id = NameToId(cx->names().caller); michael@0: if (!baseops::LookupProperty(cx, argsobj, id, &pobj, &prop)) michael@0: return false; michael@0: michael@0: for (uint32_t i = 0, argc = argsobj->initialLength(); i < argc; i++) { michael@0: id = INT_TO_JSID(i); michael@0: if (!baseops::LookupProperty(cx, argsobj, id, &pobj, &prop)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ArgumentsObject::finalize(FreeOp *fop, JSObject *obj) michael@0: { michael@0: fop->free_(reinterpret_cast(obj->as().data())); michael@0: } michael@0: michael@0: void michael@0: ArgumentsObject::trace(JSTracer *trc, JSObject *obj) michael@0: { michael@0: ArgumentsObject &argsobj = obj->as(); michael@0: ArgumentsData *data = argsobj.data(); michael@0: MarkValue(trc, &data->callee, js_callee_str); michael@0: MarkValueRange(trc, data->numArgs, data->args, js_arguments_str); michael@0: MarkScriptUnbarriered(trc, &data->script, "script"); michael@0: } michael@0: michael@0: /* michael@0: * The classes below collaborate to lazily reflect and synchronize actual michael@0: * argument values, argument count, and callee function object stored in a michael@0: * stack frame with their corresponding property values in the frame's michael@0: * arguments object. michael@0: */ michael@0: const Class NormalArgumentsObject::class_ = { michael@0: "Arguments", michael@0: JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | michael@0: JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE, michael@0: JS_PropertyStub, /* addProperty */ michael@0: args_delProperty, michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: args_enumerate, michael@0: reinterpret_cast(args_resolve), michael@0: JS_ConvertStub, michael@0: ArgumentsObject::finalize, michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: ArgumentsObject::trace michael@0: }; michael@0: michael@0: /* michael@0: * Strict mode arguments is significantly less magical than non-strict mode michael@0: * arguments, so it is represented by a different class while sharing some michael@0: * functionality. michael@0: */ michael@0: const Class StrictArgumentsObject::class_ = { michael@0: "Arguments", michael@0: JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | michael@0: JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE, michael@0: JS_PropertyStub, /* addProperty */ michael@0: args_delProperty, michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: strictargs_enumerate, michael@0: reinterpret_cast(strictargs_resolve), michael@0: JS_ConvertStub, michael@0: ArgumentsObject::finalize, michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: ArgumentsObject::trace michael@0: };