2010-07-09 15:47:07 +02:00
/**************************************************************************
* *
* * This file is part of Qt Creator
* *
2011-01-11 16:28:15 +01:00
* * Copyright ( c ) 2011 Nokia Corporation and / or its subsidiary ( - ies ) .
2010-07-09 15:47:07 +02:00
* *
* * Contact : Nokia Corporation ( qt - info @ nokia . com )
* *
2010-12-17 16:01:08 +01:00
* * No Commercial Usage
2010-07-09 15:47:07 +02:00
* *
2010-12-17 16:01:08 +01:00
* * This file contains pre - release code and may not be distributed .
* * You may use this file in accordance with the terms and conditions
* * contained in the Technology Preview License Agreement accompanying
* * this package .
2010-07-09 15:47:07 +02:00
* *
* * GNU Lesser General Public License Usage
* *
* * Alternatively , this file may be used under the terms of the GNU Lesser
* * General Public License version 2.1 as published by the Free Software
* * Foundation and appearing in the file LICENSE . LGPL included in the
* * packaging of this file . Please review the following information to
* * ensure the GNU Lesser General Public License version 2.1 requirements
* * will be met : http : //www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
* *
2010-12-17 16:01:08 +01:00
* * In addition , as a special exception , Nokia gives you certain additional
* * rights . These rights are described in the Nokia Qt LGPL Exception
* * version 1.1 , included in the file LGPL_EXCEPTION . txt in this package .
* *
* * If you have questions regarding the use of this file , please contact
* * Nokia at qt - info @ nokia . com .
2010-07-09 15:47:07 +02:00
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-01-28 14:53:53 +01:00
# include "qmljslink.h"
# include "parser/qmljsast_p.h"
# include "qmljsdocument.h"
# include "qmljsbind.h"
2010-09-16 15:29:37 +02:00
# include "qmljscheck.h"
2010-02-19 15:55:11 +01:00
# include "qmljsscopebuilder.h"
2010-08-25 14:15:57 +02:00
# include "qmljsmodelmanagerinterface.h"
2010-01-28 14:53:53 +01:00
# include <QtCore/QFileInfo>
# include <QtCore/QDir>
# include <QtCore/QDebug>
2011-02-25 16:16:37 +01:00
# include <limits>
2010-12-03 11:17:25 +01:00
using namespace LanguageUtils ;
2010-01-28 14:53:53 +01:00
using namespace QmlJS ;
using namespace QmlJS : : Interpreter ;
using namespace QmlJS : : AST ;
2010-09-16 15:29:37 +02:00
namespace {
class ImportCacheKey
{
public :
explicit ImportCacheKey ( const Interpreter : : ImportInfo & info )
: type ( info . type ( ) )
, name ( info . name ( ) )
, majorVersion ( info . version ( ) . majorVersion ( ) )
, minorVersion ( info . version ( ) . minorVersion ( ) )
{ }
int type ;
QString name ;
int majorVersion ;
int minorVersion ;
} ;
uint qHash ( const ImportCacheKey & info )
{
return : : qHash ( info . type ) ^ : : qHash ( info . name ) ^
: : qHash ( info . majorVersion ) ^ : : qHash ( info . minorVersion ) ;
}
bool operator = = ( const ImportCacheKey & i1 , const ImportCacheKey & i2 )
{
return i1 . type = = i2 . type
& & i1 . name = = i2 . name
& & i1 . majorVersion = = i2 . majorVersion
& & i1 . minorVersion = = i2 . minorVersion ;
}
}
class QmlJS : : LinkPrivate
{
public :
Document : : Ptr doc ;
Snapshot snapshot ;
Interpreter : : Context * context ;
QStringList importPaths ;
QHash < ImportCacheKey , Interpreter : : ObjectValue * > importCache ;
QList < DiagnosticMessage > diagnosticMessages ;
} ;
2010-09-15 15:25:59 +02:00
/*!
\ class QmlJS : : Link
\ brief Initializes the Context for a Document .
\ sa QmlJS : : Document QmlJS : : Interpreter : : Context
Initializes a context by resolving imports and building the root scope
chain . Currently , this is a expensive operation .
It ' s recommended to use a the \ l { LookupContext } returned by
\ l { QmlJSEditor : : SemanticInfo : : lookupContext ( ) } instead of building a new
\ l { Context } with \ l { Link } .
*/
2010-03-29 11:32:11 +02:00
Link : : Link ( Context * context , const Document : : Ptr & doc , const Snapshot & snapshot ,
2010-03-16 16:34:33 +01:00
const QStringList & importPaths )
2010-09-16 15:29:37 +02:00
: d_ptr ( new LinkPrivate )
2010-02-02 15:55:17 +01:00
{
2010-09-16 15:29:37 +02:00
Q_D ( Link ) ;
d - > context = context ;
d - > doc = doc ;
d - > snapshot = snapshot ;
d - > importPaths = importPaths ;
2011-01-06 10:01:45 +01:00
// populate engine with types from C++
ModelManagerInterface * modelManager = ModelManagerInterface : : instance ( ) ;
if ( modelManager ) {
foreach ( const QList < FakeMetaObject : : ConstPtr > & cppTypes , modelManager - > cppQmlTypes ( ) ) {
engine ( ) - > cppQmlTypes ( ) . load ( engine ( ) , cppTypes ) ;
}
}
2010-02-02 15:55:17 +01:00
linkImports ( ) ;
}
Link : : ~ Link ( )
{
}
2010-02-03 10:59:52 +01:00
Interpreter : : Engine * Link : : engine ( )
2010-02-02 15:55:17 +01:00
{
2010-09-16 15:29:37 +02:00
Q_D ( Link ) ;
return d - > context - > engine ( ) ;
2010-02-03 10:59:52 +01:00
}
2010-02-02 15:55:17 +01:00
2010-03-25 14:47:28 +01:00
QList < DiagnosticMessage > Link : : diagnosticMessages ( ) const
{
2010-09-16 15:29:37 +02:00
Q_D ( const Link ) ;
return d - > diagnosticMessages ;
2010-03-25 14:47:28 +01:00
}
2010-02-02 15:55:17 +01:00
void Link : : linkImports ( )
{
2010-09-16 15:29:37 +02:00
Q_D ( Link ) ;
2010-02-02 15:55:17 +01:00
2010-09-16 15:29:37 +02:00
// do it on d->doc first, to make sure import errors are shown
TypeEnvironment * typeEnv = new TypeEnvironment ( engine ( ) ) ;
populateImportedTypes ( typeEnv , d - > doc ) ;
d - > context - > setTypeEnvironment ( d - > doc . data ( ) , typeEnv ) ;
2010-02-04 09:44:43 +01:00
2010-09-16 15:29:37 +02:00
foreach ( Document : : Ptr doc , d - > snapshot ) {
if ( doc = = d - > doc )
continue ;
TypeEnvironment * typeEnv = new TypeEnvironment ( engine ( ) ) ;
populateImportedTypes ( typeEnv , doc ) ;
d - > context - > setTypeEnvironment ( doc . data ( ) , typeEnv ) ;
2010-02-02 15:55:17 +01:00
}
}
2010-09-16 15:29:37 +02:00
void Link : : populateImportedTypes ( TypeEnvironment * typeEnv , Document : : Ptr doc )
2010-01-28 14:53:53 +01:00
{
2010-09-16 15:29:37 +02:00
Q_D ( Link ) ;
2010-12-03 10:13:15 +01:00
if ( ! doc - > qmlProgram ( ) )
2010-01-28 14:53:53 +01:00
return ;
2010-12-03 10:13:15 +01:00
// implicit imports: the <default> package is always available
2011-02-25 16:16:37 +01:00
loadImplicitDefaultImports ( typeEnv ) ;
2010-12-03 10:13:15 +01:00
2010-01-29 13:21:50 +01:00
// implicit imports:
// qml files in the same directory are available without explicit imports
2011-02-25 16:16:37 +01:00
loadImplicitDirectoryImports ( typeEnv , doc ) ;
2010-09-16 15:29:37 +02:00
// explicit imports, whether directories, files or libraries
foreach ( const ImportInfo & info , doc - > bind ( ) - > imports ( ) ) {
ObjectValue * import = d - > importCache . value ( ImportCacheKey ( info ) ) ;
2011-04-06 09:19:46 +02:00
//### Hack: if this document is in a library, and if there is an qmldir file in the same directory, and if the prefix is an import-path, the import means to import everything in this library.
if ( info . ast ( ) & & info . ast ( ) - > fileName & & info . ast ( ) - > fileName - > asString ( ) = = QLatin1String ( " . " ) ) {
const QString importInfoName ( info . name ( ) ) ;
if ( QFileInfo ( QDir ( importInfoName ) , QLatin1String ( " qmldir " ) ) . exists ( ) ) {
foreach ( const QString & importPath , d - > importPaths ) {
if ( importInfoName . startsWith ( importPath ) ) {
// Got it.
const QString cleanPath = QFileInfo ( importInfoName ) . canonicalFilePath ( ) ;
const QString forcedPackageName = cleanPath . mid ( importPath . size ( ) + 1 ) . replace ( ' / ' , ' . ' ) . replace ( ' \\ ' , ' . ' ) ;
import = importNonFile ( doc , info , forcedPackageName ) ;
if ( import )
d - > importCache . insert ( ImportCacheKey ( info ) , import ) ;
break ;
}
}
}
}
//### End of hack.
2010-09-16 15:29:37 +02:00
if ( ! import ) {
switch ( info . type ( ) ) {
case ImportInfo : : FileImport :
case ImportInfo : : DirectoryImport :
2011-03-10 14:49:38 +01:00
import = importFileOrDirectory ( doc , info ) ;
2010-09-16 15:29:37 +02:00
break ;
case ImportInfo : : LibraryImport :
import = importNonFile ( doc , info ) ;
break ;
default :
break ;
}
if ( import )
d - > importCache . insert ( ImportCacheKey ( info ) , import ) ;
2010-02-02 15:55:17 +01:00
}
2010-09-16 15:29:37 +02:00
if ( import )
typeEnv - > addImport ( import , info ) ;
2010-02-02 15:55:17 +01:00
}
}
2010-01-28 14:53:53 +01:00
2010-02-02 15:55:17 +01:00
/*
import " content "
import " content " as Xxx
import " content " 4.6
import " content " 4.6 as Xxx
2010-01-29 13:36:07 +01:00
2010-02-02 15:55:17 +01:00
import " http://www.ovi.com/ " as Ovi
*/
2011-03-10 14:49:38 +01:00
ObjectValue * Link : : importFileOrDirectory ( Document : : Ptr doc , const ImportInfo & importInfo )
2010-02-02 15:55:17 +01:00
{
2010-09-16 15:29:37 +02:00
Q_D ( Link ) ;
2010-01-28 14:53:53 +01:00
2010-09-16 15:29:37 +02:00
ObjectValue * import = 0 ;
const QString & path = importInfo . name ( ) ;
2010-01-29 13:36:07 +01:00
2010-09-16 15:29:37 +02:00
if ( importInfo . type ( ) = = ImportInfo : : DirectoryImport
| | importInfo . type ( ) = = ImportInfo : : ImplicitDirectoryImport ) {
import = new ObjectValue ( engine ( ) ) ;
2011-03-10 14:49:38 +01:00
importLibrary ( doc , import , path , importInfo ) ;
2010-09-16 15:29:37 +02:00
const QList < Document : : Ptr > & documentsInDirectory = d - > snapshot . documentsInDirectory ( path ) ;
2010-04-01 11:27:49 +02:00
foreach ( Document : : Ptr importedDoc , documentsInDirectory ) {
2010-05-20 14:00:19 +02:00
if ( importedDoc - > bind ( ) - > rootObjectValue ( ) ) {
const QString targetName = importedDoc - > componentName ( ) ;
2010-09-16 15:29:37 +02:00
import - > setProperty ( targetName , importedDoc - > bind ( ) - > rootObjectValue ( ) ) ;
2010-05-20 14:00:19 +02:00
}
2010-03-16 16:34:33 +01:00
}
2010-09-16 15:29:37 +02:00
} else if ( importInfo . type ( ) = = ImportInfo : : FileImport ) {
Document : : Ptr importedDoc = d - > snapshot . document ( path ) ;
2010-09-24 09:49:35 +02:00
if ( importedDoc )
import = importedDoc - > bind ( ) - > rootObjectValue ( ) ;
2010-03-16 16:34:33 +01:00
}
2010-02-02 15:55:17 +01:00
2010-09-16 15:29:37 +02:00
return import ;
2010-03-25 14:47:28 +01:00
}
2010-02-02 15:55:17 +01:00
/*
import Qt 4.6
import Qt 4.6 as Xxx
( import com . nokia . qt is the same as the ones above )
*/
2011-04-06 09:19:46 +02:00
ObjectValue * Link : : importNonFile ( Document : : Ptr doc , const ImportInfo & importInfo , const QString & forcedPackageName )
2010-02-02 15:55:17 +01:00
{
2010-09-16 15:29:37 +02:00
Q_D ( Link ) ;
2010-02-02 15:55:17 +01:00
2010-09-16 15:29:37 +02:00
ObjectValue * import = new ObjectValue ( engine ( ) ) ;
2011-04-06 09:19:46 +02:00
const QString packageName = forcedPackageName . isEmpty ( ) ? Bind : : toString ( importInfo . ast ( ) - > importUri , ' . ' ) : forcedPackageName ;
2010-09-16 15:29:37 +02:00
const ComponentVersion version = importInfo . version ( ) ;
2010-03-01 13:01:05 +01:00
2010-06-09 14:27:30 +02:00
bool importFound = false ;
2010-03-16 16:34:33 +01:00
2010-06-09 14:27:30 +02:00
// check the filesystem
2010-09-16 15:29:37 +02:00
const QString & packagePath = importInfo . name ( ) ;
foreach ( const QString & importPath , d - > importPaths ) {
2010-09-08 15:10:35 +02:00
QString libraryPath = importPath ;
libraryPath + = QDir : : separator ( ) ;
libraryPath + = packagePath ;
2010-03-18 12:06:43 +01:00
2011-03-10 14:49:38 +01:00
if ( importLibrary ( doc , import , libraryPath , importInfo , importPath ) ) {
importFound = true ;
break ;
2010-08-25 14:15:57 +02:00
}
2010-06-09 14:27:30 +02:00
}
// if there are cpp-based types for this package, use them too
if ( engine ( ) - > cppQmlTypes ( ) . hasPackage ( packageName ) ) {
importFound = true ;
2010-09-16 15:29:37 +02:00
foreach ( QmlObjectValue * object ,
engine ( ) - > cppQmlTypes ( ) . typesForImport ( packageName , version ) ) {
import - > setProperty ( object - > className ( ) , object ) ;
2010-03-16 16:34:33 +01:00
}
2010-02-02 15:55:17 +01:00
}
2010-03-25 14:47:28 +01:00
2011-04-06 09:19:46 +02:00
if ( ! importFound & & importInfo . ast ( ) ) {
2010-09-16 15:29:37 +02:00
error ( doc , locationFromRange ( importInfo . ast ( ) - > firstSourceLocation ( ) ,
importInfo . ast ( ) - > lastSourceLocation ( ) ) ,
2010-06-09 14:27:30 +02:00
tr ( " package not found " ) ) ;
}
2010-09-16 15:29:37 +02:00
return import ;
2010-02-02 15:55:17 +01:00
}
2011-03-10 14:49:38 +01:00
bool Link : : importLibrary ( Document : : Ptr doc , Interpreter : : ObjectValue * import ,
const QString & libraryPath ,
const Interpreter : : ImportInfo & importInfo ,
const QString & importPath )
{
Q_D ( Link ) ;
const LibraryInfo libraryInfo = d - > snapshot . libraryInfo ( libraryPath ) ;
if ( ! libraryInfo . isValid ( ) )
return false ;
const ComponentVersion version = importInfo . version ( ) ;
const UiImport * ast = importInfo . ast ( ) ;
SourceLocation errorLoc ;
if ( ast )
errorLoc = locationFromRange ( ast - > firstSourceLocation ( ) , ast - > lastSourceLocation ( ) ) ;
if ( ! libraryInfo . plugins ( ) . isEmpty ( ) ) {
if ( libraryInfo . dumpStatus ( ) = = LibraryInfo : : DumpNotStartedOrRunning ) {
ModelManagerInterface * modelManager = ModelManagerInterface : : instance ( ) ;
if ( modelManager ) {
if ( importInfo . type ( ) = = ImportInfo : : LibraryImport ) {
2011-03-16 15:34:50 +01:00
if ( importInfo . version ( ) . isValid ( ) ) {
const QString uri = importInfo . name ( ) . replace ( QDir : : separator ( ) , QLatin1Char ( ' . ' ) ) ;
modelManager - > loadPluginTypes (
libraryPath , importPath ,
uri , version . toString ( ) ) ;
}
2011-03-10 14:49:38 +01:00
} else {
modelManager - > loadPluginTypes (
libraryPath , libraryPath ,
QString ( ) , version . toString ( ) ) ;
}
}
if ( errorLoc . isValid ( ) ) {
warning ( doc , errorLoc ,
tr ( " Library contains C++ plugins, type dump is in progress. " ) ) ;
}
} else if ( libraryInfo . dumpStatus ( ) = = LibraryInfo : : DumpError ) {
2011-04-12 09:14:31 +02:00
ModelManagerInterface * modelManager = ModelManagerInterface : : instance ( ) ;
// Only underline import if package/version isn't described in .qmltypes anyway
const QmlJS : : ModelManagerInterface : : BuiltinPackagesHash builtinPackages
= modelManager - > builtinPackages ( ) ;
const QString packageName = importInfo . name ( ) . replace ( QDir : : separator ( ) , QLatin1Char ( ' . ' ) ) ;
if ( ! builtinPackages . value ( packageName ) . contains ( importInfo . version ( ) ) ) {
if ( errorLoc . isValid ( ) ) {
error ( doc , errorLoc , libraryInfo . dumpError ( ) ) ;
}
2011-03-10 14:49:38 +01:00
}
} else {
QList < QmlObjectValue * > loadedObjects =
engine ( ) - > cppQmlTypes ( ) . load ( engine ( ) , libraryInfo . metaObjects ( ) ) ;
foreach ( QmlObjectValue * object , loadedObjects ) {
if ( object - > packageName ( ) . isEmpty ( ) ) {
import - > setProperty ( object - > className ( ) , object ) ;
}
}
}
}
loadQmldirComponents ( import , version , libraryInfo , libraryPath ) ;
return true ;
}
2010-02-02 17:06:48 +01:00
UiQualifiedId * Link : : qualifiedTypeNameId ( Node * node )
{
if ( UiObjectBinding * binding = AST : : cast < UiObjectBinding * > ( node ) )
return binding - > qualifiedTypeNameId ;
else if ( UiObjectDefinition * binding = AST : : cast < UiObjectDefinition * > ( node ) )
return binding - > qualifiedTypeNameId ;
else
return 0 ;
}
2010-03-29 11:32:11 +02:00
void Link : : error ( const Document : : Ptr & doc , const AST : : SourceLocation & loc , const QString & message )
{
2010-09-16 15:29:37 +02:00
Q_D ( Link ) ;
if ( doc - > fileName ( ) = = d - > doc - > fileName ( ) )
d - > diagnosticMessages . append ( DiagnosticMessage ( DiagnosticMessage : : Error , loc , message ) ) ;
2010-03-29 11:32:11 +02:00
}
2010-11-24 09:30:46 +01:00
void Link : : warning ( const Document : : Ptr & doc , const AST : : SourceLocation & loc , const QString & message )
{
Q_D ( Link ) ;
if ( doc - > fileName ( ) = = d - > doc - > fileName ( ) )
d - > diagnosticMessages . append ( DiagnosticMessage ( DiagnosticMessage : : Warning , loc , message ) ) ;
}
2011-02-25 16:16:37 +01:00
void Link : : loadQmldirComponents ( Interpreter : : ObjectValue * import , ComponentVersion version ,
const LibraryInfo & libraryInfo , const QString & libraryPath )
{
Q_D ( Link ) ;
2011-03-10 14:49:38 +01:00
// if the version isn't valid, import the latest
if ( ! version . isValid ( ) ) {
const int maxVersion = std : : numeric_limits < int > : : max ( ) ;
version = ComponentVersion ( maxVersion , maxVersion ) ;
}
2011-02-25 16:16:37 +01:00
QSet < QString > importedTypes ;
foreach ( const QmlDirParser : : Component & component , libraryInfo . components ( ) ) {
if ( importedTypes . contains ( component . typeName ) )
continue ;
ComponentVersion componentVersion ( component . majorVersion ,
component . minorVersion ) ;
if ( version < componentVersion )
continue ;
importedTypes . insert ( component . typeName ) ;
if ( Document : : Ptr importedDoc = d - > snapshot . document (
libraryPath + QDir : : separator ( ) + component . fileName ) ) {
if ( ObjectValue * v = importedDoc - > bind ( ) - > rootObjectValue ( ) )
import - > setProperty ( component . typeName , v ) ;
}
}
}
void Link : : loadImplicitDirectoryImports ( TypeEnvironment * typeEnv , Document : : Ptr doc )
{
Q_D ( Link ) ;
2011-03-10 14:49:38 +01:00
ImportInfo implcitDirectoryImportInfo (
ImportInfo : : ImplicitDirectoryImport , doc - > path ( ) ) ;
2011-02-25 16:16:37 +01:00
ObjectValue * directoryImport = d - > importCache . value ( ImportCacheKey ( implcitDirectoryImportInfo ) ) ;
if ( ! directoryImport ) {
2011-03-10 14:49:38 +01:00
directoryImport = importFileOrDirectory ( doc , implcitDirectoryImportInfo ) ;
2011-02-25 16:16:37 +01:00
if ( directoryImport )
d - > importCache . insert ( ImportCacheKey ( implcitDirectoryImportInfo ) , directoryImport ) ;
}
if ( directoryImport )
typeEnv - > addImport ( directoryImport , implcitDirectoryImportInfo ) ;
}
void Link : : loadImplicitDefaultImports ( TypeEnvironment * typeEnv )
{
Q_D ( Link ) ;
const QString defaultPackage = CppQmlTypes : : defaultPackage ;
if ( engine ( ) - > cppQmlTypes ( ) . hasPackage ( defaultPackage ) ) {
ImportInfo info ( ImportInfo : : LibraryImport , defaultPackage ) ;
ObjectValue * import = d - > importCache . value ( ImportCacheKey ( info ) ) ;
if ( ! import ) {
import = new ObjectValue ( engine ( ) ) ;
foreach ( QmlObjectValue * object ,
engine ( ) - > cppQmlTypes ( ) . typesForImport ( defaultPackage , ComponentVersion ( ) ) ) {
import - > setProperty ( object - > className ( ) , object ) ;
}
d - > importCache . insert ( ImportCacheKey ( info ) , import ) ;
}
typeEnv - > addImport ( import , info ) ;
}
}