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