diff --git a/src/qmljsc/purejavascriptgenerator.cpp b/src/qmljsc/purejavascriptgenerator.cpp index d6ad248..503f9fb 100644 --- a/src/qmljsc/purejavascriptgenerator.cpp +++ b/src/qmljsc/purejavascriptgenerator.cpp @@ -1,266 +1,278 @@ /* * 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::Block *) { m_outputStack << "{"; return true; } bool PureJavaScriptGenerator::visit(AST::BreakStatement *breakStatement) { QString breakStatementCode("break"); if (breakStatement->label.length() > 0) { breakStatementCode.append(' '); breakStatementCode.append(breakStatement->label.toString()); } m_outputStack << breakStatementCode; 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::FunctionBody *) { m_outputStack << "{"; return true; } bool PureJavaScriptGenerator::visit(AST::FunctionDeclaration *functionDeclaration) { const QString functionName = functionDeclaration->name.toString(); m_outputStack << QString("function") + ' ' + functionName; return true; } bool PureJavaScriptGenerator::visit(AST::IdentifierExpression *identifierExpression) { m_outputStack << identifierExpression->name.toString(); return true; } bool PureJavaScriptGenerator::visit(AST::NumericLiteral *numericLiteral) { m_outputStack.push(QString("%1").arg(numericLiteral->value)); return true; } bool PureJavaScriptGenerator::visit(AST::StringLiteral *stringLiteral) { m_outputStack.push(stringLiteral->value.toString()); return true; } bool PureJavaScriptGenerator::visit(AST::PostDecrementExpression *) { m_outputStack.push("--"); return true; } bool PureJavaScriptGenerator::visit(AST::PostIncrementExpression *) { m_outputStack.push("++"); return true; } bool PureJavaScriptGenerator::visit(AST::PreDecrementExpression *) { m_outputStack.push("--"); return true; } bool PureJavaScriptGenerator::visit(AST::PreIncrementExpression *) { m_outputStack.push("++"); return true; } bool PureJavaScriptGenerator::visit(AST::VariableDeclaration *variableDeclaration) { - QString variableDeclarationCode; const QString identifier = variableDeclaration->name.toString(); - if (variableDeclaration->readOnly) { - variableDeclarationCode += "const"; - } else { - variableDeclarationCode += "var"; - } - variableDeclarationCode += ' '; - variableDeclarationCode += identifier; + QString variableDeclarationCode = identifier; + if (variableDeclaration->expression) { variableDeclarationCode += '='; } m_outputStack << variableDeclarationCode; return true; } +bool PureJavaScriptGenerator::visit(QQmlJS::AST::VariableDeclarationList *variableDeclarationList) { + if (variableDeclarationList->declaration->readOnly) { + // read only state is the same for all declarations in one list + m_outputStack << "const"; + } else { + m_outputStack << "var"; + } + + return true; +} + 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(); const QString openingParenthesis = m_outputStack.pop(); m_outputStack << QString("%1%2}").arg(openingParenthesis, blockCode); } 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(); const QString openingBracket = m_outputStack.pop(); m_outputStack << openingBracket + 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 typeAndName = m_outputStack.pop(); m_outputStack << typeAndName + '(' + parameters + ')' + body; } void PureJavaScriptGenerator::endVisit(AST::IdentifierExpression *) { } void PureJavaScriptGenerator::endVisit(AST::NumericLiteral *) { } void PureJavaScriptGenerator::endVisit(AST::PostDecrementExpression *) { updateStackWithPostOperation(); } void PureJavaScriptGenerator::endVisit(AST::PostIncrementExpression *) { updateStackWithPostOperation(); } void PureJavaScriptGenerator::endVisit(AST::PreDecrementExpression *) { updateStackWithPreOperation(); } void PureJavaScriptGenerator::endVisit(AST::PreIncrementExpression *) { updateStackWithPreOperation(); } 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::VariableDeclaration *declaration) { const QString expression = (declaration->expression)?m_outputStack.pop():""; const QString variableName = m_outputStack.pop(); m_outputStack << variableName + expression; } +void PureJavaScriptGenerator::endVisit(AST::VariableDeclarationList *declarationList) { + reduceListStack(declarationList, ","); + const QString declarationListCode = m_outputStack.pop(); + const QString declarationType = m_outputStack.pop(); + m_outputStack << declarationType + ' ' + declarationListCode; +} + 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 a055e3a..9f100bf 100644 --- a/src/qmljsc/purejavascriptgenerator.h +++ b/src/qmljsc/purejavascriptgenerator.h @@ -1,92 +1,94 @@ /* * 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::FormalParameterList *) 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 bool visit(QQmlJS::AST::VariableDeclarationList *) override; virtual void endVisit(QQmlJS::AST::BinaryExpression *) override; virtual void endVisit(QQmlJS::AST::Block *) 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::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::VariableDeclarationList *) override; virtual void endVisit(QQmlJS::AST::VariableStatement *) override; private: void updateStackWithPostOperation(); void updateStackWithPreOperation(); template void reduceListStack(ListType* list, const char* separator = ""); 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/declarations.compiled.js b/tests/auto/data/javascript/declarations.compiled.js new file mode 100644 index 0000000..db660b9 --- /dev/null +++ b/tests/auto/data/javascript/declarations.compiled.js @@ -0,0 +1 @@ +var i;var i=5;var i,e,f;var i=5,e=2.7,f=3.14;var i=5,e,f;const i=5;const i=5,e=2.7,f=3.14; \ No newline at end of file diff --git a/tests/auto/data/javascript/declarations.js b/tests/auto/data/javascript/declarations.js new file mode 100644 index 0000000..4ea1ceb --- /dev/null +++ b/tests/auto/data/javascript/declarations.js @@ -0,0 +1,7 @@ +var i; +var i = 5; +var i, e, f; +var i = 5, e = 2.7, f = 3.14; +var i = 5, e, f; +const i = 5; +const i = 5, e = 2.7, f = 3.14; \ No newline at end of file diff --git a/tests/auto/data/javascript/expressions.js b/tests/auto/data/javascript/expressions.js index d843e04..b97808a 100644 --- a/tests/auto/data/javascript/expressions.js +++ b/tests/auto/data/javascript/expressions.js @@ -1 +1 @@ -var x;var i=5;var e=i++;e=++i;e=i--;e=--i; \ No newline at end of file +var x;var i;var e=i++;e=++i;e=i--;e=--i; \ No newline at end of file diff --git a/tests/auto/qmljsc/testpurejavascriptgenerator.cpp b/tests/auto/qmljsc/testpurejavascriptgenerator.cpp index d3cbd51..a090814 100644 --- a/tests/auto/qmljsc/testpurejavascriptgenerator.cpp +++ b/tests/auto/qmljsc/testpurejavascriptgenerator.cpp @@ -1,308 +1,319 @@ /* * 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_OBJ(testSituation, expectedStackContent, className, instancePtr) \ void test_ ## visit ## _ ## className ## _putsOnStack_ ## testSituation() { \ QStack expectedStack;\ expectedStack.append(QVectorexpectedStackContent);\ m_generator->visit(instancePtr); \ int stackElementCount = asPureJSGen(m_generator)->m_outputStack.size(); \ QCOMPARE(stackElementCount, expectedStack.size()); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack, expectedStack); \ } #define TEST_SOMEVISIT_PUTS_MULTIPLE_ON_STACK(testSituation, expectedStackContent, visitType, className, ...) \ void test_ ## visitType ## _ ## className ## _putsOnStack_ ## testSituation() { \ QQmlJS::AST::className classInstance(__VA_ARGS__); \ QStack expectedStack;\ expectedStack.append(QVectorexpectedStackContent);\ m_generator->visit(&classInstance); \ int stackElementCount = asPureJSGen(m_generator)->m_outputStack.size(); \ QCOMPARE(stackElementCount, expectedStack.size()); \ QCOMPARE(asPureJSGen(m_generator)->m_outputStack, expectedStack); \ } #define TEST_SOMEVISIT_PUTS_ON_STACK(testSituation, expectedStackContent, visitType, className, ...) \ TEST_SOMEVISIT_PUTS_MULTIPLE_ON_STACK(testSituation, ({expectedStackContent}), visitType, 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_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_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_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, 0); \ } #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, 1); \ QString stringOnStack = asPureJSGen(m_generator)->m_outputStack.top(); \ QCOMPARE(stringOnStack, QStringLiteral(expectedTopOfStack)); \ } #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(classInstancePtr); \ 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_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("")); \ } #define TEST_VISIT_BINARYOP_PUTS_ON_STACK(operatorEnumName, character) \ TEST_VISIT_PUTS_ON_STACK(operatorEnumName ## Operation, character, BinaryExpression, &m_trueExpression, QSOperator::operatorEnumName, &m_falseExpression) 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) , 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_twoParameters(m_someIdentifierStringRef) , m_parameterListPart2(&m_twoParameters, m_anotherIdentifierStringRef) , m_functionBody(nullptr) + , m_constDeclaration1(m_someIdentifierStringRef, nullptr) + , m_constDeclaration2(m_anotherIdentifierStringRef, nullptr) + , m_varDeclaration1(m_someIdentifierStringRef, nullptr) + , m_varDeclaration2(m_anotherIdentifierStringRef, nullptr) + , m_twoConstDeclarations(&m_constDeclaration1) + , m_twoConstDeclarationsPart2(&m_twoConstDeclarations, &m_constDeclaration2) + , m_twoVarDeclarations(&m_varDeclaration1) + , m_twoVarDeclarationsPart2(&m_twoVarDeclarations, &m_varDeclaration2) { m_statementListPart3.finish(); m_sourceElementsListPart3.finish(); m_parameterListPart2.finish(); + m_twoConstDeclarationsPart2.finish(true); + m_twoVarDeclarationsPart2.finish(false); } 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; 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; QQmlJS::AST::FormalParameterList m_twoParameters; QQmlJS::AST::FormalParameterList m_parameterListPart2; QQmlJS::AST::FunctionBody m_functionBody; + QQmlJS::AST::VariableDeclaration m_constDeclaration1; + QQmlJS::AST::VariableDeclaration m_constDeclaration2; + QQmlJS::AST::VariableDeclaration m_varDeclaration1; + QQmlJS::AST::VariableDeclaration m_varDeclaration2; + QQmlJS::AST::VariableDeclarationList m_twoConstDeclarations; + QQmlJS::AST::VariableDeclarationList m_twoConstDeclarationsPart2; + QQmlJS::AST::VariableDeclarationList m_twoVarDeclarations; + QQmlJS::AST::VariableDeclarationList m_twoVarDeclarationsPart2; 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_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 ") 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) //TODO TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(EmptyStatement) TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(ExpressionStatement, nullptr) TEST_VISIT_PUTS_ON_STACK_OBJ(OneParameter, ({"e"}), FormalParameterList, &m_parameterListPart2) TEST_VISIT_PUTS_ON_STACK_OBJ(TwoParameters, ({"i,e"}), FormalParameterList, &m_twoParameters) TEST_VISIT_PUTS_ON_STACK(NoSourceElements, "{", FunctionBody, nullptr) TEST_VISIT_PUTS_ON_STACK(WithParameters, "function i", FunctionDeclaration, m_someIdentifierStringRef, &m_twoParameters, nullptr) TEST_VISIT_PUTS_ON_STACK(WithoutParameters, "function i", FunctionDeclaration, m_someIdentifierStringRef, nullptr, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "i", IdentifierExpression, m_someIdentifierStringRef) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "3.14", NumericLiteral, 3.14) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "--", PostDecrementExpression, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "++", PostIncrementExpression, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "--", PreDecrementExpression, nullptr) TEST_VISIT_PUTS_ON_STACK(DefaultScenario, "++", PreIncrementExpression, nullptr) 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_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_PUTS_ON_STACK(AssignmentScenario, "i=", VariableDeclaration, m_someIdentifierStringRef, &m_trueExpression) + TEST_VISIT_PUTS_ON_STACK(NoAssignmentScenario, "i", VariableDeclaration, m_someIdentifierStringRef, nullptr) + TEST_VISIT_PUTS_ON_STACK(VarDeclarationList, "var", VariableDeclarationList, &m_varDeclaration1) + TEST_VISIT_PUTS_ON_STACK(ConstDelcarationList, "const", VariableDeclarationList, &m_constDeclaration1) TEST_VISIT_IS_DEFAULT_IMPLEMENTATION(VariableStatement, 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_OBJ(DefaultScenario, ";", ({}), EmptyStatement, &m_statement1) TEST_ENDVISIT_REDUCES_STACK(NoExpression, "expression;", ({"expression"}), ExpressionStatement, nullptr) TEST_ENDVISIT_REDUCES_STACK_OBJ(AnyNumberOfParameters, "i", ({"i"}), FormalParameterList, &m_twoParameters) // does nothing TEST_ENDVISIT_REDUCES_STACK(FunctionBodyClosesCorrectly, "{func}", ({"{", "func"}), FunctionBody, nullptr) TEST_ENDVISIT_REDUCES_STACK(WithoutParametersWithBody, "name(){body}", ({"name", "{body}"}), FunctionDeclaration, m_someIdentifierStringRef, nullptr, &m_functionBody) TEST_ENDVISIT_REDUCES_STACK(WithParametersWithBody, "name(parameters){body}", ({"name", "parameters", "{body}"}), FunctionDeclaration, m_someIdentifierStringRef, &m_twoParameters, &m_functionBody) TEST_ENDVISIT_REDUCES_STACK(WithoutBody, "name(parameters){}", ({"name", "parameters"}), FunctionDeclaration, m_someIdentifierStringRef, &m_twoParameters, 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(Assignment, "var x=5", ({"var x=", "5"}), VariableDeclaration, m_someIdentifierStringRef, &m_trueExpression) - TEST_ENDVISIT_REDUCES_STACK(NoAssignment, "var x", ({"var x"}), VariableDeclaration, m_someIdentifierStringRef, nullptr) - TEST_ENDVISIT_REDUCES_STACK(DefaultScenario, "var x;", ({"var x"}), VariableStatement, nullptr) + TEST_ENDVISIT_REDUCES_STACK(Assignment, "x=5", ({"x=", "5"}), VariableDeclaration, m_someIdentifierStringRef, &m_trueExpression) + TEST_ENDVISIT_REDUCES_STACK(NoAssignment, "x", ({"x"}), VariableDeclaration, m_someIdentifierStringRef, nullptr) + TEST_ENDVISIT_REDUCES_STACK_OBJ(TwoVarDeclarations, "var i,e=5", ({"var", "i", "e=5"}), VariableDeclarationList, &m_twoVarDeclarations) + TEST_ENDVISIT_REDUCES_STACK_OBJ(OneVarDeclaration, "var e=5", ({"var", "e=5"}), VariableDeclarationList, &m_twoVarDeclarationsPart2) + TEST_ENDVISIT_REDUCES_STACK(DefaultScenario, "x;", ({"x"}), VariableStatement, nullptr) }; QTEST_MAIN(TestPureJavaScriptGenerator) #include "testpurejavascriptgenerator.moc" diff --git a/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp b/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp index e5081bb..7488530 100644 --- a/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp +++ b/tests/auto/qmljsc/testpurejavascriptgenerator_integration.cpp @@ -1,108 +1,107 @@ /* * 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("statements"); } 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"