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());