1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef jsatominlines_h
8 #define jsatominlines_h
9 
10 #include "jsatom.h"
11 
12 #include "mozilla/PodOperations.h"
13 #include "mozilla/RangedPtr.h"
14 
15 #include "jscntxt.h"
16 #include "jsnum.h"
17 
18 #include "vm/String.h"
19 
20 inline JSAtom*
asPtr()21 js::AtomStateEntry::asPtr() const
22 {
23     MOZ_ASSERT(bits != 0);
24     JSAtom* atom = reinterpret_cast<JSAtom*>(bits & NO_TAG_MASK);
25     JSString::readBarrier(atom);
26     return atom;
27 }
28 
29 inline JSAtom*
asPtrUnbarriered()30 js::AtomStateEntry::asPtrUnbarriered() const
31 {
32     MOZ_ASSERT(bits != 0);
33     return reinterpret_cast<JSAtom*>(bits & NO_TAG_MASK);
34 }
35 
36 namespace js {
37 
38 inline jsid
AtomToId(JSAtom * atom)39 AtomToId(JSAtom* atom)
40 {
41     JS_STATIC_ASSERT(JSID_INT_MIN == 0);
42 
43     uint32_t index;
44     if (atom->isIndex(&index) && index <= JSID_INT_MAX)
45         return INT_TO_JSID(int32_t(index));
46 
47     return JSID_FROM_BITS(size_t(atom));
48 }
49 
50 inline bool
ValueToIdPure(const Value & v,jsid * id)51 ValueToIdPure(const Value& v, jsid* id)
52 {
53     int32_t i;
54     if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
55         *id = INT_TO_JSID(i);
56         return true;
57     }
58 
59     if (js::IsSymbolOrSymbolWrapper(v)) {
60         *id = SYMBOL_TO_JSID(js::ToSymbolPrimitive(v));
61         return true;
62     }
63 
64     if (!v.isString() || !v.toString()->isAtom())
65         return false;
66 
67     *id = AtomToId(&v.toString()->asAtom());
68     return true;
69 }
70 
71 template <AllowGC allowGC>
72 inline bool
ValueToId(ExclusiveContext * cx,typename MaybeRooted<Value,allowGC>::HandleType v,typename MaybeRooted<jsid,allowGC>::MutableHandleType idp)73 ValueToId(ExclusiveContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v,
74           typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
75 {
76     int32_t i;
77     if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
78         idp.set(INT_TO_JSID(i));
79         return true;
80     }
81 
82     if (js::IsSymbolOrSymbolWrapper(v)) {
83         idp.set(SYMBOL_TO_JSID(js::ToSymbolPrimitive(v)));
84         return true;
85     }
86 
87     JSAtom* atom = ToAtom<allowGC>(cx, v);
88     if (!atom)
89         return false;
90 
91     idp.set(AtomToId(atom));
92     return true;
93 }
94 
95 /*
96  * Write out character representing |index| to the memory just before |end|.
97  * Thus |*end| is not touched, but |end[-1]| and earlier are modified as
98  * appropriate.  There must be at least js::UINT32_CHAR_BUFFER_LENGTH elements
99  * before |end| to avoid buffer underflow.  The start of the characters written
100  * is returned and is necessarily before |end|.
101  */
102 template <typename T>
103 inline mozilla::RangedPtr<T>
BackfillIndexInCharBuffer(uint32_t index,mozilla::RangedPtr<T> end)104 BackfillIndexInCharBuffer(uint32_t index, mozilla::RangedPtr<T> end)
105 {
106 #ifdef DEBUG
107     /*
108      * Assert that the buffer we're filling will hold as many characters as we
109      * could write out, by dereferencing the index that would hold the most
110      * significant digit.
111      */
112     (void) *(end - UINT32_CHAR_BUFFER_LENGTH);
113 #endif
114 
115     do {
116         uint32_t next = index / 10, digit = index % 10;
117         *--end = '0' + digit;
118         index = next;
119     } while (index > 0);
120 
121     return end;
122 }
123 
124 bool
125 IndexToIdSlow(ExclusiveContext* cx, uint32_t index, MutableHandleId idp);
126 
127 inline bool
IndexToId(ExclusiveContext * cx,uint32_t index,MutableHandleId idp)128 IndexToId(ExclusiveContext* cx, uint32_t index, MutableHandleId idp)
129 {
130     if (index <= JSID_INT_MAX) {
131         idp.set(INT_TO_JSID(index));
132         return true;
133     }
134 
135     return IndexToIdSlow(cx, index, idp);
136 }
137 
138 static MOZ_ALWAYS_INLINE JSFlatString*
IdToString(JSContext * cx,jsid id)139 IdToString(JSContext* cx, jsid id)
140 {
141     if (JSID_IS_STRING(id))
142         return JSID_TO_ATOM(id);
143 
144     if (MOZ_LIKELY(JSID_IS_INT(id)))
145         return Int32ToString<CanGC>(cx, JSID_TO_INT(id));
146 
147     RootedValue idv(cx, IdToValue(id));
148     JSString* str = ToStringSlow<CanGC>(cx, idv);
149     if (!str)
150         return nullptr;
151 
152     return str->ensureFlat(cx);
153 }
154 
155 inline
Lookup(const JSAtom * atom)156 AtomHasher::Lookup::Lookup(const JSAtom* atom)
157   : isLatin1(atom->hasLatin1Chars()), length(atom->length()), atom(atom)
158 {
159     hash = atom->hash();
160     if (isLatin1) {
161         latin1Chars = atom->latin1Chars(nogc);
162         MOZ_ASSERT(mozilla::HashString(latin1Chars, length) == hash);
163     } else {
164         twoByteChars = atom->twoByteChars(nogc);
165         MOZ_ASSERT(mozilla::HashString(twoByteChars, length) == hash);
166     }
167 }
168 
169 inline bool
match(const AtomStateEntry & entry,const Lookup & lookup)170 AtomHasher::match(const AtomStateEntry& entry, const Lookup& lookup)
171 {
172     JSAtom* key = entry.asPtr();
173     if (lookup.atom)
174         return lookup.atom == key;
175     if (key->length() != lookup.length || key->hash() != lookup.hash)
176         return false;
177 
178     if (key->hasLatin1Chars()) {
179         const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
180         if (lookup.isLatin1)
181             return mozilla::PodEqual(keyChars, lookup.latin1Chars, lookup.length);
182         return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
183     }
184 
185     const char16_t* keyChars = key->twoByteChars(lookup.nogc);
186     if (lookup.isLatin1)
187         return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
188     return mozilla::PodEqual(keyChars, lookup.twoByteChars, lookup.length);
189 }
190 
191 inline Handle<PropertyName*>
TypeName(JSType type,const JSAtomState & names)192 TypeName(JSType type, const JSAtomState& names)
193 {
194     MOZ_ASSERT(type < JSTYPE_LIMIT);
195     JS_STATIC_ASSERT(offsetof(JSAtomState, undefined) +
196                      JSTYPE_LIMIT * sizeof(ImmutablePropertyNamePtr) <=
197                      sizeof(JSAtomState));
198     JS_STATIC_ASSERT(JSTYPE_VOID == 0);
199     return (&names.undefined)[type];
200 }
201 
202 inline Handle<PropertyName*>
ClassName(JSProtoKey key,JSAtomState & atomState)203 ClassName(JSProtoKey key, JSAtomState& atomState)
204 {
205     MOZ_ASSERT(key < JSProto_LIMIT);
206     JS_STATIC_ASSERT(offsetof(JSAtomState, Null) +
207                      JSProto_LIMIT * sizeof(ImmutablePropertyNamePtr) <=
208                      sizeof(JSAtomState));
209     JS_STATIC_ASSERT(JSProto_Null == 0);
210     return (&atomState.Null)[key];
211 }
212 
213 inline Handle<PropertyName*>
ClassName(JSProtoKey key,JSRuntime * rt)214 ClassName(JSProtoKey key, JSRuntime* rt)
215 {
216     return ClassName(key, *rt->commonNames);
217 }
218 
219 inline Handle<PropertyName*>
ClassName(JSProtoKey key,ExclusiveContext * cx)220 ClassName(JSProtoKey key, ExclusiveContext* cx)
221 {
222     return ClassName(key, cx->names());
223 }
224 
225 } // namespace js
226 
227 #endif /* jsatominlines_h */
228