aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/jsextensions/moduleproperties.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/corelib/jsextensions/moduleproperties.cpp')
-rw-r--r--src/lib/corelib/jsextensions/moduleproperties.cpp395
1 files changed, 186 insertions, 209 deletions
diff --git a/src/lib/corelib/jsextensions/moduleproperties.cpp b/src/lib/corelib/jsextensions/moduleproperties.cpp
index f721e0016..2c73f2c53 100644
--- a/src/lib/corelib/jsextensions/moduleproperties.cpp
+++ b/src/lib/corelib/jsextensions/moduleproperties.cpp
@@ -41,8 +41,8 @@
#include <buildgraph/artifact.h>
#include <buildgraph/artifactsscriptvalue.h>
+#include <buildgraph/buildgraph.h>
#include <buildgraph/dependencyparametersscriptvalue.h>
-#include <buildgraph/scriptclasspropertyiterator.h>
#include <language/language.h>
#include <language/propertymapinternal.h>
#include <language/qualifiedid.h>
@@ -53,28 +53,21 @@
#include <tools/qttools.h>
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
-
-#include <QtScript/qscriptclass.h>
+#include <utility>
namespace qbs {
namespace Internal {
-QScriptValue getDataForModuleScriptValue(QScriptEngine *engine, const ResolvedProduct *product,
- const Artifact *artifact, const ResolvedModule *module)
+JSValue createDataForModuleScriptValue(ScriptEngine *engine, const Artifact *artifact)
{
- QScriptValue data = engine->newObject();
- data.setProperty(ModuleNameKey, module->name);
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
- data.setProperty(ProductPtrKey, engine->newVariant(v));
- v.setValue<quintptr>(reinterpret_cast<quintptr>(artifact));
- data.setProperty(ArtifactPtrKey, engine->newVariant(v));
+ JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ attachPointerTo(data, artifact);
return data;
}
-static QScriptValue getModuleProperty(const ResolvedProduct *product, const Artifact *artifact,
- ScriptEngine *engine, const QString &moduleName,
- const QString &propertyName, bool *isPresent = nullptr)
+static JSValue getModuleProperty(const ResolvedProduct *product, const Artifact *artifact,
+ ScriptEngine *engine, const QString &moduleName,
+ const QString &propertyName, bool *isPresent = nullptr)
{
const PropertyMapConstPtr &properties = artifact ? artifact->properties
: product->moduleProperties;
@@ -84,7 +77,7 @@ static QScriptValue getModuleProperty(const ResolvedProduct *product, const Arti
if (!value.isValid()) {
value = properties->moduleProperty(moduleName, propertyName, isPresent);
- // Cache the variant value. We must not cache the QScriptValue here, because it's a
+ // Cache the variant value. We must not cache the script value here, because it's a
// reference and the user might change the actual object.
if (engine->isPropertyCacheEnabled())
engine->addToPropertyCache(moduleName, propertyName, properties, value);
@@ -102,111 +95,101 @@ static QScriptValue getModuleProperty(const ResolvedProduct *product, const Arti
return engine->toScriptValue(value);
}
-class ModulePropertyScriptClass : public QScriptClass
-{
-public:
- ModulePropertyScriptClass(QScriptEngine *engine)
- : QScriptClass(engine)
- {
- }
-
-private:
- QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name,
- QueryFlags flags, uint *id) override
- {
- Q_UNUSED(flags);
- Q_UNUSED(id);
-
- if (name == StringConstants::dependenciesProperty()
- || name == StringConstants::artifactsProperty()) {
- // The prototype is not backed by a QScriptClass.
- m_result = object.prototype().property(name);
- return HandlesReadAccess;
- }
-
- if (name == StringConstants::parametersProperty()) {
- m_result = object.data().property(DependencyParametersKey);
- return HandlesReadAccess;
- }
+struct ModuleData {
+ JSValue dependencyParameters = JS_UNDEFINED;
+ const Artifact *artifact = nullptr;
+};
- setup(object);
- QBS_ASSERT(m_product, return {});
- bool isPresent;
- m_result = getModuleProperty(m_product, m_artifact, static_cast<ScriptEngine *>(engine()),
- m_moduleName, name, &isPresent);
+ModuleData getModuleData(JSContext *ctx, JSValue obj)
+{
+ const ScopedJsValue jsData(ctx, getJsProperty(ctx, obj,
+ StringConstants::dataPropertyInternal()));
+ ModuleData data;
+ data.dependencyParameters = JS_GetPropertyUint32(ctx, jsData, DependencyParametersKey);
+ data.artifact = attachedPointer<Artifact>(
+ jsData, ScriptEngine::engineForContext(ctx)->dataWithPtrClass());
+ return data;
+}
- // It is important that we reject unknown property names. Otherwise QtScript will forward
- // *everything* to us, including built-in stuff like the hasOwnProperty function.
- return isPresent ? HandlesReadAccess : QueryFlags();
+static int getModulePropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj)
+{
+ const auto engine = ScriptEngine::engineForContext(ctx);
+ const ModuleData data = getModuleData(ctx, obj);
+ const auto module = attachedPointer<ResolvedModule>(obj, engine->modulePropertyScriptClass());
+ QBS_ASSERT(module, return -1);
+
+ const PropertyMapInternal *propertyMap;
+ QStringList additionalProperties;
+ if (data.artifact) {
+ propertyMap = data.artifact->properties.get();
+ } else {
+ propertyMap = module->product->moduleProperties.get();
+ if (JS_IsObject(data.dependencyParameters))
+ additionalProperties.push_back(StringConstants::parametersProperty());
}
+ JS_FreeValue(ctx, data.dependencyParameters);
+ getPropertyNames(ctx, ptab, plen, propertyMap->value().value(module->name).toMap(),
+ additionalProperties, engine->baseModuleScriptValue(module));
+ return 0;
+}
- QScriptValue property(const QScriptValue &, const QScriptString &, uint) override
- {
- return m_result;
+static int getModuleProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj,
+ JSAtom prop)
+{
+ if (desc) {
+ desc->getter = desc->setter = desc->value = JS_UNDEFINED;
+ desc->flags = JS_PROP_ENUMERABLE;
}
-
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override
- {
- setup(object);
- QBS_ASSERT(m_artifact || m_product, return nullptr);
- const PropertyMapInternal *propertyMap;
- std::vector<QString> additionalProperties({StringConstants::artifactsProperty(),
- StringConstants::dependenciesProperty()});
- if (m_artifact) {
- propertyMap = m_artifact->properties.get();
- } else {
- propertyMap = m_product->moduleProperties.get();
- if (object.data().property(DependencyParametersKey).isValid())
- additionalProperties.push_back(StringConstants::parametersProperty());
- }
- return new ScriptClassPropertyIterator(object,
- propertyMap->value().value(m_moduleName).toMap(),
- additionalProperties);
+ const auto engine = ScriptEngine::engineForContext(ctx);
+ const QString name = getJsString(ctx, prop);
+ const ModuleData data = getModuleData(ctx, obj);
+ const auto module = attachedPointer<ResolvedModule>(obj, engine->modulePropertyScriptClass());
+ QBS_ASSERT(module, return -1);
+
+ ScopedJsValue parametersMgr(ctx, data.dependencyParameters);
+ if (name == StringConstants::parametersProperty()) {
+ if (desc)
+ desc->value = parametersMgr.release();
+ return 1;
}
- void setup(const QScriptValue &object)
- {
- if (m_lastObjectId != object.objectId()) {
- m_lastObjectId = object.objectId();
- const QScriptValue data = object.data();
- QBS_ASSERT(data.isValid(), return);
- m_moduleName = data.property(ModuleNameKey).toString();
- m_product = reinterpret_cast<const ResolvedProduct *>(
- data.property(ProductPtrKey).toVariant().value<quintptr>());
- m_artifact = reinterpret_cast<const Artifact *>(
- data.property(ArtifactPtrKey).toVariant().value<quintptr>());
- }
+ bool isPresent;
+ JSValue value = getModuleProperty(
+ module->product, data.artifact, ScriptEngine::engineForContext(ctx),
+ module->name, name, &isPresent);
+ if (isPresent) {
+ if (desc)
+ desc->value = value;
+ return 1;
}
- qint64 m_lastObjectId = 0;
- QString m_moduleName;
- const ResolvedProduct *m_product = nullptr;
- const Artifact *m_artifact = nullptr;
- QScriptValue m_result;
-};
-
-static QString ptrKey() { return QStringLiteral("__internalPtr"); }
-static QString typeKey() { return QStringLiteral("__type"); }
-static QString artifactType() { return QStringLiteral("artifact"); }
+ ScopedJsValue v(ctx, JS_GetProperty(ctx, engine->baseModuleScriptValue(module), prop));
+ const int ret = JS_IsUndefined(v) ? 0 : 1;
+ if (desc)
+ desc->value = v.release();
+ return ret;
+}
-static QScriptValue js_moduleDependencies(QScriptContext *, ScriptEngine *engine,
- const ResolvedModule *module)
+static JSValue js_moduleDependencies(JSContext *ctx, JSValueConst this_val, int , JSValueConst *)
{
- QScriptValue result = engine->newArray();
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto module = attachedPointer<ResolvedModule>(this_val, engine->dataWithPtrClass());
+ JSValue result = JS_NewArray(engine->context());
quint32 idx = 0;
- for (const QString &depName : qAsConst(module->moduleDependencies)) {
+ for (const QString &depName : std::as_const(module->moduleDependencies)) {
for (const auto &dep : module->product->modules) {
if (dep->name != depName)
continue;
- QScriptValue obj = engine->newObject(engine->modulePropertyScriptClass());
- obj.setPrototype(engine->moduleScriptValuePrototype(dep.get()));
- QScriptValue data = getDataForModuleScriptValue(engine, module->product, nullptr,
- dep.get());
+ JSValue obj = JS_NewObjectClass(ctx, engine->modulePropertyScriptClass());
+ attachPointerTo(obj, dep.get());
+ JSValue data = createDataForModuleScriptValue(engine, nullptr);
const QVariantMap &params = module->product->moduleParameters.value(dep);
- data.setProperty(DependencyParametersKey, dependencyParametersValue(
- module->product->uniqueName(), dep->name, params, engine));
- obj.setData(data);
- result.setProperty(idx++, obj);
+ JS_SetPropertyUint32(ctx, data, DependencyParametersKey,
+ dependencyParametersValue(module->product->uniqueName(),
+ dep->name, params, engine));
+ defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
break;
}
}
@@ -214,35 +197,84 @@ static QScriptValue js_moduleDependencies(QScriptContext *, ScriptEngine *engine
return result;
}
-static QScriptValue setupModuleScriptValue(ScriptEngine *engine,
- const ResolvedModule *module)
+template<class ProductOrArtifact>
+static JSValue moduleProperty(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
+{
+ if (Q_UNLIKELY(argc < 2))
+ return throwError(ctx, Tr::tr("Function moduleProperty() expects 2 arguments"));
+
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const ResolvedProduct *product = nullptr;
+ const Artifact *artifact = nullptr;
+ if constexpr (std::is_same_v<ProductOrArtifact, ResolvedProduct>) {
+ product = attachedPointer<ResolvedProduct>(this_val, engine->productPropertyScriptClass());
+ } else {
+ artifact = attachedPointer<Artifact>(this_val, engine->dataWithPtrClass());
+ product = artifact->product;
+ }
+
+ const QString moduleName = getJsString(ctx, argv[0]);
+ const QString propertyName = getJsString(ctx, argv[1]);
+ return getModuleProperty(product, artifact, engine, moduleName, propertyName);
+}
+
+
+template<class ProductOrArtifact>
+static JSValue js_moduleProperty(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
+{
+ try {
+ return moduleProperty<ProductOrArtifact>(ctx, this_val, argc, argv);
+ } catch (const ErrorInfo &e) {
+ return throwError(ctx, e.toString());
+ }
+}
+
+template<class ProductOrArtifact>
+static void initModuleProperties(ScriptEngine *engine, JSValue objectWithProperties)
+{
+ JSContext * const ctx = engine->context();
+ JSValue func = JS_NewCFunction(ctx, js_moduleProperty<ProductOrArtifact>, "moduleProperty", 2);
+ setJsProperty(ctx, objectWithProperties, QStringLiteral("moduleProperty"), func);
+}
+
+static JSValue setupBaseModuleScriptValue(ScriptEngine *engine, const ResolvedModule *module)
{
- QScriptValue &moduleScriptValue = engine->moduleScriptValuePrototype(module);
- if (moduleScriptValue.isValid())
+ JSValue &moduleScriptValue = engine->baseModuleScriptValue(module);
+ if (JS_IsObject(moduleScriptValue))
return moduleScriptValue;
- moduleScriptValue = engine->newObject();
- QScriptValue depfunc = engine->newFunction<const ResolvedModule *>(&js_moduleDependencies,
- module);
- moduleScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
- QScriptValue artifactsFunc = engine->newFunction(&artifactsScriptValueForModule, module);
- moduleScriptValue.setProperty(StringConstants::artifactsProperty(), artifactsFunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
+ const ScopedJsValue proto(engine->context(), JS_NewObject(engine->context()));
+ moduleScriptValue = JS_NewObjectProtoClass(engine->context(), proto,
+ engine->dataWithPtrClass());
+ attachPointerTo(moduleScriptValue, module);
+ QByteArray name = StringConstants::dependenciesProperty().toUtf8();
+ const ScopedJsValue depfunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), js_moduleDependencies, name.constData(), 0));
+ const ScopedJsAtom depAtom(engine->context(), name);
+ JS_DefineProperty(engine->context(), moduleScriptValue, depAtom,
+ JS_UNDEFINED, depfunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
+ name = StringConstants::artifactsProperty().toUtf8();
+ const ScopedJsValue artifactsFunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), &artifactsScriptValueForModule,
+ name.constData(), 0));
+ const ScopedJsAtom artifactsAtom(engine->context(), name);
+ JS_DefineProperty(engine->context(), moduleScriptValue, artifactsAtom,
+ JS_UNDEFINED, artifactsFunc, JS_UNDEFINED,
+ JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
return moduleScriptValue;
}
-void ModuleProperties::init(QScriptValue productObject,
+void ModuleProperties::init(ScriptEngine *engine, JSValue productObject,
const ResolvedProduct *product)
{
- init(productObject, product, StringConstants::productValue());
- setupModules(productObject, product, nullptr);
+ initModuleProperties<ResolvedProduct>(engine, productObject);
+ setupModules(engine, productObject, product, nullptr);
}
-void ModuleProperties::init(QScriptValue artifactObject, const Artifact *artifact)
+void ModuleProperties::init(ScriptEngine *engine, JSValue artifactObject, const Artifact *artifact)
{
- init(artifactObject, artifact, artifactType());
+ initModuleProperties<Artifact>(engine, artifactObject);
const auto product = artifact->product;
const QVariantMap productProperties {
{StringConstants::buildDirectoryProperty(), product->buildDirectory()},
@@ -252,106 +284,51 @@ void ModuleProperties::init(QScriptValue artifactObject, const Artifact *artifac
{StringConstants::targetNameProperty(), product->targetName},
{StringConstants::typeProperty(), sorted(product->fileTags.toStringList())}
};
- QScriptEngine * const engine = artifactObject.engine();
- artifactObject.setProperty(StringConstants::productVar(),
- engine->toScriptValue(productProperties));
- setupModules(artifactObject, artifact->product.get(), artifact);
+ setJsProperty(engine->context(), artifactObject, StringConstants::productVar(),
+ engine->toScriptValue(productProperties));
+ setupModules(engine, artifactObject, artifact->product.get(), artifact);
}
-void ModuleProperties::setModuleScriptValue(QScriptValue targetObject,
- const QScriptValue &moduleObject, const QString &moduleName)
+void ModuleProperties::setModuleScriptValue(ScriptEngine *engine, JSValue targetObject,
+ const JSValue &moduleObject, const QString &moduleName)
{
- auto const e = static_cast<ScriptEngine *>(targetObject.engine());
const QualifiedId name = QualifiedId::fromString(moduleName);
- QScriptValue obj = targetObject;
+ JSValue obj = targetObject;
for (int i = 0; i < name.size() - 1; ++i) {
- QScriptValue tmp = obj.property(name.at(i));
- if (!tmp.isObject())
- tmp = e->newObject();
- obj.setProperty(name.at(i), tmp);
+ JSValue tmp = getJsProperty(engine->context(), obj, name.at(i));
+ if (!JS_IsObject(tmp)) {
+ tmp = engine->newObject();
+ setJsProperty(engine->context(), obj, name.at(i), tmp);
+ } else {
+ JS_FreeValue(engine->context(), tmp);
+ }
obj = tmp;
}
- obj.setProperty(name.last(), moduleObject);
- if (moduleName.size() > 1)
- targetObject.setProperty(moduleName, moduleObject);
-}
-
-void ModuleProperties::init(QScriptValue objectWithProperties, const void *ptr,
- const QString &type)
-{
- QScriptEngine * const engine = objectWithProperties.engine();
- objectWithProperties.setProperty(QStringLiteral("moduleProperty"),
- engine->newFunction(ModuleProperties::js_moduleProperty, 2));
- objectWithProperties.setProperty(ptrKey(), engine->toScriptValue(quintptr(ptr)));
- objectWithProperties.setProperty(typeKey(), type);
+ setJsProperty(engine->context(), obj, name.last(), moduleObject);
+ if (name.size() > 1) {
+ setJsProperty(engine->context(), targetObject, moduleName,
+ JS_DupValue(engine->context(), moduleObject));
+ }
}
-void ModuleProperties::setupModules(QScriptValue &object, const ResolvedProduct *product,
- const Artifact *artifact)
+void ModuleProperties::setupModules(ScriptEngine *engine, JSValue &object,
+ const ResolvedProduct *product, const Artifact *artifact)
{
- const auto engine = static_cast<ScriptEngine *>(object.engine());
- QScriptClass *modulePropertyScriptClass = engine->modulePropertyScriptClass();
- if (!modulePropertyScriptClass) {
- modulePropertyScriptClass = new ModulePropertyScriptClass(engine);
+ JSClassID modulePropertyScriptClass = engine->modulePropertyScriptClass();
+ if (modulePropertyScriptClass == 0) {
+ modulePropertyScriptClass = engine->registerClass("ModulePropertyScriptClass", nullptr,
+ nullptr, JS_UNDEFINED, &getModulePropertyNames, &getModuleProperty);
engine->setModulePropertyScriptClass(modulePropertyScriptClass);
}
for (const auto &module : product->modules) {
- QScriptValue moduleObjectPrototype = setupModuleScriptValue(engine, module.get());
- QScriptValue moduleObject = engine->newObject(modulePropertyScriptClass);
- moduleObject.setPrototype(moduleObjectPrototype);
- moduleObject.setData(getDataForModuleScriptValue(engine, product, artifact, module.get()));
- setModuleScriptValue(object, moduleObject, module->name);
- }
-}
-
-QScriptValue ModuleProperties::js_moduleProperty(QScriptContext *context, QScriptEngine *engine)
-{
- try {
- return moduleProperty(context, engine);
- } catch (const ErrorInfo &e) {
- return context->throwError(e.toString());
+ setupBaseModuleScriptValue(engine, module.get());
+ JSValue moduleObject = JS_NewObjectClass(engine->context(), modulePropertyScriptClass);
+ attachPointerTo(moduleObject, module.get());
+ defineJsProperty(engine->context(), moduleObject, StringConstants::dataPropertyInternal(),
+ createDataForModuleScriptValue(engine, artifact));
+ setModuleScriptValue(engine, object, moduleObject, module->name);
}
}
-QScriptValue ModuleProperties::moduleProperty(QScriptContext *context, QScriptEngine *engine)
-{
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("Function moduleProperty() expects 2 arguments"));
- }
-
- const QScriptValue objectWithProperties = context->thisObject();
- const QScriptValue typeScriptValue = objectWithProperties.property(typeKey());
- if (Q_UNLIKELY(!typeScriptValue.isString())) {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: __type not set up"));
- }
- const QScriptValue ptrScriptValue = objectWithProperties.property(ptrKey());
- if (Q_UNLIKELY(!ptrScriptValue.isNumber())) {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: __internalPtr not set up"));
- }
-
- const void *ptr = reinterpret_cast<const void *>(qscriptvalue_cast<quintptr>(ptrScriptValue));
- const ResolvedProduct *product = nullptr;
- const Artifact *artifact = nullptr;
- if (typeScriptValue.toString() == StringConstants::productValue()) {
- QBS_ASSERT(ptr, return {});
- product = static_cast<const ResolvedProduct *>(ptr);
- } else if (typeScriptValue.toString() == artifactType()) {
- QBS_ASSERT(ptr, return {});
- artifact = static_cast<const Artifact *>(ptr);
- product = artifact->product.get();
- } else {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: invalid type"));
- }
-
- const auto qbsEngine = static_cast<ScriptEngine *>(engine);
- const QString moduleName = context->argument(0).toString();
- const QString propertyName = context->argument(1).toString();
- return getModuleProperty(product, artifact, qbsEngine, moduleName, propertyName);
-}
-
} // namespace Internal
} // namespace qbs