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