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 : #ifndef vm_Interpreter_inl_h
8 : #define vm_Interpreter_inl_h
9 :
10 : #include "vm/Interpreter.h"
11 :
12 : #include "jscompartment.h"
13 : #include "jsnum.h"
14 : #include "jsstr.h"
15 :
16 : #include "jit/Ion.h"
17 : #include "vm/ArgumentsObject.h"
18 :
19 : #include "jsatominlines.h"
20 : #include "jsobjinlines.h"
21 :
22 : #include "vm/EnvironmentObject-inl.h"
23 : #include "vm/Stack-inl.h"
24 : #include "vm/String-inl.h"
25 : #include "vm/UnboxedObject-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 : */
39 : static inline bool
40 5478 : IsOptimizedArguments(AbstractFramePtr frame, MutableHandleValue vp)
41 : {
42 5478 : if (vp.isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj())
43 0 : vp.setObject(frame.argsObj());
44 5478 : 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 : */
52 : static inline bool
53 113 : GuardFunApplyArgumentsOptimization(JSContext* cx, AbstractFramePtr frame, CallArgs& args)
54 : {
55 113 : if (args.length() == 2 && IsOptimizedArguments(frame, args[1])) {
56 0 : if (!IsNativeFunction(args.calleev(), js::fun_apply)) {
57 0 : RootedScript script(cx, frame.script());
58 0 : if (!JSScript::argumentsOptimizationFailed(cx, script))
59 0 : return false;
60 0 : args[1].setObject(frame.argsObj());
61 : }
62 : }
63 :
64 113 : return true;
65 : }
66 :
67 : /*
68 : * Per ES6, lexical declarations may not be accessed in any fashion until they
69 : * are initialized (i.e., until the actual declaring statement is
70 : * executed). The various LEXICAL opcodes need to check if the slot is an
71 : * uninitialized let declaration, represented by the magic value
72 : * JS_UNINITIALIZED_LEXICAL.
73 : */
74 : static inline bool
75 82946 : IsUninitializedLexical(const Value& val)
76 : {
77 : // Use whyMagic here because JS_OPTIMIZED_ARGUMENTS could flow into here.
78 82946 : return val.isMagic() && val.whyMagic() == JS_UNINITIALIZED_LEXICAL;
79 : }
80 :
81 : static inline bool
82 44 : IsUninitializedLexicalSlot(HandleObject obj, Handle<PropertyResult> prop)
83 : {
84 44 : MOZ_ASSERT(prop);
85 44 : if (obj->is<WithEnvironmentObject>())
86 0 : return false;
87 :
88 : // Proxy hooks may return a non-native property.
89 44 : if (prop.isNonNativeProperty())
90 0 : return false;
91 :
92 44 : Shape* shape = prop.shape();
93 132 : if (!shape->hasSlot() ||
94 88 : !shape->hasDefaultGetter() ||
95 44 : !shape->hasDefaultSetter())
96 : {
97 0 : return false;
98 : }
99 44 : MOZ_ASSERT(obj->as<NativeObject>().containsPure(shape));
100 44 : return IsUninitializedLexical(obj->as<NativeObject>().getSlot(shape->slot()));
101 : }
102 :
103 : static inline void
104 0 : ReportUninitializedLexical(JSContext* cx, HandlePropertyName name)
105 : {
106 0 : ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name);
107 0 : }
108 :
109 : static inline void
110 0 : ReportUninitializedLexical(JSContext* cx, HandleScript script, jsbytecode* pc)
111 : {
112 0 : ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, script, pc);
113 0 : }
114 :
115 : static inline bool
116 2294 : CheckUninitializedLexical(JSContext* cx, PropertyName* name_, HandleValue val)
117 : {
118 2294 : if (IsUninitializedLexical(val)) {
119 0 : RootedPropertyName name(cx, name_);
120 0 : ReportUninitializedLexical(cx, name);
121 0 : return false;
122 : }
123 2294 : return true;
124 : }
125 :
126 : static inline bool
127 771 : CheckUninitializedLexical(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue val)
128 : {
129 771 : if (IsUninitializedLexical(val)) {
130 0 : ReportUninitializedLexical(cx, script, pc);
131 0 : return false;
132 : }
133 771 : return true;
134 : }
135 :
136 : static inline void
137 : ReportRuntimeConstAssignment(JSContext* cx, HandlePropertyName name)
138 : {
139 : ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, name);
140 : }
141 :
142 : static inline void
143 0 : ReportRuntimeConstAssignment(JSContext* cx, HandleScript script, jsbytecode* pc)
144 : {
145 0 : ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, script, pc);
146 0 : }
147 :
148 : inline bool
149 1599 : GetLengthProperty(const Value& lval, MutableHandleValue vp)
150 : {
151 : /* Optimize length accesses on strings, arrays, and arguments. */
152 1599 : if (lval.isString()) {
153 231 : vp.setInt32(lval.toString()->length());
154 231 : return true;
155 : }
156 1368 : if (lval.isObject()) {
157 1366 : JSObject* obj = &lval.toObject();
158 1366 : if (obj->is<ArrayObject>()) {
159 1168 : vp.setNumber(obj->as<ArrayObject>().length());
160 1168 : return true;
161 : }
162 :
163 198 : if (obj->is<ArgumentsObject>()) {
164 20 : ArgumentsObject* argsobj = &obj->as<ArgumentsObject>();
165 20 : if (!argsobj->hasOverriddenLength()) {
166 20 : uint32_t length = argsobj->initialLength();
167 20 : MOZ_ASSERT(length < INT32_MAX);
168 20 : vp.setInt32(int32_t(length));
169 20 : return true;
170 : }
171 : }
172 : }
173 :
174 180 : return false;
175 : }
176 :
177 : enum class GetNameMode { Normal, TypeOf };
178 :
179 : template <GetNameMode mode>
180 : inline bool
181 2308 : FetchName(JSContext* cx, HandleObject receiver, HandleObject holder, HandlePropertyName name,
182 : Handle<PropertyResult> prop, MutableHandleValue vp)
183 : {
184 2308 : if (!prop) {
185 : switch (mode) {
186 : case GetNameMode::Normal:
187 0 : return ReportIsNotDefined(cx, name);
188 : case GetNameMode::TypeOf:
189 14 : vp.setUndefined();
190 14 : return true;
191 : }
192 : }
193 :
194 : /* Take the slow path if shape was not found in a native object. */
195 2294 : if (!receiver->isNative() || !holder->isNative()) {
196 102 : Rooted<jsid> id(cx, NameToId(name));
197 51 : if (!GetProperty(cx, receiver, receiver, id, vp))
198 0 : return false;
199 : } else {
200 4486 : RootedShape shape(cx, prop.shape());
201 2243 : if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
202 : /* Fast path for Object instance properties. */
203 1491 : MOZ_ASSERT(shape->hasSlot());
204 1491 : vp.set(holder->as<NativeObject>().getSlot(shape->slot()));
205 : } else {
206 : // Unwrap 'with' environments for reasons given in
207 : // GetNameBoundInEnvironment.
208 1504 : RootedObject normalized(cx, MaybeUnwrapWithEnvironment(receiver));
209 752 : if (!NativeGetExistingProperty(cx, normalized, holder.as<NativeObject>(), shape, vp))
210 0 : return false;
211 : }
212 : }
213 :
214 : // We do our own explicit checking for |this|
215 2294 : if (name == cx->names().dotThis)
216 0 : return true;
217 :
218 : // NAME operations are the slow paths already, so unconditionally check
219 : // for uninitialized lets.
220 2294 : return CheckUninitializedLexical(cx, name, vp);
221 : }
222 :
223 : inline bool
224 15348 : FetchNameNoGC(JSObject* pobj, PropertyResult prop, MutableHandleValue vp)
225 : {
226 15348 : if (!prop || !pobj->isNative())
227 0 : return false;
228 :
229 15348 : Shape* shape = prop.shape();
230 15348 : if (!shape->isDataDescriptor() || !shape->hasDefaultGetter())
231 582 : return false;
232 :
233 14766 : vp.set(pobj->as<NativeObject>().getSlot(shape->slot()));
234 14766 : return !IsUninitializedLexical(vp);
235 : }
236 :
237 : template <js::GetNameMode mode>
238 : inline bool
239 17074 : GetEnvironmentName(JSContext* cx, HandleObject envChain, HandlePropertyName name,
240 : MutableHandleValue vp)
241 : {
242 : {
243 17074 : PropertyResult prop;
244 17074 : JSObject* obj = nullptr;
245 17074 : JSObject* pobj = nullptr;
246 17074 : if (LookupNameNoGC(cx, name, envChain, &obj, &pobj, &prop)) {
247 15348 : if (FetchNameNoGC(pobj, prop, vp))
248 14766 : return true;
249 : }
250 : }
251 :
252 4616 : Rooted<PropertyResult> prop(cx);
253 4616 : RootedObject obj(cx), pobj(cx);
254 2308 : if (!LookupName(cx, name, envChain, &obj, &pobj, &prop))
255 0 : return false;
256 :
257 2308 : return FetchName<mode>(cx, obj, pobj, name, prop, vp);
258 : }
259 :
260 : inline bool
261 28 : HasOwnProperty(JSContext* cx, HandleValue val, HandleValue idValue, bool* result)
262 : {
263 :
264 : // As an optimization, provide a fast path when rooting is not necessary and
265 : // we can safely retrieve the object's shape.
266 : jsid id;
267 28 : if (val.isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
268 28 : JSObject* obj = &val.toObject();
269 28 : PropertyResult prop;
270 138 : if (obj->isNative() &&
271 136 : NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
272 : {
273 27 : *result = prop.isFound();
274 27 : return true;
275 : }
276 : }
277 :
278 : // Step 1.
279 2 : RootedId key(cx);
280 1 : if (!ToPropertyKey(cx, idValue, &key))
281 0 : return false;
282 :
283 : // Step 2.
284 2 : RootedObject obj(cx, ToObject(cx, val));
285 1 : if (!obj)
286 0 : return false;
287 :
288 : // Step 3.
289 1 : return HasOwnProperty(cx, obj, key, result);
290 : }
291 :
292 :
293 : inline bool
294 18155 : GetIntrinsicOperation(JSContext* cx, jsbytecode* pc, MutableHandleValue vp)
295 : {
296 36310 : RootedPropertyName name(cx, cx->currentScript()->getName(pc));
297 36310 : return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
298 : }
299 :
300 : inline bool
301 87 : SetIntrinsicOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleValue val)
302 : {
303 174 : RootedPropertyName name(cx, script->getName(pc));
304 174 : return GlobalObject::setIntrinsicValue(cx, cx->global(), name, val);
305 : }
306 :
307 : inline void
308 1746 : SetAliasedVarOperation(JSContext* cx, JSScript* script, jsbytecode* pc,
309 : EnvironmentObject& obj, EnvironmentCoordinate ec, const Value& val,
310 : MaybeCheckTDZ checkTDZ)
311 : {
312 1746 : MOZ_ASSERT_IF(checkTDZ, !IsUninitializedLexical(obj.aliasedBinding(ec)));
313 :
314 : // Avoid computing the name if no type updates are needed, as this may be
315 : // expensive on scopes with large numbers of variables.
316 1746 : PropertyName* name = obj.isSingleton()
317 1746 : ? EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc)
318 1746 : : nullptr;
319 :
320 1746 : obj.setAliasedBinding(cx, ec, name, val);
321 1746 : }
322 :
323 : inline bool
324 863 : SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject env,
325 : HandleValue val)
326 : {
327 863 : MOZ_ASSERT(*pc == JSOP_SETNAME ||
328 : *pc == JSOP_STRICTSETNAME ||
329 : *pc == JSOP_SETGNAME ||
330 : *pc == JSOP_STRICTSETGNAME);
331 863 : MOZ_ASSERT_IF((*pc == JSOP_SETGNAME || *pc == JSOP_STRICTSETGNAME) &&
332 : !script->hasNonSyntacticScope(),
333 : env == cx->global() ||
334 : env == &cx->global()->lexicalEnvironment() ||
335 : env->is<RuntimeLexicalErrorObject>());
336 :
337 863 : bool strict = *pc == JSOP_STRICTSETNAME || *pc == JSOP_STRICTSETGNAME;
338 1726 : RootedPropertyName name(cx, script->getName(pc));
339 :
340 : // In strict mode, assigning to an undeclared global variable is an
341 : // error. To detect this, we call NativeSetProperty directly and pass
342 : // Unqualified. It stores the error, if any, in |result|.
343 : bool ok;
344 863 : ObjectOpResult result;
345 1726 : RootedId id(cx, NameToId(name));
346 1726 : RootedValue receiver(cx, ObjectValue(*env));
347 863 : if (env->isUnqualifiedVarObj()) {
348 1598 : RootedNativeObject varobj(cx);
349 799 : if (env->is<DebugEnvironmentProxy>())
350 0 : varobj = &env->as<DebugEnvironmentProxy>().environment().as<NativeObject>();
351 : else
352 799 : varobj = &env->as<NativeObject>();
353 799 : MOZ_ASSERT(!varobj->getOpsSetProperty());
354 799 : ok = NativeSetProperty(cx, varobj, id, val, receiver, Unqualified, result);
355 : } else {
356 64 : ok = SetProperty(cx, env, id, val, receiver, result);
357 : }
358 1726 : return ok && result.checkStrictErrorOrWarning(cx, env, id, strict);
359 : }
360 :
361 : inline bool
362 2451 : DefLexicalOperation(JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
363 : HandleObject varObj, HandlePropertyName name, unsigned attrs)
364 : {
365 : // Redeclaration checks should have already been done.
366 2451 : MOZ_ASSERT(CheckLexicalNameConflict(cx, lexicalEnv, varObj, name));
367 4902 : RootedId id(cx, NameToId(name));
368 4902 : RootedValue uninitialized(cx, MagicValue(JS_UNINITIALIZED_LEXICAL));
369 4902 : return NativeDefineProperty(cx, lexicalEnv, id, uninitialized, nullptr, nullptr, attrs);
370 : }
371 :
372 : inline bool
373 2451 : DefLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvArg,
374 : JSObject* varObjArg, JSScript* script, jsbytecode* pc)
375 : {
376 2451 : MOZ_ASSERT(*pc == JSOP_DEFLET || *pc == JSOP_DEFCONST);
377 4902 : RootedPropertyName name(cx, script->getName(pc));
378 :
379 2451 : unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
380 2451 : if (*pc == JSOP_DEFCONST)
381 2326 : attrs |= JSPROP_READONLY;
382 :
383 4902 : Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, lexicalEnvArg);
384 4902 : RootedObject varObj(cx, varObjArg);
385 2451 : MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
386 : lexicalEnv == &cx->global()->lexicalEnvironment() && varObj == cx->global());
387 :
388 4902 : return DefLexicalOperation(cx, lexicalEnv, varObj, name, attrs);
389 : }
390 :
391 : inline void
392 2450 : InitGlobalLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvArg,
393 : JSScript* script, jsbytecode* pc, HandleValue value)
394 : {
395 2450 : MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
396 : lexicalEnvArg == &cx->global()->lexicalEnvironment());
397 2450 : MOZ_ASSERT(*pc == JSOP_INITGLEXICAL);
398 4900 : Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, lexicalEnvArg);
399 4900 : RootedShape shape(cx, lexicalEnv->lookup(cx, script->getName(pc)));
400 2450 : MOZ_ASSERT(shape);
401 2450 : lexicalEnv->setSlot(shape->slot(), value);
402 2450 : }
403 :
404 : inline bool
405 14272 : InitPropertyOperation(JSContext* cx, JSOp op, HandleObject obj, HandleId id, HandleValue rhs)
406 : {
407 14272 : if (obj->is<PlainObject>() || obj->is<JSFunction>()) {
408 14189 : unsigned propAttrs = GetInitDataPropAttrs(op);
409 14189 : return NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs, nullptr, nullptr,
410 14189 : propAttrs);
411 : }
412 :
413 83 : MOZ_ASSERT(obj->as<UnboxedPlainObject>().layout().lookup(id));
414 83 : return PutProperty(cx, obj, id, rhs, false);
415 : }
416 :
417 : inline bool
418 715 : DefVarOperation(JSContext* cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs)
419 : {
420 715 : MOZ_ASSERT(varobj->isQualifiedVarObj());
421 :
422 : #ifdef DEBUG
423 : // Per spec, it is an error to redeclare a lexical binding. This should
424 : // have already been checked.
425 715 : if (JS_HasExtensibleLexicalEnvironment(varobj)) {
426 1214 : Rooted<LexicalEnvironmentObject*> lexicalEnv(cx);
427 607 : lexicalEnv = &JS_ExtensibleLexicalEnvironment(varobj)->as<LexicalEnvironmentObject>();
428 607 : MOZ_ASSERT(CheckVarNameConflict(cx, lexicalEnv, dn));
429 : }
430 : #endif
431 :
432 1430 : Rooted<PropertyResult> prop(cx);
433 1430 : RootedObject obj2(cx);
434 715 : if (!LookupProperty(cx, varobj, dn, &obj2, &prop))
435 0 : return false;
436 :
437 : /* Steps 8c, 8d. */
438 715 : if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
439 702 : if (!DefineProperty(cx, varobj, dn, UndefinedHandleValue, nullptr, nullptr, attrs))
440 0 : return false;
441 : }
442 :
443 715 : if (varobj->is<GlobalObject>()) {
444 587 : if (!varobj->compartment()->addToVarNames(cx, dn))
445 0 : return false;
446 : }
447 :
448 715 : return true;
449 : }
450 :
451 : static MOZ_ALWAYS_INLINE bool
452 0 : NegOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue val,
453 : MutableHandleValue res)
454 : {
455 : /*
456 : * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
457 : * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
458 : * results, -0.0 or INT32_MAX + 1, are double values.
459 : */
460 : int32_t i;
461 0 : if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) {
462 0 : res.setInt32(-i);
463 : } else {
464 : double d;
465 0 : if (!ToNumber(cx, val, &d))
466 0 : return false;
467 0 : res.setNumber(-d);
468 : }
469 :
470 0 : return true;
471 : }
472 :
473 : static MOZ_ALWAYS_INLINE bool
474 27 : ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval,
475 : MutableHandleValue res)
476 : {
477 27 : if (idval.isInt32()) {
478 0 : res.set(idval);
479 0 : return true;
480 : }
481 :
482 54 : RootedId id(cx);
483 27 : if (!ToPropertyKey(cx, idval, &id))
484 0 : return false;
485 :
486 27 : res.set(IdToValue(id));
487 27 : return true;
488 : }
489 :
490 : static MOZ_ALWAYS_INLINE bool
491 4921 : GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleValue receiver,
492 : HandleValue key, MutableHandleValue res)
493 : {
494 4921 : MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM || op == JSOP_GETELEM_SUPER);
495 4921 : MOZ_ASSERT_IF(op == JSOP_GETELEM || op == JSOP_CALLELEM, obj == &receiver.toObject());
496 :
497 : do {
498 : uint32_t index;
499 4921 : if (IsDefinitelyIndex(key, &index)) {
500 1504 : if (GetElementNoGC(cx, obj, receiver, index, res.address()))
501 4041 : break;
502 :
503 91 : if (!GetElement(cx, obj, receiver, index, res))
504 4 : return false;
505 91 : break;
506 : }
507 :
508 3417 : if (key.isString()) {
509 1648 : JSString* str = key.toString();
510 1648 : JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
511 1648 : if (!name)
512 0 : return false;
513 1648 : if (name->isIndex(&index)) {
514 0 : if (GetElementNoGC(cx, obj, receiver, index, res.address()))
515 0 : break;
516 : } else {
517 1648 : if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(), res.address()))
518 1124 : break;
519 : }
520 : }
521 :
522 4582 : RootedId id(cx);
523 2293 : if (!ToPropertyKey(cx, key, &id))
524 0 : return false;
525 2293 : if (!GetProperty(cx, obj, receiver, id, res))
526 4 : return false;
527 : } while (false);
528 :
529 4917 : assertSameCompartmentDebugOnly(cx, res);
530 4917 : return true;
531 : }
532 :
533 : static MOZ_ALWAYS_INLINE bool
534 0 : GetPrimitiveElementOperation(JSContext* cx, JSOp op, JS::HandleValue receiver,
535 : HandleValue key, MutableHandleValue res)
536 : {
537 0 : MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
538 :
539 : // FIXME: Bug 1234324 We shouldn't be boxing here.
540 0 : RootedObject boxed(cx, ToObjectFromStack(cx, receiver));
541 0 : if (!boxed)
542 0 : return false;
543 :
544 : do {
545 : uint32_t index;
546 0 : if (IsDefinitelyIndex(key, &index)) {
547 0 : if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
548 0 : break;
549 :
550 0 : if (!GetElement(cx, boxed, receiver, index, res))
551 0 : return false;
552 0 : break;
553 : }
554 :
555 0 : if (key.isString()) {
556 0 : JSString* str = key.toString();
557 0 : JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
558 0 : if (!name)
559 0 : return false;
560 0 : if (name->isIndex(&index)) {
561 0 : if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
562 0 : break;
563 : } else {
564 0 : if (GetPropertyNoGC(cx, boxed, receiver, name->asPropertyName(), res.address()))
565 0 : break;
566 : }
567 : }
568 :
569 0 : RootedId id(cx);
570 0 : if (!ToPropertyKey(cx, key, &id))
571 0 : return false;
572 0 : if (!GetProperty(cx, boxed, receiver, id, res))
573 0 : return false;
574 : } while (false);
575 :
576 0 : assertSameCompartmentDebugOnly(cx, res);
577 0 : return true;
578 : }
579 :
580 : static MOZ_ALWAYS_INLINE bool
581 3616 : GetElemOptimizedArguments(JSContext* cx, AbstractFramePtr frame, MutableHandleValue lref,
582 : HandleValue rref, MutableHandleValue res, bool* done)
583 : {
584 3616 : MOZ_ASSERT(!*done);
585 :
586 3616 : if (IsOptimizedArguments(frame, lref)) {
587 151 : if (rref.isInt32()) {
588 151 : int32_t i = rref.toInt32();
589 151 : if (i >= 0 && uint32_t(i) < frame.numActualArgs()) {
590 151 : res.set(frame.unaliasedActual(i));
591 151 : *done = true;
592 302 : return true;
593 : }
594 : }
595 :
596 0 : RootedScript script(cx, frame.script());
597 0 : if (!JSScript::argumentsOptimizationFailed(cx, script))
598 0 : return false;
599 :
600 0 : lref.set(ObjectValue(frame.argsObj()));
601 : }
602 :
603 3465 : return true;
604 : }
605 :
606 : static MOZ_ALWAYS_INLINE bool
607 4957 : GetElementOperation(JSContext* cx, JSOp op, HandleValue lref, HandleValue rref,
608 : MutableHandleValue res)
609 : {
610 4957 : MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
611 :
612 : uint32_t index;
613 4957 : if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
614 36 : JSString* str = lref.toString();
615 36 : if (index < str->length()) {
616 36 : str = cx->staticStrings().getUnitStringForElement(cx, str, index);
617 36 : if (!str)
618 0 : return false;
619 36 : res.setString(str);
620 36 : return true;
621 : }
622 : }
623 :
624 4921 : if (lref.isPrimitive()) {
625 0 : RootedValue thisv(cx, lref);
626 0 : return GetPrimitiveElementOperation(cx, op, thisv, rref, res);
627 : }
628 :
629 9842 : RootedObject obj(cx, &lref.toObject());
630 9842 : RootedValue thisv(cx, lref);
631 4921 : return GetObjectElementOperation(cx, op, obj, thisv, rref, res);
632 : }
633 :
634 : static MOZ_ALWAYS_INLINE JSString*
635 909 : TypeOfOperation(const Value& v, JSRuntime* rt)
636 : {
637 909 : JSType type = js::TypeOfValue(v);
638 909 : return TypeName(type, *rt->commonNames);
639 : }
640 :
641 : static MOZ_ALWAYS_INLINE bool
642 168 : InitElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval, HandleValue val)
643 : {
644 168 : MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
645 168 : MOZ_ASSERT(!obj->getClass()->getGetProperty());
646 168 : MOZ_ASSERT(!obj->getClass()->getSetProperty());
647 :
648 336 : RootedId id(cx);
649 168 : if (!ToPropertyKey(cx, idval, &id))
650 0 : return false;
651 :
652 168 : unsigned flags = JSOp(*pc) == JSOP_INITHIDDENELEM ? 0 : JSPROP_ENUMERATE;
653 168 : return DefineProperty(cx, obj, id, val, nullptr, nullptr, flags);
654 : }
655 :
656 : static MOZ_ALWAYS_INLINE bool
657 1589 : InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t index, HandleValue val)
658 : {
659 1589 : JSOp op = JSOp(*pc);
660 1589 : MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
661 :
662 1589 : MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
663 :
664 1589 : if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
665 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
666 0 : return false;
667 : }
668 :
669 : /*
670 : * If val is a hole, do not call DefineElement.
671 : *
672 : * Furthermore, if the current op is JSOP_INITELEM_INC, always call
673 : * SetLengthProperty even if it is not the last element initialiser,
674 : * because it may be followed by JSOP_SPREAD, which will not set the array
675 : * length if nothing is spread.
676 : *
677 : * Alternatively, if the current op is JSOP_INITELEM_ARRAY, the length will
678 : * have already been set by the earlier JSOP_NEWARRAY; JSOP_INITELEM_ARRAY
679 : * cannot follow JSOP_SPREAD.
680 : */
681 1589 : if (val.isMagic(JS_ELEMENTS_HOLE)) {
682 0 : if (op == JSOP_INITELEM_INC) {
683 0 : if (!SetLengthProperty(cx, obj, index + 1))
684 0 : return false;
685 : }
686 : } else {
687 1589 : if (!DefineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE))
688 0 : return false;
689 : }
690 :
691 1589 : return true;
692 : }
693 :
694 : #define RELATIONAL_OP(OP) \
695 : JS_BEGIN_MACRO \
696 : /* Optimize for two int-tagged operands (typical loop control). */ \
697 : if (lhs.isInt32() && rhs.isInt32()) { \
698 : *res = lhs.toInt32() OP rhs.toInt32(); \
699 : } else { \
700 : if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) \
701 : return false; \
702 : if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) \
703 : return false; \
704 : if (lhs.isString() && rhs.isString()) { \
705 : JSString* l = lhs.toString(); \
706 : JSString* r = rhs.toString(); \
707 : int32_t result; \
708 : if (!CompareStrings(cx, l, r, &result)) \
709 : return false; \
710 : *res = result OP 0; \
711 : } else { \
712 : double l, r; \
713 : if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) \
714 : return false; \
715 : *res = (l OP r); \
716 : } \
717 : } \
718 : return true; \
719 : JS_END_MACRO
720 :
721 : static MOZ_ALWAYS_INLINE bool
722 997 : LessThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
723 997 : RELATIONAL_OP(<);
724 : }
725 :
726 : static MOZ_ALWAYS_INLINE bool
727 91 : LessThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
728 91 : RELATIONAL_OP(<=);
729 : }
730 :
731 : static MOZ_ALWAYS_INLINE bool
732 330 : GreaterThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
733 330 : RELATIONAL_OP(>);
734 : }
735 :
736 : static MOZ_ALWAYS_INLINE bool
737 841 : GreaterThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
738 841 : RELATIONAL_OP(>=);
739 : }
740 :
741 : static MOZ_ALWAYS_INLINE bool
742 5 : BitNot(JSContext* cx, HandleValue in, int* out)
743 : {
744 : int i;
745 5 : if (!ToInt32(cx, in, &i))
746 0 : return false;
747 5 : *out = ~i;
748 5 : return true;
749 : }
750 :
751 : static MOZ_ALWAYS_INLINE bool
752 0 : BitXor(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
753 : {
754 : int left, right;
755 0 : if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
756 0 : return false;
757 0 : *out = left ^ right;
758 0 : return true;
759 : }
760 :
761 : static MOZ_ALWAYS_INLINE bool
762 20 : BitOr(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
763 : {
764 : int left, right;
765 20 : if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
766 0 : return false;
767 20 : *out = left | right;
768 20 : return true;
769 : }
770 :
771 : static MOZ_ALWAYS_INLINE bool
772 11 : BitAnd(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
773 : {
774 : int left, right;
775 11 : if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
776 0 : return false;
777 11 : *out = left & right;
778 11 : return true;
779 : }
780 :
781 : static MOZ_ALWAYS_INLINE bool
782 0 : BitLsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
783 : {
784 : int32_t left, right;
785 0 : if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
786 0 : return false;
787 0 : *out = uint32_t(left) << (right & 31);
788 0 : return true;
789 : }
790 :
791 : static MOZ_ALWAYS_INLINE bool
792 0 : BitRsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
793 : {
794 : int32_t left, right;
795 0 : if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
796 0 : return false;
797 0 : *out = left >> (right & 31);
798 0 : return true;
799 : }
800 :
801 : static MOZ_ALWAYS_INLINE bool
802 1 : UrshOperation(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out)
803 : {
804 : uint32_t left;
805 : int32_t right;
806 1 : if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
807 0 : return false;
808 1 : left >>= right & 31;
809 1 : out.setNumber(uint32_t(left));
810 1 : return true;
811 : }
812 :
813 : template <typename T>
814 : static MOZ_ALWAYS_INLINE bool
815 0 : SignExtendOperation(JSContext* cx, HandleValue in, int* out)
816 : {
817 : int32_t i;
818 0 : if (!ToInt32(cx, in, &i))
819 0 : return false;
820 0 : *out = (T)i;
821 0 : return true;
822 : }
823 :
824 : #undef RELATIONAL_OP
825 :
826 : inline JSFunction*
827 0 : ReportIfNotFunction(JSContext* cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT)
828 : {
829 0 : if (v.isObject() && v.toObject().is<JSFunction>())
830 0 : return &v.toObject().as<JSFunction>();
831 :
832 0 : ReportIsNotFunction(cx, v, -1, construct);
833 0 : return nullptr;
834 : }
835 :
836 : /*
837 : * FastCallGuard is used to optimize calls to JS functions from natives written
838 : * in C++, e.g. Array.prototype.map. If the callee is not Ion-compiled, this
839 : * will just call js::Call. If the callee has a valid IonScript, however, it
840 : * will enter Ion directly.
841 : */
842 : class FastCallGuard
843 : {
844 : InvokeArgs args_;
845 : RootedFunction fun_;
846 : RootedScript script_;
847 :
848 : // Constructing a JitContext is pretty expensive due to the TLS access,
849 : // so only do this if we have to.
850 : bool useIon_;
851 :
852 : public:
853 : FastCallGuard(JSContext* cx, const Value& fval)
854 : : args_(cx)
855 : , fun_(cx)
856 : , script_(cx)
857 : , useIon_(jit::IsIonEnabled(cx))
858 : {
859 : initFunction(fval);
860 : }
861 :
862 : void initFunction(const Value& fval) {
863 : if (fval.isObject() && fval.toObject().is<JSFunction>()) {
864 : JSFunction* fun = &fval.toObject().as<JSFunction>();
865 : if (fun->isInterpreted())
866 : fun_ = fun;
867 : }
868 : }
869 :
870 : InvokeArgs& args() {
871 : return args_;
872 : }
873 :
874 : bool call(JSContext* cx, HandleValue callee, HandleValue thisv, MutableHandleValue rval) {
875 : args_.CallArgs::setCallee(callee);
876 : args_.CallArgs::setThis(thisv);
877 :
878 : if (useIon_ && fun_) {
879 : if (!script_) {
880 : script_ = JSFunction::getOrCreateScript(cx, fun_);
881 : if (!script_)
882 : return false;
883 : }
884 : MOZ_ASSERT(fun_->nonLazyScript() == script_);
885 :
886 : jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length());
887 : if (status == jit::Method_Error)
888 : return false;
889 : if (status == jit::Method_Compiled) {
890 : jit::JitExecStatus result = jit::FastInvoke(cx, fun_, args_);
891 : if (IsErrorStatus(result))
892 : return false;
893 :
894 : MOZ_ASSERT(result == jit::JitExec_Ok);
895 : rval.set(args_.CallArgs::rval());
896 : return true;
897 : }
898 :
899 : MOZ_ASSERT(status == jit::Method_Skipped);
900 :
901 : if (script_->canIonCompile()) {
902 : // This script is not yet hot. Since calling into Ion is much
903 : // faster here, bump the warm-up counter a bit to account for this.
904 : script_->incWarmUpCounter(5);
905 : }
906 : }
907 :
908 : if (!InternalCallOrConstruct(cx, args_, NO_CONSTRUCT))
909 : return false;
910 :
911 : rval.set(args_.CallArgs::rval());
912 : return true;
913 : }
914 :
915 : private:
916 : FastCallGuard(const FastCallGuard& other) = delete;
917 : void operator=(const FastCallGuard& other) = delete;
918 : };
919 :
920 : } /* namespace js */
921 :
922 : #endif /* vm_Interpreter_inl_h */
|