2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "BatchedTransitionOptimizer.h"
25 #include "CallFrame.h"
26 #include "CustomGetterSetter.h"
27 #include "Identifier.h"
28 #include "IdentifierInlines.h"
29 #include "Intrinsic.h"
30 #include "JSGlobalObject.h"
31 #include "PropertySlot.h"
32 #include "PutPropertySlot.h"
33 #include <wtf/Assertions.h>
36 struct CompactHashIndex
{
41 // FIXME: There is no reason this get function can't be simpler.
42 // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject)
43 typedef PropertySlot::GetValueFunc GetFunction
;
44 typedef PutPropertySlot::PutValueFunc PutFunction
;
45 typedef FunctionExecutable
* (*BuiltinGenerator
)(VM
&);
47 // Hash table generated by the create_hash_table script.
48 struct HashTableValue
{
49 const char* m_key
; // property name
50 unsigned m_attributes
; // JSObject attributes
51 Intrinsic m_intrinsic
;
55 unsigned attributes() const { return m_attributes
; }
57 Intrinsic
intrinsic() const { ASSERT(m_attributes
& Function
); return m_intrinsic
; }
58 BuiltinGenerator
builtinGenerator() const { ASSERT(m_attributes
& Builtin
); return reinterpret_cast<BuiltinGenerator
>(m_value1
); }
59 NativeFunction
function() const { ASSERT(m_attributes
& Function
); return reinterpret_cast<NativeFunction
>(m_value1
); }
60 unsigned char functionLength() const { ASSERT(m_attributes
& Function
); return static_cast<unsigned char>(m_value2
); }
62 GetFunction
propertyGetter() const { ASSERT(!(m_attributes
& BuiltinOrFunctionOrConstant
)); return reinterpret_cast<GetFunction
>(m_value1
); }
63 PutFunction
propertyPutter() const { ASSERT(!(m_attributes
& BuiltinOrFunctionOrConstant
)); return reinterpret_cast<PutFunction
>(m_value2
); }
65 intptr_t constantInteger() const { ASSERT(m_attributes
& ConstantInteger
); return m_value1
; }
67 intptr_t lexerValue() const { ASSERT(!m_attributes
); return m_value1
; }
71 mutable int numberOfValues
;
73 bool hasSetterOrReadonlyProperties
;
75 const HashTableValue
* values
; // Fixed values generated by script.
76 mutable const char** keys
; // Table allocated at runtime.
77 const CompactHashIndex
* index
;
79 ALWAYS_INLINE HashTable
copy() const
81 // Don't copy dynamic table since it's thread specific.
82 HashTable result
= { numberOfValues
, indexMask
, hasSetterOrReadonlyProperties
, values
, 0, index
};
86 ALWAYS_INLINE
void initializeIfNeeded(VM
& vm
) const
92 ALWAYS_INLINE
void initializeIfNeeded(ExecState
* exec
) const
95 createTable(exec
->vm());
98 JS_EXPORT_PRIVATE
void deleteTable() const;
100 // Find an entry in the table, and return the entry.
101 ALWAYS_INLINE
const HashTableValue
* entry(VM
& vm
, PropertyName identifier
) const
103 initializeIfNeeded(vm
);
104 return entry(identifier
);
107 ALWAYS_INLINE
const HashTableValue
* entry(ExecState
* exec
, PropertyName identifier
) const
109 initializeIfNeeded(exec
);
110 return entry(identifier
);
113 class ConstIterator
{
115 ConstIterator(const HashTable
* table
, int position
)
117 , m_position(position
)
122 const HashTableValue
* value()
124 return &m_table
->values
[m_position
];
129 return m_table
->keys
[m_position
];
132 const HashTableValue
* operator->()
137 bool operator!=(const ConstIterator
& other
)
139 ASSERT(m_table
== other
.m_table
);
140 return m_position
!= other
.m_position
;
143 ConstIterator
& operator++()
145 ASSERT(m_position
< m_table
->numberOfValues
);
152 void skipInvalidKeys()
154 ASSERT(m_position
<= m_table
->numberOfValues
);
155 while (m_position
< m_table
->numberOfValues
&& !m_table
->keys
[m_position
])
157 ASSERT(m_position
<= m_table
->numberOfValues
);
160 const HashTable
* m_table
;
164 ConstIterator
begin(VM
& vm
) const
166 initializeIfNeeded(vm
);
167 return ConstIterator(this, 0);
169 ConstIterator
end(VM
& vm
) const
171 initializeIfNeeded(vm
);
172 return ConstIterator(this, numberOfValues
);
176 ALWAYS_INLINE
const HashTableValue
* entry(PropertyName propertyName
) const
178 StringImpl
* impl
= propertyName
.uid();
184 int indexEntry
= impl
->existingHash() & indexMask
;
185 int valueIndex
= index
[indexEntry
].value
;
186 if (valueIndex
== -1)
190 if (WTF::equal(impl
, keys
[valueIndex
]))
191 return &values
[valueIndex
];
193 indexEntry
= index
[indexEntry
].next
;
194 if (indexEntry
== -1)
196 valueIndex
= index
[indexEntry
].value
;
197 ASSERT(valueIndex
!= -1);
201 // Convert the hash table keys to identifiers.
202 JS_EXPORT_PRIVATE
void createTable(VM
&) const;
205 JS_EXPORT_PRIVATE
bool setUpStaticFunctionSlot(ExecState
*, const HashTableValue
*, JSObject
* thisObject
, PropertyName
, PropertySlot
&);
208 * This method does it all (looking in the hashtable, checking for function
209 * overrides, creating the function or retrieving from cache, calling
210 * getValueProperty in case of a non-function property, forwarding to parent if
213 template <class ThisImp
, class ParentImp
>
214 inline bool getStaticPropertySlot(ExecState
* exec
, const HashTable
& table
, ThisImp
* thisObj
, PropertyName propertyName
, PropertySlot
& slot
)
216 const HashTableValue
* entry
= table
.entry(exec
, propertyName
);
218 if (!entry
) // not found, forward to parent
219 return ParentImp::getOwnPropertySlot(thisObj
, exec
, propertyName
, slot
);
221 if (entry
->attributes() & BuiltinOrFunction
)
222 return setUpStaticFunctionSlot(exec
, entry
, thisObj
, propertyName
, slot
);
224 if (entry
->attributes() & ConstantInteger
) {
225 slot
.setValue(thisObj
, entry
->attributes(), jsNumber(entry
->constantInteger()));
229 slot
.setCacheableCustom(thisObj
, entry
->attributes(), entry
->propertyGetter());
234 * Simplified version of getStaticPropertySlot in case there are only functions.
235 * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
236 * a dummy getValueProperty.
238 template <class ParentImp
>
239 inline bool getStaticFunctionSlot(ExecState
* exec
, const HashTable
& table
, JSObject
* thisObj
, PropertyName propertyName
, PropertySlot
& slot
)
241 if (ParentImp::getOwnPropertySlot(thisObj
, exec
, propertyName
, slot
))
244 const HashTableValue
* entry
= table
.entry(exec
, propertyName
);
248 return setUpStaticFunctionSlot(exec
, entry
, thisObj
, propertyName
, slot
);
252 * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
253 * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
255 template <class ThisImp
, class ParentImp
>
256 inline bool getStaticValueSlot(ExecState
* exec
, const HashTable
& table
, ThisImp
* thisObj
, PropertyName propertyName
, PropertySlot
& slot
)
258 const HashTableValue
* entry
= table
.entry(exec
, propertyName
);
260 if (!entry
) // not found, forward to parent
261 return ParentImp::getOwnPropertySlot(thisObj
, exec
, propertyName
, slot
);
263 ASSERT(!(entry
->attributes() & BuiltinOrFunction
));
265 if (entry
->attributes() & ConstantInteger
) {
266 slot
.setValue(thisObj
, entry
->attributes(), jsNumber(entry
->constantInteger()));
270 slot
.setCacheableCustom(thisObj
, entry
->attributes(), entry
->propertyGetter());
274 inline void putEntry(ExecState
* exec
, const HashTableValue
* entry
, JSObject
* base
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
276 // If this is a function put it as an override property.
277 if (entry
->attributes() & BuiltinOrFunction
) {
278 if (JSObject
* thisObject
= jsDynamicCast
<JSObject
*>(slot
.thisValue()))
279 thisObject
->putDirect(exec
->vm(), propertyName
, value
);
280 } else if (!(entry
->attributes() & ReadOnly
)) {
281 entry
->propertyPutter()(exec
, base
, JSValue::encode(slot
.thisValue()), JSValue::encode(value
));
282 slot
.setCustomProperty(base
, entry
->propertyPutter());
283 } else if (slot
.isStrictMode())
284 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
288 * This one is for "put".
289 * It looks up a hash entry for the property to be set. If an entry
290 * is found it sets the value and returns true, else it returns false.
292 inline bool lookupPut(ExecState
* exec
, PropertyName propertyName
, JSObject
* base
, JSValue value
, const HashTable
& table
, PutPropertySlot
& slot
)
294 const HashTableValue
* entry
= table
.entry(exec
, propertyName
);
299 putEntry(exec
, entry
, base
, propertyName
, value
, slot
);
303 template<unsigned numberOfValues
>
304 inline void reifyStaticProperties(VM
& vm
, const HashTableValue (&values
)[numberOfValues
], JSObject
& thisObj
)
306 BatchedTransitionOptimizer
transitionOptimizer(vm
, &thisObj
);
307 for (auto& value
: values
) {
311 Identifier
propertyName(&vm
, reinterpret_cast<const LChar
*>(value
.m_key
), strlen(value
.m_key
));
312 if (value
.attributes() & Builtin
) {
313 thisObj
.putDirectBuiltinFunction(vm
, thisObj
.globalObject(), propertyName
, value
.builtinGenerator()(vm
), value
.attributes());
317 if (value
.attributes() & Function
) {
318 thisObj
.putDirectNativeFunction(vm
, thisObj
.globalObject(), propertyName
, value
.functionLength(),
319 value
.function(), value
.intrinsic(), value
.attributes());
323 if (value
.attributes() & ConstantInteger
) {
324 thisObj
.putDirect(vm
, propertyName
, jsNumber(value
.constantInteger()), value
.attributes());
328 if (value
.attributes() & Accessor
) {
329 RELEASE_ASSERT_NOT_REACHED();
333 CustomGetterSetter
* customGetterSetter
= CustomGetterSetter::create(vm
, value
.propertyGetter(), value
.propertyPutter());
334 thisObj
.putDirectCustomAccessor(vm
, propertyName
, customGetterSetter
, value
.attributes());