diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js index b16dba70421..3dc09cb40e1 100644 --- a/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js +++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js @@ -615,7 +615,8 @@ throw new Error("QML only supports ECMA Script Language Specification 5 or older"); } - if (this.options.mode === "qml" || this.options.mode === "qmltypes") { + // Disabled 'qmltypes' mode for now since the normal parser can't parse it anyway + if (this.options.mode === "qml") { // Force strict mode this.strict = true; diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js index f50d6f80b65..07816ecda7d 100644 --- a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js +++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js @@ -55,18 +55,6 @@ var stats, modes = { } } }, - "Normal QMLTypes": { - config: { - parse: acorn.parse, - options: { - mode: "qmltypes" - }, - filter: function (test) { - var opts = test.options || {}; - return opts.normal !== false && opts.qmltypes !== false; - } - } - }, "Loose QMLTypes": { config: { parse: acorn.parse_dammit, diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QMLAnalyzer.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QMLAnalyzer.java index cb30a069c25..dd87fab4c20 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QMLAnalyzer.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QMLAnalyzer.java @@ -34,11 +34,14 @@ import org.eclipse.cdt.qt.core.qmljs.IQmlASTNode; @SuppressWarnings("nls") public class QMLAnalyzer implements IQMLAnalyzer { + private QMLModuleResolver moduleResolver; private ScriptEngine engine; private Invocable invoke; private Object tern; + @Override public void load() throws ScriptException, IOException, NoSuchMethodException { + moduleResolver = new QMLModuleResolver(this); engine = new ScriptEngineManager().getEngineByName("nashorn"); invoke = (Invocable) engine; @@ -88,6 +91,7 @@ public class QMLAnalyzer implements IQMLAnalyzer { return fixPathString(path.normalize().toString()); }; options.put("resolveDirectory", invoke.invokeFunction("resolveDirectory", resolveDirectory)); + options.put("resolveModule", invoke.invokeFunction("resolveModule", moduleResolver)); synchronized (this) { tern = invoke.invokeFunction("newTernServer", options); @@ -203,19 +207,13 @@ public class QMLAnalyzer implements IQMLAnalyzer { public IQmlASTNode parseString(String text, String mode, boolean locations, boolean ranges) throws NoSuchMethodException, ScriptException { waitUntilLoaded(); - Bindings query = engine.createBindings(); - query.put("type", "parseString"); - query.put("text", text); Bindings options = engine.createBindings(); options.put("mode", mode); options.put("locations", locations); options.put("ranges", ranges); - query.put("options", options); - Bindings request = engine.createBindings(); - request.put("query", query); ASTCallback callback = new ASTCallback(); - invoke.invokeMethod(tern, "request", request, invoke.invokeFunction("requestCallback", callback)); + invoke.invokeMethod(tern, "parseString", text, options, invoke.invokeFunction("requestCallback", callback)); return callback.getAST(); } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QMLModuleResolver.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QMLModuleResolver.java new file mode 100644 index 00000000000..602533ee129 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QMLModuleResolver.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; + +import javax.script.ScriptException; + +import org.eclipse.cdt.internal.qt.core.qmltypes.QMLModelBuilder; +import org.eclipse.cdt.internal.qt.core.qmltypes.QMLModuleInfo; +import org.eclipse.cdt.qt.core.IQtInstall; +import org.eclipse.cdt.qt.core.IQtInstallManager; +import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryInfo; +import org.eclipse.cdt.qt.core.qmljs.IQmlASTNode; + +public class QMLModuleResolver { + private final QMLAnalyzer analyzer; + private final IQtInstallManager manager; + private final QMLModelBuilder builder; + + public QMLModuleResolver(QMLAnalyzer analyzer) { + this.analyzer = analyzer; + this.manager = Activator.getService(IQtInstallManager.class); + this.builder = new QMLModelBuilder(); + } + + // TODO: determine exactly how to give this to Tern. For now we'll just return the reference to the QMLModuleInfo + // that we found + public QMLModuleInfo resolveModule(String module) throws NoSuchMethodException, ScriptException { + QMLModuleInfo info = builder.getModule(module); + if (info == null) { + Path path = getModulePath(module); + if (path != null) { + File qmldir = path.resolve("qmldir").normalize().toFile(); //$NON-NLS-1$ + try { + String types = getQmlTypesFile(qmldir); + File qmlTypes = path.resolve(types).toFile(); + String typeContents = fileToString(qmlTypes); + IQmlASTNode ast = analyzer.parseString(typeContents, "qmltypes", false, false); //$NON-NLS-1$ + info = builder.addModule(module, ast); + } catch (IOException e) { + Activator.log(e); + } + } + } + return info; + } + + private String fileToString(File file) throws IOException { + try (InputStream stream = new FileInputStream(file)) { + StringBuilder sb = new StringBuilder(); + int read = -1; + while ((read = stream.read()) != -1) { + sb.append((char) read); + } + return sb.toString(); + } + } + + private String getQmlTypesFile(File qmldir) throws IOException { + try (InputStream stream = new FileInputStream(qmldir)) { + QMLDirectoryInfo info = new QMLDirectoryInfo(stream); + return info.getTypesFileName(); + } + } + + private Path getModulePath(String module) { + if (module != null) { + for (IQtInstall install : manager.getInstalls()) { + Path qmlPath = install.getQmlPath(); + Path modPath = null; + if (module.equals("QtQuick")) { //$NON-NLS-1$ + modPath = qmlPath.resolve("QtQuick.2").normalize(); //$NON-NLS-1$ + } else { + modPath = qmlPath; + for (String part : module.split("\\.")) { //$NON-NLS-1$ + modPath = modPath.resolve(part).normalize(); + } + } + if (modPath.toFile().exists()) { + return modPath; + } + } + } + return null; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QmlASTNodeHandler.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QmlASTNodeHandler.java index c59163b8385..e20a6b0e1b8 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QmlASTNodeHandler.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QmlASTNodeHandler.java @@ -150,11 +150,20 @@ public class QmlASTNodeHandler implements InvocationHandler { methodResults.put(mName, handleObject(node.get(pName), method.getReturnType())); } } - return methodResults.get(method.getName()); + return methodResults.get(mName); } private Object handleObject(Object value, Class expectedType) throws Throwable { - if (expectedType.isAssignableFrom(ISourceLocation.class)) { + if (expectedType.isArray()) { + Object arr = Array.newInstance(expectedType.getComponentType(), ((Bindings) value).size()); + int ctr = 0; + for (Object obj : ((Bindings) value).values()) { + Array.set(arr, ctr++, handleObject(obj, expectedType.getComponentType())); + } + return arr; + } else if (expectedType.equals(Object.class)) { + return value; + } else if (expectedType.isAssignableFrom(ISourceLocation.class)) { // ISourceLocation doesn't correspond to an AST Node and needs to be created manually from // the given Bindings. if (value instanceof Bindings) { @@ -170,13 +179,6 @@ public class QmlASTNodeHandler implements InvocationHandler { return loc; } return new SourceLocation(); - } else if (expectedType.isArray()) { - Object arr = Array.newInstance(expectedType.getComponentType(), ((Bindings) value).size()); - int ctr = 0; - for (Object obj : ((Bindings) value).values()) { - Array.set(arr, ctr++, handleObject(obj, expectedType.getComponentType())); - } - return arr; } else if (expectedType.isAssignableFrom(List.class)) { if (value instanceof Bindings) { List list = new ArrayList<>(); diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtInstall.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtInstall.java index fb1a853203d..3cc3d7d92f9 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtInstall.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtInstall.java @@ -40,6 +40,11 @@ public class QtInstall implements IQtInstall { return qmakePath.resolve("../lib"); //$NON-NLS-1$ } + @Override + public Path getQmlPath() { + return qmakePath.resolve("../../qml"); //$NON-NLS-1$ + } + public static String getSpec(String qmakePath) throws IOException { Process proc = new ProcessBuilder(qmakePath, "-query", "QMAKE_XSPEC").start(); //$NON-NLS-1$ //$NON-NLS-2$ try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLComponentInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLComponentInfo.java new file mode 100644 index 00000000000..b33d24a18a5 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLComponentInfo.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectDefinition; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectMember; +import org.eclipse.cdt.qt.core.qmljs.IQmlPropertyBinding; + +public class QMLComponentInfo { + static final String IDENTIFIER = "Component"; //$NON-NLS-1$ + + static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ + static final String PROPERTY_PROTOTYPE = "prototype"; //$NON-NLS-1$ + static final String PROPERTY_DEF_PROPERTY = "defaultProperty"; //$NON-NLS-1$ + static final String PROPERTY_ATTACHED_TYPE = "attachedType"; //$NON-NLS-1$ + static final String PROPERTY_EXPORTS = "exports"; //$NON-NLS-1$ + static final String PROPERTY_EXPORT_REVISIONS = "exportMetaObjectRevisions"; //$NON-NLS-1$ + + private String name; + private String prototype; + private String defaultProperty; + private String attachedType; + private Integer[] exportMetaObjectRevisions; + private List exportList = new ArrayList<>(); + private List propertyList = new ArrayList<>(); + private List methodList = new ArrayList<>(); + private List signalList = new ArrayList<>(); + private List enumList = new ArrayList<>(); + + protected QMLComponentInfo(QMLModelBuilder builder, IQmlObjectDefinition obj) { + builder.ensureIdentifier(obj.getIdentifier(), IDENTIFIER); + for (IQmlObjectMember member : obj.getBody().getMembers()) { + if (member instanceof IQmlPropertyBinding) { + IQmlPropertyBinding prop = (IQmlPropertyBinding) member; + switch (prop.getIdentifier().getName()) { + case PROPERTY_NAME: + this.name = builder.getStringBinding(prop); + break; + case PROPERTY_PROTOTYPE: + this.prototype = builder.getStringBinding(prop); + break; + case PROPERTY_DEF_PROPERTY: + this.defaultProperty = builder.getStringBinding(prop); + break; + case PROPERTY_ATTACHED_TYPE: + this.attachedType = builder.getStringBinding(prop); + break; + case PROPERTY_EXPORTS: + String[] exports = builder.getStringArrayBinding(prop); + for (String exp : exports) { + this.exportList.add(new QMLExportInfo(builder, exp)); + } + break; + case PROPERTY_EXPORT_REVISIONS: + this.exportMetaObjectRevisions = builder.getIntegerArrayBinding(prop); + break; + default: + } + } else if (member instanceof IQmlObjectDefinition) { + IQmlObjectDefinition object = (IQmlObjectDefinition) member; + switch (object.getIdentifier().getName()) { + case QMLPropertyInfo.IDENTIFIER: + this.propertyList.add(new QMLPropertyInfo(builder, object)); + break; + case QMLMethodInfo.IDENTIFIER: + this.methodList.add(new QMLMethodInfo(builder, object)); + break; + case QMLSignalInfo.IDENTIFIER: + this.signalList.add(new QMLSignalInfo(builder, object)); + break; + case QMLEnumInfo.IDENTIFIER: + this.enumList.add(new QMLEnumInfo(builder, object)); + break; + default: + } + } else { + builder.unexpectedNode(member); + } + } + exportList = Collections.unmodifiableList(exportList); + propertyList = Collections.unmodifiableList(propertyList); + methodList = Collections.unmodifiableList(methodList); + signalList = Collections.unmodifiableList(signalList); + enumList = Collections.unmodifiableList(enumList); + } + + public String getName() { + return name; + } + + public String getPrototype() { + return prototype; + } + + public String getDefaultProperty() { + return defaultProperty; + } + + public String getAttachedType() { + return attachedType; + } + + public List getExports() { + return exportList; + } + + public Integer[] getExportMetaObjectRevisions() { + return Arrays.copyOf(exportMetaObjectRevisions, exportMetaObjectRevisions.length); + } + + public List getProperties() { + return propertyList; + } + + public List getMethods() { + return methodList; + } + + public List getSignals() { + return signalList; + } + + public List getEnums() { + return enumList; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLEnumInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLEnumInfo.java new file mode 100644 index 00000000000..3d1c19dc0b2 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLEnumInfo.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.cdt.qt.core.qmljs.IJSObjectExpression; +import org.eclipse.cdt.qt.core.qmljs.IJSProperty; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectDefinition; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectMember; +import org.eclipse.cdt.qt.core.qmljs.IQmlPropertyBinding; +import org.eclipse.cdt.qt.core.qmljs.IQmlScriptBinding; +import org.eclipse.cdt.qt.core.qmljs.QMLExpressionEvaluator; +import org.eclipse.cdt.qt.core.qmljs.QMLExpressionEvaluator.InvalidExpressionException; + +public class QMLEnumInfo { + public static class EnumConst { + private final String identifier; + private final int value; + + private EnumConst(String ident, int val) { + this.identifier = ident; + this.value = val; + } + + public String getIdentifier() { + return identifier; + } + + public int getValue() { + return value; + } + } + + static final String IDENTIFIER = "Enum"; //$NON-NLS-1$ + + static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ + static final String PROPERTY_VALUE = "values"; //$NON-NLS-1$ + + private String name; + private List constantList = new ArrayList<>(); + + QMLEnumInfo(QMLModelBuilder builder, IQmlObjectDefinition obj) { + if (builder.ensureIdentifier(obj.getIdentifier(), IDENTIFIER)) { + for (IQmlObjectMember member : obj.getBody().getMembers()) { + if (builder.ensureNode(member, IQmlPropertyBinding.class)) { + IQmlPropertyBinding prop = (IQmlPropertyBinding) member; + switch (prop.getIdentifier().getName()) { + case PROPERTY_NAME: + this.name = builder.getStringBinding(prop); + break; + case PROPERTY_VALUE: + if (builder.ensureNode(prop.getBinding(), IQmlScriptBinding.class)) { + IQmlScriptBinding binding = ((IQmlScriptBinding) prop.getBinding()); + if (builder.ensureNode(binding.getScript(), IJSObjectExpression.class)) { + IJSObjectExpression objExpr = (IJSObjectExpression) binding.getScript(); + for (IJSProperty property : objExpr.getProperties()) { + Object value; + try { + value = QMLExpressionEvaluator.evaluateConstExpr(property.getValue()); + if (value instanceof Number) { + constantList.add(new EnumConst(property.getType(), ((Number) value).intValue())); + } + } catch (InvalidExpressionException e) { + builder.handleException(e); + } + } + } + } + break; + default: + } + } + } + } + constantList = Collections.unmodifiableList(constantList); + } + + public String getName() { + return name; + } + + public List getConstants() { + return constantList; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLExportInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLExportInfo.java new file mode 100644 index 00000000000..2a4707ed626 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLExportInfo.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +public class QMLExportInfo { + private String type; + private String version; + + QMLExportInfo(QMLModelBuilder builder, String export) { + String[] info = export.split("\\h+"); //$NON-NLS-1$ + switch (info.length) { + case 2: + this.type = info[0]; + this.version = info[1]; + break; + case 1: + this.type = info[0]; + break; + } + } + + public String getType() { + return type; + } + + public String getVersion() { + return version; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLMethodInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLMethodInfo.java new file mode 100644 index 00000000000..a09b6fd8f16 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLMethodInfo.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectDefinition; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectMember; +import org.eclipse.cdt.qt.core.qmljs.IQmlPropertyBinding; + +public class QMLMethodInfo { + static final String IDENTIFIER = "Method"; //$NON-NLS-1$ + + static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ s + static final String PROPERTY_TYPE = "type"; //$NON-NLS-1$ + static final String PROPERTY_REVISION = "revision"; //$NON-NLS-1$ + + private String name; + private String type; + private int revision; + private List parameterList = new ArrayList<>(); + + QMLMethodInfo(QMLModelBuilder builder, IQmlObjectDefinition obj) { + if (builder.ensureIdentifier(obj.getIdentifier(), IDENTIFIER)) { + for (IQmlObjectMember member : obj.getBody().getMembers()) { + if (member instanceof IQmlPropertyBinding) { + IQmlPropertyBinding prop = (IQmlPropertyBinding) member; + switch (prop.getIdentifier().getName()) { + case PROPERTY_NAME: + this.name = builder.getStringBinding(prop); + break; + case PROPERTY_TYPE: + this.type = builder.getStringBinding(prop); + break; + case PROPERTY_REVISION: + this.revision = builder.getIntegerBinding(prop); + break; + default: + } + } else if (member instanceof IQmlObjectDefinition) { + this.parameterList.add(new QMLParameterInfo(builder, (IQmlObjectDefinition) member)); + } else { + builder.unexpectedNode(member); + } + } + } + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public int getRevision() { + return revision; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLModelBuilder.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLModelBuilder.java new file mode 100644 index 00000000000..401d941e4b5 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLModelBuilder.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.internal.qt.core.Activator; +import org.eclipse.cdt.qt.core.qmljs.IJSArrayExpression; +import org.eclipse.cdt.qt.core.qmljs.IJSExpression; +import org.eclipse.cdt.qt.core.qmljs.IQmlASTNode; +import org.eclipse.cdt.qt.core.qmljs.IQmlBinding; +import org.eclipse.cdt.qt.core.qmljs.IQmlProgram; +import org.eclipse.cdt.qt.core.qmljs.IQmlPropertyBinding; +import org.eclipse.cdt.qt.core.qmljs.IQmlQualifiedID; +import org.eclipse.cdt.qt.core.qmljs.IQmlRootObject; +import org.eclipse.cdt.qt.core.qmljs.IQmlScriptBinding; +import org.eclipse.cdt.qt.core.qmljs.QMLExpressionEvaluator; +import org.eclipse.cdt.qt.core.qmljs.QMLExpressionEvaluator.InvalidExpressionException; + +public class QMLModelBuilder { + + private final Map moduleMap = new HashMap<>(); + + public QMLModelBuilder() { + } + + public QMLModuleInfo addModule(String module, IQmlASTNode ast) { + QMLModuleInfo info = moduleMap.get(module); + if (!moduleMap.containsKey(module)) { + if (ensureNode(ast, IQmlProgram.class)) { + IQmlRootObject obj = ((IQmlProgram) ast).getRootObject(); + if (ensureNode(obj, IQmlRootObject.class)) { + info = new QMLModuleInfo(this, obj); + moduleMap.put(module, info); + } + } + } + return info; + } + + public QMLModuleInfo getModule(String module) { + return moduleMap.get(module); + } + + public boolean hasModule(String module) { + return moduleMap.get(module) != null; + } + + boolean ensureIdentifier(IQmlQualifiedID actual, String expected) { + if (!actual.getName().equals(expected)) { + Activator.log("[QmlTypes] Unexpected node identifier: expected '" + expected + "', but was '" //$NON-NLS-1$ //$NON-NLS-2$ + + actual.getName() + "'"); //$NON-NLS-1$ + return false; + } + return true; + } + + boolean ensureNode(IQmlASTNode actual, Class expected) { + if (!expected.isInstance(actual)) { + Activator.log("[QmlTypes] Expected node '" + expected + "', but was '" + actual.getClass().getInterfaces()[0] + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + return false; + } + return true; + } + + boolean ensureValue(Object actual, Class expected) { + if (!expected.isInstance(actual)) { + Activator.log("[QmlTypes] Unexpected value: expected '" + expected + "', but was '" //$NON-NLS-1$ //$NON-NLS-2$ + + actual.getClass().getInterfaces()[0] + "'"); //$NON-NLS-1$ + return false; + } + return true; + } + + void unexpectedNode(IQmlASTNode node) { + Activator.log("[QmlTypes] Unexpected node '" + node.getClass().getInterfaces()[0] + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + String getStringBinding(IQmlPropertyBinding prop) { + IQmlBinding b = prop.getBinding(); + if (ensureNode(b, IQmlScriptBinding.class)) { + IQmlScriptBinding sb = (IQmlScriptBinding) b; + if (ensureNode(sb.getScript(), IJSExpression.class)) { + try { + Object value = QMLExpressionEvaluator.evaluateConstExpr((IJSExpression) sb.getScript()); + if (value instanceof String) { + return (String) value; + } + } catch (InvalidExpressionException e) { + handleException(e); + } + } + } + return null; + } + + String[] getStringArrayBinding(IQmlPropertyBinding prop) { + ArrayList result = new ArrayList<>(); + IQmlBinding b = prop.getBinding(); + if (ensureNode(b, IQmlScriptBinding.class)) { + IQmlScriptBinding sb = (IQmlScriptBinding) b; + if (ensureNode(sb.getScript(), IJSArrayExpression.class)) { + IJSArrayExpression arrExpr = (IJSArrayExpression) sb.getScript(); + for (IJSExpression expr : arrExpr.getElements()) { + try { + Object value = QMLExpressionEvaluator.evaluateConstExpr(expr); + if (value instanceof String) { + result.add((String) value); + } + } catch (InvalidExpressionException e) { + handleException(e); + } + } + } + } + return result.toArray(new String[result.size()]); + } + + public Integer[] getIntegerArrayBinding(IQmlPropertyBinding prop) { + ArrayList result = new ArrayList<>(); + IQmlBinding b = prop.getBinding(); + if (ensureNode(b, IQmlScriptBinding.class)) { + IQmlScriptBinding sb = (IQmlScriptBinding) b; + if (ensureNode(sb.getScript(), IJSArrayExpression.class)) { + IJSArrayExpression arrExpr = (IJSArrayExpression) sb.getScript(); + for (IJSExpression expr : arrExpr.getElements()) { + try { + Object value = QMLExpressionEvaluator.evaluateConstExpr(expr); + if (value instanceof Number) { + result.add(((Number) value).intValue()); + } + } catch (InvalidExpressionException e) { + handleException(e); + } + } + } + } + return result.toArray(new Integer[result.size()]); + } + + boolean getBooleanBinding(IQmlPropertyBinding prop) { + IQmlBinding b = prop.getBinding(); + if (ensureNode(b, IQmlScriptBinding.class)) { + IQmlScriptBinding sb = (IQmlScriptBinding) b; + if (ensureNode(sb.getScript(), IJSExpression.class)) { + try { + Object value = QMLExpressionEvaluator.evaluateConstExpr((IJSExpression) sb.getScript()); + if (value instanceof Number) { + return (Boolean) value; + } + } catch (InvalidExpressionException e) { + handleException(e); + } + } + } + return false; + } + + public Integer getIntegerBinding(IQmlPropertyBinding prop) { + IQmlBinding b = prop.getBinding(); + if (ensureNode(b, IQmlScriptBinding.class)) { + IQmlScriptBinding sb = (IQmlScriptBinding) b; + if (ensureNode(sb.getScript(), IJSExpression.class)) { + try { + Object value = QMLExpressionEvaluator.evaluateConstExpr((IJSExpression) sb.getScript()); + if (value instanceof Number) { + return ((Number) value).intValue(); + } + } catch (InvalidExpressionException e) { + handleException(e); + } + } + } + return 0; + } + + public void handleException(Throwable t) { + Activator.log("[QmlTypes] " + t.getMessage()); //$NON-NLS-1$ + } + +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLModuleInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLModuleInfo.java new file mode 100644 index 00000000000..83afa3e0e36 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLModuleInfo.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectDefinition; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectMember; +import org.eclipse.cdt.qt.core.qmljs.IQmlRootObject; + +public class QMLModuleInfo { + static final String IDENTIFIER = "Module"; //$NON-NLS-1$ + + private List componentsList = new ArrayList<>(); + + QMLModuleInfo(QMLModelBuilder builder, IQmlRootObject obj) { + if (builder.ensureIdentifier(obj.getIdentifier(), IDENTIFIER)) { + for (IQmlObjectMember member : obj.getBody().getMembers()) { + if (builder.ensureNode(member, IQmlObjectDefinition.class)) { + componentsList.add(new QMLComponentInfo(builder, (IQmlObjectDefinition) member)); + } + } + } + componentsList = Collections.unmodifiableList(componentsList); + } + + public List getComponents() { + return componentsList; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLParameterInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLParameterInfo.java new file mode 100644 index 00000000000..b1d6ad7b04a --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLParameterInfo.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectDefinition; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectMember; +import org.eclipse.cdt.qt.core.qmljs.IQmlPropertyBinding; + +public class QMLParameterInfo { + static final String IDENTIFIER = "Parameter"; //$NON-NLS-1$ + + static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ + static final String PROPERTY_TYPE = "type"; //$NON-NLS-1$ + + private String name; + private String type; + + QMLParameterInfo(QMLModelBuilder builder, IQmlObjectDefinition obj) { + if (builder.ensureIdentifier(obj.getIdentifier(), IDENTIFIER)) { + for (IQmlObjectMember member : obj.getBody().getMembers()) { + if (builder.ensureNode(member, IQmlPropertyBinding.class)) { + IQmlPropertyBinding prop = (IQmlPropertyBinding) member; + switch (prop.getIdentifier().getName()) { + case PROPERTY_NAME: + this.name = builder.getStringBinding(prop); + break; + case PROPERTY_TYPE: + this.type = builder.getStringBinding(prop); + break; + default: + } + } + } + } + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLPropertyInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLPropertyInfo.java new file mode 100644 index 00000000000..704c3a55d44 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLPropertyInfo.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectDefinition; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectMember; +import org.eclipse.cdt.qt.core.qmljs.IQmlPropertyBinding; + +public class QMLPropertyInfo { + static final String IDENTIFIER = "Property"; //$NON-NLS-1$ + + static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ + static final String PROPERTY_TYPE = "type"; //$NON-NLS-1$ + static final String PROPERTY_READONLY = "isReadonly"; //$NON-NLS-1$ + static final String PROPERTY_POINTER = "isPointer"; //$NON-NLS-1$ + static final String PROPERTY_LIST = "isList"; //$NON-NLS-1$ + static final String PROPERTY_REVISION = "revision"; //$NON-NLS-1$ + + private String name; + private String type; + private boolean readonly = false; + private boolean pointer = false; + private boolean list = false; + private int revision; + + QMLPropertyInfo(QMLModelBuilder builder, IQmlObjectDefinition obj) { + if (builder.ensureIdentifier(obj.getIdentifier(), IDENTIFIER)) { + for (IQmlObjectMember member : obj.getBody().getMembers()) { + if (builder.ensureNode(member, IQmlPropertyBinding.class)) { + IQmlPropertyBinding prop = (IQmlPropertyBinding) member; + switch (prop.getIdentifier().getName()) { + case PROPERTY_NAME: + this.name = builder.getStringBinding(prop); + break; + case PROPERTY_TYPE: + this.type = builder.getStringBinding(prop); + break; + case PROPERTY_READONLY: + this.readonly = builder.getBooleanBinding(prop); + break; + case PROPERTY_POINTER: + this.pointer = builder.getBooleanBinding(prop); + break; + case PROPERTY_LIST: + this.list = builder.getBooleanBinding(prop); + break; + case PROPERTY_REVISION: + this.revision = builder.getIntegerBinding(prop); + break; + default: + } + } + } + } + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public int getRevision() { + return revision; + } + + public boolean isReadonly() { + return readonly; + } + + public boolean isPointer() { + return pointer; + } + + public boolean isList() { + return list; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLSignalInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLSignalInfo.java new file mode 100644 index 00000000000..70193b90d28 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/qmltypes/QMLSignalInfo.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.qmltypes; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectDefinition; +import org.eclipse.cdt.qt.core.qmljs.IQmlObjectMember; +import org.eclipse.cdt.qt.core.qmljs.IQmlPropertyBinding; + +public class QMLSignalInfo { + static final String IDENTIFIER = "Signal"; //$NON-NLS-1$ + + static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ s + static final String PROPERTY_TYPE = "type"; //$NON-NLS-1$ + static final String PROPERTY_REVISION = "revision"; //$NON-NLS-1$ + + private String name; + private String type; + private Integer revision; + private List parameterList = new ArrayList<>(); + + QMLSignalInfo(QMLModelBuilder builder, IQmlObjectDefinition obj) { + if (builder.ensureIdentifier(obj.getIdentifier(), IDENTIFIER)) { + for (IQmlObjectMember member : obj.getBody().getMembers()) { + if (member instanceof IQmlPropertyBinding) { + IQmlPropertyBinding prop = (IQmlPropertyBinding) member; + switch (prop.getIdentifier().getName()) { + case PROPERTY_NAME: + this.name = builder.getStringBinding(prop); + break; + case PROPERTY_TYPE: + this.type = builder.getStringBinding(prop); + break; + case PROPERTY_REVISION: + this.revision = builder.getIntegerBinding(prop); + break; + default: + } + } else if (member instanceof IQmlObjectDefinition) { + this.parameterList.add(new QMLParameterInfo(builder, (IQmlObjectDefinition) member)); + } else { + builder.unexpectedNode(member); + } + } + } + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public int getRevision() { + return revision; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/IQtInstall.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/IQtInstall.java index 4c063b14426..b3014cbc51b 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/IQtInstall.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/IQtInstall.java @@ -25,4 +25,6 @@ public interface IQtInstall { Path getLibPath(); + Path getQmlPath(); + } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/qmldir/QMLDirectoryInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/qmldir/QMLDirectoryInfo.java new file mode 100644 index 00000000000..be70e0c39cd --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/qmldir/QMLDirectoryInfo.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.qt.core.qmldir; + +import java.io.InputStream; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; + +import org.eclipse.cdt.internal.qt.core.Activator; + +public class QMLDirectoryInfo { + public static class Module { + private final String name; + private final String initialVersion; + + public Module(String name, String ver) { + this.name = name; + this.initialVersion = ver; + } + + public String getName() { + return name; + } + + public String getInitialVersion() { + return initialVersion; + } + } + + public static class Plugin { + private final String name; + private final Path path; + + private Plugin(String name, String path) { + this.name = name; + Path p = null; + if (path != null) { + try { + p = Paths.get(path); + } catch (InvalidPathException e) { + Activator.log(e); + } + } + this.path = p; + } + + public String getName() { + return name; + } + + public Path getRelativePath() { + return path; + } + } + + public static class ResourceFile { + private final String name; + private final boolean internal; + private final boolean singleton; + private final String initialVersion; + + private ResourceFile(String name, String ver, boolean internal, boolean singleton) { + this.name = name; + this.initialVersion = ver; + this.internal = internal; + this.singleton = singleton; + } + + public String getName() { + return name; + } + + public String getInitialVersion() { + return initialVersion; + } + + public boolean isSingleton() { + return singleton; + } + + public boolean isInternal() { + return internal; + } + } + + private String moduleIdentifier; + private Plugin plugin; + private String classname; + private String typeInfo; + private final Collection depends; + private final Collection resources; + private boolean designersupported; + + public QMLDirectoryInfo(InputStream input) { + this.depends = new LinkedList<>(); + this.resources = new LinkedList<>(); + + IQDirAST ast = new QMLDirectoryParser().parse(input); + for (IQDirCommand c : ast.getCommands()) { + if (c instanceof IQDirModuleCommand) { + if (moduleIdentifier == null) { + moduleIdentifier = ((IQDirModuleCommand) c).getModuleIdentifier().getText(); + } + } else if (c instanceof IQDirPluginCommand) { + if (plugin == null) { + IQDirPluginCommand pc = (IQDirPluginCommand) c; + plugin = new Plugin(pc.getName().getText(), pc.getPath() != null ? pc.getPath().getText() : null); + } + } else if (c instanceof IQDirTypeInfoCommand) { + if (typeInfo == null) { + typeInfo = ((IQDirTypeInfoCommand) c).getFile().getText(); + } + } else if (c instanceof IQDirResourceCommand) { + IQDirResourceCommand rc = (IQDirResourceCommand) c; + resources.add(new ResourceFile(rc.getFile().getText(), + rc.getInitialVersion().getVersionString(), + false, false)); + } else if (c instanceof IQDirInternalCommand) { + IQDirInternalCommand rc = (IQDirInternalCommand) c; + resources.add(new ResourceFile(rc.getFile().getText(), + null, true, false)); + } else if (c instanceof IQDirSingletonCommand) { + IQDirSingletonCommand rc = (IQDirSingletonCommand) c; + resources.add(new ResourceFile(rc.getFile().getText(), + rc.getInitialVersion().getVersionString(), + false, true)); + } else if (c instanceof IQDirDependsCommand) { + IQDirDependsCommand dc = (IQDirDependsCommand) c; + depends.add(new Module(dc.getModuleIdentifier().getText(), + dc.getInitialVersion().getVersionString())); + } else if (c instanceof IQDirClassnameCommand) { + if (classname == null) { + classname = ((IQDirClassnameCommand) c).getIdentifier().getText(); + } + } else if (c instanceof IQDirDesignerSupportedCommand) { + designersupported = true; + } + } + } + + public String getModuleIdentifier() { + return moduleIdentifier; + } + + public Plugin getPlugin() { + return plugin; + } + + public String getClassname() { + return classname; + } + + public String getTypesFileName() { + return typeInfo; + } + + public Collection getDependentModules() { + return Collections.unmodifiableCollection(depends); + } + + public Collection getResources() { + return Collections.unmodifiableCollection(resources); + } + + public boolean isDesignersupported() { + return designersupported; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/qmljs/QMLExpressionEvaluator.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/qmljs/QMLExpressionEvaluator.java new file mode 100644 index 00000000000..4a83edaeb5f --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/qmljs/QMLExpressionEvaluator.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.qt.core.qmljs; + +public final class QMLExpressionEvaluator { + private QMLExpressionEvaluator() { + } + + public static class InvalidExpressionException extends Exception { + + private static final long serialVersionUID = 4803923632666457229L; + + private IJSExpression offendingExpression; + + public InvalidExpressionException(String msg, IJSExpression expr) { + super(msg); + this.offendingExpression = expr; + } + + public IJSExpression getOffendingExpression() { + return offendingExpression; + } + } + + /** + * Evaluates the given {@link IJSExpression} as a constant expression and returns the result. At the moment this only supports + * very simple expressions involving unary operators and literals alone. Support for more complex expressions will be added as + * needed. + * + * @param expr + * the expression to be evaluated + * + * @throws InvalidExpressionException + * if the given expression can't be reduced to a single constant + */ + public static Object evaluateConstExpr(IJSExpression expr) throws InvalidExpressionException { + if (expr instanceof IJSLiteral) { + return ((IJSLiteral) expr).getValue(); + } else if (expr instanceof IJSUnaryExpression) { + IJSUnaryExpression unary = (IJSUnaryExpression) expr; + Object arg = evaluateConstExpr(unary.getArgument()); + switch (unary.getOperator()) { + case Plus: + return unaryPlus(arg, unary.getArgument()); + case Negation: + return unaryNegate(arg, unary.getArgument()); + case BitwiseNot: + return unaryBitwiseNot(arg, unary.getArgument()); + case Not: + return unaryNot(arg, unary.getArgument()); + default: + } + } + throw new InvalidExpressionException("Cannot reduce '" + expr + "' to a constant", expr); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private static Object unaryPlus(Object object, IJSExpression expr) throws InvalidExpressionException { + if (object instanceof Byte) { + return +((Byte) object); + } else if (object instanceof Short) { + return +((Short) object); + } else if (object instanceof Integer) { + return +((Integer) object); + } else if (object instanceof Long) { + return +((Long) object); + } else if (object instanceof Float) { + return +((Float) object); + } else if (object instanceof Double) { + return +((Double) object); + } + throw new InvalidExpressionException("Cannot perform unary plus operation on a non-number", expr); //$NON-NLS-1$ + } + + private static Object unaryNegate(Object object, IJSExpression expr) throws InvalidExpressionException { + if (object instanceof Byte) { + return -((Byte) object); + } else if (object instanceof Short) { + return -((Short) object); + } else if (object instanceof Integer) { + return -((Integer) object); + } else if (object instanceof Long) { + return -((Long) object); + } else if (object instanceof Float) { + return -((Float) object); + } else if (object instanceof Double) { + return -((Double) object); + } + throw new InvalidExpressionException("Cannot perform unary negation operation on a non-number", expr); //$NON-NLS-1$ + } + + private static Object unaryBitwiseNot(Object object, IJSExpression expr) throws InvalidExpressionException { + if (object instanceof Byte) { + return ~((Byte) object); + } else if (object instanceof Short) { + return ~((Short) object); + } else if (object instanceof Integer) { + return ~((Integer) object); + } else if (object instanceof Long) { + return ~((Long) object); + } else if (object instanceof Float || object instanceof Double) { + return ~((Number) object).longValue(); + } + throw new InvalidExpressionException("Cannot perform binary not operation on a non-number", expr); //$NON-NLS-1$ + } + + private static Object unaryNot(Object object, IJSExpression expr) throws InvalidExpressionException { + if (object instanceof Boolean) { + return !((Boolean) object); + } + throw new InvalidExpressionException("Cannot perform unary not operation on a non-boolean", expr); //$NON-NLS-1$ + } +} diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/qml-nsh.js b/qt/org.eclipse.cdt.qt.core/tern-qml/qml-nsh.js index 47e3bcbd64f..45a2ae1c380 100644 --- a/qt/org.eclipse.cdt.qt.core/tern-qml/qml-nsh.js +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/qml-nsh.js @@ -8,6 +8,12 @@ function resolveDirectory(obj) { }; } +function resolveModule(obj) { + return function (module) { + return obj.resolveModule(module); + }; +} + function requestCallback(obj) { return function (err, data) { obj.callback(err, data); diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js b/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js index fec8f8538c6..9aaf81bcfd1 100644 --- a/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js @@ -65,6 +65,12 @@ } return path; }, + resolveModule: function (module) { + var impl = this.server.options.resolveModule; + if (impl) { + return impl(module); + } + }, updateDirectoryImportList: function () { if (!this.imports) { this.imports = {}; @@ -961,35 +967,6 @@ } } - /* - * Called when a parse query is made to the server. - */ - function parse(srv, query, file) { - var ast = null; - if (query.file) { - // Get the file's AST. It should have been parsed already by the server. - ast = file.ast; - } else { - // Parse the file manually and get the AST. - var options = { - directSourceFile: file, - allowReturnOutsideFunction: true, - allowImportExportEverywhere: true, - ecmaVersion: srv.options.ecmaVersion - }; - for (var opt in query.options) { - options[opt] = query.options[opt]; - } - query.text = query.text || ""; - var text = srv.signalReturnFirst("preParse", query.text, options) || query.text; - ast = infer.parse(text, options); - srv.signal("postParse", ast, text); - } - return { - ast: ast - }; - } - // Register the QML plugin in Tern tern.registerPlugin("qml", function (server) { // First we want to replace the top-level defs array with our own and save the @@ -1003,19 +980,41 @@ if (this.cx) this.reset(); }; + // Create a method on the server object that parses a string. We can't make this + // a query due to the fact that tern always does its infer processing on every + // query regardless of its contents. + server.parseString = function (text, options, callback) { + try { + var opts = { + allowReturnOutsideFunction: true, + allowImportExportEverywhere: true, + ecmaVersion: this.options.ecmaVersion + }; + for (var opt in options) { + opts[opt] = options[opt]; + } + text = this.signalReturnFirst("preParse", text, opts) || text; + var ast = infer.parse(text, opts); + callback(null, { + ast: ast + }); + this.signal("postParse", ast, text); + } catch (err) { + callback(err, null); + } + }; + // Create the QML Import Handler qmlImportHandler = exports.importHandler = new ImportHandler(server); - // Define the 'parseFile' and 'parseString' query types. The reason we need - // two separate queries for these is that Tern will not allow us to resolve - // a file without 'takesFile' being true. However, if we set 'takesFile' to - // true, Tern will not allow a null file in the query. + // Define the 'parseFile' query type. tern.defineQueryType("parseFile", { takesFile: true, - run: parse - }); - tern.defineQueryType("parseString", { - run: parse + run: function (srv, query, file) { + return { + ast: file.ast + }; + } }); // Hook into server signals diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-parse.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-parse.js index ae6ecd9367d..489808f6865 100644 --- a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-parse.js +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-parse.js @@ -61,12 +61,7 @@ test("{Parse given file}", function (server, callback, name) { }); test("{Parse empty text}", function (server, callback, name) { - server.request({ - query: { - type: "parseString", - text: "" - } - }, function (err, resp) { + server.parseString("", null, function (err, resp) { if (err) { throw err; } @@ -80,12 +75,7 @@ test("{Parse empty text}", function (server, callback, name) { }); test("{Parse text no mode}", function (server, callback, name) { - server.request({ - query: { - type: "parseString", - text: "import QtQuick 2.0\nModule {\n\tComponent {\n\t}\n}" - } - }, function (err, resp) { + server.parseString("import QtQuick 2.0\nModule {\n\tComponent {\n\t}\n}", null, function (err, resp) { if (err) { throw err; } @@ -99,14 +89,8 @@ test("{Parse text no mode}", function (server, callback, name) { }); test("{Parse text (mode: qmltypes)}", function (server, callback, name) { - server.request({ - query: { - type: "parseString", - text: "QtObject {\n\tobj: {\n\t\tprop1: 1,\n\t\tprop2: 2\n\t}\n}", - options: { - mode: "qmltypes" - } - } + server.parseString("QtObject {\n\tobj: {\n\t\tprop1: 1,\n\t\tprop2: 2\n\t}\n}", { + mode: "qmltypes" }, function (err, resp) { if (err) { throw err; @@ -121,15 +105,9 @@ test("{Parse text (mode: qmltypes)}", function (server, callback, name) { }); test("{Parse text with locations}", function (server, callback, name) { - server.request({ - query: { - type: "parseString", - text: "var w = 3", - options: { - mode: "js", - locations: true - } - } + server.parseString("var w = 3", { + mode: "js", + locations: true }, function (err, resp) { if (err) { throw err;