1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
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 #ifndef vm_Interpreter_inl_h
8 #define vm_Interpreter_inl_h
9 
10 #include "vm/Interpreter.h"
11 
12 #include "jsnum.h"
13 
14 #include "jit/Ion.h"
15 #include "js/friend/DumpFunctions.h"
16 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
17 #include "vm/ArgumentsObject.h"
18 #include "vm/BytecodeUtil.h"  // JSDVG_SEARCH_STACK
19 #include "vm/Realm.h"
20 #include "vm/SharedStencil.h"  // GCThingIndex
21 #include "vm/ThrowMsgKind.h"
22 
23 #include "vm/EnvironmentObject-inl.h"
24 #include "vm/GlobalObject-inl.h"
25 #include "vm/JSAtom-inl.h"
26 #include "vm/JSObject-inl.h"
27 #include "vm/ObjectOperations-inl.h"
28 #include "vm/Stack-inl.h"
29 #include "vm/StringType-inl.h"
30 
31 namespace js {
32 
33 /*
34  * Per ES6, lexical declarations may not be accessed in any fashion until they
35  * are initialized (i.e., until the actual declaring statement is
36  * executed). The various LEXICAL opcodes need to check if the slot is an
37  * uninitialized let declaration, represented by the magic value
38  * JS_UNINITIALIZED_LEXICAL.
39  */
IsUninitializedLexical(const Value & val)40 static inline bool IsUninitializedLexical(const Value& val) {
41   // Use whyMagic here because JS_OPTIMIZED_OUT could flow into here.
42   return val.isMagic() && val.whyMagic() == JS_UNINITIALIZED_LEXICAL;
43 }
44 
IsUninitializedLexicalSlot(HandleObject obj,const PropertyResult & prop)45 static inline bool IsUninitializedLexicalSlot(HandleObject obj,
46                                               const PropertyResult& prop) {
47   MOZ_ASSERT(prop.isFound());
48   if (obj->is<WithEnvironmentObject>()) {
49     return false;
50   }
51 
52   // Proxy hooks may return a non-native property.
53   if (prop.isNonNativeProperty()) {
54     return false;
55   }
56 
57   PropertyInfo propInfo = prop.propertyInfo();
58   if (!propInfo.isDataProperty()) {
59     return false;
60   }
61 
62   return IsUninitializedLexical(
63       obj->as<NativeObject>().getSlot(propInfo.slot()));
64 }
65 
CheckUninitializedLexical(JSContext * cx,PropertyName * name_,HandleValue val)66 static inline bool CheckUninitializedLexical(JSContext* cx, PropertyName* name_,
67                                              HandleValue val) {
68   if (IsUninitializedLexical(val)) {
69     RootedPropertyName name(cx, name_);
70     ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name);
71     return false;
72   }
73   return true;
74 }
75 
GetLengthProperty(const Value & lval,MutableHandleValue vp)76 inline bool GetLengthProperty(const Value& lval, MutableHandleValue vp) {
77   /* Optimize length accesses on strings, arrays, and arguments. */
78   if (lval.isString()) {
79     vp.setInt32(lval.toString()->length());
80     return true;
81   }
82   if (lval.isObject()) {
83     JSObject* obj = &lval.toObject();
84     if (obj->is<ArrayObject>()) {
85       vp.setNumber(obj->as<ArrayObject>().length());
86       return true;
87     }
88 
89     if (obj->is<ArgumentsObject>()) {
90       ArgumentsObject* argsobj = &obj->as<ArgumentsObject>();
91       if (!argsobj->hasOverriddenLength()) {
92         uint32_t length = argsobj->initialLength();
93         MOZ_ASSERT(length < INT32_MAX);
94         vp.setInt32(int32_t(length));
95         return true;
96       }
97     }
98   }
99 
100   return false;
101 }
102 
103 enum class GetNameMode { Normal, TypeOf };
104 
105 template <GetNameMode mode>
FetchName(JSContext * cx,HandleObject receiver,HandleObject holder,HandlePropertyName name,const PropertyResult & prop,MutableHandleValue vp)106 inline bool FetchName(JSContext* cx, HandleObject receiver, HandleObject holder,
107                       HandlePropertyName name, const PropertyResult& prop,
108                       MutableHandleValue vp) {
109   if (prop.isNotFound()) {
110     switch (mode) {
111       case GetNameMode::Normal:
112         ReportIsNotDefined(cx, name);
113         return false;
114       case GetNameMode::TypeOf:
115         vp.setUndefined();
116         return true;
117     }
118   }
119 
120   /* Take the slow path if shape was not found in a native object. */
121   if (!receiver->is<NativeObject>() || !holder->is<NativeObject>()) {
122     Rooted<jsid> id(cx, NameToId(name));
123     if (!GetProperty(cx, receiver, receiver, id, vp)) {
124       return false;
125     }
126   } else {
127     PropertyInfo propInfo = prop.propertyInfo();
128     if (propInfo.isDataProperty()) {
129       /* Fast path for Object instance properties. */
130       vp.set(holder->as<NativeObject>().getSlot(propInfo.slot()));
131     } else {
132       // Unwrap 'with' environments for reasons given in
133       // GetNameBoundInEnvironment.
134       RootedObject normalized(cx, MaybeUnwrapWithEnvironment(receiver));
135       RootedId id(cx, NameToId(name));
136       if (!NativeGetExistingProperty(cx, normalized, holder.as<NativeObject>(),
137                                      id, propInfo, vp)) {
138         return false;
139       }
140     }
141   }
142 
143   // We do our own explicit checking for |this|
144   if (name == cx->names().dotThis) {
145     return true;
146   }
147 
148   // NAME operations are the slow paths already, so unconditionally check
149   // for uninitialized lets.
150   return CheckUninitializedLexical(cx, name, vp);
151 }
152 
FetchNameNoGC(NativeObject * pobj,PropertyResult prop,Value * vp)153 inline bool FetchNameNoGC(NativeObject* pobj, PropertyResult prop, Value* vp) {
154   if (prop.isNotFound()) {
155     return false;
156   }
157 
158   PropertyInfo propInfo = prop.propertyInfo();
159   if (!propInfo.isDataProperty()) {
160     return false;
161   }
162 
163   *vp = pobj->getSlot(propInfo.slot());
164   return !IsUninitializedLexical(*vp);
165 }
166 
167 template <js::GetNameMode mode>
GetEnvironmentName(JSContext * cx,HandleObject envChain,HandlePropertyName name,MutableHandleValue vp)168 inline bool GetEnvironmentName(JSContext* cx, HandleObject envChain,
169                                HandlePropertyName name, MutableHandleValue vp) {
170   {
171     PropertyResult prop;
172     JSObject* obj = nullptr;
173     NativeObject* pobj = nullptr;
174     if (LookupNameNoGC(cx, name, envChain, &obj, &pobj, &prop)) {
175       if (FetchNameNoGC(pobj, prop, vp.address())) {
176         return true;
177       }
178     }
179   }
180 
181   PropertyResult prop;
182   RootedObject obj(cx), pobj(cx);
183   if (!LookupName(cx, name, envChain, &obj, &pobj, &prop)) {
184     return false;
185   }
186 
187   return FetchName<mode>(cx, obj, pobj, name, prop, vp);
188 }
189 
HasOwnProperty(JSContext * cx,HandleValue val,HandleValue idValue,bool * result)190 inline bool HasOwnProperty(JSContext* cx, HandleValue val, HandleValue idValue,
191                            bool* result) {
192   // As an optimization, provide a fast path when rooting is not necessary and
193   // we can safely retrieve the object's shape.
194   jsid id;
195   if (val.isObject() && idValue.isPrimitive() &&
196       PrimitiveValueToId<NoGC>(cx, idValue, &id)) {
197     JSObject* obj = &val.toObject();
198     PropertyResult prop;
199     if (obj->is<NativeObject>() &&
200         NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id,
201                                       &prop)) {
202       *result = prop.isFound();
203       return true;
204     }
205   }
206 
207   // Step 1.
208   RootedId key(cx);
209   if (!ToPropertyKey(cx, idValue, &key)) {
210     return false;
211   }
212 
213   // Step 2.
214   RootedObject obj(cx, ToObject(cx, val));
215   if (!obj) {
216     return false;
217   }
218 
219   // Step 3.
220   return HasOwnProperty(cx, obj, key, result);
221 }
222 
GetIntrinsicOperation(JSContext * cx,HandleScript script,jsbytecode * pc,MutableHandleValue vp)223 inline bool GetIntrinsicOperation(JSContext* cx, HandleScript script,
224                                   jsbytecode* pc, MutableHandleValue vp) {
225   RootedPropertyName name(cx, script->getName(pc));
226   return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
227 }
228 
SetIntrinsicOperation(JSContext * cx,JSScript * script,jsbytecode * pc,HandleValue val)229 inline bool SetIntrinsicOperation(JSContext* cx, JSScript* script,
230                                   jsbytecode* pc, HandleValue val) {
231   RootedPropertyName name(cx, script->getName(pc));
232   return GlobalObject::setIntrinsicValue(cx, cx->global(), name, val);
233 }
234 
SetAliasedVarOperation(JSContext * cx,JSScript * script,jsbytecode * pc,EnvironmentObject & obj,EnvironmentCoordinate ec,const Value & val,MaybeCheckTDZ checkTDZ)235 inline void SetAliasedVarOperation(JSContext* cx, JSScript* script,
236                                    jsbytecode* pc, EnvironmentObject& obj,
237                                    EnvironmentCoordinate ec, const Value& val,
238                                    MaybeCheckTDZ checkTDZ) {
239   MOZ_ASSERT_IF(checkTDZ, !IsUninitializedLexical(obj.aliasedBinding(ec)));
240   obj.setAliasedBinding(cx, ec, val);
241 }
242 
SetNameOperation(JSContext * cx,JSScript * script,jsbytecode * pc,HandleObject env,HandleValue val)243 inline bool SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc,
244                              HandleObject env, HandleValue val) {
245   MOZ_ASSERT(JSOp(*pc) == JSOp::SetName || JSOp(*pc) == JSOp::StrictSetName ||
246              JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName);
247   MOZ_ASSERT_IF(
248       (JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName) &&
249           !script->hasNonSyntacticScope(),
250       env == cx->global() || env == &cx->global()->lexicalEnvironment() ||
251           env->is<RuntimeLexicalErrorObject>());
252 
253   bool strict =
254       JSOp(*pc) == JSOp::StrictSetName || JSOp(*pc) == JSOp::StrictSetGName;
255   RootedPropertyName name(cx, script->getName(pc));
256 
257   // In strict mode, assigning to an undeclared global variable is an
258   // error. To detect this, we call NativeSetProperty directly and pass
259   // Unqualified. It stores the error, if any, in |result|.
260   bool ok;
261   ObjectOpResult result;
262   RootedId id(cx, NameToId(name));
263   RootedValue receiver(cx, ObjectValue(*env));
264   if (env->isUnqualifiedVarObj()) {
265     RootedNativeObject varobj(cx);
266     if (env->is<DebugEnvironmentProxy>()) {
267       varobj =
268           &env->as<DebugEnvironmentProxy>().environment().as<NativeObject>();
269     } else {
270       varobj = &env->as<NativeObject>();
271     }
272     MOZ_ASSERT(!varobj->getOpsSetProperty());
273     ok = NativeSetProperty<Unqualified>(cx, varobj, id, val, receiver, result);
274   } else {
275     ok = SetProperty(cx, env, id, val, receiver, result);
276   }
277   return ok && result.checkStrictModeError(cx, env, id, strict);
278 }
279 
InitGlobalLexicalOperation(JSContext * cx,ExtensibleLexicalEnvironmentObject * lexicalEnv,JSScript * script,jsbytecode * pc,HandleValue value)280 inline void InitGlobalLexicalOperation(
281     JSContext* cx, ExtensibleLexicalEnvironmentObject* lexicalEnv,
282     JSScript* script, jsbytecode* pc, HandleValue value) {
283   MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
284                 lexicalEnv == &cx->global()->lexicalEnvironment());
285   MOZ_ASSERT(JSOp(*pc) == JSOp::InitGLexical);
286 
287   mozilla::Maybe<PropertyInfo> prop =
288       lexicalEnv->lookup(cx, script->getName(pc));
289   MOZ_ASSERT(prop.isSome());
290   MOZ_ASSERT(IsUninitializedLexical(lexicalEnv->getSlot(prop->slot())));
291 
292   lexicalEnv->setSlot(prop->slot(), value);
293 }
294 
InitPropertyOperation(JSContext * cx,JSOp op,HandleObject obj,HandlePropertyName name,HandleValue rhs)295 inline bool InitPropertyOperation(JSContext* cx, JSOp op, HandleObject obj,
296                                   HandlePropertyName name, HandleValue rhs) {
297   unsigned propAttrs = GetInitDataPropAttrs(op);
298   return DefineDataProperty(cx, obj, name, rhs, propAttrs);
299 }
300 
NegOperation(JSContext * cx,MutableHandleValue val,MutableHandleValue res)301 static MOZ_ALWAYS_INLINE bool NegOperation(JSContext* cx,
302                                            MutableHandleValue val,
303                                            MutableHandleValue res) {
304   /*
305    * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
306    * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
307    * results, -0.0 or INT32_MAX + 1, are double values.
308    */
309   int32_t i;
310   if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) {
311     res.setInt32(-i);
312     return true;
313   }
314 
315   if (!ToNumeric(cx, val)) {
316     return false;
317   }
318 
319   if (val.isBigInt()) {
320     return BigInt::negValue(cx, val, res);
321   }
322 
323   res.setNumber(-val.toNumber());
324   return true;
325 }
326 
IncOperation(JSContext * cx,HandleValue val,MutableHandleValue res)327 static MOZ_ALWAYS_INLINE bool IncOperation(JSContext* cx, HandleValue val,
328                                            MutableHandleValue res) {
329   int32_t i;
330   if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) {
331     res.setInt32(i + 1);
332     return true;
333   }
334 
335   if (val.isNumber()) {
336     res.setNumber(val.toNumber() + 1);
337     return true;
338   }
339 
340   MOZ_ASSERT(val.isBigInt(), "+1 only callable on result of JSOp::ToNumeric");
341   return BigInt::incValue(cx, val, res);
342 }
343 
DecOperation(JSContext * cx,HandleValue val,MutableHandleValue res)344 static MOZ_ALWAYS_INLINE bool DecOperation(JSContext* cx, HandleValue val,
345                                            MutableHandleValue res) {
346   int32_t i;
347   if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) {
348     res.setInt32(i - 1);
349     return true;
350   }
351 
352   if (val.isNumber()) {
353     res.setNumber(val.toNumber() - 1);
354     return true;
355   }
356 
357   MOZ_ASSERT(val.isBigInt(), "-1 only callable on result of JSOp::ToNumeric");
358   return BigInt::decValue(cx, val, res);
359 }
360 
ToPropertyKeyOperation(JSContext * cx,HandleValue idval,MutableHandleValue res)361 static MOZ_ALWAYS_INLINE bool ToPropertyKeyOperation(JSContext* cx,
362                                                      HandleValue idval,
363                                                      MutableHandleValue res) {
364   if (idval.isInt32()) {
365     res.set(idval);
366     return true;
367   }
368 
369   RootedId id(cx);
370   if (!ToPropertyKey(cx, idval, &id)) {
371     return false;
372   }
373 
374   res.set(IdToValue(id));
375   return true;
376 }
377 
GetObjectElementOperation(JSContext * cx,JSOp op,JS::HandleObject obj,JS::HandleValue receiver,HandleValue key,MutableHandleValue res)378 static MOZ_ALWAYS_INLINE bool GetObjectElementOperation(
379     JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleValue receiver,
380     HandleValue key, MutableHandleValue res) {
381   MOZ_ASSERT(op == JSOp::GetElem || op == JSOp::GetElemSuper);
382   MOZ_ASSERT_IF(op == JSOp::GetElem, obj == &receiver.toObject());
383 
384   do {
385     uint32_t index;
386     if (IsDefinitelyIndex(key, &index)) {
387       if (GetElementNoGC(cx, obj, receiver, index, res.address())) {
388         break;
389       }
390 
391       if (!GetElement(cx, obj, receiver, index, res)) {
392         return false;
393       }
394       break;
395     }
396 
397     if (key.isString()) {
398       JSString* str = key.toString();
399       JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
400       if (!name) {
401         return false;
402       }
403       if (name->isIndex(&index)) {
404         if (GetElementNoGC(cx, obj, receiver, index, res.address())) {
405           break;
406         }
407       } else {
408         if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(),
409                             res.address())) {
410           break;
411         }
412       }
413     }
414 
415     RootedId id(cx);
416     if (!ToPropertyKey(cx, key, &id)) {
417       return false;
418     }
419     if (!GetProperty(cx, obj, receiver, id, res)) {
420       return false;
421     }
422   } while (false);
423 
424   cx->debugOnlyCheck(res);
425   return true;
426 }
427 
GetPrimitiveElementOperation(JSContext * cx,JS::HandleValue receiver,int receiverIndex,HandleValue key,MutableHandleValue res)428 static MOZ_ALWAYS_INLINE bool GetPrimitiveElementOperation(
429     JSContext* cx, JS::HandleValue receiver, int receiverIndex, HandleValue key,
430     MutableHandleValue res) {
431   // FIXME: Bug 1234324 We shouldn't be boxing here.
432   RootedObject boxed(
433       cx, ToObjectFromStackForPropertyAccess(cx, receiver, receiverIndex, key));
434   if (!boxed) {
435     return false;
436   }
437 
438   do {
439     uint32_t index;
440     if (IsDefinitelyIndex(key, &index)) {
441       if (GetElementNoGC(cx, boxed, receiver, index, res.address())) {
442         break;
443       }
444 
445       if (!GetElement(cx, boxed, receiver, index, res)) {
446         return false;
447       }
448       break;
449     }
450 
451     if (key.isString()) {
452       JSString* str = key.toString();
453       JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
454       if (!name) {
455         return false;
456       }
457       if (name->isIndex(&index)) {
458         if (GetElementNoGC(cx, boxed, receiver, index, res.address())) {
459           break;
460         }
461       } else {
462         if (GetPropertyNoGC(cx, boxed, receiver, name->asPropertyName(),
463                             res.address())) {
464           break;
465         }
466       }
467     }
468 
469     RootedId id(cx);
470     if (!ToPropertyKey(cx, key, &id)) {
471       return false;
472     }
473     if (!GetProperty(cx, boxed, receiver, id, res)) {
474       return false;
475     }
476   } while (false);
477 
478   cx->debugOnlyCheck(res);
479   return true;
480 }
481 
GetElementOperationWithStackIndex(JSContext * cx,HandleValue lref,int lrefIndex,HandleValue rref,MutableHandleValue res)482 static MOZ_ALWAYS_INLINE bool GetElementOperationWithStackIndex(
483     JSContext* cx, HandleValue lref, int lrefIndex, HandleValue rref,
484     MutableHandleValue res) {
485   uint32_t index;
486   if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
487     JSString* str = lref.toString();
488     if (index < str->length()) {
489       str = cx->staticStrings().getUnitStringForElement(cx, str, index);
490       if (!str) {
491         return false;
492       }
493       res.setString(str);
494       return true;
495     }
496   }
497 
498   if (lref.isPrimitive()) {
499     RootedValue thisv(cx, lref);
500     return GetPrimitiveElementOperation(cx, thisv, lrefIndex, rref, res);
501   }
502 
503   RootedObject obj(cx, &lref.toObject());
504   RootedValue thisv(cx, lref);
505   return GetObjectElementOperation(cx, JSOp::GetElem, obj, thisv, rref, res);
506 }
507 
508 // Wrapper for callVM from JIT.
GetElementOperation(JSContext * cx,HandleValue lref,HandleValue rref,MutableHandleValue res)509 static MOZ_ALWAYS_INLINE bool GetElementOperation(JSContext* cx,
510                                                   HandleValue lref,
511                                                   HandleValue rref,
512                                                   MutableHandleValue res) {
513   return GetElementOperationWithStackIndex(cx, lref, JSDVG_SEARCH_STACK, rref,
514                                            res);
515 }
516 
TypeOfOperation(const Value & v,JSRuntime * rt)517 static MOZ_ALWAYS_INLINE JSString* TypeOfOperation(const Value& v,
518                                                    JSRuntime* rt) {
519   JSType type = js::TypeOfValue(v);
520   return TypeName(type, *rt->commonNames);
521 }
522 
InitElemOperation(JSContext * cx,jsbytecode * pc,HandleObject obj,HandleValue idval,HandleValue val)523 static MOZ_ALWAYS_INLINE bool InitElemOperation(JSContext* cx, jsbytecode* pc,
524                                                 HandleObject obj,
525                                                 HandleValue idval,
526                                                 HandleValue val) {
527   MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
528 
529   RootedId id(cx);
530   if (!ToPropertyKey(cx, idval, &id)) {
531     return false;
532   }
533 
534   unsigned flags = GetInitDataPropAttrs(JSOp(*pc));
535   if (id.isPrivateName()) {
536     // Clear enumerate flag off of private names.
537     flags &= ~JSPROP_ENUMERATE;
538   }
539   return DefineDataProperty(cx, obj, id, val, flags);
540 }
541 
CheckPrivateFieldOperation(JSContext * cx,jsbytecode * pc,HandleValue val,HandleValue idval,bool * result)542 static MOZ_ALWAYS_INLINE bool CheckPrivateFieldOperation(JSContext* cx,
543                                                          jsbytecode* pc,
544                                                          HandleValue val,
545                                                          HandleValue idval,
546                                                          bool* result) {
547   // Result had better not be a nullptr.
548   MOZ_ASSERT(result);
549 
550   ThrowCondition condition;
551   ThrowMsgKind msgKind;
552   GetCheckPrivateFieldOperands(pc, &condition, &msgKind);
553 
554   // When we are using OnlyCheckRhs, we are implementing PrivateInExpr
555   // This requires we throw if the rhs is not an object;
556   //
557   // The InlineCache for CheckPrivateField already checks for a
558   // non-object rhs and refuses to attach in that circumstance.
559   if (condition == ThrowCondition::OnlyCheckRhs) {
560     if (!val.isObject()) {
561       ReportInNotObjectError(cx, idval, val);
562       return false;
563     }
564   }
565 
566   // js::DumpValue(idval.get());
567   // js::DumpValue(val.get());
568 
569   MOZ_ASSERT(idval.isSymbol());
570   MOZ_ASSERT(idval.toSymbol()->isPrivateName());
571 
572   if (!HasOwnProperty(cx, val, idval, result)) {
573     return false;
574   }
575 
576   if (!CheckPrivateFieldWillThrow(condition, *result)) {
577     return true;
578   }
579 
580   // Throw!
581   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
582                             ThrowMsgKindToErrNum(msgKind));
583   return false;
584 }
585 
InitElemIncOperation(JSContext * cx,HandleArrayObject arr,uint32_t index,HandleValue val)586 inline bool InitElemIncOperation(JSContext* cx, HandleArrayObject arr,
587                                  uint32_t index, HandleValue val) {
588   if (index == INT32_MAX) {
589     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
590                               JSMSG_SPREAD_TOO_LARGE);
591     return false;
592   }
593 
594   // If val is a hole, do not call DefineDataElement.
595   if (val.isMagic(JS_ELEMENTS_HOLE)) {
596     // Always call SetLengthProperty even if this is not the last element
597     // initialiser, because this may be followed by a SpreadElement loop,
598     // which will not set the array length if nothing is spread.
599     return SetLengthProperty(cx, arr, index + 1);
600   }
601 
602   return DefineDataElement(cx, arr, index, val, JSPROP_ENUMERATE);
603 }
604 
ProcessCallSiteObjOperation(JSContext * cx,HandleScript script,jsbytecode * pc)605 static inline ArrayObject* ProcessCallSiteObjOperation(JSContext* cx,
606                                                        HandleScript script,
607                                                        jsbytecode* pc) {
608   MOZ_ASSERT(JSOp(*pc) == JSOp::CallSiteObj);
609 
610   RootedArrayObject cso(cx, &script->getObject(pc)->as<ArrayObject>());
611 
612   if (cso->isExtensible()) {
613     RootedObject raw(cx, script->getObject(GET_GCTHING_INDEX(pc).next()));
614     MOZ_ASSERT(raw->is<ArrayObject>());
615 
616     RootedValue rawValue(cx, ObjectValue(*raw));
617     if (!DefineDataProperty(cx, cso, cx->names().raw, rawValue, 0)) {
618       return nullptr;
619     }
620     if (!FreezeObject(cx, raw)) {
621       return nullptr;
622     }
623     if (!FreezeObject(cx, cso)) {
624       return nullptr;
625     }
626   }
627 
628   return cso;
629 }
630 
631 inline JSFunction* ReportIfNotFunction(
632     JSContext* cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT) {
633   if (v.isObject() && v.toObject().is<JSFunction>()) {
634     return &v.toObject().as<JSFunction>();
635   }
636 
637   ReportIsNotFunction(cx, v, -1, construct);
638   return nullptr;
639 }
640 
641 } /* namespace js */
642 
643 #endif /* vm_Interpreter_inl_h */
644