source: webkit/trunk/JavaScriptCore/runtime/Structure.cpp@ 39958

Last change on this file since 39958 was 39851, checked in by [email protected], 16 years ago

JavaScriptCore:

2009-01-12 Gavin Barraclough <[email protected]>

Reviewed by Oliver Hunt.

Make the JSImmediate interface private.

All manipulation of JS values should be through the JSValuePtr class, not by using JSImmediate
directly. The key missing methods on JSValuePtr are:

  • isCell() - check for values that are JSCell*s, and as such where asCell() may be used.
  • isInt32Fast() getInt32Fast() - fast check/access for integer immediates.
  • isUInt32Fast() getUInt32Fast() - ditto for unsigned integer immediates.

The JIT is allowed full access to JSImmediate, since it needs to be able to directly
manipulate JSValuePtrs. The Interpreter is provided access to perform operations directly
on JSValuePtrs through the new JSFastMath interface.

No performance impact.

  • API/JSCallbackObjectFunctions.h: (JSC::::toNumber):
  • API/JSValueRef.cpp: (JSValueIsEqual): (JSValueIsStrictEqual):
  • JavaScriptCore.exp:
  • bytecode/CodeBlock.h: (JSC::CodeBlock::isKnownNotImmediate):
  • bytecompiler/BytecodeGenerator.cpp: (JSC::keyForImmediateSwitch):
  • bytecompiler/BytecodeGenerator.h: (JSC::BytecodeGenerator::JSValueHashTraits::constructDeletedValue): (JSC::BytecodeGenerator::JSValueHashTraits::isDeletedValue):
  • interpreter/Interpreter.cpp: (JSC::jsLess): (JSC::jsLessEq): (JSC::jsAdd): (JSC::jsIsObjectType): (JSC::cachePrototypeChain): (JSC::Interpreter::tryCachePutByID): (JSC::Interpreter::tryCacheGetByID): (JSC::Interpreter::privateExecute): (JSC::Interpreter::tryCTICachePutByID): (JSC::Interpreter::tryCTICacheGetByID): (JSC::Interpreter::cti_op_add): (JSC::Interpreter::cti_op_get_by_id_self_fail): (JSC::Interpreter::cti_op_get_by_id_proto_list): (JSC::Interpreter::cti_op_instanceof): (JSC::Interpreter::cti_op_mul): (JSC::Interpreter::cti_op_get_by_val): (JSC::Interpreter::cti_op_get_by_val_byte_array): (JSC::Interpreter::cti_op_sub): (JSC::Interpreter::cti_op_put_by_val): (JSC::Interpreter::cti_op_put_by_val_array): (JSC::Interpreter::cti_op_put_by_val_byte_array): (JSC::Interpreter::cti_op_negate): (JSC::Interpreter::cti_op_div): (JSC::Interpreter::cti_op_eq): (JSC::Interpreter::cti_op_lshift): (JSC::Interpreter::cti_op_bitand): (JSC::Interpreter::cti_op_rshift): (JSC::Interpreter::cti_op_bitnot): (JSC::Interpreter::cti_op_neq): (JSC::Interpreter::cti_op_urshift): (JSC::Interpreter::cti_op_call_eval): (JSC::Interpreter::cti_op_throw): (JSC::Interpreter::cti_op_is_undefined): (JSC::Interpreter::cti_op_stricteq): (JSC::Interpreter::cti_op_nstricteq): (JSC::Interpreter::cti_op_switch_imm): (JSC::Interpreter::cti_vm_throw):
  • interpreter/Interpreter.h: (JSC::Interpreter::isJSArray): (JSC::Interpreter::isJSString): (JSC::Interpreter::isJSByteArray):
  • jit/JIT.cpp: (JSC::JIT::compileOpStrictEq): (JSC::JIT::privateCompileMainPass):
  • jit/JIT.h: (JSC::JIT::isStrictEqCaseHandledInJITCode):
  • jit/JITArithmetic.cpp: (JSC::JIT::compileFastArith_op_rshift): (JSC::JIT::compileFastArith_op_bitand): (JSC::JIT::compileFastArith_op_mod):
  • jit/JITCall.cpp: (JSC::JIT::unlinkCall): (JSC::JIT::compileOpCall):
  • jit/JITInlineMethods.h: (JSC::JIT::getConstantOperandImmediateInt): (JSC::JIT::isOperandConstantImmediateInt):
  • parser/Nodes.cpp: (JSC::processClauseList):
  • runtime/ArrayPrototype.cpp: (JSC::arrayProtoFuncIndexOf): (JSC::arrayProtoFuncLastIndexOf):
  • runtime/BooleanPrototype.cpp: (JSC::booleanProtoFuncValueOf):
  • runtime/Collector.cpp: (JSC::Heap::protect): (JSC::Heap::unprotect): (JSC::Heap::heap):
  • runtime/JSByteArray.cpp: (JSC::JSByteArray::getOwnPropertySlot):
  • runtime/JSByteArray.h: (JSC::JSByteArray::getIndex):
  • runtime/JSCell.cpp:
  • runtime/JSCell.h: (JSC::JSValuePtr::isNumberCell): (JSC::JSValuePtr::asCell): (JSC::JSValuePtr::isNumber):
  • runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncParseInt):
  • runtime/JSImmediate.h: (JSC::js0): (JSC::jsImpossibleValue): (JSC::JSValuePtr::toInt32): (JSC::JSValuePtr::toUInt32): (JSC::JSValuePtr::isCell): (JSC::JSValuePtr::isInt32Fast): (JSC::JSValuePtr::getInt32Fast): (JSC::JSValuePtr::isUInt32Fast): (JSC::JSValuePtr::getUInt32Fast): (JSC::JSValuePtr::makeInt32Fast): (JSC::JSValuePtr::areBothInt32Fast): (JSC::JSFastMath::canDoFastBitwiseOperations): (JSC::JSFastMath::equal): (JSC::JSFastMath::notEqual): (JSC::JSFastMath::andImmediateNumbers): (JSC::JSFastMath::xorImmediateNumbers): (JSC::JSFastMath::orImmediateNumbers): (JSC::JSFastMath::canDoFastRshift): (JSC::JSFastMath::canDoFastUrshift): (JSC::JSFastMath::rightShiftImmediateNumbers): (JSC::JSFastMath::canDoFastAdditiveOperations): (JSC::JSFastMath::addImmediateNumbers): (JSC::JSFastMath::subImmediateNumbers): (JSC::JSFastMath::incImmediateNumber): (JSC::JSFastMath::decImmediateNumber):
  • runtime/JSNumberCell.h: (JSC::JSValuePtr::asNumberCell): (JSC::jsNumber): (JSC::JSValuePtr::uncheckedGetNumber): (JSC::JSNumberCell::toInt32): (JSC::JSNumberCell::toUInt32): (JSC::JSValuePtr::toJSNumber): (JSC::JSValuePtr::getNumber): (JSC::JSValuePtr::numberToInt32): (JSC::JSValuePtr::numberToUInt32):
  • runtime/JSObject.h: (JSC::JSValuePtr::isObject): (JSC::JSValuePtr::get): (JSC::JSValuePtr::put):
  • runtime/JSValue.cpp: (JSC::JSValuePtr::toInteger): (JSC::JSValuePtr::toIntegerPreserveNaN):
  • runtime/JSValue.h:
  • runtime/Operations.cpp: (JSC::JSValuePtr::equalSlowCase): (JSC::JSValuePtr::strictEqualSlowCase):
  • runtime/Operations.h: (JSC::JSValuePtr::equal): (JSC::JSValuePtr::equalSlowCaseInline): (JSC::JSValuePtr::strictEqual): (JSC::JSValuePtr::strictEqualSlowCaseInline):
  • runtime/Protect.h: (JSC::gcProtect): (JSC::gcUnprotect):
  • runtime/StringPrototype.cpp: (JSC::stringProtoFuncCharAt): (JSC::stringProtoFuncCharCodeAt):
  • runtime/Structure.cpp: (JSC::Structure::createCachedPrototypeChain):

WebCore:

2009-01-12 Gavin Barraclough <[email protected]>

Reviewed by Oliver Hunt.

Deprecate JSValuePtr::getNumber() - two ways to get a number should be enough.

  • bindings/js/JSSQLTransactionCustom.cpp: (WebCore::JSSQLTransaction::executeSql):
  • bindings/objc/WebScriptObject.mm: (+[WebScriptObject _convertValueToObjcValue:originRootObject:rootObject:]):

WebKit/mac:

2009-01-12 Gavin Barraclough <[email protected]>

Reviewed by Oliver Hunt.

Deprecate JSValuePtr::getNumber() - two ways to get a number should be enough.

  • WebView/WebView.mm: (aeDescFromJSValue):
File size: 33.6 KB
Line 
1/*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Structure.h"
28
29#include "Identifier.h"
30#include "JSObject.h"
31#include "PropertyNameArray.h"
32#include "StructureChain.h"
33#include "Lookup.h"
34#include <wtf/RefCountedLeakCounter.h>
35#include <wtf/RefPtr.h>
36
37#if ENABLE(JSC_MULTIPLE_THREADS)
38#include <wtf/Threading.h>
39#endif
40
41#define DUMP_STRUCTURE_ID_STATISTICS 0
42
43#ifndef NDEBUG
44#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
45#else
46#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
47#endif
48
49using namespace std;
50using namespace WTF;
51
52namespace JSC {
53
54// Choose a number for the following so that most property maps are smaller,
55// but it's not going to blow out the stack to allocate this number of pointers.
56static const int smallMapThreshold = 1024;
57
58// The point at which the function call overhead of the qsort implementation
59// becomes small compared to the inefficiency of insertion sort.
60static const unsigned tinyMapThreshold = 20;
61
62static const unsigned newTableSize = 16;
63
64#ifndef NDEBUG
65static WTF::RefCountedLeakCounter structureCounter("Structure");
66
67#if ENABLE(JSC_MULTIPLE_THREADS)
68static Mutex ignoreSetMutex;
69#endif
70
71static bool shouldIgnoreLeaks;
72static HashSet<Structure*> ignoreSet;
73#endif
74
75#if DUMP_STRUCTURE_ID_STATISTICS
76static HashSet<Structure*> liveStructureSet;
77#endif
78
79void Structure::dumpStatistics()
80{
81#if DUMP_STRUCTURE_ID_STATISTICS
82 unsigned numberLeaf = 0;
83 unsigned numberUsingSingleSlot = 0;
84 unsigned numberSingletons = 0;
85 unsigned numberWithPropertyMaps = 0;
86 unsigned totalPropertyMapsSize = 0;
87
88 HashSet<Structure*>::const_iterator end = liveStructureSet.end();
89 for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
90 Structure* structure = *it;
91 if (structure->m_usingSingleTransitionSlot) {
92 if (!structure->m_transitions.singleTransition)
93 ++numberLeaf;
94 else
95 ++numberUsingSingleSlot;
96
97 if (!structure->m_previous && !structure->m_transitions.singleTransition)
98 ++numberSingletons;
99 }
100
101 if (structure->m_propertyTable) {
102 ++numberWithPropertyMaps;
103 totalPropertyMapsSize += PropertyMapHashTable::allocationSize(structure->m_propertyTable->size);
104 if (structure->m_propertyTable->deletedOffsets)
105 totalPropertyMapsSize += (structure->m_propertyTable->deletedOffsets->capacity() * sizeof(unsigned));
106 }
107 }
108
109 printf("Number of live Structures: %d\n", liveStructureSet.size());
110 printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
111 printf("Number of Structures that are leaf nodes: %d\n", numberLeaf);
112 printf("Number of Structures that singletons: %d\n", numberSingletons);
113 printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
114
115 printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
116 printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
117 printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
118#else
119 printf("Dumping Structure statistics is not enabled.\n");
120#endif
121}
122
123Structure::Structure(JSValuePtr prototype, const TypeInfo& typeInfo)
124 : m_typeInfo(typeInfo)
125 , m_prototype(prototype)
126 , m_cachedPrototypeChain(0)
127 , m_previous(0)
128 , m_nameInPrevious(0)
129 , m_propertyTable(0)
130 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
131 , m_offset(noOffset)
132 , m_isDictionary(false)
133 , m_isPinnedPropertyTable(false)
134 , m_hasGetterSetterProperties(false)
135 , m_usingSingleTransitionSlot(true)
136 , m_attributesInPrevious(0)
137{
138 ASSERT(m_prototype);
139 ASSERT(m_prototype->isObject() || m_prototype->isNull());
140
141 m_transitions.singleTransition = 0;
142
143#ifndef NDEBUG
144#if ENABLE(JSC_MULTIPLE_THREADS)
145 MutexLocker protect(ignoreSetMutex);
146#endif
147 if (shouldIgnoreLeaks)
148 ignoreSet.add(this);
149 else
150 structureCounter.increment();
151#endif
152
153#if DUMP_STRUCTURE_ID_STATISTICS
154 liveStructureSet.add(this);
155#endif
156}
157
158Structure::~Structure()
159{
160 if (m_previous) {
161 if (m_previous->m_usingSingleTransitionSlot) {
162 m_previous->m_transitions.singleTransition = 0;
163 } else {
164 ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious.get(), m_attributesInPrevious)));
165 m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious));
166 }
167 }
168
169 if (m_cachedPropertyNameArrayData)
170 m_cachedPropertyNameArrayData->setCachedStructure(0);
171
172 if (!m_usingSingleTransitionSlot)
173 delete m_transitions.table;
174
175 if (m_propertyTable) {
176 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
177 for (unsigned i = 1; i <= entryCount; i++) {
178 if (UString::Rep* key = m_propertyTable->entries()[i].key)
179 key->deref();
180 }
181 fastFree(m_propertyTable);
182 }
183
184#ifndef NDEBUG
185#if ENABLE(JSC_MULTIPLE_THREADS)
186 MutexLocker protect(ignoreSetMutex);
187#endif
188 HashSet<Structure*>::iterator it = ignoreSet.find(this);
189 if (it != ignoreSet.end())
190 ignoreSet.remove(it);
191 else
192 structureCounter.decrement();
193#endif
194
195#if DUMP_STRUCTURE_ID_STATISTICS
196 liveStructureSet.remove(this);
197#endif
198}
199
200void Structure::startIgnoringLeaks()
201{
202#ifndef NDEBUG
203 shouldIgnoreLeaks = true;
204#endif
205}
206
207void Structure::stopIgnoringLeaks()
208{
209#ifndef NDEBUG
210 shouldIgnoreLeaks = false;
211#endif
212}
213
214static bool isPowerOf2(unsigned v)
215{
216 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
217
218 return !(v & (v - 1)) && v;
219}
220
221static unsigned nextPowerOf2(unsigned v)
222{
223 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
224 // Devised by Sean Anderson, Sepember 14, 2001
225
226 v--;
227 v |= v >> 1;
228 v |= v >> 2;
229 v |= v >> 4;
230 v |= v >> 8;
231 v |= v >> 16;
232 v++;
233
234 return v;
235}
236
237static unsigned sizeForKeyCount(size_t keyCount)
238{
239 if (keyCount == notFound)
240 return newTableSize;
241
242 if (keyCount < 8)
243 return newTableSize;
244
245 if (isPowerOf2(keyCount))
246 return keyCount * 4;
247
248 return nextPowerOf2(keyCount) * 2;
249}
250
251void Structure::materializePropertyMap()
252{
253 ASSERT(!m_propertyTable);
254
255 Vector<Structure*, 8> structures;
256 structures.append(this);
257
258 Structure* structure = this;
259
260 // Search for the last Structure with a property table.
261 while ((structure = structure->previousID())) {
262 if (structure->m_isPinnedPropertyTable) {
263 ASSERT(structure->m_propertyTable);
264 ASSERT(!structure->m_previous);
265
266 m_propertyTable = structure->copyPropertyTable();
267 break;
268 }
269
270 structures.append(structure);
271 }
272
273 if (!m_propertyTable)
274 createPropertyMapHashTable(sizeForKeyCount(m_offset + 1));
275 else {
276 if (sizeForKeyCount(m_offset + 1) > m_propertyTable->size)
277 rehashPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); // This could be made more efficient by combining with the copy above.
278 }
279
280 for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
281 structure = structures[i];
282 structure->m_nameInPrevious->ref();
283 PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, ++m_propertyTable->lastIndexUsed);
284 insertIntoPropertyMapHashTable(entry);
285 }
286}
287
288void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
289{
290 bool shouldCache = propertyNames.cacheable() && !(propertyNames.size() || m_isDictionary);
291
292 if (shouldCache) {
293 if (m_cachedPropertyNameArrayData) {
294 if (structureChainsAreEqual(m_cachedPropertyNameArrayData->cachedPrototypeChain(), cachedPrototypeChain())) {
295 propertyNames.setData(m_cachedPropertyNameArrayData);
296 return;
297 }
298 }
299 propertyNames.setCacheable(false);
300 }
301
302 getEnumerablePropertyNamesInternal(propertyNames);
303
304 // Add properties from the static hashtables of properties
305 for (const ClassInfo* info = baseObject->classInfo(); info; info = info->parentClass) {
306 const HashTable* table = info->propHashTable(exec);
307 if (!table)
308 continue;
309 table->initializeIfNeeded(exec);
310 ASSERT(table->table);
311#if ENABLE(PERFECT_HASH_SIZE)
312 int hashSizeMask = table->hashSizeMask;
313#else
314 int hashSizeMask = table->compactSize - 1;
315#endif
316 const HashEntry* entry = table->table;
317 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
318 if (entry->key() && !(entry->attributes() & DontEnum))
319 propertyNames.add(entry->key());
320 }
321 }
322
323 if (m_prototype->isObject())
324 asObject(m_prototype)->getPropertyNames(exec, propertyNames);
325
326 if (shouldCache) {
327 if (m_cachedPropertyNameArrayData)
328 m_cachedPropertyNameArrayData->setCachedStructure(0);
329
330 m_cachedPropertyNameArrayData = propertyNames.data();
331
332 StructureChain* chain = cachedPrototypeChain();
333 if (!chain)
334 chain = createCachedPrototypeChain();
335 m_cachedPropertyNameArrayData->setCachedPrototypeChain(chain);
336 m_cachedPropertyNameArrayData->setCachedStructure(this);
337 }
338}
339
340void Structure::clearEnumerationCache()
341{
342 if (m_cachedPropertyNameArrayData)
343 m_cachedPropertyNameArrayData->setCachedStructure(0);
344 m_cachedPropertyNameArrayData.clear();
345}
346
347void Structure::growPropertyStorageCapacity()
348{
349 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
350 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
351 else
352 m_propertyStorageCapacity *= 2;
353}
354
355PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, size_t& offset)
356{
357 ASSERT(!structure->m_isDictionary);
358 ASSERT(structure->typeInfo().type() == ObjectType);
359
360 if (structure->m_usingSingleTransitionSlot) {
361 Structure* existingTransition = structure->m_transitions.singleTransition;
362 if (existingTransition && existingTransition->m_nameInPrevious.get() == propertyName.ustring().rep() && existingTransition->m_attributesInPrevious == attributes) {
363 ASSERT(structure->m_transitions.singleTransition->m_offset != noOffset);
364 offset = structure->m_transitions.singleTransition->m_offset;
365 return existingTransition;
366 }
367 } else {
368 if (Structure* existingTransition = structure->m_transitions.table->get(make_pair(propertyName.ustring().rep(), attributes))) {
369 ASSERT(existingTransition->m_offset != noOffset);
370 offset = existingTransition->m_offset;
371 return existingTransition;
372 }
373 }
374
375 return 0;
376}
377
378PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, size_t& offset)
379{
380 ASSERT(!structure->m_isDictionary);
381 ASSERT(structure->typeInfo().type() == ObjectType);
382 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, offset));
383
384 if (structure->transitionCount() > s_maxTransitionLength) {
385 RefPtr<Structure> transition = toDictionaryTransition(structure);
386 offset = transition->put(propertyName, attributes);
387 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
388 transition->growPropertyStorageCapacity();
389 return transition.release();
390 }
391
392 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
393 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
394 transition->m_previous = structure;
395 transition->m_nameInPrevious = propertyName.ustring().rep();
396 transition->m_attributesInPrevious = attributes;
397 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
398 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
399
400 if (structure->m_propertyTable) {
401 if (structure->m_isPinnedPropertyTable)
402 transition->m_propertyTable = structure->copyPropertyTable();
403 else {
404 transition->m_propertyTable = structure->m_propertyTable;
405 structure->m_propertyTable = 0;
406 }
407 } else {
408 if (structure->m_previous)
409 transition->materializePropertyMap();
410 else
411 transition->createPropertyMapHashTable();
412 }
413
414 offset = transition->put(propertyName, attributes);
415 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
416 transition->growPropertyStorageCapacity();
417
418 transition->m_offset = offset;
419
420 if (structure->m_usingSingleTransitionSlot) {
421 if (!structure->m_transitions.singleTransition) {
422 structure->m_transitions.singleTransition = transition.get();
423 return transition.release();
424 }
425
426 Structure* existingTransition = structure->m_transitions.singleTransition;
427 structure->m_usingSingleTransitionSlot = false;
428 StructureTransitionTable* transitionTable = new StructureTransitionTable;
429 structure->m_transitions.table = transitionTable;
430 transitionTable->add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition);
431 }
432 structure->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get());
433 return transition.release();
434}
435
436PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset)
437{
438 ASSERT(!structure->m_isDictionary);
439
440 RefPtr<Structure> transition = toDictionaryTransition(structure);
441
442 offset = transition->remove(propertyName);
443
444 return transition.release();
445}
446
447PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValuePtr prototype)
448{
449 RefPtr<Structure> transition = create(prototype, structure->typeInfo());
450
451 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
452 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
453
454 // Don't set m_offset, as one can not transition to this.
455
456 structure->materializePropertyMapIfNecessary();
457 transition->m_propertyTable = structure->copyPropertyTable();
458 transition->m_isPinnedPropertyTable = true;
459
460 return transition.release();
461}
462
463PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
464{
465 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
466 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
467 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
468
469 // Don't set m_offset, as one can not transition to this.
470
471 structure->materializePropertyMapIfNecessary();
472 transition->m_propertyTable = structure->copyPropertyTable();
473 transition->m_isPinnedPropertyTable = true;
474
475 return transition.release();
476}
477
478PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure)
479{
480 ASSERT(!structure->m_isDictionary);
481
482 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
483 transition->m_isDictionary = true;
484 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
485 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
486
487 structure->materializePropertyMapIfNecessary();
488 transition->m_propertyTable = structure->copyPropertyTable();
489 transition->m_isPinnedPropertyTable = true;
490
491 return transition.release();
492}
493
494PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure)
495{
496 ASSERT(structure->m_isDictionary);
497
498 // Since dictionary Structures are not shared, and no opcodes specialize
499 // for them, we don't need to allocate a new Structure when transitioning
500 // to non-dictionary status.
501
502 // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the
503 // deleted offsets vector) before transitioning from dictionary.
504 if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty())
505 structure->m_isDictionary = false;
506
507 return structure;
508}
509
510size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes)
511{
512 ASSERT(!m_transitions.singleTransition);
513
514 materializePropertyMapIfNecessary();
515
516 m_isPinnedPropertyTable = true;
517 size_t offset = put(propertyName, attributes);
518 if (propertyStorageSize() > propertyStorageCapacity())
519 growPropertyStorageCapacity();
520 clearEnumerationCache();
521 return offset;
522}
523
524size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
525{
526 ASSERT(!m_transitions.singleTransition);
527 ASSERT(m_isDictionary);
528
529 materializePropertyMapIfNecessary();
530
531 m_isPinnedPropertyTable = true;
532 size_t offset = remove(propertyName);
533 clearEnumerationCache();
534 return offset;
535}
536
537StructureChain* Structure::createCachedPrototypeChain()
538{
539 ASSERT(typeInfo().type() == ObjectType);
540 ASSERT(!m_cachedPrototypeChain);
541
542 JSValuePtr prototype = storedPrototype();
543 if (!prototype->isCell())
544 return 0;
545
546 RefPtr<StructureChain> chain = StructureChain::create(asObject(prototype)->structure());
547 setCachedPrototypeChain(chain.release());
548 return cachedPrototypeChain();
549}
550
551#if DUMP_PROPERTYMAP_STATS
552
553static int numProbes;
554static int numCollisions;
555static int numRehashes;
556static int numRemoves;
557
558struct PropertyMapStatisticsExitLogger {
559 ~PropertyMapStatisticsExitLogger();
560};
561
562static PropertyMapStatisticsExitLogger logger;
563
564PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
565{
566 printf("\nJSC::PropertyMap statistics\n\n");
567 printf("%d probes\n", numProbes);
568 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
569 printf("%d rehashes\n", numRehashes);
570 printf("%d removes\n", numRemoves);
571}
572
573#endif
574
575static const unsigned deletedSentinelIndex = 1;
576
577#if !DO_PROPERTYMAP_CONSTENCY_CHECK
578
579inline void Structure::checkConsistency()
580{
581}
582
583#endif
584
585PropertyMapHashTable* Structure::copyPropertyTable()
586{
587 if (!m_propertyTable)
588 return 0;
589
590 size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size);
591 PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize));
592 memcpy(newTable, m_propertyTable, tableSize);
593
594 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
595 for (unsigned i = 1; i <= entryCount; ++i) {
596 if (UString::Rep* key = newTable->entries()[i].key)
597 key->ref();
598 }
599
600 // Copy the deletedOffsets vector.
601 if (m_propertyTable->deletedOffsets)
602 newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets);
603
604 return newTable;
605}
606
607size_t Structure::get(const Identifier& propertyName, unsigned& attributes)
608{
609 ASSERT(!propertyName.isNull());
610
611 materializePropertyMapIfNecessary();
612 if (!m_propertyTable)
613 return notFound;
614
615 UString::Rep* rep = propertyName._ustring.rep();
616
617 unsigned i = rep->computedHash();
618
619#if DUMP_PROPERTYMAP_STATS
620 ++numProbes;
621#endif
622
623 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
624 if (entryIndex == emptyEntryIndex)
625 return notFound;
626
627 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
628 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
629 return m_propertyTable->entries()[entryIndex - 1].offset;
630 }
631
632#if DUMP_PROPERTYMAP_STATS
633 ++numCollisions;
634#endif
635
636 unsigned k = 1 | doubleHash(rep->computedHash());
637
638 while (1) {
639 i += k;
640
641#if DUMP_PROPERTYMAP_STATS
642 ++numRehashes;
643#endif
644
645 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
646 if (entryIndex == emptyEntryIndex)
647 return notFound;
648
649 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
650 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
651 return m_propertyTable->entries()[entryIndex - 1].offset;
652 }
653 }
654}
655
656size_t Structure::put(const Identifier& propertyName, unsigned attributes)
657{
658 ASSERT(!propertyName.isNull());
659 ASSERT(get(propertyName) == notFound);
660
661 checkConsistency();
662
663 UString::Rep* rep = propertyName._ustring.rep();
664
665 if (!m_propertyTable)
666 createPropertyMapHashTable();
667
668 // FIXME: Consider a fast case for tables with no deleted sentinels.
669
670 unsigned i = rep->computedHash();
671 unsigned k = 0;
672 bool foundDeletedElement = false;
673 unsigned deletedElementIndex = 0; // initialize to make the compiler happy
674
675#if DUMP_PROPERTYMAP_STATS
676 ++numProbes;
677#endif
678
679 while (1) {
680 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
681 if (entryIndex == emptyEntryIndex)
682 break;
683
684 if (entryIndex == deletedSentinelIndex) {
685 // If we find a deleted-element sentinel, remember it for use later.
686 if (!foundDeletedElement) {
687 foundDeletedElement = true;
688 deletedElementIndex = i;
689 }
690 }
691
692 if (k == 0) {
693 k = 1 | doubleHash(rep->computedHash());
694#if DUMP_PROPERTYMAP_STATS
695 ++numCollisions;
696#endif
697 }
698
699 i += k;
700
701#if DUMP_PROPERTYMAP_STATS
702 ++numRehashes;
703#endif
704 }
705
706 // Figure out which entry to use.
707 unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2;
708 if (foundDeletedElement) {
709 i = deletedElementIndex;
710 --m_propertyTable->deletedSentinelCount;
711
712 // Since we're not making the table bigger, we can't use the entry one past
713 // the end that we were planning on using, so search backwards for the empty
714 // slot that we can use. We know it will be there because we did at least one
715 // deletion in the past that left an entry empty.
716 while (m_propertyTable->entries()[--entryIndex - 1].key) { }
717 }
718
719 // Create a new hash table entry.
720 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
721
722 // Create a new hash table entry.
723 rep->ref();
724 m_propertyTable->entries()[entryIndex - 1].key = rep;
725 m_propertyTable->entries()[entryIndex - 1].attributes = attributes;
726 m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed;
727
728 unsigned newOffset;
729 if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) {
730 newOffset = m_propertyTable->deletedOffsets->last();
731 m_propertyTable->deletedOffsets->removeLast();
732 } else
733 newOffset = m_propertyTable->keyCount;
734 m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
735
736 ++m_propertyTable->keyCount;
737
738 if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size)
739 expandPropertyMapHashTable();
740
741 checkConsistency();
742 return newOffset;
743}
744
745size_t Structure::remove(const Identifier& propertyName)
746{
747 ASSERT(!propertyName.isNull());
748
749 checkConsistency();
750
751 UString::Rep* rep = propertyName._ustring.rep();
752
753 if (!m_propertyTable)
754 return notFound;
755
756#if DUMP_PROPERTYMAP_STATS
757 ++numProbes;
758 ++numRemoves;
759#endif
760
761 // Find the thing to remove.
762 unsigned i = rep->computedHash();
763 unsigned k = 0;
764 unsigned entryIndex;
765 UString::Rep* key = 0;
766 while (1) {
767 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
768 if (entryIndex == emptyEntryIndex)
769 return notFound;
770
771 key = m_propertyTable->entries()[entryIndex - 1].key;
772 if (rep == key)
773 break;
774
775 if (k == 0) {
776 k = 1 | doubleHash(rep->computedHash());
777#if DUMP_PROPERTYMAP_STATS
778 ++numCollisions;
779#endif
780 }
781
782 i += k;
783
784#if DUMP_PROPERTYMAP_STATS
785 ++numRehashes;
786#endif
787 }
788
789 // Replace this one element with the deleted sentinel. Also clear out
790 // the entry so we can iterate all the entries as needed.
791 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex;
792
793 size_t offset = m_propertyTable->entries()[entryIndex - 1].offset;
794
795 key->deref();
796 m_propertyTable->entries()[entryIndex - 1].key = 0;
797 m_propertyTable->entries()[entryIndex - 1].attributes = 0;
798 m_propertyTable->entries()[entryIndex - 1].offset = 0;
799
800 if (!m_propertyTable->deletedOffsets)
801 m_propertyTable->deletedOffsets = new Vector<unsigned>;
802 m_propertyTable->deletedOffsets->append(offset);
803
804 ASSERT(m_propertyTable->keyCount >= 1);
805 --m_propertyTable->keyCount;
806 ++m_propertyTable->deletedSentinelCount;
807
808 if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size)
809 rehashPropertyMapHashTable();
810
811 checkConsistency();
812 return offset;
813}
814
815void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry)
816{
817 ASSERT(m_propertyTable);
818
819 unsigned i = entry.key->computedHash();
820 unsigned k = 0;
821
822#if DUMP_PROPERTYMAP_STATS
823 ++numProbes;
824#endif
825
826 while (1) {
827 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
828 if (entryIndex == emptyEntryIndex)
829 break;
830
831 if (k == 0) {
832 k = 1 | doubleHash(entry.key->computedHash());
833#if DUMP_PROPERTYMAP_STATS
834 ++numCollisions;
835#endif
836 }
837
838 i += k;
839
840#if DUMP_PROPERTYMAP_STATS
841 ++numRehashes;
842#endif
843 }
844
845 unsigned entryIndex = m_propertyTable->keyCount + 2;
846 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
847 m_propertyTable->entries()[entryIndex - 1] = entry;
848
849 ++m_propertyTable->keyCount;
850}
851
852void Structure::createPropertyMapHashTable()
853{
854 ASSERT(sizeForKeyCount(7) == newTableSize);
855 createPropertyMapHashTable(newTableSize);
856}
857
858void Structure::createPropertyMapHashTable(unsigned newTableSize)
859{
860 ASSERT(!m_propertyTable);
861 ASSERT(isPowerOf2(newTableSize));
862
863 checkConsistency();
864
865 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
866 m_propertyTable->size = newTableSize;
867 m_propertyTable->sizeMask = newTableSize - 1;
868
869 checkConsistency();
870}
871
872void Structure::expandPropertyMapHashTable()
873{
874 ASSERT(m_propertyTable);
875 rehashPropertyMapHashTable(m_propertyTable->size * 2);
876}
877
878void Structure::rehashPropertyMapHashTable()
879{
880 ASSERT(m_propertyTable);
881 ASSERT(m_propertyTable->size);
882 rehashPropertyMapHashTable(m_propertyTable->size);
883}
884
885void Structure::rehashPropertyMapHashTable(unsigned newTableSize)
886{
887 ASSERT(m_propertyTable);
888 ASSERT(isPowerOf2(newTableSize));
889
890 checkConsistency();
891
892 PropertyMapHashTable* oldTable = m_propertyTable;
893
894 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
895 m_propertyTable->size = newTableSize;
896 m_propertyTable->sizeMask = newTableSize - 1;
897
898 unsigned lastIndexUsed = 0;
899 unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount;
900 for (unsigned i = 1; i <= entryCount; ++i) {
901 if (oldTable->entries()[i].key) {
902 lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed);
903 insertIntoPropertyMapHashTable(oldTable->entries()[i]);
904 }
905 }
906 m_propertyTable->lastIndexUsed = lastIndexUsed;
907 m_propertyTable->deletedOffsets = oldTable->deletedOffsets;
908
909 fastFree(oldTable);
910
911 checkConsistency();
912}
913
914static int comparePropertyMapEntryIndices(const void* a, const void* b)
915{
916 unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index;
917 unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index;
918 if (ia < ib)
919 return -1;
920 if (ia > ib)
921 return +1;
922 return 0;
923}
924
925void Structure::getEnumerablePropertyNamesInternal(PropertyNameArray& propertyNames)
926{
927 materializePropertyMapIfNecessary();
928 if (!m_propertyTable)
929 return;
930
931 if (m_propertyTable->keyCount < tinyMapThreshold) {
932 PropertyMapEntry* a[tinyMapThreshold];
933 int i = 0;
934 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
935 for (unsigned k = 1; k <= entryCount; k++) {
936 if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) {
937 PropertyMapEntry* value = &m_propertyTable->entries()[k];
938 int j;
939 for (j = i - 1; j >= 0 && a[j]->index > value->index; --j)
940 a[j + 1] = a[j];
941 a[j + 1] = value;
942 ++i;
943 }
944 }
945 if (!propertyNames.size()) {
946 for (int k = 0; k < i; ++k)
947 propertyNames.addKnownUnique(a[k]->key);
948 } else {
949 for (int k = 0; k < i; ++k)
950 propertyNames.add(a[k]->key);
951 }
952
953 return;
954 }
955
956 // Allocate a buffer to use to sort the keys.
957 Vector<PropertyMapEntry*, smallMapThreshold> sortedEnumerables(m_propertyTable->keyCount);
958
959 // Get pointers to the enumerable entries in the buffer.
960 PropertyMapEntry** p = sortedEnumerables.data();
961 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
962 for (unsigned i = 1; i <= entryCount; i++) {
963 if (m_propertyTable->entries()[i].key && !(m_propertyTable->entries()[i].attributes & DontEnum))
964 *p++ = &m_propertyTable->entries()[i];
965 }
966
967 size_t enumerableCount = p - sortedEnumerables.data();
968 // Sort the entries by index.
969 qsort(sortedEnumerables.data(), enumerableCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices);
970 sortedEnumerables.resize(enumerableCount);
971
972 // Put the keys of the sorted entries into the list.
973 if (!propertyNames.size()) {
974 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
975 propertyNames.addKnownUnique(sortedEnumerables[i]->key);
976 } else {
977 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
978 propertyNames.add(sortedEnumerables[i]->key);
979 }
980}
981
982#if DO_PROPERTYMAP_CONSTENCY_CHECK
983
984void Structure::checkConsistency()
985{
986 if (!m_propertyTable)
987 return;
988
989 ASSERT(m_propertyTable->size >= newTableSize);
990 ASSERT(m_propertyTable->sizeMask);
991 ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1);
992 ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask));
993
994 ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2);
995 ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4);
996
997 ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2);
998
999 unsigned indexCount = 0;
1000 unsigned deletedIndexCount = 0;
1001 for (unsigned a = 0; a != m_propertyTable->size; ++a) {
1002 unsigned entryIndex = m_propertyTable->entryIndices[a];
1003 if (entryIndex == emptyEntryIndex)
1004 continue;
1005 if (entryIndex == deletedSentinelIndex) {
1006 ++deletedIndexCount;
1007 continue;
1008 }
1009 ASSERT(entryIndex > deletedSentinelIndex);
1010 ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount);
1011 ++indexCount;
1012
1013 for (unsigned b = a + 1; b != m_propertyTable->size; ++b)
1014 ASSERT(m_propertyTable->entryIndices[b] != entryIndex);
1015 }
1016 ASSERT(indexCount == m_propertyTable->keyCount);
1017 ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount);
1018
1019 ASSERT(m_propertyTable->entries()[0].key == 0);
1020
1021 unsigned nonEmptyEntryCount = 0;
1022 for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) {
1023 UString::Rep* rep = m_propertyTable->entries()[c].key;
1024 if (!rep)
1025 continue;
1026 ++nonEmptyEntryCount;
1027 unsigned i = rep->computedHash();
1028 unsigned k = 0;
1029 unsigned entryIndex;
1030 while (1) {
1031 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
1032 ASSERT(entryIndex != emptyEntryIndex);
1033 if (rep == m_propertyTable->entries()[entryIndex - 1].key)
1034 break;
1035 if (k == 0)
1036 k = 1 | doubleHash(rep->computedHash());
1037 i += k;
1038 }
1039 ASSERT(entryIndex == c + 1);
1040 }
1041
1042 ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount);
1043}
1044
1045#endif // DO_PROPERTYMAP_CONSTENCY_CHECK
1046
1047} // namespace JSC
Note: See TracBrowser for help on using the repository browser.