PageRenderTime 171ms CodeModel.GetById 7ms app.highlight 145ms RepoModel.GetById 1ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsinterp.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2138 lines | 1569 code | 225 blank | 344 comment | 373 complexity | 00b5af12134531fe470209df5d04e58b MD5 | raw file
   1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   2 * vim: set ts=8 sw=4 et tw=79:
   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 Mozilla Communicator client code, released
  18 * March 31, 1998.
  19 *
  20 * The Initial Developer of the Original Code is
  21 * Netscape Communications Corporation.
  22 * Portions created by the Initial Developer are Copyright (C) 1998
  23 * the Initial Developer. All Rights Reserved.
  24 *
  25 * Contributor(s):
  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/*
  42 * JavaScript bytecode interpreter.
  43 */
  44#include "jsstddef.h"
  45#include <stdio.h>
  46#include <string.h>
  47#include <math.h>
  48#include "jstypes.h"
  49#include "jsarena.h" /* Added by JSIFY */
  50#include "jsutil.h" /* Added by JSIFY */
  51#include "jsprf.h"
  52#include "jsapi.h"
  53#include "jsarray.h"
  54#include "jsatom.h"
  55#include "jsbool.h"
  56#include "jscntxt.h"
  57#include "jsversion.h"
  58#include "jsdbgapi.h"
  59#include "jsfun.h"
  60#include "jsgc.h"
  61#include "jsinterp.h"
  62#include "jsiter.h"
  63#include "jslock.h"
  64#include "jsnum.h"
  65#include "jsobj.h"
  66#include "jsopcode.h"
  67#include "jsscan.h"
  68#include "jsscope.h"
  69#include "jsscript.h"
  70#include "jsstr.h"
  71#include "jsstaticcheck.h"
  72#include "jstracer.h"
  73
  74#ifdef INCLUDE_MOZILLA_DTRACE
  75#include "jsdtracef.h"
  76#endif
  77
  78#if JS_HAS_XML_SUPPORT
  79#include "jsxml.h"
  80#endif
  81
  82#include "jsautooplen.h"
  83
  84/* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
  85#if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
  86
  87uint32
  88js_GenerateShape(JSContext *cx, JSBool gcLocked, JSScopeProperty *sprop)
  89{
  90    JSRuntime *rt;
  91    uint32 shape;
  92    JSTempValueRooter tvr;
  93
  94    rt = cx->runtime;
  95    shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
  96    JS_ASSERT(shape != 0);
  97    if (shape & SHAPE_OVERFLOW_BIT) {
  98        rt->gcPoke = JS_TRUE;
  99        if (sprop)
 100            JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
 101        js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);
 102        if (sprop)
 103            JS_POP_TEMP_ROOT(cx, &tvr);
 104        shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
 105        JS_ASSERT(shape != 0);
 106        JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT,
 107                     JS_PROPERTY_CACHE(cx).disabled);
 108    }
 109    return shape;
 110}
 111
 112void
 113js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
 114                     uintN scopeIndex, uintN protoIndex,
 115                     JSObject *pobj, JSScopeProperty *sprop,
 116                     JSPropCacheEntry **entryp)
 117{
 118    JSPropertyCache *cache;
 119    jsbytecode *pc;
 120    JSScope *scope;
 121    JSOp op;
 122    const JSCodeSpec *cs;
 123    jsuword vword;
 124    ptrdiff_t pcoff;
 125    jsuword khash;
 126    JSAtom *atom;
 127    JSPropCacheEntry *entry;
 128
 129    JS_ASSERT(!cx->runtime->gcRunning);
 130    cache = &JS_PROPERTY_CACHE(cx);
 131    pc = cx->fp->regs->pc;
 132    if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {
 133        PCMETER(cache->disfills++);
 134        *entryp = NULL;
 135        return;
 136    }
 137
 138    /*
 139     * Check for fill from js_SetPropertyHelper where the setter removed sprop
 140     * from pobj's scope (via unwatch or delete, e.g.).
 141     */
 142    scope = OBJ_SCOPE(pobj);
 143    JS_ASSERT(scope->object == pobj);
 144    if (!SCOPE_HAS_PROPERTY(scope, sprop)) {
 145        PCMETER(cache->oddfills++);
 146        *entryp = NULL;
 147        return;
 148    }
 149
 150    /*
 151     * Check for overdeep scope and prototype chain. Because resolve, getter,
 152     * and setter hooks can change the prototype chain using JS_SetPrototype
 153     * after js_LookupPropertyWithFlags has returned the nominal protoIndex,
 154     * we have to validate protoIndex if it is non-zero. If it is zero, then
 155     * we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact
 156     * that obj == pobj, that protoIndex is invariant.
 157     *
 158     * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
 159     * before any running script might consult a parent-linked scope chain. If
 160     * this requirement is not satisfied, the fill in progress will never hit,
 161     * but vcap vs. scope shape tests ensure nothing malfunctions.
 162     */
 163    JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
 164    if (protoIndex != 0) {
 165        JSObject *tmp;
 166
 167        JS_ASSERT(pobj != obj);
 168        protoIndex = 1;
 169        tmp = obj;
 170        for (;;) {
 171            tmp = OBJ_GET_PROTO(cx, tmp);
 172            if (!tmp) {
 173                PCMETER(cache->noprotos++);
 174                *entryp = NULL;
 175                return;
 176            }
 177            if (tmp == pobj)
 178                break;
 179            ++protoIndex;
 180        }
 181    }
 182    if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
 183        PCMETER(cache->longchains++);
 184        *entryp = NULL;
 185        return;
 186    }
 187
 188    /*
 189     * Optimize the cached vword based on our parameters and the current pc's
 190     * opcode format flags.
 191     */
 192    op = (JSOp) *pc;
 193    cs = &js_CodeSpec[op];
 194
 195    do {
 196        /*
 197         * Check for a prototype "plain old method" callee computation. What
 198         * is a plain old method? It's a function-valued property with stub
 199         * getter and setter, so get of a function is idempotent and set is
 200         * transparent.
 201         */
 202        if (cs->format & JOF_CALLOP) {
 203            if (SPROP_HAS_STUB_GETTER(sprop) &&
 204                SPROP_HAS_VALID_SLOT(sprop, scope)) {
 205                jsval v;
 206
 207                v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
 208                if (VALUE_IS_FUNCTION(cx, v)) {
 209                    /*
 210                     * Great, we have a function-valued prototype property
 211                     * where the getter is JS_PropertyStub. The type id in
 212                     * pobj's scope does not evolve with changes to property
 213                     * values, however.
 214                     *
 215                     * So here, on first cache fill for this method, we brand
 216                     * the scope with a new shape and set the SCOPE_BRANDED
 217                     * flag.  Once this scope flag is set, any write that adds
 218                     * or deletes a function-valued plain old property in
 219                     * scope->object will result in shape being regenerated.
 220                     */
 221                    if (!SCOPE_IS_BRANDED(scope)) {
 222                        PCMETER(cache->brandfills++);
 223#ifdef DEBUG_notme
 224                        fprintf(stderr,
 225                            "branding %p (%s) for funobj %p (%s), kshape %lu\n",
 226                            pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
 227                            JSVAL_TO_OBJECT(v),
 228                            JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
 229                                                 JSVAL_TO_OBJECT(v))),
 230                            kshape);
 231#endif
 232                        SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
 233                        SCOPE_SET_BRANDED(scope);
 234                        kshape = scope->shape;
 235                    }
 236                    vword = JSVAL_OBJECT_TO_PCVAL(v);
 237                    break;
 238                }
 239            }
 240        }
 241
 242        /* If getting a value via a stub getter, we can cache the slot. */
 243        if (!(cs->format & JOF_SET) &&
 244            SPROP_HAS_STUB_GETTER(sprop) &&
 245            SPROP_HAS_VALID_SLOT(sprop, scope)) {
 246            /* Great, let's cache sprop's slot and use it on cache hit. */
 247            vword = SLOT_TO_PCVAL(sprop->slot);
 248        } else {
 249            /* Best we can do is to cache sprop (still a nice speedup). */
 250            vword = SPROP_TO_PCVAL(sprop);
 251        }
 252    } while (0);
 253
 254    /*
 255     * Our caller preserved the scope shape prior to the js_GetPropertyHelper
 256     * or similar call out of the interpreter. We want to cache under that
 257     * shape if op is overtly mutating, to bias for the case where the mutator
 258     * udpates shape predictably.
 259     *
 260     * Note that an apparently non-mutating op such as JSOP_NAME may still
 261     * mutate the base object via, e.g., lazy standard class initialization,
 262     * but that is a one-time event and we'll have to miss the old shape and
 263     * re-fill under the new one.
 264     */
 265    if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj)
 266        kshape = scope->shape;
 267
 268    khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
 269    if (obj == pobj) {
 270        JS_ASSERT(kshape != 0 || scope->shape != 0);
 271        JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
 272        JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
 273    } else {
 274        if (op == JSOP_LENGTH) {
 275            atom = cx->runtime->atomState.lengthAtom;
 276        } else {
 277            pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
 278            GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
 279        }
 280        JS_ASSERT_IF(scopeIndex == 0,
 281                     protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj);
 282        if (scopeIndex != 0 || protoIndex != 1) {
 283            khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);
 284            PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
 285                        cache->pcrecycles++);
 286            pc = (jsbytecode *) atom;
 287            kshape = (jsuword) obj;
 288        }
 289    }
 290
 291    entry = &cache->table[khash];
 292    PCMETER(if (entry != *entryp) cache->modfills++);
 293    PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++);
 294    entry->kpc = pc;
 295    entry->kshape = kshape;
 296    entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex);
 297    entry->vword = vword;
 298    *entryp = entry;
 299
 300    cache->empty = JS_FALSE;
 301    PCMETER(cache->fills++);
 302}
 303
 304JSAtom *
 305js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
 306                         JSObject **objp, JSObject **pobjp,
 307                         JSPropCacheEntry **entryp)
 308{
 309    JSOp op;
 310    const JSCodeSpec *cs;
 311    ptrdiff_t pcoff;
 312    JSAtom *atom;
 313    JSObject *obj, *pobj, *tmp;
 314    JSPropCacheEntry *entry;
 315    uint32 vcap;
 316
 317    JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
 318              < cx->fp->script->length);
 319
 320    op = (JSOp) *pc;
 321    cs = &js_CodeSpec[op];
 322    if (op == JSOP_LENGTH) {
 323        atom = cx->runtime->atomState.lengthAtom;
 324    } else {
 325        pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
 326        GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
 327    }
 328
 329    obj = *objp;
 330    JS_ASSERT(OBJ_IS_NATIVE(obj));
 331    entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];
 332    *entryp = entry;
 333    vcap = entry->vcap;
 334
 335    if (entry->kpc != (jsbytecode *) atom) {
 336        PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
 337
 338#ifdef DEBUG_notme
 339        entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
 340        fprintf(stderr,
 341                "id miss for %s from %s:%u"
 342                " (pc %u, kpc %u, kshape %u, shape %u)\n",
 343                js_AtomToPrintableString(cx, atom),
 344                cx->fp->script->filename,
 345                js_PCToLineNumber(cx, cx->fp->script, pc),
 346                pc - cx->fp->script->code,
 347                entry->kpc - cx->fp->script->code,
 348                entry->kshape,
 349                OBJ_SHAPE(obj));
 350                js_Disassemble1(cx, cx->fp->script, pc,
 351                                PTRDIFF(pc, cx->fp->script->code, jsbytecode),
 352                                JS_FALSE, stderr);
 353#endif
 354
 355        return atom;
 356    }
 357
 358    if (entry->kshape != (jsuword) obj) {
 359        PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
 360        return atom;
 361    }
 362
 363    pobj = obj;
 364    JS_LOCK_OBJ(cx, pobj);
 365
 366    if (JOF_MODE(cs->format) == JOF_NAME) {
 367        while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
 368            tmp = LOCKED_OBJ_GET_PARENT(pobj);
 369            if (!tmp || !OBJ_IS_NATIVE(tmp))
 370                break;
 371            JS_UNLOCK_OBJ(cx, pobj);
 372            pobj = tmp;
 373            JS_LOCK_OBJ(cx, pobj);
 374            vcap -= PCVCAP_PROTOSIZE;
 375        }
 376
 377        *objp = pobj;
 378    }
 379
 380    while (vcap & PCVCAP_PROTOMASK) {
 381        tmp = LOCKED_OBJ_GET_PROTO(pobj);
 382        if (!tmp || !OBJ_IS_NATIVE(tmp))
 383            break;
 384        JS_UNLOCK_OBJ(cx, pobj);
 385        pobj = tmp;
 386        JS_LOCK_OBJ(cx, pobj);
 387        --vcap;
 388    }
 389
 390    if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) {
 391#ifdef DEBUG
 392        jsid id = ATOM_TO_JSID(atom);
 393
 394        CHECK_FOR_STRING_INDEX(id);
 395        JS_ASSERT(SCOPE_GET_PROPERTY(OBJ_SCOPE(pobj), id));
 396        JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);
 397#endif
 398        *pobjp = pobj;
 399        return NULL;
 400    }
 401
 402    PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
 403    JS_UNLOCK_OBJ(cx, pobj);
 404    return atom;
 405}
 406
 407#ifdef DEBUG
 408#define ASSERT_CACHE_IS_EMPTY(cache)                                          \
 409    JS_BEGIN_MACRO                                                            \
 410        JSPropertyCache *cache_ = (cache);                                    \
 411        uintN i_;                                                             \
 412        JS_ASSERT(cache_->empty);                                             \
 413        for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) {                        \
 414            JS_ASSERT(!cache_->table[i_].kpc);                                \
 415            JS_ASSERT(!cache_->table[i_].kshape);                             \
 416            JS_ASSERT(!cache_->table[i_].vcap);                               \
 417            JS_ASSERT(!cache_->table[i_].vword);                              \
 418        }                                                                     \
 419    JS_END_MACRO
 420#else
 421#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
 422#endif
 423
 424JS_STATIC_ASSERT(PCVAL_NULL == 0);
 425
 426void
 427js_FlushPropertyCache(JSContext *cx)
 428{
 429    JSPropertyCache *cache;
 430
 431    cache = &JS_PROPERTY_CACHE(cx);
 432    if (cache->empty) {
 433        ASSERT_CACHE_IS_EMPTY(cache);
 434        return;
 435    }
 436
 437    memset(cache->table, 0, sizeof cache->table);
 438    cache->empty = JS_TRUE;
 439
 440#ifdef JS_PROPERTY_CACHE_METERING
 441  { static FILE *fp;
 442    if (!fp)
 443        fp = fopen("/tmp/propcache.stats", "w");
 444    if (fp) {
 445        fputs("Property cache stats for ", fp);
 446#ifdef JS_THREADSAFE
 447        fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
 448#endif
 449        fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
 450
 451# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
 452        P(fills);
 453        P(nofills);
 454        P(rofills);
 455        P(disfills);
 456        P(oddfills);
 457        P(modfills);
 458        P(brandfills);
 459        P(noprotos);
 460        P(longchains);
 461        P(recycles);
 462        P(pcrecycles);
 463        P(tests);
 464        P(pchits);
 465        P(protopchits);
 466        P(initests);
 467        P(inipchits);
 468        P(inipcmisses);
 469        P(settests);
 470        P(addpchits);
 471        P(setpchits);
 472        P(setpcmisses);
 473        P(slotchanges);
 474        P(setmisses);
 475        P(idmisses);
 476        P(komisses);
 477        P(vcmisses);
 478        P(misses);
 479        P(flushes);
 480        P(pcpurges);
 481# undef P
 482
 483        fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
 484                (100. * cache->pchits) / cache->tests,
 485                (100. * cache->protopchits) / cache->tests,
 486                (100. * (cache->addpchits + cache->setpchits))
 487                / cache->settests,
 488                (100. * cache->inipchits) / cache->initests,
 489                (100. * (cache->tests - cache->misses)) / cache->tests);
 490        fflush(fp);
 491    }
 492  }
 493#endif
 494
 495    PCMETER(cache->flushes++);
 496}
 497
 498void
 499js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)
 500{
 501    JSPropertyCache *cache;
 502    JSPropCacheEntry *entry;
 503
 504    cache = &JS_PROPERTY_CACHE(cx);
 505    for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
 506         entry++) {
 507        if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
 508            entry->kpc = NULL;
 509            entry->kshape = 0;
 510#ifdef DEBUG
 511            entry->vcap = entry->vword = 0;
 512#endif
 513        }
 514    }
 515}
 516
 517void
 518js_DisablePropertyCache(JSContext *cx)
 519{
 520    JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
 521    ++JS_PROPERTY_CACHE(cx).disabled;
 522}
 523
 524void
 525js_EnablePropertyCache(JSContext *cx)
 526{
 527    --JS_PROPERTY_CACHE(cx).disabled;
 528    JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
 529}
 530
 531/*
 532 * Check if the current arena has enough space to fit nslots after sp and, if
 533 * so, reserve the necessary space.
 534 */
 535static JSBool
 536AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
 537{
 538    uintN surplus;
 539    jsval *sp2;
 540
 541    JS_ASSERT((jsval *) cx->stackPool.current->base <= sp);
 542    JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail);
 543    surplus = (jsval *) cx->stackPool.current->avail - sp;
 544    if (nslots <= surplus)
 545        return JS_TRUE;
 546
 547    /*
 548     * No room before current->avail, check if the arena has enough space to
 549     * fit the missing slots before the limit.
 550     */
 551    if (nslots > (size_t) ((jsval *) cx->stackPool.current->limit - sp))
 552        return JS_FALSE;
 553
 554    JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool,
 555                           (nslots - surplus) * sizeof(jsval));
 556    JS_ASSERT(sp2 == sp + surplus);
 557    return JS_TRUE;
 558}
 559
 560JS_STATIC_INTERPRET jsval *
 561js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
 562{
 563    jsval *sp;
 564
 565    if (!cx->stackPool.first.next) {
 566        int64 *timestamp;
 567
 568        JS_ARENA_ALLOCATE_CAST(timestamp, int64 *,
 569                               &cx->stackPool, sizeof *timestamp);
 570        if (!timestamp) {
 571            js_ReportOutOfScriptQuota(cx);
 572            return NULL;
 573        }
 574        *timestamp = JS_Now();
 575    }
 576
 577    if (markp)
 578        *markp = JS_ARENA_MARK(&cx->stackPool);
 579    JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
 580    if (!sp)
 581        js_ReportOutOfScriptQuota(cx);
 582    return sp;
 583}
 584
 585JS_STATIC_INTERPRET void
 586js_FreeRawStack(JSContext *cx, void *mark)
 587{
 588    JS_ARENA_RELEASE(&cx->stackPool, mark);
 589}
 590
 591JS_FRIEND_API(jsval *)
 592js_AllocStack(JSContext *cx, uintN nslots, void **markp)
 593{
 594    jsval *sp;
 595    JSArena *a;
 596    JSStackHeader *sh;
 597
 598    /* Callers don't check for zero nslots: we do to avoid empty segments. */
 599    if (nslots == 0) {
 600        *markp = NULL;
 601        return (jsval *) JS_ARENA_MARK(&cx->stackPool);
 602    }
 603
 604    /* Allocate 2 extra slots for the stack segment header we'll likely need. */
 605    sp = js_AllocRawStack(cx, 2 + nslots, markp);
 606    if (!sp)
 607        return NULL;
 608
 609    /* Try to avoid another header if we can piggyback on the last segment. */
 610    a = cx->stackPool.current;
 611    sh = cx->stackHeaders;
 612    if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
 613        /* Extend the last stack segment, give back the 2 header slots. */
 614        sh->nslots += nslots;
 615        a->avail -= 2 * sizeof(jsval);
 616    } else {
 617        /*
 618         * Need a new stack segment, so allocate and push a stack segment
 619         * header from the 2 extra slots.
 620         */
 621        sh = (JSStackHeader *)sp;
 622        sh->nslots = nslots;
 623        sh->down = cx->stackHeaders;
 624        cx->stackHeaders = sh;
 625        sp += 2;
 626    }
 627
 628    /*
 629     * Store JSVAL_NULL using memset, to let compilers optimize as they see
 630     * fit, in case a caller allocates and pushes GC-things one by one, which
 631     * could nest a last-ditch GC that will scan this segment.
 632     */
 633    memset(sp, 0, nslots * sizeof(jsval));
 634    return sp;
 635}
 636
 637JS_FRIEND_API(void)
 638js_FreeStack(JSContext *cx, void *mark)
 639{
 640    JSStackHeader *sh;
 641    jsuword slotdiff;
 642
 643    /* Check for zero nslots allocation special case. */
 644    if (!mark)
 645        return;
 646
 647    /* We can assert because js_FreeStack always balances js_AllocStack. */
 648    sh = cx->stackHeaders;
 649    JS_ASSERT(sh);
 650
 651    /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
 652    slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
 653    if (slotdiff < (jsuword)sh->nslots)
 654        sh->nslots = slotdiff;
 655    else
 656        cx->stackHeaders = sh->down;
 657
 658    /* Release the stackPool space allocated since mark was set. */
 659    JS_ARENA_RELEASE(&cx->stackPool, mark);
 660}
 661
 662JSObject *
 663js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
 664{
 665    JSObject *obj, *cursor, *clonedChild, *parent;
 666    JSTempValueRooter tvr;
 667
 668    obj = fp->blockChain;
 669    if (!obj) {
 670        /*
 671         * Don't force a call object for a lightweight function call, but do
 672         * insist that there is a call object for a heavyweight function call.
 673         */
 674        JS_ASSERT(!fp->fun ||
 675                  !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
 676                  fp->callobj);
 677        JS_ASSERT(fp->scopeChain);
 678        return fp->scopeChain;
 679    }
 680
 681    /*
 682     * We have one or more lexical scopes to reflect into fp->scopeChain, so
 683     * make sure there's a call object at the current head of the scope chain,
 684     * if this frame is a call frame.
 685     */
 686    if (fp->fun && !fp->callobj) {
 687        JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
 688                  OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
 689        if (!js_GetCallObject(cx, fp, fp->scopeChain))
 690            return NULL;
 691    }
 692
 693    /*
 694     * Clone the block chain. To avoid recursive cloning we set the parent of
 695     * the cloned child after we clone the parent. In the following loop when
 696     * clonedChild is null it indicates the first iteration when no special GC
 697     * rooting is necessary. On the second and the following iterations we
 698     * have to protect cloned so far chain against the GC during cloning of
 699     * the cursor object.
 700     */
 701    cursor = obj;
 702    clonedChild = NULL;
 703    for (;;) {
 704        parent = OBJ_GET_PARENT(cx, cursor);
 705
 706        /*
 707         * We pass fp->scopeChain and not null even if we override the parent
 708         * slot later as null triggers useless calculations of slot's value in
 709         * js_NewObject that js_CloneBlockObject calls.
 710         */
 711        cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
 712        if (!cursor) {
 713            if (clonedChild)
 714                JS_POP_TEMP_ROOT(cx, &tvr);
 715            return NULL;
 716        }
 717        if (!clonedChild) {
 718            /*
 719             * The first iteration. Check if other follow and root obj if so
 720             * to protect the whole cloned chain against GC.
 721             */
 722            obj = cursor;
 723            if (!parent)
 724                break;
 725            JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
 726        } else {
 727            /*
 728             * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
 729             * other threads.
 730             */
 731            STOBJ_SET_PARENT(clonedChild, cursor);
 732            if (!parent) {
 733                JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
 734                JS_POP_TEMP_ROOT(cx, &tvr);
 735                break;
 736            }
 737        }
 738        clonedChild = cursor;
 739        cursor = parent;
 740    }
 741    fp->flags |= JSFRAME_POP_BLOCKS;
 742    fp->scopeChain = obj;
 743    fp->blockChain = NULL;
 744    return obj;
 745}
 746
 747JSBool
 748js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
 749{
 750    jsval v;
 751    JSObject *obj;
 752
 753    v = vp[1];
 754    if (JSVAL_IS_OBJECT(v)) {
 755        obj = JS_THIS_OBJECT(cx, vp);
 756        if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
 757            return JS_FALSE;
 758        v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
 759    }
 760    *thisvp = v;
 761    return JS_TRUE;
 762}
 763
 764/*
 765 * ECMA requires "the global object", but in embeddings such as the browser,
 766 * which have multiple top-level objects (windows, frames, etc. in the DOM),
 767 * we prefer fun's parent.  An example that causes this code to run:
 768 *
 769 *   // in window w1
 770 *   function f() { return this }
 771 *   function g() { return f }
 772 *
 773 *   // in window w2
 774 *   var h = w1.g()
 775 *   alert(h() == w1)
 776 *
 777 * The alert should display "true".
 778 */
 779JS_STATIC_INTERPRET JSObject *
 780js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
 781{
 782    JSObject *thisp;
 783
 784    if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
 785        !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
 786        thisp = cx->globalObject;
 787    } else {
 788        JSStackFrame *fp;
 789        jsid id;
 790        jsval v;
 791        uintN attrs;
 792        JSBool ok;
 793        JSObject *parent;
 794
 795        /*
 796         * Walk up the parent chain, first checking that the running script
 797         * has access to the callee's parent object. Note that if lazy, the
 798         * running script whose principals we want to check is the script
 799         * associated with fp->down, not with fp.
 800         *
 801         * FIXME: 417851 -- this access check should not be required, as it
 802         * imposes a performance penalty on all js_ComputeGlobalThis calls,
 803         * and it represents a maintenance hazard.
 804         */
 805        fp = cx->fp;    /* quell GCC overwarning */
 806        if (lazy) {
 807            JS_ASSERT(fp->argv == argv);
 808            fp->dormantNext = cx->dormantFrameChain;
 809            cx->dormantFrameChain = fp;
 810            cx->fp = fp->down;
 811            fp->down = NULL;
 812        }
 813        thisp = JSVAL_TO_OBJECT(argv[-2]);
 814        id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
 815
 816        ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs);
 817        if (lazy) {
 818            cx->dormantFrameChain = fp->dormantNext;
 819            fp->dormantNext = NULL;
 820            fp->down = cx->fp;
 821            cx->fp = fp;
 822        }
 823        if (!ok)
 824            return NULL;
 825
 826        thisp = JSVAL_IS_VOID(v)
 827                ? OBJ_GET_PARENT(cx, thisp)
 828                : JSVAL_TO_OBJECT(v);
 829        while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
 830            thisp = parent;
 831    }
 832
 833    OBJ_TO_OUTER_OBJECT(cx, thisp);
 834    if (!thisp)
 835        return NULL;
 836    argv[-1] = OBJECT_TO_JSVAL(thisp);
 837    return thisp;
 838}
 839
 840static JSObject *
 841ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
 842{
 843    JSObject *thisp;
 844
 845    JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
 846    if (!JSVAL_IS_OBJECT(argv[-1])) {
 847        if (!js_PrimitiveToObject(cx, &argv[-1]))
 848            return NULL;
 849        thisp = JSVAL_TO_OBJECT(argv[-1]);
 850    } else {
 851        thisp = JSVAL_TO_OBJECT(argv[-1]);
 852        if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)
 853            return js_ComputeGlobalThis(cx, lazy, argv);
 854
 855        if (thisp->map->ops->thisObject) {
 856            /* Some objects (e.g., With) delegate 'this' to another object. */
 857            thisp = thisp->map->ops->thisObject(cx, thisp);
 858            if (!thisp)
 859                return NULL;
 860        }
 861        OBJ_TO_OUTER_OBJECT(cx, thisp);
 862        if (!thisp)
 863            return NULL;
 864        argv[-1] = OBJECT_TO_JSVAL(thisp);
 865    }
 866    return thisp;
 867}
 868
 869JSObject *
 870js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
 871{
 872    if (JSVAL_IS_NULL(argv[-1]))
 873        return js_ComputeGlobalThis(cx, lazy, argv);
 874    return ComputeThis(cx, lazy, argv);
 875}
 876
 877#if JS_HAS_NO_SUCH_METHOD
 878
 879#define JSSLOT_FOUND_FUNCTION   JSSLOT_PRIVATE
 880#define JSSLOT_SAVED_ID         (JSSLOT_PRIVATE + 1)
 881
 882JSClass js_NoSuchMethodClass = {
 883    "NoSuchMethod",
 884    JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |
 885    JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),
 886    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,   JS_PropertyStub,
 887    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,
 888    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 889};
 890
 891JS_BEGIN_EXTERN_C
 892
 893JSObject*
 894js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj);
 895
 896JS_END_EXTERN_C
 897
 898JSObject*
 899js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)
 900{
 901    JSObject *proto;
 902
 903    proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,
 904                         NULL, NULL, NULL);
 905    if (!proto)
 906        return NULL;
 907
 908    OBJ_CLEAR_PROTO(cx, proto);
 909    return proto;
 910}
 911
 912/*
 913 * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
 914 * the base object, we search for the __noSuchMethod__ method in the base.
 915 * If it exists, we store the method and the property's id into an object of
 916 * NoSuchMethod class and store this object into the callee's stack slot.
 917 * Later, js_Invoke will recognise such an object and transfer control to
 918 * NoSuchMethod that invokes the method like:
 919 *
 920 *   this.__noSuchMethod__(id, args)
 921 *
 922 * where id is the name of the method that this invocation attempted to
 923 * call by name, and args is an Array containing this invocation's actual
 924 * parameters.
 925 */
 926JS_STATIC_INTERPRET JSBool
 927js_OnUnknownMethod(JSContext *cx, jsval *vp)
 928{
 929    JSObject *obj;
 930    jsid id;
 931    JSTempValueRooter tvr;
 932    JSBool ok;
 933
 934    JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
 935    obj = JSVAL_TO_OBJECT(vp[1]);
 936    JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
 937
 938    MUST_FLOW_THROUGH("out");
 939    id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
 940#if JS_HAS_XML_SUPPORT
 941    if (OBJECT_IS_XML(cx, obj)) {
 942        JSXMLObjectOps *ops;
 943
 944        ops = (JSXMLObjectOps *) obj->map->ops;
 945        obj = ops->getMethod(cx, obj, id, &tvr.u.value);
 946        if (!obj) {
 947            ok = JS_FALSE;
 948            goto out;
 949        }
 950        vp[1] = OBJECT_TO_JSVAL(obj);
 951    } else
 952#endif
 953    {
 954        ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
 955        if (!ok)
 956            goto out;
 957    }
 958    if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
 959        vp[0] = tvr.u.value;
 960    } else {
 961#if JS_HAS_XML_SUPPORT
 962        /* Extract the function name from function::name qname. */
 963        if (!JSVAL_IS_PRIMITIVE(vp[0])) {
 964            obj = JSVAL_TO_OBJECT(vp[0]);
 965            ok = js_IsFunctionQName(cx, obj, &id);
 966            if (!ok)
 967                goto out;
 968            if (id != 0)
 969                vp[0] = ID_TO_VALUE(id);
 970        }
 971#endif
 972        obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);
 973        if (!obj) {
 974            ok = JS_FALSE;
 975            goto out;
 976        }
 977        obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
 978        obj->fslots[JSSLOT_SAVED_ID] = vp[0];
 979        vp[0] = OBJECT_TO_JSVAL(obj);
 980    }
 981    ok = JS_TRUE;
 982
 983  out:
 984    JS_POP_TEMP_ROOT(cx, &tvr);
 985    return ok;
 986}
 987
 988static JSBool
 989NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
 990{
 991    jsval *invokevp;
 992    void *mark;
 993    JSBool ok;
 994    JSObject *obj, *argsobj;
 995
 996    invokevp = js_AllocStack(cx, 2 + 2, &mark);
 997    if (!invokevp)
 998        return JS_FALSE;
 999
1000    JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0]));
1001    JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
1002    obj = JSVAL_TO_OBJECT(vp[0]);
1003    JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass);
1004
1005    invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION];
1006    invokevp[1] = vp[1];
1007    invokevp[2] = obj->fslots[JSSLOT_SAVED_ID];
1008    argsobj = js_NewArrayObject(cx, argc, vp + 2);
1009    if (!argsobj) {
1010        ok = JS_FALSE;
1011    } else {
1012        invokevp[3] = OBJECT_TO_JSVAL(argsobj);
1013        ok = (flags & JSINVOKE_CONSTRUCT)
1014             ? js_InvokeConstructor(cx, 2, JS_TRUE, invokevp)
1015             : js_Invoke(cx, 2, invokevp, flags);
1016        vp[0] = invokevp[0];
1017    }
1018    js_FreeStack(cx, mark);
1019    return ok;
1020}
1021
1022#endif /* JS_HAS_NO_SUCH_METHOD */
1023
1024/*
1025 * We check if the function accepts a primitive value as |this|. For that we
1026 * use a table that maps value's tag into the corresponding function flag.
1027 */
1028JS_STATIC_ASSERT(JSVAL_INT == 1);
1029JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
1030JS_STATIC_ASSERT(JSVAL_STRING == 4);
1031JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
1032
1033const uint16 js_PrimitiveTestFlags[] = {
1034    JSFUN_THISP_NUMBER,     /* INT     */
1035    JSFUN_THISP_NUMBER,     /* DOUBLE  */
1036    JSFUN_THISP_NUMBER,     /* INT     */
1037    JSFUN_THISP_STRING,     /* STRING  */
1038    JSFUN_THISP_NUMBER,     /* INT     */
1039    JSFUN_THISP_BOOLEAN,    /* BOOLEAN */
1040    JSFUN_THISP_NUMBER      /* INT     */
1041};
1042
1043/*
1044 * Find a function reference and its 'this' object implicit first parameter
1045 * under argc arguments on cx's stack, and call the function.  Push missing
1046 * required arguments, allocate declared local variables, and pop everything
1047 * when done.  Then push the return value.
1048 */
1049JS_FRIEND_API(JSBool)
1050js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
1051{
1052    void *mark;
1053    JSStackFrame frame;
1054    jsval *sp, *argv, *newvp;
1055    jsval v;
1056    JSObject *funobj, *parent;
1057    JSBool ok;
1058    JSClass *clasp;
1059    JSObjectOps *ops;
1060    JSNative native;
1061    JSFunction *fun;
1062    JSScript *script;
1063    uintN nslots, i;
1064    uint32 rootedArgsFlag;
1065    JSInterpreterHook hook;
1066    void *hookData;
1067
1068    /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
1069    JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
1070    JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
1071
1072    /*
1073     * Mark the top of stack and load frequently-used registers. After this
1074     * point the control should flow through label out2: to return.
1075     */
1076    mark = JS_ARENA_MARK(&cx->stackPool);
1077    v = *vp;
1078
1079    if (JSVAL_IS_PRIMITIVE(v))
1080        goto bad;
1081
1082    funobj = JSVAL_TO_OBJECT(v);
1083    parent = OBJ_GET_PARENT(cx, funobj);
1084    clasp = OBJ_GET_CLASS(cx, funobj);
1085    if (clasp != &js_FunctionClass) {
1086#if JS_HAS_NO_SUCH_METHOD
1087        if (clasp == &js_NoSuchMethodClass) {
1088            ok = NoSuchMethod(cx, argc, vp, flags);
1089            goto out2;
1090        }
1091#endif
1092
1093        /* Function is inlined, all other classes use object ops. */
1094        ops = funobj->map->ops;
1095
1096        /*
1097         * XXX this makes no sense -- why convert to function if clasp->call?
1098         * XXX better to call that hook without converting
1099         * XXX the only thing that needs fixing is liveconnect
1100         *
1101         * Try converting to function, for closure and API compatibility.
1102         * We attempt the conversion under all circumstances for 1.2, but
1103         * only if there is a call op defined otherwise.
1104         */
1105        if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1106            ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1107            if (!ok)
1108                goto out2;
1109
1110            if (VALUE_IS_FUNCTION(cx, v)) {
1111                /* Make vp refer to funobj to keep it available as argv[-2]. */
1112                *vp = v;
1113                funobj = JSVAL_TO_OBJECT(v);
1114                parent = OBJ_GET_PARENT(cx, funobj);
1115                goto have_fun;
1116            }
1117        }
1118        fun = NULL;
1119        script = NULL;
1120        nslots = 0;
1121
1122        /* Try a call or construct native object op. */
1123        if (flags & JSINVOKE_CONSTRUCT) {
1124            if (!JSVAL_IS_OBJECT(vp[1])) {
1125                ok = js_PrimitiveToObject(cx, &vp[1]);
1126                if (!ok)
1127                    goto out2;
1128            }
1129            native = ops->construct;
1130        } else {
1131            native = ops->call;
1132        }
1133        if (!native)
1134            goto bad;
1135    } else {
1136have_fun:
1137        /* Get private data and set derived locals from it. */
1138        fun = GET_FUNCTION_PRIVATE(cx, funobj);
1139        nslots = FUN_MINARGS(fun);
1140        nslots = (nslots > argc) ? nslots - argc : 0;
1141        if (FUN_INTERPRETED(fun)) {
1142            native = NULL;
1143            script = fun->u.i.script;
1144        } else {
1145            native = fun->u.n.native;
1146            script = NULL;
1147            nslots += fun->u.n.extra;
1148        }
1149
1150        if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
1151            /* Handle bound method special case. */
1152            vp[1] = OBJECT_TO_JSVAL(parent);
1153        } else if (!JSVAL_IS_OBJECT(vp[1])) {
1154            JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
1155            if (PRIMITIVE_THIS_TEST(fun, vp[1]))
1156                goto start_call;
1157        }
1158    }
1159
1160    if (flags & JSINVOKE_CONSTRUCT) {
1161        JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
1162    } else {
1163        /*
1164         * We must call js_ComputeThis in case we are not called from the
1165         * interpreter, where a prior bytecode has computed an appropriate
1166         * |this| already.
1167         *
1168         * But we need to compute |this| eagerly only for so-called "slow"
1169         * (i.e., not fast) native functions. Fast natives must use either
1170         * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
1171         * the appropriate this-computing bytecode, e.g., JSOP_THIS.
1172         */
1173        if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
1174            if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) {
1175                ok = JS_FALSE;
1176                goto out2;
1177            }
1178            flags |= JSFRAME_COMPUTED_THIS;
1179        }
1180    }
1181
1182  start_call:
1183    if (native && fun && (fun->flags & JSFUN_FAST_NATIVE)) {
1184#ifdef DEBUG_NOT_THROWING
1185        JSBool alreadyThrowing = cx->throwing;
1186#endif
1187        JS_ASSERT(nslots == 0);
1188#if JS_HAS_LVALUE_RETURN
1189        /* Set by JS_SetCallReturnValue2, used to return reference types. */
1190        cx->rval2set = JS_FALSE;
1191#endif
1192        ok = ((JSFastNative) native)(cx, argc, vp);
1193        JS_RUNTIME_METER(cx->runtime, nativeCalls);
1194#ifdef DEBUG_NOT_THROWING
1195        if (ok && !alreadyThrowing)
1196            ASSERT_NOT_THROWING(cx);
1197#endif
1198        goto out2;
1199    }
1200
1201    argv = vp + 2;
1202    sp = argv + argc;
1203
1204    rootedArgsFlag = JSFRAME_ROOTED_ARGV;
1205    if (nslots != 0) {
1206        /*
1207         * The extra slots required by the function continue with argument
1208         * slots. Thus, when the last stack pool arena does not have room to
1209         * fit nslots right after sp and AllocateAfterSP fails, we have to copy
1210         * [vp..vp+2+argc) slots and clear rootedArgsFlag to root the copy.
1211         */
1212        if (!AllocateAfterSP(cx, sp, nslots)) {
1213            rootedArgsFlag = 0;
1214            newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL);
1215            if (!newvp) {
1216                ok = JS_FALSE;
1217                goto out2;
1218            }
1219            memcpy(newvp, vp, (2 + argc) * sizeof(jsval));
1220            argv = newvp + 2;
1221            sp = argv + argc;
1222        }
1223
1224        /* Push void to initialize missing args. */
1225        i = nslots;
1226        do {
1227            *sp++ = JSVAL_VOID;
1228        } while (--i != 0);
1229    }
1230
1231    /* Allocate space for local variables and stack of interpreted function. */
1232    if (script && script->nslots != 0) {
1233        if (!AllocateAfterSP(cx, sp, script->nslots)) {
1234            /* NB: Discontinuity between argv and slots, stack slots. */
1235            sp = js_AllocRawStack(cx, script->nslots, NULL);
1236            if (!sp) {
1237                ok = JS_FALSE;
1238                goto out2;
1239            }
1240        }
1241
1242        /* Push void to initialize local variables. */
1243        for (jsval *end = sp + fun->u.i.nvars; sp != end; ++sp)
1244            *sp = JSVAL_VOID;
1245    }
1246
1247    /*
1248     * Initialize the frame.
1249     *
1250     * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
1251     * can be a primitive value here for those native functions specified with
1252     * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
1253     */
1254    frame.thisp = (JSObject *)vp[1];
1255    frame.varobj = NULL;
1256    frame.callobj = frame.argsobj = NULL;
1257    frame.script = script;
1258    frame.callee = funobj;
1259    frame.fun = fun;
1260    frame.argc = argc;
1261    frame.argv = argv;
1262
1263    /* Default return value for a constructor is the new object. */
1264    frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
1265    frame.down = cx->fp;
1266    frame.annotation = NULL;
1267    frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
1268    frame.regs = NULL;
1269    frame.imacpc = NULL;
1270    frame.slots = NULL;
1271    frame.sharpDepth = 0;
1272    frame.sharpArray = NULL;
1273    frame.flags = flags | rootedArgsFlag;
1274    frame.dormantNext = NULL;
1275    frame.xmlNamespace = NULL;
1276    frame.blockChain = NULL;
1277
1278    MUST_FLOW_THROUGH("out");
1279    cx->fp = &frame;
1280
1281    /* Init these now in case we goto out before first hook call. */
1282    hook = cx->debugHooks->callHook;
1283    hookData = NULL;
1284
1285    /* call the hook if present */
1286    if (hook && (native || script))
1287        hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
1288
1289    /* Call the function, either a native method or an interpreted script. */
1290    if (native) {
1291#ifdef DEBUG_NOT_THROWING
1292        JSBool alreadyThrowing = cx->throwing;
1293#endif
1294
1295#if JS_HAS_LVALUE_RETURN
1296        /* Set by JS_SetCallReturnValue2, used to return reference types. */
1297        cx->rval2set = JS_FALSE;
1298#endif
1299
1300        /* If native, use caller varobj and scopeChain for eval. */
1301        JS_ASSERT(!frame.varobj);
1302        JS_ASSERT(!frame.scopeChain);
1303        if (frame.down) {
1304            frame.varobj = frame.down->varobj;
1305            frame.scopeChain = frame.down->scopeChain;
1306        }
1307
1308        /* But ensure that we have a scope chain. */
1309        if (!frame.scopeChain)
1310            frame.scopeChain = parent;
1311
1312        frame.displaySave = NULL;
1313        ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1314        JS_RUNTIME_METER(cx->runtime, nativeCalls);
1315#ifdef DEBUG_NOT_THROWING
1316        if (ok && !alreadyThrowing)
1317            ASSERT_NOT_THROWING(cx);
1318#endif
1319    } else if (script) {
1320        /* Use parent scope so js_GetCallObject can find the right "Call". */
1321        frame.scopeChain = parent;
1322        if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1323            /* Scope with a call object parented by the callee's parent. */
1324            if (!js_GetCallObject(cx, &frame, parent)) {
1325                ok = JS_FALSE;
1326                goto out;
1327            }
1328        }
1329        frame.slots = sp - fun->u.i.nvars;
1330
1331        ok = js_Interpret(cx);
1332    } else {
1333        /* fun might be onerror trying to report a syntax error in itself. */
1334        frame.scopeChain = NULL;
1335        frame.displaySave = NULL;
1336        ok = JS_TRUE;
1337    }
1338
1339out:
1340    if (hookData) {
1341        hook = cx->debugHooks->callHook;
1342        if (hook)
1343            hook(cx, &frame, JS_FALSE, &ok, hookData);
1344    }
1345
1346    /* If frame has a call object, sync values and clear back-pointer. */
1347    if (frame.callobj)
1348        ok &= js_PutCallObject(cx, &frame);
1349
1350    /* If frame has an arguments object, sync values and clear back-pointer. */
1351    if (frame.argsobj)
1352        ok &= js_PutArgsObject(cx, &frame);
1353
1354    *vp = frame.rval;
1355
1356    /* Restore cx->fp now that we're done releasing frame objects. */
1357    cx->fp = frame.down;
1358
1359out2:
1360    /* Pop everything we may have allocated off the stack. */
1361    JS_ARENA_RELEASE(&cx->stackPool, mark);
1362    if (!ok)
1363        *vp = JSVAL_NULL;
1364    return ok;
1365
1366bad:
1367    js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
1368    ok = JS_FALSE;
1369    goto out2;
1370}
1371
1372JSBool
1373js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1374                  uintN argc, jsval *argv, jsval *rval)
1375{
1376    jsval *invokevp;
1377    void *mark;
1378    JSBool ok;
1379
1380    invokevp = js_AllocStack(cx, 2 + argc, &mark);
1381    if (!invokevp)
1382        return JS_FALSE;
1383
1384    invokevp[0] = fval;
1385    invokevp[1] = OBJECT_TO_JSVAL(obj);
1386    memcpy(invokevp + 2, argv, argc * sizeof *argv);
1387
1388    ok = js_Invoke(cx, argc, invokevp, flags);
1389    if (ok) {
1390        /*
1391         * Store *rval in the a scoped local root if a scope is open, else in
1392         * the lastInternalResult pigeon-hole GC root, solely so users of
1393         * js_InternalInvoke and its direct and indirect (js_ValueToString for
1394         * example) callers do not need to manage roots for local, temporary
1395         * references to such results.
1396         */
1397        *rval = *invokevp;
1398        if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
1399            if (cx->localRootStack) {
1400                if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
1401                    ok = JS_FALSE;
1402            } else {
1403                cx->weakRoots.lastInternalResult = *rval;
1404            }
1405        }
1406    }
1407
1408    js_FreeStack(cx, mark);
1409    return ok;
1410}
1411
1412JSBool
1413js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1414                    JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1415{
1416    JSSecurityCallbacks *callbacks;
1417
1418    /*
1419     * js_InternalInvoke could result in another try to get or set the same id
1420     * again, see bug 355497.
1421     */
1422    JS_CHECK_RECURSION(cx, return JS_FALSE);
1423
1424    /*
1425     * Check general (not object-ops/class-specific) access from the running
1426     * script to obj.id only if id has a scripted getter or setter that we're
1427     * about to invoke.  If we don't check this case, nothing else will -- no
1428     * other native code has the chance to check.
1429     *
1430     * Contrast this non-native (scripted) case with native getter and setter
1431     * accesses, where the native itself must do an access check, if security
1432     * policies requires it.  We make a checkAccess or checkObjectAccess call
1433     * back to the embedding program only in those cases where we're not going
1434     * to call an embedding-defined native function, getter, setter, or class
1435     * hook anyway.  Where we do call such a native, there's no need for the
1436     * engine to impose a separate access check callback on all embeddings --
1437     * many embeddings have no security policy at all.
1438     */
1439    JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1440    callbacks = JS_GetSecurityCallbacks(cx);
1441    if (callbacks &&
1442        callbacks->checkObjectAccess &&
1443        VALUE_IS_FUNCTION(cx, fval) &&
1444        FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) &&
1445        !callbacks->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) {
1446        return JS_FALSE;
1447    }
1448
1449    return js_InternalCall(cx, obj, fval, argc, argv, rval);
1450}
1451
1452JSBool
1453js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1454           JSStackFrame *down, uintN flags, jsval *result)
1455{
1456    JSInterpreterHook hook;
1457    void *hookData, *mark;
1458    JSStackFrame *oldfp, frame;
1459    JSObject *obj, *tmp;
1460    JSBool ok;
1461
1462#ifdef INCLUDE_MOZILLA_DTRACE
1463    if (JAVASCRIPT_EXECUTE_START_ENABLED())
1464        jsdtrace_execute_start(script);
1465#endif
1466
1467    hook = cx->debugHooks->executeHook;
1468    hookData = mark = NULL;
1469    oldfp = cx->fp;
1470    frame.script = script;
1471    if (down) {
1472        /* Propagate arg state for eval and the debugger API. */
1473        frame.callobj = down->callobj;
1474        frame.argsobj = down->argsobj;
1475        frame.varobj = down->varobj;
1476        frame.callee = down->callee;
1477        frame.fun = down->fun;
1478        frame.thisp = down->thisp;
1479        if (down->flags & JSFRAME_COMPUTED_THIS)
1480            flags |= JSFRAME_COMPUTED_THIS;
1481        frame.argc = down->argc;
1482        frame.argv = down->argv;
1483        frame.annotation = down->annotation;
1484        frame.sharpArray = down->sharpArray;
1485        JS_ASSERT(script->nfixed == 0);
1486    } else {
1487        frame.callobj = frame.argsobj = NULL;
1488        obj = chain;
1489        if (cx->options & JSOPTION_VAROBJFIX) {
1490            while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1491                obj = tmp;
1492        }
1493        frame.varobj = obj;
1494        frame.callee = NULL;
1495        frame.fun = NULL;
1496        frame.thisp = chain;
1497        frame.argc = 0;
1498        frame.argv = NULL;
1499        frame.annotation = NULL;
1500        frame.sharpArray = NULL;
1501    }
1502
1503    frame.imacpc = NULL;
1504    if (script->nslots != 0) {
1505        frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
1506        if (!frame.slots) {
1507            ok = JS_FALSE;
1508            goto out;
1509        }
1510        memset(frame.slots, 0, script->nfixed * sizeof(jsval));
1511    } else {
1512        frame.slots = NULL;
1513    }
1514
1515    frame.rval = JSVAL_VOID;
1516    frame.down = down;
1517    frame.scopeChain = chain;
1518    frame.regs = NULL;
1519    frame.sharpDepth = 0;
1520    frame.flags = flags;
1521    frame.dormantNext = NULL;
1522    frame.xmlNamespace = NULL;
1523    frame.blockChain = NULL;
1524
1525    /*
1526     * Here we wrap the call to js_Interpret with code to (conditionally)
1527     * save and restore the old stack frame chain into a chain of 'dormant'
1528     * frame chains.  Since we are replacing cx->fp, we were running into
1529     * the problem that if GC was called under this frame, some of the GC
1530     * things associated with the old frame chain (available here only in
1531     * the C variable 'oldfp') were not rooted and were being collected.
1532     *
1533     * So, now we preserve the links to these 'dormant' frame chains in cx
1534     * before calling js_Interpret and cleanup afterwards.  The GC walks
1535     * these dormant chains and marks objects in the same way that it marks
1536     * objects in the primary cx->fp chain.
1537     */
1538    if (oldfp && oldfp != down) {
1539        JS_ASSERT(!oldfp->dormantNext);
1540        oldfp->dormantNext = cx->dormantFrameChain;
1541        cx->dormantFrameChain = oldfp;
1542    }
1543
1544    cx->fp = &frame;
1545    if (!down) {
1546        OBJ_TO_OUTER_OBJECT(cx, frame.thisp);
1547        if (!frame.thisp) {
1548            ok = JS_FALSE;
1549            goto out2;
1550        }
1551        frame.flags |= JSFRAME_COMPUTED_THIS;
1552    }
1553
1554    if (hook) {
1555        hookData = hook(cx, &frame, JS_TRUE, 0,
1556                        cx->debugHooks->executeHookData);
1557    }
1558
1559    ok = js_Interpret(cx);
1560    if (result)
1561        *result = frame.rval;
1562
1563    if (hookData) {
1564        hook = cx->debugHooks->executeHook;
1565        if (hook)
1566            hook(cx, &frame, JS_FALSE, &ok, hookData);
1567    }
1568
1569out2:
1570    if (mark)
1571        js_FreeRawStack(cx, mark);
1572    cx->fp = oldfp;
1573
1574    if (oldfp && oldfp != down) {
1575        JS_ASSERT(cx->dormantFrameChain == oldfp);
1576        cx->dormantFrameChain = oldfp->dormantNext;
1577        oldfp->dormantNext = NULL;
1578    }
1579
1580out:
1581#ifdef INCLUDE_MOZILLA_DTRACE
1582    if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
1583        jsdtrace_execute_done(script);
1584#endif
1585    return ok;
1586}
1587
1588JSBool
1589js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1590                      JSObject **objp, JSProperty **propp)
1591{
1592    JSObject *obj2;
1593    JSProperty *prop;
1594    uintN oldAttrs, report;
1595    JSBool isFunction;
1596    jsval value;
1597    const char *type, *name;
1598
1599    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1600        return JS_FALSE;
1601    if (propp) {
1602        *objp = obj2;
1603        *propp = prop;
1604    }
1605    if (!prop)
1606        return JS_TRUE;
1607
1608    /*
1609     * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
1610     * An assertion at label bad: will insist that it is null.
1611     */
1612    if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1613        OBJ_DROP_PROPERTY(cx, obj2, prop);
1614#ifdef DEBUG
1615        prop = NULL;
1616#endif
1617        goto bad;
1618    }
1619
1620    /*
1621     * From here, return true, or else goto bad on failure to null out params.
1622     * If our caller doesn't want prop, drop it (we don't need it any longer).
1623     */
1624    if (!propp) {
1625        OBJ_DROP_PROPERTY(cx, obj2, prop);
1626        prop = NULL;
1627    }
1628
1629    if (attrs == JSPROP_INITIALIZER) {
1630        /* Allow the new object to override properties. */
1631        if (obj2 != obj)
1632            return JS_TRUE;
1633        report = JSREPORT_WARNING | JSREPORT_STRICT;
1634    } else {
1635        /* We allow redeclaring some non-readonly properties. */
1636        if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1637            /*
1638             * Allow redeclaration of variables and functions, but insist that
1639             * the new value is not a getter if the old value was, ditto for
1640             * setters -- unless prop is impermanent (in which case anyone
1641             * could delete it and redefine it, willy-nilly).
1642             */
1643            if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1644                return JS_TRUE;
1645            if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1646                return JS_TRUE;
1647            if (!(oldAttrs & JSPROP_PERMANENT))
1648                return JS_TRUE;
1649        }
1650
1651        report = JSREPORT_ERROR;
1652        isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1653        if (!isFunction) {
1654            if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1655                goto bad;
1656            isFunction = VALUE_IS_FUNCTION(cx, value);
1657        }
1658    }
1659
1660    type = (attrs == JSPROP_INITIALIZER)
1661           ? "property"
1662           : (oldAttrs & attrs & JSPROP_GETTER)
1663           ? js_getter_str
1664           : (oldAttrs & attrs & JSPROP_SETTER)
1665           ? js_setter_str
1666           : (oldAttrs & JSPROP_READONLY)
1667           ? js_const_str
1668           : isFunction
1669           ? js_function_str
1670           : js_var_str;
1671    name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
1672    if (!name)
1673        goto bad;
1674    return JS_ReportErrorFlagsAndNumber(cx, report,
1675                                        js_GetErrorMessage, NULL,
1676                                        JSMSG_REDECLARED_VAR,
1677                                        type, name);
1678
1679bad:
1680    if (propp) {
1681        *objp = NULL;
1682        *propp = NULL;
1683    }
1684    JS_ASSERT(!prop);
1685    return JS_FALSE;
1686}
1687
1688JSBool
1689js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval)
1690{
1691    jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
1692    jsdouble ld, rd;
1693
1694    if (ltag == rtag) {
1695        if (ltag == JSVAL_STRING) {
1696            JSString *lstr = JSVAL_TO_STRING(lval),
1697                     *rstr = JSVAL_TO_STRING(rval);
1698            return js_EqualStrings(lstr, rstr);
1699        }
1700        if (ltag == JSVAL_DOUBLE) {
1701            ld = *JSVAL_TO_DOUBLE(lval);
1702            rd = *JSVAL_TO_DOUBLE(rval);
1703            return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1704        }
1705        if (ltag == JSVAL_OBJECT &&
1706            lval != rval &&
1707            !JSVAL_IS_NULL(lval) &&
1708            !JSVAL_IS_NULL(rval)) {
1709            JSObject *lobj, *robj;
1710
1711            lobj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(lval));
1712            robj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(rval));
1713            lval = OBJECT_TO_JSVAL(lobj);
1714            rval = OBJECT_TO_JSVAL(robj);
1715        }
1716        return lval == rval;
1717    }
1718    if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
1719        ld = *JSVAL_TO_DOUBLE(lval);
1720        rd = JSVAL_TO_INT(rval);
1721        return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1722    }
1723    if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
1724        ld = JSVAL_TO_INT(lval);
1725        rd = *JSVAL_TO_DOUBLE(rval);
1726        return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1727    }
1728    return lval == rval;
1729}
1730
1731JSBool
1732js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
1733{
1734    JSFunction *fun, *fun2;
1735    JSObject *obj, *obj2, *proto, *parent;
1736    jsval lval, rval;
1737    JSClass *clasp;
1738
1739    fun = NULL;
1740    obj2 = NULL;
1741    lval = *vp;
1742    if (!JSVAL_IS_OBJECT(lval) ||
1743        (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
1744        /* XXX clean up to avoid special cases above ObjectOps layer */
1745        OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
1746        !obj2->map->ops->construct)
1747    {
1748        fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
1749        if (!fun)
1750            return JS_FALSE;
1751    }
1752
1753    clasp = &js_ObjectClass;
1754    if (!obj2) {
1755        proto = parent = NULL;
1756        fun = NULL;
1757    } else {
1758        /*
1759         * Get the constructor prototype object for this function.
1760         * Use the nominal 'this' parameter slot, vp[1], as a local
1761         * root to protect this prototype, in case it has no other
1762         * strong refs.
1763         */
1764        if (!OBJ_GET_PROPERTY(cx, obj2,
1765                              ATOM_TO_JSID(cx->runtime->atomState
1766                                           .classPrototypeAtom),
1767                              &vp[1])) {
1768            return JS_FALSE;
1769        }
1770        rval = vp[1];
1771        proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
1772        parent = OBJ_GET_PARENT(cx, obj2);
1773
1774        if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
1775            fun2 = GET_FUNCTION_PRIVATE(cx, obj2);
1776            if (!FUN_INTERPRETED(fun2) &&
1777                !(fun2->flags & JSFUN_TRACEABLE) &&
1778                fun2->u.n.u.clasp) {
1779                clasp = fun2->u.n.u.clasp;
1780            }
1781        }
1782    }
1783    obj = js_NewObject(cx, clasp, proto, parent, 0);
1784    if (!obj)
1785        return JS_FALSE;
1786
1787    /* Now we have an object with a constructor method; call it. */
1788    vp[1] = OBJECT_TO_JSVAL(obj);
1789    if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {
1790        cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1791        return JS_FALSE;
1792    }
1793
1794    /* Check the return value and if it's primitive, force it to be obj. */
1795    rval = *vp;
1796    if (clampReturn && JSVAL_IS_PRIMITIVE(rval)) {
1797        if (!fun) {
1798            /* native [[Construct]] returning primitive is error */
1799            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1800                                 JSMSG_BAD_NEW_RESULT,
1801                                 js_ValueToPrintableString(cx, rval));
1802            return JS_FALSE;
1803        }
1804        *vp = OBJECT_TO_JSVAL(obj);
1805    }
1806
1807    JS_RUNTIME_METER(cx->runtime, constructs);
1808    return JS_TRUE;
1809}
1810
1811JSBool
1812js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
1813{
1814    JS_ASSERT(!JSVAL_IS_INT(idval));
1815
1816#if JS_HAS_XML_SUPPORT
1817    if (!JSVAL_IS_PRIMITIVE(idval)) {
1818        if (OBJECT_IS_XML(cx, obj)) {
1819            *idp = OBJECT_JSVAL_TO_JSID(idval);
1820            return JS_TRUE;
1821        }
1822        if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
1823            return JS_FALSE;
1824        if (*idp != 0)
1825            return JS_TRUE;
1826    }
1827#endif
1828
1829    return js_ValueToStringId(cx, idval, idp);
1830}
1831
1832/*
1833 * Enter the new with scope using an object at sp[-1] and associate the depth
1834 * of the with block with sp + stackIndex.
1835 */
1836JS_STATIC_INTERPRET JSBool
1837js_EnterWith(JSContext *cx, jsint stackIndex)
1838{
1839    JSStackFrame *fp;
1840    jsval *sp;
1841    JSObject *obj, *parent, *withobj;
1842
1843    fp = cx->fp;
1844    sp = fp->regs->sp;
1845    JS_ASSERT(stackIndex < 0);
1846    JS_ASSERT(StackBase(fp) <= sp + stackIndex);
1847
1848    if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
1849        obj = JSVAL_TO_OBJECT(sp[-1]);
1850    } else {
1851        obj = js_ValueToNonNullObject(cx, sp[-1]);
1852        if (!obj)
1853            return JS_FALSE;
1854        sp[-1] = OBJECT_TO_JSVAL(obj);
1855    }
1856
1857    parent = js_GetScopeChain(cx, fp);
1858    if (!parent)
1859        return JS_FALSE;
1860
1861    OBJ_TO_INNER_OBJECT(cx, obj);
1862    if (!obj)
1863        return JS_FALSE;
1864
1865    withobj = js_NewWithObject(cx, obj, parent,
1866                               sp + stackIndex - StackBase(fp));
1867    if (!withobj)
1868        return JS_FALSE;
1869
1870    fp->scopeChain = withobj;
1871    js_DisablePropertyCache(cx);
1872    return JS_TRUE;
1873}
1874
1875JS_STATIC_INTERPRET void
1876js_LeaveWith(JSContext *cx)
1877{
1878    JSObject *withobj;
1879
1880    withobj = cx->fp->scopeChain;
1881    JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1882    JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);
1883    JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1884    cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
1885    JS_SetPrivate(cx, withobj, NULL);
1886    js_EnablePropertyCache(cx);
1887}
1888
1889JSClass *
1890js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1891{
1892    JSClass *clasp;
1893
1894    clasp = OBJ_GET_CLASS(cx, obj);
1895    if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
1896        OBJ_GET_PRIVATE(cx, obj) == cx->fp &&
1897        OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
1898        return clasp;
1899    }
1900    return NULL;
1901}
1902
1903JS_STATIC_INTERPRET jsint
1904js_CountWithBlocks(JSContext *cx, JSStackFrame *fp)
1905{
1906    jsint n;
1907    JSObject *obj;
1908    JSClass *clasp;
1909
1910    n = 0;
1911    for (obj = fp->scopeChain;
1912         (clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL;
1913         obj = OBJ_GET_PARENT(cx, obj)) {
1914        if (clasp == &js_WithClass)
1915            ++n;
1916    }
1917    return n;
1918}
1919
1920/*
1921 * Unwind block and scope chains to match the given depth. The function sets
1922 * fp->sp on return to stackDepth.
1923 */
1924JSBool
1925js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
1926               JSBool normalUnwind)
1927{
1928    JSObject *obj;
1929    JSClass *clasp;
1930
1931    JS_ASSERT(stackDepth >= 0);
1932    JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp);
1933
1934    for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
1935        JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
1936        if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
1937            break;
1938    }
1939    fp->blockChain = obj;
1940
1941    for (;;) {
1942        obj = fp->scopeChain;
1943        clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth);
1944        if (!clasp)
1945            break;
1946        if (clasp == &js_BlockClass) {
1947            /* Don't fail until after we've updated all stacks. */
1948            normalUnwind &= js_PutBlockObject(cx, normalUnwind);
1949        } else {
1950            js_LeaveWith(cx);
1951        }
1952    }
1953
1954    fp->regs->sp = StackBase(fp) + stackDepth;
1955    return normalUnwind;
1956}
1957
1958JS_STATIC_INTERPRET JSBool
1959js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
1960{
1961    jsval v;
1962    jsdouble d;
1963
1964    v = *vp;
1965    if (JSVAL_IS_DOUBLE(v)) {
1966        d = *JSVAL_TO_DOUBLE(v);
1967    } else if (JSVAL_IS_INT(v)) {
1968        d = JSVAL_TO_INT(v);
1969    } else {
1970        d = js_ValueToNumber(cx, vp);
1971        if (JSVAL_IS_NULL(*vp))
1972            return JS_FALSE;
1973        JS_ASSERT(JSVAL_IS_NUMBER(*vp) || *vp == JSVAL_TRUE);
1974
1975        /* Store the result of v conversion back in vp for post increments. */
1976        if ((cs->format & JOF_POST) &&
1977            *vp == JSVAL_TRUE
1978            && !js_NewNumberInRootedValue(cx, d, vp)) {
1979            return JS_FALSE;
1980        }
1981    }
1982
1983    (cs->format & JOF_INC) ? d++ : d--;
1984    if (!js_NewNumberInRootedValue(cx, d, vp2))
1985        return JS_FALSE;
1986
1987    if (!(cs->format & JOF_POST))
1988        *vp = *vp2;
1989    return JS_TRUE;
1990}
1991
1992#ifdef DEBUG
1993
1994JS_STATIC_INTERPRET void
1995js_TraceOpcode(JSContext *cx, jsint len)
1996{
1997    FILE *tracefp;
1998    JSStackFrame *fp;
1999    JSFrameRegs *regs;
2000    JSOp prevop;
2001    intN ndefs, n, nuses;
2002    jsval *siter;
2003    JSString *str;
2004    JSOp op;
2005
2006    tracefp = (FILE *) cx->tracefp;
2007    JS_ASSERT(tracefp);
2008    fp = cx->fp;
2009    regs = fp->regs;
2010    if (len != 0) {
2011        prevop = (JSOp) regs->pc[-len];
2012        ndefs = js_CodeSpec[prevop].ndefs;
2013        if (ndefs != 0) {
2014            for (n = -ndefs; n < 0; n++) {
2015                char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2016                                                         NULL);
2017                if (bytes) {
2018                    fprintf(tracefp, "%s %s",
2019                            (n == -ndefs) ? "  output:" : ",",
2020                            bytes);
2021                    JS_free(cx, bytes);
2022                }
2023            }
2024            fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2025        }
2026        fprintf(tracefp, "  stack: ");
2027        for (siter = StackBase(fp); siter < regs->sp; siter++) {
2028            str = js_ValueToString(cx, *siter);
2029            if (!str)
2030                fputs("<null>", tracefp);
2031            else
2032                js_FileEscapedString(tracefp, str, 0);
2033            fputc(' ', tracefp);
2034        }
2035        fputc('\n', tracefp);
2036    }
2037
2038    fprintf(tracefp, "%4u: ",
2039            js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
2040    js_Disassemble1(cx, fp->script, regs->pc,
2041                    PTRDIFF(regs->pc, fp->script->code, jsbytecode),
2042                    JS_FALSE, tracefp);
2043    op = (JSOp) *regs->pc;
2044    nuses = js_CodeSpec[op].nuses;
2045    if (nuses != 0) {
2046        for (n = -nuses; n < 0; n++) {
2047            char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2048                                                     NULL);
2049            if (bytes) {
2050                fprintf(tracefp, "%s %s",
2051                        (n == -nuses) ? "  inputs:" : ",",
2052                        bytes);
2053                JS_free(cx, bytes);
2054            }
2055        }
2056        fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2057    }
2058}
2059
2060#endif /* DEBUG */
2061
2062#ifdef JS_OPMETER
2063
2064# include <stdlib.h>
2065
2066# define HIST_NSLOTS            8
2067
2068/*
2069 * The second dimension is hardcoded at 256 because we know that many bits fit
2070 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2071 * any particular row.
2072 */
2073static uint32 succeeds[JSOP_LIMIT][256];
2074static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
2075
2076JS_STATIC_INTERPRET void
2077js_MeterOpcodePair(JSOp op1, JSOp op2)
2078{
2079    if (op1 != JSOP_STOP)
2080        ++succeeds[op1][op2];
2081}
2082
2083JS_STATIC_INTERPRET void
2084js_MeterSlotOpcode(JSOp op, uint32 slot)
2085{
2086    if (slot < HIST_NSLOTS)
2087        ++slot_ops[op][slot];
2088}
2089
2090typedef struct Edge {
2091    const char  *from;
2092    const char  *to;
2093    uint32      count;
2094} Edge;
2095
2096static int
2097compare_edges(const void *a, const void *b)
2098{
2099    const Edge *ea = (const Edge *) a;
2100    const Edge *eb = (const Edge *) b;
2101
2102    return (int32)eb->count - (int32)ea->count;
2103}
2104
2105void
2106js_DumpOpMeters()
2107{
2108    const char *name, *from, *style;
2109    FILE *fp;
2110    uint32 total, count;
2111    uint32 i, j, nedges;
2112    Edge *graph;
2113
2114    name = getenv("JS_OPMETER_FILE");
2115    if (!name)
2116        name = "/tmp/ops.dot";
2117    fp = fopen(name, "w");
2118    if (!fp) {
2119        perror(name);
2120        return;
2121    }
2122
2123    total = nedges = 0;
2124    for (i = 0; i < JSOP_LIMIT; i++) {
2125        for (j = 0; j < JSOP_LIMIT; j++) {
2126            count = succeeds[i][j];
2127            if (count != 0) {
2128                total += count;
2129                ++nedges;
2130            }
2131        }
2132    }
2133
2134# define SIGNIFICANT(count,total) (200. * (count) >= (total))
2135
2136    graph = (Edge *) calloc(nedges, sizeof graph[0]);
2137    for (i = nedges = 0; i < JSOP_LIMIT; i++) {
2138        from = js_CodeName[