diff --git a/src/qmljsc/purejavascriptgenerator.cpp b/src/qmljsc/purejavascriptgenerator.cpp index 43e80b8..4d9a4f3 100644 --- a/src/qmljsc/purejavascriptgenerator.cpp +++ b/src/qmljsc/purejavascriptgenerator.cpp @@ -1,189 +1,208 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Jan Marker * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "purejavascriptgenerator.h" +#include "error.h" + +using namespace QQmlJS; PureJavaScriptGenerator::PureJavaScriptGenerator() : m_outputStack() - , m_generatedCode(new QString()) { } QString PureJavaScriptGenerator::getGeneratedCode() { - return *m_generatedCode.string(); + if (m_outputStack.size() > 1) { + const QString errorDescription = QString("stack was not reduced correctly, top element is %1").arg(m_outputStack.top()); + QmlJSc::Error stackError(QmlJSc::Error::InternalError, errorDescription); + throw stackError; + } else if (m_outputStack.size() == 0) { + return ""; + } + return m_outputStack.pop(); } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::BinaryExpression *binaryExpression) { +bool PureJavaScriptGenerator::visit(AST::BinaryExpression *binaryExpression) { switch(binaryExpression->op) { case QSOperator::Assign: m_outputStack << "="; break; default: Q_ASSERT(binaryExpression->op); } return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::Block *) { - m_generatedCode << '{'; +bool PureJavaScriptGenerator::visit(AST::Block *) { + m_outputStack << "{"; return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::BreakStatement *breakStatement) { - m_generatedCode << "break"; +bool PureJavaScriptGenerator::visit(AST::BreakStatement *breakStatement) { + QString breakStatementCode("break"); if (breakStatement->label.length() > 0) { - m_generatedCode << ' '; - m_generatedCode << breakStatement->label.toString(); + breakStatementCode.append(' '); + breakStatementCode.append(breakStatement->label.toString()); } + m_outputStack << breakStatementCode; return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::FunctionBody *) { - m_generatedCode << "{"; +bool PureJavaScriptGenerator::visit(AST::FunctionBody *) { + m_outputStack << "{"; return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::FunctionDeclaration *functionDeclaration) { +bool PureJavaScriptGenerator::visit(AST::FunctionDeclaration *functionDeclaration) { const QString functionName = functionDeclaration->name.toString(); - m_generatedCode << "function" << ' ' << functionName; + m_outputStack << QString("function") + ' ' + functionName; return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::IdentifierExpression *identifierExpression) { +bool PureJavaScriptGenerator::visit(AST::IdentifierExpression *identifierExpression) { m_outputStack << identifierExpression->name.toString(); return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::NumericLiteral *numericLiteral) { +bool PureJavaScriptGenerator::visit(AST::NumericLiteral *numericLiteral) { m_outputStack.push(QString("%1").arg(numericLiteral->value)); return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::StringLiteral *stringLiteral) { +bool PureJavaScriptGenerator::visit(AST::StringLiteral *stringLiteral) { m_outputStack.push(stringLiteral->value.toString()); return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::PostDecrementExpression *) { +bool PureJavaScriptGenerator::visit(AST::PostDecrementExpression *) { m_outputStack.push("--"); return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::PostIncrementExpression *) { +bool PureJavaScriptGenerator::visit(AST::PostIncrementExpression *) { m_outputStack.push("++"); return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::PreDecrementExpression *) { +bool PureJavaScriptGenerator::visit(AST::PreDecrementExpression *) { m_outputStack.push("--"); return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::PreIncrementExpression *) { +bool PureJavaScriptGenerator::visit(AST::PreIncrementExpression *) { m_outputStack.push("++"); return true; } -bool PureJavaScriptGenerator::visit(QQmlJS::AST::VariableDeclaration *variableDeclaration) { +bool PureJavaScriptGenerator::visit(AST::VariableDeclaration *variableDeclaration) { + QString variableDeclarationCode; const QString identifier = variableDeclaration->name.toString(); if (variableDeclaration->readOnly) { - m_generatedCode << "const"; + variableDeclarationCode += "const"; } else { - m_generatedCode << "var"; + variableDeclarationCode += "var"; } - m_generatedCode << ' ' << identifier; + variableDeclarationCode += ' '; + variableDeclarationCode += identifier; if (variableDeclaration->expression) { - m_generatedCode << '='; + variableDeclarationCode += '='; } + + m_outputStack << variableDeclarationCode; + return true; } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::BinaryExpression *) { +void PureJavaScriptGenerator::endVisit(AST::BinaryExpression *) { const QString rightOperand = m_outputStack.pop(); const QString leftOperand = m_outputStack.pop(); const QString operation = m_outputStack.pop(); QString expression = QString("%1%2%3").arg(leftOperand).arg(operation).arg(rightOperand); m_outputStack.push(expression); - generateIfLastElementOnStack(); } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::Block *) { - m_generatedCode << '}'; +void PureJavaScriptGenerator::endVisit(AST::Block *) { + const QString blockCode = m_outputStack.pop(); + const QString openingParenthesis = m_outputStack.pop(); + m_outputStack << QString("%1%2}").arg(openingParenthesis, blockCode); } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::ExpressionStatement *) { - m_generatedCode << ';'; +void PureJavaScriptGenerator::endVisit(AST::ExpressionStatement *) { + m_outputStack << m_outputStack.pop() + ';'; } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::FunctionBody *) { - m_generatedCode << '}'; +void PureJavaScriptGenerator::endVisit(AST::FunctionBody *) { + const QString body = m_outputStack.pop(); + const QString openingBracket = m_outputStack.pop(); + m_outputStack << openingBracket + body + '}'; } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::IdentifierExpression *) { - generateIfLastElementOnStack(); +void PureJavaScriptGenerator::endVisit(AST::IdentifierExpression *) { } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::NumericLiteral *) { - generateIfLastElementOnStack(); +void PureJavaScriptGenerator::endVisit(AST::NumericLiteral *) { } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::PostDecrementExpression *) { +void PureJavaScriptGenerator::endVisit(AST::PostDecrementExpression *) { updateStackWithPostOperation(); - generateIfLastElementOnStack(); } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::PostIncrementExpression *) { +void PureJavaScriptGenerator::endVisit(AST::PostIncrementExpression *) { updateStackWithPostOperation(); - generateIfLastElementOnStack(); } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::PreDecrementExpression *) { +void PureJavaScriptGenerator::endVisit(AST::PreDecrementExpression *) { updateStackWithPreOperation(); - generateIfLastElementOnStack(); } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::PreIncrementExpression *) { +void PureJavaScriptGenerator::endVisit(AST::PreIncrementExpression *) { updateStackWithPreOperation(); - generateIfLastElementOnStack(); } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::StringLiteral *) { - generateIfLastElementOnStack(); +void PureJavaScriptGenerator::endVisit(AST::SourceElements *sourceElements) { + reduceListStack(sourceElements); } -void PureJavaScriptGenerator::generateIfLastElementOnStack() { - if (m_outputStack.size() == 1) { - m_generatedCode << m_outputStack.pop(); - } +void PureJavaScriptGenerator::endVisit(AST::StatementList *statementList) { + reduceListStack(statementList); } -void PureJavaScriptGenerator::endVisit(QQmlJS::AST::VariableStatement *) { - m_generatedCode << ';'; +void PureJavaScriptGenerator::endVisit(AST::StringLiteral *) { +} + +void PureJavaScriptGenerator::endVisit(AST::VariableDeclaration *) { + const QString expression = m_outputStack.pop(); + const QString variableName = m_outputStack.pop(); + m_outputStack << variableName + expression; +} + +void PureJavaScriptGenerator::endVisit(AST::VariableStatement *) { + m_outputStack << m_outputStack.pop() + ';'; } void PureJavaScriptGenerator::updateStackWithPostOperation() { const QString expression = m_outputStack.pop(); const QString operation = m_outputStack.pop(); m_outputStack.push(QString("%1%2").arg(expression).arg(operation)); } void PureJavaScriptGenerator::updateStackWithPreOperation() { const QString expression = m_outputStack.pop(); const QString operation = m_outputStack.pop(); m_outputStack.push(QString("%1%2").arg(operation).arg(expression)); -} +} \ No newline at end of file diff --git a/src/qmljsc/purejavascriptgenerator.h b/src/qmljsc/purejavascriptgenerator.h index ec8bdeb..9c81d0e 100644 --- a/src/qmljsc/purejavascriptgenerator.h +++ b/src/qmljsc/purejavascriptgenerator.h @@ -1,74 +1,86 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Jan Marker * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef QMLWEB_PUREJAVASCRIPTGENERATOR_H #define QMLWEB_PUREJAVASCRIPTGENERATOR_H #include #include class PureJavaScriptGenerator : public QQmlJS::AST::Visitor { public: PureJavaScriptGenerator(); QString getGeneratedCode(); virtual bool visit(QQmlJS::AST::BinaryExpression *) override; virtual bool visit(QQmlJS::AST::Block *) override; virtual bool visit(QQmlJS::AST::BreakStatement *) override; virtual bool visit(QQmlJS::AST::FunctionBody *) override; virtual bool visit(QQmlJS::AST::FunctionDeclaration *) override; virtual bool visit(QQmlJS::AST::IdentifierExpression *) override; virtual bool visit(QQmlJS::AST::NumericLiteral *) override; virtual bool visit(QQmlJS::AST::StringLiteral *) override; virtual bool visit(QQmlJS::AST::PostDecrementExpression *) override; virtual bool visit(QQmlJS::AST::PostIncrementExpression *) override; virtual bool visit(QQmlJS::AST::PreDecrementExpression *) override; virtual bool visit(QQmlJS::AST::PreIncrementExpression *) override; virtual bool visit(QQmlJS::AST::VariableDeclaration *) override; virtual void endVisit(QQmlJS::AST::BinaryExpression *) override; virtual void endVisit(QQmlJS::AST::Block *) override; virtual void endVisit(QQmlJS::AST::ExpressionStatement *) override; virtual void endVisit(QQmlJS::AST::FunctionBody *) override; virtual void endVisit(QQmlJS::AST::IdentifierExpression *) override; virtual void endVisit(QQmlJS::AST::NumericLiteral *) override; virtual void endVisit(QQmlJS::AST::PostDecrementExpression *) override; virtual void endVisit(QQmlJS::AST::PostIncrementExpression *) override; virtual void endVisit(QQmlJS::AST::PreDecrementExpression *) override; virtual void endVisit(QQmlJS::AST::PreIncrementExpression *) override; + virtual void endVisit(QQmlJS::AST::SourceElements *) override; + virtual void endVisit(QQmlJS::AST::StatementList *) override; virtual void endVisit(QQmlJS::AST::StringLiteral *) override; + virtual void endVisit(QQmlJS::AST::VariableDeclaration *) override; virtual void endVisit(QQmlJS::AST::VariableStatement *) override; private: - void generateIfLastElementOnStack(); void updateStackWithPostOperation(); void updateStackWithPreOperation(); + template void reduceListStack(ListType* list); QStack m_outputStack; - QTextStream m_generatedCode; friend class TestPureJavaScriptGenerator; }; +template void PureJavaScriptGenerator::reduceListStack(ListType* current) { + // the list is only iterated to count the elements + // current is not processed in any way + QString code; + while (current) { + code.prepend(m_outputStack.pop()); + current = current->next; + } + m_outputStack << code; +} #endif //QMLWEB_PUREJAVASCRIPTGENERATOR_H diff --git a/src/qmljsc/utils/error.h b/src/qmljsc/utils/error.h index 1942d88..8627107 100644 --- a/src/qmljsc/utils/error.h +++ b/src/qmljsc/utils/error.h @@ -1,96 +1,97 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Jan Marker * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef ERROR_H #define ERROR_H #include #include #include #include #include #include namespace QmlJSc { class Error : public std::exception { public: enum Type { UnknownError, ReadFileError, ParseError, ModuleImportError, - SymbolLookupError + SymbolLookupError, + InternalError }; Error() : m_type(UnknownError) , m_column(-1) , m_line(-1) , m_reason(0) {} Error(Type type, QString description, Error *reason = 0) : m_type(type) , m_description(description) , m_column(-1) , m_line(-1) , m_reason(reason) {} virtual ~Error() {} Type type() const { return m_type; } void setType(Type type) { m_type = type; } QUrl file() const { return m_file; } void setFile(QUrl file) { m_file = file; } QString description() const { return m_description; } void setDescription(QString description) { m_description = description; } int column() const { return m_column; } void setColumn(int column) { m_column = column; } int line() const { return m_line; } void setLine(int line) { m_line = line; } Error *reason() const { return m_reason.data(); } void setReason(Error *reason) { m_reason.reset(reason); } virtual const char* what() const noexcept; private: Type m_type; QUrl m_file; QString m_description; int m_column; int m_line; QSharedPointer m_reason; }; } QDebug operator<<(QDebug dbg, const QmlJSc::Error &error); Q_DECLARE_METATYPE(QmlJSc::Error); #endif // ERROR_H diff --git a/tests/auto/qmljsc/testpurejavascriptgenerator.cpp b/tests/auto/qmljsc/testpurejavascriptgenerator.cpp index e5dadcb..61ef4ae 100644 --- a/tests/auto/qmljsc/testpurejavascriptgenerator.cpp +++ b/tests/auto/qmljsc/testpurejavascriptgenerator.cpp @@ -1,194 +1,234 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Jan Marker * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include "../../../src/qmljsc/error.h" #include "../../../src/qmljsc/purejavascriptgenerator.h" -#define TEST_SOMEVISIT_GENERATES_FROM(testSituation, expectedResult, visitType, className, ...) \ - void test_ ## visitType ## _ ## className ## _generatesCorrectCode_ ## testSituation() { \ +#define TEST_SOMEVISIT_PUTS_ON_STACK(testSituation, expectedTopOfStack, visitType, className, ...) \ + void test_ ## visitType ## _ ## className ## _putsOnStack_ ## testSituation() { \ QQmlJS::AST::className classInstance(__VA_ARGS__); \ - m_generator->visitType(&classInstance); \ - QCOMPARE(static_cast(m_generator)->getGeneratedCode(), QStringLiteral(expectedResult)); \ + m_generator->visit(&classInstance); \ + int stackElementCount = asPureJSGen(m_generator)->m_outputStack.count(); \ + QCOMPARE(stackElementCount, 1); \ + QString stringOnStack = asPureJSGen(m_generator)->m_outputStack.top(); \ + QCOMPARE(stringOnStack, QStringLiteral(expectedTopOfStack)); \ } -#define TEST_VISIT_GENERATES(testSituation, expectedResult, className, ...) \ - TEST_SOMEVISIT_GENERATES_FROM(testSituation, expectedResult, visit, className, __VA_ARGS__) +#define TEST_VISIT_PUTS_ON_STACK_WITHOUT_RETURN(testSituation, expectedResult, className, ...) \ + TEST_SOMEVISIT_PUTS_ON_STACK(testSituation, expectedResult, visit, className, __VA_ARGS__) -#define TEST_ENDVISIT_GENERATES(testSituation, expectedResult, className, ...) \ - TEST_SOMEVISIT_GENERATES_FROM(testSituation, expectedResult, endVisit, className, __VA_ARGS__) +#define TEST_ENDVISIT_PUTS_ON_STACK(testSituation, expectedResult, className, ...) \ + TEST_SOMEVISIT_PUTS_ON_STACK(testSituation, expectedResult, endVisit, className, __VA_ARGS__) #define TEST_VISIT_RETURNS(testSituation, expectedReturnResult, className, ...) \ void test_visit ## _ ## className ## _returns_ ## expectedReturnResult ## _ ## testSituation() { \ QQmlJS::AST::className classInstance(__VA_ARGS__); \ QCOMPARE(m_generator->visit(&classInstance), expectedReturnResult); \ } -#define TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(className, ...) \ - TEST_VISIT_RETURNS(defaultImplementation, true, className, __VA_ARGS__) \ - TEST_VISIT_GENERATES(defaultImplementation, "", className, __VA_ARGS__) +#define TEST_VISIT_PUTS_ON_STACK(testSituation, expectedResult, className, ...) \ + TEST_VISIT_PUTS_ON_STACK_WITHOUT_RETURN(testSituation, expectedResult, className, __VA_ARGS__) \ + TEST_VISIT_RETURNS(testSituation, true, className, __VA_ARGS__) -#define TEST_VISIT_PUTS_ON_STACK(testScenario, expectedTopOfStack, className, ...) \ - void test_visit_## className ## _putsOnStack_ ## testScenario() { \ +#define TEST_VISIT_PUTS_NOTHING_ON_STACK(testSituation, className, ...) \ + void test_ ## visitType ## _ ## className ## _putsNothingOnStack_ ## testSituation() { \ QQmlJS::AST::className classInstance(__VA_ARGS__); \ m_generator->visit(&classInstance); \ int stackElementCount = asPureJSGen(m_generator)->m_outputStack.count(); \ - QCOMPARE(stackElementCount, 1); \ - QString stringOnStack = asPureJSGen(m_generator)->m_outputStack.top(); \ - QCOMPARE(stringOnStack, QStringLiteral(expectedTopOfStack)); \ + QCOMPARE(stackElementCount, 0); \ } -#define TEST_ENDVISIT_FROM_STACK(testScenario, action, expectedResult, expectedStackSize, stackContent, className, ...) \ - void test_endVisit_ ## className ## _ ## action ## _ ## testScenario() { \ +#define TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(className, ...) \ + TEST_VISIT_RETURNS(defaultImplementation, true, className, __VA_ARGS__) \ + TEST_VISIT_PUTS_NOTHING_ON_STACK(defaultImplementation, className, __VA_ARGS__) + +#define TEST_ENDVISIT_REDUCES_STACK(testScenario, expectedTopOfStack, stackContent, className, ...) \ + void test_endVisit_ ## className ## _reducesStack_ ## testScenario() { \ QQmlJS::AST::className classInstance(__VA_ARGS__); \ asPureJSGen(m_generator)->m_outputStack.append(QVectorstackContent); \ m_generator->endVisit(&classInstance); \ int stackElementCount = asPureJSGen(m_generator)->m_outputStack.count(); \ - QCOMPARE(stackElementCount, expectedStackSize); \ - QCOMPARE(asPureJSGen(m_generator)->getGeneratedCode(), QStringLiteral(expectedResult)); \ + QCOMPARE(stackElementCount, 1); \ + QString stringOnStack = asPureJSGen(m_generator)->m_outputStack.top(); \ + QCOMPARE(stringOnStack, QStringLiteral(expectedTopOfStack)); \ } -#define TEST_ENDVISIT_GENERATES_FROM_STACK(testScenario, expectedResult, stackContent, className, ...) \ - TEST_ENDVISIT_FROM_STACK(testScenario, generatesFromStack, expectedResult, 0, stackContent, className, __VA_ARGS__) - -#define TEST_ENDVISIT_ONLY_UPDATES_STACK(testScenario, expectedTopOfStack, stackContent, className, ...) \ - void test_endVisit_ ## className ## _updatesStack_ ## testScenario() { \ - QQmlJS::AST::className classInstance(__VA_ARGS__); \ +#define TEST_ENDVISIT_REDUCES_STACK_OBJ(testScenario, expectedTopOfStack, stackContent, className, classInstancePtr) \ + void test_endVisit_ ## className ## _reducesStack_ ## testScenario() { \ asPureJSGen(m_generator)->m_outputStack.append(QVectorstackContent); \ - m_generator->endVisit(&classInstance); \ + m_generator->endVisit(classInstancePtr); \ + int stackElementCount = asPureJSGen(m_generator)->m_outputStack.count(); \ + QCOMPARE(stackElementCount, 1); \ QString stringOnStack = asPureJSGen(m_generator)->m_outputStack.top(); \ QCOMPARE(stringOnStack, QStringLiteral(expectedTopOfStack)); \ - QCOMPARE(asPureJSGen(m_generator)->getGeneratedCode(), QStringLiteral("")); \ } #define TEST_ENDVISIT_DOES_NOTHING(testScenario, className, ...) \ void test_endVisit_ ## className ## _doesNothing_ ## testScenario() { \ QQmlJS::AST::className classInstance(__VA_ARGS__); \ m_generator->endVisit(&classInstance); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack.size(), 0); \ QCOMPARE(asPureJSGen(m_generator)->getGeneratedCode(), QStringLiteral("")); \ } class TestPureJavaScriptGenerator : public QObject { Q_OBJECT +public: + TestPureJavaScriptGenerator() + : m_someLabel("ALabel") + , m_someLabelStringRef(&m_someLabel) + , m_someString("some string") + , m_someStringStringRef(&m_someString) + , m_someIdentifier("i") + , m_someIdentifierStringRef(&m_someIdentifier) + , m_trueExpression() + , m_falseExpression() + , m_statement1() + , m_statement2() + , m_statement3() + , m_threeStatementsList(&m_statement1) + , m_statementListPart2(&m_threeStatementsList, &m_statement2) + , m_statementListPart3(&m_statementListPart2, &m_statement3) + , m_sourceElement1(nullptr) + , m_sourceElement2(nullptr) + , m_sourceElement3(nullptr) + , m_threeSourceElementsList(&m_sourceElement1) + , m_sourceElementsListPart2(&m_threeSourceElementsList, &m_sourceElement2) + , m_sourceElementsListPart3(&m_sourceElementsListPart2, &m_sourceElement3) + { + m_statementListPart3.finish(); + m_sourceElementsListPart3.finish(); + } + private: PureJavaScriptGenerator* asPureJSGen(QQmlJS::AST::Visitor* generator) { return static_cast(generator); } QQmlJS::AST::Visitor *m_generator; - const QString m_someLabel = QString("ALabel"); + const QString m_someLabel; const QStringRef m_someLabelStringRef = QStringRef(&m_someLabel); const QString m_someString = QString("some string"); const QStringRef m_someStringStringRef = QStringRef(&m_someString); const QString m_someIdentifier = QString("i"); const QStringRef m_someIdentifierStringRef = QStringRef(&m_someIdentifier); QQmlJS::AST::TrueLiteral m_trueExpression; QQmlJS::AST::FalseLiteral m_falseExpression; + QQmlJS::AST::EmptyStatement m_statement1; + QQmlJS::AST::EmptyStatement m_statement2; + QQmlJS::AST::EmptyStatement m_statement3; + QQmlJS::AST::StatementList m_threeStatementsList; + QQmlJS::AST::StatementList m_statementListPart2; + QQmlJS::AST::StatementList m_statementListPart3; + QQmlJS::AST::StatementSourceElement m_sourceElement1; + QQmlJS::AST::StatementSourceElement m_sourceElement2; + QQmlJS::AST::StatementSourceElement m_sourceElement3; + QQmlJS::AST::SourceElements m_threeSourceElementsList; + QQmlJS::AST::SourceElements m_sourceElementsListPart2; + QQmlJS::AST::SourceElements m_sourceElementsListPart3; private slots: void init() { m_generator = new PureJavaScriptGenerator(); }; void cleanup() { delete m_generator; } void testGeneratedCodeIsInitiallyEmpty() { QCOMPARE(asPureJSGen(m_generator)->getGeneratedCode(), QStringLiteral("")); } - TEST_VISIT_RETURNS(Generally, true, BinaryExpression, &m_trueExpression, QSOperator::Assign , &m_falseExpression); - TEST_VISIT_PUTS_ON_STACK(EqualOperator, "=", BinaryExpression, &m_trueExpression, QSOperator::Assign, &m_falseExpression); - TEST_VISIT_RETURNS(WithoutStatements, true, Block, nullptr) - TEST_VISIT_GENERATES(WithoutStatements, "{", Block, nullptr) - TEST_VISIT_RETURNS(WithLabel, true, BreakStatement, m_someLabelStringRef) - TEST_VISIT_GENERATES(WithLabel, "break ALabel", BreakStatement, m_someLabelStringRef) - TEST_VISIT_RETURNS(WithoutLabel, true, BreakStatement, nullptr) - TEST_VISIT_GENERATES(WithoutLabel, "break", BreakStatement, nullptr) + void test_getGeneratedCode_getsTopOfStack() { + // Prepare + asPureJSGen(m_generator)->m_outputStack << "1"; + + // Verify + QCOMPARE(asPureJSGen(m_generator)->getGeneratedCode(), QStringLiteral("1")); + } + + void test_getGeneratedCode_throwsError_OnStackSizeGreaterThanOne() { + // Prepare + asPureJSGen(m_generator)->m_outputStack << "1" << "2"; + + // Verify + QVERIFY_EXCEPTION_THROWN(asPureJSGen(m_generator)->getGeneratedCode(), QmlJSc::Error); + } + + TEST_VISIT_PUTS_ON_STACK(EqualOperator, "=", BinaryExpression, &m_trueExpression, QSOperator::Assign, &m_falseExpression) + TEST_VISIT_PUTS_ON_STACK(WithoutStatements, "{", Block, nullptr) + TEST_VISIT_PUTS_ON_STACK(WithLabel, "break ALabel", BreakStatement, m_someLabelStringRef) + TEST_VISIT_PUTS_ON_STACK(WithoutLabel, "break", BreakStatement, nullptr) TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(ExpressionStatement, nullptr) - TEST_VISIT_RETURNS(DefautScenario, true, FunctionBody, nullptr) - TEST_VISIT_GENERATES(NoSourceElements, "{", FunctionBody, nullptr) - TEST_VISIT_RETURNS(DefaultScenario, true, FunctionDeclaration, m_someIdentifierStringRef, nullptr, nullptr) - TEST_VISIT_GENERATES(DefaultScenario, "function i", FunctionDeclaration, m_someIdentifierStringRef, nullptr, nullptr) - TEST_VISIT_RETURNS(DefaultScenario, true, IdentifierExpression, m_someIdentifierStringRef) + TEST_VISIT_PUTS_ON_STACK(NoSourceElements, "{", FunctionBody, nullptr) + TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "function i", FunctionDeclaration, m_someIdentifierStringRef, nullptr, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "i", IdentifierExpression, m_someIdentifierStringRef) - TEST_VISIT_RETURNS(DefaultScenario, true, NumericLiteral, 3.14) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "3.14", NumericLiteral, 3.14) - TEST_VISIT_RETURNS(DefaultScenario, true, PostDecrementExpression, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "--", PostDecrementExpression, nullptr) - TEST_VISIT_RETURNS(DefaultScenario, true, PostIncrementExpression, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "++", PostIncrementExpression, nullptr) - TEST_VISIT_RETURNS(DefaultScenario, true, PreDecrementExpression, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "--", PreDecrementExpression, nullptr) - TEST_VISIT_RETURNS(DefaultScenario, true, PreIncrementExpression, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "++", PreIncrementExpression, nullptr) - TEST_VISIT_RETURNS(SomeString, true, StringLiteral, m_someStringStringRef) + TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(SourceElements, nullptr) + TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(StatementList, nullptr) TEST_VISIT_PUTS_ON_STACK(SomeString, "some string", StringLiteral, m_someStringStringRef) - TEST_VISIT_RETURNS(AssignmentScenario, true, VariableDeclaration, m_someIdentifierStringRef, nullptr) - TEST_VISIT_GENERATES(AssignmentScenario, "var i=", VariableDeclaration, m_someIdentifierStringRef, &m_trueExpression) - TEST_VISIT_GENERATES(NoAssignmentScenario, "var i", VariableDeclaration, m_someIdentifierStringRef, nullptr) + TEST_VISIT_PUTS_ON_STACK(AssignmentScenario, "var i=", VariableDeclaration, m_someIdentifierStringRef, &m_trueExpression) + TEST_VISIT_PUTS_ON_STACK(NoAssignmentScenario, "var i", VariableDeclaration, m_someIdentifierStringRef, nullptr) void test_visit_VariableDeclaration_generatesCorrectCode_ConstAssignment() { // Prepare QQmlJS::AST::VariableDeclaration variableDeclaration(m_someIdentifierStringRef, &m_trueExpression); variableDeclaration.readOnly = true; // Do m_generator->visit(&variableDeclaration); // Verify QCOMPARE(asPureJSGen(m_generator)->getGeneratedCode(), QStringLiteral("const i=")); } TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(VariableStatement, nullptr) - TEST_ENDVISIT_ONLY_UPDATES_STACK(MoreThanTwoOperands, "2==4", ({"++", "==", "2", "4"}), BinaryExpression, nullptr, -1, nullptr) - TEST_ENDVISIT_GENERATES_FROM_STACK(TwoOperands, "2==4", ({"==", "2", "4"}), BinaryExpression, nullptr, -1, nullptr) - TEST_ENDVISIT_GENERATES(WithoutStatements, "}", Block, nullptr) - TEST_ENDVISIT_GENERATES(NoExpression, ";", ExpressionStatement, nullptr) - TEST_ENDVISIT_GENERATES(FunctionBodyClosesCorrectly, "}", FunctionBody, nullptr); + TEST_ENDVISIT_REDUCES_STACK(TwoOperands, "2==4", ({"==", "2", "4"}), BinaryExpression, nullptr, -1, nullptr) + TEST_ENDVISIT_REDUCES_STACK(WithoutStatements, "{content}", ({"{", "content"}), Block, nullptr) + TEST_ENDVISIT_REDUCES_STACK(NoExpression, "expression;", ({"expression"}), ExpressionStatement, nullptr) + TEST_ENDVISIT_REDUCES_STACK(FunctionBodyClosesCorrectly, "{func}", ({"{", "func"}), FunctionBody, nullptr); TEST_ENDVISIT_DOES_NOTHING(DefaultScenario, FunctionDeclaration, m_someIdentifierStringRef, nullptr, nullptr); - TEST_ENDVISIT_GENERATES_FROM_STACK(DefaultScenario, "abc", ({"abc"}), IdentifierExpression, nullptr) - TEST_ENDVISIT_ONLY_UPDATES_STACK(DefaultScenario, "abc", ({"def", "abc"}), IdentifierExpression, nullptr) - TEST_ENDVISIT_GENERATES_FROM_STACK(OneElement, "2.7", ({"2.7"}), NumericLiteral, 3.14) - TEST_ENDVISIT_ONLY_UPDATES_STACK(TwoElements, "2.2", ({"2.1", "2.2"}), NumericLiteral, 3.14) - TEST_ENDVISIT_GENERATES_FROM_STACK(OneLiteral, "2.7--", ({"--", "2.7"}), PostDecrementExpression, nullptr) - TEST_ENDVISIT_ONLY_UPDATES_STACK(TwoLiterals, "2.7--", ({"0.0", "--", "2.7"}), PostDecrementExpression, nullptr) - TEST_ENDVISIT_GENERATES_FROM_STACK(OneLiteral, "2.7++", ({"++", "2.7"}), PostIncrementExpression, nullptr) - TEST_ENDVISIT_ONLY_UPDATES_STACK(TwoLiterals, "2.7++", ({"0.0", "++", "2.7"}), PostIncrementExpression, nullptr) - TEST_ENDVISIT_GENERATES_FROM_STACK(OneLiteral, "--2.7", ({"--", "2.7"}), PreDecrementExpression, nullptr) - TEST_ENDVISIT_ONLY_UPDATES_STACK(TwoLiterals, "--2.7", ({"0.0", "--", "2.7"}), PreDecrementExpression, nullptr) - TEST_ENDVISIT_GENERATES_FROM_STACK(OneLiteral, "++2.7", ({"++", "2.7"}), PreIncrementExpression, nullptr) - TEST_ENDVISIT_ONLY_UPDATES_STACK(TwoLiterals, "++2.7", ({"0.0", "++", "2.7"}), PreIncrementExpression, nullptr) - TEST_ENDVISIT_GENERATES_FROM_STACK(OneLiteral, "another string", ({"another string"}), StringLiteral, nullptr) - TEST_ENDVISIT_ONLY_UPDATES_STACK(TwoLiterals, "string", ({"another", "string"}), StringLiteral, nullptr) - TEST_ENDVISIT_DOES_NOTHING(DefaultScenario, VariableDeclaration, m_someIdentifierStringRef, nullptr) - TEST_ENDVISIT_GENERATES(NoDeclarationList, ";", VariableStatement, nullptr) + TEST_ENDVISIT_REDUCES_STACK(DefaultScenario, "abc", ({"abc"}), IdentifierExpression, nullptr) + TEST_ENDVISIT_REDUCES_STACK(OneElement, "2.7", ({"2.7"}), NumericLiteral, 3.14) + TEST_ENDVISIT_REDUCES_STACK(OneLiteral, "2.7--", ({"--", "2.7"}), PostDecrementExpression, nullptr) + TEST_ENDVISIT_REDUCES_STACK(OneLiteral, "2.7++", ({"++", "2.7"}), PostIncrementExpression, nullptr) + TEST_ENDVISIT_REDUCES_STACK(OneLiteral, "--2.7", ({"--", "2.7"}), PreDecrementExpression, nullptr) + TEST_ENDVISIT_REDUCES_STACK(OneLiteral, "++2.7", ({"++", "2.7"}), PreIncrementExpression, nullptr) + TEST_ENDVISIT_REDUCES_STACK_OBJ(ThreeSourceElements, "sEl1sEl2sEl3", ({"sEl1", "sEl2", "sEl3"}), SourceElements, &m_threeSourceElementsList) + TEST_ENDVISIT_REDUCES_STACK_OBJ(ThreeStatements, "st1st2st3", ({"st1", "st2", "st3"}), StatementList, &m_threeStatementsList) + TEST_ENDVISIT_REDUCES_STACK(OneLiteral, "another string", ({"another string"}), StringLiteral, nullptr) + TEST_ENDVISIT_REDUCES_STACK(DefaultScenario, "var x=5", ({"var x=", "5"}), VariableDeclaration, m_someIdentifierStringRef, nullptr) + TEST_ENDVISIT_REDUCES_STACK(DefaultScenario, "var x;", ({"var x"}), VariableStatement, nullptr) }; QTEST_MAIN(TestPureJavaScriptGenerator) #include "testpurejavascriptgenerator.moc"