2016-12-30 15:11:20 +00:00
/****************************************************************************
* *
* * Copyright ( C ) 2016 The Qt Company Ltd .
* * Contact : https : //www.qt.io/licensing/
* *
* * This file is part of the QtQml module of the Qt Toolkit .
* *
* * $ QT_BEGIN_LICENSE : GPL - EXCEPT $
* * Commercial License Usage
* * Licensees holding valid commercial Qt licenses may use this file in
* * accordance with the commercial license agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
* * a written agreement between you and The Qt Company . For licensing terms
* * and conditions see https : //www.qt.io/terms-conditions. For further
* * information use the contact form at https : //www.qt.io/contact-us.
* *
* * GNU General Public License Usage
* * Alternatively , this file may be used under the terms of the GNU
* * General Public License version 3 as published by the Free Software
* * Foundation with exceptions as appearing in the file LICENSE . GPL3 - EXCEPT
* * included in the packaging of this file . Please review the following
* * information to ensure the GNU General Public License requirements will
* * be met : https : //www.gnu.org/licenses/gpl-3.0.html.
* *
* * $ QT_END_LICENSE $
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <QCoreApplication>
# include <QStringList>
# include <QCommandLineParser>
# include <QFile>
# include <QFileInfo>
# include <QDateTime>
2017-03-14 18:26:14 +00:00
# include <QHashFunctions>
2016-12-30 15:11:20 +00:00
# include <private/qqmlirbuilder_p.h>
# include <private/qqmljsparser_p.h>
struct Error
{
QString message ;
void print ( ) ;
Error augment ( const QString & contextErrorMessage ) const ;
} ;
void Error : : print ( )
{
fprintf ( stderr , " %s \n " , qPrintable ( message ) ) ;
}
Error Error : : augment ( const QString & contextErrorMessage ) const
{
Error augmented ;
augmented . message = contextErrorMessage + message ;
return augmented ;
}
QString diagnosticErrorMessage ( const QString & fileName , const QQmlJS : : DiagnosticMessage & m )
{
QString message ;
message = fileName + QLatin1Char ( ' : ' ) + QString : : number ( m . loc . startLine ) + QLatin1Char ( ' : ' ) ;
if ( m . loc . startColumn > 0 )
message + = QString : : number ( m . loc . startColumn ) + QLatin1Char ( ' : ' ) ;
if ( m . isError ( ) )
message + = QLatin1String ( " error: " ) ;
else
message + = QLatin1String ( " warning: " ) ;
message + = m . message ;
return message ;
}
2017-03-10 08:17:35 +00:00
// Ensure that ListElement objects keep all property assignments in their string form
static void annotateListElements ( QmlIR : : Document * document )
{
QStringList listElementNames ;
foreach ( const QV4 : : CompiledData : : Import * import , document - > imports ) {
const QString uri = document - > stringAt ( import - > uriIndex ) ;
if ( uri ! = QStringLiteral ( " QtQml.Models " ) & & uri ! = QStringLiteral ( " QtQuick " ) )
continue ;
QString listElementName = QStringLiteral ( " ListElement " ) ;
const QString qualifier = document - > stringAt ( import - > qualifierIndex ) ;
if ( ! qualifier . isEmpty ( ) ) {
listElementName . prepend ( QLatin1Char ( ' . ' ) ) ;
listElementName . prepend ( qualifier ) ;
}
listElementNames . append ( listElementName ) ;
}
if ( listElementNames . isEmpty ( ) )
return ;
foreach ( QmlIR : : Object * object , document - > objects ) {
if ( ! listElementNames . contains ( document - > stringAt ( object - > inheritedTypeNameIndex ) ) )
continue ;
for ( QmlIR : : Binding * binding = object - > firstBinding ( ) ; binding ; binding = binding - > next ) {
if ( binding - > type ! = QV4 : : CompiledData : : Binding : : Type_Script )
continue ;
binding - > stringIndex = document - > registerString ( object - > bindingAsString ( document , binding - > value . compiledScriptIndex ) ) ;
}
}
}
2017-06-22 08:01:17 +00:00
static bool compileQmlFile ( const QString & inputFileName , const QString & outputFileName , const QString & targetABI , Error * error )
2016-12-30 15:11:20 +00:00
{
QmlIR : : Document irDocument ( /*debugMode*/ false ) ;
2017-04-05 12:47:34 +00:00
irDocument . jsModule . targetABI = targetABI ;
2016-12-30 15:11:20 +00:00
QString sourceCode ;
{
QFile f ( inputFileName ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
error - > message = QLatin1String ( " Error opening " ) + inputFileName + QLatin1Char ( ' : ' ) + f . errorString ( ) ;
return false ;
}
sourceCode = QString : : fromUtf8 ( f . readAll ( ) ) ;
if ( f . error ( ) ! = QFileDevice : : NoError ) {
error - > message = QLatin1String ( " Error reading from " ) + inputFileName + QLatin1Char ( ' : ' ) + f . errorString ( ) ;
return false ;
}
}
{
QSet < QString > illegalNames ; // ####
QmlIR : : IRBuilder irBuilder ( illegalNames ) ;
if ( ! irBuilder . generateFromQml ( sourceCode , inputFileName , & irDocument ) ) {
for ( const QQmlJS : : DiagnosticMessage & parseError : qAsConst ( irBuilder . errors ) ) {
if ( ! error - > message . isEmpty ( ) )
error - > message + = QLatin1Char ( ' \n ' ) ;
error - > message + = diagnosticErrorMessage ( inputFileName , parseError ) ;
}
return false ;
}
}
2017-03-10 08:17:35 +00:00
annotateListElements ( & irDocument ) ;
2016-12-30 15:11:20 +00:00
{
2017-06-22 08:01:17 +00:00
QmlIR : : JSCodeGen v4CodeGen ( irDocument . code ,
2017-06-12 08:27:54 +00:00
& irDocument . jsGenerator , & irDocument . jsModule ,
& irDocument . jsParserEngine , irDocument . program ,
/*import cache*/ 0 , & irDocument . jsGenerator . stringTable ) ;
2017-07-07 09:40:30 +00:00
v4CodeGen . setUseFastLookups ( false ) ; // Disable lookups in non-standalone (aka QML) mode
2016-12-30 15:11:20 +00:00
for ( QmlIR : : Object * object : qAsConst ( irDocument . objects ) ) {
if ( object - > functionsAndExpressions - > count = = 0 )
continue ;
QList < QmlIR : : CompiledFunctionOrExpression > functionsToCompile ;
for ( QmlIR : : CompiledFunctionOrExpression * foe = object - > functionsAndExpressions - > first ; foe ; foe = foe - > next ) {
foe - > disableAcceleratedLookups = true ;
functionsToCompile < < * foe ;
}
const QVector < int > runtimeFunctionIndices = v4CodeGen . generateJSCodeForFunctionsAndBindings ( functionsToCompile ) ;
QList < QQmlJS : : DiagnosticMessage > jsErrors = v4CodeGen . errors ( ) ;
if ( ! jsErrors . isEmpty ( ) ) {
for ( const QQmlJS : : DiagnosticMessage & e : qAsConst ( jsErrors ) ) {
if ( ! error - > message . isEmpty ( ) )
error - > message + = QLatin1Char ( ' \n ' ) ;
error - > message + = diagnosticErrorMessage ( inputFileName , e ) ;
}
return false ;
}
QQmlJS : : MemoryPool * pool = irDocument . jsParserEngine . pool ( ) ;
object - > runtimeFunctionIndices . allocate ( pool , runtimeFunctionIndices ) ;
}
QmlIR : : QmlUnitGenerator generator ;
2017-06-22 08:01:17 +00:00
irDocument . javaScriptCompilationUnit = v4CodeGen . generateCompilationUnit ( /*generate unit*/ false ) ;
2017-03-15 09:09:54 +00:00
QV4 : : CompiledData : : Unit * unit = generator . generate ( irDocument ) ;
2016-12-30 15:11:20 +00:00
unit - > flags | = QV4 : : CompiledData : : Unit : : StaticData ;
2017-01-03 12:19:15 +00:00
unit - > flags | = QV4 : : CompiledData : : Unit : : PendingTypeCompilation ;
2016-12-30 15:11:20 +00:00
irDocument . javaScriptCompilationUnit - > data = unit ;
2017-03-02 14:52:38 +00:00
if ( ! irDocument . javaScriptCompilationUnit - > saveToDisk ( outputFileName , & error - > message ) )
2016-12-30 15:11:20 +00:00
return false ;
free ( unit ) ;
}
return true ;
}
2017-06-22 08:01:17 +00:00
static bool compileJSFile ( const QString & inputFileName , const QString & outputFileName , const QString & targetABI , Error * error )
2016-12-30 15:11:20 +00:00
{
QmlIR : : Document irDocument ( /*debugMode*/ false ) ;
2017-04-05 12:47:34 +00:00
irDocument . jsModule . targetABI = targetABI ;
2016-12-30 15:11:20 +00:00
QString sourceCode ;
{
QFile f ( inputFileName ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
error - > message = QLatin1String ( " Error opening " ) + inputFileName + QLatin1Char ( ' : ' ) + f . errorString ( ) ;
return false ;
}
sourceCode = QString : : fromUtf8 ( f . readAll ( ) ) ;
if ( f . error ( ) ! = QFileDevice : : NoError ) {
error - > message = QLatin1String ( " Error reading from " ) + inputFileName + QLatin1Char ( ' : ' ) + f . errorString ( ) ;
return false ;
}
}
QQmlJS : : Engine * engine = & irDocument . jsParserEngine ;
QmlIR : : ScriptDirectivesCollector directivesCollector ( engine , & irDocument . jsGenerator ) ;
QQmlJS : : Directives * oldDirs = engine - > directives ( ) ;
engine - > setDirectives ( & directivesCollector ) ;
QQmlJS : : AST : : Program * program = nullptr ;
{
QQmlJS : : Lexer lexer ( engine ) ;
lexer . setCode ( sourceCode , /*line*/ 1 , /*parseAsBinding*/ false ) ;
QQmlJS : : Parser parser ( engine ) ;
bool parsed = parser . parseProgram ( ) ;
for ( const QQmlJS : : DiagnosticMessage & parseError : parser . diagnosticMessages ( ) ) {
if ( ! error - > message . isEmpty ( ) )
error - > message + = QLatin1Char ( ' \n ' ) ;
error - > message + = diagnosticErrorMessage ( inputFileName , parseError ) ;
}
if ( ! parsed ) {
engine - > setDirectives ( oldDirs ) ;
return false ;
}
program = QQmlJS : : AST : : cast < QQmlJS : : AST : : Program * > ( parser . rootNode ( ) ) ;
if ( ! program ) {
lexer . setCode ( QStringLiteral ( " undefined; " ) , 1 , false ) ;
parsed = parser . parseProgram ( ) ;
Q_ASSERT ( parsed ) ;
program = QQmlJS : : AST : : cast < QQmlJS : : AST : : Program * > ( parser . rootNode ( ) ) ;
Q_ASSERT ( program ) ;
}
}
{
2017-06-22 08:01:17 +00:00
irDocument . jsModule . fileName = inputFileName ;
QmlIR : : JSCodeGen v4CodeGen ( irDocument . code , & irDocument . jsGenerator ,
2017-06-12 08:27:54 +00:00
& irDocument . jsModule , & irDocument . jsParserEngine ,
irDocument . program , /*import cache*/ 0 ,
& irDocument . jsGenerator . stringTable ) ;
2017-07-07 09:40:30 +00:00
v4CodeGen . setUseFastLookups ( false ) ; // Disable lookups in non-standalone (aka QML) mode
2017-06-30 13:34:12 +00:00
v4CodeGen . generateFromProgram ( inputFileName , sourceCode , program , & irDocument . jsModule , QV4 : : Compiler : : GlobalCode ) ;
2016-12-30 15:11:20 +00:00
QList < QQmlJS : : DiagnosticMessage > jsErrors = v4CodeGen . errors ( ) ;
if ( ! jsErrors . isEmpty ( ) ) {
for ( const QQmlJS : : DiagnosticMessage & e : qAsConst ( jsErrors ) ) {
if ( ! error - > message . isEmpty ( ) )
error - > message + = QLatin1Char ( ' \n ' ) ;
error - > message + = diagnosticErrorMessage ( inputFileName , e ) ;
}
engine - > setDirectives ( oldDirs ) ;
return false ;
}
QmlIR : : QmlUnitGenerator generator ;
2017-06-22 08:01:17 +00:00
irDocument . javaScriptCompilationUnit = v4CodeGen . generateCompilationUnit ( /*generate unit*/ false ) ;
2017-03-15 09:09:54 +00:00
QV4 : : CompiledData : : Unit * unit = generator . generate ( irDocument ) ;
2016-12-30 15:11:20 +00:00
unit - > flags | = QV4 : : CompiledData : : Unit : : StaticData ;
irDocument . javaScriptCompilationUnit - > data = unit ;
2017-03-02 14:52:38 +00:00
if ( ! irDocument . javaScriptCompilationUnit - > saveToDisk ( outputFileName , & error - > message ) ) {
2016-12-30 15:11:20 +00:00
engine - > setDirectives ( oldDirs ) ;
return false ;
}
free ( unit ) ;
}
engine - > setDirectives ( oldDirs ) ;
return true ;
}
int main ( int argc , char * * argv )
{
// Produce reliably the same output for the same input by disabling QHash's random seeding.
2017-03-14 18:26:14 +00:00
qSetGlobalQHashSeed ( 0 ) ;
2016-12-30 15:11:20 +00:00
QCoreApplication app ( argc , argv ) ;
QCoreApplication : : setApplicationName ( QStringLiteral ( " qmlcachegen " ) ) ;
QCoreApplication : : setApplicationVersion ( QLatin1String ( QT_VERSION_STR ) ) ;
QCommandLineParser parser ;
parser . addHelpOption ( ) ;
parser . addVersionOption ( ) ;
2017-01-30 14:40:42 +00:00
QCommandLineOption targetArchitectureOption ( QStringLiteral ( " target-architecture " ) , QCoreApplication : : translate ( " main " , " Target architecture " ) , QCoreApplication : : translate ( " main " , " architecture " ) ) ;
parser . addOption ( targetArchitectureOption ) ;
2017-04-05 12:47:34 +00:00
QCommandLineOption targetABIOption ( QStringLiteral ( " target-abi " ) , QCoreApplication : : translate ( " main " , " Target architecture binary interface " ) , QCoreApplication : : translate ( " main " , " abi " ) ) ;
parser . addOption ( targetABIOption ) ;
2017-03-02 14:52:38 +00:00
QCommandLineOption outputFileOption ( QStringLiteral ( " o " ) , QCoreApplication : : translate ( " main " , " Output file name " ) , QCoreApplication : : translate ( " main " , " file name " ) ) ;
parser . addOption ( outputFileOption ) ;
2017-03-03 09:35:13 +00:00
QCommandLineOption checkIfSupportedOption ( QStringLiteral ( " check-if-supported " ) , QCoreApplication : : translate ( " main " , " Check if cache generate is supported on the specified target architecture " ) ) ;
parser . addOption ( checkIfSupportedOption ) ;
2016-12-30 15:11:20 +00:00
parser . addPositionalArgument ( QStringLiteral ( " [qml file] " ) ,
QStringLiteral ( " QML source file to generate cache for. " ) ) ;
parser . process ( app ) ;
2017-03-03 09:35:13 +00:00
if ( ! parser . isSet ( targetArchitectureOption ) ) {
fprintf ( stderr , " Target architecture not specified. Please specify with --target-architecture=<arch> \n " ) ;
2017-02-23 16:13:43 +00:00
parser . showHelp ( ) ;
2016-12-30 15:11:20 +00:00
return EXIT_FAILURE ;
}
2017-06-22 08:01:17 +00:00
// if (parser.isSet(checkIfSupportedOption)) {
// if (isel.isNull())
// return EXIT_FAILURE;
// else
// return EXIT_SUCCESS;
// }
2017-03-03 09:35:13 +00:00
const QStringList sources = parser . positionalArguments ( ) ;
if ( sources . isEmpty ( ) ) {
parser . showHelp ( ) ;
} else if ( sources . count ( ) > 1 ) {
fprintf ( stderr , " %s \n " , qPrintable ( QStringLiteral ( " Too many input files specified: ' " ) + sources . join ( QStringLiteral ( " ' ' " ) ) + QLatin1Char ( ' \' ' ) ) ) ;
return EXIT_FAILURE ;
}
const QString inputFile = sources . first ( ) ;
2016-12-30 15:11:20 +00:00
Error error ;
2017-03-02 14:52:38 +00:00
QString outputFileName = inputFile + QLatin1Char ( ' c ' ) ;
if ( parser . isSet ( outputFileOption ) )
outputFileName = parser . value ( outputFileOption ) ;
2017-04-05 12:47:34 +00:00
const QString targetABI = parser . value ( targetABIOption ) ;
2016-12-30 15:11:20 +00:00
if ( inputFile . endsWith ( QLatin1String ( " .qml " ) ) ) {
2017-06-22 08:01:17 +00:00
if ( ! compileQmlFile ( inputFile , outputFileName , targetABI , & error ) ) {
2016-12-30 15:11:20 +00:00
error . augment ( QLatin1String ( " Error compiling qml file: " ) ) . print ( ) ;
return EXIT_FAILURE ;
}
} else if ( inputFile . endsWith ( QLatin1String ( " .js " ) ) ) {
2017-06-22 08:01:17 +00:00
if ( ! compileJSFile ( inputFile , outputFileName , targetABI , & error ) ) {
2016-12-30 15:11:20 +00:00
error . augment ( QLatin1String ( " Error compiling qml file: " ) ) . print ( ) ;
return EXIT_FAILURE ;
}
} else {
fprintf ( stderr , " Ignoring %s input file as it is not QML source code - maybe remove from QML_FILES? \n " , qPrintable ( inputFile ) ) ; }
return EXIT_SUCCESS ;
}