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 QV4::ScopedValue result(
483 scope, QObjectWrapper::getMethodFallback(
484 engine, This->d(), qobj, name,
485 lookup->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods));
486
487 // In the general case we cannot rely on the method to exist or stay the same across calls.
488 // However, the AOT compiler can prove it in certain cases. For these, we store the method.
489 if (QObjectMethod *method = result->as<QV4::QObjectMethod>())
490 lookup->qobjectMethodLookup.method.set(engine, method->d());
491
492 return result->asReturnedValue();
493}
494
495ReturnedValue Lookup::getterValueType(Lookup *lookup, ExecutionEngine *engine, const Value &object)
496{
497 const auto revertLookup = [lookup, engine, &object]() {
498 lookup->qgadgetLookup.metaObject = quintptr(0);
499 lookup->call = Call::GetterGeneric;
500 return Lookup::getterGeneric(lookup, engine, object);
501 };
502
503 // we can safely cast to a QV4::Object here. If object is something else,
504 // the internal class won't match
505 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
506 if (!o || o->internalClass != lookup->qgadgetLookup.ic)
507 return revertLookup();
508
509 Heap::QQmlValueTypeWrapper *valueTypeWrapper =
510 const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o));
511 if (valueTypeWrapper->metaObject() != reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1))
512 return revertLookup();
513
514 if (valueTypeWrapper->isReference() && !valueTypeWrapper->readReference())
515 return Encode::undefined();
516
517 return QQmlValueTypeWrapper::getGadgetProperty(
518 engine, valueTypeWrapper, QMetaType(lookup->qgadgetLookup.metaType),
519 lookup->qgadgetLookup.coreIndex, lookup->qgadgetLookup.isFunction,
520 lookup->qgadgetLookup.isEnum);
521}
522
523ReturnedValue Lookup::primitiveGetterProto(Lookup *lookup, ExecutionEngine *engine, const Value &object)
524{
525 // Otherwise we cannot trust the protoIds
526 Q_ASSERT(engine->isInitialized);
527
528 if (object.type() == lookup->primitiveLookup.type && !object.isObject()) {
529 Heap::Object *o = lookup->primitiveLookup.proto;
530 if (lookup->primitiveLookup.protoId == o->internalClass->protoId)
531 return lookup->primitiveLookup.data->asReturnedValue();
532 }
533 lookup->call = Call::GetterGeneric;
534 return getterGeneric(lookup, engine, object);
535}
536
537ReturnedValue Lookup::primitiveGetterAccessor(Lookup *lookup, ExecutionEngine *engine, const Value &object)
538{
539 // Otherwise we cannot trust the protoIds
540 Q_ASSERT(engine->isInitialized);
541
542 if (object.type() == lookup->primitiveLookup.type && !object.isObject()) {
543 Heap::Object *o = lookup->primitiveLookup.proto;
544 if (lookup->primitiveLookup.protoId == o->internalClass->protoId) {
545 const Value *getter = lookup->primitiveLookup.data;
546 if (!getter->isFunctionObject()) // ### catch at resolve time
547 return Encode::undefined();
548
549 return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call(
550 &object, nullptr, 0));
551 }
552 }
553 lookup->call = Call::GetterGeneric;
554 return getterGeneric(lookup, engine, object);
555}
556
557ReturnedValue Lookup::stringLengthGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
558{
559 if (const String *s = object.as<String>())
560 return Encode(s->d()->length());
561
562 lookup->call = Call::GetterGeneric;
563 return getterGeneric(lookup, engine, object);
564}
565
566ReturnedValue Lookup::globalGetterGeneric(Lookup *lookup, ExecutionEngine *engine)
567{
568 return lookup->resolveGlobalGetter(engine);
569}
570
571ReturnedValue Lookup::globalGetterProto(Lookup *lookup, ExecutionEngine *engine)
572{
573 // Otherwise we cannot trust the protoIds
574 Q_ASSERT(engine->isInitialized);
575
576 Heap::Object *o = engine->globalObject->d();
577 if (lookup->protoLookup.protoId == o->internalClass->protoId)
578 return lookup->protoLookup.data->asReturnedValue();
579 lookup->call = Call::GlobalGetterGeneric;
580 return globalGetterGeneric(lookup, engine);
581}
582
583ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *lookup, ExecutionEngine *engine)
584{
585 // Otherwise we cannot trust the protoIds
586 Q_ASSERT(engine->isInitialized);
587
588 Heap::Object *o = engine->globalObject->d();
589 if (lookup->protoLookup.protoId == o->internalClass->protoId) {
590 const Value *getter = lookup->protoLookup.data;
591 if (!getter->isFunctionObject()) // ### catch at resolve time
592 return Encode::undefined();
593
594 return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call(
595 engine->globalObject, nullptr, 0));
596 }
597
598 lookup->call = Call::GlobalGetterGeneric;
599 return globalGetterGeneric(lookup, engine);
600}
601
602bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
603{
604 return object->resolveLookupSetter(engine, this, value);
605}
606
607bool Lookup::setterGeneric(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
608{
609 if (object.isObject())
610 return lookup->resolveSetter(engine, static_cast<Object *>(&object), value);
611
612 if (engine->currentStackFrame->v4Function->isStrict())
613 return false;
614
615 Scope scope(engine);
616 ScopedObject o(scope, RuntimeHelpers::convertToObject(scope.engine, object));
617 if (!o) // type error
618 return false;
619 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
620 return o->put(name, value);
621}
622
623bool Lookup::setterTwoClasses(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
624{
625 // A precondition of this method is that lookup->objectLookup is the active variant of the union.
626 Q_ASSERT(lookup->call == Call::Setter0MemberData || lookup->call == Call::Setter0Inline);
627
628 if (object.isObject()) {
629
630 // As lookup->objectLookup is active, we can stash some members here, before resolving.
631 Heap::InternalClass *ic = lookup->objectLookup.ic;
632 const uint index = lookup->objectLookup.index;
633
634 if (!lookup->resolveSetter(engine, static_cast<Object *>(&object), value)) {
635 lookup->call = Call::SetterQObjectPropertyFallback;
636 return false;
637 }
638
639 if (lookup->call == Call::Setter0MemberData || lookup->call == Call::Setter0Inline) {
640 auto engine = ic->engine;
641 lookup->objectLookupTwoClasses.ic.set(engine, ic);
642 lookup->objectLookupTwoClasses.ic2.set(engine, ic);
643 lookup->objectLookupTwoClasses.offset = index;
644 lookup->objectLookupTwoClasses.offset2 = index;
645 lookup->call = Call::Setter0Setter0;
646 return true;
647 }
648
649 lookup->releasePropertyCache();
650 }
651
652 lookup->call = Call::SetterQObjectPropertyFallback;
653 return setterFallback(lookup, engine, object, value);
654}
655
656bool Lookup::setterFallback(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
657{
658 QV4::Scope scope(engine);
659 QV4::ScopedObject o(scope, object.toObject(scope.engine));
660 if (!o)
661 return false;
662
663 ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
664 return o->put(name, value);
665}
666
667bool Lookup::setter0MemberData(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
668{
669 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
670 if (o && o->internalClass == lookup->objectLookup.ic) {
671 o->memberData->values.set(engine, lookup->objectLookup.offset, value);
672 return true;
673 }
674
675 return setterTwoClasses(lookup, engine, object, value);
676}
677
678bool Lookup::setter0Inline(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
679{
680 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
681 if (o && o->internalClass == lookup->objectLookup.ic) {
682 o->setInlinePropertyWithOffset(engine, lookup->objectLookup.offset, value);
683 return true;
684 }
685
686 return setterTwoClasses(lookup, engine, object, value);
687}
688
689bool Lookup::setter0setter0(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
690{
691 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
692 if (o) {
693 if (o->internalClass == lookup->objectLookupTwoClasses.ic) {
694 o->setProperty(engine, lookup->objectLookupTwoClasses.offset, value);
695 return true;
696 }
697 if (o->internalClass == lookup->objectLookupTwoClasses.ic2) {
698 o->setProperty(engine, lookup->objectLookupTwoClasses.offset2, value);
699 return true;
700 }
701 }
702
703 lookup->call = Call::SetterQObjectPropertyFallback;
704 return setterFallback(lookup, engine, object, value);
705}
706
707bool Lookup::setterInsert(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
708{
709 // Otherwise we cannot trust the protoIds
710 Q_ASSERT(engine->isInitialized);
711
712 Object *o = static_cast<Object *>(object.managed());
713 if (o && o->internalClass()->protoId == lookup->insertionLookup.protoId) {
714 o->setInternalClass(lookup->insertionLookup.newClass);
715 o->d()->setProperty(engine, lookup->insertionLookup.offset, value);
716 return true;
717 }
718
719 lookup->call = Call::SetterQObjectPropertyFallback;
720 return setterFallback(lookup, engine, object, value);
721}
722
723bool Lookup::setterQObject(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &v)
724{
725 // This setter just marks the presence of a qobjectlookup. It only does anything with it when
726 // running AOT-compiled code, though.
727 return setterFallback(lookup, engine, object, v);
728}
729
730bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value)
731{
732 Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject());
733 bool ok;
734 uint len = value.asArrayLength(&ok);
735 if (!ok) {
736 engine->throwRangeError(value);
737 return false;
738 }
739 ok = static_cast<Object &>(object).setArrayLength(len);
740 if (!ok)
741 return false;
742 return true;
743}
744
745QT_END_NAMESPACE
static void setupProtoLookupTwoClasses(Lookup *lookup, const Lookup &first, const Lookup &second)
static void setupObjectLookupTwoClasses(Lookup *lookup, const Lookup &first, const Lookup &second)