The qualifier ("MyScript" in the above example) must be unique within the QML document.
Unlike ordinary modules, multiple scripts cannot be imported into the same namespace.
+Javascript files can be provided by modules, by adding Namespace definitions to the
+\l{Writing a qmldir file}{qmldir file} for the module. For example:
+
+\code
+SystemFunctions 1.0 SystemFunctions.js
+UserFunctions 1.0 UserFunctions.js
+\endcode
+
+Javascript can be imported from a module, where they will have the namespace defined
+for them in the module's \c qmldir file:
+
+\qml
+import projects.MyQMLProject.MyFunctions 1.0
+
+Window {
+ Component.onCompleted: { SystemFunctions.cleanUp(); }
+}
+\endqml
+
+Javascript provided by modules can also be imported into namespaces:
+
+\qml
+import projects.MyQMLProject.MyFunctions 1.0 as MyFuncs
+import org.example.Functions 1.0 as TheirFuncs
+
+Window {
+ Component.onCompleted: {
+ MyFuncs.SystemFunctions.cleanUp();
+ TheirFuncs.SystemFunctions.shutdown();
+ }
+}
+\endqml
\section1 Writing a qmldir File
# <Comment>
<TypeName> [<InitialVersion>] <File>
internal <TypeName> <File>
+<Namespace> <InitialVersion> <File>
plugin <Name> [<Path>]
typeinfo <File>
\endcode
leaving other identifiers available, even if the actual installed version might otherwise
provide those identifiers.
+\bold {<Namespace> <InitialVersion> <File>} lines are used to import javascript files
+into a Namespace exported by the module. The contents of the script file are made
+available inside the namespace <Namespace>, which has the version number
+<InitialVersion>.
+
\bold {plugin <Name> [<Path>]} lines are used to add \l{QDeclarativeExtensionPlugin}{QML C++ plugins} to the module. <Name> is the name of the library. It is usually not the same as the file name
of the plugin binary, which is platform dependent; e.g. the library \c MyAppTypes would produce
\c libMyAppTypes.so on Linux and \c MyAppTypes.dll on Windows.
Without such a file QML tools may be unable to offer features such as code completion
for the types defined in your plugins.
-
\section1 Debugging
The \c QML_IMPORT_TRACE environment variable can be useful for debugging
inline v8::Handle<v8::String> string() const;
+ inline QString toString() const;
+
private:
v8::String::CompleteHashData m_hash;
v8::Handle<v8::String> m_string;
return m_string;
}
+QString QHashedV8String::toString() const
+{
+ QString result;
+ result.reserve(m_hash.length);
+
+ for (int i = 0; i < m_hash.length; ++i)
+ result.append(m_string->GetCharacter(i));
+
+ return result;
+}
+
QHashedStringRef::QHashedStringRef()
: m_data(0), m_length(0), m_utf8length(-1), m_hash(0)
{
if (componentStats)
componentStats->componentStat.lineNumber = tree->location.start.line;
- // Build global import scripts
- QStringList importedScriptIndexes;
-
- foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) {
- importedScriptIndexes.append(script.qualifier);
- }
-
// We generate the importCache before we build the tree so that
// it can be used in the binding compiler. Given we "expect" the
// QML compilation to succeed, this isn't a waste.
output->importCache = new QDeclarativeTypeNameCache();
- for (int ii = 0; ii < importedScriptIndexes.count(); ++ii)
- output->importCache->add(importedScriptIndexes.at(ii), ii);
+ foreach (const QString &ns, unit->namespaces()) {
+ output->importCache->add(ns);
+ }
+
+ int scriptIndex = 0;
+ foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) {
+ QString qualifier = script.qualifier;
+ QString enclosingNamespace;
+
+ const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
+ if (lastDotIndex != -1) {
+ enclosingNamespace = qualifier.left(lastDotIndex);
+ qualifier = qualifier.mid(lastDotIndex+1);
+ }
+
+ output->importCache->add(qualifier, scriptIndex++, enclosingNamespace);
+ }
+
unit->imports().populateCache(output->importCache, engine);
if (!buildObject(tree, BindingContext()) || !completeComponentBuild())
_errors.clear();
_plugins.clear();
_components.clear();
+ _scripts.clear();
if (_source.isEmpty() && !_filePathSouce.isEmpty()) {
QFile file(_filePathSouce);
const int minorVersion = version.mid(dotIndex + 1).toInt(&validVersionNumber);
if (validVersionNumber) {
- const Component entry(sections[0], sections[2], majorVersion, minorVersion);
-
- _components.append(entry);
+ const QString &fileName = sections[2];
+
+ if (fileName.endsWith(QLatin1String(".js"))) {
+ // A 'js' extension indicates a namespaced script import
+ const Script entry(sections[0], fileName, majorVersion, minorVersion);
+ _scripts.append(entry);
+ } else {
+ const Component entry(sections[0], fileName, majorVersion, minorVersion);
+ _components.append(entry);
+ }
}
}
}
return _components;
}
+QList<QDeclarativeDirParser::Script> QDeclarativeDirParser::scripts() const
+{
+ return _scripts;
+}
+
#ifdef QT_CREATOR
QList<QDeclarativeDirParser::TypeInfo> QDeclarativeDirParser::typeInfos() const
{
bool internal;
};
+ struct Script
+ {
+ Script()
+ : majorVersion(0), minorVersion(0) {}
+
+ Script(const QString &nameSpace, const QString &fileName, int majorVersion, int minorVersion)
+ : nameSpace(nameSpace), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion) {}
+
+ QString nameSpace;
+ QString fileName;
+ int majorVersion;
+ int minorVersion;
+ };
+
QList<Component> components() const;
+ QList<Script> scripts() const;
QList<Plugin> plugins() const;
#ifdef QT_CREATOR
QString _source;
QString _filePathSouce;
QList<Component> _components;
+ QList<Script> _scripts;
QList<Plugin> _plugins;
#ifdef QT_CREATOR
QList<TypeInfo> _typeInfos;
};
typedef QList<QDeclarativeDirParser::Component> QDeclarativeDirComponents;
+typedef QList<QDeclarativeDirParser::Script> QDeclarativeDirScripts;
QT_END_NAMESPACE
int minversion;
bool isLibrary;
QDeclarativeDirComponents qmlDirComponents;
+ QDeclarativeDirScripts qmlDirScripts;
};
QList<Data> imports;
bool importExtension(const QString &absoluteFilePath, const QString &uri,
QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
+ QDeclarativeDirScripts *scripts,
QList<QDeclarativeError> *errors);
QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
++iter) {
const QDeclarativeImportedNamespace &set = *iter.value();
- QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
QDeclarativeTypeModule *module = QDeclarativeMetaType::typeModule(data.uri, data.majversion);
- if (module)
+ if (module) {
+ QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
import.modules.append(QDeclarativeTypeModuleVersion(module, data.minversion));
+ }
QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion);
if (moduleApi.script || moduleApi.qobject) {
+ QDeclarativeTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
import.moduleApi = ep->moduleApiInstance(moduleApi);
}
}
}
+QList<QDeclarativeImports::ScriptReference> QDeclarativeImports::resolvedScripts() const
+{
+ QList<QDeclarativeImports::ScriptReference> scripts;
+
+ const QDeclarativeImportedNamespace &set = d->unqualifiedset;
+
+ for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
+ const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
+
+ foreach (const QDeclarativeDirParser::Script &script, data.qmlDirScripts) {
+ ScriptReference ref;
+ ref.nameSpace = script.nameSpace;
+ ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
+ scripts.append(ref);
+ }
+ }
+
+ for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.constBegin();
+ iter != d->set.constEnd();
+ ++iter) {
+ const QDeclarativeImportedNamespace &set = *iter.value();
+
+ for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
+ const QDeclarativeImportedNamespace::Data &data = set.imports.at(ii);
+
+ foreach (const QDeclarativeDirParser::Script &script, data.qmlDirScripts) {
+ ScriptReference ref;
+ ref.nameSpace = script.nameSpace;
+ ref.qualifier = iter.key();
+ ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
+ scripts.append(ref);
+ }
+ }
+ }
+
+ return scripts;
+}
+
/*!
\internal
delete s;
}
-bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
- QDeclarativeImportDatabase *database,
- QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors)
+bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
+ QDeclarativeImportDatabase *database,
+ QDeclarativeDirComponents* components,
+ QDeclarativeDirScripts* scripts,
+ QList<QDeclarativeError> *errors)
{
const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
if (qmldirParser->hasError()) {
if (components)
*components = qmldirParser->components();
+ if (scripts)
+ *scripts = qmldirParser->scripts();
return true;
}
static QLatin1Char Slash('/');
QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
+ QDeclarativeDirScripts qmldirscripts;
QString uri = uri_arg;
QDeclarativeImportedNamespace *s;
if (prefix.isEmpty()) {
else
url = QUrl::fromLocalFile(fi.absolutePath()).toString();
uri = resolvedUri(dir, database);
- if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
+ if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
return false;
break;
}
}
+ // TODO: Should this search be omitted if found == true?
+
// step 2: search for extension with encoded version major
foreach (const QString &p, database->fileImportPath) {
dir = p+Slash+url;
else
url = QUrl::fromLocalFile(fi.absolutePath()).toString();
uri = resolvedUri(dir, database);
- if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
+ if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
return false;
break;
}
else
url = QUrl::fromLocalFile(absolutePath).toString();
uri = resolvedUri(dir, database);
- if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors))
+ if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
return false;
break;
}
if (QDeclarativeMetaType::isModule(uri, vmaj, vmin))
versionFound = true;
- if (!versionFound && qmldircomponents.isEmpty()) {
+ if (!versionFound && qmldircomponents.isEmpty() && qmldirscripts.isEmpty()) {
if (errors) {
QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader.
if (QDeclarativeMetaType::isAnyModule(uri))
if (uri.endsWith(Slash))
uri.chop(1);
if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
- if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors))
+ if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,&qmldirscripts,errors))
return false;
}
} else {
url = resolveLocalUrl(base, url);
}
- if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
- QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
+ if (!versionFound && (vmaj > -1) && (vmin > -1) && !qmldircomponents.isEmpty()) {
int lowest_min = INT_MAX;
int highest_min = INT_MIN;
- for (; it != qmldircomponents.end(); ++it) {
- if (it->majorVersion == vmaj) {
- lowest_min = qMin(lowest_min, it->minorVersion);
- highest_min = qMax(highest_min, it->minorVersion);
+
+ QList<QDeclarativeDirParser::Component>::const_iterator cend = qmldircomponents.constEnd();
+ for (QList<QDeclarativeDirParser::Component>::const_iterator cit = qmldircomponents.constBegin(); cit != cend; ++cit) {
+ if (cit->majorVersion == vmaj) {
+ lowest_min = qMin(lowest_min, cit->minorVersion);
+ highest_min = qMax(highest_min, cit->minorVersion);
}
}
+
if (lowest_min > vmin || highest_min < vmin) {
if (errors) {
QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader.
if (!url.endsWith(Slash))
url += Slash;
+ QMap<QString, QDeclarativeDirParser::Script> scripts;
+
+ if (!qmldirscripts.isEmpty()) {
+ // Verify that we haven't imported these scripts already
+ QList<QDeclarativeImportedNamespace::Data>::const_iterator end = s->imports.constEnd();
+ for (QList<QDeclarativeImportedNamespace::Data>::const_iterator it = s->imports.constBegin(); it != end; ++it) {
+ if (it->uri == uri) {
+ QDeclarativeError error;
+ error.setDescription(QDeclarativeImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url));
+ errors->prepend(error);
+ return false;
+ }
+ }
+
+ QList<QDeclarativeDirParser::Script>::const_iterator send = qmldirscripts.constEnd();
+ for (QList<QDeclarativeDirParser::Script>::const_iterator sit = qmldirscripts.constBegin(); sit != send; ++sit) {
+ // Only include scripts that match our requested version
+ if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
+ ((vmin == -1) || (sit->minorVersion <= vmin))) {
+
+ // Load the highest version that matches
+ QMap<QString, QDeclarativeDirParser::Script>::iterator it = scripts.find(sit->nameSpace);
+ if (it == scripts.end() || (it->minorVersion < sit->minorVersion)) {
+ scripts.insert(sit->nameSpace, *sit);
+ }
+ }
+ }
+ }
+
QDeclarativeImportedNamespace::Data data;
data.uri = uri;
data.url = url;
data.minversion = vmin;
data.isLibrary = importType == QDeclarativeScript::Import::Library;
data.qmlDirComponents = qmldircomponents;
+ data.qmlDirScripts = scripts.values();
+
s->imports.prepend(data);
return true;
void populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *) const;
+ struct ScriptReference
+ {
+ QString nameSpace;
+ QString qualifier;
+ QUrl location;
+ };
+
+ QList<ScriptReference> resolvedScripts() const;
+
private:
friend class QDeclarativeImportDatabase;
QDeclarativeImportsPrivate *d;
return m_scripts;
}
+const QSet<QString> &QDeclarativeTypeData::namespaces() const
+{
+ return m_namespaces;
+}
+
QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
{
if (m_compiledData)
}
}
+ // Add any imported scripts to our resolved set
+ foreach (const QDeclarativeImports::ScriptReference &script, m_imports.resolvedScripts())
+ {
+ QDeclarativeScriptBlob *blob = typeLoader()->getScript(script.location);
+ addDependency(blob);
+
+ ScriptReference ref;
+ //ref.location = ...
+ ref.qualifier = script.nameSpace;
+ if (!script.qualifier.isEmpty())
+ {
+ ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
+
+ // Add a reference to the enclosing namespace
+ m_namespaces.insert(script.qualifier);
+ }
+
+ ref.script = blob;
+ m_scripts << ref;
+ }
+
foreach (QDeclarativeScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
TypeReference ref;
const QList<TypeReference> &resolvedTypes() const;
const QList<ScriptReference> &resolvedScripts() const;
+ const QSet<QString> &namespaces() const;
QDeclarativeCompiledData *compiledData() const;
QList<ScriptReference> m_scripts;
QList<QDeclarativeQmldirData *> m_qmldirs;
+ QSet<QString> m_namespaces;
+
QList<TypeReference> m_types;
bool m_typesResolved:1;
{
}
-void QDeclarativeTypeNameCache::add(const QHashedString &name, int importedScriptIndex)
+void QDeclarativeTypeNameCache::add(const QHashedString &name, int importedScriptIndex, const QHashedString &nameSpace)
{
+ Import import;
+ import.scriptIndex = importedScriptIndex;
+
+ if (nameSpace.length() != 0) {
+ Import *i = m_namedImports.value(nameSpace);
+ Q_ASSERT(i != 0);
+ m_namespacedImports[i].insert(name, import);
+ return;
+ }
+
if (m_namedImports.contains(name))
return;
- Import import;
- import.scriptIndex = importedScriptIndex;
m_namedImports.insert(name, import);
}
QDeclarativeTypeNameCache::Result QDeclarativeTypeNameCache::query(const QHashedStringRef &name)
{
- Import *i = m_namedImports.value(name);
- if (i) {
- if (i->scriptIndex != -1)
- return Result(i->scriptIndex);
- else
- return Result((const void *)i);
- }
+ Result result = query(m_namedImports, name);
- for (int ii = 0; ii < m_anonymousImports.count(); ++ii) {
- if (QDeclarativeType *type = m_anonymousImports.at(ii).type(name))
- return Result(type);
- }
+ if (!result.isValid())
+ result = typeSearch(m_anonymousImports, name);
- return Result();
+ return result;
}
QDeclarativeTypeNameCache::Result QDeclarativeTypeNameCache::query(const QHashedStringRef &name,
const void *importNamespace)
{
Q_ASSERT(importNamespace);
- Import *i = (Import *)importNamespace;
+ const Import *i = static_cast<const Import *>(importNamespace);
Q_ASSERT(i->scriptIndex == -1);
- for (int ii = 0; ii < i->modules.count(); ++ii) {
- if (QDeclarativeType *type = i->modules.at(ii).type(name))
- return Result(type);
- }
-
- return Result();
+ return typeSearch(i->modules, name);
}
QDeclarativeTypeNameCache::Result QDeclarativeTypeNameCache::query(const QHashedV8String &name)
{
- Import *i = m_namedImports.value(name);
- if (i) {
- if (i->scriptIndex != -1)
- return Result(i->scriptIndex);
- else
- return Result((const void *)i);
- }
+ Result result = query(m_namedImports, name);
- for (int ii = 0; ii < m_anonymousImports.count(); ++ii) {
- if (QDeclarativeType *type = m_anonymousImports.at(ii).type(name))
- return Result(type);
- }
+ if (!result.isValid())
+ result = typeSearch(m_anonymousImports, name);
- return Result();
+ return result;
}
QDeclarativeTypeNameCache::Result QDeclarativeTypeNameCache::query(const QHashedV8String &name, const void *importNamespace)
{
Q_ASSERT(importNamespace);
- Import *i = (Import *)importNamespace;
+ const Import *i = static_cast<const Import *>(importNamespace);
Q_ASSERT(i->scriptIndex == -1);
- for (int ii = 0; ii < i->modules.count(); ++ii) {
- if (QDeclarativeType *type = i->modules.at(ii).type(name))
- return Result(type);
- }
-
- return Result();
+ QMap<const Import *, QStringHash<Import> >::const_iterator it = m_namespacedImports.find(i);
+ if (it != m_namespacedImports.constEnd())
+ return query(*it, name);
+
+ return typeSearch(i->modules, name);
}
QDeclarativeMetaType::ModuleApiInstance *QDeclarativeTypeNameCache::moduleApi(const void *importNamespace)
{
Q_ASSERT(importNamespace);
- Import *i = (Import *)importNamespace;
+ const Import *i = static_cast<const Import *>(importNamespace);
Q_ASSERT(i->scriptIndex == -1);
return i->moduleApi;
inline bool isEmpty() const;
- void add(const QHashedString &, int);
+ void add(const QHashedString &name, int sciptIndex = -1, const QHashedString &nameSpace = QHashedString());
struct Result {
inline Result();
int scriptIndex;
};
+ template<typename Key>
+ Result query(const QStringHash<Import> &imports, Key key)
+ {
+ Import *i = imports.value(key);
+ if (i) {
+ if (i->scriptIndex != -1) {
+ return Result(i->scriptIndex);
+ } else {
+ return Result(static_cast<const void *>(i));
+ }
+ }
+
+ return Result();
+ }
+
+ template<typename Key>
+ Result typeSearch(const QVector<QDeclarativeTypeModuleVersion> &modules, Key key)
+ {
+ QVector<QDeclarativeTypeModuleVersion>::const_iterator end = modules.constEnd();
+ for (QVector<QDeclarativeTypeModuleVersion>::const_iterator it = modules.constBegin(); it != end; ++it) {
+ if (QDeclarativeType *type = it->type(key))
+ return Result(type);
+ }
+
+ return Result();
+ }
+
QStringHash<Import> m_namedImports;
+ QMap<const Import *, QStringHash<Import> > m_namespacedImports;
QVector<QDeclarativeTypeModuleVersion> m_anonymousImports;
QDeclarativeEngine *engine;
resource->importNamespace);
if (r.isValid()) {
- Q_ASSERT(r.type);
+ if (r.type) {
+ return v8engine->typeWrapper()->newObject(object, r.type, resource->mode);
+ } else if (r.scriptIndex != -1) {
+ int index = r.scriptIndex;
+ QDeclarativeContextData *context = v8engine->callingContext();
+ if (index < context->importedScripts.count())
+ return context->importedScripts.at(index);
+ }
- return v8engine->typeWrapper()->newObject(object, r.type, resource->mode);
+ return v8::Undefined();
} else if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) {
if (moduleApi->scriptCallback) {
--- /dev/null
+import com.nokia.PureJsModule 1.0
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((FirstAPI.greeting() == "Hello") &&
+ (FirstAPI.major == 1) &&
+ (FirstAPI.minor == 0))
+ }
+}
--- /dev/null
+import com.nokia.PureJsModule 1.0 as PJM
+import com.nokia.PureJsModule 1.0 as AnotherName
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((PJM.FirstAPI.greeting() == "Hello") &&
+ (PJM.FirstAPI.major == 1) &&
+ (PJM.FirstAPI.minor == 0) &&
+ (AnotherName.FirstAPI.greeting() == "Hello") &&
+ (AnotherName.FirstAPI.major == 1) &&
+ (AnotherName.FirstAPI.minor == 0))
+ }
+}
--- /dev/null
+import com.nokia.VersionedOnlyJsModule 9.0
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((SomeAPI.greeting() == "Hey hey hey") &&
+ (SomeAPI.major == 9) &&
+ (SomeAPI.minor == 0))
+ }
+}
--- /dev/null
+import com.nokia.PureJsModule 1.0
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((FirstAPI.greeting() == "Hello") &&
+ (FirstAPI.major == 1) &&
+ (FirstAPI.minor == 0) &&
+ (SecondAPI.greeting() == "Howdy") &&
+ (SecondAPI.major == 1) &&
+ (SecondAPI.minor == 5))
+
+ }
+}
--- /dev/null
+import com.nokia.PureJsModule 1.6
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((FirstAPI.greeting() == "Good news, everybody!") &&
+ (FirstAPI.major == 1) &&
+ (FirstAPI.minor == 6) &&
+ (SecondAPI.greeting() == "Howdy") &&
+ (SecondAPI.major == 1) &&
+ (SecondAPI.minor == 5))
+ }
+}
--- /dev/null
+1:1:module "com.nokia.VersionedOnlyJsModule" is not installed
--- /dev/null
+import com.nokia.VersionedOnlyJsModule 1.0
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+}
--- /dev/null
+2:1:"com.nokia.VersionedOnlyJsModule" is ambiguous.
--- /dev/null
+import com.nokia.VersionedOnlyJsModule 9.0
+import com.nokia.VersionedOnlyJsModule 9.0
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((SomeAPI.greeting() == "Hey hey hey") &&
+ (SomeAPI.major == 9) &&
+ (SomeAPI.minor == 0))
+ }
+}
--- /dev/null
+2:1:"com.nokia.PureJsModule" is ambiguous.
--- /dev/null
+import com.nokia.PureJsModule 1.0
+import com.nokia.PureJsModule 1.6
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((FirstAPI.greeting() == "Hello") &&
+ (FirstAPI.major == 1) &&
+ (FirstAPI.minor == 0))
+ }
+}
--- /dev/null
+import com.nokia.PureJsModule 1.5 as PJM
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((PJM.FirstAPI.greeting() == "Hello") &&
+ (PJM.FirstAPI.major == 1) &&
+ (PJM.FirstAPI.minor == 0) &&
+ (PJM.SecondAPI.greeting() == "Howdy") &&
+ (PJM.SecondAPI.major == 1) &&
+ (PJM.SecondAPI.minor == 5))
+ }
+}
--- /dev/null
+import com.nokia.PureJsModule 1.5 as PJM_1_5
+import com.nokia.PureJsModule 1.6 as PJM_1_6
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ Component.onCompleted: {
+ test = ((PJM_1_5.FirstAPI.greeting() == "Hello") &&
+ (PJM_1_5.FirstAPI.major == 1) &&
+ (PJM_1_5.FirstAPI.minor == 0) &&
+ (PJM_1_5.SecondAPI.greeting() == "Howdy") &&
+ (PJM_1_5.SecondAPI.major == 1) &&
+ (PJM_1_5.SecondAPI.minor == 5) &&
+ (PJM_1_6.FirstAPI.greeting() == "Good news, everybody!") &&
+ (PJM_1_6.FirstAPI.major == 1) &&
+ (PJM_1_6.FirstAPI.minor == 6))
+ }
+}
--- /dev/null
+var major = 1
+var minor = 6
+
+function greeting() { return "Good news, everybody!" }
+
--- /dev/null
+var major = 1
+var minor = 0
+
+function greeting() { return "Hello" }
+
--- /dev/null
+var major = 1
+var minor = 5
+
+function greeting() { return "Howdy" }
+
--- /dev/null
+FirstAPI 1.0 FirstAPI.js
+FirstAPI 1.6 FirstAPI.1.6.js
+SecondAPI 1.5 SecondAPI.js
--- /dev/null
+var major = 1
+var minor = 0
+
+function greeting() { return "Hello" }
+
--- /dev/null
+var major = 1
+var minor = 5
+
+function greeting() { return "Howdy" }
+
--- /dev/null
+FirstAPI 1.0 FirstAPI.js
+SecondAPI 1.5 SecondAPI.js
--- /dev/null
+var major = 9
+var minor = 0
+
+function greeting() { return "Hey hey hey" }
+
--- /dev/null
+SomeAPI 9.0 SomeAPI.js
void importsOrder_data();
void importsOrder();
void importIncorrectCase();
+ void importJs_data();
+ void importJs();
void qmlAttachedPropertiesObjectMethod();
void customOnProperty();
void testType(const QString& qml, const QString& type, const QString& error);
};
-#define VERIFY_ERRORS(errorfile) \
- if (!errorfile) { \
- if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \
- qWarning() << "Unexpected Errors:" << component.errors(); \
- QVERIFY(!component.isError()); \
- QVERIFY(component.errors().isEmpty()); \
- } else { \
+#define DETERMINE_ERRORS(errorfile,expected,actual)\
+ QList<QByteArray> expected; \
+ QList<QByteArray> actual; \
+ do { \
QFile file(testdata(QLatin1String(errorfile))); \
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); \
QByteArray data = file.readAll(); \
file.close(); \
- QList<QByteArray> expected = data.split('\n'); \
+ expected = data.split('\n'); \
expected.removeAll(QByteArray("")); \
QList<QDeclarativeError> errors = component.errors(); \
- QList<QByteArray> actual; \
for (int ii = 0; ii < errors.count(); ++ii) { \
const QDeclarativeError &error = errors.at(ii); \
QByteArray errorStr = QByteArray::number(error.line()) + ":" + \
error.description().toUtf8(); \
actual << errorStr; \
} \
+ } while (false);
+
+#define VERIFY_ERRORS(errorfile) \
+ if (!errorfile) { \
+ if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \
+ qWarning() << "Unexpected Errors:" << component.errors(); \
+ QVERIFY(!component.isError()); \
+ QVERIFY(component.errors().isEmpty()); \
+ } else { \
+ DETERMINE_ERRORS(errorfile,actual,expected);\
if (qgetenv("DEBUG") != "" && expected != actual) \
qWarning() << "Expected:" << expected << "Actual:" << actual; \
if (qgetenv("QDECLARATIVELANGUAGE_UPDATEERRORS") != "" && expected != actual) {\
QCOMPARE(errors.at(0).description(), expectedError);
}
+void tst_qdeclarativelanguage::importJs_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QString>("errorFile");
+ QTest::addColumn<bool>("performTest");
+
+ QTest::newRow("defaultVersion")
+ << "importJs.1.qml"
+ << "importJs.1.errors.txt"
+ << true;
+
+ QTest::newRow("specifiedVersion")
+ << "importJs.2.qml"
+ << "importJs.2.errors.txt"
+ << true;
+
+ QTest::newRow("excludeExcessiveVersion")
+ << "importJs.3.qml"
+ << "importJs.3.errors.txt"
+ << false;
+
+ QTest::newRow("includeAppropriateVersion")
+ << "importJs.4.qml"
+ << "importJs.4.errors.txt"
+ << true;
+
+ QTest::newRow("noDefaultVersion")
+ << "importJs.5.qml"
+ << "importJs.5.errors.txt"
+ << false;
+
+ QTest::newRow("repeatImportFails")
+ << "importJs.6.qml"
+ << "importJs.6.errors.txt"
+ << false;
+
+ QTest::newRow("multipleVersionImportFails")
+ << "importJs.7.qml"
+ << "importJs.7.errors.txt"
+ << false;
+
+ QTest::newRow("namespacedImport")
+ << "importJs.8.qml"
+ << "importJs.8.errors.txt"
+ << true;
+
+ QTest::newRow("namespacedVersionedImport")
+ << "importJs.9.qml"
+ << "importJs.9.errors.txt"
+ << true;
+
+ QTest::newRow("namespacedRepeatImport")
+ << "importJs.10.qml"
+ << "importJs.10.errors.txt"
+ << true;
+}
+
+void tst_qdeclarativelanguage::importJs()
+{
+ QFETCH(QString, file);
+ QFETCH(QString, errorFile);
+ QFETCH(bool, performTest);
+
+ QDeclarativeComponent component(&engine, TEST_FILE(file));
+
+ {
+ DETERMINE_ERRORS(errorFile.toLatin1().constData(),expected,actual);
+ QCOMPARE(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); ++i)
+ {
+ size_t compareLen = std::min(expected.at(i).length(), actual.at(i).length());
+ QCOMPARE(expected.at(i).left(compareLen), actual.at(i).left(compareLen));
+ }
+ }
+
+ if (performTest) {
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("test").toBool(),true);
+ delete object;
+ }
+}
+
void tst_qdeclarativelanguage::qmlAttachedPropertiesObjectMethod()
{
QObject object;