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