PageRenderTime 93ms CodeModel.GetById 18ms app.highlight 67ms RepoModel.GetById 1ms app.codeStats 1ms

/js/src/jsinterpinlines.h

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