9using namespace Qt::StringLiterals;
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
38QQmlJSCompilePass::BlocksAndAnnotations QQmlJSShadowCheck::run(
const Function *function)
40 m_function = function;
41 m_state = initialState(function);
42 decode(m_function->code.constData(),
static_cast<uint>(m_function->code.size()));
44 for (
const auto &store : std::as_const(m_resettableStores))
45 checkResettable(store.accumulatorIn, store.instructionOffset);
48 for (
const auto &base : std::as_const(m_baseTypes)) {
49 if (checkBaseType(base) == Shadowable)
53 return { std::move(m_basicBlocks), std::move(m_annotations) };
56void QQmlJSShadowCheck::generate_LoadProperty(
int nameIndex)
58 if (!m_state.readsRegister(Accumulator))
61 auto accumulatorIn = m_state.registers.find(Accumulator);
62 if (accumulatorIn != m_state.registers.end()) {
64 accumulatorIn.value().content, m_jsUnitGenerator->stringForIndex(nameIndex),
69void QQmlJSShadowCheck::generate_GetLookup(
int index)
71 if (!m_state.readsRegister(Accumulator))
74 auto accumulatorIn = m_state.registers.find(Accumulator);
75 if (accumulatorIn != m_state.registers.end()) {
77 accumulatorIn.value().content, m_jsUnitGenerator->lookupName(index), Accumulator);
81void QQmlJSShadowCheck::generate_GetOptionalLookup(
int index,
int offset)
84 generate_GetLookup(index);
87void QQmlJSShadowCheck::handleStore(
int base,
const QString &memberName)
89 const int instructionOffset = currentInstructionOffset();
90 QQmlJSRegisterContent readAccumulator
91 = m_annotations[instructionOffset].readRegisters[Accumulator].content;
92 const auto baseType = m_state.registers[base].content;
95 if (readAccumulator.contains(m_typeResolver->varType())) {
96 if (checkBaseType(baseType) == NotShadowable)
97 m_baseTypes.append(baseType);
101 if (checkShadowing(baseType, memberName, base) == Shadowable)
107 const QQmlJSRegisterContent member = m_typeResolver->memberType(baseType, memberName);
108 if (member.isProperty())
109 m_resettableStores.append({m_state.accumulatorIn(), instructionOffset});
112void QQmlJSShadowCheck::generate_StoreProperty(
int nameIndex,
int base)
114 handleStore(base, m_jsUnitGenerator->stringForIndex(nameIndex));
117void QQmlJSShadowCheck::generate_SetLookup(
int index,
int base)
119 handleStore(base, m_jsUnitGenerator->lookupName(index));
122void QQmlJSShadowCheck::generate_CallProperty(
int nameIndex,
int base,
int argc,
int argv)
126 checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
129void QQmlJSShadowCheck::generate_CallPropertyLookup(
int nameIndex,
int base,
int argc,
int argv)
133 checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
136QV4::Moth::ByteCodeHandler::Verdict QQmlJSShadowCheck::startInstruction(QV4::Moth::Instr::Type)
138 m_state = nextStateFromAnnotations(m_state, m_annotations);
139 return (m_state.hasInternalSideEffects() || m_state.changedRegisterIndex() != InvalidRegister)
144void QQmlJSShadowCheck::endInstruction(QV4::Moth::Instr::Type)
148QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkShadowing(
149 QQmlJSRegisterContent baseType,
const QString &memberName,
int baseRegister)
151 if (checkBaseType(baseType) == Shadowable)
154 m_baseTypes.append(baseType);
157 if (baseType.containedType()->isJavaScriptBuiltin())
158 return NotShadowable;
160 if (!baseType.containedType()->isReferenceType())
161 return NotShadowable;
163 switch (baseType.variant()) {
164 case QQmlJSRegisterContent::Singleton:
167 if (baseType.containedType()->isComposite())
168 return NotShadowable;
174 if (baseType.containedType()->extensionType().extensionSpecifier
175 != QQmlJSScope::NotExtension) {
176 return NotShadowable;
180 case QQmlJSRegisterContent::MethodCall:
181 case QQmlJSRegisterContent::Property:
182 case QQmlJSRegisterContent::TypeByName:
183 case QQmlJSRegisterContent::Cast:
184 case QQmlJSRegisterContent::Unknown: {
185 const QQmlJSRegisterContent member = m_typeResolver->memberType(baseType, memberName);
191 if (!member.isValid()) {
192 Q_ASSERT(m_typeResolver->isPrefix(memberName));
193 return NotShadowable;
196 if (member.isProperty()) {
197 if (member.property().isFinal())
198 return NotShadowable;
199 }
else if (!member.isMethod()) {
200 return NotShadowable;
204 u"Member %1 of %2 can be shadowed"_s.arg(memberName, baseType.descriptiveName()),
205 qmlCompiler, currentSourceLocation());
208 const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
209 InstructionAnnotation ¤tAnnotation = m_annotations[currentInstructionOffset()];
210 currentAnnotation.isShadowable =
true;
212 if (currentAnnotation.changedRegisterIndex != InvalidRegister) {
213 m_typeResolver->adjustOriginalType(currentAnnotation.changedRegister, varType);
214 m_adjustedTypes.insert(currentAnnotation.changedRegister);
217 for (
auto it = currentAnnotation.readRegisters.begin(),
218 end = currentAnnotation.readRegisters.end();
220 if (it.key() != baseRegister)
221 it->second.content = m_typeResolver->convert(it->second.content, varType);
228 return NotShadowable;
232void QQmlJSShadowCheck::checkResettable(
233 QQmlJSRegisterContent accumulatorIn,
int instructionOffset)
235 if (!m_typeResolver->canHoldUndefined(accumulatorIn))
238 QQmlJSRegisterContent &readAccumulator
239 = m_annotations[instructionOffset].readRegisters[Accumulator].content;
240 readAccumulator = m_typeResolver->convert(readAccumulator, m_typeResolver->varType());
243QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkBaseType(
244 QQmlJSRegisterContent baseType)
246 if (!m_adjustedTypes.contains(baseType))
247 return NotShadowable;
248 addError(u"Cannot use shadowable base type for further lookups: %1"_s.arg(baseType.descriptiveName()));