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

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