js/src/vm/Interpreter-inl.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef vm_Interpreter_inl_h
michael@0 8 #define vm_Interpreter_inl_h
michael@0 9
michael@0 10 #include "vm/Interpreter.h"
michael@0 11
michael@0 12 #include "jscompartment.h"
michael@0 13 #include "jsinfer.h"
michael@0 14 #include "jsnum.h"
michael@0 15 #include "jsstr.h"
michael@0 16
michael@0 17 #include "jit/Ion.h"
michael@0 18 #include "vm/ArgumentsObject.h"
michael@0 19 #include "vm/ForkJoin.h"
michael@0 20
michael@0 21 #include "jsatominlines.h"
michael@0 22 #include "jsinferinlines.h"
michael@0 23 #include "jsobjinlines.h"
michael@0 24
michael@0 25 #include "vm/Stack-inl.h"
michael@0 26 #include "vm/String-inl.h"
michael@0 27
michael@0 28 namespace js {
michael@0 29
michael@0 30 inline bool
michael@0 31 ComputeThis(JSContext *cx, AbstractFramePtr frame)
michael@0 32 {
michael@0 33 JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
michael@0 34
michael@0 35 if (frame.isFunctionFrame() && frame.fun()->isArrow()) {
michael@0 36 /*
michael@0 37 * Arrow functions store their (lexical) |this| value in an
michael@0 38 * extended slot.
michael@0 39 */
michael@0 40 frame.thisValue() = frame.fun()->getExtendedSlot(0);
michael@0 41 return true;
michael@0 42 }
michael@0 43
michael@0 44 if (frame.thisValue().isObject())
michael@0 45 return true;
michael@0 46 RootedValue thisv(cx, frame.thisValue());
michael@0 47 if (frame.isFunctionFrame()) {
michael@0 48 if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin())
michael@0 49 return true;
michael@0 50 /*
michael@0 51 * Eval function frames have their own |this| slot, which is a copy of the function's
michael@0 52 * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
michael@0 53 * eval's frame will get the wrapper, but the function's frame will not. To prevent
michael@0 54 * this, we always wrap a function's |this| before pushing an eval frame, and should
michael@0 55 * thus never see an unwrapped primitive in a non-strict eval function frame. Null
michael@0 56 * and undefined |this| values will unwrap to the same object in the function and
michael@0 57 * eval frames, so are not required to be wrapped.
michael@0 58 */
michael@0 59 JS_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull());
michael@0 60 }
michael@0 61
michael@0 62 JSObject *thisObj = BoxNonStrictThis(cx, thisv);
michael@0 63 if (!thisObj)
michael@0 64 return false;
michael@0 65
michael@0 66 frame.thisValue().setObject(*thisObj);
michael@0 67 return true;
michael@0 68 }
michael@0 69
michael@0 70 /*
michael@0 71 * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
michael@0 72 * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
michael@0 73 * one is received, act as if the value were the function's ArgumentsObject.
michael@0 74 * Additionally, it is possible that, after 'arguments' was copied into a
michael@0 75 * temporary, the arguments object has been created a some other failed guard
michael@0 76 * that called JSScript::argumentsOptimizationFailed. In this case, it is
michael@0 77 * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real
michael@0 78 * arguments object.
michael@0 79 */
michael@0 80 static inline bool
michael@0 81 IsOptimizedArguments(AbstractFramePtr frame, Value *vp)
michael@0 82 {
michael@0 83 if (vp->isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj())
michael@0 84 *vp = ObjectValue(frame.argsObj());
michael@0 85 return vp->isMagic(JS_OPTIMIZED_ARGUMENTS);
michael@0 86 }
michael@0 87
michael@0 88 /*
michael@0 89 * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply.
michael@0 90 * However, this speculation must be guarded before calling 'apply' in case it
michael@0 91 * is not the builtin Function.prototype.apply.
michael@0 92 */
michael@0 93 static inline bool
michael@0 94 GuardFunApplyArgumentsOptimization(JSContext *cx, AbstractFramePtr frame, HandleValue callee,
michael@0 95 Value *args, uint32_t argc)
michael@0 96 {
michael@0 97 if (argc == 2 && IsOptimizedArguments(frame, &args[1])) {
michael@0 98 if (!IsNativeFunction(callee, js_fun_apply)) {
michael@0 99 RootedScript script(cx, frame.script());
michael@0 100 if (!JSScript::argumentsOptimizationFailed(cx, script))
michael@0 101 return false;
michael@0 102 args[1] = ObjectValue(frame.argsObj());
michael@0 103 }
michael@0 104 }
michael@0 105
michael@0 106 return true;
michael@0 107 }
michael@0 108
michael@0 109 /*
michael@0 110 * Return an object on which we should look for the properties of |value|.
michael@0 111 * This helps us implement the custom [[Get]] method that ES5's GetValue
michael@0 112 * algorithm uses for primitive values, without actually constructing the
michael@0 113 * temporary object that the specification does.
michael@0 114 *
michael@0 115 * For objects, return the object itself. For string, boolean, and number
michael@0 116 * primitive values, return the appropriate constructor's prototype. For
michael@0 117 * undefined and null, throw an error and return nullptr, attributing the
michael@0 118 * problem to the value at |spindex| on the stack.
michael@0 119 */
michael@0 120 MOZ_ALWAYS_INLINE JSObject *
michael@0 121 ValuePropertyBearer(JSContext *cx, InterpreterFrame *fp, HandleValue v, int spindex)
michael@0 122 {
michael@0 123 if (v.isObject())
michael@0 124 return &v.toObject();
michael@0 125
michael@0 126 Rooted<GlobalObject*> global(cx, &fp->global());
michael@0 127
michael@0 128 if (v.isString())
michael@0 129 return GlobalObject::getOrCreateStringPrototype(cx, global);
michael@0 130 if (v.isNumber())
michael@0 131 return GlobalObject::getOrCreateNumberPrototype(cx, global);
michael@0 132 if (v.isBoolean())
michael@0 133 return GlobalObject::getOrCreateBooleanPrototype(cx, global);
michael@0 134
michael@0 135 JS_ASSERT(v.isNull() || v.isUndefined());
michael@0 136 js_ReportIsNullOrUndefined(cx, spindex, v, NullPtr());
michael@0 137 return nullptr;
michael@0 138 }
michael@0 139
michael@0 140 inline bool
michael@0 141 GetLengthProperty(const Value &lval, MutableHandleValue vp)
michael@0 142 {
michael@0 143 /* Optimize length accesses on strings, arrays, and arguments. */
michael@0 144 if (lval.isString()) {
michael@0 145 vp.setInt32(lval.toString()->length());
michael@0 146 return true;
michael@0 147 }
michael@0 148 if (lval.isObject()) {
michael@0 149 JSObject *obj = &lval.toObject();
michael@0 150 if (obj->is<ArrayObject>()) {
michael@0 151 vp.setNumber(obj->as<ArrayObject>().length());
michael@0 152 return true;
michael@0 153 }
michael@0 154
michael@0 155 if (obj->is<ArgumentsObject>()) {
michael@0 156 ArgumentsObject *argsobj = &obj->as<ArgumentsObject>();
michael@0 157 if (!argsobj->hasOverriddenLength()) {
michael@0 158 uint32_t length = argsobj->initialLength();
michael@0 159 JS_ASSERT(length < INT32_MAX);
michael@0 160 vp.setInt32(int32_t(length));
michael@0 161 return true;
michael@0 162 }
michael@0 163 }
michael@0 164 }
michael@0 165
michael@0 166 return false;
michael@0 167 }
michael@0 168
michael@0 169 template <bool TypeOf> inline bool
michael@0 170 FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName name,
michael@0 171 HandleShape shape, MutableHandleValue vp)
michael@0 172 {
michael@0 173 if (!shape) {
michael@0 174 if (TypeOf) {
michael@0 175 vp.setUndefined();
michael@0 176 return true;
michael@0 177 }
michael@0 178 JSAutoByteString printable;
michael@0 179 if (AtomToPrintableString(cx, name, &printable))
michael@0 180 js_ReportIsNotDefined(cx, printable.ptr());
michael@0 181 return false;
michael@0 182 }
michael@0 183
michael@0 184 /* Take the slow path if shape was not found in a native object. */
michael@0 185 if (!obj->isNative() || !obj2->isNative()) {
michael@0 186 Rooted<jsid> id(cx, NameToId(name));
michael@0 187 if (!JSObject::getGeneric(cx, obj, obj, id, vp))
michael@0 188 return false;
michael@0 189 } else {
michael@0 190 Rooted<JSObject*> normalized(cx, obj);
michael@0 191 if (normalized->is<DynamicWithObject>() && !shape->hasDefaultGetter())
michael@0 192 normalized = &normalized->as<DynamicWithObject>().object();
michael@0 193 if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
michael@0 194 /* Fast path for Object instance properties. */
michael@0 195 JS_ASSERT(shape->hasSlot());
michael@0 196 vp.set(obj2->nativeGetSlot(shape->slot()));
michael@0 197 } else if (!NativeGet(cx, normalized, obj2, shape, vp)) {
michael@0 198 return false;
michael@0 199 }
michael@0 200 }
michael@0 201 return true;
michael@0 202 }
michael@0 203
michael@0 204 inline bool
michael@0 205 FetchNameNoGC(JSObject *pobj, Shape *shape, MutableHandleValue vp)
michael@0 206 {
michael@0 207 if (!shape || !pobj->isNative() || !shape->isDataDescriptor() || !shape->hasDefaultGetter())
michael@0 208 return false;
michael@0 209
michael@0 210 vp.set(pobj->nativeGetSlot(shape->slot()));
michael@0 211 return true;
michael@0 212 }
michael@0 213
michael@0 214 inline bool
michael@0 215 GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp)
michael@0 216 {
michael@0 217 RootedPropertyName name(cx, cx->currentScript()->getName(pc));
michael@0 218 return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
michael@0 219 }
michael@0 220
michael@0 221 inline bool
michael@0 222 SetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleValue val)
michael@0 223 {
michael@0 224 RootedPropertyName name(cx, script->getName(pc));
michael@0 225 return cx->global()->setIntrinsicValue(cx, name, val);
michael@0 226 }
michael@0 227
michael@0 228 inline bool
michael@0 229 SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope,
michael@0 230 HandleValue val)
michael@0 231 {
michael@0 232 JS_ASSERT(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME);
michael@0 233 JS_ASSERT_IF(*pc == JSOP_SETGNAME, scope == cx->global());
michael@0 234
michael@0 235 bool strict = script->strict();
michael@0 236 RootedPropertyName name(cx, script->getName(pc));
michael@0 237 RootedValue valCopy(cx, val);
michael@0 238
michael@0 239 /*
michael@0 240 * In strict-mode, we need to trigger an error when trying to assign to an
michael@0 241 * undeclared global variable. To do this, we call SetPropertyHelper
michael@0 242 * directly and pass Unqualified.
michael@0 243 */
michael@0 244 if (scope->is<GlobalObject>()) {
michael@0 245 JS_ASSERT(!scope->getOps()->setProperty);
michael@0 246 RootedId id(cx, NameToId(name));
michael@0 247 return baseops::SetPropertyHelper<SequentialExecution>(cx, scope, scope, id,
michael@0 248 baseops::Unqualified, &valCopy,
michael@0 249 strict);
michael@0 250 }
michael@0 251
michael@0 252 return JSObject::setProperty(cx, scope, scope, name, &valCopy, strict);
michael@0 253 }
michael@0 254
michael@0 255 inline bool
michael@0 256 DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs)
michael@0 257 {
michael@0 258 JS_ASSERT(varobj->isVarObj());
michael@0 259
michael@0 260 RootedShape prop(cx);
michael@0 261 RootedObject obj2(cx);
michael@0 262 if (!JSObject::lookupProperty(cx, varobj, dn, &obj2, &prop))
michael@0 263 return false;
michael@0 264
michael@0 265 /* Steps 8c, 8d. */
michael@0 266 if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
michael@0 267 if (!JSObject::defineProperty(cx, varobj, dn, UndefinedHandleValue, JS_PropertyStub,
michael@0 268 JS_StrictPropertyStub, attrs)) {
michael@0 269 return false;
michael@0 270 }
michael@0 271 } else if (attrs & JSPROP_READONLY) {
michael@0 272 /*
michael@0 273 * Extension: ordinarily we'd be done here -- but for |const|. If we
michael@0 274 * see a redeclaration that's |const|, we consider it a conflict.
michael@0 275 */
michael@0 276 unsigned oldAttrs;
michael@0 277 RootedId id(cx, NameToId(dn));
michael@0 278 if (!JSObject::getGenericAttributes(cx, varobj, id, &oldAttrs))
michael@0 279 return false;
michael@0 280
michael@0 281 JSAutoByteString bytes;
michael@0 282 if (AtomToPrintableString(cx, dn, &bytes)) {
michael@0 283 JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
michael@0 284 js_GetErrorMessage,
michael@0 285 nullptr, JSMSG_REDECLARED_VAR,
michael@0 286 (oldAttrs & JSPROP_READONLY)
michael@0 287 ? "const"
michael@0 288 : "var",
michael@0 289 bytes.ptr()));
michael@0 290 }
michael@0 291 return false;
michael@0 292 }
michael@0 293
michael@0 294 return true;
michael@0 295 }
michael@0 296
michael@0 297 static MOZ_ALWAYS_INLINE bool
michael@0 298 NegOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue val,
michael@0 299 MutableHandleValue res)
michael@0 300 {
michael@0 301 /*
michael@0 302 * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
michael@0 303 * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
michael@0 304 * results, -0.0 or INT32_MAX + 1, are double values.
michael@0 305 */
michael@0 306 int32_t i;
michael@0 307 if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) {
michael@0 308 res.setInt32(-i);
michael@0 309 } else {
michael@0 310 double d;
michael@0 311 if (!ToNumber(cx, val, &d))
michael@0 312 return false;
michael@0 313 res.setNumber(-d);
michael@0 314 }
michael@0 315
michael@0 316 return true;
michael@0 317 }
michael@0 318
michael@0 319 static MOZ_ALWAYS_INLINE bool
michael@0 320 ToIdOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue objval,
michael@0 321 HandleValue idval, MutableHandleValue res)
michael@0 322 {
michael@0 323 if (idval.isInt32()) {
michael@0 324 res.set(idval);
michael@0 325 return true;
michael@0 326 }
michael@0 327
michael@0 328 JSObject *obj = ToObjectFromStack(cx, objval);
michael@0 329 if (!obj)
michael@0 330 return false;
michael@0 331
michael@0 332 RootedId id(cx);
michael@0 333 if (!ValueToId<CanGC>(cx, idval, &id))
michael@0 334 return false;
michael@0 335
michael@0 336 res.set(IdToValue(id));
michael@0 337 return true;
michael@0 338 }
michael@0 339
michael@0 340 static MOZ_ALWAYS_INLINE bool
michael@0 341 GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObject,
michael@0 342 HandleValue rref, MutableHandleValue res)
michael@0 343 {
michael@0 344 JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
michael@0 345
michael@0 346 do {
michael@0 347 uint32_t index;
michael@0 348 if (IsDefinitelyIndex(rref, &index)) {
michael@0 349 if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address()))
michael@0 350 break;
michael@0 351
michael@0 352 RootedObject obj(cx, objArg);
michael@0 353 if (!JSObject::getElement(cx, obj, obj, index, res))
michael@0 354 return false;
michael@0 355 objArg = obj;
michael@0 356 break;
michael@0 357 }
michael@0 358
michael@0 359 JSAtom *name = ToAtom<NoGC>(cx, rref);
michael@0 360 if (name) {
michael@0 361 if (name->isIndex(&index)) {
michael@0 362 if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address()))
michael@0 363 break;
michael@0 364 } else {
michael@0 365 if (JSObject::getPropertyNoGC(cx, objArg, objArg, name->asPropertyName(), res.address()))
michael@0 366 break;
michael@0 367 }
michael@0 368 }
michael@0 369
michael@0 370 RootedObject obj(cx, objArg);
michael@0 371
michael@0 372 name = ToAtom<CanGC>(cx, rref);
michael@0 373 if (!name)
michael@0 374 return false;
michael@0 375
michael@0 376 if (name->isIndex(&index)) {
michael@0 377 if (!JSObject::getElement(cx, obj, obj, index, res))
michael@0 378 return false;
michael@0 379 } else {
michael@0 380 if (!JSObject::getProperty(cx, obj, obj, name->asPropertyName(), res))
michael@0 381 return false;
michael@0 382 }
michael@0 383
michael@0 384 objArg = obj;
michael@0 385 } while (0);
michael@0 386
michael@0 387 #if JS_HAS_NO_SUCH_METHOD
michael@0 388 if (op == JSOP_CALLELEM && MOZ_UNLIKELY(res.isUndefined()) && wasObject) {
michael@0 389 RootedObject obj(cx, objArg);
michael@0 390 if (!OnUnknownMethod(cx, obj, rref, res))
michael@0 391 return false;
michael@0 392 }
michael@0 393 #endif
michael@0 394
michael@0 395 assertSameCompartmentDebugOnly(cx, res);
michael@0 396 return true;
michael@0 397 }
michael@0 398
michael@0 399 static MOZ_ALWAYS_INLINE bool
michael@0 400 GetElemOptimizedArguments(JSContext *cx, AbstractFramePtr frame, MutableHandleValue lref,
michael@0 401 HandleValue rref, MutableHandleValue res, bool *done)
michael@0 402 {
michael@0 403 JS_ASSERT(!*done);
michael@0 404
michael@0 405 if (IsOptimizedArguments(frame, lref.address())) {
michael@0 406 if (rref.isInt32()) {
michael@0 407 int32_t i = rref.toInt32();
michael@0 408 if (i >= 0 && uint32_t(i) < frame.numActualArgs()) {
michael@0 409 res.set(frame.unaliasedActual(i));
michael@0 410 *done = true;
michael@0 411 return true;
michael@0 412 }
michael@0 413 }
michael@0 414
michael@0 415 RootedScript script(cx, frame.script());
michael@0 416 if (!JSScript::argumentsOptimizationFailed(cx, script))
michael@0 417 return false;
michael@0 418
michael@0 419 lref.set(ObjectValue(frame.argsObj()));
michael@0 420 }
michael@0 421
michael@0 422 return true;
michael@0 423 }
michael@0 424
michael@0 425 static MOZ_ALWAYS_INLINE bool
michael@0 426 GetElementOperation(JSContext *cx, JSOp op, MutableHandleValue lref, HandleValue rref,
michael@0 427 MutableHandleValue res)
michael@0 428 {
michael@0 429 JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
michael@0 430
michael@0 431 uint32_t index;
michael@0 432 if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
michael@0 433 JSString *str = lref.toString();
michael@0 434 if (index < str->length()) {
michael@0 435 str = cx->staticStrings().getUnitStringForElement(cx, str, index);
michael@0 436 if (!str)
michael@0 437 return false;
michael@0 438 res.setString(str);
michael@0 439 return true;
michael@0 440 }
michael@0 441 }
michael@0 442
michael@0 443 bool isObject = lref.isObject();
michael@0 444 JSObject *obj = ToObjectFromStack(cx, lref);
michael@0 445 if (!obj)
michael@0 446 return false;
michael@0 447 return GetObjectElementOperation(cx, op, obj, isObject, rref, res);
michael@0 448 }
michael@0 449
michael@0 450 static MOZ_ALWAYS_INLINE JSString *
michael@0 451 TypeOfOperation(const Value &v, JSRuntime *rt)
michael@0 452 {
michael@0 453 JSType type = js::TypeOfValue(v);
michael@0 454 return TypeName(type, *rt->commonNames);
michael@0 455 }
michael@0 456
michael@0 457 static inline JSString *
michael@0 458 TypeOfObjectOperation(JSObject *obj, JSRuntime *rt)
michael@0 459 {
michael@0 460 JSType type = js::TypeOfObject(obj);
michael@0 461 return TypeName(type, *rt->commonNames);
michael@0 462 }
michael@0 463
michael@0 464 static MOZ_ALWAYS_INLINE bool
michael@0 465 InitElemOperation(JSContext *cx, HandleObject obj, HandleValue idval, HandleValue val)
michael@0 466 {
michael@0 467 JS_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
michael@0 468
michael@0 469 RootedId id(cx);
michael@0 470 if (!ValueToId<CanGC>(cx, idval, &id))
michael@0 471 return false;
michael@0 472
michael@0 473 return JSObject::defineGeneric(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE);
michael@0 474 }
michael@0 475
michael@0 476 static MOZ_ALWAYS_INLINE bool
michael@0 477 InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue val)
michael@0 478 {
michael@0 479 JSOp op = JSOp(*pc);
michael@0 480 JS_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
michael@0 481
michael@0 482 JS_ASSERT(obj->is<ArrayObject>());
michael@0 483
michael@0 484 /*
michael@0 485 * If val is a hole, do not call JSObject::defineElement. In this case,
michael@0 486 * if the current op is the last element initialiser, set the array length
michael@0 487 * to one greater than id.
michael@0 488 */
michael@0 489 if (val.isMagic(JS_ELEMENTS_HOLE)) {
michael@0 490 JSOp next = JSOp(*GetNextPc(pc));
michael@0 491
michael@0 492 if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) ||
michael@0 493 (op == JSOP_INITELEM_INC && next == JSOP_POP))
michael@0 494 {
michael@0 495 if (!SetLengthProperty(cx, obj, index + 1))
michael@0 496 return false;
michael@0 497 }
michael@0 498 } else {
michael@0 499 if (!JSObject::defineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE))
michael@0 500 return false;
michael@0 501 }
michael@0 502
michael@0 503 if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
michael@0 504 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
michael@0 505 return false;
michael@0 506 }
michael@0 507
michael@0 508 return true;
michael@0 509 }
michael@0 510
michael@0 511 #define RELATIONAL_OP(OP) \
michael@0 512 JS_BEGIN_MACRO \
michael@0 513 /* Optimize for two int-tagged operands (typical loop control). */ \
michael@0 514 if (lhs.isInt32() && rhs.isInt32()) { \
michael@0 515 *res = lhs.toInt32() OP rhs.toInt32(); \
michael@0 516 } else { \
michael@0 517 if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) \
michael@0 518 return false; \
michael@0 519 if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) \
michael@0 520 return false; \
michael@0 521 if (lhs.isString() && rhs.isString()) { \
michael@0 522 JSString *l = lhs.toString(), *r = rhs.toString(); \
michael@0 523 int32_t result; \
michael@0 524 if (!CompareStrings(cx, l, r, &result)) \
michael@0 525 return false; \
michael@0 526 *res = result OP 0; \
michael@0 527 } else { \
michael@0 528 double l, r; \
michael@0 529 if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) \
michael@0 530 return false;; \
michael@0 531 *res = (l OP r); \
michael@0 532 } \
michael@0 533 } \
michael@0 534 return true; \
michael@0 535 JS_END_MACRO
michael@0 536
michael@0 537 static MOZ_ALWAYS_INLINE bool
michael@0 538 LessThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
michael@0 539 RELATIONAL_OP(<);
michael@0 540 }
michael@0 541
michael@0 542 static MOZ_ALWAYS_INLINE bool
michael@0 543 LessThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
michael@0 544 RELATIONAL_OP(<=);
michael@0 545 }
michael@0 546
michael@0 547 static MOZ_ALWAYS_INLINE bool
michael@0 548 GreaterThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
michael@0 549 RELATIONAL_OP(>);
michael@0 550 }
michael@0 551
michael@0 552 static MOZ_ALWAYS_INLINE bool
michael@0 553 GreaterThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
michael@0 554 RELATIONAL_OP(>=);
michael@0 555 }
michael@0 556
michael@0 557 static MOZ_ALWAYS_INLINE bool
michael@0 558 BitNot(JSContext *cx, HandleValue in, int *out)
michael@0 559 {
michael@0 560 int i;
michael@0 561 if (!ToInt32(cx, in, &i))
michael@0 562 return false;
michael@0 563 *out = ~i;
michael@0 564 return true;
michael@0 565 }
michael@0 566
michael@0 567 static MOZ_ALWAYS_INLINE bool
michael@0 568 BitXor(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
michael@0 569 {
michael@0 570 int left, right;
michael@0 571 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
michael@0 572 return false;
michael@0 573 *out = left ^ right;
michael@0 574 return true;
michael@0 575 }
michael@0 576
michael@0 577 static MOZ_ALWAYS_INLINE bool
michael@0 578 BitOr(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
michael@0 579 {
michael@0 580 int left, right;
michael@0 581 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
michael@0 582 return false;
michael@0 583 *out = left | right;
michael@0 584 return true;
michael@0 585 }
michael@0 586
michael@0 587 static MOZ_ALWAYS_INLINE bool
michael@0 588 BitAnd(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
michael@0 589 {
michael@0 590 int left, right;
michael@0 591 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
michael@0 592 return false;
michael@0 593 *out = left & right;
michael@0 594 return true;
michael@0 595 }
michael@0 596
michael@0 597 static MOZ_ALWAYS_INLINE bool
michael@0 598 BitLsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
michael@0 599 {
michael@0 600 int32_t left, right;
michael@0 601 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
michael@0 602 return false;
michael@0 603 *out = uint32_t(left) << (right & 31);
michael@0 604 return true;
michael@0 605 }
michael@0 606
michael@0 607 static MOZ_ALWAYS_INLINE bool
michael@0 608 BitRsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
michael@0 609 {
michael@0 610 int32_t left, right;
michael@0 611 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
michael@0 612 return false;
michael@0 613 *out = left >> (right & 31);
michael@0 614 return true;
michael@0 615 }
michael@0 616
michael@0 617 static MOZ_ALWAYS_INLINE bool
michael@0 618 UrshOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out)
michael@0 619 {
michael@0 620 uint32_t left;
michael@0 621 int32_t right;
michael@0 622 if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
michael@0 623 return false;
michael@0 624 left >>= right & 31;
michael@0 625 out.setNumber(uint32_t(left));
michael@0 626 return true;
michael@0 627 }
michael@0 628
michael@0 629 #undef RELATIONAL_OP
michael@0 630
michael@0 631 inline JSFunction *
michael@0 632 ReportIfNotFunction(JSContext *cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT)
michael@0 633 {
michael@0 634 if (v.isObject() && v.toObject().is<JSFunction>())
michael@0 635 return &v.toObject().as<JSFunction>();
michael@0 636
michael@0 637 ReportIsNotFunction(cx, v, -1, construct);
michael@0 638 return nullptr;
michael@0 639 }
michael@0 640
michael@0 641 /*
michael@0 642 * FastInvokeGuard is used to optimize calls to JS functions from natives written
michael@0 643 * in C++, for instance Array.map. If the callee is not Ion-compiled, this will
michael@0 644 * just call Invoke. If the callee has a valid IonScript, however, it will enter
michael@0 645 * Ion directly.
michael@0 646 */
michael@0 647 class FastInvokeGuard
michael@0 648 {
michael@0 649 InvokeArgs args_;
michael@0 650 RootedFunction fun_;
michael@0 651 RootedScript script_;
michael@0 652 #ifdef JS_ION
michael@0 653 // Constructing an IonContext is pretty expensive due to the TLS access,
michael@0 654 // so only do this if we have to.
michael@0 655 bool useIon_;
michael@0 656 #endif
michael@0 657
michael@0 658 public:
michael@0 659 FastInvokeGuard(JSContext *cx, const Value &fval)
michael@0 660 : args_(cx)
michael@0 661 , fun_(cx)
michael@0 662 , script_(cx)
michael@0 663 #ifdef JS_ION
michael@0 664 , useIon_(jit::IsIonEnabled(cx))
michael@0 665 #endif
michael@0 666 {
michael@0 667 JS_ASSERT(!InParallelSection());
michael@0 668 initFunction(fval);
michael@0 669 }
michael@0 670
michael@0 671 void initFunction(const Value &fval) {
michael@0 672 if (fval.isObject() && fval.toObject().is<JSFunction>()) {
michael@0 673 JSFunction *fun = &fval.toObject().as<JSFunction>();
michael@0 674 if (fun->isInterpreted())
michael@0 675 fun_ = fun;
michael@0 676 }
michael@0 677 }
michael@0 678
michael@0 679 InvokeArgs &args() {
michael@0 680 return args_;
michael@0 681 }
michael@0 682
michael@0 683 bool invoke(JSContext *cx) {
michael@0 684 #ifdef JS_ION
michael@0 685 if (useIon_ && fun_) {
michael@0 686 if (!script_) {
michael@0 687 script_ = fun_->getOrCreateScript(cx);
michael@0 688 if (!script_)
michael@0 689 return false;
michael@0 690 }
michael@0 691 JS_ASSERT(fun_->nonLazyScript() == script_);
michael@0 692
michael@0 693 jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length());
michael@0 694 if (status == jit::Method_Error)
michael@0 695 return false;
michael@0 696 if (status == jit::Method_Compiled) {
michael@0 697 jit::IonExecStatus result = jit::FastInvoke(cx, fun_, args_);
michael@0 698 if (IsErrorStatus(result))
michael@0 699 return false;
michael@0 700
michael@0 701 JS_ASSERT(result == jit::IonExec_Ok);
michael@0 702 return true;
michael@0 703 }
michael@0 704
michael@0 705 JS_ASSERT(status == jit::Method_Skipped);
michael@0 706
michael@0 707 if (script_->canIonCompile()) {
michael@0 708 // This script is not yet hot. Since calling into Ion is much
michael@0 709 // faster here, bump the use count a bit to account for this.
michael@0 710 script_->incUseCount(5);
michael@0 711 }
michael@0 712 }
michael@0 713 #endif
michael@0 714
michael@0 715 return Invoke(cx, args_);
michael@0 716 }
michael@0 717
michael@0 718 private:
michael@0 719 FastInvokeGuard(const FastInvokeGuard& other) MOZ_DELETE;
michael@0 720 const FastInvokeGuard& operator=(const FastInvokeGuard& other) MOZ_DELETE;
michael@0 721 };
michael@0 722
michael@0 723 } /* namespace js */
michael@0 724
michael@0 725 #endif /* vm_Interpreter_inl_h */

mercurial