diff --git a/src/qmljsc/purejavascriptgenerator.cpp b/src/qmljsc/purejavascriptgenerator.cpp index b5adc31..5c4a399 100644 --- a/src/qmljsc/purejavascriptgenerator.cpp +++ b/src/qmljsc/purejavascriptgenerator.cpp @@ -1,279 +1,343 @@ /* * 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 "utils/error.h" using namespace QQmlJS; PureJavaScriptGenerator::PureJavaScriptGenerator() : m_outputStack() { } QString PureJavaScriptGenerator::getGeneratedCode() { 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(AST::BinaryExpression *binaryExpression) { switch(binaryExpression->op) { case QSOperator::Assign: m_outputStack << "="; break; case QSOperator::InplaceAdd: m_outputStack << "+="; break; case QSOperator::InplaceSub: m_outputStack << "-="; break; case QSOperator::InplaceMul: m_outputStack << "*="; break; case QSOperator::InplaceDiv: m_outputStack << "/="; break; case QSOperator::InplaceMod: m_outputStack << "%="; break; case QSOperator::InplaceLeftShift: m_outputStack << "<<="; break; case QSOperator::InplaceRightShift: m_outputStack << ">>="; break; case QSOperator::InplaceURightShift: m_outputStack << ">>>="; break; case QSOperator::InplaceAnd: m_outputStack << "&="; break; case QSOperator::InplaceXor: m_outputStack << "^="; break; case QSOperator::InplaceOr: m_outputStack << "|="; break; case QSOperator::Add: m_outputStack << "+"; break; case QSOperator::Sub: m_outputStack << "-"; break; case QSOperator::Mul: m_outputStack << "*"; break; case QSOperator::Div: m_outputStack << "/"; break; case QSOperator::Mod: m_outputStack << "%"; break; case QSOperator::LShift: m_outputStack << "<<"; break; case QSOperator::RShift: m_outputStack << ">>"; break; case QSOperator::URShift: m_outputStack << ">>>"; break; case QSOperator::BitAnd: m_outputStack << "&"; break; case QSOperator::BitXor: m_outputStack << "^"; break; case QSOperator::BitOr: m_outputStack << "|"; break; case QSOperator::Equal: m_outputStack << "=="; break; case QSOperator::NotEqual: m_outputStack << "!="; break; case QSOperator::StrictEqual: m_outputStack << "==="; break; case QSOperator::StrictNotEqual: m_outputStack << "!=="; break; case QSOperator::Gt: m_outputStack << ">"; break; case QSOperator::Ge: m_outputStack << ">="; break; case QSOperator::Lt: m_outputStack << "<"; break; case QSOperator::Le: m_outputStack << "<="; break; case QSOperator::And: m_outputStack << "&&"; break; case QSOperator::Or: m_outputStack << "||"; break; case QSOperator::In: m_outputStack << " in "; break; case QSOperator::InstanceOf: m_outputStack << " instanceof "; break; default: Q_ASSERT_X(binaryExpression->op, __FILE__, "The operator is not handled yet"); } return true; } bool PureJavaScriptGenerator::visit(AST::BreakStatement *breakStatement) { reduceJumpStatement("break", breakStatement->label); return true; } bool PureJavaScriptGenerator::visit(AST::CaseClause *) { m_outputStack << "case"; return true; } bool PureJavaScriptGenerator::visit(AST::ContinueStatement *continueStatement) { reduceJumpStatement("continue", continueStatement->label); return true; } bool PureJavaScriptGenerator::visit(AST::DefaultClause *) { m_outputStack << "default"; return true; } +bool PureJavaScriptGenerator::visit(AST::Elision *currentElision) { + QString output; + while (currentElision) { + output += ','; + currentElision = currentElision->next; + } + m_outputStack << output; + return true; +} + +bool PureJavaScriptGenerator::visit(AST::FalseLiteral *) { + m_outputStack << "false"; + return true; +} + bool PureJavaScriptGenerator::visit(AST::FormalParameterList *currentParameter) { QString parameterCode; while (currentParameter) { parameterCode += currentParameter->name.toString(); if (currentParameter->next) { parameterCode += ','; } currentParameter = currentParameter->next; } m_outputStack << parameterCode; return true; } bool PureJavaScriptGenerator::visit(AST::IdentifierExpression *identifierExpression) { m_outputStack << identifierExpression->name.toString(); return true; } +bool PureJavaScriptGenerator::visit(AST::NullExpression *) { + m_outputStack << "null"; + return true; +} + bool PureJavaScriptGenerator::visit(AST::NumericLiteral *numericLiteral) { m_outputStack.push(QString::number(numericLiteral->value)); return true; } bool PureJavaScriptGenerator::visit(AST::StringLiteral *stringLiteral) { m_outputStack << '"' + stringLiteral->value.toString() + '"'; return true; } +bool PureJavaScriptGenerator::visit(AST::ThisExpression *) { + m_outputStack << "this"; + return true; +} + +bool PureJavaScriptGenerator::visit(AST::TrueLiteral *) { + m_outputStack << "true"; + return true; +} + +void PureJavaScriptGenerator::endVisit(AST::ArrayLiteral *arrayLiteral) { + const QString elision = (arrayLiteral->elision)?m_outputStack.pop():""; + const QString elements = (arrayLiteral->elements)?m_outputStack.pop():""; + m_outputStack << '[' + elements + elision + ']'; +} + 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); } void PureJavaScriptGenerator::endVisit(AST::Block *) { const QString blockCode = m_outputStack.pop(); m_outputStack << '{' + blockCode + '}'; } void PureJavaScriptGenerator::endVisit(AST::CaseBlock *caseBlock) { QString clausesRight; if (caseBlock->moreClauses) { clausesRight = m_outputStack.pop(); } QString defaultClause; if (caseBlock->defaultClause) { defaultClause = m_outputStack.pop(); } const QString clausesLeft = m_outputStack.pop(); m_outputStack << '{' + clausesLeft + defaultClause + clausesRight + '}'; } void PureJavaScriptGenerator::endVisit(AST::CaseClause *) { const QString statement = m_outputStack.pop(); const QString expression = m_outputStack.pop(); const QString caseKeyword = m_outputStack.pop(); m_outputStack << caseKeyword + ' ' + expression + ':' + statement; } void PureJavaScriptGenerator::endVisit(AST::CaseClauses *caseClauses) { reduceListStack(caseClauses); } void PureJavaScriptGenerator::endVisit(AST::DefaultClause *) { const QString statement = m_outputStack.pop(); const QString defaultKeyword = m_outputStack.pop(); m_outputStack << defaultKeyword + ':' + statement; } +void PureJavaScriptGenerator::endVisit(AST::ElementList *elementList) { + QString code; + AST::ElementList *currentElement = elementList; + QStack::iterator positionOnStack = m_outputStack.end(); + + for (currentElement = elementList; currentElement; currentElement = currentElement->next) { + if (currentElement->elision) { + --positionOnStack; + } + if (currentElement->expression) { + --positionOnStack; + } + } + + for (currentElement = elementList; currentElement; currentElement = currentElement->next) { + if (currentElement->elision) { + code += *positionOnStack; + positionOnStack = m_outputStack.erase(positionOnStack); + } + if (currentElement->expression) { + code += *positionOnStack + ','; + positionOnStack = m_outputStack.erase(positionOnStack); + } + } + + m_outputStack << code; +} + void PureJavaScriptGenerator::endVisit(AST::EmptyStatement *) { m_outputStack << ";"; } void PureJavaScriptGenerator::endVisit(AST::ExpressionStatement *) { m_outputStack << m_outputStack.pop() + ';'; } void PureJavaScriptGenerator::endVisit(AST::FunctionBody *) { const QString body = m_outputStack.pop(); m_outputStack << '{' + body + '}'; } void PureJavaScriptGenerator::endVisit(AST::FunctionDeclaration *functionDeclaration) { const QString body = (functionDeclaration->body) ? m_outputStack.pop() : "{}"; const QString parameters = (functionDeclaration->formals) ? m_outputStack.pop() : ""; const QString name = functionDeclaration->name.toString(); m_outputStack << "function " + name + '(' + parameters + ')' + body; } void PureJavaScriptGenerator::endVisit(AST::IdentifierExpression *) { } void PureJavaScriptGenerator::endVisit(AST::IfStatement *ifExpression) { const QString elseStatements = (ifExpression->ko) ? m_outputStack.pop() : ""; const QString ifStatements = m_outputStack.pop(); const QString expression = m_outputStack.pop(); QString code("if(" + expression + ')' + ifStatements); if (ifExpression->ko) { code += "else " + elseStatements; } m_outputStack << code; } void PureJavaScriptGenerator::endVisit(AST::NumericLiteral *) { } void PureJavaScriptGenerator::endVisit(AST::PostDecrementExpression *) { const QString expression = m_outputStack.pop(); m_outputStack << expression + "--"; } void PureJavaScriptGenerator::endVisit(AST::PostIncrementExpression *) { const QString expression = m_outputStack.pop(); m_outputStack << expression + "++"; } void PureJavaScriptGenerator::endVisit(AST::PreDecrementExpression *) { const QString expression = m_outputStack.pop(); m_outputStack << "--" + expression; } void PureJavaScriptGenerator::endVisit(AST::PreIncrementExpression *) { const QString expression = m_outputStack.pop(); m_outputStack << "++" + expression; } void PureJavaScriptGenerator::endVisit(AST::ReturnStatement *returnStatement) { const QString expression = (returnStatement->expression) ? ' '+m_outputStack.pop() : ""; m_outputStack << "return" + expression + ';'; } void PureJavaScriptGenerator::endVisit(AST::SourceElements *sourceElements) { reduceListStack(sourceElements); } void PureJavaScriptGenerator::endVisit(AST::StatementList *statementList) { reduceListStack(statementList); } void PureJavaScriptGenerator::endVisit(AST::StringLiteral *) { } void PureJavaScriptGenerator::endVisit(AST::SwitchStatement *) { const QString clauseBlock = m_outputStack.pop(); const QString expression = m_outputStack.pop(); m_outputStack << "switch(" + expression + ')' + clauseBlock; } void PureJavaScriptGenerator::endVisit(AST::VariableDeclaration *declaration) { const QString expression = (declaration->expression) ? "="+m_outputStack.pop() : ""; const QString variableName = declaration->name.toString(); m_outputStack << variableName + expression; } void PureJavaScriptGenerator::endVisit(AST::VariableDeclarationList *declarationList) { reduceListStack(declarationList, ","); const QString declarationListCode = m_outputStack.pop(); const QString declarationType = (declarationList->declaration->readOnly) ? "const" : "var"; m_outputStack << declarationType + ' ' + declarationListCode; } void PureJavaScriptGenerator::endVisit(AST::VariableStatement *) { m_outputStack << m_outputStack.pop() + ';'; } void PureJavaScriptGenerator::reduceJumpStatement(const char *keyword, QStringRef label) { QString labelStatementCode(keyword); if (label.length() > 0) { labelStatementCode.append(' '); labelStatementCode.append(label.toString()); } m_outputStack << labelStatementCode + ';'; } \ No newline at end of file diff --git a/src/qmljsc/purejavascriptgenerator.h b/src/qmljsc/purejavascriptgenerator.h index 45b6088..f1374d5 100644 --- a/src/qmljsc/purejavascriptgenerator.h +++ b/src/qmljsc/purejavascriptgenerator.h @@ -1,94 +1,101 @@ /* * 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::BreakStatement *) override; virtual bool visit(QQmlJS::AST::CaseClause *) override; virtual bool visit(QQmlJS::AST::ContinueStatement *) override; virtual bool visit(QQmlJS::AST::DefaultClause *) override; + virtual bool visit(QQmlJS::AST::Elision *) override; + virtual bool visit(QQmlJS::AST::FalseLiteral *) override; virtual bool visit(QQmlJS::AST::FormalParameterList *) override; virtual bool visit(QQmlJS::AST::IdentifierExpression *) override; + virtual bool visit(QQmlJS::AST::NullExpression *) override; virtual bool visit(QQmlJS::AST::NumericLiteral *) override; virtual bool visit(QQmlJS::AST::StringLiteral *) override; - + virtual bool visit(QQmlJS::AST::ThisExpression *) override; + virtual bool visit(QQmlJS::AST::TrueLiteral *) override; + + virtual void endVisit(QQmlJS::AST::ArrayLiteral *) override; virtual void endVisit(QQmlJS::AST::BinaryExpression *) override; virtual void endVisit(QQmlJS::AST::Block *) override; virtual void endVisit(QQmlJS::AST::CaseBlock *) override; virtual void endVisit(QQmlJS::AST::CaseClause *) override; virtual void endVisit(QQmlJS::AST::CaseClauses *) override; virtual void endVisit(QQmlJS::AST::DefaultClause *) override; + virtual void endVisit(QQmlJS::AST::ElementList *) override; virtual void endVisit(QQmlJS::AST::EmptyStatement *) override; virtual void endVisit(QQmlJS::AST::ExpressionStatement *) override; virtual void endVisit(QQmlJS::AST::FunctionBody *) override; virtual void endVisit(QQmlJS::AST::FunctionDeclaration *) override; virtual void endVisit(QQmlJS::AST::IdentifierExpression *) override; virtual void endVisit(QQmlJS::AST::IfStatement *) 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::ReturnStatement *) override; virtual void endVisit(QQmlJS::AST::SourceElements *) override; virtual void endVisit(QQmlJS::AST::StringLiteral *) override; virtual void endVisit(QQmlJS::AST::StatementList *) override; virtual void endVisit(QQmlJS::AST::SwitchStatement *) override; virtual void endVisit(QQmlJS::AST::VariableDeclaration *) override; virtual void endVisit(QQmlJS::AST::VariableDeclarationList *) override; virtual void endVisit(QQmlJS::AST::VariableStatement *) override; private: template void reduceListStack(ListType* list, const char* separator = ""); void reduceJumpStatement(const char *keyword, QStringRef label); QStack m_outputStack; friend class TestPureJavaScriptGenerator; }; template void PureJavaScriptGenerator::reduceListStack(ListType* current, const char* separator) { // 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()); if (current->next) { code.prepend(separator); } current = current->next; } m_outputStack << code; } #endif //QMLWEB_PUREJAVASCRIPTGENERATOR_H diff --git a/tests/auto/data/javascript/literals.compiled.js b/tests/auto/data/javascript/literals.compiled.js new file mode 100644 index 0000000..bcb5ddd --- /dev/null +++ b/tests/auto/data/javascript/literals.compiled.js @@ -0,0 +1 @@ +this;null;identifier;true;false;4.1;"string";[];[1,2,];[,,,5,];[5,,,5,];[5,]; \ No newline at end of file diff --git a/tests/auto/data/javascript/literals.js b/tests/auto/data/javascript/literals.js new file mode 100644 index 0000000..7f07a67 --- /dev/null +++ b/tests/auto/data/javascript/literals.js @@ -0,0 +1,12 @@ +this; +null; +identifier; +true; +false; +4.1; +"string"; +[]; +[1, 2,]; +[,,,5,]; +[5,,,5,]; +[5]; \ No newline at end of file diff --git a/tests/auto/qmljsc/testpurejavascriptgenerator.cpp b/tests/auto/qmljsc/testpurejavascriptgenerator.cpp index c24e419..9282edc 100644 --- a/tests/auto/qmljsc/testpurejavascriptgenerator.cpp +++ b/tests/auto/qmljsc/testpurejavascriptgenerator.cpp @@ -1,379 +1,423 @@ /* * 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/utils/error.h" #include "../../../src/qmljsc/purejavascriptgenerator.h" #define TEST_VISIT_PUTS_ON_STACK(className, testSituation, expectedStackContent, instance) \ void test_visit_ ## className ## _ ## testSituation ## _returnsTrue() { \ QCOMPARE(m_generator->visit(&instance), true); \ } \ void test_ ## visit ## _ ## className ## _ ## testSituation ## _putsOnStack() { \ m_generator->visit(&instance); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack.top(), QStringLiteral(expectedStackContent)); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack.size(), 1); \ } #define TEST_VISIT_BINARYOP_PUTS_ON_STACK(operatorEnumName, character) \ void test_visit_BinaryExpression_ ## operatorEnumName ## _returnsTrue() { \ QQmlJS::AST::BinaryExpression classInstance(nullptr, QSOperator::operatorEnumName, nullptr); \ QCOMPARE(m_generator->visit(&classInstance), true); \ } \ void test_visit_BinaryExpression_ ## operatorEnumName ## _putsOnStack() { \ QQmlJS::AST::BinaryExpression instance(nullptr, QSOperator::operatorEnumName, nullptr); \ QStack expectedStack;\ expectedStack.append(character);\ m_generator->visit(&instance); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack, expectedStack); \ } #define TEST_VISIT_DEFAULT_IMPL_(className, instance) \ void test_visit ## _ ## className ## _defaultImplementation_returns_true() { \ QCOMPARE(m_generator->visit(&instance), true); \ } \ void test_ ## visit ## _ ## className ## _ ## testSituation ## _putsNothingOnStack() { \ m_generator->visit(&instance); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack.count(), 0); \ } #define TEST_ENDVISIT_REDUCES(className, scenarioName, expectedTopOfStack, stackContent, instance) \ - void test_endVisit_ ## className ## scenarioName ## _reducesStack() { \ + void test_endVisit_ ## className ## _ ## scenarioName ## _reducesStack() { \ asPureJSGen(m_generator)->m_outputStack.append(QVectorstackContent); \ m_generator->endVisit(&instance); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack.top(), QStringLiteral(expectedTopOfStack)); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack.count(), 1); \ } using namespace QQmlJS; 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_anotherIdentifier("e") , m_anotherIdentifierStringRef(&m_anotherIdentifier) /* Expressions */ + , m_thisExpression() + , m_nullExpression() , m_identifierExpression(m_someIdentifierStringRef) + , m_trueLiteral() + , m_falseLiteral() , m_numericalExpressionPi(3.14) , m_stringLiteral(m_someStringStringRef) - , m_trueExpression() - , m_falseExpression() + , m_twoElisions() + , m_elisionsPart2(&m_twoElisions) + , m_arrayElementsExp(nullptr, &m_trueLiteral) + , m_arrayElementsExpExp(nullptr, &m_trueLiteral) + , m_arrayElementsExpExpPart2(&m_arrayElementsExpExp, nullptr, &m_falseLiteral) + , m_arrayElementsElisionExp(&m_twoElisions, &m_falseLiteral) + , m_arrayElementsExpElisionExp(nullptr, &m_falseLiteral) + , m_arrayLiteralWithElision(&m_arrayElementsExp, &m_twoElisions) + , m_arrayLiteralWithoutElision(&m_arrayElementsExp) + , m_arrayLiteralOnlyElision(&m_twoElisions) + , m_arrayElementsExpElisionExpPart2(&m_arrayElementsExpElisionExp, &m_twoElisions, &m_trueLiteral) , m_equalsBinaryExpression(nullptr, QSOperator::Equal, nullptr) , m_postDecrementExpression(&m_numericalExpressionPi) , m_postIncrementExpression(&m_numericalExpressionPi) , m_preDecrementExpression(&m_numericalExpressionPi) , m_preIncrementExpression(&m_numericalExpressionPi) , m_block(nullptr) /* Variable Declarations */ , m_constDeclaration1(m_someIdentifierStringRef, nullptr) , m_constDeclaration2(m_anotherIdentifierStringRef, nullptr) - , m_variableDeclarationWithAssignment(m_someIdentifierStringRef, &m_trueExpression) + , m_variableDeclarationWithAssignment(m_someIdentifierStringRef, &m_trueLiteral) , m_variableDeclarationWithoutAssignment(m_someIdentifierStringRef, nullptr) , m_twoConstDeclarations(&m_constDeclaration1) , m_twoConstDeclarationsPart2(&m_twoConstDeclarations, &m_constDeclaration2) , m_twoVarDeclarations(&m_variableDeclarationWithAssignment) , m_twoVarDeclarationsPart2(&m_twoVarDeclarations, &m_variableDeclarationWithoutAssignment) /* Statements */ , m_breakStatementWithLabel(m_someLabelStringRef) , m_breakStatementWithoutLabel(nullptr) , m_continueStatementWithLabel(m_someLabelStringRef) , m_continueStatementWithoutLabel(nullptr) , m_emptyStatement() , m_statement1() , m_statement2() , m_statement3() , m_expressionStatement(nullptr) - , m_ifStatementWithoutElse(&m_trueExpression, &m_statement1) - , m_ifStatementWithElse(&m_trueExpression, &m_statement1, &m_statement2) + , m_ifStatementWithoutElse(&m_trueLiteral, &m_statement1) + , m_ifStatementWithElse(&m_trueLiteral, &m_statement1, &m_statement2) , m_returnStatementWithoutValue(nullptr) - , m_returnStatementWithValue(&m_trueExpression) + , m_returnStatementWithValue(&m_trueLiteral) , m_variableStatement(&m_twoVarDeclarations) , 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) /* Function declaration */ , m_twoParameters(m_someIdentifierStringRef) , m_parameterListPart2(&m_twoParameters, m_anotherIdentifierStringRef) , m_functionBody(nullptr) , m_functionDeclarationWithoutParameters(m_someIdentifierStringRef, nullptr, &m_functionBody) , m_functionDeclarationWithParameters(m_someIdentifierStringRef, &m_twoParameters , &m_functionBody) , m_functionDeclarationWithoutBody(m_someIdentifierStringRef, &m_twoParameters, nullptr) /* Switch */ - , m_caseClause1(&m_trueExpression, &m_threeStatementsList) - , m_caseClause2(&m_trueExpression, &m_threeStatementsList) + , m_caseClause1(&m_trueLiteral, &m_threeStatementsList) + , m_caseClause2(&m_trueLiteral, &m_threeStatementsList) , m_twoCaseClauses(&m_caseClause1) , m_twoCaseClausesPart2(&m_twoCaseClauses, &m_caseClause2) , m_defaultClause(&m_threeStatementsList) , m_caseBlock(&m_twoCaseClauses, &m_defaultClause, &m_twoCaseClauses) , m_caseBlockOnlyCases(&m_twoCaseClauses) , m_caseBlockCasesAndDefault(&m_twoCaseClauses, &m_defaultClause) , m_caseBlockCasesDefaultCases(&m_twoCaseClauses, &m_defaultClause, &m_twoCaseClauses) - , m_caseClause(&m_trueExpression, &m_threeStatementsList) - , m_switchStatement(&m_trueExpression, &m_caseBlock) + , m_caseClause(&m_trueLiteral, &m_threeStatementsList) + , m_switchStatement(&m_trueLiteral, &m_caseBlock) { + m_elisionsPart2.finish(); + m_arrayElementsExp.finish(); + m_arrayElementsExpExpPart2.finish(); + m_arrayElementsElisionExp.finish(); + m_arrayElementsExpElisionExpPart2.finish(); m_statementListPart3.finish(); m_sourceElementsListPart3.finish(); m_parameterListPart2.finish(); m_twoConstDeclarationsPart2.finish(true); m_twoVarDeclarationsPart2.finish(false); m_twoCaseClausesPart2.finish(); } private: PureJavaScriptGenerator* asPureJSGen(QQmlJS::AST::Visitor* generator) { return static_cast(generator); } QQmlJS::AST::Visitor *m_generator; const QString m_someLabel; const QStringRef m_someLabelStringRef; const QString m_someString; const QStringRef m_someStringStringRef; const QString m_someIdentifier; const QStringRef m_someIdentifierStringRef; const QString m_anotherIdentifier; const QStringRef m_anotherIdentifierStringRef; /* Expressions */ + AST::ThisExpression m_thisExpression; + AST::NullExpression m_nullExpression; AST::IdentifierExpression m_identifierExpression; + AST::TrueLiteral m_trueLiteral; + AST::FalseLiteral m_falseLiteral; AST::NumericLiteral m_numericalExpressionPi; AST::StringLiteral m_stringLiteral; - AST::TrueLiteral m_trueExpression; - AST::FalseLiteral m_falseExpression; + + AST::Elision m_twoElisions; + AST::Elision m_elisionsPart2; + AST::ElementList m_arrayElementsExp; + AST::ElementList m_arrayElementsExpExp; + AST::ElementList m_arrayElementsExpExpPart2; + AST::ElementList m_arrayElementsElisionExp; + AST::ElementList m_arrayElementsExpElisionExp; + AST::ElementList m_arrayElementsExpElisionExpPart2; + AST::ArrayLiteral m_arrayLiteralWithElision; + AST::ArrayLiteral m_arrayLiteralWithoutElision; + AST::ArrayLiteral m_arrayLiteralOnlyElision; AST::BinaryExpression m_equalsBinaryExpression; AST::PostDecrementExpression m_postDecrementExpression; AST::PostIncrementExpression m_postIncrementExpression; AST::PreDecrementExpression m_preDecrementExpression; AST::PreIncrementExpression m_preIncrementExpression; /* Variable Declarations */ AST::VariableDeclaration m_constDeclaration1; AST::VariableDeclaration m_constDeclaration2; AST::VariableDeclaration m_variableDeclarationWithAssignment; AST::VariableDeclaration m_variableDeclarationWithoutAssignment; AST::VariableDeclarationList m_twoConstDeclarations; AST::VariableDeclarationList m_twoConstDeclarationsPart2; AST::VariableDeclarationList m_twoVarDeclarations; AST::VariableDeclarationList m_twoVarDeclarationsPart2; /* Statements */ AST::BreakStatement m_breakStatementWithLabel; AST::BreakStatement m_breakStatementWithoutLabel; AST::ContinueStatement m_continueStatementWithLabel; AST::ContinueStatement m_continueStatementWithoutLabel; AST::EmptyStatement m_emptyStatement; AST::EmptyStatement m_statement1; AST::EmptyStatement m_statement2; AST::EmptyStatement m_statement3; AST::ExpressionStatement m_expressionStatement; AST::IfStatement m_ifStatementWithoutElse; AST::IfStatement m_ifStatementWithElse; AST::ReturnStatement m_returnStatementWithoutValue; AST::ReturnStatement m_returnStatementWithValue; AST::VariableStatement m_variableStatement; AST::Block m_block; AST::StatementList m_threeStatementsList; AST::StatementList m_statementListPart2; AST::StatementList m_statementListPart3; AST::StatementSourceElement m_sourceElement1; AST::StatementSourceElement m_sourceElement2; AST::StatementSourceElement m_sourceElement3; AST::SourceElements m_threeSourceElementsList; AST::SourceElements m_sourceElementsListPart2; AST::SourceElements m_sourceElementsListPart3; /* Function declarations */ AST::FormalParameterList m_twoParameters; AST::FormalParameterList m_parameterListPart2; AST::FunctionBody m_functionBody; AST::FunctionDeclaration m_functionDeclarationWithoutParameters; AST::FunctionDeclaration m_functionDeclarationWithParameters; AST::FunctionDeclaration m_functionDeclarationWithoutBody; /* Switch */ AST::CaseClause m_caseClause1; AST::CaseClause m_caseClause2; AST::CaseClauses m_twoCaseClauses; AST::CaseClauses m_twoCaseClausesPart2; AST::DefaultClause m_defaultClause; AST::CaseBlock m_caseBlock; AST::CaseBlock m_caseBlockOnlyCases; AST::CaseBlock m_caseBlockCasesAndDefault; AST::CaseBlock m_caseBlockCasesDefaultCases; AST::CaseClause m_caseClause; AST::SwitchStatement m_switchStatement; private slots: void init() { m_generator = new PureJavaScriptGenerator(); }; void cleanup() { delete m_generator; } void testGeneratedCodeIsInitiallyEmpty() { QCOMPARE(asPureJSGen(m_generator)->getGeneratedCode(), QStringLiteral("")); } 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_DEFAULT_IMPL_(Block , m_block) TEST_VISIT_PUTS_ON_STACK(BreakStatement , WithLabel , "break ALabel;" , m_breakStatementWithLabel) TEST_VISIT_PUTS_ON_STACK(BreakStatement , WithoutLabel , "break;" , m_breakStatementWithoutLabel) TEST_VISIT_DEFAULT_IMPL_(CaseBlock , m_caseBlock) TEST_VISIT_PUTS_ON_STACK(CaseClause , AnyCase , "case" , m_caseClause1) TEST_VISIT_DEFAULT_IMPL_(CaseClauses , m_twoCaseClauses) TEST_VISIT_PUTS_ON_STACK(DefaultClause , Default , "default" , m_defaultClause) TEST_VISIT_PUTS_ON_STACK(ContinueStatement , WithLabel , "continue ALabel;", m_continueStatementWithLabel) TEST_VISIT_PUTS_ON_STACK(ContinueStatement , WithoutLabel , "continue;" , m_continueStatementWithoutLabel) + TEST_VISIT_PUTS_ON_STACK(Elision , AnyCase , ",," , m_twoElisions) TEST_VISIT_DEFAULT_IMPL_(EmptyStatement , m_emptyStatement) TEST_VISIT_DEFAULT_IMPL_(ExpressionStatement , m_expressionStatement) TEST_VISIT_PUTS_ON_STACK(FormalParameterList , OneParameter , "e" , m_parameterListPart2) TEST_VISIT_PUTS_ON_STACK(FormalParameterList , TwoParameters , "i,e" , m_twoParameters) + TEST_VISIT_PUTS_ON_STACK(FalseLiteral , AnyCase , "false" , m_falseLiteral) TEST_VISIT_DEFAULT_IMPL_(FunctionBody , m_functionBody) TEST_VISIT_DEFAULT_IMPL_(FunctionDeclaration , m_functionDeclarationWithParameters) TEST_VISIT_PUTS_ON_STACK(IdentifierExpression , AnyCase , "i" , m_identifierExpression) TEST_VISIT_DEFAULT_IMPL_(IfStatement , m_ifStatementWithoutElse) + TEST_VISIT_PUTS_ON_STACK(NullExpression , AnyCase , "null" , m_nullExpression) TEST_VISIT_PUTS_ON_STACK(NumericLiteral , Pi , "3.14" , m_numericalExpressionPi) TEST_VISIT_DEFAULT_IMPL_(PostDecrementExpression , m_postDecrementExpression) TEST_VISIT_DEFAULT_IMPL_(PostIncrementExpression , m_postIncrementExpression) TEST_VISIT_DEFAULT_IMPL_(PreDecrementExpression , m_preDecrementExpression) TEST_VISIT_DEFAULT_IMPL_(PreIncrementExpression , m_preIncrementExpression) TEST_VISIT_DEFAULT_IMPL_(ReturnStatement , m_returnStatementWithoutValue) TEST_VISIT_DEFAULT_IMPL_(SourceElements , m_threeSourceElementsList) TEST_VISIT_DEFAULT_IMPL_(StatementList , m_threeStatementsList) TEST_VISIT_PUTS_ON_STACK(StringLiteral , AnyCase , "\"some string\"" , m_stringLiteral) TEST_VISIT_DEFAULT_IMPL_(SwitchStatement , m_switchStatement) + TEST_VISIT_PUTS_ON_STACK(ThisExpression , AnyCase , "this" , m_thisExpression) + TEST_VISIT_PUTS_ON_STACK(TrueLiteral , AnyCase , "true" , m_trueLiteral) TEST_VISIT_DEFAULT_IMPL_(VariableDeclaration , m_variableDeclarationWithoutAssignment) TEST_VISIT_DEFAULT_IMPL_(VariableDeclarationList , m_twoConstDeclarations) TEST_VISIT_DEFAULT_IMPL_(VariableStatement , m_variableStatement) + TEST_ENDVISIT_REDUCES(ArrayLiteral , OnlyElision , "[,,,]" , ({",,,"}) , m_arrayLiteralOnlyElision) + TEST_ENDVISIT_REDUCES(ArrayLiteral , WithElision , "[5,,,]" , ({"5", ",,,"}) , m_arrayLiteralWithElision) + TEST_ENDVISIT_REDUCES(ArrayLiteral , WithoutElision , "[5]" , ({"5"}) , m_arrayLiteralWithoutElision) TEST_ENDVISIT_REDUCES(BinaryExpression , TwoOperands , "2==4" , ({"==", "2", "4"}) , m_equalsBinaryExpression) TEST_ENDVISIT_REDUCES(Block , AnyCase , "{content}" , ({"content"}) , m_block) TEST_ENDVISIT_REDUCES(CaseBlock , OnlyCases , "{cases;}" , ({"cases;"}) , m_caseBlockOnlyCases) TEST_ENDVISIT_REDUCES(CaseBlock , CasesAndDefault , "{cases;default;}", ({"cases;", "default;"}) , m_caseBlockCasesAndDefault) TEST_ENDVISIT_REDUCES(CaseBlock , CasesDefaultCases , "{cases;default;cases;}", ({"cases;", "default;", "cases;"}), m_caseBlockCasesDefaultCases) TEST_ENDVISIT_REDUCES(CaseClause , CaseWithStatement , "case exp:stm;" , ({"case", "exp", "stm;"}) , m_caseClause) TEST_ENDVISIT_REDUCES(CaseClauses , TwoClauses , "case e:s;case e2:s2;", ({"case e:s;", "case e2:s2;"}), m_twoCaseClauses) TEST_ENDVISIT_REDUCES(DefaultClause , AnyCase , "default:stm" , ({"default", "stm"}) , m_defaultClause) + TEST_ENDVISIT_REDUCES(ElementList , Expression , "expr," , ({"expr"}) , m_arrayElementsExp) + TEST_ENDVISIT_REDUCES(ElementList , TwoExpressions , "expr,expr," , ({"expr", "expr"}) , m_arrayElementsExpExp) + TEST_ENDVISIT_REDUCES(ElementList , ElisionExpression , "elisionexpr," , ({"elision", "expr"}) , m_arrayElementsElisionExp) + TEST_ENDVISIT_REDUCES(ElementList , ExprElisionExpr , "expr,eliexpr," , ({"expr", "eli", "expr"}) , m_arrayElementsExpElisionExp) TEST_ENDVISIT_REDUCES(EmptyStatement , DefaultScenario , ";" , ({}) , m_emptyStatement) TEST_ENDVISIT_REDUCES(ExpressionStatement , AnyCase , "expression;" , ({"expression"}) , m_expressionStatement) TEST_ENDVISIT_REDUCES(FormalParameterList , AnyCase , "i" , ({"i"}) , m_twoParameters) // does nothing TEST_ENDVISIT_REDUCES(FunctionBody , ClosesCorrectly , "{func}" , ({"func"}) , m_functionBody) TEST_ENDVISIT_REDUCES(FunctionDeclaration , BodyNoParameters , "function i(){body}" , ({"{body}"}) , m_functionDeclarationWithoutParameters) TEST_ENDVISIT_REDUCES(FunctionDeclaration , BodyParameters , "function i(para){body}", ({"para", "{body}"}) , m_functionDeclarationWithParameters) TEST_ENDVISIT_REDUCES(FunctionDeclaration , WithoutBody , "function i(para){}" , ({"para"}) , m_functionDeclarationWithoutBody) TEST_ENDVISIT_REDUCES(IdentifierExpression , AnyCase , "abc" , ({"abc"}) , m_identifierExpression) TEST_ENDVISIT_REDUCES(IfStatement , OnlyIf , "if(exp)stm;" , ({"exp", "stm;"}) , m_ifStatementWithoutElse) TEST_ENDVISIT_REDUCES(IfStatement , IfElse , "if(exp)s;else s;", ({"exp", "s;", "s;"}) , m_ifStatementWithElse) TEST_ENDVISIT_REDUCES(NumericLiteral , AnyCase , "2.7" , ({"2.7"}) , m_numericalExpressionPi) TEST_ENDVISIT_REDUCES(PostDecrementExpression , AnyCase , "2.7--" , ({"2.7"}) , m_postDecrementExpression) TEST_ENDVISIT_REDUCES(PostIncrementExpression , AnyCase , "2.7++" , ({"2.7"}) , m_postIncrementExpression) TEST_ENDVISIT_REDUCES(PreDecrementExpression , AnyCase , "--2.7" , ({"2.7"}) , m_preDecrementExpression) TEST_ENDVISIT_REDUCES(PreIncrementExpression , AnyCase , "++2.7" , ({"2.7"}) , m_preIncrementExpression) TEST_ENDVISIT_REDUCES(ReturnStatement , WithoutReturnValue, "return;" , ({}) , m_returnStatementWithoutValue) TEST_ENDVISIT_REDUCES(ReturnStatement , WithReturnValue , "return true;" , ({"true"}) , m_returnStatementWithValue) TEST_ENDVISIT_REDUCES(SourceElements , ThreeSrcElements , "sEl1sEl2sEl3" , ({"sEl1", "sEl2", "sEl3"}) , m_threeSourceElementsList) TEST_ENDVISIT_REDUCES(SourceElements , ThreeStatements , "st1st2st3" , ({"st1", "st2", "st3"}) , m_threeStatementsList) TEST_ENDVISIT_REDUCES(StringLiteral , AnyCase , "another string" , ({"another string"}) , m_stringLiteral) TEST_ENDVISIT_REDUCES(SwitchStatement , AnyCase , "switch(e){blk}" , ({"e", "{blk}"}) , m_switchStatement) TEST_ENDVISIT_REDUCES(VariableDeclaration , WithAssignment , "i=5" , ({"5"}) , m_variableDeclarationWithAssignment) TEST_ENDVISIT_REDUCES(VariableDeclaration , WithoutAssignment , "i" , ({}) , m_variableDeclarationWithoutAssignment) TEST_ENDVISIT_REDUCES(VariableDeclarationList , TwoDeclarations , "var i,e=5" , ({"i", "e=5"}) , m_twoVarDeclarations) TEST_ENDVISIT_REDUCES(VariableDeclarationList , OneDeclaration , "var e=5" , ({"e=5"}) , m_twoVarDeclarationsPart2) TEST_ENDVISIT_REDUCES(VariableDeclarationList , ConstDeclaration , "const e=5" , ({"e=5"}) , m_twoConstDeclarationsPart2) TEST_ENDVISIT_REDUCES(VariableStatement , AnyCase , "x;" , ({"x"}) , m_variableStatement) TEST_VISIT_BINARYOP_PUTS_ON_STACK(Assign, "=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceAdd, "+=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceSub, "-=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceMul, "*=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceDiv, "/=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceMod, "%=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceLeftShift, "<<=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceRightShift, ">>=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceURightShift, ">>>=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceAnd, "&=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceXor, "^=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InplaceOr, "|=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Add, "+") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Sub, "-") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Mul, "*") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Div, "/") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Mod, "%") TEST_VISIT_BINARYOP_PUTS_ON_STACK(LShift, "<<") TEST_VISIT_BINARYOP_PUTS_ON_STACK(RShift, ">>") TEST_VISIT_BINARYOP_PUTS_ON_STACK(URShift, ">>>") TEST_VISIT_BINARYOP_PUTS_ON_STACK(BitAnd, "&") TEST_VISIT_BINARYOP_PUTS_ON_STACK(BitXor, "^") TEST_VISIT_BINARYOP_PUTS_ON_STACK(BitOr, "|") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Equal, "==") TEST_VISIT_BINARYOP_PUTS_ON_STACK(NotEqual, "!=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(StrictEqual, "===") TEST_VISIT_BINARYOP_PUTS_ON_STACK(StrictNotEqual, "!==") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Gt, ">") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Ge, ">=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Lt, "<") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Le, "<=") TEST_VISIT_BINARYOP_PUTS_ON_STACK(And, "&&") TEST_VISIT_BINARYOP_PUTS_ON_STACK(Or, "||") TEST_VISIT_BINARYOP_PUTS_ON_STACK(In, " in ") TEST_VISIT_BINARYOP_PUTS_ON_STACK(InstanceOf, " instanceof ") }; QTEST_MAIN(TestPureJavaScriptGenerator) #include "testpurejavascriptgenerator.moc" diff --git a/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp b/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp index 0a9a000..ba68e96 100644 --- a/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp +++ b/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp @@ -1,111 +1,112 @@ /* * 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 "../../../src/qmljsc/purejavascriptgenerator.h" #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QQmlJS::AST::Node*) class TestPureJavaScriptGeneratorIntegration : public QObject { Q_OBJECT private: QQmlJS::AST::Node* astForFile(QString fileName) { QString input = fileContent(fileName); QQmlJS::Engine* engine = new QQmlJS::Engine(); QQmlJS::Lexer* lexer = new QQmlJS::Lexer(engine); lexer->setCode(input, 1, true); QQmlJS::Parser* parser = new QQmlJS::Parser(engine); if (!parser->parseProgram()) { qDebug() << parser->errorMessage(); } return parser->rootNode(); } QString fileContent(QString fileName) { QFile inputFile(fileName); Q_ASSERT_X(inputFile.open(QFile::ReadOnly), __FILE__, "File could not be opened. Is it added as resource in the CMakeLists.txt?"); return QString(inputFile.readAll()); } void addRowForFileWithCompiled(QString sourceFileName, QString compiledFileName) { const QString folder(":/test/%1.js"); QTest::newRow(sourceFileName.toLocal8Bit()) << astForFile(folder.arg(sourceFileName)) << fileContent(folder.arg(compiledFileName)); } void addRowForFile(QString fileName) { addRowForFileWithCompiled(fileName, fileName); } void addRowForFileWithCompiled(QString fileName) { addRowForFileWithCompiled(fileName, QString("%1.compiled").arg(fileName)); } private slots: void test_compileJavaScriptFile_data() { QTest::addColumn("ast"); QTest::addColumn("expectedOutput"); addRowForFileWithCompiled("declarations"); addRowForFile("expressions"); addRowForFileWithCompiled("functions"); addRowForFileWithCompiled("binaryoperations"); addRowForFileWithCompiled("otherstatements"); addRowForFileWithCompiled("ifstatements"); addRowForFileWithCompiled("switchstatements"); + addRowForFileWithCompiled("literals"); } void test_compileJavaScriptFile() { // Prepare QFETCH(QQmlJS::AST::Node*, ast); QFETCH(QString, expectedOutput); PureJavaScriptGenerator generator; // Do ast->accept(&generator); // Verify QCOMPARE(generator.getGeneratedCode(), expectedOutput); } }; QTEST_MAIN(TestPureJavaScriptGeneratorIntegration) #include "testpurejavascriptgenerator_integration.moc"