Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmljsshadowcheck.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
7
8using namespace Qt::StringLiterals;
9
39{
41 m_error = error;
42 m_state = initialState(function);
43 decode(m_function->code.constData(), static_cast<uint>(m_function->code.size()));
44
45 for (const auto &store : m_resettableStores)
46 checkResettable(store.accumulatorIn, store.instructionOffset);
47
48 // Re-check all base types. We may have made them var after detecting them.
49 for (const auto &base : m_baseTypes) {
50 if (checkBaseType(base) == Shadowable)
51 break;
52 }
53
54 return { std::move(m_basicBlocks), std::move(m_annotations) };
55}
56
58{
59 if (!m_state.readsRegister(Accumulator))
60 return; // enum lookup cannot be shadowed.
61
62 auto accumulatorIn = m_state.registers.find(Accumulator);
63 if (accumulatorIn != m_state.registers.end()) {
64 checkShadowing(
65 accumulatorIn.value().content, m_jsUnitGenerator->stringForIndex(nameIndex),
67 }
68}
69
71{
72 if (!m_state.readsRegister(Accumulator))
73 return; // enum lookup cannot be shadowed.
74
75 auto accumulatorIn = m_state.registers.find(Accumulator);
76 if (accumulatorIn != m_state.registers.end()) {
77 checkShadowing(
78 accumulatorIn.value().content, m_jsUnitGenerator->lookupName(index), Accumulator);
79 }
80}
81
87
88void QQmlJSShadowCheck::handleStore(int base, const QString &memberName)
89{
90 const int instructionOffset = currentInstructionOffset();
91 const QQmlJSRegisterContent &readAccumulator
92 = m_annotations[instructionOffset].readRegisters[Accumulator].content;
93 const auto baseType = m_state.registers[base].content;
94
95 // If the accumulator is already read as var, we don't have to do anything.
96 if (m_typeResolver->registerContains(readAccumulator, m_typeResolver->varType())) {
97 if (checkBaseType(baseType) == NotShadowable)
98 m_baseTypes.append(baseType);
99 return;
100 }
101
102 if (checkShadowing(baseType, memberName, base) == Shadowable)
103 return;
104
105 // If the property isn't shadowable, we have to turn the read register into
106 // var if the accumulator can hold undefined. This has to be done in a second pass
107 // because the accumulator may still turn into var due to its own shadowing.
108 const QQmlJSRegisterContent member = m_typeResolver->memberType(baseType, memberName);
109 if (member.isProperty())
110 m_resettableStores.append({m_state.accumulatorIn(), instructionOffset});
111}
112
114{
115 handleStore(base, m_jsUnitGenerator->stringForIndex(nameIndex));
116}
117
122
123void QQmlJSShadowCheck::generate_CallProperty(int nameIndex, int base, int argc, int argv)
124{
125 Q_UNUSED(argc);
126 Q_UNUSED(argv);
127 checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
128}
129
130void QQmlJSShadowCheck::generate_CallPropertyLookup(int nameIndex, int base, int argc, int argv)
131{
132 Q_UNUSED(argc);
133 Q_UNUSED(argv);
134 checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
135}
136
144
148
149QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkShadowing(
150 const QQmlJSRegisterContent &baseType, const QString &memberName, int baseRegister)
151{
152 if (checkBaseType(baseType) == Shadowable)
153 return Shadowable;
154 else
155 m_baseTypes.append(baseType);
156
157 if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference)
158 return NotShadowable;
159
160 switch (baseType.variant()) {
167 const QQmlJSRegisterContent member = m_typeResolver->memberType(baseType, memberName);
168
169 // You can have something like parent.QtQuick.Screen.pixelDensity
170 // In that case "QtQuick" cannot be resolved as member type and we would later have to look
171 // for "QtQuick.Screen" instead. However, you can only do that with attached properties and
172 // those are not shadowable.
173 if (!member.isValid()) {
174 Q_ASSERT(m_typeResolver->isPrefix(memberName));
175 return NotShadowable;
176 }
177
178 if (member.isProperty()) {
179 if (member.property().isFinal())
180 return NotShadowable; // final properties can't be shadowed
181 } else if (!member.isMethod()) {
182 return NotShadowable; // Only properties and methods can be shadowed
183 }
184
185 m_logger->log(
186 u"Member %1 of %2 can be shadowed"_s.arg(memberName, baseType.descriptiveName()),
188
189 // Make it "var". We don't know what it is.
191 const QQmlJSRegisterContent varContent = m_typeResolver->globalType(varType);
192 InstructionAnnotation &currentAnnotation = m_annotations[currentInstructionOffset()];
193
194 if (currentAnnotation.changedRegisterIndex != InvalidRegister) {
196 currentAnnotation.changedRegister.storedType(), varType);
198 m_typeResolver->containedType(currentAnnotation.changedRegister), varType);
199 m_adjustedTypes.insert(currentAnnotation.changedRegister);
200 }
201
202 for (auto it = currentAnnotation.readRegisters.begin(),
203 end = currentAnnotation.readRegisters.end();
204 it != end; ++it) {
205 if (it.key() != baseRegister)
206 it->second.content = m_typeResolver->convert(it->second.content, varContent);
207 }
208 return Shadowable;
209 }
210 default:
211 // In particular ObjectById is fine as that cannot change into something else
212 // Singleton should also be fine, unless the factory function creates an object
213 // with different property types than the declared class.
214 return NotShadowable;
215 }
216}
217
218void QQmlJSShadowCheck::checkResettable(
219 const QQmlJSRegisterContent &accumulatorIn, int instructionOffset)
220{
222
223 // The stored type is not necessarily updated by the shadow check, but it
224 // will be in the basic blocks pass. For the purpose of adjusting newly
225 // shadowable types we can ignore it. We only want to know if any of the
226 // contents can hold undefined.
227 if (!m_typeResolver->canHoldUndefined(accumulatorIn.storedIn(varType)))
228 return;
229
230 const QQmlJSRegisterContent varContent = m_typeResolver->globalType(varType);
231
232 QQmlJSRegisterContent &readAccumulator
233 = m_annotations[instructionOffset].readRegisters[Accumulator].content;
234 readAccumulator = m_typeResolver->convert(readAccumulator, varContent);
235}
236
237QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkBaseType(
238 const QQmlJSRegisterContent &baseType)
239{
240 if (!m_adjustedTypes.contains(baseType))
241 return NotShadowable;
242 setError(u"Cannot use shadowable base type for further lookups: %1"_s.arg(baseType.descriptiveName()));
243 return Shadowable;
244}
245
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
iterator end()
Definition qflatmap_p.h:773
iterator find(const Key &key)
Definition qflatmap_p.h:816
void append(parameter_type t)
Definition qlist.h:458
const QV4::Compiler::JSUnitGenerator * m_jsUnitGenerator
const Function * m_function
const QQmlJSTypeResolver * m_typeResolver
QQmlJS::DiagnosticMessage * m_error
InstructionAnnotations m_annotations
State initialState(const Function *function)
State nextStateFromAnnotations(const State &oldState, const InstructionAnnotations &annotations)
QQmlJS::SourceLocation currentSourceLocation() const
void setError(const QString &message, int instructionOffset)
void log(const QString &message, QQmlJS::LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, bool showContext=true, bool showFileName=true, const std::optional< QQmlJSFixSuggestion > &suggestion={}, const QString overrideFileName=QString())
QQmlJSRegisterContent storedIn(const QQmlJSScope::ConstPtr &newStoredType) const
QQmlJSMetaProperty property() const
ContentVariant variant() const
QQmlJSScope::ConstPtr storedType() const
AccessSemantics accessSemantics() const
void endInstruction(QV4::Moth::Instr::Type) override
void generate_CallPropertyLookup(int nameIndex, int base, int argc, int argv) override
void generate_GetOptionalLookup(int index, int offset) override
QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type) override
void generate_SetLookup(int index, int base) override
void generate_LoadProperty(int nameIndex) override
void generate_CallProperty(int nameIndex, int base, int argc, int argv) override
BlocksAndAnnotations run(const Function *function, QQmlJS::DiagnosticMessage *error)
void generate_GetLookup(int index) override
void generate_StoreProperty(int nameIndex, int base) override
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const
bool registerContains(const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
bool isPrefix(const QString &name) const
QQmlJSRegisterContent globalType(const QQmlJSScope::ConstPtr &type) const
QQmlJSRegisterContent convert(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const
bool canHoldUndefined(const QQmlJSRegisterContent &content) const
void adjustOriginalType(const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name, int lookupIndex=QQmlJSRegisterContent::InvalidLookupIndex) const
QQmlJSScope::ConstPtr varType() const
iterator begin()
Definition qset.h:136
bool contains(const T &value) const
Definition qset.h:71
iterator insert(const T &value)
Definition qset.h:155
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QSet< QString >::iterator it
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
GLuint index
[2]
GLuint GLuint end
GLenum GLuint GLintptr offset
const QQmlSA::LoggerWarningId qmlCompiler
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:34
static const uint base
Definition qurlidna.cpp:20
#define decode(x)
const QQmlJSRegisterContent & accumulatorIn() const
The accumulatorIn is the input register of the current instruction.
bool readsRegister(int registerIndex) const
QString lookupName(int index) const
QString stringForIndex(int index) const