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