PageRenderTime 283ms CodeModel.GetById 41ms app.highlight 220ms RepoModel.GetById 1ms app.codeStats 2ms

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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2127 lines | 1648 code | 232 blank | 247 comment | 338 complexity | 9c5a81c78934020d816cc510d7eacc68 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   2 * vim: set ts=8 sw=4 et tw=78:
   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 * JS object implementation.
  43 */
  44#include "jsstddef.h"
  45#include <stdlib.h>
  46#include <string.h>
  47#include "jstypes.h"
  48#include "jsarena.h" /* Added by JSIFY */
  49#include "jsbit.h"
  50#include "jsutil.h" /* Added by JSIFY */
  51#include "jshash.h" /* Added by JSIFY */
  52#include "jsdhash.h"
  53#include "jsprf.h"
  54#include "jsapi.h"
  55#include "jsarray.h"
  56#include "jsatom.h"
  57#include "jsbool.h"
  58#include "jsbuiltins.h"
  59#include "jscntxt.h"
  60#include "jsversion.h"
  61#include "jsemit.h"
  62#include "jsfun.h"
  63#include "jsgc.h"
  64#include "jsinterp.h"
  65#include "jslock.h"
  66#include "jsnum.h"
  67#include "jsobj.h"
  68#include "jsopcode.h"
  69#include "jsparse.h"
  70#include "jsscope.h"
  71#include "jsscript.h"
  72#include "jsstr.h"
  73#include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
  74#include "jsstaticcheck.h"
  75
  76#if JS_HAS_GENERATORS
  77#include "jsiter.h"
  78#endif
  79
  80#if JS_HAS_XML_SUPPORT
  81#include "jsxml.h"
  82#endif
  83
  84#if JS_HAS_XDR
  85#include "jsxdrapi.h"
  86#endif
  87
  88#ifdef INCLUDE_MOZILLA_DTRACE
  89#include "jsdtracef.h"
  90#endif
  91
  92#include "jsautooplen.h"
  93
  94#ifdef JS_THREADSAFE
  95#define NATIVE_DROP_PROPERTY js_DropProperty
  96
  97extern void
  98js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
  99#else
 100#define NATIVE_DROP_PROPERTY NULL
 101#endif
 102
 103JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
 104    js_NewObjectMap,        js_DestroyObjectMap,
 105    js_LookupProperty,      js_DefineProperty,
 106    js_GetProperty,         js_SetProperty,
 107    js_GetAttributes,       js_SetAttributes,
 108    js_DeleteProperty,      js_DefaultValue,
 109    js_Enumerate,           js_CheckAccess,
 110    NULL,                   NATIVE_DROP_PROPERTY,
 111    js_Call,                js_Construct,
 112    NULL,                   js_HasInstance,
 113    js_SetProtoOrParent,    js_SetProtoOrParent,
 114    js_TraceObject,         js_Clear,
 115    js_GetRequiredSlot,     js_SetRequiredSlot
 116};
 117
 118JSClass js_ObjectClass = {
 119    js_Object_str,
 120    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
 121    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
 122    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
 123    JSCLASS_NO_OPTIONAL_MEMBERS
 124};
 125
 126#if JS_HAS_OBJ_PROTO_PROP
 127
 128static JSBool
 129obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 130
 131static JSBool
 132obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 133
 134static JSBool
 135obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 136
 137static JSPropertySpec object_props[] = {
 138    /* These two must come first; see object_props[slot].name usage below. */
 139    {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
 140                                                  obj_getSlot,  obj_setSlot},
 141    {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
 142                                                  obj_getSlot,  obj_setSlot},
 143    {js_count_str, 0,            JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
 144                                                  obj_getCount, NULL},
 145    {0,0,0,0,0}
 146};
 147
 148/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
 149#define JSSLOT_COUNT 2
 150
 151static JSBool
 152ReportStrictSlot(JSContext *cx, uint32 slot)
 153{
 154    if (slot == JSSLOT_PROTO)
 155        return JS_TRUE;
 156    return JS_ReportErrorFlagsAndNumber(cx,
 157                                        JSREPORT_WARNING | JSREPORT_STRICT,
 158                                        js_GetErrorMessage, NULL,
 159                                        JSMSG_DEPRECATED_USAGE,
 160                                        object_props[slot].name);
 161}
 162
 163static JSBool
 164obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 165{
 166    uint32 slot;
 167    jsid propid;
 168    JSAccessMode mode;
 169    uintN attrs;
 170    JSObject *pobj;
 171    JSClass *clasp;
 172    JSExtendedClass *xclasp;
 173
 174    slot = (uint32) JSVAL_TO_INT(id);
 175    if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
 176        propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
 177        mode = JSACC_PROTO;
 178    } else {
 179        propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
 180        mode = JSACC_PARENT;
 181    }
 182
 183    /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
 184    if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
 185        return JS_FALSE;
 186
 187    pobj = JSVAL_TO_OBJECT(*vp);
 188    if (pobj) {
 189        clasp = OBJ_GET_CLASS(cx, pobj);
 190        if (clasp == &js_CallClass || clasp == &js_BlockClass) {
 191            /* Censor activations and lexical scopes per ECMA-262. */
 192            *vp = JSVAL_NULL;
 193        } else if (clasp->flags & JSCLASS_IS_EXTENDED) {
 194            xclasp = (JSExtendedClass *) clasp;
 195            if (xclasp->outerObject) {
 196                pobj = xclasp->outerObject(cx, pobj);
 197                if (!pobj)
 198                    return JS_FALSE;
 199                *vp = OBJECT_TO_JSVAL(pobj);
 200            }
 201        }
 202    }
 203    return JS_TRUE;
 204}
 205
 206static JSBool
 207obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 208{
 209    JSObject *pobj;
 210    uint32 slot;
 211    jsid propid;
 212    uintN attrs;
 213
 214    if (!JSVAL_IS_OBJECT(*vp))
 215        return JS_TRUE;
 216    pobj = JSVAL_TO_OBJECT(*vp);
 217
 218    if (pobj) {
 219        /*
 220         * Innerize pobj here to avoid sticking unwanted properties on the
 221         * outer object. This ensures that any with statements only grant
 222         * access to the inner object.
 223         */
 224        OBJ_TO_INNER_OBJECT(cx, pobj);
 225        if (!pobj)
 226            return JS_FALSE;
 227    }
 228    slot = (uint32) JSVAL_TO_INT(id);
 229    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
 230        return JS_FALSE;
 231
 232    /* __parent__ is readonly and permanent, only __proto__ may be set. */
 233    propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
 234    if (!OBJ_CHECK_ACCESS(cx, obj, propid,
 235                          (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp,
 236                          &attrs)) {
 237        return JS_FALSE;
 238    }
 239
 240    return js_SetProtoOrParent(cx, obj, slot, pobj);
 241}
 242
 243static JSBool
 244obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 245{
 246    jsval iter_state;
 247    jsid num_properties;
 248    JSBool ok;
 249
 250    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
 251        return JS_FALSE;
 252
 253    /* Get the number of properties to enumerate. */
 254    iter_state = JSVAL_NULL;
 255    ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
 256    if (!ok)
 257        goto out;
 258
 259    if (!JSVAL_IS_INT(num_properties)) {
 260        JS_ASSERT(0);
 261        *vp = JSVAL_ZERO;
 262        goto out;
 263    }
 264    *vp = num_properties;
 265
 266out:
 267    if (iter_state != JSVAL_NULL)
 268        ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
 269    return ok;
 270}
 271
 272#else  /* !JS_HAS_OBJ_PROTO_PROP */
 273
 274#define object_props NULL
 275
 276#endif /* !JS_HAS_OBJ_PROTO_PROP */
 277
 278JSBool
 279js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
 280{
 281    JSSetSlotRequest ssr;
 282    JSRuntime *rt;
 283
 284    /* Optimize the null case to avoid the unnecessary overhead of js_GC. */
 285    if (!pobj) {
 286        JS_LOCK_OBJ(cx, obj);
 287        if (slot == JSSLOT_PROTO && !js_GetMutableScope(cx, obj)) {
 288            JS_UNLOCK_OBJ(cx, obj);
 289            return JS_FALSE;
 290        }
 291        LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_NULL);
 292        JS_UNLOCK_OBJ(cx, obj);
 293        return JS_TRUE;
 294    }
 295
 296    ssr.obj = obj;
 297    ssr.pobj = pobj;
 298    ssr.slot = (uint16) slot;
 299    ssr.errnum = (uint16) JSMSG_NOT_AN_ERROR;
 300
 301    rt = cx->runtime;
 302    JS_LOCK_GC(rt);
 303    ssr.next = rt->setSlotRequests;
 304    rt->setSlotRequests = &ssr;
 305    for (;;) {
 306        js_GC(cx, GC_SET_SLOT_REQUEST);
 307        JS_UNLOCK_GC(rt);
 308        if (!rt->setSlotRequests)
 309            break;
 310        JS_LOCK_GC(rt);
 311    }
 312
 313    if (ssr.errnum != JSMSG_NOT_AN_ERROR) {
 314        if (ssr.errnum == JSMSG_OUT_OF_MEMORY) {
 315            JS_ReportOutOfMemory(cx);
 316        } else {
 317            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, ssr.errnum,
 318#if JS_HAS_OBJ_PROTO_PROP
 319                                 object_props[slot].name
 320#else
 321                                 (slot == JSSLOT_PROTO) ? js_proto_str
 322                                                        : js_parent_str
 323#endif
 324                                 );
 325        }
 326        return JS_FALSE;
 327    }
 328
 329    // Maintain the "any Array prototype has indexed properties hazard" flag.
 330    if (slot == JSSLOT_PROTO &&
 331        OBJ_IS_ARRAY(cx, pobj) &&
 332        pobj->fslots[JSSLOT_ARRAY_LENGTH] != 0) {
 333        rt->anyArrayProtoHasElement = JS_TRUE;
 334    }
 335    return JS_TRUE;
 336}
 337
 338static JSHashNumber
 339js_hash_object(const void *key)
 340{
 341    return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
 342}
 343
 344static JSHashEntry *
 345MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
 346{
 347    JSSharpObjectMap *map;
 348    JSHashTable *table;
 349    JSHashNumber hash;
 350    JSHashEntry **hep, *he;
 351    jsatomid sharpid;
 352    JSIdArray *ida;
 353    JSBool ok;
 354    jsint i, length;
 355    jsid id;
 356#if JS_HAS_GETTER_SETTER
 357    JSObject *obj2;
 358    JSProperty *prop;
 359    uintN attrs;
 360#endif
 361    jsval val;
 362
 363    JS_CHECK_RECURSION(cx, return NULL);
 364
 365    map = &cx->sharpObjectMap;
 366    table = map->table;
 367    hash = js_hash_object(obj);
 368    hep = JS_HashTableRawLookup(table, hash, obj);
 369    he = *hep;
 370    if (!he) {
 371        sharpid = 0;
 372        he = JS_HashTableRawAdd(table, hep, hash, obj,
 373                                JS_UINT32_TO_PTR(sharpid));
 374        if (!he) {
 375            JS_ReportOutOfMemory(cx);
 376            return NULL;
 377        }
 378
 379        /*
 380         * Increment map->depth to protect js_EnterSharpObject from reentering
 381         * itself badly.  Without this fix, if we reenter the basis case where
 382         * map->depth == 0, when unwinding the inner call we will destroy the
 383         * newly-created hash table and crash.
 384         */
 385        ++map->depth;
 386        ida = JS_Enumerate(cx, obj);
 387        --map->depth;
 388        if (!ida)
 389            return NULL;
 390
 391        ok = JS_TRUE;
 392        for (i = 0, length = ida->length; i < length; i++) {
 393            id = ida->vector[i];
 394#if JS_HAS_GETTER_SETTER
 395            ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
 396            if (!ok)
 397                break;
 398            if (!prop)
 399                continue;
 400            ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
 401            if (ok) {
 402                if (OBJ_IS_NATIVE(obj2) &&
 403                    (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
 404                    val = JSVAL_NULL;
 405                    if (attrs & JSPROP_GETTER)
 406                        val = (jsval) ((JSScopeProperty*)prop)->getter;
 407                    if (attrs & JSPROP_SETTER) {
 408                        if (val != JSVAL_NULL) {
 409                            /* Mark the getter, then set val to setter. */
 410                            ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
 411                                                   NULL)
 412                                  != NULL);
 413                        }
 414                        val = (jsval) ((JSScopeProperty*)prop)->setter;
 415                    }
 416                } else {
 417                    ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
 418                }
 419            }
 420            OBJ_DROP_PROPERTY(cx, obj2, prop);
 421#else
 422            ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
 423#endif
 424            if (!ok)
 425                break;
 426            if (!JSVAL_IS_PRIMITIVE(val) &&
 427                !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
 428                ok = JS_FALSE;
 429                break;
 430            }
 431        }
 432        if (!ok || !idap)
 433            JS_DestroyIdArray(cx, ida);
 434        if (!ok)
 435            return NULL;
 436    } else {
 437        sharpid = JS_PTR_TO_UINT32(he->value);
 438        if (sharpid == 0) {
 439            sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
 440            he->value = JS_UINT32_TO_PTR(sharpid);
 441        }
 442        ida = NULL;
 443    }
 444    if (idap)
 445        *idap = ida;
 446    return he;
 447}
 448
 449JSHashEntry *
 450js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
 451                    jschar **sp)
 452{
 453    JSSharpObjectMap *map;
 454    JSHashTable *table;
 455    JSIdArray *ida;
 456    JSHashNumber hash;
 457    JSHashEntry *he, **hep;
 458    jsatomid sharpid;
 459    char buf[20];
 460    size_t len;
 461
 462    if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP))
 463        return NULL;
 464
 465    /* Set to null in case we return an early error. */
 466    *sp = NULL;
 467    map = &cx->sharpObjectMap;
 468    table = map->table;
 469    if (!table) {
 470        table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
 471                                JS_CompareValues, NULL, NULL);
 472        if (!table) {
 473            JS_ReportOutOfMemory(cx);
 474            return NULL;
 475        }
 476        map->table = table;
 477        JS_KEEP_ATOMS(cx->runtime);
 478    }
 479
 480    /* From this point the control must flow either through out: or bad:. */
 481    ida = NULL;
 482    if (map->depth == 0) {
 483        he = MarkSharpObjects(cx, obj, &ida);
 484        if (!he)
 485            goto bad;
 486        JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
 487        if (!idap) {
 488            JS_DestroyIdArray(cx, ida);
 489            ida = NULL;
 490        }
 491    } else {
 492        hash = js_hash_object(obj);
 493        hep = JS_HashTableRawLookup(table, hash, obj);
 494        he = *hep;
 495
 496        /*
 497         * It's possible that the value of a property has changed from the
 498         * first time the object's properties are traversed (when the property
 499         * ids are entered into the hash table) to the second (when they are
 500         * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
 501         * idempotent.
 502         */
 503        if (!he) {
 504            he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
 505            if (!he) {
 506                JS_ReportOutOfMemory(cx);
 507                goto bad;
 508            }
 509            sharpid = 0;
 510            goto out;
 511        }
 512    }
 513
 514    sharpid = JS_PTR_TO_UINT32(he->value);
 515    if (sharpid != 0) {
 516        len = JS_snprintf(buf, sizeof buf, "#%u%c",
 517                          sharpid >> SHARP_ID_SHIFT,
 518                          (sharpid & SHARP_BIT) ? '#' : '=');
 519        *sp = js_InflateString(cx, buf, &len);
 520        if (!*sp) {
 521            if (ida)
 522                JS_DestroyIdArray(cx, ida);
 523            goto bad;
 524        }
 525    }
 526
 527out:
 528    JS_ASSERT(he);
 529    if ((sharpid & SHARP_BIT) == 0) {
 530        if (idap && !ida) {
 531            ida = JS_Enumerate(cx, obj);
 532            if (!ida) {
 533                if (*sp) {
 534                    JS_free(cx, *sp);
 535                    *sp = NULL;
 536                }
 537                goto bad;
 538            }
 539        }
 540        map->depth++;
 541    }
 542
 543    if (idap)
 544        *idap = ida;
 545    return he;
 546
 547bad:
 548    /* Clean up the sharpObjectMap table on outermost error. */
 549    if (map->depth == 0) {
 550        JS_UNKEEP_ATOMS(cx->runtime);
 551        map->sharpgen = 0;
 552        JS_HashTableDestroy(map->table);
 553        map->table = NULL;
 554    }
 555    return NULL;
 556}
 557
 558void
 559js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
 560{
 561    JSSharpObjectMap *map;
 562    JSIdArray *ida;
 563
 564    map = &cx->sharpObjectMap;
 565    JS_ASSERT(map->depth > 0);
 566    if (--map->depth == 0) {
 567        JS_UNKEEP_ATOMS(cx->runtime);
 568        map->sharpgen = 0;
 569        JS_HashTableDestroy(map->table);
 570        map->table = NULL;
 571    }
 572    if (idap) {
 573        ida = *idap;
 574        if (ida) {
 575            JS_DestroyIdArray(cx, ida);
 576            *idap = NULL;
 577        }
 578    }
 579}
 580
 581static intN
 582gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
 583{
 584    JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
 585                          "sharp table entry");
 586    return JS_DHASH_NEXT;
 587}
 588
 589void
 590js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
 591{
 592    JS_ASSERT(map->depth > 0);
 593    JS_ASSERT(map->table);
 594
 595    /*
 596     * During recursive calls to MarkSharpObjects a non-native object or
 597     * object with a custom getProperty method can potentially return an
 598     * unrooted value or even cut from the object graph an argument of one of
 599     * MarkSharpObjects recursive invocations. So we must protect map->table
 600     * entries against GC.
 601     *
 602     * We can not simply use JSTempValueRooter to mark the obj argument of
 603     * MarkSharpObjects during recursion as we have to protect *all* entries
 604     * in JSSharpObjectMap including those that contains otherwise unreachable
 605     * objects just allocated through custom getProperty. Otherwise newer
 606     * allocations can re-use the address of an object stored in the hashtable
 607     * confusing js_EnterSharpObject. So to address the problem we simply
 608     * mark all objects from map->table.
 609     *
 610     * An alternative "proper" solution is to use JSTempValueRooter in
 611     * MarkSharpObjects with code to remove during finalization entries
 612     * with otherwise unreachable objects. But this is way too complex
 613     * to justify spending efforts.
 614     */
 615    JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
 616}
 617
 618#if JS_HAS_TOSOURCE
 619static JSBool
 620obj_toSource(JSContext *cx, uintN argc, jsval *vp)
 621{
 622    JSBool ok, outermost;
 623    JSObject *obj;
 624    JSHashEntry *he;
 625    JSIdArray *ida;
 626    jschar *chars, *ochars, *vsharp;
 627    const jschar *idstrchars, *vchars;
 628    size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
 629    const char *comma;
 630    jsint i, j, length, valcnt;
 631    jsid id;
 632#if JS_HAS_GETTER_SETTER
 633    JSObject *obj2;
 634    JSProperty *prop;
 635    uintN attrs;
 636#endif
 637    jsval *val;
 638    jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
 639    JSTempValueRooter tvr;
 640    JSString *gsopold[2];
 641    JSString *gsop[2];
 642    JSString *idstr, *valstr, *str;
 643
 644    JS_CHECK_RECURSION(cx, return JS_FALSE);
 645
 646    MUST_FLOW_THROUGH("out");
 647    JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr);
 648
 649    /* If outermost, we need parentheses to be an expression, not a block. */
 650    outermost = (cx->sharpObjectMap.depth == 0);
 651    obj = JS_THIS_OBJECT(cx, vp);
 652    if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
 653        ok = JS_FALSE;
 654        goto out;
 655    }
 656    if (IS_SHARP(he)) {
 657        /*
 658         * We didn't enter -- obj is already "sharp", meaning we've visited it
 659         * already in our depth first search, and therefore chars contains a
 660         * string of the form "#n#".
 661         */
 662        JS_ASSERT(!ida);
 663#if JS_HAS_SHARP_VARS
 664        nchars = js_strlen(chars);
 665#else
 666        chars[0] = '{';
 667        chars[1] = '}';
 668        chars[2] = 0;
 669        nchars = 2;
 670#endif
 671        goto make_string;
 672    }
 673    JS_ASSERT(ida);
 674    ok = JS_TRUE;
 675
 676    if (!chars) {
 677        /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
 678        chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
 679        nchars = 0;
 680        if (!chars)
 681            goto error;
 682        if (outermost)
 683            chars[nchars++] = '(';
 684    } else {
 685        /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
 686        MAKE_SHARP(he);
 687        nchars = js_strlen(chars);
 688        chars = (jschar *)
 689            realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
 690        if (!chars) {
 691            free(ochars);
 692            goto error;
 693        }
 694        if (outermost) {
 695            /*
 696             * No need for parentheses around the whole shebang, because #n=
 697             * unambiguously begins an object initializer, and never a block
 698             * statement.
 699             */
 700            outermost = JS_FALSE;
 701        }
 702    }
 703
 704    chars[nchars++] = '{';
 705
 706    comma = NULL;
 707
 708    /*
 709     * We have four local roots for cooked and raw value GC safety.  Hoist the
 710     * "localroot + 2" out of the loop using the val local, which refers to
 711     * the raw (unconverted, "uncooked") values.
 712     */
 713    val = localroot + 2;
 714
 715    for (i = 0, length = ida->length; i < length; i++) {
 716        JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
 717
 718        /* Get strings for id and value and GC-root them via vp. */
 719        id = ida->vector[i];
 720
 721#if JS_HAS_GETTER_SETTER
 722        ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
 723        if (!ok)
 724            goto error;
 725#endif
 726
 727        /*
 728         * Convert id to a jsval and then to a string.  Decide early whether we
 729         * prefer get/set or old getter/setter syntax.
 730         */
 731        idstr = js_ValueToString(cx, ID_TO_VALUE(id));
 732        if (!idstr) {
 733            ok = JS_FALSE;
 734            OBJ_DROP_PROPERTY(cx, obj2, prop);
 735            goto error;
 736        }
 737        *vp = STRING_TO_JSVAL(idstr);                   /* local root */
 738        idIsLexicalIdentifier = js_IsIdentifier(idstr);
 739        needOldStyleGetterSetter =
 740            !idIsLexicalIdentifier ||
 741            js_CheckKeyword(JSSTRING_CHARS(idstr),
 742                            JSSTRING_LENGTH(idstr)) != TOK_EOF;
 743
 744#if JS_HAS_GETTER_SETTER
 745
 746        valcnt = 0;
 747        if (prop) {
 748            ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
 749            if (!ok) {
 750                OBJ_DROP_PROPERTY(cx, obj2, prop);
 751                goto error;
 752            }
 753            if (OBJ_IS_NATIVE(obj2) &&
 754                (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
 755                if (attrs & JSPROP_GETTER) {
 756                    val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
 757                    gsopold[valcnt] =
 758                        ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
 759                    gsop[valcnt] =
 760                        ATOM_TO_STRING(cx->runtime->atomState.getAtom);
 761
 762                    valcnt++;
 763                }
 764                if (attrs & JSPROP_SETTER) {
 765                    val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
 766                    gsopold[valcnt] =
 767                        ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
 768                    gsop[valcnt] =
 769                        ATOM_TO_STRING(cx->runtime->atomState.setAtom);
 770
 771                    valcnt++;
 772                }
 773            } else {
 774                valcnt = 1;
 775                gsop[0] = NULL;
 776                gsopold[0] = NULL;
 777                ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
 778            }
 779            OBJ_DROP_PROPERTY(cx, obj2, prop);
 780        }
 781
 782#else  /* !JS_HAS_GETTER_SETTER */
 783
 784        /*
 785         * We simplify the source code at the price of minor dead code bloat in
 786         * the ECMA version (for testing only, see jsversion.h).  The null
 787         * default values in gsop[j] suffice to disable non-ECMA getter and
 788         * setter code.
 789         */
 790        valcnt = 1;
 791        gsop[0] = NULL;
 792        gsopold[0] = NULL;
 793        ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
 794
 795#endif /* !JS_HAS_GETTER_SETTER */
 796
 797        if (!ok)
 798            goto error;
 799
 800        /*
 801         * If id is a string that's not an identifier, then it needs to be
 802         * quoted.  Also, negative integer ids must be quoted.
 803         */
 804        if (JSID_IS_ATOM(id)
 805            ? !idIsLexicalIdentifier
 806            : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
 807            idstr = js_QuoteString(cx, idstr, (jschar)'\'');
 808            if (!idstr) {
 809                ok = JS_FALSE;
 810                goto error;
 811            }
 812            *vp = STRING_TO_JSVAL(idstr);               /* local root */
 813        }
 814        JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength);
 815
 816        for (j = 0; j < valcnt; j++) {
 817            /* Convert val[j] to its canonical source form. */
 818            valstr = js_ValueToSource(cx, val[j]);
 819            if (!valstr) {
 820                ok = JS_FALSE;
 821                goto error;
 822            }
 823            localroot[j] = STRING_TO_JSVAL(valstr);     /* local root */
 824            JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength);
 825
 826            if (vchars[0] == '#')
 827                needOldStyleGetterSetter = JS_TRUE;
 828
 829            if (needOldStyleGetterSetter)
 830                gsop[j] = gsopold[j];
 831
 832            /* If val[j] is a non-sharp object, consider sharpening it. */
 833            vsharp = NULL;
 834            vsharplength = 0;
 835#if JS_HAS_SHARP_VARS
 836            if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
 837                he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
 838                                         &vsharp);
 839                if (!he) {
 840                    ok = JS_FALSE;
 841                    goto error;
 842                }
 843                if (IS_SHARP(he)) {
 844                    vchars = vsharp;
 845                    vlength = js_strlen(vchars);
 846                    needOldStyleGetterSetter = JS_TRUE;
 847                    gsop[j] = gsopold[j];
 848                } else {
 849                    if (vsharp) {
 850                        vsharplength = js_strlen(vsharp);
 851                        MAKE_SHARP(he);
 852                        needOldStyleGetterSetter = JS_TRUE;
 853                        gsop[j] = gsopold[j];
 854                    }
 855                    js_LeaveSharpObject(cx, NULL);
 856                }
 857            }
 858#endif
 859
 860#ifndef OLD_GETTER_SETTER
 861            /*
 862             * Remove '(function ' from the beginning of valstr and ')' from the
 863             * end so that we can put "get" in front of the function definition.
 864             */
 865            if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
 866                !needOldStyleGetterSetter) {
 867                JSFunction *fun = JS_ValueToFunction(cx, val[j]);
 868                const jschar *start = vchars;
 869                const jschar *end = vchars + vlength;
 870
 871                uint8 parenChomp = 0;
 872                if (vchars[0] == '(') {
 873                    vchars++;
 874                    parenChomp = 1;
 875                }
 876
 877                /*
 878                 * Try to jump "getter" or "setter" keywords, if we suspect
 879                 * they might appear here.  This code can be confused by people
 880                 * defining Function.prototype.toString, so let's be cautious.
 881                 */
 882                if (JSFUN_GETTER_TEST(fun->flags) ||
 883                    JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */
 884                    const jschar *tmp = js_strchr_limit(vchars, ' ', end);
 885                    if (tmp)
 886                        vchars = tmp + 1;
 887                }
 888
 889                /* Try to jump "function" keyword. */
 890                if (vchars)
 891                    vchars = js_strchr_limit(vchars, ' ', end);
 892
 893                if (vchars) {
 894                    if (*vchars == ' ')
 895                        vchars++;
 896                    vlength = end - vchars - parenChomp;
 897                } else {
 898                    gsop[j] = NULL;
 899                    vchars = start;
 900                }
 901            }
 902#else
 903            needOldStyleGetterSetter = JS_TRUE;
 904            gsop[j] = gsopold[j];
 905#endif
 906
 907#define SAFE_ADD(n)                                                          \
 908    JS_BEGIN_MACRO                                                           \
 909        size_t n_ = (n);                                                     \
 910        curlen += n_;                                                        \
 911        if (curlen < n_)                                                     \
 912            goto overflow;                                                   \
 913    JS_END_MACRO
 914
 915            curlen = nchars;
 916            if (comma)
 917                SAFE_ADD(2);
 918            SAFE_ADD(idstrlength + 1);
 919            if (gsop[j])
 920                SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
 921            SAFE_ADD(vsharplength);
 922            SAFE_ADD(vlength);
 923            /* Account for the trailing null. */
 924            SAFE_ADD((outermost ? 2 : 1) + 1);
 925#undef SAFE_ADD
 926
 927            if (curlen > (size_t)-1 / sizeof(jschar))
 928                goto overflow;
 929
 930            /* Allocate 1 + 1 at end for closing brace and terminating 0. */
 931            chars = (jschar *)
 932                realloc((ochars = chars), curlen * sizeof(jschar));
 933            if (!chars) {
 934                /* Save code space on error: let JS_free ignore null vsharp. */
 935                JS_free(cx, vsharp);
 936                free(ochars);
 937                goto error;
 938            }
 939
 940            if (comma) {
 941                chars[nchars++] = comma[0];
 942                chars[nchars++] = comma[1];
 943            }
 944            comma = ", ";
 945
 946            if (needOldStyleGetterSetter) {
 947                js_strncpy(&chars[nchars], idstrchars, idstrlength);
 948                nchars += idstrlength;
 949                if (gsop[j]) {
 950                    chars[nchars++] = ' ';
 951                    gsoplength = JSSTRING_LENGTH(gsop[j]);
 952                    js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
 953                               gsoplength);
 954                    nchars += gsoplength;
 955                }
 956                chars[nchars++] = ':';
 957            } else {  /* New style "decompilation" */
 958                if (gsop[j]) {
 959                    gsoplength = JSSTRING_LENGTH(gsop[j]);
 960                    js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
 961                               gsoplength);
 962                    nchars += gsoplength;
 963                    chars[nchars++] = ' ';
 964                }
 965                js_strncpy(&chars[nchars], idstrchars, idstrlength);
 966                nchars += idstrlength;
 967                /* Extraneous space after id here will be extracted later */
 968                chars[nchars++] = gsop[j] ? ' ' : ':';
 969            }
 970
 971            if (vsharplength) {
 972                js_strncpy(&chars[nchars], vsharp, vsharplength);
 973                nchars += vsharplength;
 974            }
 975            js_strncpy(&chars[nchars], vchars, vlength);
 976            nchars += vlength;
 977
 978            if (vsharp)
 979                JS_free(cx, vsharp);
 980        }
 981    }
 982
 983    chars[nchars++] = '}';
 984    if (outermost)
 985        chars[nchars++] = ')';
 986    chars[nchars] = 0;
 987
 988  error:
 989    js_LeaveSharpObject(cx, &ida);
 990
 991    if (!ok) {
 992        if (chars)
 993            free(chars);
 994        goto out;
 995    }
 996
 997    if (!chars) {
 998        JS_ReportOutOfMemory(cx);
 999        ok = JS_FALSE;
1000        goto out;
1001    }
1002  make_string:
1003    str = js_NewString(cx, chars, nchars);
1004    if (!str) {
1005        free(chars);
1006        ok = JS_FALSE;
1007        goto out;
1008    }
1009    *vp = STRING_TO_JSVAL(str);
1010    ok = JS_TRUE;
1011  out:
1012    JS_POP_TEMP_ROOT(cx, &tvr);
1013    return ok;
1014
1015  overflow:
1016    JS_free(cx, vsharp);
1017    free(chars);
1018    chars = NULL;
1019    goto error;
1020}
1021#endif /* JS_HAS_TOSOURCE */
1022
1023static JSBool
1024obj_toString(JSContext *cx, uintN argc, jsval *vp)
1025{
1026    JSObject *obj;
1027    jschar *chars;
1028    size_t nchars;
1029    const char *clazz, *prefix;
1030    JSString *str;
1031
1032    obj = JS_THIS_OBJECT(cx, vp);
1033    if (!obj)
1034        return JS_FALSE;
1035    obj = js_GetWrappedObject(cx, obj);
1036    clazz = OBJ_GET_CLASS(cx, obj)->name;
1037    nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
1038    chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
1039    if (!chars)
1040        return JS_FALSE;
1041
1042    prefix = "[object ";
1043    nchars = 0;
1044    while ((chars[nchars] = (jschar)*prefix) != 0)
1045        nchars++, prefix++;
1046    while ((chars[nchars] = (jschar)*clazz) != 0)
1047        nchars++, clazz++;
1048    chars[nchars++] = ']';
1049    chars[nchars] = 0;
1050
1051    str = js_NewString(cx, chars, nchars);
1052    if (!str) {
1053        JS_free(cx, chars);
1054        return JS_FALSE;
1055    }
1056    *vp = STRING_TO_JSVAL(str);
1057    return JS_TRUE;
1058}
1059
1060static JSBool
1061obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1062{
1063    jsval thisv;
1064    JSString *str;
1065
1066    thisv = JS_THIS(cx, vp);
1067    if (JSVAL_IS_NULL(thisv))
1068        return JS_FALSE;
1069
1070    str = js_ValueToString(cx, thisv);
1071    if (!str)
1072        return JS_FALSE;
1073
1074    *vp = STRING_TO_JSVAL(str);
1075    return JS_TRUE;
1076}
1077
1078static JSBool
1079obj_valueOf(JSContext *cx, uintN argc, jsval *vp)
1080{
1081    *vp = JS_THIS(cx, vp);
1082    return !JSVAL_IS_NULL(*vp);
1083}
1084
1085/*
1086 * Check whether principals subsumes scopeobj's principals, and return true
1087 * if so (or if scopeobj has no principals, for backward compatibility with
1088 * the JS API, which does not require principals), and false otherwise.
1089 */
1090JSBool
1091js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
1092                         JSPrincipals *principals, JSAtom *caller)
1093{
1094    JSSecurityCallbacks *callbacks;
1095    JSPrincipals *scopePrincipals;
1096    const char *callerstr;
1097
1098    callbacks = JS_GetSecurityCallbacks(cx);
1099    if (callbacks && callbacks->findObjectPrincipals) {
1100        scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
1101        if (!principals || !scopePrincipals ||
1102            !principals->subsume(principals, scopePrincipals)) {
1103            callerstr = js_AtomToPrintableString(cx, caller);
1104            if (!callerstr)
1105                return JS_FALSE;
1106            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1107                                 JSMSG_BAD_INDIRECT_CALL, callerstr);
1108            return JS_FALSE;
1109        }
1110    }
1111    return JS_TRUE;
1112}
1113
1114JSObject *
1115js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
1116{
1117    JSClass *clasp;
1118    JSExtendedClass *xclasp;
1119    JSObject *inner;
1120
1121    if (!scopeobj)
1122        goto bad;
1123
1124    OBJ_TO_INNER_OBJECT(cx, scopeobj);
1125    if (!scopeobj)
1126        return NULL;
1127
1128    inner = scopeobj;
1129
1130    /* XXX This is an awful gross hack. */
1131    while (scopeobj) {
1132        clasp = OBJ_GET_CLASS(cx, scopeobj);
1133        if (clasp->flags & JSCLASS_IS_EXTENDED) {
1134            xclasp = (JSExtendedClass*)clasp;
1135            if (xclasp->innerObject &&
1136                xclasp->innerObject(cx, scopeobj) != scopeobj) {
1137                goto bad;
1138            }
1139        }
1140
1141        scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1142    }
1143
1144    return inner;
1145
1146bad:
1147    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1148                         JSMSG_BAD_INDIRECT_CALL, caller);
1149    return NULL;
1150}
1151
1152const char *
1153js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
1154                   JSPrincipals *principals, uintN *linenop)
1155{
1156    uint32 flags;
1157#ifdef DEBUG
1158    JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
1159#endif
1160
1161    JS_ASSERT(principals || !(callbacks  && callbacks->findObjectPrincipals));
1162    flags = JS_GetScriptFilenameFlags(caller->script);
1163    if ((flags & JSFILENAME_PROTECTED) &&
1164        principals &&
1165        strcmp(principals->codebase, "[System Principal]")) {
1166        *linenop = 0;
1167        return principals->codebase;
1168    }
1169
1170    if (caller->regs && *caller->regs->pc == JSOP_EVAL) {
1171        JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO);
1172        *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH);
1173    } else {
1174        *linenop = js_FramePCToLineNumber(cx, caller);
1175    }
1176    return caller->script->filename;
1177}
1178
1179static JSBool
1180obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1181{
1182    JSStackFrame *fp, *caller;
1183    JSBool indirectCall;
1184    JSObject *scopeobj;
1185    JSString *str;
1186    const char *file;
1187    uintN line;
1188    JSPrincipals *principals;
1189    uint32 tcflags;
1190    JSScript *script;
1191    JSBool ok;
1192#if JS_HAS_EVAL_THIS_SCOPE
1193    JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1194    JSObject *setCallerScopeChain = NULL;
1195    JSBool setCallerVarObj = JS_FALSE;
1196#endif
1197
1198    fp = cx->fp;
1199    caller = JS_GetScriptedCaller(cx, fp);
1200    indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL);
1201
1202    /*
1203     * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
1204     * calls that attempt to use a non-global object as the "with" object in
1205     * the former indirect case.
1206     */
1207    scopeobj = OBJ_GET_PARENT(cx, obj);
1208    if (scopeobj) {
1209        scopeobj = js_GetWrappedObject(cx, obj);
1210        scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1211    }
1212    if (indirectCall || scopeobj) {
1213        uintN flags = scopeobj
1214                      ? JSREPORT_ERROR
1215                      : JSREPORT_STRICT | JSREPORT_WARNING;
1216        if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
1217                                          JSMSG_BAD_INDIRECT_CALL,
1218                                          js_eval_str)) {
1219            return JS_FALSE;
1220        }
1221    }
1222
1223    if (!JSVAL_IS_STRING(argv[0])) {
1224        *rval = argv[0];
1225        return JS_TRUE;
1226    }
1227
1228    /*
1229     * If the caller is a lightweight function and doesn't have a variables
1230     * object, then we need to provide one for the compiler to stick any
1231     * declared (var) variables into.
1232     */
1233    if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
1234        return JS_FALSE;
1235
1236    /* eval no longer takes an optional trailing argument. */
1237    if (argc >= 2 &&
1238        !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT,
1239                                      js_GetErrorMessage, NULL,
1240                                      JSMSG_EVAL_ARITY)) {
1241        return JS_FALSE;
1242    }
1243
1244    /* From here on, control must exit through label out with ok set. */
1245    MUST_FLOW_THROUGH("out");
1246    if (!scopeobj) {
1247#if JS_HAS_EVAL_THIS_SCOPE
1248        /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1249        if (indirectCall) {
1250            callerScopeChain = js_GetScopeChain(cx, caller);
1251            if (!callerScopeChain) {
1252                ok = JS_FALSE;
1253                goto out;
1254            }
1255            OBJ_TO_INNER_OBJECT(cx, obj);
1256            if (!obj) {
1257                ok = JS_FALSE;
1258                goto out;
1259            }
1260            if (obj != callerScopeChain) {
1261                ok = js_CheckPrincipalsAccess(cx, obj,
1262                                              caller->script->principals,
1263                                              cx->runtime->atomState.evalAtom);
1264                if (!ok)
1265                    goto out;
1266
1267                scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
1268                if (!scopeobj) {
1269                    ok = JS_FALSE;
1270                    goto out;
1271                }
1272
1273                /* Set fp->scopeChain too, for the compiler. */
1274                caller->scopeChain = fp->scopeChain = scopeobj;
1275
1276                /* Remember scopeobj so we can null its private when done. */
1277                setCallerScopeChain = scopeobj;
1278            }
1279
1280            callerVarObj = caller->varobj;
1281            if (obj != callerVarObj) {
1282                /* Set fp->varobj too, for the compiler. */
1283                caller->varobj = fp->varobj = obj;
1284                setCallerVarObj = JS_TRUE;
1285            }
1286        }
1287#endif
1288
1289        /*
1290         * Compile using caller's current scope object.
1291         *
1292         * NB: This means that native callers (who reach this point through
1293         * the C API) must use the two parameter form.
1294         */
1295        if (caller) {
1296            scopeobj = js_GetScopeChain(cx, caller);
1297            if (!scopeobj) {
1298                ok = JS_FALSE;
1299                goto out;
1300            }
1301        }
1302    }
1303
1304    /* Ensure we compile this eval with the right object in the scope chain. */
1305    scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1306    if (!scopeobj) {
1307        ok = JS_FALSE;
1308        goto out;
1309    }
1310
1311    str = JSVAL_TO_STRING(argv[0]);
1312    if (caller) {
1313        principals = JS_EvalFramePrincipals(cx, fp, caller);
1314        file = js_ComputeFilename(cx, caller, principals, &line);
1315    } else {
1316        file = NULL;
1317        line = 0;
1318        principals = NULL;
1319    }
1320
1321    tcflags = TCF_COMPILE_N_GO;
1322    if (caller)
1323        tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
1324    script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
1325                              JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
1326                              NULL, file, line);
1327    if (!script) {
1328        ok = JS_FALSE;
1329        goto out;
1330    }
1331
1332    if (argc < 2) {
1333        /* Execute using caller's new scope object (might be a Call object). */
1334        if (caller)
1335            scopeobj = caller->scopeChain;
1336    }
1337
1338    /*
1339     * Belt-and-braces: check that the lesser of eval's principals and the
1340     * caller's principals has access to scopeobj.
1341     */
1342    ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1343                                  cx->runtime->atomState.evalAtom);
1344    if (ok)
1345        ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1346
1347    script->u.nextToGC = JS_SCRIPTS_TO_GC(cx);
1348    JS_SCRIPTS_TO_GC(cx) = script;
1349#ifdef CHECK_SCRIPT_OWNER
1350    script->owner = NULL;
1351#endif
1352
1353out:
1354#if JS_HAS_EVAL_THIS_SCOPE
1355    /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1356    if (setCallerScopeChain) {
1357        caller->scopeChain = callerScopeChain;
1358        JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
1359        JS_SetPrivate(cx, setCallerScopeChain, NULL);
1360    }
1361    if (setCallerVarObj)
1362        caller->varobj = callerVarObj;
1363#endif
1364    return ok;
1365}
1366
1367#if JS_HAS_OBJ_WATCHPOINT
1368
1369static JSBool
1370obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1371                  void *closure)
1372{
1373    JSObject *callable;
1374    JSSecurityCallbacks *callbacks;
1375    JSStackFrame *caller;
1376    JSPrincipals *subject, *watcher;
1377    JSResolvingKey key;
1378    JSResolvingEntry *entry;
1379    uint32 generation;
1380    jsval argv[3];
1381    JSBool ok;
1382
1383    callable = (JSObject *) closure;
1384
1385    callbacks = JS_GetSecurityCallbacks(cx);
1386    if (callbacks && callbacks->findObjectPrincipals) {
1387        /* Skip over any obj_watch_* frames between us and the real subject. */
1388        caller = JS_GetScriptedCaller(cx, cx->fp);
1389        if (caller) {
1390            /*
1391             * Only call the watch handler if the watcher is allowed to watch
1392             * the currently executing script.
1393             */
1394            watcher = callbacks->findObjectPrincipals(cx, callable);
1395            subject = JS_StackFramePrincipals(cx, caller);
1396
1397            if (watcher && subject && !watcher->subsume(watcher, subject)) {
1398                /* Silently don't call the watch handler. */
1399                return JS_TRUE;
1400            }
1401        }
1402    }
1403
1404    /* Avoid recursion on (obj, id) already being watched on cx. */
1405    key.obj = obj;
1406    key.id = id;
1407    if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1408        return JS_FALSE;
1409    if (!entry)
1410        return JS_TRUE;
1411    generation = cx->resolvingTable->generation;
1412
1413    argv[0] = id;
1414    argv[1] = old;
1415    argv[2] = *nvp;
1416    ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
1417    js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1418    return ok;
1419}
1420
1421static JSBool
1422obj_watch(JSContext *cx, uintN argc, jsval *vp)
1423{
1424    JSObject *callable;
1425    jsval userid, value;
1426    jsid propid;
1427    JSObject *obj;
1428    uintN attrs;
1429
1430    if (argc <= 1) {
1431        js_ReportMissingArg(cx, vp, 1);
1432        return JS_FALSE;
1433    }
1434
1435    callable = js_ValueToCallableObject(cx, &vp[3], 0);
1436    if (!callable)
1437        return JS_FALSE;
1438
1439    /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1440    userid = vp[2];
1441    if (!JS_ValueToId(cx, userid, &propid))
1442        return JS_FALSE;
1443
1444    obj = JS_THIS_OBJECT(cx, vp);
1445    if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1446        return JS_FALSE;
1447    if (attrs & JSPROP_READONLY)
1448        return JS_TRUE;
1449    *vp = JSVAL_VOID;
1450
1451    if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj))
1452        return JS_FALSE;
1453    return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
1454}
1455
1456static JSBool
1457obj_unwatch(JSContext *cx, uintN argc, jsval *vp)
1458{
1459    JSObject *obj;
1460
1461    obj = JS_THIS_OBJECT(cx, vp);
1462    if (!obj)
1463        return JS_FALSE;
1464    *vp = JSVAL_VOID;
1465    return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID,
1466                              NULL, NULL);
1467}
1468
1469#endif /* JS_HAS_OBJ_WATCHPOINT */
1470
1471/*
1472 * Prototype and property query methods, to complement the 'in' and
1473 * 'instanceof' operators.
1474 */
1475
1476/* Proposed ECMA 15.2.4.5. */
1477static JSBool
1478obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
1479{
1480    JSObject *obj;
1481
1482    obj = JS_THIS_OBJECT(cx, vp);
1483    return obj &&
1484           js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
1485}
1486
1487JSBool
1488js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
1489                        jsval *vp)
1490{
1491    jsid id;
1492    JSObject *obj;
1493
1494    if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1495        return JS_FALSE;
1496    obj = JS_THIS_OBJECT(cx, vp);
1497    return obj && js_HasOwnProperty(cx, lookup, obj, id, vp);
1498}
1499
1500JSBool
1501js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
1502                  jsval *vp)
1503{
1504    JSObject *obj2;
1505    JSProperty *prop;
1506    JSScopeProperty *sprop;
1507
1508    if (!lookup(cx, obj, id, &obj2, &prop))
1509        return JS_FALSE;
1510    if (!prop) {
1511        *vp = JSVAL_FALSE;
1512    } else if (obj2 == obj) {
1513        *vp = JSVAL_TRUE;
1514    } else {
1515        JSClass *clasp;
1516        JSExtendedClass *xclasp;
1517        JSObject *outer;
1518
1519        clasp = OBJ_GET_CLASS(cx, obj2);
1520        if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
1521            !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
1522            outer = NULL;
1523        } else {
1524            outer = xclasp->outerObject(cx, obj2);
1525            if (!outer)
1526                return JS_FALSE;
1527        }
1528        if (outer == obj) {
1529            *vp = JSVAL_TRUE;
1530        } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
1531            /*
1532             * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1533             * delegated property makes that property appear to be direct in
1534             * all delegating instances of the same native class.  This hack
1535             * avoids bloating every function instance with its own 'length'
1536             * (AKA 'arity') property.  But it must not extend across class
1537             * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1538             *
1539             * It's not really a hack, of course: a permanent property can't
1540             * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1541             * any instance, prototype or delegating".  Without a slot, and
1542             * without the ability to remove and recreate (with differences)
1543             * the property, there is no way to tell whether it is directly
1544             * owned, or indirectly delegated.
1545             */
1546            sprop = (JSScopeProperty *)prop;
1547            *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1548        } else {
1549            *vp = JSVAL_FALSE;
1550        }
1551    }
1552    if (prop)
1553        OBJ_DROP_PROPERTY(cx, obj2, prop);
1554    return JS_TRUE;
1555}
1556
1557#ifdef JS_TRACER
1558static int32 FASTCALL
1559Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
1560{
1561    jsid id;
1562    jsval v;
1563
1564    if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id))
1565        return JSVAL_TO_BOOLEAN(JSVAL_VOID);
1566    if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v))
1567        return JSVAL_TO_BOOLEAN(JSVAL_VOID);
1568    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
1569    return JSVAL_TO_BOOLEAN(v);
1570}
1571#endif
1572
1573/* Proposed ECMA 15.2.4.6. */
1574static JSBool
1575obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
1576{
1577    JSBool b;
1578
1579    if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp),
1580                       argc != 0 ? vp[2] : JSVAL_VOID, &b)) {
1581        return JS_FALSE;
1582    }
1583    *vp = BOOLEAN_TO_JSVAL(b);
1584    return JS_TRUE;
1585}
1586
1587/* Proposed ECMA 15.2.4.7. */
1588static JSBool
1589obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
1590{
1591    jsid id;
1592    JSObject *obj;
1593
1594    if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1595        return JS_FALSE;
1596
1597    obj = JS_THIS_OBJECT(cx, vp);
1598    return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1599}
1600
1601#ifdef JS_TRACER
1602static int32 FASTCALL
1603Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str)
1604{
1605    jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str));
1606    jsval v;
1607    if (!js_PropertyIsEnumerable(cx, obj, id, &v))
1608        return JSVAL_TO_BOOLEAN(JSVAL_VOID);
1609    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
1610    return JSVAL_TO_BOOLEAN(v);
1611}
1612#endif
1613
1614JSBool
1615js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1616{
1617    JSObject *pobj;
1618    uintN attrs;
1619    JSProperty *prop;
1620    JSBool ok;
1621
1622    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1623        return JS_FALSE;
1624
1625    if (!prop) {
1626        *vp = JSVAL_FALSE;
1627        return JS_TRUE;
1628    }
1629
1630    /*
1631     * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1632     * The ECMA spec really should be fixed so propertyIsEnumerable and the
1633     * for..in loop agree on whether prototype properties are enumerable,
1634     * obviously by fixing this method (not by breaking the for..in loop!).
1635     *
1636     * We ch…

Large files files are truncated, but you can click here to view the full file