summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Komissarov <abbapoh@gmail.com>2023-05-16 01:32:37 +0300
committerIvan Komissarov <ABBAPOH@gmail.com>2023-05-16 10:37:54 +0000
commitce626e5e919cabb32a7fa1be35025837196b8204 (patch)
treeb96320c45b1b2d460a37694977a4777169c30735
parent4e8e1b2719b84a7371acafc311fc74b10ff36362 (diff)
downloadqbs-ce626e5e919cabb32a7fa1be35025837196b8204.tar.gz
js: represent QByteArray as JsArrayBuffer
On macOS, Info.plist can (rarely) contain binary data which is parsed into a QByteArray. However, since byte arrays were discarded when converting from variant, such keys were lost e.g. when doing plist merging. Fix that by converting QByteArray to a JS ArrayBuffer object. Using such types is a bit awkward as seen in the testcase, but conforms JS type system. Change-Id: I7a680aa7943ba3bde1ddf4ac84e3485fb0ba01d8 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--src/lib/corelib/language/scriptengine.cpp6
-rw-r--r--src/lib/corelib/language/scriptengine.h1
-rw-r--r--src/lib/corelib/tools/scripttools.cpp17
-rw-r--r--src/lib/corelib/tools/scripttools.h1
-rw-r--r--src/shared/quickjs/quickjs.c8
-rw-r--r--src/shared/quickjs/quickjs.h1
-rw-r--r--tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist11
-rw-r--r--tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs37
-rw-r--r--tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/main.c1
-rw-r--r--tests/auto/blackbox/tst_blackboxapple.cpp17
-rw-r--r--tests/auto/blackbox/tst_blackboxapple.h1
11 files changed, 98 insertions, 3 deletions
diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp
index 58c2817c6..6a67c1228 100644
--- a/src/lib/corelib/language/scriptengine.cpp
+++ b/src/lib/corelib/language/scriptengine.cpp
@@ -481,6 +481,12 @@ void ScriptEngine::addInternalExtension(const char *name, JSValue ext)
m_internalExtensions.insert(QLatin1String(name), JS_DupValue(m_context, ext));
}
+JSValue ScriptEngine::asJsValue(const QByteArray &s)
+{
+ return JS_NewArrayBufferCopy(
+ m_context, reinterpret_cast<const uint8_t *>(s.constData()), s.size());
+}
+
JSValue ScriptEngine::asJsValue(const QString &s)
{
const auto it = m_stringCache.constFind(s);
diff --git a/src/lib/corelib/language/scriptengine.h b/src/lib/corelib/language/scriptengine.h
index 8c97e3079..4d797dd43 100644
--- a/src/lib/corelib/language/scriptengine.h
+++ b/src/lib/corelib/language/scriptengine.h
@@ -283,6 +283,7 @@ public:
JSValue getInternalExtension(const char *name) const;
void addInternalExtension(const char *name, JSValue ext);
+ JSValue asJsValue(const QByteArray &s);
JSValue asJsValue(const QString &s);
JSValue asJsValue(const QStringList &l);
JSValue asJsValue(const QVariantList &l);
diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp
index 109b74486..4953bbbb5 100644
--- a/src/lib/corelib/tools/scripttools.cpp
+++ b/src/lib/corelib/tools/scripttools.cpp
@@ -179,6 +179,11 @@ bool getJsBoolProperty(JSContext *ctx, JSValue obj, const QString &prop)
return JS_VALUE_GET_BOOL(getJsProperty(ctx, obj, prop));
}
+JSValue makeJsArrayBuffer(JSContext *ctx, const QByteArray &s)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(s);
+}
+
JSValue makeJsString(JSContext *ctx, const QString &s)
{
return ScriptEngine::engineForContext(ctx)->asJsValue(s);
@@ -212,6 +217,8 @@ QStringList getJsStringList(JSContext *ctx, JSValue val)
JSValue makeJsVariant(JSContext *ctx, const QVariant &v)
{
switch (static_cast<QMetaType::Type>(v.userType())) {
+ case QMetaType::QByteArray:
+ return makeJsArrayBuffer(ctx, v.toByteArray());
case QMetaType::QString:
return makeJsString(ctx, v.toString());
case QMetaType::QStringList:
@@ -230,9 +237,6 @@ JSValue makeJsVariant(JSContext *ctx, const QVariant &v)
return JS_NewBool(ctx, v.toBool());
case QMetaType::QVariantMap:
return makeJsVariantMap(ctx, v.toMap());
- case QMetaType::QByteArray:
- QBS_ASSERT(!"QByteArray is not a valid type for JS variant", return JS_UNDEFINED);
- [[fallthrough]];
default:
return JS_UNDEFINED;
}
@@ -254,6 +258,13 @@ static QVariant getJsVariantImpl(JSContext *ctx, JSValue val, QList<JSValue> pat
return getJsString(ctx, val);
if (JS_IsBool(val))
return bool(JS_VALUE_GET_BOOL(val));
+ if (JS_IsArrayBuffer(val)) {
+ size_t size = 0;
+ const auto data = JS_GetArrayBuffer(ctx, &size, val);
+ if (!data || !size)
+ return QByteArray();
+ return QByteArray(reinterpret_cast<const char *>(data), size);
+ }
if (JS_IsArray(ctx, val)) {
if (path.contains(val))
return {};
diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h
index 1af7b2b9e..ea7993485 100644
--- a/src/lib/corelib/tools/scripttools.h
+++ b/src/lib/corelib/tools/scripttools.h
@@ -72,6 +72,7 @@ QVariant getJsVariantProperty(JSContext *ctx, JSValueConst obj, const QString &p
QString getJsString(JSContext *ctx, JSValueConst val);
QString getJsString(JSContext *ctx, JSAtom atom);
QBS_AUTOTEST_EXPORT QVariant getJsVariant(JSContext *ctx, JSValueConst val);
+JSValue makeJsArrayBuffer(JSContext *ctx, const QByteArray &s);
JSValue makeJsString(JSContext *ctx, const QString &s);
JSValue makeJsStringList(JSContext *ctx, const QStringList &l);
JSValue makeJsVariant(JSContext *ctx, const QVariant &v);
diff --git a/src/shared/quickjs/quickjs.c b/src/shared/quickjs/quickjs.c
index f90fb9e4f..5dee09b93 100644
--- a/src/shared/quickjs/quickjs.c
+++ b/src/shared/quickjs/quickjs.c
@@ -9822,6 +9822,14 @@ BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
return TRUE;
}
+JS_BOOL JS_IsArrayBuffer(JSValueConst v)
+{
+ if (!JS_IsObject(v))
+ return FALSE;
+ JSObject *p = JS_VALUE_GET_OBJ(v);
+ return p->class_id == JS_CLASS_ARRAY_BUFFER || p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER;
+}
+
BOOL JS_IsError(JSContext *ctx, JSValueConst val)
{
JSObject *p;
diff --git a/src/shared/quickjs/quickjs.h b/src/shared/quickjs/quickjs.h
index a5adf0cd2..73ba9a58e 100644
--- a/src/shared/quickjs/quickjs.h
+++ b/src/shared/quickjs/quickjs.h
@@ -665,6 +665,7 @@ JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);
JS_BOOL JS_IsRegExp(JSContext* ctx, JSValueConst val);
JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);
JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val);
+JS_BOOL JS_IsArrayBuffer(JSValueConst v);
JSValue JS_NewArray(JSContext *ctx);
int JS_IsArray(JSContext *ctx, JSValueConst val);
diff --git a/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist
new file mode 100644
index 000000000..df0429f25
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>DataKey</key>
+ <!--The data value-->
+ <data>VGhlIGRhdGEgdmFsdWU=</data>
+ <key>StringKey</key>
+ <string>The string value</string>
+</dict>
+</plist>
diff --git a/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs
new file mode 100644
index 000000000..4df0886ff
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs
@@ -0,0 +1,37 @@
+import qbs.BundleTools
+import qbs.TextFile
+
+CppApplication {
+ Depends { name: "bundle" }
+ cpp.minimumMacosVersion: "10.7"
+ files: ["main.c", "ByteArray-Info.plist"]
+ type: base.concat(["txt_output"])
+
+ Properties {
+ condition: qbs.targetOS.includes("darwin")
+ bundle.isBundle: true
+ bundle.identifierPrefix: "com.test"
+ }
+
+ Rule {
+ inputs: ["aggregate_infoplist"]
+ Artifact {
+ filePath: input.fileName + ".out"
+ fileTags: ["txt_output"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating" + output.fileName + " from " + input.fileName;
+ cmd.highlight = "codegen";
+ cmd.sourceCode = function() {
+ var plist = new BundleTools.infoPlistContents(input.filePath);
+ var content = plist["DataKey"];
+ var int8view = new Uint8Array(content);
+ file = new TextFile(output.filePath, TextFile.WriteOnly);
+ file.write(String.fromCharCode.apply(null, int8view));
+ file.close();
+ }
+ return [cmd];
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/main.c b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/main.c
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/main.c
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/tst_blackboxapple.cpp b/tests/auto/blackbox/tst_blackboxapple.cpp
index 5de371792..01446c815 100644
--- a/tests/auto/blackbox/tst_blackboxapple.cpp
+++ b/tests/auto/blackbox/tst_blackboxapple.cpp
@@ -684,6 +684,23 @@ void TestBlackboxApple::bundleStructure_data()
QTest::newRow("G") << "G" << "com.apple.product-type.in-app-purchase-content";
}
+void TestBlackboxApple::byteArrayInfoPlist()
+{
+ QDir::setCurrent(testDataDir + "/byteArrayInfoPlist");
+
+ QCOMPARE(runQbs(), 0);
+
+ const auto infoPlistPath = getInfoPlistPath(
+ relativeProductBuildDir("byteArrayInfoPlist") + "/byteArrayInfoPlist.app");
+ QVERIFY(QFile::exists(infoPlistPath));
+ const auto outFilePath =
+ relativeProductBuildDir("byteArrayInfoPlist") + "/bytearrayInfoPlist-Info.plist.out";
+ QFile file(outFilePath);
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QCOMPARE(file.readAll(), "The data value");
+}
+
void TestBlackboxApple::codesign()
{
QFETCH(bool, isBundle);
diff --git a/tests/auto/blackbox/tst_blackboxapple.h b/tests/auto/blackbox/tst_blackboxapple.h
index 32eee2432..9c329e961 100644
--- a/tests/auto/blackbox/tst_blackboxapple.h
+++ b/tests/auto/blackbox/tst_blackboxapple.h
@@ -55,6 +55,7 @@ private slots:
void assetCatalogsMultiple();
void bundleStructure();
void bundleStructure_data();
+ void byteArrayInfoPlist();
void codesign();
void codesign_data();
void deploymentTarget();