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 97473 : ComputeImplicitThis(JSContext *cx, JSObject *obj, Value *vp)
91 : {
92 97473 : vp->setUndefined();
93 :
94 97473 : if (obj->isGlobal())
95 97392 : return true;
96 :
97 81 : if (IsCacheableNonGlobalScope(obj))
98 63 : 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 7644062 : ComputeThis(JSContext *cx, StackFrame *fp)
110 : {
111 7644062 : Value &thisv = fp->thisValue();
112 7644062 : if (thisv.isObject())
113 7582825 : return true;
114 61237 : if (fp->isFunctionFrame()) {
115 61237 : if (fp->fun()->inStrictMode())
116 1890 : 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 59347 : JS_ASSERT(!fp->isEvalFrame());
125 : }
126 59347 : 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 77112435 : NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, unsigned getHow, Value *vp)
162 : {
163 77112435 : if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
164 : /* Fast path for Object instance properties. */
165 76256420 : JS_ASSERT(shape->hasSlot());
166 76256420 : *vp = pobj->nativeGetSlot(shape->slot());
167 : } else {
168 856015 : if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))
169 3286 : return false;
170 : }
171 77109149 : 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 76166115 : AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
181 : PropertyCacheEntry *entry)
182 76166115 : {}
183 : #endif
184 :
185 : inline bool
186 2132976 : 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 2132976 : if (op == JSOP_CALLPROP && obj->isXML())
194 81 : return js_GetXMLMethod(cx, obj, id, vp);
195 : #endif
196 :
197 2132895 : return obj->getGeneric(cx, id, vp);
198 : }
199 :
200 : inline bool
201 64801185 : GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp)
202 : {
203 64801185 : JS_ASSERT(vp != &lval);
204 :
205 64801185 : JSOp op = JSOp(*pc);
206 :
207 64801185 : if (op == JSOP_LENGTH) {
208 : /* Optimize length accesses on strings, arrays, and arguments. */
209 42479932 : if (lval.isString()) {
210 216338 : *vp = Int32Value(lval.toString()->length());
211 216338 : return true;
212 : }
213 42263594 : if (lval.isMagic(JS_OPTIMIZED_ARGUMENTS)) {
214 95803 : *vp = Int32Value(cx->fp()->numActualArgs());
215 95803 : return true;
216 : }
217 42167791 : if (lval.isObject()) {
218 42167629 : JSObject *obj = &lval.toObject();
219 42167629 : if (obj->isArray()) {
220 22043988 : uint32_t length = obj->getArrayLength();
221 22043988 : *vp = NumberValue(length);
222 22043988 : return true;
223 : }
224 :
225 20123641 : if (obj->isArguments()) {
226 16226 : ArgumentsObject *argsobj = &obj->asArguments();
227 16226 : if (!argsobj->hasOverriddenLength()) {
228 15750 : uint32_t length = argsobj->initialLength();
229 15750 : JS_ASSERT(length < INT32_MAX);
230 15750 : *vp = Int32Value(int32_t(length));
231 15750 : return true;
232 : }
233 : }
234 :
235 20107891 : if (js_IsTypedArray(obj)) {
236 6983 : JSObject *tarray = TypedArray::getTypedArray(obj);
237 6983 : *vp = Int32Value(TypedArray::getLength(tarray));
238 6983 : return true;
239 : }
240 : }
241 : }
242 :
243 42422323 : JSObject *obj = ValueToObject(cx, lval);
244 42422323 : if (!obj)
245 126 : return false;
246 :
247 : PropertyCacheEntry *entry;
248 : JSObject *obj2;
249 : PropertyName *name;
250 42422197 : JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
251 42422197 : if (!name) {
252 39776767 : AssertValidPropertyCacheHit(cx, obj, obj2, entry);
253 39776767 : if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_CACHE_RESULT, vp))
254 0 : return false;
255 39776767 : return true;
256 : }
257 :
258 2645430 : jsid id = ATOM_TO_JSID(name);
259 :
260 2645430 : if (obj->getOps()->getProperty) {
261 2132976 : if (!GetPropertyGenericMaybeCallXML(cx, op, obj, id, vp))
262 0 : return false;
263 : } else {
264 512454 : if (!GetPropertyHelper(cx, obj, id, JSGET_CACHE_RESULT, vp))
265 108 : return false;
266 : }
267 :
268 : #if JS_HAS_NO_SUCH_METHOD
269 5050607 : if (op == JSOP_CALLPROP &&
270 2404902 : JS_UNLIKELY(vp->isPrimitive()) &&
271 383 : lval.isObject())
272 : {
273 286 : if (!OnUnknownMethod(cx, obj, IdToValue(id), vp))
274 0 : return false;
275 : }
276 : #endif
277 :
278 2645322 : return true;
279 : }
280 :
281 : inline bool
282 16214551 : SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Value &rval)
283 : {
284 16214551 : JSObject *obj = ValueToObject(cx, lval);
285 16214551 : if (!obj)
286 18 : return false;
287 :
288 16214533 : JS_ASSERT_IF(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME, lval.isObject());
289 16214533 : JS_ASSERT_IF(*pc == JSOP_SETGNAME, obj == &cx->fp()->scopeChain().global());
290 :
291 : PropertyCacheEntry *entry;
292 : JSObject *obj2;
293 : PropertyName *name;
294 16214533 : if (JS_PROPERTY_CACHE(cx).testForSet(cx, pc, obj, &entry, &obj2, &name)) {
295 : /*
296 : * Property cache hit, only partially confirmed by testForSet. We
297 : * know that the entry applies to regs.pc and that obj's shape
298 : * matches.
299 : *
300 : * The entry predicts a set either an existing "own" property, or
301 : * on a prototype property that has a setter.
302 : */
303 14771734 : const Shape *shape = entry->prop;
304 14771734 : JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
305 14771734 : JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
306 :
307 14779618 : if (entry->isOwnPropertyHit() ||
308 7884 : ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
309 : #ifdef DEBUG
310 14771729 : if (entry->isOwnPropertyHit()) {
311 14767792 : JS_ASSERT(obj->nativeContains(cx, *shape));
312 : } else {
313 3937 : JS_ASSERT(obj2->nativeContains(cx, *shape));
314 3937 : JS_ASSERT(entry->isPrototypePropertyHit());
315 3937 : JS_ASSERT(entry->kshape != entry->pshape);
316 3937 : JS_ASSERT(!shape->hasSlot());
317 : }
318 : #endif
319 :
320 14771729 : if (shape->hasDefaultSetter() && shape->hasSlot()) {
321 : /* Fast path for, e.g., plain Object instance properties. */
322 14750119 : obj->nativeSetSlotWithType(cx, shape, rval);
323 : } else {
324 21610 : Value rref = rval;
325 21610 : bool strict = cx->stack.currentScript()->strictModeCode;
326 21610 : if (!js_NativeSet(cx, obj, shape, false, strict, &rref))
327 72 : return false;
328 : }
329 14771657 : return true;
330 : }
331 :
332 5 : GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name);
333 : }
334 :
335 1442804 : bool strict = cx->stack.currentScript()->strictModeCode;
336 1442804 : Value rref = rval;
337 :
338 1442804 : JSOp op = JSOp(*pc);
339 :
340 1442804 : jsid id = ATOM_TO_JSID(name);
341 1442804 : if (JS_LIKELY(!obj->getOps()->setProperty)) {
342 : unsigned defineHow = (op == JSOP_SETNAME)
343 : ? DNP_CACHE_RESULT | DNP_UNQUALIFIED
344 1431828 : : DNP_CACHE_RESULT;
345 1431828 : if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref, strict))
346 136 : return false;
347 : } else {
348 10976 : if (!obj->setGeneric(cx, id, &rref, strict))
349 36 : return false;
350 : }
351 :
352 1442632 : return true;
353 : }
354 :
355 : inline bool
356 37337139 : NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
357 : {
358 37337139 : JSObject *obj = cx->stack.currentScriptedScopeChain();
359 :
360 : /*
361 : * Skip along the scope chain to the enclosing global object. This is
362 : * used for GNAME opcodes where the bytecode emitter has determined a
363 : * name access must be on the global. It also insulates us from bugs
364 : * in the emitter: type inference will assume that GNAME opcodes are
365 : * accessing the global object, and the inferred behavior should match
366 : * the actual behavior even if the id could be found on the scope chain
367 : * before the global object.
368 : */
369 37337139 : if (js_CodeSpec[*pc].format & JOF_GNAME)
370 36150086 : obj = &obj->global();
371 :
372 : PropertyCacheEntry *entry;
373 : JSObject *obj2;
374 : PropertyName *name;
375 37337139 : JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
376 37337139 : if (!name) {
377 36389348 : AssertValidPropertyCacheHit(cx, obj, obj2, entry);
378 36389348 : if (!NativeGet(cx, obj, obj2, entry->prop, 0, vp))
379 3259 : return false;
380 36386089 : return true;
381 : }
382 :
383 947791 : jsid id = ATOM_TO_JSID(name);
384 :
385 : JSProperty *prop;
386 947791 : if (!FindPropertyHelper(cx, name, true, obj, &obj, &obj2, &prop))
387 0 : return false;
388 947791 : if (!prop) {
389 : /* Kludge to allow (typeof foo == "undefined") tests. */
390 1444 : JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
391 1444 : if (op2 == JSOP_TYPEOF) {
392 79 : vp->setUndefined();
393 79 : return true;
394 : }
395 2730 : JSAutoByteString printable;
396 1365 : if (js_AtomToPrintableString(cx, name, &printable))
397 1365 : js_ReportIsNotDefined(cx, printable.ptr());
398 1365 : return false;
399 : }
400 :
401 : /* Take the slow path if prop was not found in a native object. */
402 946347 : if (!obj->isNative() || !obj2->isNative()) {
403 27 : if (!obj->getGeneric(cx, id, vp))
404 0 : return false;
405 : } else {
406 946320 : Shape *shape = (Shape *)prop;
407 946320 : JSObject *normalized = obj;
408 946320 : if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
409 28 : normalized = &normalized->asWith().object();
410 946320 : if (!NativeGet(cx, normalized, obj2, shape, 0, vp))
411 27 : return false;
412 : }
413 :
414 946320 : return true;
415 : }
416 :
417 : inline bool
418 58937 : DefVarOrConstOperation(JSContext *cx, JSObject &varobj, PropertyName *dn, unsigned attrs)
419 : {
420 58937 : JS_ASSERT(varobj.isVarObj());
421 58937 : JS_ASSERT(!varobj.getOps()->defineProperty);
422 :
423 : JSProperty *prop;
424 : JSObject *obj2;
425 58937 : if (!varobj.lookupProperty(cx, dn, &obj2, &prop))
426 0 : return false;
427 :
428 : /* Steps 8c, 8d. */
429 58937 : if (!prop || (obj2 != &varobj && varobj.isGlobal())) {
430 57810 : if (!DefineNativeProperty(cx, &varobj, dn, UndefinedValue(),
431 57810 : JS_PropertyStub, JS_StrictPropertyStub, attrs, 0, 0))
432 : {
433 0 : return false;
434 : }
435 : } else {
436 : /*
437 : * Extension: ordinarily we'd be done here -- but for |const|. If we
438 : * see a redeclaration that's |const|, we consider it a conflict.
439 : */
440 : unsigned oldAttrs;
441 1127 : if (!varobj.getPropertyAttributes(cx, dn, &oldAttrs))
442 0 : return false;
443 1127 : if (attrs & JSPROP_READONLY) {
444 0 : JSAutoByteString bytes;
445 0 : if (js_AtomToPrintableString(cx, dn, &bytes)) {
446 0 : JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
447 : js_GetErrorMessage,
448 : NULL, JSMSG_REDECLARED_VAR,
449 : (oldAttrs & JSPROP_READONLY)
450 : ? "const"
451 : : "var",
452 0 : bytes.ptr()));
453 : }
454 0 : return false;
455 : }
456 : }
457 :
458 58937 : return true;
459 : }
460 :
461 : inline bool
462 : FunctionNeedsPrologue(JSContext *cx, JSFunction *fun)
463 : {
464 : /* Heavyweight functions need call objects created. */
465 : if (fun->isHeavyweight())
466 : return true;
467 :
468 : /* Outer and inner functions need to preserve nesting invariants. */
469 : if (cx->typeInferenceEnabled() && fun->script()->nesting())
470 : return true;
471 :
472 : return false;
473 : }
474 :
475 : inline bool
476 9685721 : ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType)
477 : {
478 9685721 : JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj());
479 :
480 9685721 : if (fp->isConstructing()) {
481 872149 : JSObject *obj = js_CreateThisForFunction(cx, &fp->callee(), newType);
482 872149 : if (!obj)
483 0 : return false;
484 872149 : fp->functionThis().setObject(*obj);
485 : }
486 :
487 9685721 : Probes::enterJSFun(cx, fp->maybeFun(), fp->script());
488 :
489 9685721 : return true;
490 : }
491 :
492 : inline bool
493 11773210 : ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok)
494 : {
495 11773210 : Probes::exitJSFun(cx, fp->maybeFun(), fp->script());
496 :
497 : /*
498 : * If inline-constructing, replace primitive rval with the new object
499 : * passed in via |this|, and instrument this constructor invocation.
500 : */
501 11773210 : if (fp->isConstructing() && ok) {
502 808831 : if (fp->returnValue().isPrimitive())
503 808789 : fp->setReturnValue(ObjectValue(fp->constructorThis()));
504 : }
505 :
506 11773210 : return ok;
507 : }
508 :
509 : inline bool
510 502154 : ScriptPrologueOrGeneratorResume(JSContext *cx, StackFrame *fp, bool newType)
511 : {
512 502154 : if (!fp->isGeneratorFrame())
513 484528 : return ScriptPrologue(cx, fp, newType);
514 17626 : return true;
515 : }
516 :
517 : inline bool
518 517069 : ScriptEpilogueOrGeneratorYield(JSContext *cx, StackFrame *fp, bool ok)
519 : {
520 517069 : if (!fp->isYielding())
521 501643 : return ScriptEpilogue(cx, fp, ok);
522 15426 : return ok;
523 : }
524 :
525 : inline void
526 2541 : InterpreterFrames::enableInterruptsIfRunning(JSScript *script)
527 : {
528 2541 : if (script == regs->fp()->script())
529 588 : enabler.enableInterrupts();
530 2541 : }
531 :
532 : static JS_ALWAYS_INLINE bool
533 50410500 : AddOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
534 : {
535 50410500 : Value lval = lhs;
536 50410500 : Value rval = rhs;
537 :
538 50410500 : if (lval.isInt32() && rval.isInt32()) {
539 40807759 : int32_t l = lval.toInt32(), r = rval.toInt32();
540 40807759 : int32_t sum = l + r;
541 40807759 : if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) {
542 179633 : res->setDouble(double(l) + double(r));
543 179633 : types::TypeScript::MonitorOverflow(cx);
544 : } else {
545 40628126 : res->setInt32(sum);
546 : }
547 : } else
548 : #if JS_HAS_XML_SUPPORT
549 9602741 : if (IsXML(lval) && IsXML(rval)) {
550 0 : if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), res))
551 0 : return false;
552 0 : types::TypeScript::MonitorUnknown(cx);
553 : } else
554 : #endif
555 : {
556 : /*
557 : * If either operand is an object, any non-integer result must be
558 : * reported to inference.
559 : */
560 9602741 : bool lIsObject = lval.isObject(), rIsObject = rval.isObject();
561 :
562 9602741 : if (!ToPrimitive(cx, &lval))
563 11 : return false;
564 9602730 : if (!ToPrimitive(cx, &rval))
565 32 : return false;
566 : bool lIsString, rIsString;
567 9602698 : if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
568 13442124 : js::AutoStringRooter lstr(cx), rstr(cx);
569 4480708 : if (lIsString) {
570 4329485 : lstr.setString(lval.toString());
571 : } else {
572 151223 : lstr.setString(ToString(cx, lval));
573 151223 : if (!lstr.string())
574 0 : return false;
575 : }
576 4480708 : if (rIsString) {
577 3887772 : rstr.setString(rval.toString());
578 : } else {
579 592936 : rstr.setString(ToString(cx, rval));
580 592936 : if (!rstr.string())
581 0 : return false;
582 : }
583 4480708 : JSString *str = js_ConcatStrings(cx, lstr.string(), rstr.string());
584 4480708 : if (!str)
585 13 : return false;
586 4480695 : if (lIsObject || rIsObject)
587 118047 : types::TypeScript::MonitorString(cx);
588 8961403 : res->setString(str);
589 : } else {
590 : double l, r;
591 5121990 : if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))
592 0 : return false;
593 5121990 : l += r;
594 10390198 : if (!res->setNumber(l) &&
595 5268208 : (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) {
596 3238 : types::TypeScript::MonitorOverflow(cx);
597 : }
598 : }
599 : }
600 50410444 : return true;
601 : }
602 :
603 : static JS_ALWAYS_INLINE bool
604 6564664 : SubOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
605 : {
606 : double d1, d2;
607 6564664 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
608 0 : return false;
609 6564664 : double d = d1 - d2;
610 6564664 : if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
611 112 : types::TypeScript::MonitorOverflow(cx);
612 6564664 : return true;
613 : }
614 :
615 : static JS_ALWAYS_INLINE bool
616 24958944 : MulOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
617 : {
618 : double d1, d2;
619 24958944 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
620 0 : return false;
621 24958944 : double d = d1 * d2;
622 24958944 : if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
623 80969 : types::TypeScript::MonitorOverflow(cx);
624 24958944 : return true;
625 : }
626 :
627 : static JS_ALWAYS_INLINE bool
628 1388305 : DivOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
629 : {
630 : double d1, d2;
631 1388305 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
632 0 : return false;
633 1388305 : res->setNumber(NumberDiv(d1, d2));
634 :
635 1388305 : if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble())))
636 725596 : types::TypeScript::MonitorOverflow(cx);
637 1388305 : return true;
638 : }
639 :
640 : static JS_ALWAYS_INLINE bool
641 457306 : ModOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
642 : {
643 : int32_t l, r;
644 457306 : if (lhs.isInt32() && rhs.isInt32() &&
645 : (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) {
646 447280 : int32_t mod = l % r;
647 447280 : res->setInt32(mod);
648 447280 : return true;
649 : }
650 :
651 : double d1, d2;
652 10026 : if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
653 0 : return false;
654 :
655 10026 : if (d2 == 0)
656 145 : res->setDouble(js_NaN);
657 : else
658 9881 : res->setDouble(js_fmod(d1, d2));
659 10026 : types::TypeScript::MonitorOverflow(cx);
660 10026 : return true;
661 : }
662 :
663 : static inline bool
664 2229128 : FetchElementId(JSContext *cx, JSObject *obj, const Value &idval, jsid &id, Value *vp)
665 : {
666 : int32_t i_;
667 2229128 : if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
668 1607492 : id = INT_TO_JSID(i_);
669 1607492 : return true;
670 : }
671 621636 : return !!js_InternNonIntElementId(cx, obj, idval, &id, vp);
672 : }
673 :
674 : static JS_ALWAYS_INLINE bool
675 83249 : ToIdOperation(JSContext *cx, const Value &objval, const Value &idval, Value *res)
676 : {
677 83249 : if (idval.isInt32()) {
678 82375 : *res = idval;
679 82375 : return true;
680 : }
681 :
682 874 : JSObject *obj = ValueToObject(cx, objval);
683 874 : if (!obj)
684 0 : return false;
685 :
686 : jsid dummy;
687 874 : if (!js_InternNonIntElementId(cx, obj, idval, &dummy, res))
688 0 : return false;
689 :
690 874 : if (!res->isInt32())
691 874 : types::TypeScript::MonitorUnknown(cx);
692 874 : return true;
693 : }
694 :
695 : static JS_ALWAYS_INLINE bool
696 27729996 : GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *obj, const Value &rref, Value *res)
697 : {
698 : #if JS_HAS_XML_SUPPORT
699 27729996 : if (op == JSOP_CALLELEM && JS_UNLIKELY(obj->isXML())) {
700 : jsid id;
701 540 : if (!FetchElementId(cx, obj, rref, id, res))
702 0 : return false;
703 540 : return js_GetXMLMethod(cx, obj, id, res);
704 : }
705 : #endif
706 :
707 : uint32_t index;
708 27729456 : if (IsDefinitelyIndex(rref, &index)) {
709 : do {
710 26903584 : if (obj->isDenseArray()) {
711 26341581 : if (index < obj->getDenseArrayInitializedLength()) {
712 26245726 : *res = obj->getDenseArrayElement(index);
713 26245726 : if (!res->isMagic())
714 26244530 : break;
715 : }
716 562003 : } else if (obj->isArguments()) {
717 114425 : if (obj->asArguments().getElement(index, res))
718 111854 : break;
719 : }
720 547200 : if (!obj->getElement(cx, index, res))
721 27 : return false;
722 : } while(0);
723 : } else {
724 : JSScript *script;
725 : jsbytecode *pc;
726 825872 : types::TypeScript::GetPcScript(cx, &script, &pc);
727 :
728 825872 : if (script->hasAnalysis())
729 825839 : script->analysis()->getCode(pc).getStringElement = true;
730 :
731 825872 : SpecialId special;
732 825872 : *res = rref;
733 825872 : if (ValueIsSpecial(obj, res, &special, cx)) {
734 0 : if (!obj->getSpecial(cx, obj, special, res))
735 0 : return false;
736 : } else {
737 : JSAtom *name;
738 825872 : if (!js_ValueToAtom(cx, *res, &name))
739 0 : return false;
740 :
741 825872 : if (name->isIndex(&index)) {
742 187646 : if (!obj->getElement(cx, index, res))
743 0 : return false;
744 : } else {
745 638226 : if (!obj->getProperty(cx, name->asPropertyName(), res))
746 0 : return false;
747 : }
748 : }
749 : }
750 :
751 27729429 : assertSameCompartment(cx, *res);
752 27729429 : return true;
753 : }
754 :
755 : static JS_ALWAYS_INLINE bool
756 27828053 : GetElementOperation(JSContext *cx, JSOp op, const Value &lref, const Value &rref, Value *res)
757 : {
758 27828053 : JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
759 :
760 27828053 : if (lref.isString() && rref.isInt32()) {
761 2259 : JSString *str = lref.toString();
762 2259 : int32_t i = rref.toInt32();
763 2259 : if (size_t(i) < str->length()) {
764 2088 : str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
765 2088 : if (!str)
766 0 : return false;
767 2088 : res->setString(str);
768 2088 : return true;
769 : }
770 : }
771 :
772 27825965 : if (lref.isMagic(JS_OPTIMIZED_ARGUMENTS))
773 95906 : return NormalArgumentsObject::optimizedGetElem(cx, cx->fp(), rref, res);
774 :
775 27730059 : bool isObject = lref.isObject();
776 27730059 : JSObject *obj = ValueToObject(cx, lref);
777 27730059 : if (!obj)
778 63 : return false;
779 27729996 : if (!GetObjectElementOperation(cx, op, obj, rref, res))
780 27 : return false;
781 :
782 : #if JS_HAS_NO_SUCH_METHOD
783 27729969 : if (op == JSOP_CALLELEM && JS_UNLIKELY(res->isPrimitive()) && isObject) {
784 527 : if (!OnUnknownMethod(cx, obj, rref, res))
785 0 : return false;
786 : }
787 : #endif
788 27729969 : return true;
789 : }
790 :
791 : static JS_ALWAYS_INLINE bool
792 11318786 : SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value, bool strict)
793 : {
794 11318786 : types::TypeScript::MonitorAssign(cx, obj, id);
795 :
796 : do {
797 11318786 : if (obj->isDenseArray() && JSID_IS_INT(id)) {
798 11061995 : uint32_t length = obj->getDenseArrayInitializedLength();
799 11061995 : int32_t i = JSID_TO_INT(id);
800 11061995 : if ((uint32_t)i < length) {
801 10386841 : if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
802 14202 : if (js_PrototypeHasIndexedProperties(cx, obj))
803 0 : break;
804 14202 : if ((uint32_t)i >= obj->getArrayLength())
805 0 : obj->setArrayLength(cx, i + 1);
806 : }
807 10386841 : obj->setDenseArrayElementWithType(cx, i, value);
808 10386841 : return true;
809 : } else {
810 : JSScript *script;
811 : jsbytecode *pc;
812 675154 : types::TypeScript::GetPcScript(cx, &script, &pc);
813 :
814 675154 : if (script->hasAnalysis())
815 675154 : script->analysis()->getCode(pc).arrayWriteHole = true;
816 : }
817 : }
818 : } while (0);
819 :
820 931945 : Value tmp = value;
821 931945 : return obj->setGeneric(cx, id, &tmp, strict);
822 : }
823 :
824 : #define RELATIONAL_OP(OP) \
825 : JS_BEGIN_MACRO \
826 : Value lval = lhs; \
827 : Value rval = rhs; \
828 : /* Optimize for two int-tagged operands (typical loop control). */ \
829 : if (lval.isInt32() && rval.isInt32()) { \
830 : *res = lval.toInt32() OP rval.toInt32(); \
831 : } else { \
832 : if (!ToPrimitive(cx, JSTYPE_NUMBER, &lval)) \
833 : return false; \
834 : if (!ToPrimitive(cx, JSTYPE_NUMBER, &rval)) \
835 : return false; \
836 : if (lval.isString() && rval.isString()) { \
837 : JSString *l = lval.toString(), *r = rval.toString(); \
838 : int32_t result; \
839 : if (!CompareStrings(cx, l, r, &result)) \
840 : return false; \
841 : *res = result OP 0; \
842 : } else { \
843 : double l, r; \
844 : if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) \
845 : return false;; \
846 : *res = (l OP r); \
847 : } \
848 : } \
849 : return true; \
850 : JS_END_MACRO
851 :
852 : static JS_ALWAYS_INLINE bool
853 156837398 : LessThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
854 156837398 : RELATIONAL_OP(<);
855 : }
856 :
857 : static JS_ALWAYS_INLINE bool
858 1705631 : LessThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
859 1705631 : RELATIONAL_OP(<=);
860 : }
861 :
862 : static JS_ALWAYS_INLINE bool
863 1539957 : GreaterThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
864 1539957 : RELATIONAL_OP(>);
865 : }
866 :
867 : static JS_ALWAYS_INLINE bool
868 6143501 : GreaterThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
869 6143501 : RELATIONAL_OP(>=);
870 : }
871 :
872 : #undef RELATIONAL_OP
873 :
874 : } /* namespace js */
875 :
876 : #endif /* jsinterpinlines_h__ */
|