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 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | using namespace QV4; |
14 | |
15 | |
16 | void 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 | |
36 | ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object) |
37 | { |
38 | return object->resolveLookupGetter(engine, lookup: this); |
39 | } |
40 | |
41 | ReturnedValue 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 | |
89 | ReturnedValue 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 | |
112 | ReturnedValue 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 | |
119 | static 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 | |
132 | static 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 | |
145 | ReturnedValue 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 | |
196 | ReturnedValue 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 | |
206 | ReturnedValue 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 | |
221 | ReturnedValue 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 | |
233 | ReturnedValue 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 | |
245 | ReturnedValue 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 | |
260 | ReturnedValue 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 | |
275 | ReturnedValue 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 | |
290 | ReturnedValue 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 | |
305 | ReturnedValue 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 | |
324 | ReturnedValue 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 | |
343 | ReturnedValue 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 | |
362 | ReturnedValue 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 | |
388 | ReturnedValue 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 | |
405 | ReturnedValue 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 | |
421 | ReturnedValue 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 | |
436 | ReturnedValue 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 | |
452 | ReturnedValue 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 | |
466 | ReturnedValue 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 | |
486 | ReturnedValue 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 | |
495 | ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) |
496 | { |
497 | return l->resolveGlobalGetter(engine); |
498 | } |
499 | |
500 | ReturnedValue 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 | |
512 | ReturnedValue 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 | |
530 | bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value) |
531 | { |
532 | return object->resolveLookupSetter(engine, lookup: this, value); |
533 | } |
534 | |
535 | bool 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 | |
551 | bool 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 | |
583 | bool 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 | |
594 | bool 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 | |
609 | bool 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 | |
620 | bool 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 | |
631 | bool 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 | |
649 | bool 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 | |
665 | bool 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 | |
672 | bool 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 | |
688 | bool 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 | |
703 | QT_END_NAMESPACE |
704 | |