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 <private/qv4functionobject_p.h>
5#include <private/qv4identifiertable_p.h>
6#include <private/qv4lookup_p.h>
7#include <private/qv4qobjectwrapper_p.h>
8#include <private/qv4runtime_p.h>
9#include <private/qv4stackframe_p.h>
10
11QT_BEGIN_NAMESPACE
12
13using namespace QV4;
14
15
16void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
17{
18 while (proto) {
19 auto index = proto->internalClass->findValueOrGetter(id: name);
20 if (index.isValid()) {
21 PropertyAttributes attrs = index.attrs;
22 protoLookup.data = proto->propertyData(index: index.index);
23 if (attrs.isData()) {
24 getter = getterProto;
25 } else {
26 getter = getterProtoAccessor;
27 }
28 return;
29 }
30 proto = proto->prototype();
31 }
32 // ### put in a getterNotFound!
33 getter = getterFallback;
34}
35
36ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
37{
38 return object->resolveLookupGetter(engine, lookup: this);
39}
40
41ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object)
42{
43 // Otherwise we cannot trust the protoIds
44 Q_ASSERT(engine->isInitialized);
45
46 primitiveLookup.type = object.type();
47 switch (primitiveLookup.type) {
48 case Value::Undefined_Type:
49 case Value::Null_Type: {
50 Scope scope(engine);
51 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
52 const QString message = QStringLiteral("Cannot read property '%1' of %2").arg(a: name->toQString())
53 .arg(a: QLatin1String(primitiveLookup.type == Value::Undefined_Type ? "undefined" : "null"));
54 return engine->throwTypeError(message);
55 }
56 case Value::Boolean_Type:
57 primitiveLookup.proto = engine->booleanPrototype()->d();
58 break;
59 case Value::Managed_Type: {
60 // ### Should move this over to the Object path, as strings also have an internalClass
61 Q_ASSERT(object.isStringOrSymbol());
62 primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype;
63 Q_ASSERT(primitiveLookup.proto);
64 Scope scope(engine);
65 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
66 if (object.isString() && name->equals(other: engine->id_length())) {
67 // special case, as the property is on the object itself
68 getter = stringLengthGetter;
69 return stringLengthGetter(l: this, engine, object);
70 }
71 break;
72 }
73 case Value::Integer_Type:
74 default: // Number
75 primitiveLookup.proto = engine->numberPrototype()->d();
76 }
77
78 PropertyKey name = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
79 protoLookup.protoId = primitiveLookup.proto->internalClass->protoId;
80 resolveProtoGetter(name, proto: primitiveLookup.proto);
81
82 if (getter == getterProto)
83 getter = primitiveGetterProto;
84 else if (getter == getterProtoAccessor)
85 getter = primitiveGetterAccessor;
86 return getter(this, engine, object);
87}
88
89ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine)
90{
91 // Otherwise we cannot trust the protoIds
92 Q_ASSERT(engine->isInitialized);
93
94 Object *o = engine->globalObject;
95 PropertyKey name = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
96 protoLookup.protoId = o->internalClass()->protoId;
97 resolveProtoGetter(name, proto: o->d());
98
99 if (getter == getterProto)
100 globalGetter = globalGetterProto;
101 else if (getter == getterProtoAccessor)
102 globalGetter = globalGetterProtoAccessor;
103 else {
104 globalGetter = globalGetterGeneric;
105 Scope scope(engine);
106 ScopedString n(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
107 return engine->throwReferenceError(value: n);
108 }
109 return globalGetter(this, engine);
110}
111
112ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object)
113{
114 if (const Object *o = object.as<Object>())
115 return l->resolveGetter(engine, object: o);
116 return l->resolvePrimitiveGetter(engine, object);
117}
118
119static inline void setupObjectLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second)
120{
121 Heap::InternalClass *ic1 = first.objectLookup.ic;
122 const uint offset1 = first.objectLookup.offset;
123 Heap::InternalClass *ic2 = second.objectLookup.ic;
124 const uint offset2 = second.objectLookup.offset;
125
126 l->objectLookupTwoClasses.ic = ic1;
127 l->objectLookupTwoClasses.ic2 = ic2;
128 l->objectLookupTwoClasses.offset = offset1;
129 l->objectLookupTwoClasses.offset2 = offset2;
130}
131
132static inline void setupProtoLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second)
133{
134 const quintptr protoId1 = first.protoLookup.protoId;
135 const Value *data1 = first.protoLookup.data;
136 const quintptr protoId2 = second.protoLookup.protoId;
137 const Value *data2 = second.protoLookup.data;
138
139 l->protoLookupTwoClasses.protoId = protoId1;
140 l->protoLookupTwoClasses.protoId2 = protoId2;
141 l->protoLookupTwoClasses.data = data1;
142 l->protoLookupTwoClasses.data2 = data2;
143}
144
145ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object)
146{
147 if (const Object *o = object.as<Object>()) {
148
149 // Do the resolution on a second lookup, then merge.
150 Lookup second;
151 memset(s: &second, c: 0, n: sizeof(Lookup));
152 second.nameIndex = l->nameIndex;
153 second.forCall = l->forCall;
154 second.getter = getterGeneric;
155 const ReturnedValue result = second.resolveGetter(engine, object: o);
156
157 if (l->getter == getter0Inline
158 && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
159 setupObjectLookupTwoClasses(l, first: *l, second);
160 l->getter = (second.getter == getter0Inline)
161 ? getter0Inlinegetter0Inline
162 : getter0Inlinegetter0MemberData;
163 return result;
164 }
165
166 if (l->getter == getter0MemberData
167 && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
168 setupObjectLookupTwoClasses(l, first: second, second: *l);
169 l->getter = (second.getter == getter0Inline)
170 ? getter0Inlinegetter0MemberData
171 : getter0MemberDatagetter0MemberData;
172 return result;
173 }
174
175
176 if (l->getter == getterProto && second.getter == getterProto) {
177 setupProtoLookupTwoClasses(l, first: *l, second);
178 l->getter = getterProtoTwoClasses;
179 return result;
180 }
181
182 if (l->getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
183 setupProtoLookupTwoClasses(l, first: *l, second);
184 l->getter = getterProtoAccessorTwoClasses;
185 return result;
186 }
187
188 // If any of the above options were true, the propertyCache was inactive.
189 second.releasePropertyCache();
190 }
191
192 l->getter = getterFallback;
193 return getterFallback(l, engine, object);
194}
195
196ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object)
197{
198 QV4::Scope scope(engine);
199 QV4::ScopedObject o(scope, object.toObject(e: scope.engine));
200 if (!o)
201 return Encode::undefined();
202 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
203 return o->get(name);
204}
205
206ReturnedValue Lookup::getterFallbackAsVariant(
207 Lookup *l, ExecutionEngine *engine, const Value &object)
208{
209 if (&Lookup::getterFallback == &Lookup::getterFallbackAsVariant) {
210 // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
211 // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
212 // decides how to retrieve the property.
213 qFatal(msg: "Your C++ compiler is broken.");
214 }
215
216 // This getter just marks the presence of a fallback lookup with variant conversion.
217 // It only does anything with it when running AOT-compiled code.
218 return getterFallback(l, engine, object);
219}
220
221ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
222{
223 // we can safely cast to a QV4::Object here. If object is actually a string,
224 // the internal class won't match
225 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
226 if (o) {
227 if (l->objectLookup.ic == o->internalClass)
228 return o->memberData->values.data()[l->objectLookup.offset].asReturnedValue();
229 }
230 return getterTwoClasses(l, engine, object);
231}
232
233ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object)
234{
235 // we can safely cast to a QV4::Object here. If object is actually a string,
236 // the internal class won't match
237 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
238 if (o) {
239 if (l->objectLookup.ic == o->internalClass)
240 return o->inlinePropertyDataWithOffset(indexWithOffset: l->objectLookup.offset)->asReturnedValue();
241 }
242 return getterTwoClasses(l, engine, object);
243}
244
245ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
246{
247 // Otherwise we cannot trust the protoIds
248 Q_ASSERT(engine->isInitialized);
249
250 // we can safely cast to a QV4::Object here. If object is actually a string,
251 // the internal class won't match
252 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
253 if (o) {
254 if (l->protoLookup.protoId == o->internalClass->protoId)
255 return l->protoLookup.data->asReturnedValue();
256 }
257 return getterTwoClasses(l, engine, object);
258}
259
260ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object)
261{
262 // we can safely cast to a QV4::Object here. If object is actually a string,
263 // the internal class won't match
264 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
265 if (o) {
266 if (l->objectLookupTwoClasses.ic == o->internalClass)
267 return o->inlinePropertyDataWithOffset(indexWithOffset: l->objectLookupTwoClasses.offset)->asReturnedValue();
268 if (l->objectLookupTwoClasses.ic2 == o->internalClass)
269 return o->inlinePropertyDataWithOffset(indexWithOffset: l->objectLookupTwoClasses.offset2)->asReturnedValue();
270 }
271 l->getter = getterFallback;
272 return getterFallback(l, engine, object);
273}
274
275ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
276{
277 // we can safely cast to a QV4::Object here. If object is actually a string,
278 // the internal class won't match
279 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
280 if (o) {
281 if (l->objectLookupTwoClasses.ic == o->internalClass)
282 return o->inlinePropertyDataWithOffset(indexWithOffset: l->objectLookupTwoClasses.offset)->asReturnedValue();
283 if (l->objectLookupTwoClasses.ic2 == o->internalClass)
284 return o->memberData->values.data()[l->objectLookupTwoClasses.offset2].asReturnedValue();
285 }
286 l->getter = getterFallback;
287 return getterFallback(l, engine, object);
288}
289
290ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
291{
292 // we can safely cast to a QV4::Object here. If object is actually a string,
293 // the internal class won't match
294 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
295 if (o) {
296 if (l->objectLookupTwoClasses.ic == o->internalClass)
297 return o->memberData->values.data()[l->objectLookupTwoClasses.offset].asReturnedValue();
298 if (l->objectLookupTwoClasses.ic2 == o->internalClass)
299 return o->memberData->values.data()[l->objectLookupTwoClasses.offset2].asReturnedValue();
300 }
301 l->getter = getterFallback;
302 return getterFallback(l, engine, object);
303}
304
305ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object)
306{
307 // Otherwise we cannot trust the protoIds
308 Q_ASSERT(engine->isInitialized);
309
310 // we can safely cast to a QV4::Object here. If object is actually a string,
311 // the internal class won't match
312 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
313 if (o) {
314 if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
315 return l->protoLookupTwoClasses.data->asReturnedValue();
316 if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
317 return l->protoLookupTwoClasses.data2->asReturnedValue();
318 return getterFallback(l, engine, object);
319 }
320 l->getter = getterFallback;
321 return getterFallback(l, engine, object);
322}
323
324ReturnedValue Lookup::getterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
325{
326 // we can safely cast to a QV4::Object here. If object is actually a string,
327 // the internal class won't match
328 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
329 if (o) {
330 if (l->objectLookup.ic == o->internalClass) {
331 const Value *getter = o->propertyData(index: l->objectLookup.offset);
332 if (!getter->isFunctionObject()) // ### catch at resolve time
333 return Encode::undefined();
334
335 return checkedResult(v4: engine, result: static_cast<const FunctionObject *>(getter)->call(
336 thisObject: &object, argv: nullptr, argc: 0));
337 }
338 }
339 l->getter = getterFallback;
340 return getterFallback(l, engine, object);
341}
342
343ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
344{
345 // Otherwise we cannot trust the protoIds
346 Q_ASSERT(engine->isInitialized);
347
348 // we can safely cast to a QV4::Object here. If object is actually a string,
349 // the internal class won't match
350 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
351 if (o && l->protoLookup.protoId == o->internalClass->protoId) {
352 const Value *getter = l->protoLookup.data;
353 if (!getter->isFunctionObject()) // ### catch at resolve time
354 return Encode::undefined();
355
356 return checkedResult(v4: engine, result: static_cast<const FunctionObject *>(getter)->call(
357 thisObject: &object, argv: nullptr, argc: 0));
358 }
359 return getterTwoClasses(l, engine, object);
360}
361
362ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object)
363{
364 // Otherwise we cannot trust the protoIds
365 Q_ASSERT(engine->isInitialized);
366
367 // we can safely cast to a QV4::Object here. If object is actually a string,
368 // the internal class won't match
369 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
370 if (o) {
371 const Value *getter = nullptr;
372 if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
373 getter = l->protoLookupTwoClasses.data;
374 else if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
375 getter = l->protoLookupTwoClasses.data2;
376 if (getter) {
377 if (!getter->isFunctionObject()) // ### catch at resolve time
378 return Encode::undefined();
379
380 return checkedResult(v4: engine, result: static_cast<const FunctionObject *>(getter)->call(
381 thisObject: &object, argv: nullptr, argc: 0));
382 }
383 }
384 l->getter = getterFallback;
385 return getterFallback(l, engine, object);
386}
387
388ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object)
389{
390 Object *o = object.objectValue();
391 if (o) {
392 Heap::Object *ho = o->d();
393 if (ho->arrayData && ho->arrayData->type == Heap::ArrayData::Simple) {
394 Heap::SimpleArrayData *s = ho->arrayData.cast<Heap::SimpleArrayData>();
395 if (l->indexedLookup.index < s->values.size)
396 if (!s->data(index: l->indexedLookup.index).isEmpty())
397 return s->data(index: l->indexedLookup.index).asReturnedValue();
398 }
399 return o->get(idx: l->indexedLookup.index);
400 }
401 l->getter = getterFallback;
402 return getterFallback(l, engine, object);
403}
404
405ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, const Value &object)
406{
407 const auto revertLookup = [lookup, engine, &object]() {
408 lookup->qobjectLookup.propertyCache->release();
409 lookup->qobjectLookup.propertyCache = nullptr;
410 lookup->getter = Lookup::getterGeneric;
411 return Lookup::getterGeneric(l: lookup, engine, object);
412 };
413
414 const QObjectWrapper::Flags flags = lookup->forCall
415 ? QObjectWrapper::AllowOverride
416 : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods);
417
418 return QObjectWrapper::lookupPropertyGetterImpl(lookup, engine, object, flags, revertLookup);
419}
420
421ReturnedValue Lookup::getterQObjectAsVariant(
422 Lookup *lookup, ExecutionEngine *engine, const Value &object)
423{
424 if (&Lookup::getterQObject == &Lookup::getterQObjectAsVariant) {
425 // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
426 // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
427 // decides how to retrieve the property.
428 qFatal(msg: "Your C++ compiler is broken.");
429 }
430
431 // This getter marks the presence of a qobjectlookup with variant conversion.
432 // It only does anything with it when running AOT-compiled code.
433 return getterQObject(lookup, engine, object);
434}
435
436ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object)
437{
438 const auto revertLookup = [lookup, engine, &object]() {
439 lookup->qobjectMethodLookup.propertyCache->release();
440 lookup->qobjectMethodLookup.propertyCache = nullptr;
441 lookup->getter = Lookup::getterGeneric;
442 return Lookup::getterGeneric(l: lookup, engine, object);
443 };
444
445 const QObjectWrapper::Flags flags = lookup->forCall
446 ? QObjectWrapper::AllowOverride
447 : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods);
448
449 return QObjectWrapper::lookupMethodGetterImpl(lookup, engine, object, flags, revertLookup);
450}
451
452ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
453{
454 // Otherwise we cannot trust the protoIds
455 Q_ASSERT(engine->isInitialized);
456
457 if (object.type() == l->primitiveLookup.type && !object.isObject()) {
458 Heap::Object *o = l->primitiveLookup.proto;
459 if (l->primitiveLookup.protoId == o->internalClass->protoId)
460 return l->primitiveLookup.data->asReturnedValue();
461 }
462 l->getter = getterGeneric;
463 return getterGeneric(l, engine, object);
464}
465
466ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
467{
468 // Otherwise we cannot trust the protoIds
469 Q_ASSERT(engine->isInitialized);
470
471 if (object.type() == l->primitiveLookup.type && !object.isObject()) {
472 Heap::Object *o = l->primitiveLookup.proto;
473 if (l->primitiveLookup.protoId == o->internalClass->protoId) {
474 const Value *getter = l->primitiveLookup.data;
475 if (!getter->isFunctionObject()) // ### catch at resolve time
476 return Encode::undefined();
477
478 return checkedResult(v4: engine, result: static_cast<const FunctionObject *>(getter)->call(
479 thisObject: &object, argv: nullptr, argc: 0));
480 }
481 }
482 l->getter = getterGeneric;
483 return getterGeneric(l, engine, object);
484}
485
486ReturnedValue Lookup::stringLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object)
487{
488 if (const String *s = object.as<String>())
489 return Encode(s->d()->length());
490
491 l->getter = getterGeneric;
492 return getterGeneric(l, engine, object);
493}
494
495ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
496{
497 return l->resolveGlobalGetter(engine);
498}
499
500ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine)
501{
502 // Otherwise we cannot trust the protoIds
503 Q_ASSERT(engine->isInitialized);
504
505 Heap::Object *o = engine->globalObject->d();
506 if (l->protoLookup.protoId == o->internalClass->protoId)
507 return l->protoLookup.data->asReturnedValue();
508 l->globalGetter = globalGetterGeneric;
509 return globalGetterGeneric(l, engine);
510}
511
512ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine)
513{
514 // Otherwise we cannot trust the protoIds
515 Q_ASSERT(engine->isInitialized);
516
517 Heap::Object *o = engine->globalObject->d();
518 if (l->protoLookup.protoId == o->internalClass->protoId) {
519 const Value *getter = l->protoLookup.data;
520 if (!getter->isFunctionObject()) // ### catch at resolve time
521 return Encode::undefined();
522
523 return checkedResult(v4: engine, result: static_cast<const FunctionObject *>(getter)->call(
524 thisObject: engine->globalObject, argv: nullptr, argc: 0));
525 }
526 l->globalGetter = globalGetterGeneric;
527 return globalGetterGeneric(l, engine);
528}
529
530bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
531{
532 return object->resolveLookupSetter(engine, lookup: this, value);
533}
534
535bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
536{
537 if (object.isObject())
538 return l->resolveSetter(engine, object: static_cast<Object *>(&object), value);
539
540 if (engine->currentStackFrame->v4Function->isStrict())
541 return false;
542
543 Scope scope(engine);
544 ScopedObject o(scope, RuntimeHelpers::convertToObject(engine: scope.engine, value: object));
545 if (!o) // type error
546 return false;
547 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
548 return o->put(name, v: value);
549}
550
551bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
552{
553 // A precondition of this method is that l->objectLookup is the active variant of the union.
554 Q_ASSERT(l->setter == setter0MemberData || l->setter == setter0Inline);
555
556 if (object.isObject()) {
557
558 // As l->objectLookup is active, we can stash some members here, before resolving.
559 Heap::InternalClass *ic = l->objectLookup.ic;
560 const uint index = l->objectLookup.index;
561
562 if (!l->resolveSetter(engine, object: static_cast<Object *>(&object), value)) {
563 l->setter = setterFallback;
564 return false;
565 }
566
567 if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) {
568 l->objectLookupTwoClasses.ic = ic;
569 l->objectLookupTwoClasses.ic2 = ic;
570 l->objectLookupTwoClasses.offset = index;
571 l->objectLookupTwoClasses.offset2 = index;
572 l->setter = setter0setter0;
573 return true;
574 }
575
576 l->releasePropertyCache();
577 }
578
579 l->setter = setterFallback;
580 return setterFallback(l, engine, object, value);
581}
582
583bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
584{
585 QV4::Scope scope(engine);
586 QV4::ScopedObject o(scope, object.toObject(e: scope.engine));
587 if (!o)
588 return false;
589
590 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
591 return o->put(name, v: value);
592}
593
594bool Lookup::setterFallbackAsVariant(
595 Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
596{
597 if (&Lookup::setterFallback == &Lookup::setterFallbackAsVariant) {
598 // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
599 // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
600 // decides how to retrieve the property.
601 qFatal(msg: "Your C++ compiler is broken.");
602 }
603
604 // This setter just marks the presence of a fallback lookup with QVariant conversion.
605 // It only does anything with it when running AOT-compiled code.
606 return setterFallback(l, engine, object, value);
607}
608
609bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
610{
611 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
612 if (o && o->internalClass == l->objectLookup.ic) {
613 o->memberData->values.set(e: engine, index: l->objectLookup.offset, v: value);
614 return true;
615 }
616
617 return setterTwoClasses(l, engine, object, value);
618}
619
620bool Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
621{
622 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
623 if (o && o->internalClass == l->objectLookup.ic) {
624 o->setInlinePropertyWithOffset(e: engine, indexWithOffset: l->objectLookup.offset, v: value);
625 return true;
626 }
627
628 return setterTwoClasses(l, engine, object, value);
629}
630
631bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
632{
633 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
634 if (o) {
635 if (o->internalClass == l->objectLookupTwoClasses.ic) {
636 o->setProperty(e: engine, index: l->objectLookupTwoClasses.offset, v: value);
637 return true;
638 }
639 if (o->internalClass == l->objectLookupTwoClasses.ic2) {
640 o->setProperty(e: engine, index: l->objectLookupTwoClasses.offset2, v: value);
641 return true;
642 }
643 }
644
645 l->setter = setterFallback;
646 return setterFallback(l, engine, object, value);
647}
648
649bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
650{
651 // Otherwise we cannot trust the protoIds
652 Q_ASSERT(engine->isInitialized);
653
654 Object *o = static_cast<Object *>(object.managed());
655 if (o && o->internalClass()->protoId == l->insertionLookup.protoId) {
656 o->setInternalClass(l->insertionLookup.newClass);
657 o->d()->setProperty(e: engine, index: l->insertionLookup.offset, v: value);
658 return true;
659 }
660
661 l->setter = setterFallback;
662 return setterFallback(l, engine, object, value);
663}
664
665bool Lookup::setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
666{
667 // This setter just marks the presence of a qobjectlookup. It only does anything with it when
668 // running AOT-compiled code, though.
669 return setterFallback(l, engine, object, value: v);
670}
671
672bool Lookup::setterQObjectAsVariant(
673 Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
674{
675 if (&Lookup::setterQObject == &Lookup::setterQObjectAsVariant) {
676 // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
677 // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
678 // decides how to retrieve the property.
679 qFatal(msg: "Your C++ compiler is broken.");
680 }
681
682 // This setter marks the presence of a qobjectlookup with QVariant conversion.
683 // It only does anything with it when running AOT-compiled code.
684 return setterQObject(l, engine, object, v);
685}
686
687
688bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value)
689{
690 Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject());
691 bool ok;
692 uint len = value.asArrayLength(ok: &ok);
693 if (!ok) {
694 engine->throwRangeError(value);
695 return false;
696 }
697 ok = static_cast<Object &>(object).setArrayLength(len);
698 if (!ok)
699 return false;
700 return true;
701}
702
703QT_END_NAMESPACE
704

source code of qtdeclarative/src/qml/jsruntime/qv4lookup.cpp