// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4CODEGEN_P_H #define QV4CODEGEN_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QV4 { namespace Moth { struct Instruction; } namespace CompiledData { struct CompilationUnit; } namespace Compiler { struct ControlFlow; struct ControlFlowCatch; struct ControlFlowFinally; class Q_QML_COMPILER_EXPORT CodegenWarningInterface { public: virtual void reportVarUsedBeforeDeclaration(const QString &name, const QString &fileName, QQmlJS::SourceLocation declarationLocation, QQmlJS::SourceLocation accessLocation); virtual ~CodegenWarningInterface() = default; }; inline CodegenWarningInterface *defaultCodegenWarningInterface() { static CodegenWarningInterface iface; return &iface; } class Q_QML_COMPILER_EXPORT Codegen: protected QQmlJS::AST::Visitor { protected: using BytecodeGenerator = QV4::Moth::BytecodeGenerator; using Instruction = QV4::Moth::Instruction; public: Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict, CodegenWarningInterface *iface = defaultCodegenWarningInterface(), bool storeSourceLocations = false); void generateFromProgram(const QString &fileName, const QString &finalUrl, const QString &sourceCode, QQmlJS::AST::Program *ast, Module *module, ContextType contextType = ContextType::Global); void generateFromModule(const QString &fileName, const QString &finalUrl, const QString &sourceCode, QQmlJS::AST::ESModule *ast, Module *module); public: class VolatileMemoryLocationScanner; class VolatileMemoryLocations { friend VolatileMemoryLocationScanner; bool allVolatile = false; QList specificLocations; public: bool isVolatile(QStringView name) { if (allVolatile) return true; return specificLocations.contains(name); } void add(QStringView name) { if (!allVolatile) specificLocations.append(name); } void setAllVolatile() { allVolatile = true; } }; class RValue { Codegen *codegen; enum Type { Invalid, Accumulator, StackSlot, Const } type; union { Moth::StackSlot theStackSlot; QV4::ReturnedValue constant; }; public: static RValue fromStackSlot(Codegen *codegen, Moth::StackSlot stackSlot) { RValue r; r.codegen = codegen; r.type = StackSlot; r.theStackSlot = stackSlot; return r; } static RValue fromAccumulator(Codegen *codegen) { RValue r; r.codegen = codegen; r.type = Accumulator; return r; } static RValue fromConst(Codegen *codegen, QV4::ReturnedValue value) { RValue r; r.codegen = codegen; r.type = Const; r.constant = value; return r; } bool operator==(const RValue &other) const; bool isValid() const { return type != Invalid; } bool isAccumulator() const { return type == Accumulator; } bool isStackSlot() const { return type == StackSlot; } bool isConst() const { return type == Const; } Moth::StackSlot stackSlot() const { Q_ASSERT(isStackSlot()); return theStackSlot; } QV4::ReturnedValue constantValue() const { Q_ASSERT(isConst()); return constant; } Q_REQUIRED_RESULT RValue storeOnStack() const; void loadInAccumulator() const; }; struct Reference { enum Type { Invalid, Accumulator, Super, SuperProperty, StackSlot, ScopedLocal, Name, Member, Subscript, Import, LastLValue = Import, Const } type = Invalid; bool isLValue() const { return !isReadonly && type > Accumulator; } Reference(Codegen *cg, Type t = Invalid) : Reference() { type = t; codegen = cg; } Reference(const QString &name = QString()) : constant(0), name(name), isArgOrEval(false), isReadonly(false), isReferenceToConst(false), requiresTDZCheck(false), subscriptRequiresTDZCheck(false), stackSlotIsLocalOrArgument(false), isVolatile(false), global(false), qmlGlobal(false), throwsReferenceError(false), subscriptLoadedForCall(false), isOptional(false), hasSavedCallBaseSlot(false) {} Reference(const Reference &) = default; Reference(Reference &&) = default; Reference &operator =(const Reference &) = default; Reference &operator =(Reference &&) = default; bool operator==(const Reference &other) const; bool operator!=(const Reference &other) const { return !(*this == other); } bool isValid() const { return type != Invalid; } bool loadTriggersSideEffect() const { switch (type) { case Name: case Member: case Subscript: case SuperProperty: return true; default: return requiresTDZCheck; } } bool isConstant() const { return type == Const; } bool isAccumulator() const { return type == Accumulator; } bool isSuper() const { return type == Super; } bool isSuperProperty() const { return type == SuperProperty; } bool isStackSlot() const { return type == StackSlot; } bool isRegister() const { return isStackSlot(); } static Reference fromAccumulator(Codegen *cg) { return Reference(cg, Accumulator); } static Reference fromSuper(Codegen *cg) { return Reference(cg, Super); } static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) { Reference r(cg, StackSlot); if (tempIndex == -1) tempIndex = cg->bytecodeGenerator->newRegister(); r.theStackSlot = Moth::StackSlot::createRegister(tempIndex); r.stackSlotIsLocalOrArgument = isLocal; return r; } static Reference fromScopedLocal(Codegen *cg, int index, int scope) { Reference r(cg, ScopedLocal); r.index = index; r.scope = scope; return r; } static Reference fromImport(Codegen *cg, int index) { Reference r(cg, Import); r.index = index; return r; } static Reference fromName(Codegen *cg, const QString &name) { Reference r(cg, Name); r.name = name; return r; } static Reference fromMember(const Reference &baseRef, const QString &name, QQmlJS::SourceLocation sourceLocation = QQmlJS::SourceLocation(), bool isOptional = false, std::vector *optionalChainJumpsToPatch = nullptr) { Q_ASSERT(baseRef.isValid()); Reference r(baseRef.codegen, Member); r.propertyBase = baseRef.asRValue(); r.propertyNameIndex = r.codegen->registerString(name); r.requiresTDZCheck = baseRef.requiresTDZCheck; r.sourceLocation = sourceLocation; r.optionalChainJumpsToPatch = optionalChainJumpsToPatch; r.isOptional = isOptional; return r; } static Reference fromSuperProperty(const Reference &property) { Q_ASSERT(property.isStackSlot()); Reference r(property.codegen, SuperProperty); r.property = property.stackSlot(); r.subscriptRequiresTDZCheck = property.requiresTDZCheck; return r; } static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) { Q_ASSERT(baseRef.isStackSlot()); Reference r(baseRef.codegen, Subscript); r.elementBase = baseRef.stackSlot(); r.elementSubscript = subscript.asRValue(); r.requiresTDZCheck = baseRef.requiresTDZCheck; r.subscriptRequiresTDZCheck = subscript.requiresTDZCheck; return r; } static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) { Reference r(cg, Const); r.constant = constant; r.isReadonly = true; return r; } static Reference fromThis(Codegen *cg) { Reference r = fromStackSlot(cg, CallData::This); r.isReadonly = true; // ### Optimize this. Functions that are not derived constructors or arrow functions can't have an // empty this object r.requiresTDZCheck = true; return r; } RValue asRValue() const; Reference asLValue() const; Q_REQUIRED_RESULT static Reference storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant) { return Reference::fromConst(cg, constant).storeOnStack(); } static void storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant, int stackSlot) { Reference::fromConst(cg, constant).storeOnStack(stackSlot); } Q_REQUIRED_RESULT Reference storeOnStack() const; void storeOnStack(int tempIndex) const; Q_REQUIRED_RESULT Reference storeRetainAccumulator() const; Reference storeConsumeAccumulator() const; Q_REQUIRED_RESULT Reference baseObject() const; bool storeWipesAccumulator() const; void loadInAccumulator() const; int nameAsIndex() const { Q_ASSERT(type == Name); return codegen->registerString(name); } Moth::StackSlot stackSlot() const { if (Q_UNLIKELY(!isStackSlot())) Q_UNREACHABLE(); return theStackSlot; } void tdzCheck() const { if (isAccumulator()) tdzCheck(requiresTDZCheck, throwsReferenceError); else if (isStackSlot()) tdzCheckStackSlot(stackSlot(), requiresTDZCheck, throwsReferenceError); } union { Moth::StackSlot theStackSlot; QV4::ReturnedValue constant; struct { // Scoped arguments/Local int index; int scope; }; struct { RValue propertyBase; int propertyNameIndex; }; struct { Moth::StackSlot elementBase; union { RValue elementSubscript; Moth::StackSlot element; }; }; Moth::StackSlot property; // super property }; QString name; Codegen *codegen = nullptr; quint32 isArgOrEval:1; quint32 isReadonly:1; quint32 isReferenceToConst:1; quint32 requiresTDZCheck:1; quint32 subscriptRequiresTDZCheck:1; quint32 stackSlotIsLocalOrArgument:1; quint32 isVolatile:1; quint32 global:1; quint32 qmlGlobal:1; quint32 throwsReferenceError:1; quint32 subscriptLoadedForCall:1; quint32 isOptional: 1; quint32 hasSavedCallBaseSlot: 1; QQmlJS::SourceLocation sourceLocation = QQmlJS::SourceLocation(); std::vector *optionalChainJumpsToPatch = nullptr; int savedCallBaseSlot = -1; int savedCallPropertyNameIndex = -1; private: void storeAccumulator() const; Reference doStoreOnStack(int tempIndex) const; void tdzCheck(bool requiresCheck, bool throwsReferenceError) const; void tdzCheckStackSlot( Moth::StackSlot slot, bool requiresCheck, bool throwsReferenceError) const; }; struct RegisterScope { RegisterScope(Codegen *cg) : generator(cg->bytecodeGenerator), regCountForScope(generator->currentReg) {} ~RegisterScope() { generator->currentReg = regCountForScope; } BytecodeGenerator *generator; int regCountForScope; }; struct ObjectPropertyValue { ObjectPropertyValue() {} Reference rvalue; int getter = -1; // index in _module->functions or -1 if not set int setter = -1; uint keyAsIndex = UINT_MAX; bool hasGetter() const { return getter >= 0; } bool hasSetter() const { return setter >= 0; } }; protected: enum Format { ex, cx, nx }; class Result { Reference _result; const BytecodeGenerator::Label *_iftrue = nullptr; const BytecodeGenerator::Label *_iffalse = nullptr; Format _format = ex; Format _requested; bool _trueBlockFollowsCondition = false; public: explicit Result(const QString &name) : _result(name) , _requested(ex) {} explicit Result(const Reference &lrvalue) : _result(lrvalue) , _requested(ex) {} explicit Result(Format requested = ex) : _requested(requested) {} explicit Result(const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition) : _iftrue(iftrue) , _iffalse(iffalse) , _requested(cx) , _trueBlockFollowsCondition(trueBlockFollowsCondition) { Q_ASSERT(iftrue); Q_ASSERT(iffalse); } const BytecodeGenerator::Label *iftrue() const { Q_ASSERT(_requested == cx); return _iftrue; } const BytecodeGenerator::Label *iffalse() const { Q_ASSERT(_requested == cx); return _iffalse; } Format format() const { return _format; } bool accept(Format f) { if (_requested == f) { _format = f; return true; } return false; } bool trueBlockFollowsCondition() const { return _trueBlockFollowsCondition; } const Reference &result() const { return _result; } void setResult(const Reference &result) { _result = result; } void setResult(Reference &&result) { _result = std::move(result); } void clearResultName() { _result.name.clear(); } }; void enterContext(QQmlJS::AST::Node *node); int leaveContext(); public: Context *enterBlock(QQmlJS::AST::Node *node); int leaveBlock() { return leaveContext(); } protected: void leaveLoop(); enum UnaryOperation { UPlus, UMinus, PreIncrement, PreDecrement, PostIncrement, PostDecrement, Not, Compl }; Reference unop(UnaryOperation op, const Reference &expr); void addCJump(); public: int registerString(const QString &name) { return jsUnitGenerator->registerString(name); } int registerConstant(QV4::ReturnedValue v) { return jsUnitGenerator->registerConstant(v); } int registerGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) { return jsUnitGenerator->registerGetterLookup(nameIndex, mode); } int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); } int registerGlobalGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex, mode); } int registerQmlContextPropertyGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex, mode); } // Returns index in _module->functions virtual int defineFunction(const QString &name, QQmlJS::AST::Node *ast, QQmlJS::AST::FormalParameterList *formals, QQmlJS::AST::StatementList *body); protected: void statement(QQmlJS::AST::Statement *ast); void statement(QQmlJS::AST::ExpressionNode *ast); void condition(QQmlJS::AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition); inline Reference expression(QQmlJS::AST::ExpressionNode *ast, const QString &name = QString()) { if (!ast || hasError()) return Reference(); pushExpr(name); ast->accept(this); return popResult(); } inline void accept(QQmlJS::AST::Node *node) { if (!hasError() && node) node->accept(this); } void program(QQmlJS::AST::Program *ast); void statementList(QQmlJS::AST::StatementList *ast); void variableDeclaration(QQmlJS::AST::PatternElement *ast); void variableDeclarationList(QQmlJS::AST::VariableDeclarationList *ast); Reference targetForPatternElement(QQmlJS::AST::PatternElement *p); void initializeAndDestructureBindingElement(QQmlJS::AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false); void destructurePropertyList(const Reference &object, QQmlJS::AST::PatternPropertyList *bindingList, bool isDefinition = false); void destructureElementList(const Reference &array, QQmlJS::AST::PatternElementList *bindingList, bool isDefinition = false); void destructurePattern(QQmlJS::AST::Pattern *p, const Reference &rhs); Reference referenceForPropertyName(const Codegen::Reference &object, QQmlJS::AST::PropertyName *name); void emitReturn(const Reference &expr); // nodes bool visit(QQmlJS::AST::ArgumentList *ast) override; bool visit(QQmlJS::AST::CaseBlock *ast) override; bool visit(QQmlJS::AST::CaseClause *ast) override; bool visit(QQmlJS::AST::CaseClauses *ast) override; bool visit(QQmlJS::AST::Catch *ast) override; bool visit(QQmlJS::AST::DefaultClause *ast) override; bool visit(QQmlJS::AST::Elision *ast) override; bool visit(QQmlJS::AST::Finally *ast) override; bool visit(QQmlJS::AST::FormalParameterList *ast) override; bool visit(QQmlJS::AST::Program *ast) override; bool visit(QQmlJS::AST::StatementList *ast) override; bool visit(QQmlJS::AST::UiArrayMemberList *ast) override; bool visit(QQmlJS::AST::UiImport *ast) override; bool visit(QQmlJS::AST::UiHeaderItemList *ast) override; bool visit(QQmlJS::AST::UiPragmaValueList *ast) override; bool visit(QQmlJS::AST::UiPragma *ast) override; bool visit(QQmlJS::AST::UiObjectInitializer *ast) override; bool visit(QQmlJS::AST::UiObjectMemberList *ast) override; bool visit(QQmlJS::AST::UiParameterList *ast) override; bool visit(QQmlJS::AST::UiProgram *ast) override; bool visit(QQmlJS::AST::UiQualifiedId *ast) override; bool visit(QQmlJS::AST::VariableDeclarationList *ast) override; bool visit(QQmlJS::AST::PatternElement *ast) override; bool visit(QQmlJS::AST::PatternElementList *ast) override; bool visit(QQmlJS::AST::PatternProperty *ast) override; bool visit(QQmlJS::AST::PatternPropertyList *ast) override; bool visit(QQmlJS::AST::ExportDeclaration *ast) override; bool visit(QQmlJS::AST::TypeAnnotation *ast) override; // expressions bool visit(QQmlJS::AST::Expression *ast) override; bool visit(QQmlJS::AST::ArrayPattern *ast) override; bool visit(QQmlJS::AST::ArrayMemberExpression *ast) override; bool visit(QQmlJS::AST::BinaryExpression *ast) override; bool visit(QQmlJS::AST::CallExpression *ast) override; void endVisit(QQmlJS::AST::CallExpression *ast) override; bool visit(QQmlJS::AST::ConditionalExpression *ast) override; bool visit(QQmlJS::AST::DeleteExpression *ast) override; void endVisit(QQmlJS::AST::DeleteExpression *ast) override; bool visit(QQmlJS::AST::FalseLiteral *ast) override; bool visit(QQmlJS::AST::SuperLiteral *ast) override; bool visit(QQmlJS::AST::FieldMemberExpression *ast) override; void endVisit(QQmlJS::AST::FieldMemberExpression *ast) override; bool visit(QQmlJS::AST::TaggedTemplate *ast) override; bool visit(QQmlJS::AST::FunctionExpression *ast) override; bool visit(QQmlJS::AST::IdentifierExpression *ast) override; bool visit(QQmlJS::AST::NestedExpression *ast) override; bool visit(QQmlJS::AST::NewExpression *ast) override; bool visit(QQmlJS::AST::NewMemberExpression *ast) override; bool visit(QQmlJS::AST::NotExpression *ast) override; bool visit(QQmlJS::AST::NullExpression *ast) override; bool visit(QQmlJS::AST::NumericLiteral *ast) override; bool visit(QQmlJS::AST::ObjectPattern *ast) override; bool visit(QQmlJS::AST::PostDecrementExpression *ast) override; bool visit(QQmlJS::AST::PostIncrementExpression *ast) override; bool visit(QQmlJS::AST::PreDecrementExpression *ast) override; bool visit(QQmlJS::AST::PreIncrementExpression *ast) override; bool visit(QQmlJS::AST::RegExpLiteral *ast) override; bool visit(QQmlJS::AST::StringLiteral *ast) override; bool visit(QQmlJS::AST::TemplateLiteral *ast) override; bool visit(QQmlJS::AST::ThisExpression *ast) override; bool visit(QQmlJS::AST::TildeExpression *ast) override; bool visit(QQmlJS::AST::TrueLiteral *ast) override; bool visit(QQmlJS::AST::TypeOfExpression *ast) override; bool visit(QQmlJS::AST::UnaryMinusExpression *ast) override; bool visit(QQmlJS::AST::UnaryPlusExpression *ast) override; bool visit(QQmlJS::AST::VoidExpression *ast) override; bool visit(QQmlJS::AST::FunctionDeclaration *ast) override; bool visit(QQmlJS::AST::YieldExpression *ast) override; bool visit(QQmlJS::AST::ClassExpression *ast) override; bool visit(QQmlJS::AST::ClassDeclaration *ast) override; // statements bool visit(QQmlJS::AST::Block *ast) override; bool visit(QQmlJS::AST::BreakStatement *ast) override; bool visit(QQmlJS::AST::ContinueStatement *ast) override; bool visit(QQmlJS::AST::DebuggerStatement *ast) override; bool visit(QQmlJS::AST::DoWhileStatement *ast) override; bool visit(QQmlJS::AST::EmptyStatement *ast) override; bool visit(QQmlJS::AST::ExpressionStatement *ast) override; bool visit(QQmlJS::AST::ForEachStatement *ast) override; bool visit(QQmlJS::AST::ForStatement *ast) override; bool visit(QQmlJS::AST::IfStatement *ast) override; bool visit(QQmlJS::AST::LabelledStatement *ast) override; bool visit(QQmlJS::AST::ReturnStatement *ast) override; bool visit(QQmlJS::AST::SwitchStatement *ast) override; bool visit(QQmlJS::AST::ThrowStatement *ast) override; bool visit(QQmlJS::AST::TryStatement *ast) override; bool visit(QQmlJS::AST::VariableStatement *ast) override; bool visit(QQmlJS::AST::WhileStatement *ast) override; bool visit(QQmlJS::AST::WithStatement *ast) override; // ui object members bool visit(QQmlJS::AST::UiArrayBinding *ast) override; bool visit(QQmlJS::AST::UiObjectBinding *ast) override; bool visit(QQmlJS::AST::UiObjectDefinition *ast) override; bool visit(QQmlJS::AST::UiPublicMember *ast) override; bool visit(QQmlJS::AST::UiScriptBinding *ast) override; bool visit(QQmlJS::AST::UiSourceElement *ast) override; bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const QQmlJS::SourceLocation &loc); virtual void throwSyntaxError(const QQmlJS::SourceLocation &loc, const QString &detail); virtual void throwReferenceError(const QQmlJS::SourceLocation &loc, const QString &detail); void throwRecursionDepthError() override { throwSyntaxError(QQmlJS::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } public: enum ErrorType { NoError, SyntaxError, ReferenceError }; ErrorType errorType() const { return _errorType; } bool hasError() const { return _errorType != NoError; } QQmlJS::DiagnosticMessage error() const; QUrl url() const; Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right); Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right); struct Arguments { int argc; int argv; bool hasSpread; }; Arguments pushArgs(QQmlJS::AST::ArgumentList *args); void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject, bool optional = false); Arguments pushTemplateArgs(QQmlJS::AST::TemplateLiteral *args); bool handleTaggedTemplate(Reference base, QQmlJS::AST::TaggedTemplate *ast); void createTemplateObject(QQmlJS::AST::TemplateLiteral *t); void setUseFastLookups(bool b) { useFastLookups = b; } void handleTryCatch(QQmlJS::AST::TryStatement *ast); void handleTryFinally(QQmlJS::AST::TryStatement *ast); Reference referenceForName( const QString &name, bool lhs, const QQmlJS::SourceLocation &accessLocation = QQmlJS::SourceLocation()); QQmlRefPointer generateCompilationUnit( bool generateUnitData = true); static QQmlRefPointer compileModule( bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList *diagnostics); Context *currentContext() const { return _context; } BytecodeGenerator *generator() const { return bytecodeGenerator; } void loadClosure(int index); Module *module() const { return _module; } BytecodeGenerator::Label returnLabel() { if (!_returnLabel) _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel()); return *_returnLabel; } void setGlobalNames(const QSet& globalNames) { m_globalNames = globalNames; } static const char *s_globalNames[]; protected: friend class ScanFunctions; friend struct ControlFlow; friend struct ControlFlowCatch; friend struct ControlFlowFinally; inline void setExprResult(const Reference &result) { m_expressions.back().setResult(result); } inline void setExprResult(Reference &&result) { m_expressions.back().setResult(std::move(result)); } inline Reference exprResult() const { return m_expressions.back().result(); } inline void clearExprResultName() { m_expressions.back().clearResultName(); } inline bool exprAccept(Format f) { return m_expressions.back().accept(f); } inline const Result ¤tExpr() const { return m_expressions.back(); } inline void pushExpr(Result &&expr) { m_expressions.push_back(std::move(expr)); } inline void pushExpr(const Result &expr) { m_expressions.push_back(expr); } inline void pushExpr(const QString &name = QString()) { m_expressions.emplace_back(name); } inline Result popExpr() { const Result result = m_expressions.back(); m_expressions.pop_back(); return result; } inline Reference popResult() { const Reference result = m_expressions.back().result(); m_expressions.pop_back(); return result; } std::vector m_expressions; VolatileMemoryLocations _volatileMemoryLocations; Module *_module; int _returnAddress; Context *_context; Context *_functionContext = nullptr; QQmlJS::AST::LabelledStatement *_labelledStatement; QV4::Compiler::JSUnitGenerator *jsUnitGenerator; BytecodeGenerator *bytecodeGenerator = nullptr; Moth::BytecodeGenerator::Label *_returnLabel = nullptr; bool _strictMode; bool useFastLookups = true; bool requiresReturnValue = false; bool insideSwitch = false; bool inFormalParameterList = false; bool functionEndsWithReturn = false; bool _tailCallsAreAllowed = true; bool storeSourceLocations = false; QSet m_globalNames; struct OptionalChainState { QQmlJS::AST::Node *tailNodeOfChain = nullptr; std::vector jumpsToPatch; bool actuallyHasOptionals = false; }; QSet m_seenOptionalChainNodes; std::stack m_optionalChainsStates; ControlFlow *controlFlow = nullptr; bool _fileNameIsUrl; ErrorType _errorType = NoError; QQmlJS::DiagnosticMessage _error; CodegenWarningInterface *_interface; class TailCallBlocker { public: TailCallBlocker(Codegen *cg, bool onoff = false) : _cg(cg) , _saved(_cg->_tailCallsAreAllowed) , _onoff(onoff) { _cg->_tailCallsAreAllowed = onoff; } ~TailCallBlocker() { _cg->_tailCallsAreAllowed = _saved; } void unblock() const { _cg->_tailCallsAreAllowed = _saved; } void reblock() const { _cg->_tailCallsAreAllowed = _onoff; } private: Codegen *_cg; bool _saved; bool _onoff; }; private: Q_DISABLE_COPY(Codegen) VolatileMemoryLocations scanVolatileMemoryLocations(QQmlJS::AST::Node *ast); void handleConstruct(const Reference &base, QQmlJS::AST::ArgumentList *args); void throwError(ErrorType errorType, const QQmlJS::SourceLocation &loc, const QString &detail); bool traverseOptionalChain(QQmlJS::AST::Node *node); void optionalChainFinalizer(const Reference &expressionResult, bool tailOfChain, bool isDeleteExpression = false); Reference loadSubscriptForCall(const Reference &base); void generateThrowException(const QString &type, const QString &text = QString()); }; } } QT_END_NAMESPACE #endif // QV4CODEGEN_P_H