LCOV - app.info - js/src/builtin/MapObject.cpp

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-04-07 | 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            1419 : InitClass(JSContext *cx, GlobalObject *global, Class *clasp, JSProtoKey key, Native construct,
          58                 :           JSFunctionSpec *methods)
          59                 : {
          60            1419 :     JSObject *proto = global->createBlankPrototype(cx, clasp);
          61            1419 :     if (!proto)
          62               0 :         return NULL;
          63            1419 :     proto->setPrivate(NULL);
          64                 :
          65            1419 :     JSAtom *atom = cx->runtime->atomState.classAtoms[key];
          66            1419 :     JSFunction *ctor = global->createConstructor(cx, construct, atom, 1);
          67            5676 :     if (!ctor ||
          68            1419 :         !LinkConstructorAndPrototype(cx, ctor, proto) ||
          69            1419 :         !DefinePropertiesAndBrand(cx, proto, NULL, methods) ||
          70            1419 :         !DefineConstructorAndPrototype(cx, global, key, ctor, proto))
          71                 :     {
          72               0 :         return NULL;
          73                 :     }
          74            1419 :     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             750 : MapObject::initClass(JSContext *cx, JSObject *obj)
         179                 : {
         180             750 :     return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Map, construct, methods);
         181                 : }
         182                 :
         183                 : void
         184            5375 : MapObject::mark(JSTracer *trc, JSObject *obj)
         185                 : {
         186            5375 :     MapObject *mapobj = static_cast<MapObject *>(obj);
         187            5375 :     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            5375 : }
         197                 :
         198                 : void
         199            1632 : MapObject::finalize(FreeOp *fop, JSObject *obj)
         200                 : {
         201            1632 :     MapObject *mapobj = static_cast<MapObject *>(obj);
         202            1632 :     if (ValueMap *map = mapobj->getData())
         203             882 :         fop->delete_(map);
         204            1632 : }
         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             750 : js_InitMapClass(JSContext *cx, JSObject *obj)
         345                 : {
         346             750 :     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             669 : SetObject::initClass(JSContext *cx, JSObject *obj)
         381                 : {
         382             669 :     return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Set, construct, methods);
         383                 : }
         384                 :
         385                 : void
         386            2729 : SetObject::mark(JSTracer *trc, JSObject *obj)
         387                 : {
         388            2729 :     SetObject *setobj = static_cast<SetObject *>(obj);
         389            2729 :     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            2729 : }
         398                 :
         399                 : void
         400            1263 : SetObject::finalize(FreeOp *fop, JSObject *obj)
         401                 : {
         402            1263 :     SetObject *setobj = static_cast<SetObject *>(obj);
         403            1263 :     if (ValueSet *set = setobj->getData())
         404             594 :         fop->delete_(set);
         405            1263 : }
         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             669 : js_InitSetClass(JSContext *cx, JSObject *obj)
         501                 : {
         502             669 :     return SetObject::initClass(cx, obj);
         503                 : }

Generated by: LCOV version 1.7