LCOV - code coverage report
Current view: top level - js/src/vm - ArgumentsObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 211 491 43.0 %
Date: 2017-07-14 16:53:18 Functions: 21 44 47.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "vm/ArgumentsObject-inl.h"
       8             : 
       9             : #include "mozilla/PodOperations.h"
      10             : 
      11             : #include "jit/JitFrames.h"
      12             : #include "vm/AsyncFunction.h"
      13             : #include "vm/GlobalObject.h"
      14             : #include "vm/Stack.h"
      15             : 
      16             : #include "jsobjinlines.h"
      17             : 
      18             : #include "gc/Nursery-inl.h"
      19             : #include "vm/NativeObject-inl.h"
      20             : #include "vm/Stack-inl.h"
      21             : 
      22             : using namespace js;
      23             : using namespace js::gc;
      24             : 
      25             : /* static */ size_t
      26           0 : RareArgumentsData::bytesRequired(size_t numActuals)
      27             : {
      28           0 :     size_t extraBytes = NumWordsForBitArrayOfLength(numActuals) * sizeof(size_t);
      29           0 :     return offsetof(RareArgumentsData, deletedBits_) + extraBytes;
      30             : }
      31             : 
      32             : /* static */ RareArgumentsData*
      33           0 : RareArgumentsData::create(JSContext* cx, ArgumentsObject* obj)
      34             : {
      35           0 :     size_t bytes = RareArgumentsData::bytesRequired(obj->initialLength());
      36             : 
      37           0 :     uint8_t* data = AllocateObjectBuffer<uint8_t>(cx, obj, bytes);
      38           0 :     if (!data)
      39           0 :         return nullptr;
      40             : 
      41           0 :     mozilla::PodZero(data, bytes);
      42             : 
      43           0 :     return new(data) RareArgumentsData();
      44             : }
      45             : 
      46             : bool
      47           0 : ArgumentsObject::createRareData(JSContext* cx)
      48             : {
      49           0 :     MOZ_ASSERT(!data()->rareData);
      50             : 
      51           0 :     RareArgumentsData* rareData = RareArgumentsData::create(cx, this);
      52           0 :     if (!rareData)
      53           0 :         return false;
      54             : 
      55           0 :     data()->rareData = rareData;
      56           0 :     return true;
      57             : }
      58             : 
      59             : bool
      60           0 : ArgumentsObject::markElementDeleted(JSContext* cx, uint32_t i)
      61             : {
      62           0 :     RareArgumentsData* data = getOrCreateRareData(cx);
      63           0 :     if (!data)
      64           0 :         return false;
      65             : 
      66           0 :     data->markElementDeleted(initialLength(), i);
      67           0 :     return true;
      68             : }
      69             : 
      70             : static void
      71         127 : CopyStackFrameArguments(const AbstractFramePtr frame, GCPtrValue* dst, unsigned totalArgs)
      72             : {
      73         127 :     MOZ_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
      74             : 
      75         127 :     MOZ_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs);
      76             : 
      77             :     /* Copy arguments. */
      78         127 :     Value* src = frame.argv();
      79         127 :     Value* end = src + totalArgs;
      80         595 :     while (src != end)
      81         234 :         (dst++)->init(*src++);
      82         127 : }
      83             : 
      84             : /* static */ void
      85         127 : ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, ArgumentsObject* obj,
      86             :                                           ArgumentsData* data)
      87             : {
      88         127 :     JSScript* script = frame.script();
      89         127 :     if (frame.callee()->needsCallObject() && script->argumentsAliasesFormals()) {
      90           6 :         obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj()));
      91          24 :         for (PositionalFormalParameterIter fi(script); fi; fi++) {
      92          18 :             if (fi.closedOver())
      93          14 :                 data->args[fi.argumentSlot()] = MagicEnvSlotValue(fi.location().slot());
      94             :         }
      95             :     }
      96         127 : }
      97             : 
      98             : /* static */ void
      99           0 : ArgumentsObject::MaybeForwardToCallObject(jit::JitFrameLayout* frame, HandleObject callObj,
     100             :                                           ArgumentsObject* obj, ArgumentsData* data)
     101             : {
     102           0 :     JSFunction* callee = jit::CalleeTokenToFunction(frame->calleeToken());
     103           0 :     JSScript* script = callee->nonLazyScript();
     104           0 :     if (callee->needsCallObject() && script->argumentsAliasesFormals()) {
     105           0 :         MOZ_ASSERT(callObj && callObj->is<CallObject>());
     106           0 :         obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get()));
     107           0 :         for (PositionalFormalParameterIter fi(script); fi; fi++) {
     108           0 :             if (fi.closedOver())
     109           0 :                 data->args[fi.argumentSlot()] = MagicEnvSlotValue(fi.location().slot());
     110             :         }
     111             :     }
     112           0 : }
     113             : 
     114             : struct CopyFrameArgs
     115             : {
     116             :     AbstractFramePtr frame_;
     117             : 
     118         127 :     explicit CopyFrameArgs(AbstractFramePtr frame)
     119         127 :       : frame_(frame)
     120         127 :     { }
     121             : 
     122         127 :     void copyArgs(JSContext*, GCPtrValue* dst, unsigned totalArgs) const {
     123         127 :         CopyStackFrameArguments(frame_, dst, totalArgs);
     124         127 :     }
     125             : 
     126             :     /*
     127             :      * If a call object exists and the arguments object aliases formals, the
     128             :      * call object is the canonical location for formals.
     129             :      */
     130         127 :     void maybeForwardToCallObject(ArgumentsObject* obj, ArgumentsData* data) {
     131         127 :         ArgumentsObject::MaybeForwardToCallObject(frame_, obj, data);
     132         127 :     }
     133             : };
     134             : 
     135             : struct CopyJitFrameArgs
     136             : {
     137             :     jit::JitFrameLayout* frame_;
     138             :     HandleObject callObj_;
     139             : 
     140           0 :     CopyJitFrameArgs(jit::JitFrameLayout* frame, HandleObject callObj)
     141           0 :       : frame_(frame), callObj_(callObj)
     142           0 :     { }
     143             : 
     144           0 :     void copyArgs(JSContext*, GCPtrValue* dstBase, unsigned totalArgs) const {
     145           0 :         unsigned numActuals = frame_->numActualArgs();
     146           0 :         unsigned numFormals = jit::CalleeTokenToFunction(frame_->calleeToken())->nargs();
     147           0 :         MOZ_ASSERT(numActuals <= totalArgs);
     148           0 :         MOZ_ASSERT(numFormals <= totalArgs);
     149           0 :         MOZ_ASSERT(Max(numActuals, numFormals) == totalArgs);
     150             : 
     151             :         /* Copy all arguments. */
     152           0 :         Value* src = frame_->argv() + 1;  /* +1 to skip this. */
     153           0 :         Value* end = src + numActuals;
     154           0 :         GCPtrValue* dst = dstBase;
     155           0 :         while (src != end)
     156           0 :             (dst++)->init(*src++);
     157             : 
     158           0 :         if (numActuals < numFormals) {
     159           0 :             GCPtrValue* dstEnd = dstBase + totalArgs;
     160           0 :             while (dst != dstEnd)
     161           0 :                 (dst++)->init(UndefinedValue());
     162             :         }
     163           0 :     }
     164             : 
     165             :     /*
     166             :      * If a call object exists and the arguments object aliases formals, the
     167             :      * call object is the canonical location for formals.
     168             :      */
     169           0 :     void maybeForwardToCallObject(ArgumentsObject* obj, ArgumentsData* data) {
     170           0 :         ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data);
     171           0 :     }
     172             : };
     173             : 
     174             : struct CopyScriptFrameIterArgs
     175             : {
     176             :     ScriptFrameIter& iter_;
     177             : 
     178           0 :     explicit CopyScriptFrameIterArgs(ScriptFrameIter& iter)
     179           0 :       : iter_(iter)
     180           0 :     { }
     181             : 
     182           0 :     void copyArgs(JSContext* cx, GCPtrValue* dstBase, unsigned totalArgs) const {
     183             :         /* Copy actual arguments. */
     184           0 :         iter_.unaliasedForEachActual(cx, CopyToHeap(dstBase));
     185             : 
     186             :         /* Define formals which are not part of the actuals. */
     187           0 :         unsigned numActuals = iter_.numActualArgs();
     188           0 :         unsigned numFormals = iter_.calleeTemplate()->nargs();
     189           0 :         MOZ_ASSERT(numActuals <= totalArgs);
     190           0 :         MOZ_ASSERT(numFormals <= totalArgs);
     191           0 :         MOZ_ASSERT(Max(numActuals, numFormals) == totalArgs);
     192             : 
     193           0 :         if (numActuals < numFormals) {
     194           0 :             GCPtrValue* dst = dstBase + numActuals;
     195           0 :             GCPtrValue* dstEnd = dstBase + totalArgs;
     196           0 :             while (dst != dstEnd)
     197           0 :                 (dst++)->init(UndefinedValue());
     198             :         }
     199           0 :     }
     200             : 
     201             :     /*
     202             :      * Ion frames are copying every argument onto the stack, other locations are
     203             :      * invalid.
     204             :      */
     205           0 :     void maybeForwardToCallObject(ArgumentsObject* obj, ArgumentsData* data) {
     206           0 :         if (!iter_.isIon())
     207           0 :             ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data);
     208           0 :     }
     209             : };
     210             : 
     211             : ArgumentsObject*
     212          12 : ArgumentsObject::createTemplateObject(JSContext* cx, bool mapped)
     213             : {
     214             :     const Class* clasp = mapped
     215          12 :                          ? &MappedArgumentsObject::class_
     216          12 :                          : &UnmappedArgumentsObject::class_;
     217             : 
     218          24 :     RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
     219          12 :     if (!proto)
     220           0 :         return nullptr;
     221             : 
     222          24 :     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto.get())));
     223          12 :     if (!group)
     224           0 :         return nullptr;
     225             : 
     226          24 :     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
     227          24 :                                                       FINALIZE_KIND, BaseShape::INDEXED));
     228          12 :     if (!shape)
     229           0 :         return nullptr;
     230             : 
     231          24 :     AutoSetNewObjectMetadata metadata(cx);
     232             :     JSObject* base;
     233          12 :     JS_TRY_VAR_OR_RETURN_NULL(cx, base, NativeObject::create(cx, FINALIZE_KIND, gc::TenuredHeap,
     234             :                                                              shape, group));
     235             : 
     236          12 :     ArgumentsObject* obj = &base->as<js::ArgumentsObject>();
     237          12 :     obj->initFixedSlot(ArgumentsObject::DATA_SLOT, PrivateValue(nullptr));
     238          12 :     return obj;
     239             : }
     240             : 
     241             : ArgumentsObject*
     242         135 : JSCompartment::maybeArgumentsTemplateObject(bool mapped) const
     243             : {
     244         135 :     return mapped ? mappedArgumentsTemplate_ : unmappedArgumentsTemplate_;
     245             : }
     246             : 
     247             : ArgumentsObject*
     248         127 : JSCompartment::getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped)
     249             : {
     250             :     ReadBarriered<ArgumentsObject*>& obj =
     251         127 :         mapped ? mappedArgumentsTemplate_ : unmappedArgumentsTemplate_;
     252             : 
     253         127 :     ArgumentsObject* templateObj = obj;
     254         127 :     if (templateObj)
     255         115 :         return templateObj;
     256             : 
     257          12 :     templateObj = ArgumentsObject::createTemplateObject(cx, mapped);
     258          12 :     if (!templateObj)
     259           0 :         return nullptr;
     260             : 
     261          12 :     obj.set(templateObj);
     262          12 :     return templateObj;
     263             : }
     264             : 
     265             : template <typename CopyArgs>
     266             : /* static */ ArgumentsObject*
     267         127 : ArgumentsObject::create(JSContext* cx, HandleFunction callee, unsigned numActuals, CopyArgs& copy)
     268             : {
     269         127 :     bool mapped = callee->nonLazyScript()->hasMappedArgsObj();
     270         127 :     ArgumentsObject* templateObj = cx->compartment()->getOrCreateArgumentsTemplateObject(cx, mapped);
     271         127 :     if (!templateObj)
     272           0 :         return nullptr;
     273             : 
     274         254 :     RootedShape shape(cx, templateObj->lastProperty());
     275         254 :     RootedObjectGroup group(cx, templateObj->group());
     276             : 
     277         127 :     unsigned numFormals = callee->nargs();
     278         127 :     unsigned numArgs = Max(numActuals, numFormals);
     279         127 :     unsigned numBytes = ArgumentsData::bytesRequired(numArgs);
     280             : 
     281         254 :     Rooted<ArgumentsObject*> obj(cx);
     282         127 :     ArgumentsData* data = nullptr;
     283             :     {
     284             :         // The copyArgs call below can allocate objects, so add this block scope
     285             :         // to make sure we set the metadata for this arguments object first.
     286         254 :         AutoSetNewObjectMetadata metadata(cx);
     287             : 
     288             :         JSObject* base;
     289         127 :         JS_TRY_VAR_OR_RETURN_NULL(cx, base, NativeObject::create(cx, FINALIZE_KIND,
     290             :                                                                  gc::DefaultHeap, shape, group));
     291         127 :         obj = &base->as<ArgumentsObject>();
     292             : 
     293         127 :         data =
     294         127 :             reinterpret_cast<ArgumentsData*>(AllocateObjectBuffer<uint8_t>(cx, obj, numBytes));
     295         127 :         if (!data) {
     296             :             // Make the object safe for GC.
     297           0 :             obj->initFixedSlot(DATA_SLOT, PrivateValue(nullptr));
     298           0 :             return nullptr;
     299             :         }
     300             : 
     301         127 :         data->numArgs = numArgs;
     302         127 :         data->rareData = nullptr;
     303             : 
     304             :         // Zero the argument Values. This sets each value to DoubleValue(0), which
     305             :         // is safe for GC tracing.
     306         127 :         memset(data->args, 0, numArgs * sizeof(Value));
     307         127 :         MOZ_ASSERT(DoubleValue(0).asRawBits() == 0x0);
     308         127 :         MOZ_ASSERT_IF(numArgs > 0, data->args[0].asRawBits() == 0x0);
     309             : 
     310         127 :         obj->initFixedSlot(DATA_SLOT, PrivateValue(data));
     311         127 :         obj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
     312             :     }
     313         127 :     MOZ_ASSERT(data != nullptr);
     314             : 
     315             :     /* Copy [0, numArgs) into data->slots. */
     316         127 :     copy.copyArgs(cx, data->args, numArgs);
     317             : 
     318         127 :     obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT));
     319             : 
     320         127 :     copy.maybeForwardToCallObject(obj, data);
     321             : 
     322         127 :     MOZ_ASSERT(obj->initialLength() == numActuals);
     323         127 :     MOZ_ASSERT(!obj->hasOverriddenLength());
     324         127 :     return obj;
     325             : }
     326             : 
     327             : ArgumentsObject*
     328         127 : ArgumentsObject::createExpected(JSContext* cx, AbstractFramePtr frame)
     329             : {
     330         127 :     MOZ_ASSERT(frame.script()->needsArgsObj());
     331         254 :     RootedFunction callee(cx, frame.callee());
     332         127 :     CopyFrameArgs copy(frame);
     333         127 :     ArgumentsObject* argsobj = create(cx, callee, frame.numActualArgs(), copy);
     334         127 :     if (!argsobj)
     335           0 :         return nullptr;
     336             : 
     337         127 :     frame.initArgsObj(*argsobj);
     338         127 :     return argsobj;
     339             : }
     340             : 
     341             : ArgumentsObject*
     342           0 : ArgumentsObject::createUnexpected(JSContext* cx, ScriptFrameIter& iter)
     343             : {
     344           0 :     RootedFunction callee(cx, iter.callee(cx));
     345           0 :     CopyScriptFrameIterArgs copy(iter);
     346           0 :     return create(cx, callee, iter.numActualArgs(), copy);
     347             : }
     348             : 
     349             : ArgumentsObject*
     350           0 : ArgumentsObject::createUnexpected(JSContext* cx, AbstractFramePtr frame)
     351             : {
     352           0 :     RootedFunction callee(cx, frame.callee());
     353           0 :     CopyFrameArgs copy(frame);
     354           0 :     return create(cx, callee, frame.numActualArgs(), copy);
     355             : }
     356             : 
     357             : ArgumentsObject*
     358           0 : ArgumentsObject::createForIon(JSContext* cx, jit::JitFrameLayout* frame, HandleObject scopeChain)
     359             : {
     360           0 :     jit::CalleeToken token = frame->calleeToken();
     361           0 :     MOZ_ASSERT(jit::CalleeTokenIsFunction(token));
     362           0 :     RootedFunction callee(cx, jit::CalleeTokenToFunction(token));
     363           0 :     RootedObject callObj(cx, scopeChain->is<CallObject>() ? scopeChain.get() : nullptr);
     364           0 :     CopyJitFrameArgs copy(frame, callObj);
     365           0 :     return create(cx, callee, frame->numActualArgs(), copy);
     366             : }
     367             : 
     368             : /* static */ ArgumentsObject*
     369           0 : ArgumentsObject::finishForIon(JSContext* cx, jit::JitFrameLayout* frame,
     370             :                               JSObject* scopeChain, ArgumentsObject* obj)
     371             : {
     372             :     // JIT code calls this directly (no callVM), because it's faster, so we're
     373             :     // not allowed to GC in here.
     374           0 :     JS::AutoCheckCannotGC nogc;
     375             : 
     376           0 :     JSFunction* callee = jit::CalleeTokenToFunction(frame->calleeToken());
     377           0 :     RootedObject callObj(cx, scopeChain->is<CallObject>() ? scopeChain : nullptr);
     378           0 :     CopyJitFrameArgs copy(frame, callObj);
     379             : 
     380           0 :     unsigned numActuals = frame->numActualArgs();
     381           0 :     unsigned numFormals = callee->nargs();
     382           0 :     unsigned numArgs = Max(numActuals, numFormals);
     383           0 :     unsigned numBytes = ArgumentsData::bytesRequired(numArgs);
     384             : 
     385             :     ArgumentsData* data =
     386           0 :         reinterpret_cast<ArgumentsData*>(AllocateObjectBuffer<uint8_t>(cx, obj, numBytes));
     387           0 :     if (!data) {
     388             :         // Make the object safe for GC. Don't report OOM, the slow path will
     389             :         // retry the allocation.
     390           0 :         cx->recoverFromOutOfMemory();
     391           0 :         obj->initFixedSlot(DATA_SLOT, PrivateValue(nullptr));
     392           0 :         return nullptr;
     393             :     }
     394             : 
     395           0 :     data->numArgs = numArgs;
     396           0 :     data->rareData = nullptr;
     397             : 
     398           0 :     obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT));
     399           0 :     obj->initFixedSlot(DATA_SLOT, PrivateValue(data));
     400           0 :     obj->initFixedSlot(MAYBE_CALL_SLOT, UndefinedValue());
     401           0 :     obj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
     402             : 
     403           0 :     copy.copyArgs(cx, data->args, numArgs);
     404             : 
     405           0 :     if (callObj && callee->needsCallObject())
     406           0 :         copy.maybeForwardToCallObject(obj, data);
     407             : 
     408           0 :     MOZ_ASSERT(obj->initialLength() == numActuals);
     409           0 :     MOZ_ASSERT(!obj->hasOverriddenLength());
     410           0 :     return obj;
     411             : }
     412             : 
     413             : /* static */ bool
     414          17 : ArgumentsObject::obj_delProperty(JSContext* cx, HandleObject obj, HandleId id,
     415             :                                  ObjectOpResult& result)
     416             : {
     417          17 :     ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
     418          17 :     if (JSID_IS_INT(id)) {
     419           0 :         unsigned arg = unsigned(JSID_TO_INT(id));
     420           0 :         if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
     421           0 :             if (!argsobj.markElementDeleted(cx, arg))
     422           0 :                 return false;
     423             :         }
     424          17 :     } else if (JSID_IS_ATOM(id, cx->names().length)) {
     425          17 :         argsobj.markLengthOverridden();
     426           0 :     } else if (JSID_IS_ATOM(id, cx->names().callee)) {
     427           0 :         argsobj.as<MappedArgumentsObject>().markCalleeOverridden();
     428           0 :     } else if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
     429           0 :         argsobj.markIteratorOverridden();
     430             :     }
     431          17 :     return result.succeed();
     432             : }
     433             : 
     434             : /* static */ bool
     435          89 : ArgumentsObject::obj_mayResolve(const JSAtomState& names, jsid id, JSObject*)
     436             : {
     437             :     // Arguments might resolve indexes or Symbol.iterator.
     438          89 :     if (!JSID_IS_ATOM(id))
     439          68 :         return true;
     440             : 
     441          21 :     JSAtom* atom = JSID_TO_ATOM(id);
     442             :     uint32_t index;
     443          21 :     if (atom->isIndex(&index))
     444           0 :         return true;
     445             : 
     446          21 :     return atom == names.length || atom == names.callee;
     447             : }
     448             : 
     449             : static bool
     450         133 : MappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
     451             : {
     452         133 :     MappedArgumentsObject& argsobj = obj->as<MappedArgumentsObject>();
     453         133 :     if (JSID_IS_INT(id)) {
     454             :         /*
     455             :          * arg can exceed the number of arguments if a script changed the
     456             :          * prototype to point to another Arguments object with a bigger argc.
     457             :          */
     458         113 :         unsigned arg = unsigned(JSID_TO_INT(id));
     459         113 :         if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
     460         113 :             vp.set(argsobj.element(arg));
     461          20 :     } else if (JSID_IS_ATOM(id, cx->names().length)) {
     462          20 :         if (!argsobj.hasOverriddenLength())
     463          20 :             vp.setInt32(argsobj.initialLength());
     464             :     } else {
     465           0 :         MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().callee));
     466           0 :         if (!argsobj.hasOverriddenCallee()) {
     467           0 :             RootedFunction callee(cx, &argsobj.callee());
     468           0 :             if (callee->isAsync())
     469           0 :                 vp.setObject(*GetWrappedAsyncFunction(callee));
     470             :             else
     471           0 :                 vp.setObject(*callee);
     472             :         }
     473             :     }
     474         133 :     return true;
     475             : }
     476             : 
     477             : static bool
     478          66 : MappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
     479             :                 ObjectOpResult& result)
     480             : {
     481          66 :     if (!obj->is<MappedArgumentsObject>())
     482           0 :         return result.succeed();
     483          66 :     Handle<MappedArgumentsObject*> argsobj = obj.as<MappedArgumentsObject>();
     484             : 
     485         132 :     Rooted<PropertyDescriptor> desc(cx);
     486          66 :     if (!GetOwnPropertyDescriptor(cx, argsobj, id, &desc))
     487           0 :         return false;
     488          66 :     MOZ_ASSERT(desc.object());
     489          66 :     unsigned attrs = desc.attributes();
     490          66 :     MOZ_ASSERT(!(attrs & JSPROP_READONLY));
     491          66 :     attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
     492             : 
     493         132 :     RootedFunction callee(cx, &argsobj->callee());
     494         132 :     RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
     495          66 :     if (!script)
     496           0 :         return false;
     497             : 
     498          66 :     if (JSID_IS_INT(id)) {
     499          49 :         unsigned arg = unsigned(JSID_TO_INT(id));
     500          49 :         if (arg < argsobj->initialLength() && !argsobj->isElementDeleted(arg)) {
     501          49 :             argsobj->setElement(cx, arg, vp);
     502          49 :             if (arg < script->functionNonDelazifying()->nargs())
     503           0 :                 TypeScript::SetArgument(cx, script, arg, vp);
     504          49 :             return result.succeed();
     505             :         }
     506             :     } else {
     507          17 :         MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee));
     508             :     }
     509             : 
     510             :     /*
     511             :      * For simplicity we use delete/define to replace the property with a
     512             :      * simple data property. Note that we rely on ArgumentsObject::obj_delProperty
     513             :      * to set the corresponding override-bit.
     514             :      * Note also that we must define the property instead of setting it in case
     515             :      * the user has changed the prototype to an object that has a setter for
     516             :      * this id.
     517             :      */
     518          17 :     ObjectOpResult ignored;
     519         102 :     return NativeDeleteProperty(cx, argsobj, id, ignored) &&
     520          68 :            NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result);
     521             : }
     522             : 
     523             : static bool
     524           0 : DefineArgumentsIterator(JSContext* cx, Handle<ArgumentsObject*> argsobj)
     525             : {
     526           0 :     RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
     527           0 :     HandlePropertyName shName = cx->names().ArrayValues;
     528           0 :     RootedAtom name(cx, cx->names().values);
     529           0 :     RootedValue val(cx);
     530           0 :     if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, name, 0, &val))
     531           0 :         return false;
     532           0 :     return NativeDefineProperty(cx, argsobj, iteratorId, val, nullptr, nullptr, JSPROP_RESOLVING);
     533             : }
     534             : 
     535             : /* static */ bool
     536          17 : ArgumentsObject::reifyLength(JSContext* cx, Handle<ArgumentsObject*> obj)
     537             : {
     538          17 :     if (obj->hasOverriddenLength())
     539          17 :         return true;
     540             : 
     541           0 :     RootedId id(cx, NameToId(cx->names().length));
     542           0 :     RootedValue val(cx, Int32Value(obj->initialLength()));
     543           0 :     if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_RESOLVING))
     544           0 :         return false;
     545             : 
     546           0 :     obj->markLengthOverridden();
     547           0 :     return true;
     548             : }
     549             : 
     550             : /* static */ bool
     551           0 : ArgumentsObject::reifyIterator(JSContext* cx, Handle<ArgumentsObject*> obj)
     552             : {
     553           0 :     if (obj->hasOverriddenIterator())
     554           0 :         return true;
     555             : 
     556           0 :     if (!DefineArgumentsIterator(cx, obj))
     557           0 :         return false;
     558             : 
     559           0 :     obj->markIteratorOverridden();
     560           0 :     return true;
     561             : }
     562             : 
     563             : /* static */ bool
     564         134 : MappedArgumentsObject::obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
     565             : {
     566         268 :     Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
     567             : 
     568         134 :     if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
     569           0 :         if (argsobj->hasOverriddenIterator())
     570           0 :             return true;
     571             : 
     572           0 :         if (!DefineArgumentsIterator(cx, argsobj))
     573           0 :             return false;
     574           0 :         *resolvedp = true;
     575           0 :         return true;
     576             :     }
     577             : 
     578         134 :     unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE | JSPROP_RESOLVING;
     579         134 :     if (JSID_IS_INT(id)) {
     580          97 :         uint32_t arg = uint32_t(JSID_TO_INT(id));
     581          97 :         if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
     582          34 :             return true;
     583             : 
     584          63 :         attrs |= JSPROP_ENUMERATE;
     585          37 :     } else if (JSID_IS_ATOM(id, cx->names().length)) {
     586          37 :         if (argsobj->hasOverriddenLength())
     587          17 :             return true;
     588             :     } else {
     589           0 :         if (!JSID_IS_ATOM(id, cx->names().callee))
     590           0 :             return true;
     591             : 
     592           0 :         if (argsobj->hasOverriddenCallee())
     593           0 :             return true;
     594             :     }
     595             : 
     596          83 :     if (!NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue,
     597             :                               MappedArgGetter, MappedArgSetter, attrs))
     598             :     {
     599           0 :         return false;
     600             :     }
     601             : 
     602          83 :     *resolvedp = true;
     603          83 :     return true;
     604             : }
     605             : 
     606             : /* static */ bool
     607           0 : MappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
     608             : {
     609           0 :     Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
     610             : 
     611           0 :     RootedId id(cx);
     612             :     bool found;
     613             : 
     614             :     // Trigger reflection.
     615           0 :     id = NameToId(cx->names().length);
     616           0 :     if (!HasOwnProperty(cx, argsobj, id, &found))
     617           0 :         return false;
     618             : 
     619           0 :     id = NameToId(cx->names().callee);
     620           0 :     if (!HasOwnProperty(cx, argsobj, id, &found))
     621           0 :         return false;
     622             : 
     623           0 :     id = SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator);
     624           0 :     if (!HasOwnProperty(cx, argsobj, id, &found))
     625           0 :         return false;
     626             : 
     627           0 :     for (unsigned i = 0; i < argsobj->initialLength(); i++) {
     628           0 :         id = INT_TO_JSID(i);
     629           0 :         if (!HasOwnProperty(cx, argsobj, id, &found))
     630           0 :             return false;
     631             :     }
     632             : 
     633           0 :     return true;
     634             : }
     635             : 
     636             : // ES 2017 draft 9.4.4.2
     637             : /* static */ bool
     638          17 : MappedArgumentsObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
     639             :                                           Handle<PropertyDescriptor> desc, ObjectOpResult& result)
     640             : {
     641             :     // Step 1.
     642          34 :     Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
     643             : 
     644             :     // Steps 2-3.
     645          17 :     bool isMapped = false;
     646          17 :     if (JSID_IS_INT(id)) {
     647          17 :         unsigned arg = unsigned(JSID_TO_INT(id));
     648          17 :         isMapped = arg < argsobj->initialLength() && !argsobj->isElementDeleted(arg);
     649             :     }
     650             : 
     651             :     // Step 4.
     652          34 :     Rooted<PropertyDescriptor> newArgDesc(cx, desc);
     653          17 :     if (!desc.isAccessorDescriptor() && isMapped) {
     654             :         // In this case the live mapping is supposed to keep working,
     655             :         // we have to pass along the Getter/Setter otherwise they are overwritten.
     656           0 :         newArgDesc.setGetter(MappedArgGetter);
     657           0 :         newArgDesc.setSetter(MappedArgSetter);
     658             :     }
     659             : 
     660             :     // Steps 5-6. NativeDefineProperty will lookup [[Value]] for us.
     661          17 :     if (!NativeDefineProperty(cx, obj.as<NativeObject>(), id, newArgDesc, result))
     662           0 :         return false;
     663             :     // Step 7.
     664          17 :     if (!result.ok())
     665           0 :         return true;
     666             : 
     667             :     // Step 8.
     668          17 :     if (isMapped) {
     669           0 :         unsigned arg = unsigned(JSID_TO_INT(id));
     670           0 :         if (desc.isAccessorDescriptor()) {
     671           0 :             if (!argsobj->markElementDeleted(cx, arg))
     672           0 :                 return false;
     673             :         } else {
     674           0 :             if (desc.hasValue()) {
     675           0 :                 RootedFunction callee(cx, &argsobj->callee());
     676           0 :                 RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
     677           0 :                 if (!script)
     678           0 :                     return false;
     679           0 :                 argsobj->setElement(cx, arg, desc.value());
     680           0 :                 if (arg < script->functionNonDelazifying()->nargs())
     681           0 :                     TypeScript::SetArgument(cx, script, arg, desc.value());
     682             :             }
     683           0 :             if (desc.hasWritable() && !desc.writable()) {
     684           0 :                 if (!argsobj->markElementDeleted(cx, arg))
     685           0 :                     return false;
     686             :             }
     687             :         }
     688             :     }
     689             : 
     690             :     // Step 9.
     691          17 :     return result.succeed();
     692             : }
     693             : 
     694             : static bool
     695           6 : UnmappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
     696             : {
     697           6 :     UnmappedArgumentsObject& argsobj = obj->as<UnmappedArgumentsObject>();
     698             : 
     699           6 :     if (JSID_IS_INT(id)) {
     700             :         /*
     701             :          * arg can exceed the number of arguments if a script changed the
     702             :          * prototype to point to another Arguments object with a bigger argc.
     703             :          */
     704           5 :         unsigned arg = unsigned(JSID_TO_INT(id));
     705           5 :         if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
     706           5 :             vp.set(argsobj.element(arg));
     707             :     } else {
     708           1 :         MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length));
     709           1 :         if (!argsobj.hasOverriddenLength())
     710           1 :             vp.setInt32(argsobj.initialLength());
     711             :     }
     712           6 :     return true;
     713             : }
     714             : 
     715             : static bool
     716           0 : UnmappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
     717             :                   ObjectOpResult& result)
     718             : {
     719           0 :     if (!obj->is<UnmappedArgumentsObject>())
     720           0 :         return result.succeed();
     721           0 :     Handle<UnmappedArgumentsObject*> argsobj = obj.as<UnmappedArgumentsObject>();
     722             : 
     723           0 :     Rooted<PropertyDescriptor> desc(cx);
     724           0 :     if (!GetOwnPropertyDescriptor(cx, argsobj, id, &desc))
     725           0 :         return false;
     726           0 :     MOZ_ASSERT(desc.object());
     727           0 :     unsigned attrs = desc.attributes();
     728           0 :     MOZ_ASSERT(!(attrs & JSPROP_READONLY));
     729           0 :     attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
     730             : 
     731           0 :     if (JSID_IS_INT(id)) {
     732           0 :         unsigned arg = unsigned(JSID_TO_INT(id));
     733           0 :         if (arg < argsobj->initialLength()) {
     734           0 :             argsobj->setElement(cx, arg, vp);
     735           0 :             return result.succeed();
     736             :         }
     737             :     } else {
     738           0 :         MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length));
     739             :     }
     740             : 
     741             :     /*
     742             :      * For simplicity we use delete/define to replace the property with a
     743             :      * simple data property. Note that we rely on ArgumentsObject::obj_delProperty
     744             :      * to set the corresponding override-bit.
     745             :      */
     746           0 :     ObjectOpResult ignored;
     747           0 :     return NativeDeleteProperty(cx, argsobj, id, ignored) &&
     748           0 :            NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result);
     749             : }
     750             : 
     751             : /* static */ bool
     752           6 : UnmappedArgumentsObject::obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
     753             : {
     754          12 :     Rooted<UnmappedArgumentsObject*> argsobj(cx, &obj->as<UnmappedArgumentsObject>());
     755             : 
     756           6 :     if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
     757           0 :         if (argsobj->hasOverriddenIterator())
     758           0 :             return true;
     759             : 
     760           0 :         if (!DefineArgumentsIterator(cx, argsobj))
     761           0 :             return false;
     762           0 :         *resolvedp = true;
     763           0 :         return true;
     764             :     }
     765             : 
     766           6 :     unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
     767           6 :     GetterOp getter = UnmappedArgGetter;
     768           6 :     SetterOp setter = UnmappedArgSetter;
     769             : 
     770           6 :     if (JSID_IS_INT(id)) {
     771           5 :         uint32_t arg = uint32_t(JSID_TO_INT(id));
     772           5 :         if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
     773           0 :             return true;
     774             : 
     775           5 :         attrs |= JSPROP_ENUMERATE;
     776           1 :     } else if (JSID_IS_ATOM(id, cx->names().length)) {
     777           1 :         if (argsobj->hasOverriddenLength())
     778           0 :             return true;
     779             :     } else {
     780           0 :         if (!JSID_IS_ATOM(id, cx->names().callee))
     781           0 :             return true;
     782             : 
     783           0 :         attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
     784           0 :         getter = CastAsGetterOp(argsobj->global().getThrowTypeError());
     785           0 :         setter = CastAsSetterOp(argsobj->global().getThrowTypeError());
     786             :     }
     787             : 
     788           6 :     attrs |= JSPROP_RESOLVING;
     789           6 :     if (!NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs))
     790           0 :         return false;
     791             : 
     792           6 :     *resolvedp = true;
     793           6 :     return true;
     794             : }
     795             : 
     796             : /* static */ bool
     797           0 : UnmappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
     798             : {
     799           0 :     Rooted<UnmappedArgumentsObject*> argsobj(cx, &obj->as<UnmappedArgumentsObject>());
     800             : 
     801           0 :     RootedId id(cx);
     802             :     bool found;
     803             : 
     804             :     // Trigger reflection.
     805           0 :     id = NameToId(cx->names().length);
     806           0 :     if (!HasOwnProperty(cx, argsobj, id, &found))
     807           0 :         return false;
     808             : 
     809           0 :     id = NameToId(cx->names().callee);
     810           0 :     if (!HasOwnProperty(cx, argsobj, id, &found))
     811           0 :         return false;
     812             : 
     813           0 :     id = SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator);
     814           0 :     if (!HasOwnProperty(cx, argsobj, id, &found))
     815           0 :         return false;
     816             : 
     817           0 :     for (unsigned i = 0; i < argsobj->initialLength(); i++) {
     818           0 :         id = INT_TO_JSID(i);
     819           0 :         if (!HasOwnProperty(cx, argsobj, id, &found))
     820           0 :             return false;
     821             :     }
     822             : 
     823           0 :     return true;
     824             : }
     825             : 
     826             : void
     827           0 : ArgumentsObject::finalize(FreeOp* fop, JSObject* obj)
     828             : {
     829           0 :     MOZ_ASSERT(!IsInsideNursery(obj));
     830           0 :     if (obj->as<ArgumentsObject>().data()) {
     831           0 :         fop->free_(obj->as<ArgumentsObject>().maybeRareData());
     832           0 :         fop->free_(obj->as<ArgumentsObject>().data());
     833             :     }
     834           0 : }
     835             : 
     836             : void
     837           3 : ArgumentsObject::trace(JSTracer* trc, JSObject* obj)
     838             : {
     839           3 :     ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
     840           3 :     if (ArgumentsData* data = argsobj.data()) // Template objects have no ArgumentsData.
     841           3 :         TraceRange(trc, data->numArgs, data->begin(), js_arguments_str);
     842           3 : }
     843             : 
     844             : /* static */ size_t
     845           3 : ArgumentsObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src)
     846             : {
     847           3 :     ArgumentsObject* ndst = &dst->as<ArgumentsObject>();
     848           3 :     ArgumentsObject* nsrc = &src->as<ArgumentsObject>();
     849           3 :     MOZ_ASSERT(ndst->data() == nsrc->data());
     850             : 
     851           3 :     Nursery& nursery = dst->zone()->group()->nursery();
     852             : 
     853           3 :     size_t nbytesTotal = 0;
     854           3 :     if (!nursery.isInside(nsrc->data())) {
     855           0 :         nursery.removeMallocedBuffer(nsrc->data());
     856             :     } else {
     857           6 :         AutoEnterOOMUnsafeRegion oomUnsafe;
     858           3 :         uint32_t nbytes = ArgumentsData::bytesRequired(nsrc->data()->numArgs);
     859           3 :         uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
     860           3 :         if (!data)
     861           0 :             oomUnsafe.crash("Failed to allocate ArgumentsObject data while tenuring.");
     862           3 :         ndst->initFixedSlot(DATA_SLOT, PrivateValue(data));
     863             : 
     864           3 :         mozilla::PodCopy(data, reinterpret_cast<uint8_t*>(nsrc->data()), nbytes);
     865           3 :         nbytesTotal += nbytes;
     866             :     }
     867             : 
     868           3 :     if (RareArgumentsData* srcRareData = nsrc->maybeRareData()) {
     869           0 :         if (!nursery.isInside(srcRareData)) {
     870           0 :             nursery.removeMallocedBuffer(srcRareData);
     871             :         } else {
     872           0 :             AutoEnterOOMUnsafeRegion oomUnsafe;
     873           0 :             uint32_t nbytes = RareArgumentsData::bytesRequired(nsrc->initialLength());
     874           0 :             uint8_t* dstRareData = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
     875           0 :             if (!dstRareData)
     876           0 :                 oomUnsafe.crash("Failed to allocate RareArgumentsData data while tenuring.");
     877           0 :             ndst->data()->rareData = (RareArgumentsData*)dstRareData;
     878             : 
     879           0 :             mozilla::PodCopy(dstRareData, reinterpret_cast<uint8_t*>(srcRareData), nbytes);
     880           0 :             nbytesTotal += nbytes;
     881             :         }
     882             :     }
     883             : 
     884           3 :     return nbytesTotal;
     885             : }
     886             : 
     887             : /*
     888             :  * The classes below collaborate to lazily reflect and synchronize actual
     889             :  * argument values, argument count, and callee function object stored in a
     890             :  * stack frame with their corresponding property values in the frame's
     891             :  * arguments object.
     892             :  */
     893             : const ClassOps MappedArgumentsObject::classOps_ = {
     894             :     nullptr,                 /* addProperty */
     895             :     ArgumentsObject::obj_delProperty,
     896             :     nullptr,                 /* getProperty */
     897             :     nullptr,                 /* setProperty */
     898             :     MappedArgumentsObject::obj_enumerate,
     899             :     nullptr,                 /* newEnumerate */
     900             :     MappedArgumentsObject::obj_resolve,
     901             :     ArgumentsObject::obj_mayResolve,
     902             :     ArgumentsObject::finalize,
     903             :     nullptr,                 /* call        */
     904             :     nullptr,                 /* hasInstance */
     905             :     nullptr,                 /* construct   */
     906             :     ArgumentsObject::trace
     907             : };
     908             : 
     909             : const ObjectOps MappedArgumentsObject::objectOps_ = {
     910             :     nullptr,                 /* lookupProperty */
     911             :     MappedArgumentsObject::obj_defineProperty
     912             : };
     913             : 
     914             : const Class MappedArgumentsObject::class_ = {
     915             :     "Arguments",
     916             :     JSCLASS_DELAY_METADATA_BUILDER |
     917             :     JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
     918             :     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
     919             :     JSCLASS_SKIP_NURSERY_FINALIZE |
     920             :     JSCLASS_BACKGROUND_FINALIZE,
     921             :     &MappedArgumentsObject::classOps_,
     922             :     nullptr,
     923             :     nullptr,
     924             :     &MappedArgumentsObject::objectOps_
     925             : };
     926             : 
     927             : /*
     928             :  * Unmapped arguments is significantly less magical than mapped arguments, so
     929             :  * it is represented by a different class while sharing some functionality.
     930             :  */
     931             : const ClassOps UnmappedArgumentsObject::classOps_ = {
     932             :     nullptr,                 /* addProperty */
     933             :     ArgumentsObject::obj_delProperty,
     934             :     nullptr,                 /* getProperty */
     935             :     nullptr,                 /* setProperty */
     936             :     UnmappedArgumentsObject::obj_enumerate,
     937             :     nullptr,                 /* newEnumerate */
     938             :     UnmappedArgumentsObject::obj_resolve,
     939             :     ArgumentsObject::obj_mayResolve,
     940             :     ArgumentsObject::finalize,
     941             :     nullptr,                 /* call        */
     942             :     nullptr,                 /* hasInstance */
     943             :     nullptr,                 /* construct   */
     944             :     ArgumentsObject::trace
     945             : };
     946             : 
     947             : const Class UnmappedArgumentsObject::class_ = {
     948             :     "Arguments",
     949             :     JSCLASS_DELAY_METADATA_BUILDER |
     950             :     JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
     951             :     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
     952             :     JSCLASS_SKIP_NURSERY_FINALIZE |
     953             :     JSCLASS_BACKGROUND_FINALIZE,
     954             :     &UnmappedArgumentsObject::classOps_
     955             : };

Generated by: LCOV version 1.13