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