LCOV - code coverage report
Current view: directory - js/src/builtin - MapObject.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 200 176 88.0 %
Date: 2012-06-02 Functions: 27 27 100.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=99 ft=cpp:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is SpiderMonkey JavaScript engine.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * the Mozilla Foundation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2011-2012
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *  Jason Orendorff <jorendorff@mozilla.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * 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                 : #include "builtin/MapObject.h"
      42                 : 
      43                 : #include "jscntxt.h"
      44                 : #include "jsgcmark.h"
      45                 : #include "jsiter.h"
      46                 : #include "jsobj.h"
      47                 : 
      48                 : #include "vm/GlobalObject.h"
      49                 : #include "vm/MethodGuard.h"
      50                 : #include "vm/Stack.h"
      51                 : 
      52                 : #include "jsobjinlines.h"
      53                 : 
      54                 : using namespace js;
      55                 : 
      56                 : static JSObject *
      57            1885 : InitClass(JSContext *cx, GlobalObject *global, Class *clasp, JSProtoKey key, Native construct,
      58                 :           JSFunctionSpec *methods)
      59                 : {
      60            1885 :     JSObject *proto = global->createBlankPrototype(cx, clasp);
      61            1885 :     if (!proto)
      62               0 :         return NULL;
      63            1885 :     proto->setPrivate(NULL);
      64                 : 
      65            1885 :     JSAtom *atom = cx->runtime->atomState.classAtoms[key];
      66            1885 :     JSFunction *ctor = global->createConstructor(cx, construct, clasp, atom, 1);
      67            7540 :     if (!ctor ||
      68            1885 :         !LinkConstructorAndPrototype(cx, ctor, proto) ||
      69            1885 :         !DefinePropertiesAndBrand(cx, proto, NULL, methods) ||
      70            1885 :         !DefineConstructorAndPrototype(cx, global, key, ctor, proto))
      71                 :     {
      72               0 :         return NULL;
      73                 :     }
      74            1885 :     return proto;
      75                 : }
      76                 : 
      77                 : 
      78                 : /*** HashableValue *******************************************************************************/
      79                 : 
      80                 : bool
      81         2369286 : HashableValue::setValue(JSContext *cx, const Value &v)
      82                 : {
      83         2369286 :     if (v.isString() && v.toString()->isRope()) {
      84                 :         /* Flatten this rope so that equals() is infallible. */
      85             378 :         JSString *str = v.toString()->ensureLinear(cx);
      86             378 :         if (!str)
      87               0 :             return false;
      88             378 :         value = StringValue(str);
      89         2368908 :     } else if (v.isDouble()) {
      90            1683 :         double d = v.toDouble();
      91                 :         int32_t i;
      92            1683 :         if (JSDOUBLE_IS_INT32(d, &i)) {
      93                 :             /* Normalize int32-valued doubles to int32 for faster hashing and testing. */
      94             108 :             value = Int32Value(i);
      95            1575 :         } else if (JSDOUBLE_IS_NaN(d)) {
      96                 :             /* NaNs with different bits must hash and test identically. */
      97             225 :             value = DoubleValue(js_NaN);
      98                 :         } else {
      99            1350 :             value = v;
     100                 :         }
     101                 :     } else {
     102         2367225 :         value = v;
     103                 :     }
     104                 : 
     105         9483480 :     JS_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() ||
     106         9483480 :               value.isNumber() || value.isString() || value.isObject());
     107         2369286 :     return true;
     108                 : }
     109                 : 
     110                 : HashNumber
     111         2369457 : HashableValue::hash() const
     112                 : {
     113                 :     /*
     114                 :      * HashableValue::setValue normalizes values so that the SameValue relation
     115                 :      * on HashableValues is the same as the == relationship on
     116                 :      * value.data.asBits, except for strings.
     117                 :      */
     118         2369457 :     if (value.isString()) {
     119            3906 :         JSLinearString &s = value.toString()->asLinear();
     120            3906 :         return HashChars(s.chars(), s.length());
     121                 :     }
     122                 : 
     123                 :     /* Having dispensed with strings, we can just hash asBits. */
     124         2365551 :     uint64_t u = value.asRawBits();
     125         2365551 :     return HashNumber((u >> 3) ^ (u >> (32 + 3)) ^ (u << (32 - 3)));
     126                 : }
     127                 : 
     128                 : bool
     129         1183797 : HashableValue::equals(const HashableValue &other) const
     130                 : {
     131                 :     /* Two HashableValues are equal if they have equal bits or they're equal strings. */
     132         1183797 :     bool b = (value.asRawBits() == other.value.asRawBits()) ||
     133            1521 :               (value.isString() &&
     134            1431 :                other.value.isString() &&
     135            1431 :                EqualStrings(&value.toString()->asLinear(),
     136         1188180 :                             &other.value.toString()->asLinear()));
     137                 : 
     138                 : #ifdef DEBUG
     139                 :     bool same;
     140         1183797 :     JS_ASSERT(SameValue(NULL, value, other.value, &same));
     141         1183797 :     JS_ASSERT(same == b);
     142                 : #endif
     143         1183797 :     return b;
     144                 : }
     145                 : 
     146                 : 
     147                 : /*** Map *****************************************************************************************/
     148                 : 
     149                 : Class MapObject::class_ = {
     150                 :     "Map",
     151                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     152                 :     JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
     153                 :     JS_PropertyStub,         /* addProperty */
     154                 :     JS_PropertyStub,         /* delProperty */
     155                 :     JS_PropertyStub,         /* getProperty */
     156                 :     JS_StrictPropertyStub,   /* setProperty */
     157                 :     JS_EnumerateStub,
     158                 :     JS_ResolveStub,
     159                 :     JS_ConvertStub,
     160                 :     finalize,
     161                 :     NULL,                    /* checkAccess */
     162                 :     NULL,                    /* call        */
     163                 :     NULL,                    /* construct   */
     164                 :     NULL,                    /* hasInstance */
     165                 :     mark
     166                 : };
     167                 : 
     168                 : JSFunctionSpec MapObject::methods[] = {
     169                 :     JS_FN("size", size, 0, 0),
     170                 :     JS_FN("get", get, 1, 0),
     171                 :     JS_FN("has", has, 1, 0),
     172                 :     JS_FN("set", set, 2, 0),
     173                 :     JS_FN("delete", delete_, 1, 0),
     174                 :     JS_FS_END
     175                 : };
     176                 : 
     177                 : JSObject *
     178             983 : MapObject::initClass(JSContext *cx, JSObject *obj)
     179                 : {
     180             983 :     return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Map, construct, methods);
     181                 : }
     182                 : 
     183                 : void
     184            7748 : MapObject::mark(JSTracer *trc, JSObject *obj)
     185                 : {
     186            7748 :     MapObject *mapobj = static_cast<MapObject *>(obj);
     187            7748 :     if (ValueMap *map = mapobj->getData()) {
     188            9396 :         for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
     189            4500 :             const HeapValue &key = r.front().key;
     190            9000 :             HeapValue tmp(key);
     191            4500 :             gc::MarkValue(trc, &tmp, "key");
     192            4500 :             JS_ASSERT(tmp.get() == key.get());
     193            4500 :             gc::MarkValue(trc, &r.front().value, "value");
     194                 :         }
     195                 :     }
     196            7748 : }
     197                 : 
     198                 : void
     199            1864 : MapObject::finalize(JSContext *cx, JSObject *obj)
     200                 : {
     201            1864 :     MapObject *mapobj = static_cast<MapObject *>(obj);
     202            1864 :     if (ValueMap *map = mapobj->getData())
     203             882 :         cx->delete_(map);
     204            1864 : }
     205                 : 
     206                 : class AddToMap {
     207                 :   private:
     208                 :     ValueMap *map;
     209                 : 
     210                 :   public:
     211             207 :     AddToMap(ValueMap *map) : map(map) {}
     212                 : 
     213             684 :     bool operator()(JSContext *cx, const Value &v) {
     214             684 :         JSObject *pairobj = js_ValueToNonNullObject(cx, v);
     215             684 :         if (!pairobj)
     216              36 :             return false;
     217                 : 
     218                 :         Value key;
     219             648 :         if (!pairobj->getElement(cx, 0, &key))
     220               0 :             return false;
     221            1296 :         HashableValue hkey;
     222             648 :         if (!hkey.setValue(cx, key))
     223               0 :             return false;
     224                 : 
     225                 :         Value val;
     226             648 :         if (!pairobj->getElement(cx, 1, &val))
     227               0 :             return false;
     228                 : 
     229             648 :         if (!map->put(hkey, val)) {
     230               0 :             js_ReportOutOfMemory(cx);
     231               0 :             return false;
     232                 :         }
     233             648 :         return true;
     234                 :     }
     235                 : };
     236                 : 
     237                 : JSBool
     238             882 : MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
     239                 : {
     240             882 :     JSObject *obj = NewBuiltinClassInstance(cx, &class_);
     241             882 :     if (!obj)
     242               0 :         return false;
     243                 : 
     244             882 :     ValueMap *map = cx->new_<ValueMap>(cx->runtime);
     245             882 :     if (!map)
     246               0 :         return false;
     247             882 :     if (!map->init()) {
     248               0 :         js_ReportOutOfMemory(cx);
     249               0 :         return false;
     250                 :     }
     251             882 :     obj->setPrivate(map);
     252                 : 
     253             882 :     CallArgs args = CallArgsFromVp(argc, vp);
     254             882 :     if (args.hasDefined(0)) {
     255             207 :         if (!ForOf(cx, args[0], AddToMap(map)))
     256             144 :             return false;
     257                 :     }
     258                 : 
     259             738 :     args.rval().setObject(*obj);
     260             738 :     return true;
     261                 : }
     262                 : 
     263                 : #define UNPACK_THIS(T, native, cx, argc, vp, args, data)                      \
     264                 :     CallArgs args = CallArgsFromVp(argc, vp);                                 \
     265                 :     if (!args.thisv().isObject() ||                                           \
     266                 :         !args.thisv().toObject().hasClass(&T::class_))                        \
     267                 :     {                                                                         \
     268                 :         return js::HandleNonGenericMethodClassMismatch(cx, args, native,      \
     269                 :                                                        &T::class_);           \
     270                 :     }                                                                         \
     271                 :     if (!args.thisv().toObject().getPrivate()) {                              \
     272                 :         ReportIncompatibleMethod(cx, args, &T::class_);                       \
     273                 :         return false;                                                         \
     274                 :     }                                                                         \
     275                 :     T::Data &data = *static_cast<T &>(args.thisv().toObject()).getData();     \
     276                 :     (void) data
     277                 : 
     278                 : #define THIS_MAP(native, cx, argc, vp, args, map)                             \
     279                 :     UNPACK_THIS(MapObject, native, cx, argc, vp, args, map)
     280                 : 
     281                 : #define ARG0_KEY(cx, args, key)                                               \
     282                 :     HashableValue key;                                                        \
     283                 :     if (args.length() > 0 && !key.setValue(cx, args[0]))                      \
     284                 :         return false
     285                 : 
     286                 : JSBool
     287             252 : MapObject::size(JSContext *cx, unsigned argc, Value *vp)
     288                 : {
     289             252 :     THIS_MAP(get, cx, argc, vp, args, map);
     290                 :     JS_STATIC_ASSERT(sizeof map.count() <= sizeof(uint32_t));
     291             198 :     args.rval().setNumber(map.count());
     292             198 :     return true;
     293                 : }
     294                 : 
     295                 : JSBool
     296          591318 : MapObject::get(JSContext *cx, unsigned argc, Value *vp)
     297                 : {
     298          591318 :     THIS_MAP(get, cx, argc, vp, args, map);
     299         1182402 :     ARG0_KEY(cx, args, key);
     300                 : 
     301          591201 :     if (ValueMap::Ptr p = map.lookup(key))
     302          590445 :         args.rval() = p->value;
     303                 :     else
     304             756 :         args.rval().setUndefined();
     305          591201 :     return true;
     306                 : }
     307                 : 
     308                 : JSBool
     309            1683 : MapObject::has(JSContext *cx, unsigned argc, Value *vp)
     310                 : {
     311            1683 :     THIS_MAP(has, cx, argc, vp, args, map);
     312            3132 :     ARG0_KEY(cx, args, key);
     313            1566 :     args.rval().setBoolean(map.lookup(key));
     314            1566 :     return true;
     315                 : }
     316                 : 
     317                 : JSBool
     318          591201 : MapObject::set(JSContext *cx, unsigned argc, Value *vp)
     319                 : {
     320          591201 :     THIS_MAP(set, cx, argc, vp, args, map);
     321         1182168 :     ARG0_KEY(cx, args, key);
     322          591084 :     if (!map.put(key, args.length() > 1 ? args[1] : UndefinedValue())) {
     323               0 :         js_ReportOutOfMemory(cx);
     324               0 :         return false;
     325                 :     }
     326          591084 :     args.rval().setUndefined();
     327          591084 :     return true;
     328                 : }
     329                 : 
     330                 : JSBool
     331             585 : MapObject::delete_(JSContext *cx, unsigned argc, Value *vp)
     332                 : {
     333             585 :     THIS_MAP(delete_, cx, argc, vp, args, map);
     334             936 :     ARG0_KEY(cx, args, key);
     335             468 :     ValueMap::Ptr p = map.lookup(key);
     336             468 :     bool found = p.found();
     337             468 :     if (found)
     338             378 :         map.remove(p);
     339             468 :     args.rval().setBoolean(found);
     340             468 :     return true;
     341                 : }
     342                 : 
     343                 : JSObject *
     344             983 : js_InitMapClass(JSContext *cx, JSObject *obj)
     345                 : {
     346             983 :     return MapObject::initClass(cx, obj);
     347                 : }
     348                 : 
     349                 : 
     350                 : /*** Set *****************************************************************************************/
     351                 : 
     352                 : Class SetObject::class_ = {
     353                 :     "Set",
     354                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     355                 :     JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
     356                 :     JS_PropertyStub,         /* addProperty */
     357                 :     JS_PropertyStub,         /* delProperty */
     358                 :     JS_PropertyStub,         /* getProperty */
     359                 :     JS_StrictPropertyStub,   /* setProperty */
     360                 :     JS_EnumerateStub,
     361                 :     JS_ResolveStub,
     362                 :     JS_ConvertStub,
     363                 :     finalize,
     364                 :     NULL,                    /* checkAccess */
     365                 :     NULL,                    /* call        */
     366                 :     NULL,                    /* construct   */
     367                 :     NULL,                    /* hasInstance */
     368                 :     mark
     369                 : };
     370                 : 
     371                 : JSFunctionSpec SetObject::methods[] = {
     372                 :     JS_FN("size", size, 0, 0),
     373                 :     JS_FN("has", has, 1, 0),
     374                 :     JS_FN("add", add, 1, 0),
     375                 :     JS_FN("delete", delete_, 1, 0),
     376                 :     JS_FS_END
     377                 : };
     378                 : 
     379                 : JSObject *
     380             902 : SetObject::initClass(JSContext *cx, JSObject *obj)
     381                 : {
     382             902 :     return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Set, construct, methods);
     383                 : }
     384                 : 
     385                 : void
     386            5102 : SetObject::mark(JSTracer *trc, JSObject *obj)
     387                 : {
     388            5102 :     SetObject *setobj = static_cast<SetObject *>(obj);
     389            5102 :     if (ValueSet *set = setobj->getData()) {
     390            4698 :         for (ValueSet::Range r = set->all(); !r.empty(); r.popFront()) {
     391            2250 :             const HeapValue &key = r.front();
     392            4500 :             HeapValue tmp(key);
     393            2250 :             gc::MarkValue(trc, &tmp, "key");
     394            2250 :             JS_ASSERT(tmp.get() == key.get());
     395                 :         }
     396                 :     }
     397            5102 : }
     398                 : 
     399                 : void
     400            1495 : SetObject::finalize(JSContext *cx, JSObject *obj)
     401                 : {
     402            1495 :     SetObject *setobj = static_cast<SetObject *>(obj);
     403            1495 :     if (ValueSet *set = setobj->getData())
     404             594 :         cx->delete_(set);
     405            1495 : }
     406                 : 
     407                 : class AddToSet {
     408                 :   private:
     409                 :     ValueSet *set;
     410                 : 
     411                 :   public:
     412             162 :     AddToSet(ValueSet *set) : set(set) {}
     413                 : 
     414            3285 :     bool operator()(JSContext *cx, const Value &v) {
     415            6570 :         HashableValue key;
     416            3285 :         if (!key.setValue(cx, v))
     417               0 :             return false;
     418            3285 :         if (!set->put(key)) {
     419               0 :             js_ReportOutOfMemory(cx);
     420               0 :             return false;
     421                 :         }
     422            3285 :         return true;
     423                 :     }
     424                 : };
     425                 : 
     426                 : JSBool
     427             594 : SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
     428                 : {
     429             594 :     JSObject *obj = NewBuiltinClassInstance(cx, &class_);
     430             594 :     if (!obj)
     431               0 :         return false;
     432                 : 
     433             594 :     ValueSet *set = cx->new_<ValueSet>(cx->runtime);
     434             594 :     if (!set)
     435               0 :         return false;
     436             594 :     if (!set->init()) {
     437               0 :         js_ReportOutOfMemory(cx);
     438               0 :         return false;
     439                 :     }
     440             594 :     obj->setPrivate(set);
     441                 : 
     442             594 :     CallArgs args = CallArgsFromVp(argc, vp);
     443             594 :     if (args.hasDefined(0)) {
     444             162 :         if (!ForOf(cx, args[0], AddToSet(set)))
     445               0 :             return false;
     446                 :     }
     447                 : 
     448             594 :     args.rval().setObject(*obj);
     449             594 :     return true;
     450                 : }
     451                 : 
     452                 : #define THIS_SET(native, cx, argc, vp, args, set)                             \
     453                 :     UNPACK_THIS(SetObject, native, cx, argc, vp, args, set)
     454                 : 
     455                 : JSBool
     456             756 : SetObject::size(JSContext *cx, unsigned argc, Value *vp)
     457                 : {
     458             756 :     THIS_SET(has, cx, argc, vp, args, set);
     459                 :     JS_STATIC_ASSERT(sizeof set.count() <= sizeof(uint32_t));
     460             756 :     args.rval().setNumber(set.count());
     461             756 :     return true;
     462                 : }
     463                 : 
     464                 : JSBool
     465          590346 : SetObject::has(JSContext *cx, unsigned argc, Value *vp)
     466                 : {
     467          590346 :     THIS_SET(has, cx, argc, vp, args, set);
     468         1180458 :     ARG0_KEY(cx, args, key);
     469          590229 :     args.rval().setBoolean(set.has(key));
     470          590229 :     return true;
     471                 : }
     472                 : 
     473                 : JSBool
     474          590742 : SetObject::add(JSContext *cx, unsigned argc, Value *vp)
     475                 : {
     476          590742 :     THIS_SET(add, cx, argc, vp, args, set);
     477         1181250 :     ARG0_KEY(cx, args, key);
     478          590625 :     if (!set.put(key)) {
     479               0 :         js_ReportOutOfMemory(cx);
     480               0 :         return false;
     481                 :     }
     482          590625 :     args.rval().setUndefined();
     483          590625 :     return true;
     484                 : }
     485                 : 
     486                 : JSBool
     487             468 : SetObject::delete_(JSContext *cx, unsigned argc, Value *vp)
     488                 : {
     489             468 :     THIS_SET(delete_, cx, argc, vp, args, set);
     490             702 :     ARG0_KEY(cx, args, key);
     491             351 :     ValueSet::Ptr p = set.lookup(key);
     492             351 :     bool found = p.found();
     493             351 :     if (found)
     494             126 :         set.remove(p);
     495             351 :     args.rval().setBoolean(found);
     496             351 :     return true;
     497                 : }
     498                 : 
     499                 : JSObject *
     500             902 : js_InitSetClass(JSContext *cx, JSObject *obj)
     501                 : {
     502             902 :     return SetObject::initClass(cx, obj);
     503                 : } 

Generated by: LCOV version 1.7