1 | /* |
2 | * Copyright (C) 1999-2001 Harri Porten ([email protected]) |
3 | * Copyright (C) 2001 Peter Kelly ([email protected]) |
4 | * Copyright (C) 2003-2019 Apple Inc. All rights reserved. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #pragma once |
24 | |
25 | #include "ArrayConventions.h" |
26 | #include "ArrayStorage.h" |
27 | #include "Butterfly.h" |
28 | #include "CPU.h" |
29 | #include "CagedBarrierPtr.h" |
30 | #include "CallFrame.h" |
31 | #include "ClassInfo.h" |
32 | #include "CustomGetterSetter.h" |
33 | #include "DOMAttributeGetterSetter.h" |
34 | #include "Heap.h" |
35 | #include "IndexingHeaderInlines.h" |
36 | #include "JSCast.h" |
37 | #include "ObjectInitializationScope.h" |
38 | #include "PropertySlot.h" |
39 | #include "PropertyStorage.h" |
40 | #include "PutDirectIndexMode.h" |
41 | #include "PutPropertySlot.h" |
42 | #include "Structure.h" |
43 | #include "StructureTransitionTable.h" |
44 | #include "VM.h" |
45 | #include "JSString.h" |
46 | #include "SparseArrayValueMap.h" |
47 | #include <wtf/StdLibExtras.h> |
48 | |
49 | namespace JSC { |
50 | namespace DOMJIT { |
51 | class Signature; |
52 | } |
53 | |
54 | inline JSCell* getJSFunction(JSValue value) |
55 | { |
56 | if (value.isCell() && (value.asCell()->type() == JSFunctionType)) |
57 | return value.asCell(); |
58 | return 0; |
59 | } |
60 | |
61 | class Exception; |
62 | class GetterSetter; |
63 | class InternalFunction; |
64 | class JSFunction; |
65 | class ; |
66 | class MarkedBlock; |
67 | class PropertyDescriptor; |
68 | class PropertyNameArray; |
69 | class Structure; |
70 | class ThrowScope; |
71 | struct HashTable; |
72 | struct HashTableValue; |
73 | |
74 | JS_EXPORT_PRIVATE Exception* throwTypeError(ExecState*, ThrowScope&, const String&); |
75 | extern JS_EXPORT_PRIVATE const ASCIILiteral NonExtensibleObjectPropertyDefineError; |
76 | extern JS_EXPORT_PRIVATE const ASCIILiteral ReadonlyPropertyWriteError; |
77 | extern JS_EXPORT_PRIVATE const ASCIILiteral ReadonlyPropertyChangeError; |
78 | extern JS_EXPORT_PRIVATE const ASCIILiteral UnableToDeletePropertyError; |
79 | extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeAccessMechanismError; |
80 | extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeConfigurabilityError; |
81 | extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeEnumerabilityError; |
82 | extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeWritabilityError; |
83 | |
84 | COMPILE_ASSERT(PropertyAttribute::None < FirstInternalAttribute, None_is_below_FirstInternalAttribute); |
85 | COMPILE_ASSERT(PropertyAttribute::ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute); |
86 | COMPILE_ASSERT(PropertyAttribute::DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute); |
87 | COMPILE_ASSERT(PropertyAttribute::DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute); |
88 | COMPILE_ASSERT(PropertyAttribute::Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute); |
89 | |
90 | class JSFinalObject; |
91 | |
92 | class JSObject : public JSCell { |
93 | friend class BatchedTransitionOptimizer; |
94 | friend class JIT; |
95 | friend class JSCell; |
96 | friend class JSFinalObject; |
97 | friend class MarkedBlock; |
98 | JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(VM&, const HashTableValue*, JSObject*, PropertyName, PropertySlot&); |
99 | |
100 | enum PutMode { |
101 | PutModePut, |
102 | PutModeDefineOwnProperty, |
103 | }; |
104 | |
105 | public: |
106 | typedef JSCell Base; |
107 | |
108 | // This is a super dangerous method for JITs. Sometimes the JITs will want to create either a |
109 | // JSFinalObject or a JSArray. This is the method that will do that. |
110 | static JSObject* createRawObject(ExecState* exec, Structure* structure, Butterfly* = nullptr); |
111 | |
112 | JS_EXPORT_PRIVATE static size_t estimatedSize(JSCell*, VM&); |
113 | JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); |
114 | JS_EXPORT_PRIVATE static void heapSnapshot(JSCell*, HeapSnapshotBuilder&); |
115 | |
116 | JS_EXPORT_PRIVATE static String className(const JSObject*, VM&); |
117 | JS_EXPORT_PRIVATE static String calculatedClassName(JSObject*); |
118 | |
119 | // This function is what Object.prototype.toString() will use to get the name of |
120 | // an object when using Symbol.toStringTag fails. For the most part there is no |
121 | // difference between this and className(). The main use case is for new JS language |
122 | // objects to set the default tag to "Object". |
123 | JS_EXPORT_PRIVATE static String toStringName(const JSObject*, ExecState*); |
124 | |
125 | // This is the fully virtual [[GetPrototypeOf]] internal function defined |
126 | // in the ECMAScript 6 specification. Use this when doing a [[GetPrototypeOf]] |
127 | // operation as dictated in the specification. |
128 | JSValue getPrototype(VM&, ExecState*); |
129 | JS_EXPORT_PRIVATE static JSValue getPrototype(JSObject*, ExecState*); |
130 | // This gets the prototype directly off of the structure. This does not do |
131 | // dynamic dispatch on the getPrototype method table method. It is not valid |
132 | // to use this when performing a [[GetPrototypeOf]] operation in the specification. |
133 | // It is valid to use though when you know that you want to directly get it |
134 | // without consulting the method table. This is akin to getting the [[Prototype]] |
135 | // internal field directly as described in the specification. |
136 | JSValue getPrototypeDirect(VM&) const; |
137 | |
138 | // This sets the prototype without checking for cycles and without |
139 | // doing dynamic dispatch on [[SetPrototypeOf]] operation in the specification. |
140 | // It is not valid to use this when performing a [[SetPrototypeOf]] operation in |
141 | // the specification. It is valid to use though when you know that you want to directly |
142 | // set it without consulting the method table and when you definitely won't |
143 | // introduce a cycle in the prototype chain. This is akin to setting the |
144 | // [[Prototype]] internal field directly as described in the specification. |
145 | JS_EXPORT_PRIVATE void setPrototypeDirect(VM&, JSValue prototype); |
146 | private: |
147 | // This is OrdinarySetPrototypeOf in the specification. Section 9.1.2.1 |
148 | // https://tc39.github.io/ecma262/#sec-ordinarysetprototypeof |
149 | JS_EXPORT_PRIVATE bool setPrototypeWithCycleCheck(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet); |
150 | public: |
151 | // This is the fully virtual [[SetPrototypeOf]] internal function defined |
152 | // in the ECMAScript 6 specification. Use this when doing a [[SetPrototypeOf]] |
153 | // operation as dictated in the specification. |
154 | bool setPrototype(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet = false); |
155 | JS_EXPORT_PRIVATE static bool setPrototype(JSObject*, ExecState*, JSValue prototype, bool shouldThrowIfCantSet); |
156 | |
157 | inline bool mayInterceptIndexedAccesses(VM&); |
158 | |
159 | JSValue get(ExecState*, PropertyName) const; |
160 | JSValue get(ExecState*, unsigned propertyName) const; |
161 | |
162 | template<bool checkNullStructure = false> |
163 | bool getPropertySlot(ExecState*, PropertyName, PropertySlot&); |
164 | bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); |
165 | template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, CallbackWhenNoException) const; |
166 | template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, PropertySlot&, CallbackWhenNoException) const; |
167 | |
168 | static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); |
169 | JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); |
170 | bool getOwnPropertySlotInline(ExecState*, PropertyName, PropertySlot&); |
171 | |
172 | // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot |
173 | // currently returns incorrect results for the DOM window (with non-own properties) |
174 | // being returned. Once this is fixed we should migrate code & remove this method. |
175 | JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); |
176 | |
177 | unsigned getArrayLength() const |
178 | { |
179 | if (!hasIndexedProperties(indexingType())) |
180 | return 0; |
181 | return m_butterfly->publicLength(); |
182 | } |
183 | |
184 | unsigned getVectorLength() |
185 | { |
186 | if (!hasIndexedProperties(indexingType())) |
187 | return 0; |
188 | return m_butterfly->vectorLength(); |
189 | } |
190 | |
191 | static bool putInlineForJSObject(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); |
192 | |
193 | JS_EXPORT_PRIVATE static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); |
194 | // putByIndex assumes that the receiver is this JSCell object. |
195 | JS_EXPORT_PRIVATE static bool putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); |
196 | |
197 | // This performs the ECMAScript Set() operation. |
198 | ALWAYS_INLINE bool putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) |
199 | { |
200 | if (canSetIndexQuickly(propertyName)) { |
201 | setIndexQuickly(exec->vm(), propertyName, value); |
202 | return true; |
203 | } |
204 | return methodTable(exec->vm())->putByIndex(this, exec, propertyName, value, shouldThrow); |
205 | } |
206 | |
207 | // This is similar to the putDirect* methods: |
208 | // - the prototype chain is not consulted |
209 | // - accessors are not called. |
210 | // - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default). |
211 | // This method creates a property with attributes writable, enumerable and configurable all set to true if attributes is zero, |
212 | // otherwise, it creates a property with the provided attributes. Semantically, this is performing defineOwnProperty. |
213 | bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode) |
214 | { |
215 | ASSERT(!value.isCustomGetterSetter()); |
216 | auto canSetIndexQuicklyForPutDirect = [&] () -> bool { |
217 | switch (indexingMode()) { |
218 | case ALL_BLANK_INDEXING_TYPES: |
219 | case ALL_UNDECIDED_INDEXING_TYPES: |
220 | return false; |
221 | case ALL_WRITABLE_INT32_INDEXING_TYPES: |
222 | case ALL_WRITABLE_DOUBLE_INDEXING_TYPES: |
223 | case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES: |
224 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: |
225 | return propertyName < m_butterfly->vectorLength(); |
226 | default: |
227 | if (isCopyOnWrite(indexingMode())) |
228 | return false; |
229 | RELEASE_ASSERT_NOT_REACHED(); |
230 | return false; |
231 | } |
232 | }; |
233 | |
234 | if (!attributes && canSetIndexQuicklyForPutDirect()) { |
235 | setIndexQuickly(exec->vm(), propertyName, value); |
236 | return true; |
237 | } |
238 | return putDirectIndexSlowOrBeyondVectorLength(exec, propertyName, value, attributes, mode); |
239 | } |
240 | // This is semantically equivalent to performing defineOwnProperty(propertyName, {configurable:true, writable:true, enumerable:true, value:value}). |
241 | bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value) |
242 | { |
243 | return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect); |
244 | } |
245 | |
246 | // A generally non-throwing version of putDirect and putDirectIndex. |
247 | // However, it's only guaranteed to not throw based on what the receiver is. |
248 | // For example, if the receiver is a ProxyObject, this is not guaranteed, since |
249 | // it may call into arbitrary JS code. It's the responsibility of the user of |
250 | // this API to ensure that the receiver object is a well known type if they |
251 | // want to ensure that this won't throw an exception. |
252 | JS_EXPORT_PRIVATE bool putDirectMayBeIndex(ExecState*, PropertyName, JSValue); |
253 | |
254 | bool (VM& vm) const |
255 | { |
256 | return structure(vm)->hasIndexingHeader(this); |
257 | } |
258 | |
259 | bool canGetIndexQuickly(unsigned i) |
260 | { |
261 | Butterfly* butterfly = this->butterfly(); |
262 | switch (indexingType()) { |
263 | case ALL_BLANK_INDEXING_TYPES: |
264 | case ALL_UNDECIDED_INDEXING_TYPES: |
265 | return false; |
266 | case ALL_INT32_INDEXING_TYPES: |
267 | case ALL_CONTIGUOUS_INDEXING_TYPES: |
268 | return i < butterfly->vectorLength() && butterfly->contiguous().at(this, i); |
269 | case ALL_DOUBLE_INDEXING_TYPES: { |
270 | if (i >= butterfly->vectorLength()) |
271 | return false; |
272 | double value = butterfly->contiguousDouble().at(this, i); |
273 | if (value != value) |
274 | return false; |
275 | return true; |
276 | } |
277 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: |
278 | return i < butterfly->arrayStorage()->vectorLength() && butterfly->arrayStorage()->m_vector[i]; |
279 | default: |
280 | RELEASE_ASSERT_NOT_REACHED(); |
281 | return false; |
282 | } |
283 | } |
284 | |
285 | JSValue getIndexQuickly(unsigned i) |
286 | { |
287 | Butterfly* butterfly = this->butterfly(); |
288 | switch (indexingType()) { |
289 | case ALL_INT32_INDEXING_TYPES: |
290 | return jsNumber(butterfly->contiguous().at(this, i).get().asInt32()); |
291 | case ALL_CONTIGUOUS_INDEXING_TYPES: |
292 | return butterfly->contiguous().at(this, i).get(); |
293 | case ALL_DOUBLE_INDEXING_TYPES: |
294 | return JSValue(JSValue::EncodeAsDouble, butterfly->contiguousDouble().at(this, i)); |
295 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: |
296 | return butterfly->arrayStorage()->m_vector[i].get(); |
297 | default: |
298 | RELEASE_ASSERT_NOT_REACHED(); |
299 | return JSValue(); |
300 | } |
301 | } |
302 | |
303 | JSValue tryGetIndexQuickly(unsigned i) const |
304 | { |
305 | Butterfly* butterfly = const_cast<JSObject*>(this)->butterfly(); |
306 | switch (indexingType()) { |
307 | case ALL_BLANK_INDEXING_TYPES: |
308 | case ALL_UNDECIDED_INDEXING_TYPES: |
309 | break; |
310 | case ALL_INT32_INDEXING_TYPES: |
311 | if (i < butterfly->publicLength()) { |
312 | JSValue result = butterfly->contiguous().at(this, i).get(); |
313 | ASSERT(result.isInt32() || !result); |
314 | return result; |
315 | } |
316 | break; |
317 | case ALL_CONTIGUOUS_INDEXING_TYPES: |
318 | if (i < butterfly->publicLength()) |
319 | return butterfly->contiguous().at(this, i).get(); |
320 | break; |
321 | case ALL_DOUBLE_INDEXING_TYPES: { |
322 | if (i >= butterfly->publicLength()) |
323 | break; |
324 | double result = butterfly->contiguousDouble().at(this, i); |
325 | if (result != result) |
326 | break; |
327 | return JSValue(JSValue::EncodeAsDouble, result); |
328 | } |
329 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: |
330 | if (i < butterfly->arrayStorage()->vectorLength()) |
331 | return butterfly->arrayStorage()->m_vector[i].get(); |
332 | break; |
333 | default: |
334 | RELEASE_ASSERT_NOT_REACHED(); |
335 | break; |
336 | } |
337 | return JSValue(); |
338 | } |
339 | |
340 | JSValue getDirectIndex(ExecState* exec, unsigned i) |
341 | { |
342 | if (JSValue result = tryGetIndexQuickly(i)) |
343 | return result; |
344 | PropertySlot slot(this, PropertySlot::InternalMethodType::Get); |
345 | if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot)) |
346 | return slot.getValue(exec, i); |
347 | return JSValue(); |
348 | } |
349 | |
350 | JSValue getIndex(ExecState* exec, unsigned i) const |
351 | { |
352 | if (JSValue result = tryGetIndexQuickly(i)) |
353 | return result; |
354 | return get(exec, i); |
355 | } |
356 | |
357 | bool canSetIndexQuickly(unsigned i) |
358 | { |
359 | Butterfly* butterfly = this->butterfly(); |
360 | switch (indexingMode()) { |
361 | case ALL_BLANK_INDEXING_TYPES: |
362 | case ALL_UNDECIDED_INDEXING_TYPES: |
363 | return false; |
364 | case ALL_WRITABLE_INT32_INDEXING_TYPES: |
365 | case ALL_WRITABLE_DOUBLE_INDEXING_TYPES: |
366 | case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES: |
367 | case NonArrayWithArrayStorage: |
368 | case ArrayWithArrayStorage: |
369 | return i < butterfly->vectorLength(); |
370 | case NonArrayWithSlowPutArrayStorage: |
371 | case ArrayWithSlowPutArrayStorage: |
372 | return i < butterfly->arrayStorage()->vectorLength() |
373 | && !!butterfly->arrayStorage()->m_vector[i]; |
374 | default: |
375 | if (isCopyOnWrite(indexingMode())) |
376 | return false; |
377 | RELEASE_ASSERT_NOT_REACHED(); |
378 | return false; |
379 | } |
380 | } |
381 | |
382 | void setIndexQuickly(VM& vm, unsigned i, JSValue v) |
383 | { |
384 | Butterfly* butterfly = m_butterfly.get(); |
385 | ASSERT(!isCopyOnWrite(indexingMode())); |
386 | switch (indexingType()) { |
387 | case ALL_INT32_INDEXING_TYPES: { |
388 | ASSERT(i < butterfly->vectorLength()); |
389 | if (!v.isInt32()) { |
390 | convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); |
391 | return; |
392 | } |
393 | FALLTHROUGH; |
394 | } |
395 | case ALL_CONTIGUOUS_INDEXING_TYPES: { |
396 | ASSERT(i < butterfly->vectorLength()); |
397 | butterfly->contiguous().at(this, i).set(vm, this, v); |
398 | if (i >= butterfly->publicLength()) |
399 | butterfly->setPublicLength(i + 1); |
400 | break; |
401 | } |
402 | case ALL_DOUBLE_INDEXING_TYPES: { |
403 | ASSERT(i < butterfly->vectorLength()); |
404 | if (!v.isNumber()) { |
405 | convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); |
406 | return; |
407 | } |
408 | double value = v.asNumber(); |
409 | if (value != value) { |
410 | convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); |
411 | return; |
412 | } |
413 | butterfly->contiguousDouble().at(this, i) = value; |
414 | if (i >= butterfly->publicLength()) |
415 | butterfly->setPublicLength(i + 1); |
416 | break; |
417 | } |
418 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: { |
419 | ArrayStorage* storage = butterfly->arrayStorage(); |
420 | WriteBarrier<Unknown>& x = storage->m_vector[i]; |
421 | JSValue old = x.get(); |
422 | x.set(vm, this, v); |
423 | if (!old) { |
424 | ++storage->m_numValuesInVector; |
425 | if (i >= storage->length()) |
426 | storage->setLength(i + 1); |
427 | } |
428 | break; |
429 | } |
430 | default: |
431 | RELEASE_ASSERT_NOT_REACHED(); |
432 | } |
433 | } |
434 | |
435 | void initializeIndex(ObjectInitializationScope& scope, unsigned i, JSValue v) |
436 | { |
437 | initializeIndex(scope, i, v, indexingType()); |
438 | } |
439 | |
440 | // NOTE: Clients of this method may call it more than once for any index, and this is supposed |
441 | // to work. |
442 | ALWAYS_INLINE void initializeIndex(ObjectInitializationScope& scope, unsigned i, JSValue v, IndexingType indexingType) |
443 | { |
444 | VM& vm = scope.vm(); |
445 | Butterfly* butterfly = m_butterfly.get(); |
446 | switch (indexingType) { |
447 | case ALL_UNDECIDED_INDEXING_TYPES: { |
448 | setIndexQuicklyToUndecided(vm, i, v); |
449 | break; |
450 | } |
451 | case ALL_INT32_INDEXING_TYPES: { |
452 | ASSERT(i < butterfly->publicLength()); |
453 | ASSERT(i < butterfly->vectorLength()); |
454 | if (!v.isInt32()) { |
455 | convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); |
456 | break; |
457 | } |
458 | FALLTHROUGH; |
459 | } |
460 | case ALL_CONTIGUOUS_INDEXING_TYPES: { |
461 | ASSERT(i < butterfly->publicLength()); |
462 | ASSERT(i < butterfly->vectorLength()); |
463 | butterfly->contiguous().at(this, i).set(vm, this, v); |
464 | break; |
465 | } |
466 | case ALL_DOUBLE_INDEXING_TYPES: { |
467 | ASSERT(i < butterfly->publicLength()); |
468 | ASSERT(i < butterfly->vectorLength()); |
469 | if (!v.isNumber()) { |
470 | convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); |
471 | return; |
472 | } |
473 | double value = v.asNumber(); |
474 | if (value != value) { |
475 | convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); |
476 | return; |
477 | } |
478 | butterfly->contiguousDouble().at(this, i) = value; |
479 | break; |
480 | } |
481 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: { |
482 | ArrayStorage* storage = butterfly->arrayStorage(); |
483 | ASSERT(i < storage->length()); |
484 | ASSERT(i < storage->m_numValuesInVector); |
485 | storage->m_vector[i].set(vm, this, v); |
486 | break; |
487 | } |
488 | default: |
489 | RELEASE_ASSERT_NOT_REACHED(); |
490 | } |
491 | } |
492 | |
493 | void initializeIndexWithoutBarrier(ObjectInitializationScope& scope, unsigned i, JSValue v) |
494 | { |
495 | initializeIndexWithoutBarrier(scope, i, v, indexingType()); |
496 | } |
497 | |
498 | // This version of initializeIndex is for cases where you know that you will not need any |
499 | // barriers. This implies not having any data format conversions. |
500 | ALWAYS_INLINE void initializeIndexWithoutBarrier(ObjectInitializationScope&, unsigned i, JSValue v, IndexingType indexingType) |
501 | { |
502 | Butterfly* butterfly = m_butterfly.get(); |
503 | switch (indexingType) { |
504 | case ALL_UNDECIDED_INDEXING_TYPES: { |
505 | RELEASE_ASSERT_NOT_REACHED(); |
506 | break; |
507 | } |
508 | case ALL_INT32_INDEXING_TYPES: { |
509 | ASSERT(i < butterfly->publicLength()); |
510 | ASSERT(i < butterfly->vectorLength()); |
511 | RELEASE_ASSERT(v.isInt32()); |
512 | FALLTHROUGH; |
513 | } |
514 | case ALL_CONTIGUOUS_INDEXING_TYPES: { |
515 | ASSERT(i < butterfly->publicLength()); |
516 | ASSERT(i < butterfly->vectorLength()); |
517 | butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v); |
518 | break; |
519 | } |
520 | case ALL_DOUBLE_INDEXING_TYPES: { |
521 | ASSERT(i < butterfly->publicLength()); |
522 | ASSERT(i < butterfly->vectorLength()); |
523 | RELEASE_ASSERT(v.isNumber()); |
524 | double value = v.asNumber(); |
525 | RELEASE_ASSERT(value == value); |
526 | butterfly->contiguousDouble().at(this, i) = value; |
527 | break; |
528 | } |
529 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: { |
530 | ArrayStorage* storage = butterfly->arrayStorage(); |
531 | ASSERT(i < storage->length()); |
532 | ASSERT(i < storage->m_numValuesInVector); |
533 | storage->m_vector[i].setWithoutWriteBarrier(v); |
534 | break; |
535 | } |
536 | default: |
537 | RELEASE_ASSERT_NOT_REACHED(); |
538 | } |
539 | } |
540 | |
541 | bool hasSparseMap() |
542 | { |
543 | switch (indexingType()) { |
544 | case ALL_BLANK_INDEXING_TYPES: |
545 | case ALL_UNDECIDED_INDEXING_TYPES: |
546 | case ALL_INT32_INDEXING_TYPES: |
547 | case ALL_DOUBLE_INDEXING_TYPES: |
548 | case ALL_CONTIGUOUS_INDEXING_TYPES: |
549 | return false; |
550 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: |
551 | return !!m_butterfly->arrayStorage()->m_sparseMap; |
552 | default: |
553 | RELEASE_ASSERT_NOT_REACHED(); |
554 | return false; |
555 | } |
556 | } |
557 | |
558 | bool inSparseIndexingMode() |
559 | { |
560 | switch (indexingType()) { |
561 | case ALL_BLANK_INDEXING_TYPES: |
562 | case ALL_UNDECIDED_INDEXING_TYPES: |
563 | case ALL_INT32_INDEXING_TYPES: |
564 | case ALL_DOUBLE_INDEXING_TYPES: |
565 | case ALL_CONTIGUOUS_INDEXING_TYPES: |
566 | return false; |
567 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: |
568 | return m_butterfly->arrayStorage()->inSparseMode(); |
569 | default: |
570 | RELEASE_ASSERT_NOT_REACHED(); |
571 | return false; |
572 | } |
573 | } |
574 | |
575 | void enterDictionaryIndexingMode(VM&); |
576 | |
577 | // putDirect is effectively an unchecked vesion of 'defineOwnProperty': |
578 | // - the prototype chain is not consulted |
579 | // - accessors are not called. |
580 | // - attributes will be respected (after the call the property will exist with the given attributes) |
581 | // - the property name is assumed to not be an index. |
582 | bool putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0); |
583 | bool putDirect(VM&, PropertyName, JSValue, PutPropertySlot&); |
584 | void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0); |
585 | bool putDirectNonIndexAccessor(VM&, PropertyName, GetterSetter*, unsigned attributes); |
586 | void putDirectNonIndexAccessorWithoutTransition(VM&, PropertyName, GetterSetter*, unsigned attributes); |
587 | bool putDirectAccessor(ExecState*, PropertyName, GetterSetter*, unsigned attributes); |
588 | JS_EXPORT_PRIVATE bool putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes); |
589 | |
590 | bool putGetter(ExecState*, PropertyName, JSValue, unsigned attributes); |
591 | bool putSetter(ExecState*, PropertyName, JSValue, unsigned attributes); |
592 | |
593 | JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const; |
594 | JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const; |
595 | bool hasPropertyGeneric(ExecState*, PropertyName, PropertySlot::InternalMethodType) const; |
596 | bool hasPropertyGeneric(ExecState*, unsigned propertyName, PropertySlot::InternalMethodType) const; |
597 | bool hasOwnProperty(ExecState*, PropertyName, PropertySlot&) const; |
598 | bool hasOwnProperty(ExecState*, PropertyName) const; |
599 | bool hasOwnProperty(ExecState*, unsigned) const; |
600 | |
601 | JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); |
602 | JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); |
603 | |
604 | JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); |
605 | JSValue ordinaryToPrimitive(ExecState*, PreferredPrimitiveType) const; |
606 | |
607 | JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue value, JSValue hasInstanceValue); |
608 | JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue); |
609 | static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty); |
610 | |
611 | JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
612 | JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
613 | JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
614 | |
615 | JS_EXPORT_PRIVATE static uint32_t getEnumerableLength(ExecState*, JSObject*); |
616 | JS_EXPORT_PRIVATE static void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
617 | JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
618 | |
619 | JS_EXPORT_PRIVATE JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; |
620 | bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; |
621 | JS_EXPORT_PRIVATE double toNumber(ExecState*) const; |
622 | JS_EXPORT_PRIVATE JSString* toString(ExecState*) const; |
623 | |
624 | JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode); |
625 | |
626 | // This get function only looks at the property map. |
627 | JSValue getDirect(VM& vm, PropertyName propertyName) const |
628 | { |
629 | Structure* structure = this->structure(vm); |
630 | PropertyOffset offset = structure->get(vm, propertyName); |
631 | checkOffset(offset, structure->inlineCapacity()); |
632 | return offset != invalidOffset ? getDirect(offset) : JSValue(); |
633 | } |
634 | |
635 | JSValue getDirect(VM& vm, PropertyName propertyName, unsigned& attributes) const |
636 | { |
637 | Structure* structure = this->structure(vm); |
638 | PropertyOffset offset = structure->get(vm, propertyName, attributes); |
639 | checkOffset(offset, structure->inlineCapacity()); |
640 | return offset != invalidOffset ? getDirect(offset) : JSValue(); |
641 | } |
642 | |
643 | PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName) |
644 | { |
645 | Structure* structure = this->structure(vm); |
646 | PropertyOffset offset = structure->get(vm, propertyName); |
647 | checkOffset(offset, structure->inlineCapacity()); |
648 | return offset; |
649 | } |
650 | |
651 | PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes) |
652 | { |
653 | Structure* structure = this->structure(vm); |
654 | PropertyOffset offset = structure->get(vm, propertyName, attributes); |
655 | checkOffset(offset, structure->inlineCapacity()); |
656 | return offset; |
657 | } |
658 | |
659 | bool hasInlineStorage() const { return structure()->hasInlineStorage(); } |
660 | ConstPropertyStorage inlineStorageUnsafe() const |
661 | { |
662 | return bitwise_cast<ConstPropertyStorage>(this + 1); |
663 | } |
664 | PropertyStorage inlineStorageUnsafe() |
665 | { |
666 | return bitwise_cast<PropertyStorage>(this + 1); |
667 | } |
668 | ConstPropertyStorage inlineStorage() const |
669 | { |
670 | ASSERT(hasInlineStorage()); |
671 | return inlineStorageUnsafe(); |
672 | } |
673 | PropertyStorage inlineStorage() |
674 | { |
675 | ASSERT(hasInlineStorage()); |
676 | return inlineStorageUnsafe(); |
677 | } |
678 | |
679 | const Butterfly* butterfly() const { return m_butterfly.get(); } |
680 | Butterfly* butterfly() { return m_butterfly.get(); } |
681 | |
682 | ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); } |
683 | PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); } |
684 | |
685 | ALWAYS_INLINE const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const |
686 | { |
687 | if (isInlineOffset(offset)) |
688 | return &inlineStorage()[offsetInInlineStorage(offset)]; |
689 | return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; |
690 | } |
691 | |
692 | ALWAYS_INLINE WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) |
693 | { |
694 | if (isInlineOffset(offset)) |
695 | return &inlineStorage()[offsetInInlineStorage(offset)]; |
696 | return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; |
697 | } |
698 | |
699 | void transitionTo(VM&, Structure*); |
700 | |
701 | bool hasCustomProperties(VM& vm) { return structure(vm)->didTransition(); } |
702 | bool hasGetterSetterProperties(VM& vm) { return structure(vm)->hasGetterSetterProperties(); } |
703 | bool hasCustomGetterSetterProperties(VM& vm) { return structure(vm)->hasCustomGetterSetterProperties(); } |
704 | |
705 | // putOwnDataProperty has 'put' like semantics, however this method: |
706 | // - assumes the object contains no own getter/setter properties. |
707 | // - provides no special handling for __proto__ |
708 | // - does not walk the prototype chain (to check for accessors or non-writable properties). |
709 | // This is used by JSLexicalEnvironment. |
710 | bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&); |
711 | bool putOwnDataPropertyMayBeIndex(ExecState*, PropertyName, JSValue, PutPropertySlot&); |
712 | |
713 | // Fast access to known property offsets. |
714 | ALWAYS_INLINE JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); } |
715 | JSValue getDirectConcurrently(Structure* expectedStructure, PropertyOffset) const; |
716 | void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); } |
717 | void putDirectWithoutBarrier(PropertyOffset offset, JSValue value) { locationForOffset(offset)->setWithoutWriteBarrier(value); } |
718 | void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); } |
719 | |
720 | JS_EXPORT_PRIVATE bool putDirectNativeIntrinsicGetter(VM&, JSGlobalObject*, Identifier, NativeFunction, Intrinsic, unsigned attributes); |
721 | JS_EXPORT_PRIVATE void putDirectNativeIntrinsicGetterWithoutTransition(VM&, JSGlobalObject*, Identifier, NativeFunction, Intrinsic, unsigned attributes); |
722 | JS_EXPORT_PRIVATE bool putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); |
723 | JS_EXPORT_PRIVATE bool putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, const DOMJIT::Signature*, unsigned attributes); |
724 | JS_EXPORT_PRIVATE void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); |
725 | |
726 | JS_EXPORT_PRIVATE JSFunction* putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); |
727 | JSFunction* putDirectBuiltinFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); |
728 | |
729 | JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); |
730 | |
731 | bool isEnvironment() const; |
732 | bool isGlobalObject() const; |
733 | bool isJSLexicalEnvironment() const; |
734 | bool isGlobalLexicalEnvironment() const; |
735 | bool isStrictEvalActivation() const; |
736 | bool isWithScope() const; |
737 | |
738 | bool isErrorInstance() const; |
739 | |
740 | JS_EXPORT_PRIVATE void seal(VM&); |
741 | JS_EXPORT_PRIVATE void freeze(VM&); |
742 | JS_EXPORT_PRIVATE static bool preventExtensions(JSObject*, ExecState*); |
743 | JS_EXPORT_PRIVATE static bool isExtensible(JSObject*, ExecState*); |
744 | bool isSealed(VM& vm) { return structure(vm)->isSealed(vm); } |
745 | bool isFrozen(VM& vm) { return structure(vm)->isFrozen(vm); } |
746 | |
747 | bool anyObjectInChainMayInterceptIndexedAccesses(VM&) const; |
748 | JS_EXPORT_PRIVATE bool prototypeChainMayInterceptStoreTo(VM&, PropertyName); |
749 | bool needsSlowPutIndexing(VM&) const; |
750 | |
751 | private: |
752 | NonPropertyTransition suggestedArrayStorageTransition(VM&) const; |
753 | public: |
754 | // You should only call isStructureExtensible() when: |
755 | // - Performing this check in a way that isn't described in the specification |
756 | // as calling the virtual [[IsExtensible]] trap. |
757 | // - When you're guaranteed that object->methodTable(vm)->isExtensible isn't |
758 | // overridden. |
759 | ALWAYS_INLINE bool isStructureExtensible(VM& vm) { return structure(vm)->isStructureExtensible(); } |
760 | // You should call this when performing [[IsExtensible]] trap in a place |
761 | // that is described in the specification. This performs the fully virtual |
762 | // [[IsExtensible]] trap. |
763 | bool isExtensible(ExecState*); |
764 | bool indexingShouldBeSparse(VM& vm) |
765 | { |
766 | return !isStructureExtensible(vm) |
767 | || structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); |
768 | } |
769 | |
770 | bool staticPropertiesReified(VM& vm) { return structure(vm)->staticPropertiesReified(); } |
771 | void reifyAllStaticProperties(ExecState*); |
772 | |
773 | JS_EXPORT_PRIVATE Butterfly* allocateMoreOutOfLineStorage(VM&, size_t oldSize, size_t newSize); |
774 | |
775 | // Call this when you do not need to change the structure. |
776 | void setButterfly(VM&, Butterfly*); |
777 | |
778 | // Call this if you do need to change the structure, or if you changed something about a structure |
779 | // in-place. |
780 | void nukeStructureAndSetButterfly(VM&, StructureID oldStructureID, Butterfly*); |
781 | |
782 | void setStructure(VM&, Structure*); |
783 | |
784 | JS_EXPORT_PRIVATE void convertToDictionary(VM&); |
785 | |
786 | void flattenDictionaryObject(VM& vm) |
787 | { |
788 | structure(vm)->flattenDictionaryStructure(vm, this); |
789 | } |
790 | void shiftButterflyAfterFlattening(const GCSafeConcurrentJSLocker&, VM&, Structure* structure, size_t outOfLineCapacityAfter); |
791 | |
792 | JSGlobalObject* globalObject() const |
793 | { |
794 | ASSERT(structure()->globalObject()); |
795 | ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this); |
796 | return structure()->globalObject(); |
797 | } |
798 | |
799 | JSGlobalObject* globalObject(VM& vm) const |
800 | { |
801 | ASSERT(structure(vm)->globalObject()); |
802 | ASSERT(!isGlobalObject() || ((JSObject*)structure(vm)->globalObject()) == this); |
803 | return structure(vm)->globalObject(); |
804 | } |
805 | |
806 | void switchToSlowPutArrayStorage(VM&); |
807 | |
808 | // The receiver is the prototype in this case. The following: |
809 | // |
810 | // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...) |
811 | // |
812 | // is equivalent to: |
813 | // |
814 | // foo->attemptToInterceptPutByIndexOnHole(...); |
815 | bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow, bool& putResult); |
816 | |
817 | // Returns 0 if int32 storage cannot be created - either because |
818 | // indexing should be sparse, we're having a bad time, or because |
819 | // we already have a more general form of storage (double, |
820 | // contiguous, array storage). |
821 | ContiguousJSValues tryMakeWritableInt32(VM& vm) |
822 | { |
823 | if (LIKELY(hasInt32(indexingType()) && !isCopyOnWrite(indexingMode()))) |
824 | return m_butterfly->contiguousInt32(); |
825 | |
826 | return tryMakeWritableInt32Slow(vm); |
827 | } |
828 | |
829 | // Returns 0 if double storage cannot be created - either because |
830 | // indexing should be sparse, we're having a bad time, or because |
831 | // we already have a more general form of storage (contiguous, |
832 | // or array storage). |
833 | ContiguousDoubles tryMakeWritableDouble(VM& vm) |
834 | { |
835 | if (LIKELY(hasDouble(indexingType()) && !isCopyOnWrite(indexingMode()))) |
836 | return m_butterfly->contiguousDouble(); |
837 | |
838 | return tryMakeWritableDoubleSlow(vm); |
839 | } |
840 | |
841 | // Returns 0 if contiguous storage cannot be created - either because |
842 | // indexing should be sparse or because we're having a bad time. |
843 | ContiguousJSValues tryMakeWritableContiguous(VM& vm) |
844 | { |
845 | if (LIKELY(hasContiguous(indexingType()) && !isCopyOnWrite(indexingMode()))) |
846 | return m_butterfly->contiguous(); |
847 | |
848 | return tryMakeWritableContiguousSlow(vm); |
849 | } |
850 | |
851 | // Ensure that the object is in a mode where it has array storage. Use |
852 | // this if you're about to perform actions that would have required the |
853 | // object to be converted to have array storage, if it didn't have it |
854 | // already. |
855 | ArrayStorage* ensureArrayStorage(VM& vm) |
856 | { |
857 | if (LIKELY(hasAnyArrayStorage(indexingType()))) |
858 | return m_butterfly->arrayStorage(); |
859 | |
860 | return ensureArrayStorageSlow(vm); |
861 | } |
862 | |
863 | void ensureWritable(VM& vm) |
864 | { |
865 | if (isCopyOnWrite(indexingMode())) |
866 | convertFromCopyOnWrite(vm); |
867 | } |
868 | |
869 | static size_t offsetOfInlineStorage(); |
870 | |
871 | static ptrdiff_t butterflyOffset() |
872 | { |
873 | return OBJECT_OFFSETOF(JSObject, m_butterfly); |
874 | } |
875 | void* butterflyAddress() |
876 | { |
877 | return &m_butterfly; |
878 | } |
879 | |
880 | JS_EXPORT_PRIVATE JSValue getMethod(ExecState*, CallData&, CallType&, const Identifier&, const String& errorMessage); |
881 | |
882 | bool canPerformFastPutInline(VM&, PropertyName); |
883 | bool canPerformFastPutInlineExcludingProto(VM&); |
884 | |
885 | bool mayBePrototype() const; |
886 | void didBecomePrototype(); |
887 | |
888 | DECLARE_EXPORT_INFO; |
889 | |
890 | protected: |
891 | void finishCreation(VM& vm) |
892 | { |
893 | Base::finishCreation(vm); |
894 | ASSERT(jsDynamicCast<JSObject*>(vm, this)); |
895 | ASSERT(structure(vm)->hasPolyProto() || getPrototypeDirect(vm).isNull() || Heap::heap(this) == Heap::heap(getPrototypeDirect(vm))); |
896 | ASSERT(structure(vm)->isObject()); |
897 | ASSERT(classInfo(vm)); |
898 | } |
899 | |
900 | static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) |
901 | { |
902 | return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); |
903 | } |
904 | |
905 | // To instantiate objects you likely want JSFinalObject, below. |
906 | // To create derived types you likely want JSNonFinalObject, below. |
907 | JSObject(VM&, Structure*, Butterfly* = nullptr); |
908 | |
909 | // Visits the butterfly unless there is a race. Returns the structure if there was no race. |
910 | Structure* visitButterfly(SlotVisitor&); |
911 | |
912 | Structure* visitButterflyImpl(SlotVisitor&); |
913 | |
914 | void markAuxiliaryAndVisitOutOfLineProperties(SlotVisitor&, Butterfly*, Structure*, PropertyOffset lastOffset); |
915 | |
916 | // Call this if you know that the object is in a mode where it has array |
917 | // storage. This will assert otherwise. |
918 | ArrayStorage* arrayStorage() |
919 | { |
920 | ASSERT(hasAnyArrayStorage(indexingType())); |
921 | return m_butterfly->arrayStorage(); |
922 | } |
923 | |
924 | // Call this if you want to predicate some actions on whether or not the |
925 | // object is in a mode where it has array storage. |
926 | ArrayStorage* arrayStorageOrNull() |
927 | { |
928 | switch (indexingType()) { |
929 | case ALL_ARRAY_STORAGE_INDEXING_TYPES: |
930 | return m_butterfly->arrayStorage(); |
931 | |
932 | default: |
933 | return 0; |
934 | } |
935 | } |
936 | |
937 | size_t butterflyTotalSize(); |
938 | size_t butterflyPreCapacity(); |
939 | |
940 | Butterfly* createInitialUndecided(VM&, unsigned length); |
941 | ContiguousJSValues createInitialInt32(VM&, unsigned length); |
942 | ContiguousDoubles createInitialDouble(VM&, unsigned length); |
943 | ContiguousJSValues createInitialContiguous(VM&, unsigned length); |
944 | |
945 | void convertUndecidedForValue(VM&, JSValue); |
946 | void createInitialForValueAndSet(VM&, unsigned index, JSValue); |
947 | void convertInt32ForValue(VM&, JSValue); |
948 | void convertDoubleForValue(VM&, JSValue); |
949 | void convertFromCopyOnWrite(VM&); |
950 | |
951 | static Butterfly* createArrayStorageButterfly(VM&, JSObject* intendedOwner, Structure*, unsigned length, unsigned vectorLength, Butterfly* oldButterfly = nullptr); |
952 | ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength); |
953 | ArrayStorage* createInitialArrayStorage(VM&); |
954 | |
955 | ContiguousJSValues convertUndecidedToInt32(VM&); |
956 | ContiguousDoubles convertUndecidedToDouble(VM&); |
957 | ContiguousJSValues convertUndecidedToContiguous(VM&); |
958 | ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition); |
959 | ArrayStorage* convertUndecidedToArrayStorage(VM&); |
960 | |
961 | ContiguousDoubles convertInt32ToDouble(VM&); |
962 | ContiguousJSValues convertInt32ToContiguous(VM&); |
963 | ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition); |
964 | ArrayStorage* convertInt32ToArrayStorage(VM&); |
965 | |
966 | ContiguousJSValues convertDoubleToContiguous(VM&); |
967 | ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition); |
968 | ArrayStorage* convertDoubleToArrayStorage(VM&); |
969 | |
970 | ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition); |
971 | ArrayStorage* convertContiguousToArrayStorage(VM&); |
972 | |
973 | |
974 | ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM&); |
975 | |
976 | bool defineOwnNonIndexProperty(ExecState*, PropertyName, const PropertyDescriptor&, bool throwException); |
977 | |
978 | template<IndexingType indexingShape> |
979 | bool putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); |
980 | bool putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); |
981 | |
982 | bool increaseVectorLength(VM&, unsigned newLength); |
983 | void deallocateSparseIndexMap(); |
984 | bool defineOwnIndexedProperty(ExecState*, unsigned, const PropertyDescriptor&, bool throwException); |
985 | SparseArrayValueMap* allocateSparseIndexMap(VM&); |
986 | |
987 | void notifyPresenceOfIndexedAccessors(VM&); |
988 | |
989 | bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow, bool& putResult); |
990 | |
991 | // Call this if you want setIndexQuickly to succeed and you're sure that |
992 | // the array is contiguous. |
993 | bool WARN_UNUSED_RETURN ensureLength(VM& vm, unsigned length) |
994 | { |
995 | RELEASE_ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH); |
996 | ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); |
997 | |
998 | if (m_butterfly->vectorLength() < length || isCopyOnWrite(indexingMode())) { |
999 | if (!ensureLengthSlow(vm, length)) |
1000 | return false; |
1001 | } |
1002 | |
1003 | if (m_butterfly->publicLength() < length) |
1004 | m_butterfly->setPublicLength(length); |
1005 | return true; |
1006 | } |
1007 | |
1008 | // Call this if you want to shrink the butterfly backing store, and you're |
1009 | // sure that the array is contiguous. |
1010 | void reallocateAndShrinkButterfly(VM&, unsigned length); |
1011 | |
1012 | template<IndexingType indexingShape> |
1013 | unsigned countElements(Butterfly*); |
1014 | |
1015 | // This is relevant to undecided, int32, double, and contiguous. |
1016 | unsigned countElements(); |
1017 | |
1018 | private: |
1019 | friend class LLIntOffsetsExtractor; |
1020 | friend class VMInspector; |
1021 | |
1022 | // Nobody should ever ask any of these questions on something already known to be a JSObject. |
1023 | using JSCell::isAPIValueWrapper; |
1024 | using JSCell::isGetterSetter; |
1025 | void getObject(); |
1026 | void getString(ExecState* exec); |
1027 | void isObject(); |
1028 | void isString(); |
1029 | |
1030 | Butterfly* createInitialIndexedStorage(VM&, unsigned length); |
1031 | |
1032 | ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*); |
1033 | |
1034 | template<PutMode> |
1035 | bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&); |
1036 | |
1037 | JS_EXPORT_PRIVATE NEVER_INLINE bool putInlineSlow(ExecState*, PropertyName, JSValue, PutPropertySlot&); |
1038 | |
1039 | bool getNonIndexPropertySlot(ExecState*, PropertyName, PropertySlot&); |
1040 | bool getOwnNonIndexPropertySlot(VM&, Structure*, PropertyName, PropertySlot&); |
1041 | JS_EXPORT_PRIVATE void fillGetterPropertySlot(VM&, PropertySlot&, JSCell*, unsigned, PropertyOffset); |
1042 | void fillCustomGetterPropertySlot(VM&, PropertySlot&, CustomGetterSetter*, unsigned, Structure*); |
1043 | |
1044 | JS_EXPORT_PRIVATE bool getOwnStaticPropertySlot(VM&, PropertyName, PropertySlot&); |
1045 | struct PropertyHashEntry { |
1046 | const HashTable* table; |
1047 | const HashTableValue* value; |
1048 | }; |
1049 | Optional<PropertyHashEntry> findPropertyHashEntry(VM&, PropertyName) const; |
1050 | |
1051 | bool putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); |
1052 | bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*); |
1053 | JS_EXPORT_PRIVATE bool putDirectIndexSlowOrBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode); |
1054 | |
1055 | unsigned getNewVectorLength(VM&, unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength); |
1056 | unsigned getNewVectorLength(VM&, unsigned desiredLength); |
1057 | |
1058 | ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(VM&, unsigned neededLength); |
1059 | |
1060 | JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(VM&, unsigned index, JSValue); |
1061 | JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); |
1062 | JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); |
1063 | |
1064 | bool ensureLengthSlow(VM&, unsigned length); |
1065 | |
1066 | ContiguousJSValues tryMakeWritableInt32Slow(VM&); |
1067 | ContiguousDoubles tryMakeWritableDoubleSlow(VM&); |
1068 | ContiguousJSValues tryMakeWritableContiguousSlow(VM&); |
1069 | JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&); |
1070 | |
1071 | PropertyOffset prepareToPutDirectWithoutTransition(VM&, PropertyName, unsigned attributes, StructureID, Structure*); |
1072 | |
1073 | AuxiliaryBarrier<Butterfly*> m_butterfly; |
1074 | #if CPU(ADDRESS32) |
1075 | unsigned m_32BitPadding; |
1076 | #endif |
1077 | }; |
1078 | |
1079 | // JSNonFinalObject is a type of JSObject that has some internal storage, |
1080 | // but also preserves some space in the collector cell for additional |
1081 | // data members in derived types. |
1082 | class JSNonFinalObject : public JSObject { |
1083 | friend class JSObject; |
1084 | |
1085 | public: |
1086 | typedef JSObject Base; |
1087 | |
1088 | static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) |
1089 | { |
1090 | return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); |
1091 | } |
1092 | |
1093 | protected: |
1094 | explicit JSNonFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = 0) |
1095 | : JSObject(vm, structure, butterfly) |
1096 | { |
1097 | } |
1098 | |
1099 | void finishCreation(VM& vm) |
1100 | { |
1101 | Base::finishCreation(vm); |
1102 | ASSERT(!this->structure(vm)->hasInlineStorage()); |
1103 | ASSERT(classInfo(vm)); |
1104 | } |
1105 | }; |
1106 | |
1107 | class JSFinalObject; |
1108 | |
1109 | // JSFinalObject is a type of JSObject that contains sufficient internal |
1110 | // storage to fully make use of the collector cell containing it. |
1111 | class JSFinalObject final : public JSObject { |
1112 | friend class JSObject; |
1113 | |
1114 | public: |
1115 | typedef JSObject Base; |
1116 | static const unsigned StructureFlags = Base::StructureFlags; |
1117 | |
1118 | static size_t allocationSize(Checked<size_t> inlineCapacity) |
1119 | { |
1120 | return (sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>)).unsafeGet(); |
1121 | } |
1122 | |
1123 | static inline const TypeInfo typeInfo() { return TypeInfo(FinalObjectType, StructureFlags); } |
1124 | static const IndexingType defaultIndexingType = NonArray; |
1125 | |
1126 | static const unsigned defaultSize = 64; |
1127 | static inline unsigned defaultInlineCapacity() |
1128 | { |
1129 | return (defaultSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); |
1130 | } |
1131 | |
1132 | static const unsigned maxSize = 512; |
1133 | static inline unsigned maxInlineCapacity() |
1134 | { |
1135 | return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); |
1136 | } |
1137 | |
1138 | static JSFinalObject* create(ExecState*, Structure*, Butterfly* = nullptr); |
1139 | static JSFinalObject* create(VM&, Structure*); |
1140 | static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, unsigned inlineCapacity) |
1141 | { |
1142 | return Structure::create(vm, globalObject, prototype, typeInfo(), info(), defaultIndexingType, inlineCapacity); |
1143 | } |
1144 | |
1145 | JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); |
1146 | |
1147 | DECLARE_EXPORT_INFO; |
1148 | |
1149 | protected: |
1150 | void visitChildrenCommon(SlotVisitor&); |
1151 | |
1152 | void finishCreation(VM& vm) |
1153 | { |
1154 | Base::finishCreation(vm); |
1155 | ASSERT(structure(vm)->totalStorageCapacity() == structure(vm)->inlineCapacity()); |
1156 | ASSERT(classInfo(vm)); |
1157 | } |
1158 | |
1159 | private: |
1160 | friend class LLIntOffsetsExtractor; |
1161 | |
1162 | explicit JSFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = nullptr) |
1163 | : JSObject(vm, structure, butterfly) |
1164 | { |
1165 | memset(inlineStorageUnsafe(), 0, structure->inlineCapacity() * sizeof(EncodedJSValue)); |
1166 | } |
1167 | }; |
1168 | |
1169 | JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*); |
1170 | |
1171 | inline JSObject* JSObject::createRawObject( |
1172 | ExecState* exec, Structure* structure, Butterfly* butterfly) |
1173 | { |
1174 | VM& vm = exec->vm(); |
1175 | JSObject* finalObject = new ( |
1176 | NotNull, |
1177 | allocateCell<JSFinalObject>( |
1178 | vm.heap, |
1179 | JSFinalObject::allocationSize(structure->inlineCapacity()) |
1180 | ) |
1181 | ) JSObject(vm, structure, butterfly); |
1182 | finalObject->finishCreation(vm); |
1183 | return finalObject; |
1184 | } |
1185 | |
1186 | inline JSFinalObject* JSFinalObject::create( |
1187 | ExecState* exec, Structure* structure, Butterfly* butterfly) |
1188 | { |
1189 | VM& vm = exec->vm(); |
1190 | JSFinalObject* finalObject = new ( |
1191 | NotNull, |
1192 | allocateCell<JSFinalObject>( |
1193 | vm.heap, |
1194 | allocationSize(structure->inlineCapacity()) |
1195 | ) |
1196 | ) JSFinalObject(vm, structure, butterfly); |
1197 | finalObject->finishCreation(vm); |
1198 | return finalObject; |
1199 | } |
1200 | |
1201 | inline JSFinalObject* JSFinalObject::create(VM& vm, Structure* structure) |
1202 | { |
1203 | JSFinalObject* finalObject = new (NotNull, allocateCell<JSFinalObject>(vm.heap, allocationSize(structure->inlineCapacity()))) JSFinalObject(vm, structure); |
1204 | finalObject->finishCreation(vm); |
1205 | return finalObject; |
1206 | } |
1207 | |
1208 | inline size_t JSObject::offsetOfInlineStorage() |
1209 | { |
1210 | return sizeof(JSObject); |
1211 | } |
1212 | |
1213 | inline bool JSObject::isGlobalObject() const |
1214 | { |
1215 | return type() == GlobalObjectType; |
1216 | } |
1217 | |
1218 | inline bool JSObject::isJSLexicalEnvironment() const |
1219 | { |
1220 | return type() == LexicalEnvironmentType || type() == ModuleEnvironmentType; |
1221 | } |
1222 | |
1223 | inline bool JSObject::isGlobalLexicalEnvironment() const |
1224 | { |
1225 | return type() == GlobalLexicalEnvironmentType; |
1226 | } |
1227 | |
1228 | inline bool JSObject::isStrictEvalActivation() const |
1229 | { |
1230 | return type() == StrictEvalActivationType; |
1231 | } |
1232 | |
1233 | inline bool JSObject::isEnvironment() const |
1234 | { |
1235 | bool result = GlobalObjectType <= type() && type() <= StrictEvalActivationType; |
1236 | ASSERT((isGlobalObject() || isJSLexicalEnvironment() || isGlobalLexicalEnvironment() || isStrictEvalActivation()) == result); |
1237 | return result; |
1238 | } |
1239 | |
1240 | inline bool JSObject::isErrorInstance() const |
1241 | { |
1242 | return type() == ErrorInstanceType; |
1243 | } |
1244 | |
1245 | inline bool JSObject::isWithScope() const |
1246 | { |
1247 | return type() == WithScopeType; |
1248 | } |
1249 | |
1250 | inline void JSObject::setStructure(VM& vm, Structure* structure) |
1251 | { |
1252 | ASSERT(structure); |
1253 | ASSERT(!m_butterfly == !(structure->outOfLineCapacity() || structure->hasIndexingHeader(this))); |
1254 | JSCell::setStructure(vm, structure); |
1255 | } |
1256 | |
1257 | inline void JSObject::setButterfly(VM& vm, Butterfly* butterfly) |
1258 | { |
1259 | if (isX86() || vm.heap.mutatorShouldBeFenced()) { |
1260 | WTF::storeStoreFence(); |
1261 | m_butterfly.set(vm, this, butterfly); |
1262 | WTF::storeStoreFence(); |
1263 | return; |
1264 | } |
1265 | |
1266 | m_butterfly.set(vm, this, butterfly); |
1267 | } |
1268 | |
1269 | inline void JSObject::nukeStructureAndSetButterfly(VM& vm, StructureID oldStructureID, Butterfly* butterfly) |
1270 | { |
1271 | if (isX86() || vm.heap.mutatorShouldBeFenced()) { |
1272 | setStructureIDDirectly(nuke(oldStructureID)); |
1273 | WTF::storeStoreFence(); |
1274 | m_butterfly.set(vm, this, butterfly); |
1275 | WTF::storeStoreFence(); |
1276 | return; |
1277 | } |
1278 | |
1279 | m_butterfly.set(vm, this, butterfly); |
1280 | } |
1281 | |
1282 | inline CallType getCallData(VM& vm, JSValue value, CallData& callData) |
1283 | { |
1284 | CallType result = value.isCell() ? value.asCell()->methodTable(vm)->getCallData(value.asCell(), callData) : CallType::None; |
1285 | ASSERT(result == CallType::None || value.isValidCallee()); |
1286 | return result; |
1287 | } |
1288 | |
1289 | inline ConstructType getConstructData(VM& vm, JSValue value, ConstructData& constructData) |
1290 | { |
1291 | ConstructType result = value.isCell() ? value.asCell()->methodTable(vm)->getConstructData(value.asCell(), constructData) : ConstructType::None; |
1292 | ASSERT(result == ConstructType::None || value.isValidCallee()); |
1293 | return result; |
1294 | } |
1295 | |
1296 | inline JSObject* asObject(JSCell* cell) |
1297 | { |
1298 | ASSERT(cell->isObject()); |
1299 | return jsCast<JSObject*>(cell); |
1300 | } |
1301 | |
1302 | inline JSObject* asObject(JSValue value) |
1303 | { |
1304 | return asObject(value.asCell()); |
1305 | } |
1306 | |
1307 | inline JSObject::JSObject(VM& vm, Structure* structure, Butterfly* butterfly) |
1308 | : JSCell(vm, structure) |
1309 | , m_butterfly(vm, this, butterfly) |
1310 | { |
1311 | } |
1312 | |
1313 | inline JSValue JSObject::getPrototypeDirect(VM& vm) const |
1314 | { |
1315 | return structure(vm)->storedPrototype(this); |
1316 | } |
1317 | |
1318 | inline JSValue JSObject::getPrototype(VM& vm, ExecState* exec) |
1319 | { |
1320 | auto getPrototypeMethod = methodTable(vm)->getPrototype; |
1321 | MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype; |
1322 | if (LIKELY(getPrototypeMethod == defaultGetPrototype)) |
1323 | return getPrototypeDirect(vm); |
1324 | return getPrototypeMethod(this, exec); |
1325 | } |
1326 | |
1327 | // Normally, we never shrink the butterfly so if we know an offset is valid for some |
1328 | // past structure then it should be valid for any new structure. However, we may sometimes |
1329 | // shrink the butterfly when we are holding the Structure's ConcurrentJSLock, such as when we |
1330 | // flatten an object. |
1331 | inline JSValue JSObject::getDirectConcurrently(Structure* structure, PropertyOffset offset) const |
1332 | { |
1333 | ConcurrentJSLocker locker(structure->lock()); |
1334 | if (!structure->isValidOffset(offset)) |
1335 | return { }; |
1336 | return getDirect(offset); |
1337 | } |
1338 | |
1339 | // It is safe to call this method with a PropertyName that is actually an index, |
1340 | // but if so will always return false (doesn't search index storage). |
1341 | ALWAYS_INLINE bool JSObject::getOwnNonIndexPropertySlot(VM& vm, Structure* structure, PropertyName propertyName, PropertySlot& slot) |
1342 | { |
1343 | unsigned attributes; |
1344 | PropertyOffset offset = structure->get(vm, propertyName, attributes); |
1345 | if (!isValidOffset(offset)) { |
1346 | if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags())) |
1347 | return false; |
1348 | return getOwnStaticPropertySlot(vm, propertyName, slot); |
1349 | } |
1350 | |
1351 | // getPropertySlot relies on this method never returning index properties! |
1352 | ASSERT(!parseIndex(propertyName)); |
1353 | |
1354 | JSValue value = getDirect(offset); |
1355 | if (value.isCell()) { |
1356 | ASSERT(value); |
1357 | JSCell* cell = value.asCell(); |
1358 | JSType type = cell->type(); |
1359 | switch (type) { |
1360 | case GetterSetterType: |
1361 | fillGetterPropertySlot(vm, slot, cell, attributes, offset); |
1362 | return true; |
1363 | case CustomGetterSetterType: |
1364 | fillCustomGetterPropertySlot(vm, slot, jsCast<CustomGetterSetter*>(cell), attributes, structure); |
1365 | return true; |
1366 | default: |
1367 | break; |
1368 | } |
1369 | } |
1370 | |
1371 | slot.setValue(this, attributes, value, offset); |
1372 | return true; |
1373 | } |
1374 | |
1375 | ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(VM& vm, PropertySlot& slot, CustomGetterSetter* customGetterSetter, unsigned attributes, Structure* structure) |
1376 | { |
1377 | ASSERT(attributes & PropertyAttribute::CustomAccessorOrValue); |
1378 | if (customGetterSetter->inherits<DOMAttributeGetterSetter>(vm)) { |
1379 | auto* domAttribute = jsCast<DOMAttributeGetterSetter*>(customGetterSetter); |
1380 | if (structure->isUncacheableDictionary()) |
1381 | slot.setCustom(this, attributes, domAttribute->getter(), domAttribute->domAttribute()); |
1382 | else |
1383 | slot.setCacheableCustom(this, attributes, domAttribute->getter(), domAttribute->domAttribute()); |
1384 | return; |
1385 | } |
1386 | |
1387 | if (structure->isUncacheableDictionary()) |
1388 | slot.setCustom(this, attributes, customGetterSetter->getter()); |
1389 | else |
1390 | slot.setCacheableCustom(this, attributes, customGetterSetter->getter()); |
1391 | } |
1392 | |
1393 | // It may seem crazy to inline a function this large, especially a virtual function, |
1394 | // but it makes a big difference to property lookup that derived classes can inline their |
1395 | // base class call to this. |
1396 | ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
1397 | { |
1398 | VM& vm = exec->vm(); |
1399 | Structure* structure = object->structure(vm); |
1400 | if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot)) |
1401 | return true; |
1402 | if (Optional<uint32_t> index = parseIndex(propertyName)) |
1403 | return getOwnPropertySlotByIndex(object, exec, index.value(), slot); |
1404 | return false; |
1405 | } |
1406 | |
1407 | // It may seem crazy to inline a function this large but it makes a big difference |
1408 | // since this is function very hot in variable lookup |
1409 | template<bool checkNullStructure> |
1410 | ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
1411 | { |
1412 | VM& vm = exec->vm(); |
1413 | auto& structureIDTable = vm.heap.structureIDTable(); |
1414 | JSObject* object = this; |
1415 | while (true) { |
1416 | if (UNLIKELY(TypeInfo::overridesGetOwnPropertySlot(object->inlineTypeFlags()))) { |
1417 | // If propertyName is an index then we may have missed it (as this loop is using |
1418 | // getOwnNonIndexPropertySlot), so we cannot safely call the overridden getOwnPropertySlot |
1419 | // (lest we return a property from a prototype that is shadowed). Check now for an index, |
1420 | // if so we need to start afresh from this object. |
1421 | if (Optional<uint32_t> index = parseIndex(propertyName)) |
1422 | return getPropertySlot(exec, index.value(), slot); |
1423 | // Safe to continue searching from current position; call getNonIndexPropertySlot to avoid |
1424 | // parsing the int again. |
1425 | return object->getNonIndexPropertySlot(exec, propertyName, slot); |
1426 | } |
1427 | ASSERT(object->type() != ProxyObjectType); |
1428 | Structure* structure = structureIDTable.get(object->structureID()); |
1429 | #if USE(JSVALUE64) |
1430 | if (checkNullStructure && UNLIKELY(!structure)) |
1431 | CRASH_WITH_INFO(object->type(), object->structureID(), structureIDTable.size()); |
1432 | #endif |
1433 | if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot)) |
1434 | return true; |
1435 | // FIXME: This doesn't look like it's following the specification: |
1436 | // https://bugs.webkit.org/show_bug.cgi?id=172572 |
1437 | JSValue prototype = structure->storedPrototype(object); |
1438 | if (!prototype.isObject()) |
1439 | break; |
1440 | object = asObject(prototype); |
1441 | } |
1442 | |
1443 | if (Optional<uint32_t> index = parseIndex(propertyName)) |
1444 | return getPropertySlot(exec, index.value(), slot); |
1445 | return false; |
1446 | } |
1447 | |
1448 | inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const |
1449 | { |
1450 | VM& vm = exec->vm(); |
1451 | auto scope = DECLARE_THROW_SCOPE(vm); |
1452 | PropertySlot slot(this, PropertySlot::InternalMethodType::Get); |
1453 | bool hasProperty = const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); |
1454 | EXCEPTION_ASSERT(!scope.exception() || !hasProperty); |
1455 | if (hasProperty) |
1456 | RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName)); |
1457 | |
1458 | return jsUndefined(); |
1459 | } |
1460 | |
1461 | inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const |
1462 | { |
1463 | VM& vm = exec->vm(); |
1464 | auto scope = DECLARE_THROW_SCOPE(vm); |
1465 | PropertySlot slot(this, PropertySlot::InternalMethodType::Get); |
1466 | bool hasProperty = const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); |
1467 | EXCEPTION_ASSERT(!scope.exception() || !hasProperty); |
1468 | if (hasProperty) |
1469 | RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName)); |
1470 | |
1471 | return jsUndefined(); |
1472 | } |
1473 | |
1474 | inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
1475 | { |
1476 | ASSERT(value); |
1477 | ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); |
1478 | ASSERT(!structure(vm)->hasGetterSetterProperties()); |
1479 | ASSERT(!structure(vm)->hasCustomGetterSetterProperties()); |
1480 | |
1481 | return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot); |
1482 | } |
1483 | |
1484 | inline bool JSObject::putOwnDataPropertyMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
1485 | { |
1486 | VM& vm = exec->vm(); |
1487 | ASSERT(value); |
1488 | ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); |
1489 | ASSERT(!structure(vm)->hasGetterSetterProperties()); |
1490 | ASSERT(!structure(vm)->hasCustomGetterSetterProperties()); |
1491 | |
1492 | if (Optional<uint32_t> index = parseIndex(propertyName)) |
1493 | return putDirectIndex(exec, index.value(), value, 0, PutDirectIndexLikePutDirect); |
1494 | |
1495 | return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot); |
1496 | } |
1497 | |
1498 | inline bool JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) |
1499 | { |
1500 | ASSERT(!value.isGetterSetter() && !(attributes & PropertyAttribute::Accessor)); |
1501 | ASSERT(!value.isCustomGetterSetter() && !(attributes & PropertyAttribute::CustomAccessorOrValue)); |
1502 | PutPropertySlot slot(this); |
1503 | return putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot); |
1504 | } |
1505 | |
1506 | inline bool JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
1507 | { |
1508 | ASSERT(!value.isGetterSetter()); |
1509 | ASSERT(!value.isCustomGetterSetter()); |
1510 | return putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot); |
1511 | } |
1512 | |
1513 | ALWAYS_INLINE JSObject* Register::object() const |
1514 | { |
1515 | return asObject(jsValue()); |
1516 | } |
1517 | |
1518 | ALWAYS_INLINE Register& Register::operator=(JSObject* object) |
1519 | { |
1520 | u.value = JSValue::encode(JSValue(object)); |
1521 | return *this; |
1522 | } |
1523 | |
1524 | inline size_t offsetInButterfly(PropertyOffset offset) |
1525 | { |
1526 | return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); |
1527 | } |
1528 | |
1529 | inline size_t JSObject::butterflyPreCapacity() |
1530 | { |
1531 | VM& vm = *this->vm(); |
1532 | if (UNLIKELY(hasIndexingHeader(vm))) |
1533 | return butterfly()->indexingHeader()->preCapacity(structure(vm)); |
1534 | return 0; |
1535 | } |
1536 | |
1537 | inline size_t JSObject::butterflyTotalSize() |
1538 | { |
1539 | VM& vm = *this->vm(); |
1540 | Structure* structure = this->structure(vm); |
1541 | Butterfly* butterfly = this->butterfly(); |
1542 | size_t preCapacity; |
1543 | size_t indexingPayloadSizeInBytes; |
1544 | bool = this->hasIndexingHeader(vm); |
1545 | |
1546 | if (UNLIKELY(hasIndexingHeader)) { |
1547 | preCapacity = butterfly->indexingHeader()->preCapacity(structure); |
1548 | indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); |
1549 | } else { |
1550 | preCapacity = 0; |
1551 | indexingPayloadSizeInBytes = 0; |
1552 | } |
1553 | |
1554 | return Butterfly::totalSize(preCapacity, structure->outOfLineCapacity(), hasIndexingHeader, indexingPayloadSizeInBytes); |
1555 | } |
1556 | |
1557 | inline int indexRelativeToBase(PropertyOffset offset) |
1558 | { |
1559 | if (isOutOfLineOffset(offset)) |
1560 | return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); |
1561 | ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue))); |
1562 | return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset); |
1563 | } |
1564 | |
1565 | inline int offsetRelativeToBase(PropertyOffset offset) |
1566 | { |
1567 | if (isOutOfLineOffset(offset)) |
1568 | return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage(); |
1569 | return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue); |
1570 | } |
1571 | |
1572 | // Returns the maximum offset (away from zero) a load instruction will encode. |
1573 | inline size_t maxOffsetRelativeToBase(PropertyOffset offset) |
1574 | { |
1575 | ptrdiff_t addressOffset = offsetRelativeToBase(offset); |
1576 | #if USE(JSVALUE32_64) |
1577 | if (addressOffset >= 0) |
1578 | return static_cast<size_t>(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag); |
1579 | #endif |
1580 | return static_cast<size_t>(addressOffset); |
1581 | } |
1582 | |
1583 | COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment); |
1584 | |
1585 | template<unsigned charactersCount> |
1586 | ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char (&characters)[charactersCount]) |
1587 | { |
1588 | return Identifier::fromString(&vm, characters); |
1589 | } |
1590 | |
1591 | ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name) |
1592 | { |
1593 | return Identifier::fromString(&vm, name); |
1594 | } |
1595 | |
1596 | ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) |
1597 | { |
1598 | return name; |
1599 | } |
1600 | |
1601 | bool validateAndApplyPropertyDescriptor(ExecState*, JSObject*, PropertyName, bool isExtensible, |
1602 | const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException); |
1603 | |
1604 | JS_EXPORT_PRIVATE NEVER_INLINE bool ordinarySetSlow(ExecState*, JSObject*, PropertyName, JSValue, JSValue receiver, bool shouldThrow); |
1605 | |
1606 | // Helper for defining native functions, if you're not using a static hash table. |
1607 | // Use this macro from within finishCreation() methods in prototypes. This assumes |
1608 | // you've defined variables called exec, globalObject, and vm, and they |
1609 | // have the expected meanings. |
1610 | #define JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, attributes, length, intrinsic) \ |
1611 | putDirectNativeFunction(\ |
1612 | vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \ |
1613 | (intrinsic), (attributes)) |
1614 | |
1615 | #define JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length, intrinsic) \ |
1616 | putDirectNativeFunctionWithoutTransition(\ |
1617 | vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \ |
1618 | (intrinsic), (attributes)) |
1619 | |
1620 | // As above, but this assumes that the function you're defining doesn't have an |
1621 | // intrinsic. |
1622 | #define JSC_NATIVE_FUNCTION(jsName, cppName, attributes, length) \ |
1623 | JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, (attributes), (length), NoIntrinsic) |
1624 | |
1625 | #define JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length) \ |
1626 | JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, (attributes), (length), NoIntrinsic) |
1627 | |
1628 | // Identical helpers but for builtins. Note that currently, we don't support builtins that are |
1629 | // also intrinsics, but we probably will do that eventually. |
1630 | #define JSC_BUILTIN_FUNCTION(jsName, generatorName, attributes) \ |
1631 | putDirectBuiltinFunction(\ |
1632 | vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes)) |
1633 | |
1634 | #define JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(jsName, generatorName, attributes) \ |
1635 | putDirectBuiltinFunctionWithoutTransition(\ |
1636 | vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes)) |
1637 | |
1638 | // Helper for defining native getters on properties. |
1639 | #define JSC_NATIVE_INTRINSIC_GETTER(jsName, cppName, attributes, intrinsic) \ |
1640 | putDirectNativeIntrinsicGetter(\ |
1641 | vm, globalObject, makeIdentifier(vm, (jsName)), (cppName), \ |
1642 | (intrinsic), ((attributes) | PropertyAttribute::Accessor)) |
1643 | |
1644 | #define JSC_NATIVE_INTRINSIC_GETTER_WITHOUT_TRANSITION(jsName, cppName, attributes, intrinsic) \ |
1645 | putDirectNativeIntrinsicGetterWithoutTransition(\ |
1646 | vm, globalObject, makeIdentifier(vm, (jsName)), (cppName), \ |
1647 | (intrinsic), ((attributes) | PropertyAttribute::Accessor)) |
1648 | |
1649 | #define JSC_NATIVE_GETTER(jsName, cppName, attributes) \ |
1650 | JSC_NATIVE_INTRINSIC_GETTER((jsName), (cppName), (attributes), NoIntrinsic) |
1651 | |
1652 | #define JSC_NATIVE_GETTER_WITHOUT_TRANSITION(jsName, cppName, attributes) \ |
1653 | JSC_NATIVE_INTRINSIC_GETTER_WITHOUT_TRANSITION((jsName), (cppName), (attributes), NoIntrinsic) |
1654 | |
1655 | } // namespace JSC |
1656 | |