/js/src/jsinterpinlines.h

http://github.com/zpao/v8monkey · C Header · 887 lines · 662 code · 102 blank · 123 comment · 210 complexity · ced114b536aa092968fa4d811755fc46 MD5 · raw file

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=4 sw=4 et tw=99:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is SpiderMonkey code.
  18. *
  19. * The Initial Developer of the Original Code is
  20. * Mozilla Corporation.
  21. * Portions created by the Initial Developer are Copyright (C) 2010
  22. * the Initial Developer. All Rights Reserved.
  23. *
  24. * Contributor(s):
  25. * Luke Wagner <lw@mozilla.com>
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #ifndef jsinterpinlines_h__
  41. #define jsinterpinlines_h__
  42. #include "jsapi.h"
  43. #include "jsbool.h"
  44. #include "jscompartment.h"
  45. #include "jsinfer.h"
  46. #include "jsinterp.h"
  47. #include "jslibmath.h"
  48. #include "jsnum.h"
  49. #include "jsprobes.h"
  50. #include "jsstr.h"
  51. #include "methodjit/MethodJIT.h"
  52. #include "jsfuninlines.h"
  53. #include "jsinferinlines.h"
  54. #include "jspropertycacheinlines.h"
  55. #include "jstypedarrayinlines.h"
  56. #include "vm/Stack-inl.h"
  57. namespace js {
  58. class AutoPreserveEnumerators {
  59. JSContext *cx;
  60. JSObject *enumerators;
  61. public:
  62. AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
  63. {
  64. }
  65. ~AutoPreserveEnumerators()
  66. {
  67. cx->enumerators = enumerators;
  68. }
  69. };
  70. /*
  71. * Compute the implicit |this| parameter for a call expression where the callee
  72. * funval was resolved from an unqualified name reference to a property on obj
  73. * (an object on the scope chain).
  74. *
  75. * We can avoid computing |this| eagerly and push the implicit callee-coerced
  76. * |this| value, undefined, if any of these conditions hold:
  77. *
  78. * 1. The nominal |this|, obj, is a global object.
  79. *
  80. * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
  81. * is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
  82. * censored with undefined.
  83. *
  84. * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with|
  85. * statements and embedding-specific scope objects fall into this category.
  86. *
  87. * If the callee is a strict mode function, then code implementing JSOP_THIS
  88. * in the interpreter and JITs will leave undefined as |this|. If funval is a
  89. * function not in strict mode, JSOP_THIS code replaces undefined with funval's
  90. * global.
  91. *
  92. * We set *vp to undefined early to reduce code size and bias this code for the
  93. * common and future-friendly cases.
  94. */
  95. inline bool
  96. ComputeImplicitThis(JSContext *cx, JSObject *obj, Value *vp)
  97. {
  98. vp->setUndefined();
  99. if (obj->isGlobal())
  100. return true;
  101. if (IsCacheableNonGlobalScope(obj))
  102. return true;
  103. obj = obj->thisObject(cx);
  104. if (!obj)
  105. return false;
  106. vp->setObject(*obj);
  107. return true;
  108. }
  109. inline bool
  110. ComputeThis(JSContext *cx, StackFrame *fp)
  111. {
  112. Value &thisv = fp->thisValue();
  113. if (thisv.isObject())
  114. return true;
  115. if (fp->isFunctionFrame()) {
  116. if (fp->fun()->inStrictMode())
  117. return true;
  118. /*
  119. * Eval function frames have their own |this| slot, which is a copy of the function's
  120. * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
  121. * eval's frame will get the wrapper, but the function's frame will not. To prevent
  122. * this, we always wrap a function's |this| before pushing an eval frame, and should
  123. * thus never see an unwrapped primitive in a non-strict eval function frame.
  124. */
  125. JS_ASSERT(!fp->isEvalFrame());
  126. }
  127. return BoxNonStrictThis(cx, fp->callReceiver());
  128. }
  129. /*
  130. * Return an object on which we should look for the properties of |value|.
  131. * This helps us implement the custom [[Get]] method that ES5's GetValue
  132. * algorithm uses for primitive values, without actually constructing the
  133. * temporary object that the specification does.
  134. *
  135. * For objects, return the object itself. For string, boolean, and number
  136. * primitive values, return the appropriate constructor's prototype. For
  137. * undefined and null, throw an error and return NULL, attributing the
  138. * problem to the value at |spindex| on the stack.
  139. */
  140. JS_ALWAYS_INLINE JSObject *
  141. ValuePropertyBearer(JSContext *cx, StackFrame *fp, const Value &v, int spindex)
  142. {
  143. if (v.isObject())
  144. return &v.toObject();
  145. GlobalObject &global = fp->scopeChain().global();
  146. if (v.isString())
  147. return global.getOrCreateStringPrototype(cx);
  148. if (v.isNumber())
  149. return global.getOrCreateNumberPrototype(cx);
  150. if (v.isBoolean())
  151. return global.getOrCreateBooleanPrototype(cx);
  152. JS_ASSERT(v.isNull() || v.isUndefined());
  153. js_ReportIsNullOrUndefined(cx, spindex, v, NULL);
  154. return NULL;
  155. }
  156. inline bool
  157. NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow, Value *vp)
  158. {
  159. if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
  160. /* Fast path for Object instance properties. */
  161. JS_ASSERT(shape->hasSlot());
  162. *vp = pobj->nativeGetSlot(shape->slot());
  163. } else {
  164. if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))
  165. return false;
  166. }
  167. return true;
  168. }
  169. #if defined(DEBUG) && !defined(JS_THREADSAFE)
  170. extern void
  171. AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
  172. PropertyCacheEntry *entry);
  173. #else
  174. inline void
  175. AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
  176. PropertyCacheEntry *entry)
  177. {}
  178. #endif
  179. inline bool
  180. GetPropertyGenericMaybeCallXML(JSContext *cx, JSOp op, JSObject *obj, jsid id, Value *vp)
  181. {
  182. /*
  183. * Various XML properties behave differently when accessed in a
  184. * call vs. normal context, and getGeneric will not work right.
  185. */
  186. #if JS_HAS_XML_SUPPORT
  187. if (op == JSOP_CALLPROP && obj->isXML())
  188. return js_GetXMLMethod(cx, obj, id, vp);
  189. #endif
  190. return obj->getGeneric(cx, id, vp);
  191. }
  192. inline bool
  193. GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp)
  194. {
  195. JS_ASSERT(vp != &lval);
  196. JSOp op = JSOp(*pc);
  197. if (op == JSOP_LENGTH) {
  198. /* Optimize length accesses on strings, arrays, and arguments. */
  199. if (lval.isString()) {
  200. *vp = Int32Value(lval.toString()->length());
  201. return true;
  202. }
  203. if (lval.isMagic(JS_LAZY_ARGUMENTS)) {
  204. *vp = Int32Value(cx->fp()->numActualArgs());
  205. return true;
  206. }
  207. if (lval.isObject()) {
  208. JSObject *obj = &lval.toObject();
  209. if (obj->isArray()) {
  210. jsuint length = obj->getArrayLength();
  211. *vp = NumberValue(length);
  212. return true;
  213. }
  214. if (obj->isArguments()) {
  215. ArgumentsObject *argsobj = &obj->asArguments();
  216. if (!argsobj->hasOverriddenLength()) {
  217. uint32_t length = argsobj->initialLength();
  218. JS_ASSERT(length < INT32_MAX);
  219. *vp = Int32Value(int32_t(length));
  220. return true;
  221. }
  222. }
  223. if (js_IsTypedArray(obj)) {
  224. JSObject *tarray = TypedArray::getTypedArray(obj);
  225. *vp = Int32Value(TypedArray::getLength(tarray));
  226. return true;
  227. }
  228. }
  229. }
  230. JSObject *obj = ValueToObjectOrPrototype(cx, lval);
  231. if (!obj)
  232. return false;
  233. uintN flags = (op == JSOP_CALLPROP)
  234. ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
  235. : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER;
  236. PropertyCacheEntry *entry;
  237. JSObject *obj2;
  238. PropertyName *name;
  239. JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
  240. if (!name) {
  241. AssertValidPropertyCacheHit(cx, obj, obj2, entry);
  242. if (!NativeGet(cx, obj, obj2, entry->prop, flags, vp))
  243. return false;
  244. return true;
  245. }
  246. jsid id = ATOM_TO_JSID(name);
  247. if (obj->getOps()->getProperty) {
  248. if (!GetPropertyGenericMaybeCallXML(cx, op, obj, id, vp))
  249. return false;
  250. } else {
  251. if (!GetPropertyHelper(cx, obj, id, flags, vp))
  252. return false;
  253. }
  254. #if JS_HAS_NO_SUCH_METHOD
  255. if (op == JSOP_CALLPROP &&
  256. JS_UNLIKELY(vp->isPrimitive()) &&
  257. lval.isObject())
  258. {
  259. if (!OnUnknownMethod(cx, obj, IdToValue(id), vp))
  260. return false;
  261. }
  262. #endif
  263. return true;
  264. }
  265. inline bool
  266. SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Value &rval)
  267. {
  268. JSObject *obj = ValueToObject(cx, lval);
  269. if (!obj)
  270. return false;
  271. JS_ASSERT_IF(*pc == JSOP_SETMETHOD, IsFunctionObject(rval));
  272. JS_ASSERT_IF(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME, lval.isObject());
  273. JS_ASSERT_IF(*pc == JSOP_SETGNAME, obj == &cx->fp()->scopeChain().global());
  274. PropertyCacheEntry *entry;
  275. JSObject *obj2;
  276. PropertyName *name;
  277. if (JS_PROPERTY_CACHE(cx).testForSet(cx, pc, obj, &entry, &obj2, &name)) {
  278. /*
  279. * Property cache hit, only partially confirmed by testForSet. We
  280. * know that the entry applies to regs.pc and that obj's shape
  281. * matches.
  282. *
  283. * The entry predicts a set either an existing "own" property, or
  284. * on a prototype property that has a setter.
  285. */
  286. const Shape *shape = entry->prop;
  287. JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
  288. JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
  289. if (entry->isOwnPropertyHit() ||
  290. ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
  291. #ifdef DEBUG
  292. if (entry->isOwnPropertyHit()) {
  293. JS_ASSERT(obj->nativeContains(cx, *shape));
  294. } else {
  295. JS_ASSERT(obj2->nativeContains(cx, *shape));
  296. JS_ASSERT(entry->isPrototypePropertyHit());
  297. JS_ASSERT(entry->kshape != entry->pshape);
  298. JS_ASSERT(!shape->hasSlot());
  299. }
  300. #endif
  301. if (shape->hasDefaultSetter() && shape->hasSlot() && !shape->isMethod()) {
  302. /* Fast path for, e.g., plain Object instance properties. */
  303. obj->nativeSetSlotWithType(cx, shape, rval);
  304. } else {
  305. Value rref = rval;
  306. bool strict = cx->stack.currentScript()->strictModeCode;
  307. if (!js_NativeSet(cx, obj, shape, false, strict, &rref))
  308. return false;
  309. }
  310. return true;
  311. }
  312. GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name);
  313. }
  314. bool strict = cx->stack.currentScript()->strictModeCode;
  315. Value rref = rval;
  316. JSOp op = JSOp(*pc);
  317. jsid id = ATOM_TO_JSID(name);
  318. if (JS_LIKELY(!obj->getOps()->setProperty)) {
  319. uintN defineHow;
  320. if (op == JSOP_SETMETHOD)
  321. defineHow = DNP_CACHE_RESULT | DNP_SET_METHOD;
  322. else if (op == JSOP_SETNAME)
  323. defineHow = DNP_CACHE_RESULT | DNP_UNQUALIFIED;
  324. else
  325. defineHow = DNP_CACHE_RESULT;
  326. if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref, strict))
  327. return false;
  328. } else {
  329. if (!obj->setGeneric(cx, id, &rref, strict))
  330. return false;
  331. }
  332. return true;
  333. }
  334. inline bool
  335. NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
  336. {
  337. JSObject *obj = cx->stack.currentScriptedScopeChain();
  338. /*
  339. * Skip along the scope chain to the enclosing global object. This is
  340. * used for GNAME opcodes where the bytecode emitter has determined a
  341. * name access must be on the global. It also insulates us from bugs
  342. * in the emitter: type inference will assume that GNAME opcodes are
  343. * accessing the global object, and the inferred behavior should match
  344. * the actual behavior even if the id could be found on the scope chain
  345. * before the global object.
  346. */
  347. if (js_CodeSpec[*pc].format & JOF_GNAME)
  348. obj = &obj->global();
  349. PropertyCacheEntry *entry;
  350. JSObject *obj2;
  351. PropertyName *name;
  352. JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
  353. if (!name) {
  354. AssertValidPropertyCacheHit(cx, obj, obj2, entry);
  355. if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, vp))
  356. return false;
  357. return true;
  358. }
  359. jsid id = ATOM_TO_JSID(name);
  360. JSProperty *prop;
  361. if (!FindPropertyHelper(cx, name, true, obj, &obj, &obj2, &prop))
  362. return false;
  363. if (!prop) {
  364. /* Kludge to allow (typeof foo == "undefined") tests. */
  365. JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
  366. if (op2 == JSOP_TYPEOF) {
  367. vp->setUndefined();
  368. return true;
  369. }
  370. JSAutoByteString printable;
  371. if (js_AtomToPrintableString(cx, name, &printable))
  372. js_ReportIsNotDefined(cx, printable.ptr());
  373. return false;
  374. }
  375. /* Take the slow path if prop was not found in a native object. */
  376. if (!obj->isNative() || !obj2->isNative()) {
  377. if (!obj->getGeneric(cx, id, vp))
  378. return false;
  379. } else {
  380. Shape *shape = (Shape *)prop;
  381. JSObject *normalized = obj;
  382. if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
  383. normalized = &normalized->asWith().object();
  384. if (!NativeGet(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, vp))
  385. return false;
  386. }
  387. return true;
  388. }
  389. inline bool
  390. DefVarOrConstOperation(JSContext *cx, JSObject &varobj, PropertyName *dn, uintN attrs)
  391. {
  392. JS_ASSERT(varobj.isVarObj());
  393. JS_ASSERT(!varobj.getOps()->defineProperty);
  394. JSProperty *prop;
  395. JSObject *obj2;
  396. if (!varobj.lookupProperty(cx, dn, &obj2, &prop))
  397. return false;
  398. /* Steps 8c, 8d. */
  399. if (!prop || (obj2 != &varobj && varobj.isGlobal())) {
  400. if (!DefineNativeProperty(cx, &varobj, dn, UndefinedValue(),
  401. JS_PropertyStub, JS_StrictPropertyStub, attrs, 0, 0))
  402. {
  403. return false;
  404. }
  405. } else {
  406. /*
  407. * Extension: ordinarily we'd be done here -- but for |const|. If we
  408. * see a redeclaration that's |const|, we consider it a conflict.
  409. */
  410. uintN oldAttrs;
  411. if (!varobj.getPropertyAttributes(cx, dn, &oldAttrs))
  412. return false;
  413. if (attrs & JSPROP_READONLY) {
  414. JSAutoByteString bytes;
  415. if (js_AtomToPrintableString(cx, dn, &bytes)) {
  416. JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
  417. js_GetErrorMessage,
  418. NULL, JSMSG_REDECLARED_VAR,
  419. (oldAttrs & JSPROP_READONLY)
  420. ? "const"
  421. : "var",
  422. bytes.ptr()));
  423. }
  424. return false;
  425. }
  426. }
  427. return true;
  428. }
  429. inline bool
  430. FunctionNeedsPrologue(JSContext *cx, JSFunction *fun)
  431. {
  432. /* Heavyweight functions need call objects created. */
  433. if (fun->isHeavyweight())
  434. return true;
  435. /* Outer and inner functions need to preserve nesting invariants. */
  436. if (cx->typeInferenceEnabled() && fun->script()->nesting())
  437. return true;
  438. return false;
  439. }
  440. inline bool
  441. ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType)
  442. {
  443. JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj());
  444. if (fp->isConstructing()) {
  445. JSObject *obj = js_CreateThisForFunction(cx, &fp->callee(), newType);
  446. if (!obj)
  447. return false;
  448. fp->functionThis().setObject(*obj);
  449. }
  450. Probes::enterJSFun(cx, fp->maybeFun(), fp->script());
  451. return true;
  452. }
  453. inline bool
  454. ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok)
  455. {
  456. Probes::exitJSFun(cx, fp->maybeFun(), fp->script());
  457. /*
  458. * If inline-constructing, replace primitive rval with the new object
  459. * passed in via |this|, and instrument this constructor invocation.
  460. */
  461. if (fp->isConstructing() && ok) {
  462. if (fp->returnValue().isPrimitive())
  463. fp->setReturnValue(ObjectValue(fp->constructorThis()));
  464. }
  465. return ok;
  466. }
  467. inline bool
  468. ScriptPrologueOrGeneratorResume(JSContext *cx, StackFrame *fp, bool newType)
  469. {
  470. if (!fp->isGeneratorFrame())
  471. return ScriptPrologue(cx, fp, newType);
  472. return true;
  473. }
  474. inline bool
  475. ScriptEpilogueOrGeneratorYield(JSContext *cx, StackFrame *fp, bool ok)
  476. {
  477. if (!fp->isYielding())
  478. return ScriptEpilogue(cx, fp, ok);
  479. return ok;
  480. }
  481. inline void
  482. InterpreterFrames::enableInterruptsIfRunning(JSScript *script)
  483. {
  484. if (script == regs->fp()->script())
  485. enabler.enableInterrupts();
  486. }
  487. static JS_ALWAYS_INLINE bool
  488. AddOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
  489. {
  490. Value lval = lhs;
  491. Value rval = rhs;
  492. if (lval.isInt32() && rval.isInt32()) {
  493. int32_t l = lval.toInt32(), r = rval.toInt32();
  494. int32_t sum = l + r;
  495. if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) {
  496. res->setDouble(double(l) + double(r));
  497. types::TypeScript::MonitorOverflow(cx);
  498. } else {
  499. res->setInt32(sum);
  500. }
  501. } else
  502. #if JS_HAS_XML_SUPPORT
  503. if (IsXML(lval) && IsXML(rval)) {
  504. if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), res))
  505. return false;
  506. types::TypeScript::MonitorUnknown(cx);
  507. } else
  508. #endif
  509. {
  510. /*
  511. * If either operand is an object, any non-integer result must be
  512. * reported to inference.
  513. */
  514. bool lIsObject = lval.isObject(), rIsObject = rval.isObject();
  515. if (!ToPrimitive(cx, &lval))
  516. return false;
  517. if (!ToPrimitive(cx, &rval))
  518. return false;
  519. bool lIsString, rIsString;
  520. if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
  521. js::AutoStringRooter lstr(cx), rstr(cx);
  522. if (lIsString) {
  523. lstr.setString(lval.toString());
  524. } else {
  525. lstr.setString(ToString(cx, lval));
  526. if (!lstr.string())
  527. return false;
  528. }
  529. if (rIsString) {
  530. rstr.setString(rval.toString());
  531. } else {
  532. rstr.setString(ToString(cx, rval));
  533. if (!rstr.string())
  534. return false;
  535. }
  536. JSString *str = js_ConcatStrings(cx, lstr.string(), rstr.string());
  537. if (!str)
  538. return false;
  539. if (lIsObject || rIsObject)
  540. types::TypeScript::MonitorString(cx);
  541. res->setString(str);
  542. } else {
  543. double l, r;
  544. if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))
  545. return false;
  546. l += r;
  547. if (!res->setNumber(l) &&
  548. (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) {
  549. types::TypeScript::MonitorOverflow(cx);
  550. }
  551. }
  552. }
  553. return true;
  554. }
  555. static JS_ALWAYS_INLINE bool
  556. SubOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
  557. {
  558. double d1, d2;
  559. if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
  560. return false;
  561. double d = d1 - d2;
  562. if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
  563. types::TypeScript::MonitorOverflow(cx);
  564. return true;
  565. }
  566. static JS_ALWAYS_INLINE bool
  567. MulOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
  568. {
  569. double d1, d2;
  570. if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
  571. return false;
  572. double d = d1 * d2;
  573. if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
  574. types::TypeScript::MonitorOverflow(cx);
  575. return true;
  576. }
  577. static JS_ALWAYS_INLINE bool
  578. DivOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
  579. {
  580. double d1, d2;
  581. if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
  582. return false;
  583. res->setNumber(NumberDiv(d1, d2));
  584. if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble())))
  585. types::TypeScript::MonitorOverflow(cx);
  586. return true;
  587. }
  588. static JS_ALWAYS_INLINE bool
  589. ModOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
  590. {
  591. int32_t l, r;
  592. if (lhs.isInt32() && rhs.isInt32() &&
  593. (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) {
  594. int32_t mod = l % r;
  595. res->setInt32(mod);
  596. return true;
  597. }
  598. double d1, d2;
  599. if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
  600. return false;
  601. if (d2 == 0)
  602. res->setDouble(js_NaN);
  603. else
  604. res->setDouble(js_fmod(d1, d2));
  605. types::TypeScript::MonitorOverflow(cx);
  606. return true;
  607. }
  608. static inline bool
  609. FetchElementId(JSContext *cx, JSObject *obj, const Value &idval, jsid &id, Value *vp)
  610. {
  611. int32_t i_;
  612. if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
  613. id = INT_TO_JSID(i_);
  614. return true;
  615. }
  616. return !!js_InternNonIntElementId(cx, obj, idval, &id, vp);
  617. }
  618. static JS_ALWAYS_INLINE bool
  619. ToIdOperation(JSContext *cx, const Value &objval, const Value &idval, Value *res)
  620. {
  621. if (idval.isInt32()) {
  622. *res = idval;
  623. return true;
  624. }
  625. JSObject *obj = ValueToObject(cx, objval);
  626. if (!obj)
  627. return false;
  628. jsid dummy;
  629. if (!js_InternNonIntElementId(cx, obj, idval, &dummy, res))
  630. return false;
  631. if (!res->isInt32())
  632. types::TypeScript::MonitorUnknown(cx);
  633. return true;
  634. }
  635. static JS_ALWAYS_INLINE bool
  636. GetObjectElementOperation(JSContext *cx, JSObject *obj, const Value &rref, Value *res)
  637. {
  638. JSScript *script;
  639. jsbytecode *pc;
  640. types::TypeScript::GetPcScript(cx, &script, &pc);
  641. uint32_t index;
  642. if (IsDefinitelyIndex(rref, &index)) {
  643. do {
  644. if (obj->isDenseArray()) {
  645. if (index < obj->getDenseArrayInitializedLength()) {
  646. *res = obj->getDenseArrayElement(index);
  647. if (!res->isMagic())
  648. break;
  649. }
  650. } else if (obj->isArguments()) {
  651. if (obj->asArguments().getElement(index, res))
  652. break;
  653. }
  654. if (!obj->getElement(cx, index, res))
  655. return false;
  656. } while(0);
  657. } else {
  658. if (script->hasAnalysis())
  659. script->analysis()->getCode(pc).getStringElement = true;
  660. SpecialId special;
  661. *res = rref;
  662. if (ValueIsSpecial(obj, res, &special, cx)) {
  663. if (!obj->getSpecial(cx, obj, special, res))
  664. return false;
  665. } else {
  666. JSAtom *name;
  667. if (!js_ValueToAtom(cx, *res, &name))
  668. return false;
  669. if (name->isIndex(&index)) {
  670. if (!obj->getElement(cx, index, res))
  671. return false;
  672. } else {
  673. if (!obj->getProperty(cx, name->asPropertyName(), res))
  674. return false;
  675. }
  676. }
  677. }
  678. assertSameCompartment(cx, *res);
  679. types::TypeScript::Monitor(cx, script, pc, *res);
  680. return true;
  681. }
  682. static JS_ALWAYS_INLINE bool
  683. GetElementOperation(JSContext *cx, const Value &lref, const Value &rref, Value *res)
  684. {
  685. if (lref.isString() && rref.isInt32()) {
  686. JSString *str = lref.toString();
  687. int32_t i = rref.toInt32();
  688. if (size_t(i) < str->length()) {
  689. str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
  690. if (!str)
  691. return false;
  692. res->setString(str);
  693. types::TypeScript::Monitor(cx, *res);
  694. return true;
  695. }
  696. }
  697. if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
  698. if (rref.isInt32() && size_t(rref.toInt32()) < cx->regs().fp()->numActualArgs()) {
  699. *res = cx->regs().fp()->canonicalActualArg(rref.toInt32());
  700. types::TypeScript::Monitor(cx, *res);
  701. return true;
  702. }
  703. types::MarkArgumentsCreated(cx, cx->fp()->script());
  704. JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
  705. }
  706. JSObject *obj = ValueToObject(cx, lref);
  707. if (!obj)
  708. return false;
  709. return GetObjectElementOperation(cx, obj, rref, res);
  710. }
  711. static JS_ALWAYS_INLINE bool
  712. SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value)
  713. {
  714. JSScript *script;
  715. jsbytecode *pc;
  716. types::TypeScript::GetPcScript(cx, &script, &pc);
  717. types::TypeScript::MonitorAssign(cx, script, pc, obj, id, value);
  718. do {
  719. if (obj->isDenseArray() && JSID_IS_INT(id)) {
  720. jsuint length = obj->getDenseArrayInitializedLength();
  721. jsint i = JSID_TO_INT(id);
  722. if ((jsuint)i < length) {
  723. if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
  724. if (js_PrototypeHasIndexedProperties(cx, obj))
  725. break;
  726. if ((jsuint)i >= obj->getArrayLength())
  727. obj->setArrayLength(cx, i + 1);
  728. }
  729. obj->setDenseArrayElementWithType(cx, i, value);
  730. return true;
  731. } else {
  732. if (script->hasAnalysis())
  733. script->analysis()->getCode(pc).arrayWriteHole = true;
  734. }
  735. }
  736. } while (0);
  737. Value tmp = value;
  738. return obj->setGeneric(cx, id, &tmp, script->strictModeCode);
  739. }
  740. #define RELATIONAL_OP(OP) \
  741. JS_BEGIN_MACRO \
  742. Value lval = lhs; \
  743. Value rval = rhs; \
  744. /* Optimize for two int-tagged operands (typical loop control). */ \
  745. if (lval.isInt32() && rval.isInt32()) { \
  746. *res = lval.toInt32() OP rval.toInt32(); \
  747. } else { \
  748. if (!ToPrimitive(cx, JSTYPE_NUMBER, &lval)) \
  749. return false; \
  750. if (!ToPrimitive(cx, JSTYPE_NUMBER, &rval)) \
  751. return false; \
  752. if (lval.isString() && rval.isString()) { \
  753. JSString *l = lval.toString(), *r = rval.toString(); \
  754. int32_t result; \
  755. if (!CompareStrings(cx, l, r, &result)) \
  756. return false; \
  757. *res = result OP 0; \
  758. } else { \
  759. double l, r; \
  760. if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) \
  761. return false;; \
  762. *res = (l OP r); \
  763. } \
  764. } \
  765. return true; \
  766. JS_END_MACRO
  767. static JS_ALWAYS_INLINE bool
  768. LessThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
  769. RELATIONAL_OP(<);
  770. }
  771. static JS_ALWAYS_INLINE bool
  772. LessThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
  773. RELATIONAL_OP(<=);
  774. }
  775. static JS_ALWAYS_INLINE bool
  776. GreaterThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
  777. RELATIONAL_OP(>);
  778. }
  779. static JS_ALWAYS_INLINE bool
  780. GreaterThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
  781. RELATIONAL_OP(>=);
  782. }
  783. #undef RELATIONAL_OP
  784. } /* namespace js */
  785. #endif /* jsinterpinlines_h__ */