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 :
41 : #ifndef jsinterpinlines_h__
42 : #define jsinterpinlines_h__
43 :
44 : #include "jsapi.h"
45 : #include "jsbool.h"
46 : #include "jscompartment.h"
47 : #include "jsinfer.h"
48 : #include "jsinterp.h"
49 : #include "jslibmath.h"
50 : #include "jsnum.h"
51 : #include "jsprobes.h"
52 : #include "jsstr.h"
53 : #include "methodjit/MethodJIT.h"
54 :
55 : #include "jsfuninlines.h"
56 : #include "jsinferinlines.h"
57 : #include "jspropertycacheinlines.h"
58 : #include "jstypedarrayinlines.h"
59 :
60 : #include "vm/Stack-inl.h"
61 :
62 : namespace js {
63 :
64 : /*
65 : * Compute the implicit |this| parameter for a call expression where the callee
66 : * funval was resolved from an unqualified name reference to a property on obj
67 : * (an object on the scope chain).
68 : *
69 : * We can avoid computing |this| eagerly and push the implicit callee-coerced
70 : * |this| value, undefined, if any of these conditions hold:
71 : *
72 : * 1. The nominal |this|, obj, is a global object.
73 : *
74 : * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
75 : * is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
76 : * censored with undefined.
77 : *
78 : * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with|
79 : * statements and embedding-specific scope objects fall into this category.
80 : *
81 : * If the callee is a strict mode function, then code implementing JSOP_THIS
82 : * in the interpreter and JITs will leave undefined as |this|. If funval is a
83 : * function not in strict mode, JSOP_THIS code replaces undefined with funval's
84 : * global.
85 : *
86 : * We set *vp to undefined early to reduce code size and bias this code for the
87 : * common and future-friendly cases.
88 : */
89 : inline bool
90 3217867 : ComputeImplicitThis(JSContext *cx, JSObject *obj, Value *vp)
91 : {
92 3217867 : vp->setUndefined();
93 :
94 3217867 : if (obj->isGlobal())
95 3189072 : return true;
96 :
97 28795 : if (IsCacheableNonGlobalScope(obj))
98 28777 : return true;
99 :
100 18 : obj = obj->thisObject(cx);
101 18 : if (!obj)
102 0 : return false;
103 :
104 18 : vp->setObject(*obj);
105 18 : return true;
106 : }
107 :
108 : inline bool
109 12152873 : ComputeThis(JSContext *cx, StackFrame *fp)
110 : {
111 12152873 : Value &thisv = fp->thisValue();
112 12152873 : if (thisv.isObject())
113 12090876 : return true;
114 61997 : if (fp->isFunctionFrame()) {
115 61997 : if (fp->fun()->inStrictMode())
116 1976 : return true;
117 : /*
118 : * Eval function frames have their own |this| slot, which is a copy of the function's
119 : * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
120 : * eval's frame will get the wrapper, but the function's frame will not. To prevent
121 : * this, we always wrap a function's |this| before pushing an eval frame, and should
122 : * thus never see an unwrapped primitive in a non-strict eval function frame.
123 : */
124 60021 : JS_ASSERT(!fp->isEvalFrame());
125 : }
126 60021 : return BoxNonStrictThis(cx, fp->callReceiver());
127 : }
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 :
146 : GlobalObject &global = fp->scopeChain().global();
147 :
148 : if (v.isString())
149 : return global.getOrCreateStringPrototype(cx);
150 : if (v.isNumber())
151 : return global.getOrCreateNumberPrototype(cx);
152 : if (v.isBoolean())
153 : return global.getOrCreateBooleanPrototype(cx);
154 :
155 : JS_ASSERT(v.isNull() || v.isUndefined());
156 : js_ReportIsNullOrUndefined(cx, spindex, v, NULL);
157 : return NULL;
158 : }
159 :
160 : inline bool
161 107021630 : NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, unsigned getHow, Value *vp)
162 : {
163 107021630 : if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
164 : /* Fast path for Object instance properties. */
165 104037204 : JS_ASSERT(shape->hasSlot());
166 104037204 : *vp = pobj->nativeGetSlot(shape->slot());
167 : } else {
168 2984426 : if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))
169 3384 : return false;
170 : }
171 107018246 : return true;
172 : }
173 :
174 : #if defined(DEBUG) && !defined(JS_THREADSAFE)
175 : extern void
176 : AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
177 : PropertyCacheEntry *entry);
178 : #else
179 : inline void
180 99500702 : AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
181 : PropertyCacheEntry *entry)
182 99500702 : {}
183 : #endif
184 :
185 : inline bool
186 2586902 : GetPropertyGenericMaybeCallXML(JSContext *cx, JSOp op, JSObject *obj, jsid id, Value *vp)
187 : {
188 : /*
189 : * Various XML properties behave differently when accessed in a
190 : * call vs. normal context, and getGeneric will not work right.
191 : */
192 : #if JS_HAS_XML_SUPPORT
193 2586902 : if (op == JSOP_CALLPROP && obj->isXML())
194 81 : return js_GetXMLMethod(cx, obj, id, vp);
195 : #endif
196 :
197 2586821 : return obj->getGeneric(cx, id, vp);
198 : }
199 :
200 : inline bool
201 90980582 : GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp)
202 : {
203 90980582 : JS_ASSERT(vp != &lval);
204 :
205 90980582 : JSOp op = JSOp(*pc);
206 :
207 90980582 : if (op == JSOP_LENGTH) {
208 : /* Optimize length accesses on strings, arrays, and arguments. */
209 43033004 : if (lval.isString()) {
210 336345 : *vp = Int32Value(lval.toString()->length());
211 336345 : return true;
212 : }
213 42696659 : if (lval.isMagic(JS_LAZY_ARGUMENTS)) {
214 36138 : *vp = Int32Value(cx->fp()->numActualArgs());
215 36138 : return true;
216 : }
217 42660521 : if (lval.isObject()) {
218 42660354 : JSObject *obj = &lval.toObject();
219 42660354 : if (obj->isArray()) {
220 22514426 : uint32_t length = obj->getArrayLength();
221 22514426 : *vp = NumberValue(length);
222 22514426 : return true;
223 : }
224 :
225 20145928 : if (obj->isArguments()) {
226 24442 : ArgumentsObject *argsobj = &obj->asArguments();
227 24442 : if (!argsobj->hasOverriddenLength()) {
228 23937 : uint32_t length = argsobj->initialLength();
229 23937 : JS_ASSERT(length < INT32_MAX);
230 23937 : *vp = Int32Value(int32_t(length));
231 23937 : return true;
232 : }
233 : }
234 :
235 20121991 : if (js_IsTypedArray(obj)) {
236 7010 : JSObject *tarray = TypedArray::getTypedArray(obj);
237 7010 : *vp = Int32Value(TypedArray::getLength(tarray));
238 7010 : return true;
239 : }
240 : }
241 : }
242 :
243 68062726 : JSObject *obj = ValueToObject(cx, lval);
244 68062726 : if (!obj)
245 293 : return false;
246 :
247 : unsigned flags = (op == JSOP_CALLPROP)
248 : ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
249 68062433 : : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER;
250 :
251 : PropertyCacheEntry *entry;
252 : JSObject *obj2;
253 : PropertyName *name;
254 68062433 : JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
255 68062433 : if (!name) {
256 54673693 : AssertValidPropertyCacheHit(cx, obj, obj2, entry);
257 54673693 : if (!NativeGet(cx, obj, obj2, entry->prop, flags, vp))
258 57 : return false;
259 54673636 : return true;
260 : }
261 :
262 13388740 : jsid id = ATOM_TO_JSID(name);
263 :
264 13388740 : if (obj->getOps()->getProperty) {
265 2586902 : if (!GetPropertyGenericMaybeCallXML(cx, op, obj, id, vp))
266 0 : return false;
267 : } else {
268 10801838 : if (!GetPropertyHelper(cx, obj, id, flags, vp))
269 421 : return false;
270 : }
271 :
272 : #if JS_HAS_NO_SUCH_METHOD
273 18383326 : if (op == JSOP_CALLPROP &&
274 4994222 : JS_UNLIKELY(vp->isPrimitive()) &&
275 785 : lval.isObject())
276 : {
277 687 : if (!OnUnknownMethod(cx, obj, IdToValue(id), vp))
278 0 : return false;
279 : }
280 : #endif
281 :
282 13388319 : return true;
283 : }
284 :
285 : inline bool
286 17694237 : SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Value &rval)
287 : {
288 17694237 : JSObject *obj = ValueToObject(cx, lval);
289 17694237 : if (!obj)
290 9 : return false;
291 :
292 17694228 : JS_ASSERT_IF(*pc == JSOP_SETMETHOD, IsFunctionObject(rval));
293 17694228 : JS_ASSERT_IF(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME, lval.isObject());
294 17694228 : JS_ASSERT_IF(*pc == JSOP_SETGNAME, obj == &cx->fp()->scopeChain().global());
295 :
296 : PropertyCacheEntry *entry;
297 : JSObject *obj2;
298 : PropertyName *name;
299 17694228 : if (JS_PROPERTY_CACHE(cx).testForSet(cx, pc, obj, &entry, &obj2, &name)) {
300 : /*
301 : * Property cache hit, only partially confirmed by testForSet. We
302 : * know that the entry applies to regs.pc and that obj's shape
303 : * matches.
304 : *
305 : * The entry predicts a set either an existing "own" property, or
306 : * on a prototype property that has a setter.
307 : */
308 15016855 : const Shape *shape = entry->prop;
309 15016855 : JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
310 15016855 : JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
311 :
312 15042481 : if (entry->isOwnPropertyHit() ||
313 25626 : ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
314 : #ifdef DEBUG
315 15016845 : if (entry->isOwnPropertyHit()) {
316 15004042 : JS_ASSERT(obj->nativeContains(cx, *shape));
317 : } else {
318 12803 : JS_ASSERT(obj2->nativeContains(cx, *shape));
319 12803 : JS_ASSERT(entry->isPrototypePropertyHit());
320 12803 : JS_ASSERT(entry->kshape != entry->pshape);
321 12803 : JS_ASSERT(!shape->hasSlot());
322 : }
323 : #endif
324 :
325 15016845 : if (shape->hasDefaultSetter() && shape->hasSlot() && !shape->isMethod()) {
326 : /* Fast path for, e.g., plain Object instance properties. */
327 14929174 : obj->nativeSetSlotWithType(cx, shape, rval);
328 : } else {
329 87671 : Value rref = rval;
330 87671 : bool strict = cx->stack.currentScript()->strictModeCode;
331 87671 : if (!js_NativeSet(cx, obj, shape, false, strict, &rref))
332 121 : return false;
333 : }
334 15016724 : return true;
335 : }
336 :
337 10 : GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name);
338 : }
339 :
340 2677383 : bool strict = cx->stack.currentScript()->strictModeCode;
341 2677383 : Value rref = rval;
342 :
343 2677383 : JSOp op = JSOp(*pc);
344 :
345 2677383 : jsid id = ATOM_TO_JSID(name);
346 2677383 : if (JS_LIKELY(!obj->getOps()->setProperty)) {
347 : unsigned defineHow;
348 2661099 : if (op == JSOP_SETMETHOD)
349 0 : defineHow = DNP_CACHE_RESULT | DNP_SET_METHOD;
350 2661099 : else if (op == JSOP_SETNAME)
351 154104 : defineHow = DNP_CACHE_RESULT | DNP_UNQUALIFIED;
352 : else
353 2506995 : defineHow = DNP_CACHE_RESULT;
354 2661099 : if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref, strict))
355 655 : return false;
356 : } else {
357 16284 : if (!obj->setGeneric(cx, id, &rref, strict))
358 18 : return false;
359 : }
360 :
361 2676710 : return true;
362 : }
363 :
364 : inline bool
365 52501662 : NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
366 : {
367 52501662 : JSObject *obj = cx->stack.currentScriptedScopeChain();
368 :
369 : /*
370 : * Skip along the scope chain to the enclosing global object. This is
371 : * used for GNAME opcodes where the bytecode emitter has determined a
372 : * name access must be on the global. It also insulates us from bugs
373 : * in the emitter: type inference will assume that GNAME opcodes are
374 : * accessing the global object, and the inferred behavior should match
375 : * the actual behavior even if the id could be found on the scope chain
376 : * before the global object.
377 : */
378 52501662 : if (js_CodeSpec[*pc].format & JOF_GNAME)
379 36124468 : obj = &obj->global();
380 :
381 : PropertyCacheEntry *entry;
382 : JSObject *obj2;
383 : PropertyName *name;
384 52501662 : JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
385 52501662 : if (!name) {
386 44827009 : AssertValidPropertyCacheHit(cx, obj, obj2, entry);
387 44827009 : if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, vp))
388 3293 : return false;
389 44823716 : return true;
390 : }
391 :
392 7674653 : jsid id = ATOM_TO_JSID(name);
393 :
394 : JSProperty *prop;
395 7674653 : if (!FindPropertyHelper(cx, name, true, obj, &obj, &obj2, &prop))
396 0 : return false;
397 7674653 : if (!prop) {
398 : /* Kludge to allow (typeof foo == "undefined") tests. */
399 153698 : JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
400 153698 : if (op2 == JSOP_TYPEOF) {
401 152352 : vp->setUndefined();
402 152352 : return true;
403 : }
404 2692 : JSAutoByteString printable;
405 1346 : if (js_AtomToPrintableString(cx, name, &printable))
406 1346 : js_ReportIsNotDefined(cx, printable.ptr());
407 1346 : return false;
408 : }
409 :
410 : /* Take the slow path if prop was not found in a native object. */
411 7520955 : if (!obj->isNative() || !obj2->isNative()) {
412 27 : if (!obj->getGeneric(cx, id, vp))
413 0 : return false;
414 : } else {
415 7520928 : Shape *shape = (Shape *)prop;
416 7520928 : JSObject *normalized = obj;
417 7520928 : if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
418 28 : normalized = &normalized->asWith().object();
419 7520928 : if (!NativeGet(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, vp))
420 34 : return false;
421 : }
422 :
423 7520921 : return true;
424 : }
425 :
426 : inline bool
427 303904 : DefVarOrConstOperation(JSContext *cx, JSObject &varobj, PropertyName *dn, unsigned attrs)
428 : {
429 303904 : JS_ASSERT(varobj.isVarObj());
430 303904 : JS_ASSERT(!varobj.getOps()->defineProperty);
431 :
432 : JSProperty *prop;
433 : JSObject *obj2;
434 303904 : if (!varobj.lookupProperty(cx, dn, &obj2, &prop))
435 0 : return false;
436 :
437 : /* Steps 8c, 8d. */
438 303904 : if (!prop || (obj2 != &varobj && varobj.isGlobal())) {
439 302687 : if (!DefineNativeProperty(cx, &varobj, dn, UndefinedValue(),
440 302687 : JS_PropertyStub, JS_StrictPropertyStub, attrs, 0, 0))
441 : {
442 0 : return false;
443 : }
444 : } else {
445 : /*
446 : * Extension: ordinarily we'd be done here -- but for |const|. If we
447 : * see a redeclaration that's |const|, we consider it a conflict.
448 : */
449 : unsigned oldAttrs;
450 1217 : if (!varobj.getPropertyAttributes(cx, dn, &oldAttrs))
451 0 : return false;
452 1217 : if (attrs & JSPROP_READONLY) {
453 0 : JSAutoByteString bytes;
454 0 : if (js_AtomToPrintableString(cx, dn, &bytes)) {
455 0 : JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
456 : js_GetErrorMessage,
457 : NULL, JSMSG_REDECLARED_VAR,
458 : (oldAttrs & JSPROP_READONLY)
459 : ? "const"
460 : : "var",
461 0 : bytes.ptr()));
462 : }
463 0 : return false;
464 : }
465 : }
466 :
467 303904 : return true;
468 : }
469 :
470 : inline bool
471 : FunctionNeedsPrologue(JSContext *cx, JSFunction *fun)
472 : {
473 : /* Heavyweight functions need call objects created. */
474 : if (fun->isHeavyweight())
475 : return true;
476 :
477 : /* Outer and inner functions need to preserve nesting invariants. */
478 : if (cx->typeInferenceEnabled() && fun->script()->nesting())
479 : return true;
480 :
481 : return false;
482 : }
483 :
484 : inline bool
485 13185783 : ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType)
486 : {
487 13185783 : JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj());
488 :
489 13185783 : if (fp->isConstructing()) {
490 996775 : JSObject *obj = js_CreateThisForFunction(cx, &fp->callee(), newType);
491 996775 : if (!obj)
492 0 : return false;
493 996775 : fp->functionThis().setObject(*obj);
494 : }
495 :
496 13185783 : Probes::enterJSFun(cx, fp->maybeFun(), fp->script());
497 :
498 13185783 : return true;
499 : }
500 :
501 : inline bool
502 15272726 : ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok)
503 : {
504 15272726 : Probes::exitJSFun(cx, fp->maybeFun(), fp->script());
505 :
506 : /*
507 : * If inline-constructing, replace primitive rval with the new object
508 : * passed in via |this|, and instrument this constructor invocation.
509 : */
510 15272726 : if (fp->isConstructing() && ok) {
511 933442 : if (fp->returnValue().isPrimitive())
512 933370 : fp->setReturnValue(ObjectValue(fp->constructorThis()));
513 : }
514 :
515 15272726 : return ok;
516 : }
517 :
518 : inline bool
519 1962019 : ScriptPrologueOrGeneratorResume(JSContext *cx, StackFrame *fp, bool newType)
520 : {
521 1962019 : if (!fp->isGeneratorFrame())
522 1932690 : return ScriptPrologue(cx, fp, newType);
523 29329 : return true;
524 : }
525 :
526 : inline bool
527 1977034 : ScriptEpilogueOrGeneratorYield(JSContext *cx, StackFrame *fp, bool ok)
528 : {
529 1977034 : if (!fp->isYielding())
530 1953903 : return ScriptEpilogue(cx, fp, ok);
531 23131 : return ok;
532 : }
533 :
534 : inline void
535 2607 : InterpreterFrames::enableInterruptsIfRunning(JSScript *script)
536 : {
537 2607 : if (script == regs->fp()->script())
538 593 : enabler.enableInterrupts();
539 2607 : }
540 :
541 : static JS_ALWAYS_INLINE bool
542 59135463 : AddOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
543 : {
544 59135463 : Value lval = lhs;
545 59135463 : Value rval = rhs;
546 :
547 59135463 : if (lval.isInt32() && rval.isInt32()) {
548 47876241 : int32_t l = lval.toInt32(), r = rval.toInt32();
549 47876241 : int32_t sum = l + r;
550 47876241 : if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) {
551 179634 : res->setDouble(double(l) + double(r));
552 179634 : types::TypeScript::MonitorOverflow(cx);
553 : } else {
554 47696607 : res->setInt32(sum);
555 : }
556 : } else
557 : #if JS_HAS_XML_SUPPORT
558 11259222 : if (IsXML(lval) && IsXML(rval)) {
559 0 : if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), res))
560 0 : return false;
561 0 : types::TypeScript::MonitorUnknown(cx);
562 : } else
563 : #endif
564 : {
565 : /*
566 : * If either operand is an object, any non-integer result must be
567 : * reported to inference.
568 : */
569 11259222 : bool lIsObject = lval.isObject(), rIsObject = rval.isObject();
570 :
571 11259222 : if (!ToPrimitive(cx, &lval))
572 11 : return false;
573 11259211 : if (!ToPrimitive(cx, &rval))
574 16 : return false;
575 : bool lIsString, rIsString;
576 11259195 : if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
577 18406032 : js::AutoStringRooter lstr(cx), rstr(cx);
578 6135344 : if (lIsString) {
579 5930345 : lstr.setString(lval.toString());
580 : } else {
581 204999 : lstr.setString(ToString(cx, lval));
582 204999 : if (!lstr.string())
583 0 : return false;
584 : }
585 6135344 : if (rIsString) {
586 5304125 : rstr.setString(rval.toString());
587 : } else {
588 831219 : rstr.setString(ToString(cx, rval));
589 831219 : if (!rstr.string())
590 0 : return false;
591 : }
592 6135344 : JSString *str = js_ConcatStrings(cx, lstr.string(), rstr.string());
593 6135344 : if (!str)
594 13 : return false;
595 6135331 : if (lIsObject || rIsObject)
596 149802 : types::TypeScript::MonitorString(cx);
597 12270675 : res->setString(str);
598 : } else {
599 : double l, r;
600 5123851 : if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))
601 0 : return false;
602 5123851 : l += r;
603 10393197 : if (!res->setNumber(l) &&
604 5269346 : (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) {
605 3238 : types::TypeScript::MonitorOverflow(cx);
606 : }
607 : }
608 : }
609 59135423 : return true;
610 : }
611 :
612 : static JS_ALWAYS_INLINE bool
613 6633500 : SubOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
614 : {
615 : double d1, d2;
616 6633500 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
617 0 : return false;
618 6633500 : double d = d1 - d2;
619 6633500 : if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
620 1114 : types::TypeScript::MonitorOverflow(cx);
621 6633500 : return true;
622 : }
623 :
624 : static JS_ALWAYS_INLINE bool
625 24972227 : MulOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
626 : {
627 : double d1, d2;
628 24972227 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
629 0 : return false;
630 24972227 : double d = d1 * d2;
631 24972227 : if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
632 81361 : types::TypeScript::MonitorOverflow(cx);
633 24972227 : return true;
634 : }
635 :
636 : static JS_ALWAYS_INLINE bool
637 1396383 : DivOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
638 : {
639 : double d1, d2;
640 1396383 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
641 0 : return false;
642 1396383 : res->setNumber(NumberDiv(d1, d2));
643 :
644 1396383 : if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble())))
645 726047 : types::TypeScript::MonitorOverflow(cx);
646 1396383 : return true;
647 : }
648 :
649 : static JS_ALWAYS_INLINE bool
650 461322 : ModOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
651 : {
652 : int32_t l, r;
653 461322 : if (lhs.isInt32() && rhs.isInt32() &&
654 : (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) {
655 451000 : int32_t mod = l % r;
656 451000 : res->setInt32(mod);
657 451000 : return true;
658 : }
659 :
660 : double d1, d2;
661 10322 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
662 0 : return false;
663 :
664 10322 : if (d2 == 0)
665 145 : res->setDouble(js_NaN);
666 : else
667 10177 : res->setDouble(js_fmod(d1, d2));
668 10322 : types::TypeScript::MonitorOverflow(cx);
669 10322 : return true;
670 : }
671 :
672 : static inline bool
673 9119686 : FetchElementId(JSContext *cx, JSObject *obj, const Value &idval, jsid &id, Value *vp)
674 : {
675 : int32_t i_;
676 9119686 : if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
677 8442122 : id = INT_TO_JSID(i_);
678 8442122 : return true;
679 : }
680 677564 : return !!js_InternNonIntElementId(cx, obj, idval, &id, vp);
681 : }
682 :
683 : static JS_ALWAYS_INLINE bool
684 6947632 : ToIdOperation(JSContext *cx, const Value &objval, const Value &idval, Value *res)
685 : {
686 6947632 : if (idval.isInt32()) {
687 6946748 : *res = idval;
688 6946748 : return true;
689 : }
690 :
691 884 : JSObject *obj = ValueToObject(cx, objval);
692 884 : if (!obj)
693 0 : return false;
694 :
695 : jsid dummy;
696 884 : if (!js_InternNonIntElementId(cx, obj, idval, &dummy, res))
697 0 : return false;
698 :
699 884 : if (!res->isInt32())
700 884 : types::TypeScript::MonitorUnknown(cx);
701 884 : return true;
702 : }
703 :
704 : static JS_ALWAYS_INLINE bool
705 44762406 : GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *obj, const Value &rref, Value *res)
706 : {
707 : #if JS_HAS_XML_SUPPORT
708 44762406 : if (op == JSOP_CALLELEM && JS_UNLIKELY(obj->isXML())) {
709 : jsid id;
710 540 : if (!FetchElementId(cx, obj, rref, id, res))
711 0 : return false;
712 540 : return js_GetXMLMethod(cx, obj, id, res);
713 : }
714 : #endif
715 :
716 : uint32_t index;
717 44761866 : if (IsDefinitelyIndex(rref, &index)) {
718 : do {
719 42974892 : if (obj->isDenseArray()) {
720 29106374 : if (index < obj->getDenseArrayInitializedLength()) {
721 29005441 : *res = obj->getDenseArrayElement(index);
722 29005441 : if (!res->isMagic())
723 29002860 : break;
724 : }
725 13868518 : } else if (obj->isArguments()) {
726 50376 : if (obj->asArguments().getElement(index, res))
727 45661 : break;
728 : }
729 13926371 : if (!obj->getElement(cx, index, res))
730 28 : return false;
731 : } while(0);
732 : } else {
733 : JSScript *script;
734 : jsbytecode *pc;
735 1786974 : types::TypeScript::GetPcScript(cx, &script, &pc);
736 :
737 1786974 : if (script->hasAnalysis())
738 1786693 : script->analysis()->getCode(pc).getStringElement = true;
739 :
740 1786974 : SpecialId special;
741 1786974 : *res = rref;
742 1786974 : if (ValueIsSpecial(obj, res, &special, cx)) {
743 0 : if (!obj->getSpecial(cx, obj, special, res))
744 0 : return false;
745 : } else {
746 : JSAtom *name;
747 1786974 : if (!js_ValueToAtom(cx, *res, &name))
748 0 : return false;
749 :
750 1786974 : if (name->isIndex(&index)) {
751 193379 : if (!obj->getElement(cx, index, res))
752 0 : return false;
753 : } else {
754 1593595 : if (!obj->getProperty(cx, name->asPropertyName(), res))
755 55 : return false;
756 : }
757 : }
758 : }
759 :
760 44761783 : assertSameCompartment(cx, *res);
761 44761783 : return true;
762 : }
763 :
764 : static JS_ALWAYS_INLINE bool
765 44834722 : GetElementOperation(JSContext *cx, JSOp op, const Value &lref, const Value &rref, Value *res)
766 : {
767 44834722 : JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
768 :
769 44834722 : if (lref.isString() && rref.isInt32()) {
770 52930 : JSString *str = lref.toString();
771 52930 : int32_t i = rref.toInt32();
772 52930 : if (size_t(i) < str->length()) {
773 52653 : str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
774 52653 : if (!str)
775 0 : return false;
776 52653 : res->setString(str);
777 52653 : return true;
778 : }
779 : }
780 :
781 44782069 : if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
782 19345 : if (rref.isInt32() && size_t(rref.toInt32()) < cx->regs().fp()->numActualArgs()) {
783 19330 : *res = cx->regs().fp()->canonicalActualArg(rref.toInt32());
784 19330 : return true;
785 : }
786 15 : types::MarkArgumentsCreated(cx, cx->fp()->script());
787 15 : JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
788 : }
789 :
790 44762739 : bool isObject = lref.isObject();
791 44762739 : JSObject *obj = ValueToObject(cx, lref);
792 44762739 : if (!obj)
793 333 : return false;
794 44762406 : if (!GetObjectElementOperation(cx, op, obj, rref, res))
795 83 : return false;
796 :
797 : #if JS_HAS_NO_SUCH_METHOD
798 44762323 : if (op == JSOP_CALLELEM && JS_UNLIKELY(res->isPrimitive()) && isObject) {
799 527 : if (!OnUnknownMethod(cx, obj, rref, res))
800 0 : return false;
801 : }
802 : #endif
803 44762323 : return true;
804 : }
805 :
806 : static JS_ALWAYS_INLINE bool
807 20220091 : SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value, bool strict)
808 : {
809 20220091 : types::TypeScript::MonitorAssign(cx, obj, id);
810 :
811 : do {
812 20220091 : if (obj->isDenseArray() && JSID_IS_INT(id)) {
813 12573381 : uint32_t length = obj->getDenseArrayInitializedLength();
814 12573381 : int32_t i = JSID_TO_INT(id);
815 12573381 : if ((uint32_t)i < length) {
816 10390506 : if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
817 15325 : if (js_PrototypeHasIndexedProperties(cx, obj))
818 0 : break;
819 15325 : if ((uint32_t)i >= obj->getArrayLength())
820 0 : obj->setArrayLength(cx, i + 1);
821 : }
822 10390506 : obj->setDenseArrayElementWithType(cx, i, value);
823 10390506 : return true;
824 : } else {
825 : JSScript *script;
826 : jsbytecode *pc;
827 2182875 : types::TypeScript::GetPcScript(cx, &script, &pc);
828 :
829 2182875 : if (script->hasAnalysis())
830 2182875 : script->analysis()->getCode(pc).arrayWriteHole = true;
831 : }
832 : }
833 : } while (0);
834 :
835 9829585 : Value tmp = value;
836 9829585 : return obj->setGeneric(cx, id, &tmp, strict);
837 : }
838 :
839 : #define RELATIONAL_OP(OP) \
840 : JS_BEGIN_MACRO \
841 : Value lval = lhs; \
842 : Value rval = rhs; \
843 : /* Optimize for two int-tagged operands (typical loop control). */ \
844 : if (lval.isInt32() && rval.isInt32()) { \
845 : *res = lval.toInt32() OP rval.toInt32(); \
846 : } else { \
847 : if (!ToPrimitive(cx, JSTYPE_NUMBER, &lval)) \
848 : return false; \
849 : if (!ToPrimitive(cx, JSTYPE_NUMBER, &rval)) \
850 : return false; \
851 : if (lval.isString() && rval.isString()) { \
852 : JSString *l = lval.toString(), *r = rval.toString(); \
853 : int32_t result; \
854 : if (!CompareStrings(cx, l, r, &result)) \
855 : return false; \
856 : *res = result OP 0; \
857 : } else { \
858 : double l, r; \
859 : if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) \
860 : return false;; \
861 : *res = (l OP r); \
862 : } \
863 : } \
864 : return true; \
865 : JS_END_MACRO
866 :
867 : static JS_ALWAYS_INLINE bool
868 157465842 : LessThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
869 157465842 : RELATIONAL_OP(<);
870 : }
871 :
872 : static JS_ALWAYS_INLINE bool
873 1765038 : LessThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
874 1765038 : RELATIONAL_OP(<=);
875 : }
876 :
877 : static JS_ALWAYS_INLINE bool
878 1724099 : GreaterThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
879 1724099 : RELATIONAL_OP(>);
880 : }
881 :
882 : static JS_ALWAYS_INLINE bool
883 6230173 : GreaterThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
884 6230173 : RELATIONAL_OP(>=);
885 : }
886 :
887 : #undef RELATIONAL_OP
888 :
889 : } /* namespace js */
890 :
891 : #endif /* jsinterpinlines_h__ */
|