DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (d38398e5144e)

VCS Links

HashableValue

Hasher

IteratorKind

IteratorKind

MapIteratorObject

MapObject

MutableWrappedPtrOperations

SetIteratorObject

SetObject

WrappedPtrOperations

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef builtin_MapObject_h
#define builtin_MapObject_h

#include "jsobj.h"

#include "builtin/SelfHostingDefines.h"
#include "vm/GlobalObject.h"
#include "vm/NativeObject.h"
#include "vm/PIC.h"
#include "vm/Runtime.h"

namespace js {

/*
 * Comparing two ropes for equality can fail. The js::HashTable template
 * requires infallible hash() and match() operations. Therefore we require
 * all values to be converted to hashable form before being used as a key
 * in a Map or Set object.
 *
 * All values except ropes are hashable as-is.
 */
class HashableValue
{
    PreBarrieredValue value;

  public:
    struct Hasher {
        typedef HashableValue Lookup;
        static HashNumber hash(const Lookup& v, const mozilla::HashCodeScrambler& hcs) {
            return v.hash(hcs);
        }
        static bool match(const HashableValue& k, const Lookup& l) { return k == l; }
        static bool isEmpty(const HashableValue& v) { return v.value.isMagic(JS_HASH_KEY_EMPTY); }
        static void makeEmpty(HashableValue* vp) { vp->value = MagicValue(JS_HASH_KEY_EMPTY); }
    };

    HashableValue() : value(UndefinedValue()) {}

    MOZ_MUST_USE bool setValue(JSContext* cx, HandleValue v);
    HashNumber hash(const mozilla::HashCodeScrambler& hcs) const;
    bool operator==(const HashableValue& other) const;
    HashableValue trace(JSTracer* trc) const;
    Value get() const { return value.get(); }

    void trace(JSTracer* trc) {
        TraceEdge(trc, &value, "HashableValue");
    }
};

template <typename Wrapper>
class WrappedPtrOperations<HashableValue, Wrapper>
{
  public:
    Value value() const {
        return static_cast<const Wrapper*>(this)->get().get();
    }
};

template <typename Wrapper>
class MutableWrappedPtrOperations<HashableValue, Wrapper>
  : public WrappedPtrOperations<HashableValue, Wrapper>
{
  public:
    MOZ_MUST_USE bool setValue(JSContext* cx, HandleValue v) {
        return static_cast<Wrapper*>(this)->get().setValue(cx, v);
    }
};

template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashMap;

template <class T, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashSet;

typedef OrderedHashMap<HashableValue,
                       HeapPtr<Value>,
                       HashableValue::Hasher,
                       RuntimeAllocPolicy> ValueMap;

typedef OrderedHashSet<HashableValue,
                       HashableValue::Hasher,
                       RuntimeAllocPolicy> ValueSet;

template <typename ObjectT>
class OrderedHashTableRef;

struct UnbarrieredHashPolicy;

class MapObject : public NativeObject {
  public:
    enum IteratorKind { Keys, Values, Entries };
    static_assert(Keys == ITEM_KIND_KEY,
                  "IteratorKind Keys must match self-hosting define for item kind key.");
    static_assert(Values == ITEM_KIND_VALUE,
                  "IteratorKind Values must match self-hosting define for item kind value.");
    static_assert(Entries == ITEM_KIND_KEY_AND_VALUE,
                  "IteratorKind Entries must match self-hosting define for item kind "
                  "key-and-value.");

    static const Class class_;
    static const Class protoClass_;

    enum { NurseryKeysSlot, SlotCount };

    static MOZ_MUST_USE bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
                                            JS::MutableHandle<GCVector<JS::Value>> entries);
    static MOZ_MUST_USE bool entries(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool has(JSContext* cx, unsigned argc, Value* vp);
    static MapObject* create(JSContext* cx, HandleObject proto = nullptr);

    // Publicly exposed Map calls for JSAPI access (webidl maplike/setlike
    // interfaces, etc.)
    static uint32_t size(JSContext *cx, HandleObject obj);
    static MOZ_MUST_USE bool get(JSContext *cx, HandleObject obj, HandleValue key,
                                 MutableHandleValue rval);
    static MOZ_MUST_USE bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);
    static MOZ_MUST_USE bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);

    // Set call for public JSAPI exposure. Does not actually return map object
    // as stated in spec, expects caller to return a value. for instance, with
    // webidl maplike/setlike, should return interface object.
    static MOZ_MUST_USE bool set(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val);
    static MOZ_MUST_USE bool clear(JSContext *cx, HandleObject obj);
    static MOZ_MUST_USE bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj,
                                      MutableHandleValue iter);

    using UnbarrieredTable = OrderedHashMap<Value, Value, UnbarrieredHashPolicy, RuntimeAllocPolicy>;
    friend class OrderedHashTableRef<MapObject>;

  private:
    static const ClassSpec classSpec_;
    static const ClassOps classOps_;

    static const JSPropertySpec properties[];
    static const JSFunctionSpec methods[];
    static const JSPropertySpec staticProperties[];
    ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
    static ValueMap& extract(HandleObject o);
    static ValueMap& extract(const CallArgs& args);
    static void trace(JSTracer* trc, JSObject* obj);
    static void finalize(FreeOp* fop, JSObject* obj);
    static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);

    static bool is(HandleValue v);
    static bool is(HandleObject o);

    static MOZ_MUST_USE bool iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind);

    static MOZ_MUST_USE bool size_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool size(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool get_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool get(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool has_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool set_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool set(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool delete_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool delete_(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool keys_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool keys(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool values_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool values(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool entries_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool clear_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool clear(JSContext* cx, unsigned argc, Value* vp);
};

class MapIteratorObject : public NativeObject
{
  public:
    static const Class class_;

    enum { TargetSlot, RangeSlot, KindSlot, SlotCount };

    static_assert(TargetSlot == ITERATOR_SLOT_TARGET,
                  "TargetSlot must match self-hosting define for iterated object slot.");
    static_assert(RangeSlot == ITERATOR_SLOT_RANGE,
                  "RangeSlot must match self-hosting define for range or index slot.");
    static_assert(KindSlot == ITERATOR_SLOT_ITEM_KIND,
                  "KindSlot must match self-hosting define for item kind slot.");

    static const JSFunctionSpec methods[];
    static MapIteratorObject* create(JSContext* cx, HandleObject mapobj, ValueMap* data,
                                     MapObject::IteratorKind kind);
    static void finalize(FreeOp* fop, JSObject* obj);

    static MOZ_MUST_USE bool next(Handle<MapIteratorObject*> mapIterator,
                                  HandleArrayObject resultPairObj, JSContext* cx);

    static JSObject* createResultPair(JSContext* cx);

  private:
    inline MapObject::IteratorKind kind() const;
};

class SetObject : public NativeObject {
  public:
    enum IteratorKind { Keys, Values, Entries };

    static_assert(Keys == ITEM_KIND_KEY,
                  "IteratorKind Keys must match self-hosting define for item kind key.");
    static_assert(Values == ITEM_KIND_VALUE,
                  "IteratorKind Values must match self-hosting define for item kind value.");
    static_assert(Entries == ITEM_KIND_KEY_AND_VALUE,
                  "IteratorKind Entries must match self-hosting define for item kind "
                  "key-and-value.");

    static const Class class_;
    static const Class protoClass_;

    enum { NurseryKeysSlot, SlotCount };

    static MOZ_MUST_USE bool keys(JSContext *cx, HandleObject obj,
                                  JS::MutableHandle<GCVector<JS::Value>> keys);
    static MOZ_MUST_USE bool values(JSContext *cx, unsigned argc, Value *vp);
    static MOZ_MUST_USE bool add(JSContext *cx, HandleObject obj, HandleValue key);
    static MOZ_MUST_USE bool has(JSContext *cx, unsigned argc, Value *vp);

    // Publicly exposed Set calls for JSAPI access (webidl maplike/setlike
    // interfaces, etc.)
    static SetObject* create(JSContext *cx, HandleObject proto = nullptr);
    static uint32_t size(JSContext *cx, HandleObject obj);
    static MOZ_MUST_USE bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);
    static MOZ_MUST_USE bool clear(JSContext *cx, HandleObject obj);
    static MOZ_MUST_USE bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj,
                                      MutableHandleValue iter);
    static MOZ_MUST_USE bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval);

    using UnbarrieredTable = OrderedHashSet<Value, UnbarrieredHashPolicy, RuntimeAllocPolicy>;
    friend class OrderedHashTableRef<SetObject>;

  private:
    static const ClassSpec classSpec_;
    static const ClassOps classOps_;

    static const JSPropertySpec properties[];
    static const JSFunctionSpec methods[];
    static const JSPropertySpec staticProperties[];

    ValueSet* getData() { return static_cast<ValueSet*>(getPrivate()); }
    static ValueSet& extract(HandleObject o);
    static ValueSet& extract(const CallArgs& args);
    static void trace(JSTracer* trc, JSObject* obj);
    static void finalize(FreeOp* fop, JSObject* obj);
    static bool construct(JSContext* cx, unsigned argc, Value* vp);

    static bool is(HandleValue v);
    static bool is(HandleObject o);

    static bool isBuiltinAdd(HandleValue add, JSContext* cx);

    static MOZ_MUST_USE bool iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind);

    static MOZ_MUST_USE bool size_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool size(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool has_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool add_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool add(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool delete_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool delete_(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool values_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool entries_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool entries(JSContext* cx, unsigned argc, Value* vp);
    static MOZ_MUST_USE bool clear_impl(JSContext* cx, const CallArgs& args);
    static MOZ_MUST_USE bool clear(JSContext* cx, unsigned argc, Value* vp);
};

class SetIteratorObject : public NativeObject
{
  public:
    static const Class class_;

    enum { TargetSlot, RangeSlot, KindSlot, SlotCount };

    static_assert(TargetSlot == ITERATOR_SLOT_TARGET,
                  "TargetSlot must match self-hosting define for iterated object slot.");
    static_assert(RangeSlot == ITERATOR_SLOT_RANGE,
                  "RangeSlot must match self-hosting define for range or index slot.");
    static_assert(KindSlot == ITERATOR_SLOT_ITEM_KIND,
                  "KindSlot must match self-hosting define for item kind slot.");

    static const JSFunctionSpec methods[];
    static SetIteratorObject* create(JSContext* cx, HandleObject setobj, ValueSet* data,
                                     SetObject::IteratorKind kind);
    static void finalize(FreeOp* fop, JSObject* obj);

    static MOZ_MUST_USE bool next(Handle<SetIteratorObject*> setIterator,
                                  HandleArrayObject resultObj, JSContext* cx);

    static JSObject* createResult(JSContext* cx);

  private:
    inline SetObject::IteratorKind kind() const;
};

using SetInitGetPrototypeOp = NativeObject* (*)(JSContext*, Handle<GlobalObject*>);
using SetInitIsBuiltinOp = bool (*)(HandleValue, JSContext*);

template <SetInitGetPrototypeOp getPrototypeOp, SetInitIsBuiltinOp isBuiltinOp>
static MOZ_MUST_USE bool
IsOptimizableInitForSet(JSContext* cx, HandleObject setObject, HandleValue iterable, bool* optimized)
{
    MOZ_ASSERT(!*optimized);

    if (!iterable.isObject())
        return true;

    RootedObject array(cx, &iterable.toObject());
    if (!IsPackedArray(array))
        return true;

    // Get the canonical prototype object.
    RootedNativeObject setProto(cx, getPrototypeOp(cx, cx->global()));
    if (!setProto)
        return false;

    // Ensures setObject's prototype is the canonical prototype.
    if (setObject->staticPrototype() != setProto)
        return true;

    // Look up the 'add' value on the prototype object.
    Shape* addShape = setProto->lookup(cx, cx->names().add);
    if (!addShape || !addShape->hasSlot())
        return true;

    // Get the referred value, ensure it holds the canonical add function.
    RootedValue add(cx, setProto->getSlot(addShape->slot()));
    if (!isBuiltinOp(add, cx))
        return true;

    ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx);
    if (!stubChain)
        return false;

    return stubChain->tryOptimizeArray(cx, array.as<ArrayObject>(), optimized);
}

} /* namespace js */

#endif /* builtin_MapObject_h */