1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qv4object_p.h" |
41 | #include "qv4objectproto_p.h" |
42 | #include "qv4stringobject_p.h" |
43 | #include "qv4argumentsobject_p.h" |
44 | #include <private/qv4mm_p.h> |
45 | #include "qv4lookup_p.h" |
46 | #include "qv4scopedvalue_p.h" |
47 | #include "qv4memberdata_p.h" |
48 | #include "qv4objectiterator_p.h" |
49 | #include "qv4identifier_p.h" |
50 | #include "qv4string_p.h" |
51 | #include "qv4identifiertable_p.h" |
52 | #include "qv4jscall_p.h" |
53 | #include "qv4symbol_p.h" |
54 | #include "qv4proxy_p.h" |
55 | |
56 | #include <stdint.h> |
57 | |
58 | using namespace QV4; |
59 | |
60 | DEFINE_OBJECT_VTABLE(Object); |
61 | |
62 | void Object::setInternalClass(Heap::InternalClass *ic) |
63 | { |
64 | d()->internalClass.set(e: engine(), newVal: ic); |
65 | if (ic->isUsedAsProto) |
66 | ic->updateProtoUsage(o: d()); |
67 | Q_ASSERT(ic && ic->vtable); |
68 | uint nInline = d()->vtable()->nInlineProperties; |
69 | if (ic->size <= nInline) |
70 | return; |
71 | bool hasMD = d()->memberData != nullptr; |
72 | uint requiredSize = ic->size - nInline; |
73 | if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize)) |
74 | d()->memberData.set(e: ic->engine, newVal: MemberData::allocate(e: ic->engine, n: requiredSize, old: d()->memberData)); |
75 | } |
76 | |
77 | void Object::getProperty(const InternalClassEntry &entry, Property *p) const |
78 | { |
79 | p->value = *propertyData(index: entry.index); |
80 | if (entry.attributes.isAccessor()) |
81 | p->set = *propertyData(index: entry.setterIndex); |
82 | } |
83 | |
84 | void Object::setProperty(const InternalClassEntry &entry, const Property *p) |
85 | { |
86 | setProperty(index: entry.index, v: p->value); |
87 | if (entry.attributes.isAccessor()) |
88 | setProperty(index: entry.setterIndex, v: p->set); |
89 | } |
90 | |
91 | void Heap::Object::setUsedAsProto() |
92 | { |
93 | internalClass.set(e: internalClass->engine, newVal: internalClass->asProtoClass()); |
94 | } |
95 | |
96 | ReturnedValue Object::getValueAccessor(const Value *thisObject, const Value &v, PropertyAttributes attrs) |
97 | { |
98 | if (!attrs.isAccessor()) |
99 | return v.asReturnedValue(); |
100 | const QV4::FunctionObject *f = v.as<FunctionObject>(); |
101 | if (!f) |
102 | return Encode::undefined(); |
103 | |
104 | Scope scope(f->engine()); |
105 | JSCallData jsCallData(scope); |
106 | if (thisObject) |
107 | *jsCallData->thisObject = *thisObject; |
108 | return checkedResult(v4: scope.engine, result: f->call(data: jsCallData)); |
109 | } |
110 | |
111 | bool Object::putValue(uint memberIndex, PropertyAttributes attrs, const Value &value) |
112 | { |
113 | Heap::InternalClass *ic = internalClass(); |
114 | if (ic->engine->hasException) |
115 | return false; |
116 | |
117 | if (attrs.isAccessor()) { |
118 | const FunctionObject *set = propertyData(index: memberIndex)->as<FunctionObject>(); |
119 | if (set) { |
120 | Scope scope(ic->engine); |
121 | ScopedFunctionObject setter(scope, set); |
122 | JSCallData jsCallData(scope, 1); |
123 | jsCallData->args[0] = value; |
124 | *jsCallData->thisObject = this; |
125 | setter->call(data: jsCallData); |
126 | return !ic->engine->hasException; |
127 | } |
128 | return false; |
129 | } |
130 | |
131 | if (!attrs.isWritable()) |
132 | return false; |
133 | |
134 | setProperty(index: memberIndex, v: value); |
135 | return true; |
136 | } |
137 | |
138 | void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes) |
139 | { |
140 | ExecutionEngine *e = engine(); |
141 | Scope scope(e); |
142 | ScopedString s(scope, e->newIdentifier(text: name)); |
143 | defineDefaultProperty(name: s, value, attributes); |
144 | } |
145 | |
146 | void Object::defineDefaultProperty(const QString &name, VTable::Call code, |
147 | int argumentCount, PropertyAttributes attributes) |
148 | { |
149 | ExecutionEngine *e = engine(); |
150 | Scope scope(e); |
151 | ScopedString s(scope, e->newIdentifier(text: name)); |
152 | ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(engine: e, nameOrSymbol: s, code, argumentCount)); |
153 | defineDefaultProperty(name: s, value: function, attributes); |
154 | } |
155 | |
156 | void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, VTable::Call code, |
157 | int argumentCount, PropertyAttributes attributes) |
158 | { |
159 | ExecutionEngine *e = engine(); |
160 | Scope scope(e); |
161 | ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(engine: e, nameOrSymbol, code, argumentCount)); |
162 | defineDefaultProperty(name: nameOrSymbol, value: function, attributes); |
163 | } |
164 | |
165 | void Object::defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter) |
166 | { |
167 | ExecutionEngine *e = engine(); |
168 | Scope scope(e); |
169 | ScopedString s(scope, e->newIdentifier(text: name)); |
170 | defineAccessorProperty(name: s, getter, setter); |
171 | } |
172 | |
173 | void Object::defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter) |
174 | { |
175 | ExecutionEngine *v4 = engine(); |
176 | QV4::Scope scope(v4); |
177 | ScopedProperty p(scope); |
178 | QString n = name->toQString(); |
179 | if (n.at(i: 0) == QLatin1Char('@')) |
180 | n = QChar::fromLatin1(c: '[') + n.midRef(position: 1) + QChar::fromLatin1(c: ']'); |
181 | if (getter) { |
182 | ScopedString getName(scope, v4->newString(s: QString::fromLatin1(str: "get " ) + n)); |
183 | p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(engine: v4, nameOrSymbol: getName, code: getter, argumentCount: 0))); |
184 | } else { |
185 | p->setGetter(nullptr); |
186 | } |
187 | if (setter) { |
188 | ScopedString setName(scope, v4->newString(s: QString::fromLatin1(str: "set " ) + n)); |
189 | p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(engine: v4, nameOrSymbol: setName, code: setter, argumentCount: 0))); |
190 | } else { |
191 | p->setSetter(nullptr); |
192 | } |
193 | insertMember(s: name, p, attributes: QV4::Attr_Accessor|QV4::Attr_NotEnumerable); |
194 | } |
195 | |
196 | |
197 | |
198 | void Object::defineReadonlyProperty(const QString &name, const Value &value) |
199 | { |
200 | QV4::ExecutionEngine *e = engine(); |
201 | Scope scope(e); |
202 | ScopedString s(scope, e->newIdentifier(text: name)); |
203 | defineReadonlyProperty(name: s, value); |
204 | } |
205 | |
206 | void Object::defineReadonlyProperty(String *name, const Value &value) |
207 | { |
208 | insertMember(s: name, v: value, attributes: Attr_ReadOnly); |
209 | } |
210 | |
211 | void Object::defineReadonlyConfigurableProperty(const QString &name, const Value &value) |
212 | { |
213 | QV4::ExecutionEngine *e = engine(); |
214 | Scope scope(e); |
215 | ScopedString s(scope, e->newIdentifier(text: name)); |
216 | defineReadonlyConfigurableProperty(name: s, value); |
217 | } |
218 | |
219 | void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value) |
220 | { |
221 | insertMember(s: name, v: value, attributes: Attr_ReadOnly_ButConfigurable); |
222 | } |
223 | |
224 | void Object::addSymbolSpecies() |
225 | { |
226 | Scope scope(engine()); |
227 | ScopedProperty p(scope); |
228 | p->setGetter(scope.engine->getSymbolSpecies()); |
229 | p->setSetter(nullptr); |
230 | insertMember(s: scope.engine->symbol_species(), p, attributes: QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable); |
231 | } |
232 | |
233 | void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack) |
234 | { |
235 | Base::markObjects(b, stack); |
236 | Object *o = static_cast<Object *>(b); |
237 | if (o->memberData) |
238 | o->memberData->mark(markStack: stack); |
239 | if (o->arrayData) |
240 | o->arrayData->mark(markStack: stack); |
241 | uint nInline = o->vtable()->nInlineProperties; |
242 | Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset; |
243 | const Value *end = v + nInline; |
244 | while (v < end) { |
245 | v->mark(markStack: stack); |
246 | ++v; |
247 | } |
248 | } |
249 | |
250 | void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes) |
251 | { |
252 | InternalClassEntry idx; |
253 | PropertyKey key = s->toPropertyKey(); |
254 | Heap::InternalClass::addMember(object: this, id: key, data: attributes, entry: &idx); |
255 | |
256 | setProperty(index: idx.index, v: p->value); |
257 | if (attributes.isAccessor()) |
258 | setProperty(index: idx.setterIndex, v: p->set); |
259 | } |
260 | |
261 | void Object::setPrototypeUnchecked(const Object *p) |
262 | { |
263 | setInternalClass(internalClass()->changePrototype(proto: p ? p->d() : nullptr)); |
264 | } |
265 | |
266 | // Section 8.12.2 |
267 | PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs) |
268 | { |
269 | if (id.isArrayIndex()) { |
270 | uint index = id.asArrayIndex(); |
271 | Heap::Object *o = d(); |
272 | while (o) { |
273 | if (o->arrayData) { |
274 | uint idx = o->arrayData->mappedIndex(index); |
275 | if (idx != UINT_MAX) { |
276 | *attrs = o->arrayData->attributes(i: index); |
277 | return { .base: o->arrayData , .slot: o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) }; |
278 | } |
279 | } |
280 | if (o->vtable()->type == Type_StringObject) { |
281 | if (index < static_cast<const Heap::StringObject *>(o)->length()) { |
282 | // this is an evil hack, but it works, as the method is only ever called from put, |
283 | // where we don't use the returned pointer there for non writable attributes |
284 | *attrs = (Attr_NotWritable|Attr_NotConfigurable); |
285 | return { .base: reinterpret_cast<Heap::ArrayData *>(0x1), .slot: nullptr }; |
286 | } |
287 | } |
288 | o = o->prototype(); |
289 | } |
290 | } else { |
291 | Heap::Object *o = d(); |
292 | while (o) { |
293 | auto idx = o->internalClass->findValueOrSetter(id); |
294 | if (idx.isValid()) { |
295 | *attrs = idx.attrs; |
296 | return o->writablePropertyData(index: idx.index); |
297 | } |
298 | |
299 | o = o->prototype(); |
300 | } |
301 | } |
302 | *attrs = Attr_Invalid; |
303 | return { .base: nullptr, .slot: nullptr }; |
304 | } |
305 | |
306 | ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
307 | { |
308 | return static_cast<const Object *>(m)->internalGet(id, receiver, hasProperty); |
309 | } |
310 | |
311 | bool Object::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) |
312 | { |
313 | return static_cast<Object *>(m)->internalPut(id, value, receiver); |
314 | } |
315 | |
316 | bool Object::virtualDeleteProperty(Managed *m, PropertyKey id) |
317 | { |
318 | return static_cast<Object *>(m)->internalDeleteProperty(id); |
319 | } |
320 | |
321 | PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) |
322 | { |
323 | if (arrayIndex != UINT_MAX && o->arrayData()) { |
324 | SparseArrayNode *arrayNode = nullptr; |
325 | if (o->arrayType() == Heap::ArrayData::Sparse) { |
326 | SparseArray *sparse = o->arrayData()->sparse; |
327 | arrayNode = arrayIndex ? sparse->lowerBound(akey: arrayIndex) : sparse->begin(); |
328 | } |
329 | |
330 | // sparse arrays |
331 | if (arrayNode) { |
332 | while (arrayNode != o->sparseEnd()) { |
333 | uint k = arrayNode->key(); |
334 | uint pidx = arrayNode->value; |
335 | Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>(); |
336 | const Property *p = reinterpret_cast<const Property *>(sa->values.data() + pidx); |
337 | arrayNode = arrayNode->nextNode(); |
338 | PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data; |
339 | arrayIndex = k + 1; |
340 | if (pd) |
341 | pd->copy(other: p, attrs: a); |
342 | if (attrs) |
343 | *attrs = a; |
344 | return PropertyKey::fromArrayIndex(idx: k); |
345 | } |
346 | arrayIndex = UINT_MAX; |
347 | } |
348 | // dense arrays |
349 | while (arrayIndex < o->d()->arrayData->values.size) { |
350 | Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>(); |
351 | const Value &val = sa->data(index: arrayIndex); |
352 | PropertyAttributes a = o->arrayData()->attributes(i: arrayIndex); |
353 | int index = arrayIndex; |
354 | ++arrayIndex; |
355 | if (!val.isEmpty()) { |
356 | if (pd) |
357 | pd->value = val; |
358 | if (attrs) |
359 | *attrs = a; |
360 | return PropertyKey::fromArrayIndex(idx: index); |
361 | } |
362 | } |
363 | arrayIndex = UINT_MAX; |
364 | } |
365 | |
366 | while (true) { |
367 | while (memberIndex < o->internalClass()->size) { |
368 | PropertyKey n = o->internalClass()->nameMap.at(i: memberIndex); |
369 | ++memberIndex; |
370 | if (!n.isStringOrSymbol()) |
371 | // accessor properties have a dummy entry with n == 0 |
372 | continue; |
373 | if (!iterateOverSymbols && n.isSymbol()) |
374 | continue; |
375 | if (iterateOverSymbols && !n.isSymbol()) |
376 | continue; |
377 | |
378 | InternalClassEntry e = o->internalClass()->find(id: n); |
379 | if (!e.isValid()) |
380 | continue; |
381 | if (pd) { |
382 | pd->value = *o->propertyData(index: e.index); |
383 | if (e.attributes.isAccessor()) |
384 | pd->set = *o->propertyData(index: e.setterIndex); |
385 | } |
386 | if (attrs) |
387 | *attrs = e.attributes; |
388 | return n; |
389 | } |
390 | if (iterateOverSymbols) |
391 | break; |
392 | iterateOverSymbols = true; |
393 | memberIndex = 0; |
394 | } |
395 | |
396 | return PropertyKey::invalid(); |
397 | } |
398 | |
399 | OwnPropertyKeyIterator *Object::virtualOwnPropertyKeys(const Object *o, Value *target) |
400 | { |
401 | *target = *o; |
402 | return new ObjectOwnPropertyKeyIterator; |
403 | } |
404 | |
405 | // Section 8.12.3 |
406 | ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *hasProperty) const |
407 | { |
408 | Heap::Object *o = d(); |
409 | |
410 | if (id.isArrayIndex()) { |
411 | const uint index = id.asArrayIndex(); |
412 | Scope scope(this); |
413 | PropertyAttributes attrs; |
414 | ScopedProperty pd(scope); |
415 | while (1) { |
416 | if (o->arrayData && o->arrayData->getProperty(index, p: pd, attrs: &attrs)) { |
417 | if (hasProperty) |
418 | *hasProperty = true; |
419 | return Object::getValue(thisObject: receiver, v: pd->value, attrs); |
420 | } |
421 | if (o->internalClass->vtable->type == Type_StringObject) { |
422 | ScopedString str(scope, static_cast<Heap::StringObject *>(o)->getIndex(index)); |
423 | if (str) { |
424 | attrs = (Attr_NotWritable|Attr_NotConfigurable); |
425 | if (hasProperty) |
426 | *hasProperty = true; |
427 | return str.asReturnedValue(); |
428 | } |
429 | } |
430 | o = o->prototype(); |
431 | if (!o || o->internalClass->vtable->get != Object::virtualGet) |
432 | break; |
433 | } |
434 | } else { |
435 | while (1) { |
436 | auto idx = o->internalClass->findValueOrGetter(id); |
437 | if (idx.isValid()) { |
438 | if (hasProperty) |
439 | *hasProperty = true; |
440 | return Object::getValue(thisObject: receiver, v: *o->propertyData(index: idx.index), attrs: idx.attrs); |
441 | } |
442 | o = o->prototype(); |
443 | if (!o || o->internalClass->vtable->get != Object::virtualGet) |
444 | break; |
445 | } |
446 | } |
447 | |
448 | if (o) { |
449 | const Value v = Value::fromHeapObject(m: o); |
450 | const Object &obj = static_cast<const Object &>(v); |
451 | return obj.get(id, receiver, hasProperty); |
452 | } |
453 | |
454 | if (hasProperty) |
455 | *hasProperty = false; |
456 | return Encode::undefined(); |
457 | } |
458 | |
459 | // Section 8.12.5 |
460 | bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) |
461 | { |
462 | Scope scope(this); |
463 | if (scope.engine->hasException) |
464 | return false; |
465 | |
466 | Object *r = receiver->objectValue(); |
467 | if (r && r->d() == d()) { |
468 | // receiver and this object are the same |
469 | if (d()->internalClass->vtable->getOwnProperty == Object::virtualGetOwnProperty) { |
470 | // This object standard methods in the vtable, so we can take a shortcut |
471 | // and avoid the calls to getOwnProperty and defineOwnProperty |
472 | |
473 | PropertyAttributes attrs; |
474 | PropertyIndex propertyIndex{.base: nullptr, .slot: nullptr}; |
475 | |
476 | if (id.isArrayIndex()) { |
477 | if (arrayData()) |
478 | propertyIndex = arrayData()->getValueOrSetter(index: id.asArrayIndex(), attrs: &attrs); |
479 | } else { |
480 | auto member = internalClass()->findValueOrSetter(id); |
481 | if (member.isValid()) { |
482 | attrs = member.attrs; |
483 | propertyIndex = d()->writablePropertyData(index: member.index); |
484 | } |
485 | } |
486 | |
487 | if (!propertyIndex.isNull() && !attrs.isAccessor()) { |
488 | if (!attrs.isWritable()) |
489 | return false; |
490 | else if (isArrayObject() && id == scope.engine->id_length()->propertyKey()) { |
491 | bool ok; |
492 | uint l = value.asArrayLength(ok: &ok); |
493 | if (!ok) { |
494 | scope.engine->throwRangeError(value); |
495 | return false; |
496 | } |
497 | ok = setArrayLength(l); |
498 | if (!ok) |
499 | return false; |
500 | } else { |
501 | propertyIndex.set(e: scope.engine, newVal: value); |
502 | } |
503 | return true; |
504 | } |
505 | } |
506 | } |
507 | |
508 | ScopedProperty p(scope); |
509 | PropertyAttributes attrs; |
510 | attrs = getOwnProperty(id, p); |
511 | if (attrs == Attr_Invalid) { |
512 | ScopedObject p(scope, getPrototypeOf()); |
513 | if (p) |
514 | return p->put(id, v: value, receiver); |
515 | attrs = Attr_Data; |
516 | } |
517 | |
518 | if (attrs.isAccessor()) { |
519 | ScopedFunctionObject setter(scope, p->setter()); |
520 | if (!setter) |
521 | return false; |
522 | JSCallData jsCallData(scope, 1); |
523 | jsCallData->args[0] = value; |
524 | *jsCallData->thisObject = *receiver; |
525 | setter->call(data: jsCallData); |
526 | return !scope.engine->hasException; |
527 | } |
528 | |
529 | // Data property |
530 | if (!attrs.isWritable()) |
531 | return false; |
532 | if (!r) |
533 | return false; |
534 | attrs = r->getOwnProperty(id, p); |
535 | |
536 | if (attrs != Attr_Invalid) { |
537 | if (attrs.isAccessor() || !attrs.isWritable()) |
538 | return false; |
539 | } else { |
540 | if (!r->isExtensible()) |
541 | return false; |
542 | attrs = Attr_Data; |
543 | } |
544 | |
545 | if (r->internalClass()->vtable->defineOwnProperty == virtualDefineOwnProperty) { |
546 | // standard object, we can avoid some more checks |
547 | if (id.isArrayIndex()) { |
548 | r->arraySet(index: id.asArrayIndex(), value); |
549 | } else { |
550 | ScopedStringOrSymbol s(scope, id.asStringOrSymbol()); |
551 | r->insertMember(s, v: value); |
552 | } |
553 | return true; |
554 | } |
555 | |
556 | p->value = value; |
557 | return r->defineOwnProperty(id, p, attrs); |
558 | } |
559 | |
560 | // Section 8.12.7 |
561 | bool Object::internalDeleteProperty(PropertyKey id) |
562 | { |
563 | if (internalClass()->engine->hasException) |
564 | return false; |
565 | |
566 | if (id.isArrayIndex()) { |
567 | uint index = id.asArrayIndex(); |
568 | Scope scope(engine()); |
569 | if (scope.engine->hasException) |
570 | return false; |
571 | |
572 | Scoped<ArrayData> ad(scope, arrayData()); |
573 | if (!ad || ad->vtable()->del(this, index)) |
574 | return true; |
575 | |
576 | return false; |
577 | } |
578 | |
579 | auto memberIdx = internalClass()->findValueOrGetter(id); |
580 | if (memberIdx.isValid()) { |
581 | if (memberIdx.attrs.isConfigurable()) { |
582 | Heap::InternalClass::removeMember(object: this, identifier: id); |
583 | return true; |
584 | } |
585 | return false; |
586 | } |
587 | |
588 | return true; |
589 | } |
590 | |
591 | bool Object::internalDefineOwnProperty(ExecutionEngine *engine, uint index, const InternalClassEntry *memberEntry, const Property *p, PropertyAttributes attrs) |
592 | { |
593 | // clause 5 |
594 | if (attrs.isEmpty()) |
595 | return true; |
596 | |
597 | Scope scope(engine); |
598 | ScopedProperty current(scope); |
599 | PropertyAttributes cattrs; |
600 | if (memberEntry) { |
601 | getProperty(entry: *memberEntry, p: current); |
602 | cattrs = memberEntry->attributes; |
603 | } else if (arrayData()) { |
604 | arrayData()->getProperty(index, p: current, attrs: &cattrs); |
605 | cattrs = arrayData()->attributes(i: index); |
606 | } |
607 | |
608 | // clause 6 |
609 | if (p->isSubset(attrs, other: current, otherAttrs: cattrs)) |
610 | return true; |
611 | |
612 | // clause 7 |
613 | if (!cattrs.isConfigurable()) { |
614 | if (attrs.isConfigurable()) |
615 | return false; |
616 | if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) |
617 | return false; |
618 | } |
619 | |
620 | // clause 8 |
621 | if (attrs.isGeneric() || current->value.isEmpty()) |
622 | goto accept; |
623 | |
624 | // clause 9 |
625 | if (cattrs.isData() != attrs.isData()) { |
626 | // 9a |
627 | if (!cattrs.isConfigurable()) |
628 | return false; |
629 | if (cattrs.isData()) { |
630 | // 9b |
631 | cattrs.setType(PropertyAttributes::Accessor); |
632 | cattrs.clearWritable(); |
633 | if (!memberEntry) { |
634 | // need to convert the array and the slot |
635 | initSparseArray(); |
636 | Q_ASSERT(arrayData()); |
637 | setArrayAttributes(i: index, a: cattrs); |
638 | } |
639 | current->setGetter(nullptr); |
640 | current->setSetter(nullptr); |
641 | } else { |
642 | // 9c |
643 | cattrs.setType(PropertyAttributes::Data); |
644 | cattrs.setWritable(false); |
645 | if (!memberEntry) { |
646 | // need to convert the array and the slot |
647 | setArrayAttributes(i: index, a: cattrs); |
648 | } |
649 | current->value = Value::undefinedValue(); |
650 | } |
651 | } else if (cattrs.isData() && attrs.isData()) { // clause 10 |
652 | if (!cattrs.isConfigurable() && !cattrs.isWritable()) { |
653 | if (attrs.isWritable() || !current->value.sameValue(other: p->value)) |
654 | return false; |
655 | } |
656 | } else { // clause 10 |
657 | Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor()); |
658 | if (!cattrs.isConfigurable()) { |
659 | if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue()) |
660 | return false; |
661 | if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue()) |
662 | return false; |
663 | } |
664 | } |
665 | |
666 | accept: |
667 | |
668 | current->merge(attrs&: cattrs, other: p, otherAttrs: attrs); |
669 | if (memberEntry) { |
670 | PropertyKey key = internalClass()->nameMap.at(i: memberEntry->index); |
671 | InternalClassEntry e; |
672 | Heap::InternalClass::changeMember(object: this, id: key, data: cattrs, entry: &e); |
673 | setProperty(entry: e, p: current); |
674 | } else { |
675 | setArrayAttributes(i: index, a: cattrs); |
676 | arrayData()->setProperty(e: scope.engine, index, p: current); |
677 | } |
678 | return true; |
679 | } |
680 | |
681 | void Object::copyArrayData(Object *other) |
682 | { |
683 | Q_ASSERT(isArrayObject()); |
684 | Scope scope(engine()); |
685 | |
686 | if (other->protoHasArray() || ArgumentsObject::isNonStrictArgumentsObject(m: other) || |
687 | (other->arrayType() == Heap::ArrayData::Sparse && other->arrayData()->attrs)) { |
688 | uint len = other->getLength(); |
689 | Q_ASSERT(len); |
690 | |
691 | ScopedValue v(scope); |
692 | for (uint i = 0; i < len; ++i) { |
693 | arraySet(index: i, value: (v = other->get(idx: i))); |
694 | } |
695 | } else if (!other->arrayData()) { |
696 | ; |
697 | } else { |
698 | Q_ASSERT(!arrayData() && other->arrayData()); |
699 | ArrayData::realloc(o: this, newType: static_cast<ArrayData::Type>(other->d()->arrayData->type), |
700 | alloc: other->d()->arrayData->values.alloc, enforceAttributes: false); |
701 | if (other->arrayType() == Heap::ArrayData::Sparse) { |
702 | Heap::ArrayData *od = other->d()->arrayData; |
703 | Heap::ArrayData *dd = d()->arrayData; |
704 | dd->sparse = new SparseArray(*od->sparse); |
705 | } else { |
706 | Heap::ArrayData *dd = d()->arrayData; |
707 | dd->values.size = other->d()->arrayData->values.size; |
708 | dd->offset = other->d()->arrayData->offset; |
709 | } |
710 | // ### need a write barrier |
711 | memcpy(dest: d()->arrayData->values.values, src: other->d()->arrayData->values.values, n: other->d()->arrayData->values.alloc*sizeof(Value)); |
712 | } |
713 | setArrayLengthUnchecked(other->getLength()); |
714 | } |
715 | |
716 | qint64 Object::virtualGetLength(const Managed *m) |
717 | { |
718 | Scope scope(static_cast<const Object *>(m)->engine()); |
719 | ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(name: scope.engine->id_length())); |
720 | return v->toLength(); |
721 | } |
722 | |
723 | // 'var' is 'V' in 15.3.5.3. |
724 | ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &var) |
725 | { |
726 | QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; |
727 | |
728 | // 15.3.5.3, Assume F is a Function object. |
729 | const FunctionObject *function = typeObject->as<FunctionObject>(); |
730 | if (!function) |
731 | return engine->throwTypeError(); |
732 | |
733 | return checkedInstanceOf(engine, typeObject: function, var); |
734 | } |
735 | |
736 | ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) |
737 | { |
738 | Heap::Object *obj = object->d(); |
739 | PropertyKey name = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); |
740 | if (name.isArrayIndex()) { |
741 | lookup->indexedLookup.index = name.asArrayIndex(); |
742 | lookup->getter = Lookup::getterIndexed; |
743 | return lookup->getter(lookup, engine, *object); |
744 | } |
745 | |
746 | auto index = obj->internalClass->findValueOrGetter(id: name); |
747 | if (index.isValid()) { |
748 | PropertyAttributes attrs = index.attrs; |
749 | uint nInline = obj->vtable()->nInlineProperties; |
750 | if (attrs.isData()) { |
751 | if (index.index < obj->vtable()->nInlineProperties) { |
752 | index.index += obj->vtable()->inlinePropertyOffset; |
753 | lookup->getter = Lookup::getter0Inline; |
754 | } else { |
755 | index.index -= nInline; |
756 | lookup->getter = Lookup::getter0MemberData; |
757 | } |
758 | } else { |
759 | lookup->getter = Lookup::getterAccessor; |
760 | } |
761 | lookup->objectLookup.ic = obj->internalClass; |
762 | lookup->objectLookup.offset = index.index; |
763 | return lookup->getter(lookup, engine, *object); |
764 | } |
765 | |
766 | lookup->protoLookup.protoId = obj->internalClass->protoId; |
767 | lookup->resolveProtoGetter(name, proto: obj->prototype()); |
768 | return lookup->getter(lookup, engine, *object); |
769 | } |
770 | |
771 | bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) |
772 | { |
773 | Scope scope(engine); |
774 | ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); |
775 | |
776 | Heap::InternalClass *c = object->internalClass(); |
777 | PropertyKey key = name->toPropertyKey(); |
778 | auto idx = c->findValueOrSetter(id: key); |
779 | if (idx.isValid()) { |
780 | if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) { |
781 | Q_ASSERT(!idx.attrs.isAccessor()); |
782 | lookup->setter = Lookup::arrayLengthSetter; |
783 | return lookup->setter(lookup, engine, *object, value); |
784 | } else if (idx.attrs.isData() && idx.attrs.isWritable()) { |
785 | lookup->objectLookup.ic = object->internalClass(); |
786 | lookup->objectLookup.index = idx.index; |
787 | const auto nInline = object->d()->vtable()->nInlineProperties; |
788 | if (idx.index < nInline) { |
789 | lookup->setter = Lookup::setter0Inline; |
790 | lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset; |
791 | } else { |
792 | lookup->setter = Lookup::setter0MemberData; |
793 | lookup->objectLookup.offset = idx.index - nInline; |
794 | } |
795 | return lookup->setter(lookup, engine, *object, value); |
796 | } else { |
797 | // ### handle setter |
798 | lookup->setter = Lookup::setterFallback; |
799 | } |
800 | return lookup->setter(lookup, engine, *object, value); |
801 | } |
802 | |
803 | lookup->insertionLookup.protoId = c->protoId; |
804 | if (!object->put(id: key, v: value)) { |
805 | lookup->setter = Lookup::setterFallback; |
806 | return false; |
807 | } |
808 | |
809 | if (object->internalClass() == c) { |
810 | // ### setter in the prototype, should handle this |
811 | lookup->setter = Lookup::setterFallback; |
812 | return true; |
813 | } |
814 | idx = object->internalClass()->findValueOrSetter(id: key); |
815 | if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen? |
816 | lookup->setter = Lookup::setterFallback; |
817 | return false; |
818 | } |
819 | lookup->insertionLookup.newClass = object->internalClass(); |
820 | lookup->insertionLookup.offset = idx.index; |
821 | lookup->setter = Lookup::setterInsert; |
822 | return true; |
823 | } |
824 | |
825 | ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var) |
826 | { |
827 | Scope scope(engine); |
828 | if (f->isBoundFunction()) { |
829 | ScopedValue v(scope, static_cast<const BoundFunction *>(f)->target()); |
830 | f = v->as<FunctionObject>(); |
831 | } |
832 | |
833 | // 15.3.5.3, 1: HasInstance can only be used on an object |
834 | const Object *lhs = var.as<Object>(); |
835 | if (!lhs) |
836 | return Encode(false); |
837 | |
838 | // 15.3.5.3, 2 |
839 | Value p = Value::fromReturnedValue(val: f->protoProperty()); |
840 | const Object *o = p.objectValue(); |
841 | if (!o) // 15.3.5.3, 3 |
842 | return f->engine()->throwTypeError(); |
843 | |
844 | Heap::Object *v = lhs->d(); |
845 | |
846 | // 15.3.5.3, 4 |
847 | while (v) { |
848 | // 15.3.5.3, 4, a |
849 | v = v->prototype(); |
850 | |
851 | // 15.3.5.3, 4, b |
852 | if (!v) |
853 | break; // will return false |
854 | |
855 | // 15.3.5.3, 4, c |
856 | else if (o->d() == v) |
857 | return Encode(true); |
858 | } |
859 | |
860 | return Encode(false); |
861 | } |
862 | |
863 | bool Object::virtualHasProperty(const Managed *m, PropertyKey id) |
864 | { |
865 | Scope scope(m->engine()); |
866 | ScopedObject o(scope, m); |
867 | ScopedProperty p(scope); |
868 | |
869 | if (o->getOwnProperty(id, p) != Attr_Invalid) |
870 | return true; |
871 | |
872 | o = o->getPrototypeOf(); |
873 | if (o) |
874 | return o->hasProperty(id); |
875 | |
876 | return false; |
877 | } |
878 | |
879 | PropertyAttributes Object::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) |
880 | { |
881 | PropertyAttributes attrs; |
882 | const Object *o = static_cast<const Object *>(m); |
883 | if (id.isArrayIndex()) { |
884 | uint index = id.asArrayIndex(); |
885 | if (o->arrayData()) { |
886 | if (o->arrayData()->getProperty(index, p, attrs: &attrs)) |
887 | return attrs; |
888 | } |
889 | } else { |
890 | Q_ASSERT(id.asStringOrSymbol()); |
891 | |
892 | auto member = o->internalClass()->find(id); |
893 | if (member.isValid()) { |
894 | attrs = member.attributes; |
895 | if (p) { |
896 | p->value = *o->propertyData(index: member.index); |
897 | if (attrs.isAccessor()) |
898 | p->set = *o->propertyData(index: member.setterIndex); |
899 | } |
900 | return attrs; |
901 | } |
902 | } |
903 | |
904 | return Attr_Invalid; |
905 | } |
906 | |
907 | bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) |
908 | { |
909 | Object *o = static_cast<Object *>(m); |
910 | Scope scope(o); |
911 | |
912 | if (id.isArrayIndex()) { |
913 | uint index = id.asArrayIndex(); |
914 | |
915 | bool hasProperty = false; |
916 | |
917 | if (o->arrayData()) { |
918 | hasProperty = o->arrayData()->mappedIndex(index) != UINT_MAX; |
919 | if (!hasProperty && o->isStringObject()) |
920 | hasProperty = (index < static_cast<StringObject *>(o)->length()); |
921 | } |
922 | |
923 | if (!hasProperty) { |
924 | if (!o->isExtensible()) |
925 | return false; |
926 | |
927 | ScopedProperty pp(scope); |
928 | pp->copy(other: p, attrs); |
929 | pp->fullyPopulated(attrs: &attrs); |
930 | if (attrs == Attr_Data) { |
931 | ScopedValue v(scope, pp->value); |
932 | o->arraySet(index, value: v); |
933 | } else { |
934 | o->arraySet(index, p: pp, attributes: attrs); |
935 | } |
936 | return true; |
937 | } |
938 | |
939 | return o->internalDefineOwnProperty(engine: scope.engine, index, memberEntry: nullptr, p, attrs); |
940 | } |
941 | |
942 | auto memberIndex = o->internalClass()->find(id); |
943 | |
944 | if (!memberIndex.isValid()) { |
945 | if (!o->isExtensible()) |
946 | return false; |
947 | |
948 | Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); |
949 | ScopedProperty pd(scope); |
950 | pd->copy(other: p, attrs); |
951 | pd->fullyPopulated(attrs: &attrs); |
952 | o->insertMember(s: name, p: pd, attributes: attrs); |
953 | return true; |
954 | } |
955 | |
956 | return o->internalDefineOwnProperty(engine: scope.engine, UINT_MAX, memberEntry: &memberIndex, p, attrs); |
957 | } |
958 | |
959 | bool Object::virtualIsExtensible(const Managed *m) |
960 | { |
961 | return m->d()->internalClass->extensible; |
962 | } |
963 | |
964 | bool Object::virtualPreventExtensions(Managed *m) |
965 | { |
966 | Q_ASSERT(m->isObject()); |
967 | Object *o = static_cast<Object *>(m); |
968 | o->setInternalClass(o->internalClass()->nonExtensible()); |
969 | return true; |
970 | } |
971 | |
972 | Heap::Object *Object::virtualGetPrototypeOf(const Managed *m) |
973 | { |
974 | return m->internalClass()->prototype; |
975 | } |
976 | |
977 | bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto) |
978 | { |
979 | Q_ASSERT(m->isObject()); |
980 | Object *o = static_cast<Object *>(m); |
981 | Heap::Object *current = o->internalClass()->prototype; |
982 | Heap::Object *protod = proto ? proto->d() : nullptr; |
983 | if (current == protod) |
984 | return true; |
985 | if (!o->internalClass()->extensible) |
986 | return false; |
987 | Heap::Object *p = protod; |
988 | while (p) { |
989 | if (p == o->d()) |
990 | return false; |
991 | if (p->vtable()->getPrototypeOf != Object::staticVTable()->getPrototypeOf) |
992 | break; |
993 | p = p->prototype(); |
994 | } |
995 | o->setInternalClass(o->internalClass()->changePrototype(proto: protod)); |
996 | return true; |
997 | } |
998 | |
999 | bool Object::setArrayLength(uint newLen) |
1000 | { |
1001 | Q_ASSERT(isArrayObject()); |
1002 | if (!internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) |
1003 | return false; |
1004 | uint oldLen = getLength(); |
1005 | bool ok = true; |
1006 | if (newLen < oldLen) { |
1007 | if (arrayData()) { |
1008 | uint l = arrayData()->vtable()->truncate(this, newLen); |
1009 | if (l != newLen) |
1010 | ok = false; |
1011 | newLen = l; |
1012 | } |
1013 | } else { |
1014 | if (newLen >= 0x100000) |
1015 | initSparseArray(); |
1016 | } |
1017 | setArrayLengthUnchecked(newLen); |
1018 | return ok; |
1019 | } |
1020 | |
1021 | void Object::initSparseArray() |
1022 | { |
1023 | if (arrayType() == Heap::ArrayData::Sparse) |
1024 | return; |
1025 | |
1026 | ArrayData::realloc(o: this, newType: Heap::ArrayData::Sparse, alloc: 0, enforceAttributes: false); |
1027 | } |
1028 | |
1029 | bool Object::isConcatSpreadable() const |
1030 | { |
1031 | Scope scope(this); |
1032 | ScopedValue spreadable(scope, get(name: scope.engine->symbol_isConcatSpreadable())); |
1033 | if (!spreadable->isUndefined()) |
1034 | return spreadable->toBoolean(); |
1035 | return isArray(); |
1036 | } |
1037 | |
1038 | bool Object::isArray() const |
1039 | { |
1040 | if (isArrayObject()) |
1041 | return true; |
1042 | if (vtable() == ProxyObject::staticVTable()) { |
1043 | const ProxyObject *p = static_cast<const ProxyObject *>(this); |
1044 | Scope scope(this); |
1045 | if (!p->d()->handler) { |
1046 | scope.engine->throwTypeError(); |
1047 | return false; |
1048 | } |
1049 | ScopedObject o(scope, p->d()->target); |
1050 | return o->isArray(); |
1051 | } |
1052 | return false; |
1053 | } |
1054 | |
1055 | const FunctionObject *Object::speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const |
1056 | { |
1057 | ScopedValue C(scope, get(name: scope.engine->id_constructor())); |
1058 | if (C->isUndefined()) |
1059 | return defaultConstructor; |
1060 | const Object *c = C->objectValue(); |
1061 | if (!c) { |
1062 | scope.engine->throwTypeError(); |
1063 | return nullptr; |
1064 | } |
1065 | ScopedValue S(scope, c->get(name: scope.engine->symbol_species())); |
1066 | if (S->isNullOrUndefined()) |
1067 | return defaultConstructor; |
1068 | const FunctionObject *f = S->as<FunctionObject>(); |
1069 | if (!f || !f->isConstructor()) { |
1070 | scope.engine->throwTypeError(); |
1071 | return nullptr; |
1072 | } |
1073 | Q_ASSERT(f->isFunctionObject()); |
1074 | return static_cast<const FunctionObject *>(f); |
1075 | } |
1076 | |
1077 | bool Object::setProtoFromNewTarget(const Value *newTarget) |
1078 | { |
1079 | if (!newTarget || newTarget->isUndefined()) |
1080 | return false; |
1081 | |
1082 | Q_ASSERT(newTarget->isFunctionObject()); |
1083 | Scope scope(this); |
1084 | ScopedObject proto(scope, static_cast<const FunctionObject *>(newTarget)->protoProperty()); |
1085 | if (proto) { |
1086 | setPrototypeOf(proto); |
1087 | return true; |
1088 | } |
1089 | return false; |
1090 | } |
1091 | |
1092 | |
1093 | DEFINE_OBJECT_VTABLE(ArrayObject); |
1094 | |
1095 | void Heap::ArrayObject::init(const QStringList &list) |
1096 | { |
1097 | Object::init(); |
1098 | commonInit(); |
1099 | Scope scope(internalClass->engine); |
1100 | ScopedObject a(scope, this); |
1101 | |
1102 | // Converts a QStringList to JS. |
1103 | // The result is a new Array object with length equal to the length |
1104 | // of the QStringList, and the elements being the QStringList's |
1105 | // elements converted to JS Strings. |
1106 | int len = list.count(); |
1107 | a->arrayReserve(n: len); |
1108 | ScopedValue v(scope); |
1109 | for (int ii = 0; ii < len; ++ii) |
1110 | a->arrayPut(index: ii, value: (v = scope.engine->newString(s: list.at(i: ii)))); |
1111 | a->setArrayLengthUnchecked(len); |
1112 | } |
1113 | |
1114 | qint64 ArrayObject::virtualGetLength(const Managed *m) |
1115 | { |
1116 | const ArrayObject *a = static_cast<const ArrayObject *>(m); |
1117 | return a->propertyData(index: Heap::ArrayObject::LengthPropertyIndex)->toLength(); |
1118 | } |
1119 | |
1120 | QStringList ArrayObject::toQStringList() const |
1121 | { |
1122 | QStringList result; |
1123 | |
1124 | QV4::ExecutionEngine *engine = internalClass()->engine; |
1125 | Scope scope(engine); |
1126 | ScopedValue v(scope); |
1127 | |
1128 | uint length = getLength(); |
1129 | for (uint i = 0; i < length; ++i) { |
1130 | v = const_cast<ArrayObject *>(this)->get(idx: i); |
1131 | result.append(t: v->toQStringNoThrow()); |
1132 | } |
1133 | return result; |
1134 | } |
1135 | |
1136 | bool ArrayObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) |
1137 | { |
1138 | Q_ASSERT(m->isArrayObject()); |
1139 | ArrayObject *a = static_cast<ArrayObject *>(m); |
1140 | |
1141 | if (id.isArrayIndex()) { |
1142 | uint index = id.asArrayIndex(); |
1143 | uint len = a->getLength(); |
1144 | if (index >= len && !a->internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) |
1145 | return false; |
1146 | |
1147 | bool succeeded = Object::virtualDefineOwnProperty(m, id, p, attrs); |
1148 | if (!succeeded) |
1149 | return false; |
1150 | |
1151 | if (index >= len) |
1152 | a->setArrayLengthUnchecked(index + 1); |
1153 | |
1154 | return true; |
1155 | } |
1156 | |
1157 | ExecutionEngine *engine = m->engine(); |
1158 | if (id == engine->id_length()->propertyKey()) { |
1159 | Scope scope(engine); |
1160 | Q_ASSERT(a->internalClass()->verifyIndex(engine->id_length()->propertyKey(), Heap::ArrayObject::LengthPropertyIndex)); |
1161 | ScopedProperty lp(scope); |
1162 | InternalClassEntry e = a->internalClass()->find(id: scope.engine->id_length()->propertyKey()); |
1163 | a->getProperty(entry: e, p: lp); |
1164 | if (attrs.isEmpty() || p->isSubset(attrs, other: lp, otherAttrs: e.attributes)) |
1165 | return true; |
1166 | if (!e.attributes.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) |
1167 | return false; |
1168 | bool succeeded = true; |
1169 | if (attrs.type() == PropertyAttributes::Data) { |
1170 | bool ok; |
1171 | uint l = p->value.asArrayLength(ok: &ok); |
1172 | if (!ok) { |
1173 | ScopedValue v(scope, p->value); |
1174 | engine->throwRangeError(value: v); |
1175 | return false; |
1176 | } |
1177 | succeeded = a->setArrayLength(l); |
1178 | } |
1179 | if (attrs.hasWritable() && !attrs.isWritable()) { |
1180 | e.attributes.setWritable(false); |
1181 | Heap::InternalClass::changeMember(object: a, id: engine->id_length()->propertyKey(), data: e.attributes); |
1182 | } |
1183 | if (!succeeded) |
1184 | return false; |
1185 | return true; |
1186 | } |
1187 | return Object::virtualDefineOwnProperty(m, id, p, attrs); |
1188 | } |
1189 | |